From 41d8663b092ddcdcf14b75c72f243b9d0486d71f Mon Sep 17 00:00:00 2001 From: Anthony Dresser Date: Fri, 16 Aug 2019 21:47:46 -0700 Subject: [PATCH] Merge from vscode 70dc55955d586ebd427658b43cdb344f2047f9c2 (#6789) --- .../darwin/product-build-darwin.yml | 10 + extensions/package.json | 2 +- .../theme-seti/build/update-icon-theme.js | 6 +- extensions/theme-seti/cgmanifest.json | 2 +- .../theme-seti/icons/vs-seti-icon-theme.json | 10 +- extensions/yarn.lock | 8 +- package.json | 2 +- remote/package.json | 2 +- remote/yarn.lock | 8 +- src/buildfile.js | 2 +- src/vs/base/browser/dom.ts | 23 +- src/vs/base/browser/markdownRenderer.ts | 7 +- .../browser/ui/octiconLabel/octiconLabel.ts | 4 +- .../octiconLabel/octicons/octicons-main.css | 21 + .../ui/octiconLabel/octicons/octicons.css | 31 +- .../ui/octiconLabel/octicons/octicons.svg | 22 +- .../ui/octiconLabel/octicons/octicons.ttf | Bin 37504 -> 37448 bytes .../ui/octiconLabel/octicons/octicons2.css | 251 ++++++++ .../ui/octiconLabel/octicons/octicons2.svg | 570 ++++++++++++++++++ .../ui/octiconLabel/octicons/octicons2.ttf | Bin 0 -> 35276 bytes src/vs/base/common/errors.ts | 10 + src/vs/base/common/network.ts | 17 +- .../electron-browser/workbench/workbench.html | 2 +- src/vs/code/electron-main/app.ts | 132 +--- .../browser/services/codeEditorServiceImpl.ts | 8 +- .../editor/contrib/zoneWidget/zoneWidget.ts | 5 +- .../browser/menuEntryActionViewItem.ts | 6 +- .../notification/common/notification.ts | 19 + .../remoteAuthorityResolverService.ts | 2 - .../storage/browser/storageService.ts | 159 ++++- src/vs/platform/storage/common/storage.ts | 74 --- .../storage.test.ts | 10 +- .../api/browser/mainThreadDebugService.ts | 29 +- .../api/browser/viewsExtensionPoint.ts | 12 +- .../workbench/api/common/extHost.protocol.ts | 15 +- src/vs/workbench/api/common/extHostTypes.ts | 18 + .../api/common/menusExtensionPoint.ts | 20 +- .../workbench/api/node/extHostDebugService.ts | 7 +- src/vs/workbench/browser/layout.ts | 29 + .../parts/activitybar/activitybarActions.ts | 2 +- .../parts/quickinput/quickInputUtils.ts | 4 +- .../browser/parts/views/customView.ts | 2 +- src/vs/workbench/browser/web.main.ts | 5 + .../browser/workbench.contribution.ts | 5 + .../contrib/debug/browser/breakpointsView.ts | 90 ++- .../contrib/debug/browser/debugActions.ts | 6 +- .../contrib/debug/browser/debugCommands.ts | 4 +- .../contrib/debug/browser/debugService.ts | 46 +- .../contrib/debug/browser/debugSession.ts | 30 +- .../contrib/debug/browser/rawDebugSession.ts | 14 + .../contrib/debug/browser/variablesView.ts | 14 +- .../workbench/contrib/debug/common/debug.ts | 27 +- .../contrib/debug/common/debugModel.ts | 71 ++- .../debug/test/browser/debugModel.test.ts | 2 +- .../contrib/debug/test/common/mockDebug.ts | 16 +- .../browser/extensionTipsService.ts | 4 +- .../browser/extensionsWorkbenchService.ts | 2 +- .../contrib/files/browser/saveErrorHandler.ts | 23 +- .../files/browser/views/explorerViewer.ts | 50 +- .../browser/localizations.contribution.ts | 14 +- .../contrib/snippets/browser/insertSnippet.ts | 7 +- .../electron-browser/workspaceStatsService.ts | 20 +- .../terminal/browser/terminalConfigHelper.ts | 19 +- .../terminal/common/terminalEnvironment.ts | 32 +- .../contrib/update/electron-browser/update.ts | 68 +-- .../webview/browser/webviewEditorInput.ts | 6 +- .../extensions/browser/extensionService.ts | 44 +- .../browser/webWorkerExtensionHostStarter.ts | 4 +- .../browser/webWorkerFileSystemProvider.ts | 62 ++ .../extensions/common/extensionsRegistry.ts | 12 +- .../extensions/common/staticExtensions.ts | 34 ++ .../electron-browser/extensionService.ts | 23 +- .../services/files/common/workspaceWatcher.ts | 32 +- .../common/notificationService.ts | 12 +- .../themes/browser/fileIconThemeData.ts | 6 +- src/vs/workbench/workbench.desktop.main.ts | 2 + src/vs/workbench/workbench.web.api.ts | 6 + test/smoke/src/vscode/puppeteerDriver.ts | 4 +- yarn.lock | 8 +- 79 files changed, 1815 insertions(+), 572 deletions(-) create mode 100644 src/vs/base/browser/ui/octiconLabel/octicons/octicons-main.css create mode 100644 src/vs/base/browser/ui/octiconLabel/octicons/octicons2.css create mode 100644 src/vs/base/browser/ui/octiconLabel/octicons/octicons2.svg create mode 100644 src/vs/base/browser/ui/octiconLabel/octicons/octicons2.ttf rename src/vs/platform/storage/test/{node => electron-browser}/storage.test.ts (92%) create mode 100644 src/vs/workbench/services/extensions/browser/webWorkerFileSystemProvider.ts create mode 100644 src/vs/workbench/services/extensions/common/staticExtensions.ts diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index 175e3d0eb0..01f5420843 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -107,6 +107,16 @@ steps: displayName: Run integration tests condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) +- script: | + set -e + cd test/smoke + yarn compile + cd - + yarn smoketest --web --headless + continueOnError: true + displayName: Run web smoke tests + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - script: | set -e cd test/smoke diff --git a/extensions/package.json b/extensions/package.json index 57d5f5da31..5c73fcbd3d 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "3.6.0-dev.20190810" + "typescript": "3.6.1-rc" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/theme-seti/build/update-icon-theme.js b/extensions/theme-seti/build/update-icon-theme.js index 69d945693c..4bd77f37f3 100644 --- a/extensions/theme-seti/build/update-icon-theme.js +++ b/extensions/theme-seti/build/update-icon-theme.js @@ -32,7 +32,8 @@ let nonBuiltInLanguages = { // { fileNames, extensions } "haml": { extensions: ['haml'] }, "stylus": { extensions: ['styl'] }, "vala": { extensions: ['vala'] }, - "todo": { fileNames: ['todo'] } + "todo": { fileNames: ['todo'] }, + "jsonc": { extensions: ['json'] } }; let FROM_DISK = true; // set to true to take content from a repo checked out next to the vscode repo @@ -109,7 +110,7 @@ function downloadBinary(source, dest) { return new Promise((c, e) => { https.get(source, function (response) { switch (response.statusCode) { - case 200: + case 200: { let file = fs.createWriteStream(dest); response.on('data', function (chunk) { file.write(chunk); @@ -121,6 +122,7 @@ function downloadBinary(source, dest) { e(err.message); }); break; + } case 301: case 302: case 303: diff --git a/extensions/theme-seti/cgmanifest.json b/extensions/theme-seti/cgmanifest.json index c742c019ea..1c86b8bcb2 100644 --- a/extensions/theme-seti/cgmanifest.json +++ b/extensions/theme-seti/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "seti-ui", "repositoryUrl": "https://github.com/jesseweed/seti-ui", - "commitHash": "904c16acced1134a81b31d71d60293288c31334b" + "commitHash": "85a222708824c6f19bbecbec71633d2c97077dad" } }, "version": "0.1.0" diff --git a/extensions/theme-seti/icons/vs-seti-icon-theme.json b/extensions/theme-seti/icons/vs-seti-icon-theme.json index bdbbcb89ce..b339ff6092 100644 --- a/extensions/theme-seti/icons/vs-seti-icon-theme.json +++ b/extensions/theme-seti/icons/vs-seti-icon-theme.json @@ -1580,6 +1580,7 @@ "version.md": "_clock", "version": "_clock", "mvnw": "_maven", + "tsconfig.json": "_tsconfig", "swagger.json": "_json_1", "swagger.yml": "_json_1", "swagger.yaml": "_json_1", @@ -1589,6 +1590,8 @@ "docker-healthcheck": "_docker_2", "docker-compose.yml": "_docker_3", "docker-compose.yaml": "_docker_3", + "docker-compose.override.yml": "_docker_3", + "docker-compose.override.yaml": "_docker_3", "firebase.json": "_firebase", "geckodriver": "_firefox", "gruntfile.js": "_grunt", @@ -1968,6 +1971,7 @@ "version.md": "_clock_light", "version": "_clock_light", "mvnw": "_maven_light", + "tsconfig.json": "_tsconfig_light", "swagger.json": "_json_1_light", "swagger.yml": "_json_1_light", "swagger.yaml": "_json_1_light", @@ -1977,6 +1981,8 @@ "docker-healthcheck": "_docker_2_light", "docker-compose.yml": "_docker_3_light", "docker-compose.yaml": "_docker_3_light", + "docker-compose.override.yml": "_docker_3_light", + "docker-compose.override.yaml": "_docker_3_light", "firebase.json": "_firebase_light", "geckodriver": "_firefox_light", "gruntfile.js": "_grunt_light", @@ -2011,5 +2017,5 @@ "Schema Compare": "scmp" } }, - "version": "https://github.com/jesseweed/seti-ui/commit/904c16acced1134a81b31d71d60293288c31334b" -} + "version": "https://github.com/jesseweed/seti-ui/commit/85a222708824c6f19bbecbec71633d2c97077dad" +} \ No newline at end of file diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 233cf189e1..26979cdf9d 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -typescript@3.6.0-dev.20190810: - version "3.6.0-dev.20190810" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.0-dev.20190810.tgz#dda80279480131eec9b05e3b78182a1ba1efe105" - integrity sha512-gubcQ8Sn2G5AO1KhjvLpoFrutV7o/ZJ7wCDBC1IKgNI8R2vadIxTystJxAFqkb9boQ7tyRrZ6FwM5EL5ZYfJrg== +typescript@3.6.1-rc: + version "3.6.1-rc" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.1-rc.tgz#9db650b25d8ef033d9e25b3057bdd1e102bb434b" + integrity sha512-u6AQN9AoocZKYSz8zcc1Qh/V/mbAO+BHc73fTiKlIdjzU60A8TesrK9/7kg3GM8o2RxNyCeOFpcevEtnfUyaLg== diff --git a/package.json b/package.json index 4c8595e3f9..79243939aa 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "vscode-sqlite3": "4.0.8", "vscode-textmate": "^4.2.2", "xterm": "3.15.0-beta99", - "xterm-addon-search": "0.2.0-beta3", + "xterm-addon-search": "0.2.0-beta5", "xterm-addon-web-links": "0.1.0-beta10", "yauzl": "^2.9.2", "yazl": "^2.4.3", diff --git a/remote/package.json b/remote/package.json index ae754c1ae6..9bbc0f44c5 100644 --- a/remote/package.json +++ b/remote/package.json @@ -22,7 +22,7 @@ "vscode-ripgrep": "^1.5.6", "vscode-textmate": "^4.2.2", "xterm": "3.15.0-beta99", - "xterm-addon-search": "0.2.0-beta3", + "xterm-addon-search": "0.2.0-beta5", "xterm-addon-web-links": "0.1.0-beta10", "yauzl": "^2.9.2", "yazl": "^2.4.3" diff --git a/remote/yarn.lock b/remote/yarn.lock index 5d578d3130..54e0619915 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -1217,10 +1217,10 @@ vscode-windows-registry@1.0.1: dependencies: nan "^2.12.1" -xterm-addon-search@0.2.0-beta3: - version "0.2.0-beta3" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.2.0-beta3.tgz#710ce14658e269c5a4791f5a9e2f520883a2e62b" - integrity sha512-KzVdkEtGbKJe9ER2TmrI7XjF/wUq1lir9U63vPJi0t2ymQvIECl1V63f9QtOp1vvpdhbZiXBxO+vGTj+y0tRow== +xterm-addon-search@0.2.0-beta5: + version "0.2.0-beta5" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.2.0-beta5.tgz#258d7cb1511d9060cd4520f0f82e408000fd4f53" + integrity sha512-Tg+d8scch0rYOVmzBbX35Y1GXtq+eu/YlzbXznmTo/yD83j3BQlXOhgECu/Yv8EX5JwFmzbfVRWC+JWnfigwGg== xterm-addon-web-links@0.1.0-beta10: version "0.1.0-beta10" diff --git a/src/buildfile.js b/src/buildfile.js index fc52d8acb5..5e578b6da3 100644 --- a/src/buildfile.js +++ b/src/buildfile.js @@ -26,7 +26,7 @@ exports.serviceWorker = [{ exports.workerExtensionHost = [{ name: 'vs/workbench/services/extensions/worker/extensionHostWorker', // include: [], - prepend: ['vs/loader.js'], + prepend: ['vs/loader.js', 'vs/nls.js'], append: ['vs/workbench/services/extensions/worker/extensionHostWorkerMain'], dest: 'vs/workbench/services/extensions/worker/extensionHostWorkerMain.js' }]; diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index d390282a78..2851c07421 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -15,7 +15,7 @@ import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle' import * as platform from 'vs/base/common/platform'; import { coalesce } from 'vs/base/common/arrays'; import { URI } from 'vs/base/common/uri'; -import { Schemas } from 'vs/base/common/network'; +import { Schemas, RemoteAuthorities } from 'vs/base/common/network'; export function clearNode(node: HTMLElement): void { while (node.firstChild) { @@ -1193,14 +1193,21 @@ export function asDomUri(uri: URI): URI { if (!uri) { return uri; } - if (!platform.isWeb) { - //todo@joh remove this once we have sw in electron going - return uri; - } if (Schemas.vscodeRemote === uri.scheme) { - // rewrite vscode-remote-uris to uris of the window location - // so that they can be intercepted by the service worker - return _location.with({ path: '/vscode-remote', query: JSON.stringify(uri) }); + if (platform.isWeb) { + // rewrite vscode-remote-uris to uris of the window location + // so that they can be intercepted by the service worker + return _location.with({ path: '/vscode-remote', query: JSON.stringify(uri) }); + } else { + return RemoteAuthorities.rewrite(uri.authority, uri.path); + } } return uri; } + +/** + * returns url('...') + */ +export function asCSSUrl(uri: URI): string { + return `url('${asDomUri(uri).toString(true).replace(/'/g, '%27')}')`; +} diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index 2878142f0b..07d282c874 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -157,8 +157,9 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende }; } - if (options.actionHandler) { - options.actionHandler.disposeables.add(DOM.addStandardDisposableListener(element, 'click', event => { + const actionHandler = options.actionHandler; + if (actionHandler) { + actionHandler.disposeables.add(DOM.addStandardDisposableListener(element, 'click', event => { let target: HTMLElement | null = event.target; if (target.tagName !== 'A') { target = target.parentElement; @@ -169,7 +170,7 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende try { const href = target.dataset['href']; if (href) { - options.actionHandler!.callback(href, event); + actionHandler.callback(href, event); } } catch (err) { onUnexpectedError(err); diff --git a/src/vs/base/browser/ui/octiconLabel/octiconLabel.ts b/src/vs/base/browser/ui/octiconLabel/octiconLabel.ts index 0332245db3..f43ac7bd68 100644 --- a/src/vs/base/browser/ui/octiconLabel/octiconLabel.ts +++ b/src/vs/base/browser/ui/octiconLabel/octiconLabel.ts @@ -4,6 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./octicons/octicons'; +import 'vs/css!./octicons/octicons2'; +import 'vs/css!./octicons/octicons-main'; import 'vs/css!./octicons/octicons-animations'; import { escape } from 'vs/base/common/strings'; @@ -30,4 +32,4 @@ export class OcticonLabel { set title(title: string) { this._container.title = title; } -} \ No newline at end of file +} diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons-main.css b/src/vs/base/browser/ui/octiconLabel/octicons/octicons-main.css new file mode 100644 index 0000000000..841e5e575c --- /dev/null +++ b/src/vs/base/browser/ui/octiconLabel/octicons/octicons-main.css @@ -0,0 +1,21 @@ +body[data-octicons-update="enabled"] { + --version: octicons2; +} + +body { + --version: octicons; +} + +.octicon, .mega-octicon { + font-family: var(--version) !important; +} + +body[data-octicons-update="enabled"] .monaco-workbench .part.statusbar > .items-container > .statusbar-item span.octicon { + font-size: 16px; +} + +body[data-octicons-update="enabled"] .monaco-workbench .part.statusbar > .items-container > .statusbar-item > a { + display: flex; + align-items: center; + justify-content: center; +} diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css index d9cc1b7a4f..29ed93db1b 100644 --- a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css +++ b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css @@ -1,7 +1,7 @@ @font-face { font-family: "octicons"; - src: url("./octicons.ttf?1b0f2a9535896866c74dd24eedeb4374") format("truetype"), -url("./octicons.svg?1b0f2a9535896866c74dd24eedeb4374#octicons") format("svg"); + src: url("./octicons.ttf?dda6b6d46f87b1fa91a76fc0389eeb1d") format("truetype"), +url("./octicons.svg?dda6b6d46f87b1fa91a76fc0389eeb1d#octicons") format("svg"); } .octicon, .mega-octicon { @@ -169,7 +169,7 @@ url("./octicons.svg?1b0f2a9535896866c74dd24eedeb4374#octicons") format("svg"); .octicon-person-outline:before { content: "\f018" } .octicon-pin:before { content: "\f041" } .octicon-plug:before { content: "\f0d4" } -.octicon-plus-small:before { content: "\f28a" } +.octicon-plus-small:before { content: "\f05d" } .octicon-plus:before { content: "\f05d" } .octicon-primitive-dot:before { content: "\f052" } .octicon-primitive-square:before { content: "\f053" } @@ -233,16 +233,19 @@ url("./octicons.svg?1b0f2a9535896866c74dd24eedeb4374#octicons") format("svg"); .octicon-watch:before { content: "\f0e0" } .octicon-x:before { content: "\f081" } .octicon-zap:before { content: "\26a1" } +.octicon-error:before { content: "\26a2" } +.octicon-eye-closed:before { content: "\26a3" } +.octicon-fold-down:before { content: "\26a4" } +.octicon-fold-up:before { content: "\26a5" } +.octicon-github-action:before { content: "\26a6" } +.octicon-info-outline:before { content: "\26a7" } +.octicon-play:before { content: "\26a8" } +.octicon-remote:before { content: "\26a9" } +.octicon-request-changes:before { content: "\26aa" } +.octicon-smiley-outline:before { content: "\f27d" } +.octicon-warning:before { content: "\f02d" } +.octicon-controls:before { content: "\26ad" } +.octicon-event:before { content: "\26ae" } +.octicon-record-keys:before { content: "\26af" } .octicon-archive:before { content: "\f101" } .octicon-arrow-both:before { content: "\f102" } -.octicon-error:before { content: "\f103" } -.octicon-eye-closed:before { content: "\f104" } -.octicon-fold-down:before { content: "\f105" } -.octicon-fold-up:before { content: "\f106" } -.octicon-github-action:before { content: "\f107" } -.octicon-info-outline:before { content: "\f108" } -.octicon-play:before { content: "\f109" } -.octicon-remote:before { content: "\f10a" } -.octicon-request-changes:before { content: "\f10b" } -.octicon-smiley-outline:before { content: "\f10c" } -.octicon-warning:before { content: "\f10d" } diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.svg b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.svg index 3f4ab4f180..48f7d1b222 100644 --- a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.svg +++ b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.svg @@ -167,10 +167,10 @@ unicode="" horiz-adv-x="750" d=" M687.5 507.5H62.5C28.125 507.5 0 479.375 0 445V195C0 160.625 28.125 132.5 62.5 132.5H687.5C721.875 132.5 750 160.625 750 195V445C750 479.375 721.875 507.5 687.5 507.5zM250 257.5H125V382.5H250V257.5zM437.5 257.5H312.5V382.5H437.5V257.5zM625 257.5H500V382.5H625V257.5z" /> +=y8Xq&=d?#f{lO&{&SxnNTqzo2QWTde0wPB&=c^Z}) zGrkr!ZzBsS7PGTuqe+s5Vlnm(H|O84bGx_B@1FIPl1?hI7VklIVFjRNfc&?a$ntb_ zFdGJn27qXGHat1y`ntR~f~|_4HnuBuKcJM(&qWtsJhwhzCum%?xP11`hY6?u8+qzG zxSm$qF8{lw(Wlzr2K`>BWl;Z4jofqD>V~F#@IwgQu%TRp#f9m%4U=L_Y==0P@IN3fB#2x6j-?#4TQ8x6HLBNd`~c5JTv-4B delta 487 zcmXw#-7CXU7{|ZQ@3$8-^DmxL&@a^ceW z3wEQ_Qf}l*?i3|g?sPQb2G4f#eEamA&gneg?>RSTO8i_o^whNnOQ!+dGhlHBL(8+_ z!E691j|1WCTwrp>QSYlw_}dgK5<4|z5kRpmE`%d5?kg{_yPvr5IDXCb^AjbVA26%0 z;CirqKmGT#j^5NJ4V2qZT~PC@X6}XTszIk@`Ct@1(8I0;v{cfmA0|sOq`w{;#}p#0 z4Qyc>d$`3N?(vBq0vX6k6=b7ImYwAwC%LFGuU3-sPzS4vdTD@$XojK`WiLN6h#{Un zF?3Uv7e4f%A0r%L69+iPDK621RQl1_K&G!;YOYMO*yOgv04*iq`$wiF + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.ttf b/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.ttf new file mode 100644 index 0000000000000000000000000000000000000000..654cfe91291a4b391122cf0b76277f89e8723e80 GIT binary patch literal 35276 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 literal 0 HcmV?d00001 diff --git a/src/vs/base/common/errors.ts b/src/vs/base/common/errors.ts index 99dfffcec4..4a04854bf8 100644 --- a/src/vs/base/common/errors.ts +++ b/src/vs/base/common/errors.ts @@ -194,3 +194,13 @@ export function getErrorMessage(err: any): string { return String(err); } + + +export class NotImplementedError extends Error { + constructor(message?: string) { + super('NotImplemented'); + if (message) { + this.message = message; + } + } +} diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index 3a599d62a5..cb865f2771 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -3,6 +3,8 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { URI } from 'vs/base/common/uri'; + export namespace Schemas { /** @@ -62,13 +64,26 @@ class RemoteAuthoritiesImpl { } public set(authority: string, host: string, port: number): void { - this._hosts[authority] = host; + this._hosts[authority] = (host === 'localhost' ? '127.0.0.1' : host); this._ports[authority] = port; } public setConnectionToken(authority: string, connectionToken: string): void { this._connectionTokens[authority] = connectionToken; } + + public rewrite(authority: string, path: string): URI { + const host = this._hosts[authority]; + const port = this._ports[authority]; + const connectionToken = this._connectionTokens[authority]; + const scheme = (host === '127.0.0.1' ? Schemas.http : Schemas.vscodeRemote); + return URI.from({ + scheme: scheme, + authority: `${host}:${port}`, + path: `/vscode-remote2`, + query: `path=${encodeURIComponent(path)}&tkn=${encodeURIComponent(connectionToken)}` + }); + } } export const RemoteAuthorities = new RemoteAuthoritiesImpl(); diff --git a/src/vs/code/electron-browser/workbench/workbench.html b/src/vs/code/electron-browser/workbench/workbench.html index a8e69a3aab..004dd3db57 100644 --- a/src/vs/code/electron-browser/workbench/workbench.html +++ b/src/vs/code/electron-browser/workbench/workbench.html @@ -4,7 +4,7 @@ - + diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 1fef401f99..b64ef63fc6 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -39,7 +39,6 @@ import { ProxyAuthHandler } from 'vs/code/electron-main/auth'; import { Disposable } from 'vs/base/common/lifecycle'; import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows'; import { IHistoryMainService } from 'vs/platform/history/common/history'; -import { withUndefinedAsNull } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { WorkspacesChannel } from 'vs/platform/workspaces/node/workspacesIpc'; import { IWorkspacesMainService, hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces'; @@ -54,7 +53,6 @@ import { LogLevelSetterChannel } from 'vs/platform/log/common/logIpc'; import { setUnexpectedErrorHandler, onUnexpectedError } from 'vs/base/common/errors'; import { ElectronURLListener } from 'vs/platform/url/electron-main/electronUrlListener'; import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver'; -import { connectRemoteAgentManagement, ManagementPersistentConnection, IConnectionOptions } from 'vs/platform/remote/common/remoteAgentConnection'; import { IMenubarService } from 'vs/platform/menubar/common/menubar'; import { MenubarService } from 'vs/platform/menubar/electron-main/menubarService'; import { MenubarChannel } from 'vs/platform/menubar/node/menubarIpc'; @@ -65,8 +63,6 @@ import { homedir } from 'os'; import { join, sep } from 'vs/base/common/path'; import { localize } from 'vs/nls'; import { Schemas } from 'vs/base/common/network'; -import { REMOTE_FILE_SYSTEM_CHANNEL_NAME } from 'vs/platform/remote/common/remoteAgentFileSystemChannel'; -import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { SnapUpdateService } from 'vs/platform/update/electron-main/updateService.snap'; import { IStorageMainService, StorageMainService } from 'vs/platform/storage/node/storageMainService'; import { GlobalStorageDatabaseChannel } from 'vs/platform/storage/node/storageIpc'; @@ -76,11 +72,7 @@ import { IBackupMainService } from 'vs/platform/backup/common/backup'; import { HistoryMainService } from 'vs/platform/history/electron-main/historyMainService'; import { URLService } from 'vs/platform/url/common/urlService'; import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; -import { RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment'; -import { nodeSocketFactory } from 'vs/platform/remote/node/nodeSocketFactory'; -import { VSBuffer } from 'vs/base/common/buffer'; import { statSync } from 'fs'; -import { ISignService } from 'vs/platform/sign/common/sign'; import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsIpc'; import { IDiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService'; import { FileService } from 'vs/platform/files/common/fileService'; @@ -103,8 +95,7 @@ export class CodeApplication extends Disposable { @IEnvironmentService private readonly environmentService: IEnvironmentService, @ILifecycleService private readonly lifecycleService: ILifecycleService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IStateService private readonly stateService: IStateService, - @ISignService private readonly signService: ISignService + @IStateService private readonly stateService: IStateService ) { super(); @@ -167,11 +158,13 @@ export class CodeApplication extends Disposable { event.preventDefault(); }); app.on('remote-get-current-web-contents', event => { - // The driver needs access to web contents - if (!this.environmentService.args.driver) { - this.logService.trace(`App#on(remote-get-current-web-contents): prevented`); - event.preventDefault(); + if (this.environmentService.args.driver) { + return; // the driver needs access to web contents } + + this.logService.trace(`App#on(remote-get-current-web-contents): prevented`); + + event.preventDefault(); }); app.on('web-contents-created', (_event: Electron.Event, contents) => { contents.on('will-attach-webview', (event: Electron.Event, webPreferences, params) => { @@ -695,112 +688,11 @@ export class CodeApplication extends Disposable { } private handleRemoteAuthorities(): void { - const connectionPool: Map = new Map(); - - class ActiveConnection { - private readonly _authority: string; - private readonly _connection: Promise; - private readonly _disposeRunner: RunOnceScheduler; - - constructor(authority: string, host: string, port: number, signService: ISignService) { - this._authority = authority; - - const options: IConnectionOptions = { - commit: product.commit, - socketFactory: nodeSocketFactory, - addressProvider: { - getAddress: () => { - return Promise.resolve({ host, port }); - } - }, - signService - }; - - this._connection = connectRemoteAgentManagement(options, authority, `main`); - this._disposeRunner = new RunOnceScheduler(() => this.dispose(), 5000); - } - - dispose(): void { - this._disposeRunner.dispose(); - connectionPool.delete(this._authority); - this._connection.then(connection => connection.dispose()); - } - - async getClient(): Promise> { - this._disposeRunner.schedule(); - const connection = await this._connection; - - return connection.client; - } - } - - const resolvedAuthorities = new Map(); - ipc.on('vscode:remoteAuthorityResolved', (event: Electron.Event, data: ResolvedAuthority) => { - this.logService.info('Received resolved authority', data.authority); - - resolvedAuthorities.set(data.authority, data); - - // Make sure to close and remove any existing connections - if (connectionPool.has(data.authority)) { - connectionPool.get(data.authority)!.dispose(); - } - }); - - const resolveAuthority = (authority: string): ResolvedAuthority | null => { - this.logService.info('Resolving authority', authority); - - if (authority.indexOf('+') >= 0) { - if (resolvedAuthorities.has(authority)) { - return withUndefinedAsNull(resolvedAuthorities.get(authority)); - } - - this.logService.info('Didnot find resolved authority for', authority); - - return null; - } else { - const [host, strPort] = authority.split(':'); - const port = parseInt(strPort, 10); - - return { authority, host, port }; - } - }; - - protocol.registerBufferProtocol(Schemas.vscodeRemote, async (request, callback) => { - if (request.method !== 'GET') { - return callback(undefined); - } - - const uri = URI.parse(request.url); - - let activeConnection: ActiveConnection | undefined; - if (connectionPool.has(uri.authority)) { - activeConnection = connectionPool.get(uri.authority); - } else { - const resolvedAuthority = resolveAuthority(uri.authority); - if (!resolvedAuthority) { - callback(undefined); - return; - } - - activeConnection = new ActiveConnection(uri.authority, resolvedAuthority.host, resolvedAuthority.port, this.signService); - connectionPool.set(uri.authority, activeConnection); - } - - try { - const rawClient = await activeConnection!.getClient(); - if (connectionPool.has(uri.authority)) { // not disposed in the meantime - const channel = rawClient.getChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME); - - // TODO@alex don't use call directly, wrap it around a `RemoteExtensionsFileSystemProvider` - const fileContents = await channel.call('readFile', [uri]); - callback(fileContents.buffer); - } else { - callback(undefined); - } - } catch (err) { - onUnexpectedError(err); - callback(undefined); - } + protocol.registerHttpProtocol(Schemas.vscodeRemote, (request, callback) => { + callback({ + url: request.url.replace(/^vscode-remote:/, 'http:'), + method: request.method + }); }); } } diff --git a/src/vs/editor/browser/services/codeEditorServiceImpl.ts b/src/vs/editor/browser/services/codeEditorServiceImpl.ts index 8f11c89704..1840d47596 100644 --- a/src/vs/editor/browser/services/codeEditorServiceImpl.ts +++ b/src/vs/editor/browser/services/codeEditorServiceImpl.ts @@ -229,11 +229,11 @@ const _CSS_MAP: { [prop: string]: string; } = { cursor: 'cursor:{0};', letterSpacing: 'letter-spacing:{0};', - gutterIconPath: 'background:url(\'{0}\') center center no-repeat;', + gutterIconPath: 'background:{0} center center no-repeat;', gutterIconSize: 'background-size:{0};', contentText: 'content:\'{0}\';', - contentIconPath: 'content:url(\'{0}\');', + contentIconPath: 'content:{0};', margin: 'margin:{0};', width: 'width:{0};', height: 'height:{0};' @@ -399,7 +399,7 @@ class DecorationCSSRules { if (typeof opts !== 'undefined') { this.collectBorderSettingsCSSText(opts, cssTextArr); if (typeof opts.contentIconPath !== 'undefined') { - cssTextArr.push(strings.format(_CSS_MAP.contentIconPath, dom.asDomUri(URI.revive(opts.contentIconPath)).toString(true).replace(/'/g, '%27'))); + cssTextArr.push(strings.format(_CSS_MAP.contentIconPath, dom.asCSSUrl(URI.revive(opts.contentIconPath)))); } if (typeof opts.contentText === 'string') { const truncated = opts.contentText.match(/^.*$/m)![0]; // only take first line @@ -426,7 +426,7 @@ class DecorationCSSRules { const cssTextArr: string[] = []; if (typeof opts.gutterIconPath !== 'undefined') { - cssTextArr.push(strings.format(_CSS_MAP.gutterIconPath, dom.asDomUri(URI.revive(opts.gutterIconPath)).toString(true).replace(/'/g, '%27'))); + cssTextArr.push(strings.format(_CSS_MAP.gutterIconPath, dom.asCSSUrl(URI.revive(opts.gutterIconPath)))); if (typeof opts.gutterIconSize !== 'undefined') { cssTextArr.push(strings.format(_CSS_MAP.gutterIconSize, opts.gutterIconSize)); } diff --git a/src/vs/editor/contrib/zoneWidget/zoneWidget.ts b/src/vs/editor/contrib/zoneWidget/zoneWidget.ts index c63c66611d..30cf954e2e 100644 --- a/src/vs/editor/contrib/zoneWidget/zoneWidget.ts +++ b/src/vs/editor/contrib/zoneWidget/zoneWidget.ts @@ -192,9 +192,6 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { } public dispose(): void { - - this._disposables.dispose(); - if (this._overlayWidget) { this.editor.removeOverlayWidget(this._overlayWidget); this._overlayWidget = null; @@ -211,6 +208,8 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { this.editor.deltaDecorations(this._positionMarkerId, []); this._positionMarkerId = []; + + this._disposables.dispose(); } public create(): void { diff --git a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts index c71ce227c4..a3753d92d5 100644 --- a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts +++ b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { addClasses, createCSSRule, removeClasses, asDomUri } from 'vs/base/browser/dom'; +import { addClasses, createCSSRule, removeClasses, asCSSUrl } from 'vs/base/browser/dom'; import { domEvent } from 'vs/base/browser/event'; import { ActionViewItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IAction } from 'vs/base/common/actions'; @@ -245,8 +245,8 @@ export class MenuEntryActionViewItem extends ActionViewItem { iconClass = MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.get(iconPathMapKey)!; } else { iconClass = ids.nextId(); - createCSSRule(`.icon.${iconClass}`, `background-image: url("${asDomUri(item.iconLocation.light || item.iconLocation.dark).toString()}")`); - createCSSRule(`.vs-dark .icon.${iconClass}, .hc-black .icon.${iconClass}`, `background-image: url("${asDomUri(item.iconLocation.dark).toString()}")`); + createCSSRule(`.icon.${iconClass}`, `background-image: ${asCSSUrl(item.iconLocation.light || item.iconLocation.dark)}`); + createCSSRule(`.vs-dark .icon.${iconClass}, .hc-black .icon.${iconClass}`, `background-image: ${asCSSUrl(item.iconLocation.dark)}`); MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.set(iconPathMapKey, iconClass); } diff --git a/src/vs/platform/notification/common/notification.ts b/src/vs/platform/notification/common/notification.ts index 37624915fe..4a487dfaa0 100644 --- a/src/vs/platform/notification/common/notification.ts +++ b/src/vs/platform/notification/common/notification.ts @@ -37,6 +37,19 @@ export interface INotificationProperties { neverShowAgain?: INeverShowAgainOptions; } +export enum NeverShowAgainScope { + + /** + * Will never show this notification on the current workspace again. + */ + WORKSPACE, + + /** + * Will never show this notification on any workspace again. + */ + GLOBAL +} + export interface INeverShowAgainOptions { /** @@ -49,6 +62,12 @@ export interface INeverShowAgainOptions { * make it a secondary action instead. */ isSecondary?: boolean; + + /** + * Wether to persist the choice in the current workspace or for all workspaces. By + * default it will be persisted for all workspaces. + */ + scope?: NeverShowAgainScope; } export interface INotification extends INotificationProperties { diff --git a/src/vs/platform/remote/electron-browser/remoteAuthorityResolverService.ts b/src/vs/platform/remote/electron-browser/remoteAuthorityResolverService.ts index b92262cef5..71c5538c75 100644 --- a/src/vs/platform/remote/electron-browser/remoteAuthorityResolverService.ts +++ b/src/vs/platform/remote/electron-browser/remoteAuthorityResolverService.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { ResolvedAuthority, IRemoteAuthorityResolverService, ResolverResult, ResolvedOptions } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { ipcRenderer as ipc } from 'electron'; import * as errors from 'vs/base/common/errors'; import { RemoteAuthorities } from 'vs/base/common/network'; @@ -50,7 +49,6 @@ export class RemoteAuthorityResolverService implements IRemoteAuthorityResolverS setResolvedAuthority(resolvedAuthority: ResolvedAuthority, options?: ResolvedOptions) { if (this._resolveAuthorityRequests[resolvedAuthority.authority]) { let request = this._resolveAuthorityRequests[resolvedAuthority.authority]; - ipc.send('vscode:remoteAuthorityResolved', resolvedAuthority); RemoteAuthorities.set(resolvedAuthority.authority, resolvedAuthority.host, resolvedAuthority.port); request.resolve({ authority: resolvedAuthority, options }); } diff --git a/src/vs/platform/storage/browser/storageService.ts b/src/vs/platform/storage/browser/storageService.ts index 560e93dca5..1d0241e76c 100644 --- a/src/vs/platform/storage/browser/storageService.ts +++ b/src/vs/platform/storage/browser/storageService.ts @@ -5,15 +5,17 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; -import { IWorkspaceStorageChangeEvent, IStorageService, StorageScope, IWillSaveStateEvent, WillSaveStateReason, logStorage, FileStorageDatabase } from 'vs/platform/storage/common/storage'; +import { IWorkspaceStorageChangeEvent, IStorageService, StorageScope, IWillSaveStateEvent, WillSaveStateReason, logStorage } from 'vs/platform/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; -import { IFileService } from 'vs/platform/files/common/files'; -import { IStorage, Storage } from 'vs/base/parts/storage/common/storage'; +import { IFileService, FileChangeType } from 'vs/platform/files/common/files'; +import { IStorage, Storage, IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest } from 'vs/base/parts/storage/common/storage'; import { URI } from 'vs/base/common/uri'; import { joinPath } from 'vs/base/common/resources'; -import { runWhenIdle } from 'vs/base/common/async'; +import { runWhenIdle, RunOnceScheduler } from 'vs/base/common/async'; +import { serializableToMap, mapToSerializable } from 'vs/base/common/map'; +import { VSBuffer } from 'vs/base/common/buffer'; export class BrowserStorageService extends Disposable implements IStorageService { @@ -35,6 +37,7 @@ export class BrowserStorageService extends Disposable implements IStorageService private workspaceStorageFile: URI; private initializePromise: Promise; + private periodicSaveScheduler = this._register(new RunOnceScheduler(() => this.saveState(), 5000)); get hasPendingUpdate(): boolean { return this.globalStorageDatabase.hasPendingUpdate || this.workspaceStorageDatabase.hasPendingUpdate; @@ -51,20 +54,23 @@ export class BrowserStorageService extends Disposable implements IStorageService // long running operation. // Instead, periodically ask customers to save save. The library will be clever enough // to only save state that has actually changed. - this.saveStatePeriodically(); + this.periodicSaveScheduler.schedule(); } - private saveStatePeriodically(): void { - setTimeout(() => { - runWhenIdle(() => { + private saveState(): void { + runWhenIdle(() => { - // this event will potentially cause new state to be stored + // this event will potentially cause new state to be stored + // since new state will only be created while the document + // has focus, one optimization is to not run this when the + // document has no focus, assuming that state has not changed + if (document.hasFocus()) { this._onWillSaveState.fire({ reason: WillSaveStateReason.NONE }); + } - // repeat - this.saveStatePeriodically(); - }); - }, 5000); + // repeat + this.periodicSaveScheduler.schedule(); + }); } initialize(payload: IWorkspaceInitializationPayload): Promise { @@ -83,14 +89,14 @@ export class BrowserStorageService extends Disposable implements IStorageService // Workspace Storage this.workspaceStorageFile = joinPath(stateRoot, `${payload.id}.json`); - this.workspaceStorageDatabase = this._register(new FileStorageDatabase(this.workspaceStorageFile, this.fileService)); - this.workspaceStorage = new Storage(this.workspaceStorageDatabase); + this.workspaceStorageDatabase = this._register(new FileStorageDatabase(this.workspaceStorageFile, false /* do not watch for external changes */, this.fileService)); + this.workspaceStorage = this._register(new Storage(this.workspaceStorageDatabase)); this._register(this.workspaceStorage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key, scope: StorageScope.WORKSPACE }))); // Global Storage this.globalStorageFile = joinPath(stateRoot, 'global.json'); - this.globalStorageDatabase = this._register(new FileStorageDatabase(this.globalStorageFile, this.fileService)); - this.globalStorage = new Storage(this.globalStorageDatabase); + this.globalStorageDatabase = this._register(new FileStorageDatabase(this.globalStorageFile, true /* watch for external changes */, this.fileService)); + this.globalStorage = this._register(new Storage(this.globalStorageDatabase)); this._register(this.globalStorage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key, scope: StorageScope.GLOBAL }))); // Init both @@ -140,14 +146,125 @@ export class BrowserStorageService extends Disposable implements IStorageService } close(): void { - - // Signal as event so that clients can still store data - this._onWillSaveState.fire({ reason: WillSaveStateReason.SHUTDOWN }); - // We explicitly do not close our DBs because writing data onBeforeUnload() // can result in unexpected results. Namely, it seems that - even though this // operation is async - sometimes it is being triggered on unload and // succeeds. Often though, the DBs turn out to be empty because the write // never had a chance to complete. + // + // Instead we trigger dispose() to ensure that no timeouts or callbacks + // get triggered in this phase. + this.dispose(); + } +} + +export class FileStorageDatabase extends Disposable implements IStorageDatabase { + + private readonly _onDidChangeItemsExternal: Emitter = this._register(new Emitter()); + readonly onDidChangeItemsExternal: Event = this._onDidChangeItemsExternal.event; + + private cache: Map | undefined; + + private pendingUpdate: Promise = Promise.resolve(); + + private _hasPendingUpdate = false; + get hasPendingUpdate(): boolean { + return this._hasPendingUpdate; + } + + private isWatching = false; + + constructor( + private readonly file: URI, + private readonly watchForExternalChanges: boolean, + @IFileService private readonly fileService: IFileService + ) { + super(); + } + + private async ensureWatching(): Promise { + if (this.isWatching || !this.watchForExternalChanges) { + return; + } + + const exists = await this.fileService.exists(this.file); + if (this.isWatching || !exists) { + return; // file must exist to be watched + } + + this.isWatching = true; + + this._register(this.fileService.watch(this.file)); + this._register(this.fileService.onFileChanges(e => { + if (document.hasFocus()) { + return; // ignore changes from ourselves by checking for focus + } + + if (!e.contains(this.file, FileChangeType.UPDATED)) { + return; // not our file + } + + this.onDidStorageChangeExternal(); + })); + } + + private async onDidStorageChangeExternal(): Promise { + const items = await this.doGetItemsFromFile(); + + this.cache = items; + + this._onDidChangeItemsExternal.fire({ items }); + } + + async getItems(): Promise> { + if (!this.cache) { + try { + this.cache = await this.doGetItemsFromFile(); + } catch (error) { + this.cache = new Map(); + } + } + + return this.cache; + } + + private async doGetItemsFromFile(): Promise> { + await this.pendingUpdate; + + const itemsRaw = await this.fileService.readFile(this.file); + + this.ensureWatching(); // now that the file must exist, ensure we watch it for changes + + return serializableToMap(JSON.parse(itemsRaw.value.toString())); + } + + async updateItems(request: IUpdateRequest): Promise { + const items = await this.getItems(); + + if (request.insert) { + request.insert.forEach((value, key) => items.set(key, value)); + } + + if (request.delete) { + request.delete.forEach(key => items.delete(key)); + } + + await this.pendingUpdate; + + this._hasPendingUpdate = true; + + this.pendingUpdate = this.fileService.writeFile(this.file, VSBuffer.fromString(JSON.stringify(mapToSerializable(items)))) + .then(() => { + this.ensureWatching(); // now that the file must exist, ensure we watch it for changes + }) + .finally(() => { + this._hasPendingUpdate = false; + }); + + return this.pendingUpdate; + } + + close(): Promise { + return this.pendingUpdate; } } diff --git a/src/vs/platform/storage/common/storage.ts b/src/vs/platform/storage/common/storage.ts index 8691e4e242..8f2ff78a08 100644 --- a/src/vs/platform/storage/common/storage.ts +++ b/src/vs/platform/storage/common/storage.ts @@ -7,11 +7,6 @@ import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/co import { Event, Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { isUndefinedOrNull } from 'vs/base/common/types'; -import { IUpdateRequest, IStorageDatabase } from 'vs/base/parts/storage/common/storage'; -import { serializableToMap, mapToSerializable } from 'vs/base/common/map'; -import { VSBuffer } from 'vs/base/common/buffer'; -import { URI } from 'vs/base/common/uri'; -import { IFileService } from 'vs/platform/files/common/files'; export const IStorageService = createDecorator('storageService'); @@ -212,75 +207,6 @@ export class InMemoryStorageService extends Disposable implements IStorageServic } } -export class FileStorageDatabase extends Disposable implements IStorageDatabase { - - readonly onDidChangeItemsExternal = Event.None; // TODO@Ben implement global UI storage events - - private cache: Map | undefined; - - private pendingUpdate: Promise = Promise.resolve(); - - private _hasPendingUpdate = false; - get hasPendingUpdate(): boolean { - return this._hasPendingUpdate; - } - - constructor( - private readonly file: URI, - private readonly fileService: IFileService - ) { - super(); - } - - async getItems(): Promise> { - if (!this.cache) { - try { - this.cache = await this.doGetItemsFromFile(); - } catch (error) { - this.cache = new Map(); - } - } - - return this.cache; - } - - private async doGetItemsFromFile(): Promise> { - await this.pendingUpdate; - - const itemsRaw = await this.fileService.readFile(this.file); - - return serializableToMap(JSON.parse(itemsRaw.value.toString())); - } - - async updateItems(request: IUpdateRequest): Promise { - const items = await this.getItems(); - - if (request.insert) { - request.insert.forEach((value, key) => items.set(key, value)); - } - - if (request.delete) { - request.delete.forEach(key => items.delete(key)); - } - - await this.pendingUpdate; - - this._hasPendingUpdate = true; - - this.pendingUpdate = this.fileService.writeFile(this.file, VSBuffer.fromString(JSON.stringify(mapToSerializable(items)))) - .then(() => undefined) - .finally(() => { - this._hasPendingUpdate = false; - }); - - return this.pendingUpdate; - } - - close(): Promise { - return this.pendingUpdate; - } -} - export async function logStorage(global: Map, workspace: Map, globalPath: string, workspacePath: string): Promise { const safeParse = (value: string) => { try { diff --git a/src/vs/platform/storage/test/node/storage.test.ts b/src/vs/platform/storage/test/electron-browser/storage.test.ts similarity index 92% rename from src/vs/platform/storage/test/node/storage.test.ts rename to src/vs/platform/storage/test/electron-browser/storage.test.ts index 02cfa6ca46..14e208a060 100644 --- a/src/vs/platform/storage/test/node/storage.test.ts +++ b/src/vs/platform/storage/test/electron-browser/storage.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { equal } from 'assert'; -import { FileStorageDatabase } from 'vs/platform/storage/common/storage'; +import { FileStorageDatabase } from 'vs/platform/storage/browser/storageService'; import { generateUuid } from 'vs/base/common/uuid'; import { join } from 'vs/base/common/path'; import { tmpdir } from 'os'; @@ -49,7 +49,7 @@ suite('Storage', () => { }); test('File Based Storage', async () => { - let storage = new Storage(new FileStorageDatabase(URI.file(join(testDir, 'storage.json')), fileService)); + let storage = new Storage(new FileStorageDatabase(URI.file(join(testDir, 'storage.json')), false, fileService)); await storage.init(); @@ -63,7 +63,7 @@ suite('Storage', () => { await storage.close(); - storage = new Storage(new FileStorageDatabase(URI.file(join(testDir, 'storage.json')), fileService)); + storage = new Storage(new FileStorageDatabase(URI.file(join(testDir, 'storage.json')), false, fileService)); await storage.init(); @@ -81,7 +81,7 @@ suite('Storage', () => { await storage.close(); - storage = new Storage(new FileStorageDatabase(URI.file(join(testDir, 'storage.json')), fileService)); + storage = new Storage(new FileStorageDatabase(URI.file(join(testDir, 'storage.json')), false, fileService)); await storage.init(); @@ -89,4 +89,4 @@ suite('Storage', () => { equal(storage.get('barNumber', 'undefinedNumber'), 'undefinedNumber'); equal(storage.get('barBoolean', 'undefinedBoolean'), 'undefinedBoolean'); }); -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/api/browser/mainThreadDebugService.ts b/src/vs/workbench/api/browser/mainThreadDebugService.ts index fecbe4df62..de610635e2 100644 --- a/src/vs/workbench/api/browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/browser/mainThreadDebugService.ts @@ -5,10 +5,10 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI as uri } from 'vs/base/common/uri'; -import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, IDebugAdapter, IDebugAdapterDescriptorFactory, IDebugSession, IDebugAdapterFactory } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, IDebugAdapter, IDebugAdapterDescriptorFactory, IDebugSession, IDebugAdapterFactory, IDataBreakpoint } from 'vs/workbench/contrib/debug/common/debug'; import { ExtHostContext, ExtHostDebugServiceShape, MainThreadDebugServiceShape, DebugSessionUUID, MainContext, - IExtHostContext, IBreakpointsDeltaDto, ISourceMultiBreakpointDto, ISourceBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto + IExtHostContext, IBreakpointsDeltaDto, ISourceMultiBreakpointDto, ISourceBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto, IDataBreakpointDto } from 'vs/workbench/api/common/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import severity from 'vs/base/common/severity'; @@ -110,15 +110,16 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb // send all breakpoints const bps = this.debugService.getModel().getBreakpoints(); const fbps = this.debugService.getModel().getFunctionBreakpoints(); + const dbps = this.debugService.getModel().getDataBreakpoints(); if (bps.length > 0 || fbps.length > 0) { this._proxy.$acceptBreakpointsDelta({ - added: this.convertToDto(bps).concat(this.convertToDto(fbps)) + added: this.convertToDto(bps).concat(this.convertToDto(fbps)).concat(this.convertToDto(dbps)) }); } } } - public $registerBreakpoints(DTOs: Array): Promise { + public $registerBreakpoints(DTOs: Array): Promise { for (let dto of DTOs) { if (dto.type === 'sourceMulti') { @@ -136,14 +137,17 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb this.debugService.addBreakpoints(uri.revive(dto.uri), rawbps, 'extension'); } else if (dto.type === 'function') { this.debugService.addFunctionBreakpoint(dto.functionName, dto.id); + } else if (dto.type === 'data') { + this.debugService.addDataBreakpoint(dto.label, dto.dataId, dto.canPersist); } } return Promise.resolve(); } - public $unregisterBreakpoints(breakpointIds: string[], functionBreakpointIds: string[]): Promise { + public $unregisterBreakpoints(breakpointIds: string[], functionBreakpointIds: string[], dataBreakpointIds: string[]): Promise { breakpointIds.forEach(id => this.debugService.removeBreakpoints(id)); functionBreakpointIds.forEach(id => this.debugService.removeFunctionBreakpoints(id)); + dataBreakpointIds.forEach(id => this.debugService.removeDataBreakpoints(id)); return Promise.resolve(); } @@ -294,7 +298,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb return undefined; } - private convertToDto(bps: (ReadonlyArray)): Array { + private convertToDto(bps: (ReadonlyArray)): Array { return bps.map(bp => { if ('name' in bp) { const fbp = bp; @@ -307,6 +311,19 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb logMessage: fbp.logMessage, functionName: fbp.name }; + } else if ('dataId' in bp) { + const dbp = bp; + return { + type: 'data', + id: dbp.getId(), + dataId: dbp.dataId, + enabled: dbp.enabled, + condition: dbp.condition, + hitCondition: dbp.hitCondition, + logMessage: dbp.logMessage, + label: dbp.label, + canPersist: dbp.canPersist + }; } else { const sbp = bp; return { diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index b28522521d..55a348d834 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -37,7 +37,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { createCSSRule, asDomUri } from 'vs/base/browser/dom'; +import { createCSSRule, asCSSUrl } from 'vs/base/browser/dom'; export interface IUserFriendlyViewsContainerDescriptor { id: string; @@ -210,7 +210,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { }); } - private addCustomViewContainers(extensionPoints: IExtensionPointUser[], existingViewContainers: ViewContainer[]): void { + private addCustomViewContainers(extensionPoints: readonly IExtensionPointUser[], existingViewContainers: ViewContainer[]): void { const viewContainersRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); let order = TEST_VIEW_CONTAINER_ORDER + viewContainersRegistry.all.filter(v => !!v.extensionId).length + 1; for (let { value, collector, description } of extensionPoints) { @@ -227,7 +227,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { } } - private removeCustomViewContainers(extensionPoints: IExtensionPointUser[]): void { + private removeCustomViewContainers(extensionPoints: readonly IExtensionPointUser[]): void { const viewContainersRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); const removedExtensions: Set = extensionPoints.reduce((result, e) => { result.add(ExtensionIdentifier.toKey(e.description.identifier)); return result; }, new Set()); for (const viewContainer of viewContainersRegistry.all) { @@ -356,7 +356,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { // Generate CSS to show the icon in the activity bar const iconClass = `.monaco-workbench .activitybar .monaco-action-bar .action-label.${cssClass}`; - createCSSRule(iconClass, `-webkit-mask: url('${asDomUri(icon)}') no-repeat 50% 50%; -webkit-mask-size: 24px;`); + createCSSRule(iconClass, `-webkit-mask: ${asCSSUrl(icon)} no-repeat 50% 50%; -webkit-mask-size: 24px;`); } return viewContainer; @@ -378,7 +378,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { }); } - private addViews(extensions: IExtensionPointUser[]): void { + private addViews(extensions: readonly IExtensionPointUser[]): void { for (const extension of extensions) { const { value, collector } = extension; @@ -442,7 +442,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { return this.viewContainersRegistry.get(EXPLORER)!; } - private removeViews(extensions: IExtensionPointUser[]): void { + private removeViews(extensions: readonly IExtensionPointUser[]): void { const removedExtensions: Set = extensions.reduce((result, e) => { result.add(ExtensionIdentifier.toKey(e.description.identifier)); return result; }, new Set()); for (const viewContainer of this.viewContainersRegistry.all) { const removedViews = this.viewsRegistry.getViews(viewContainer).filter((v: ICustomViewDescriptor) => v.extensionId && removedExtensions.has(ExtensionIdentifier.toKey(v.extensionId))); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 35f3d0a695..016b4b097c 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -719,8 +719,8 @@ export interface MainThreadDebugServiceShape extends IDisposable { $customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): Promise; $appendDebugConsole(value: string): void; $startBreakpointEvents(): void; - $registerBreakpoints(breakpoints: Array): Promise; - $unregisterBreakpoints(breakpointIds: string[], functionBreakpointIds: string[]): Promise; + $registerBreakpoints(breakpoints: Array): Promise; + $unregisterBreakpoints(breakpointIds: string[], functionBreakpointIds: string[], dataBreakpointIds: string[]): Promise; } export interface IOpenUriOptions { @@ -1203,6 +1203,13 @@ export interface IFunctionBreakpointDto extends IBreakpointDto { functionName: string; } +export interface IDataBreakpointDto extends IBreakpointDto { + type: 'data'; + dataId: string; + canPersist: boolean; + label: string; +} + export interface ISourceBreakpointDto extends IBreakpointDto { type: 'source'; uri: UriComponents; @@ -1211,9 +1218,9 @@ export interface ISourceBreakpointDto extends IBreakpointDto { } export interface IBreakpointsDeltaDto { - added?: Array; + added?: Array; removed?: string[]; - changed?: Array; + changed?: Array; } export interface ISourceMultiBreakpointDto { diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 70e9d5851e..5d863ad87d 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -2166,6 +2166,24 @@ export class FunctionBreakpoint extends Breakpoint { } } +@es5ClassCompat +export class DataBreakpoint extends Breakpoint { + readonly label: string; + readonly dataId: string; + readonly canPersist: boolean; + + constructor(label: string, dataId: string, canPersist: boolean, enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string) { + super(enabled, condition, hitCondition, logMessage); + if (!dataId) { + throw illegalArgument('dataId'); + } + this.label = label; + this.dataId = dataId; + this.canPersist = canPersist; + } +} + + @es5ClassCompat export class DebugAdapterExecutable implements vscode.DebugAdapterExecutable { readonly command: string; diff --git a/src/vs/workbench/api/common/menusExtensionPoint.ts b/src/vs/workbench/api/common/menusExtensionPoint.ts index 80942178ae..a1b8cd5f4a 100644 --- a/src/vs/workbench/api/common/menusExtensionPoint.ts +++ b/src/vs/workbench/api/common/menusExtensionPoint.ts @@ -12,7 +12,7 @@ import { IExtensionPointUser, ExtensionMessageCollector, ExtensionsRegistry } fr import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { MenuId, MenuRegistry, ILocalizedString, IMenuItem } from 'vs/platform/actions/common/actions'; import { URI } from 'vs/base/common/uri'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle'; namespace schema { @@ -334,7 +334,7 @@ namespace schema { }; } -let _commandRegistrations: IDisposable[] = []; +const _commandRegistrations = new DisposableStore(); export const commandsExtensionPoint = ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'commands', @@ -343,7 +343,7 @@ export const commandsExtensionPoint = ExtensionsRegistry.registerExtensionPoint< commandsExtensionPoint.setHandler(extensions => { - function handleCommand(userFriendlyCommand: schema.IUserFriendlyCommand, extension: IExtensionPointUser, disposables: IDisposable[]) { + function handleCommand(userFriendlyCommand: schema.IUserFriendlyCommand, extension: IExtensionPointUser) { if (!schema.isValidCommand(userFriendlyCommand, extension.collector)) { return; @@ -373,20 +373,20 @@ commandsExtensionPoint.setHandler(extensions => { precondition: ContextKeyExpr.deserialize(enablement), iconLocation: absoluteIcon }); - disposables.push(registration); + _commandRegistrations.add(registration); } // remove all previous command registrations - _commandRegistrations = dispose(_commandRegistrations); + _commandRegistrations.clear(); - for (let extension of extensions) { + for (const extension of extensions) { const { value } = extension; - if (Array.isArray(value)) { - for (let command of value) { - handleCommand(command, extension, _commandRegistrations); + if (Array.isArray(value)) { + for (const command of value) { + handleCommand(command, extension); } } else { - handleCommand(value, extension, _commandRegistrations); + handleCommand(value, extension); } } }); diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index 114b9f3f90..bbf3c7529b 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -14,7 +14,7 @@ import { IBreakpointsDeltaDto, ISourceMultiBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto } from 'vs/workbench/api/common/extHost.protocol'; import * as vscode from 'vscode'; -import { Disposable, Position, Location, SourceBreakpoint, FunctionBreakpoint, DebugAdapterServer, DebugAdapterExecutable } from 'vs/workbench/api/common/extHostTypes'; +import { Disposable, Position, Location, SourceBreakpoint, FunctionBreakpoint, DebugAdapterServer, DebugAdapterExecutable, DataBreakpoint } from 'vs/workbench/api/common/extHostTypes'; import { ExecutableDebugAdapter, SocketDebugAdapter } from 'vs/workbench/contrib/debug/node/debugAdapter'; import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter'; import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; @@ -248,7 +248,8 @@ export class ExtHostDebugService implements IExtHostDebugService, ExtHostDebugSe // unregister with VS Code const ids = breakpoints.filter(bp => bp instanceof SourceBreakpoint).map(bp => bp.id); const fids = breakpoints.filter(bp => bp instanceof FunctionBreakpoint).map(bp => bp.id); - return this._debugServiceProxy.$unregisterBreakpoints(ids, fids); + const dids = breakpoints.filter(bp => bp instanceof DataBreakpoint).map(bp => bp.id); + return this._debugServiceProxy.$unregisterBreakpoints(ids, fids, dids); } public startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration, parentSession?: vscode.DebugSession): Promise { @@ -554,6 +555,8 @@ export class ExtHostDebugService implements IExtHostDebugService, ExtHostDebugSe let bp: vscode.Breakpoint; if (bpd.type === 'function') { bp = new FunctionBreakpoint(bpd.functionName, bpd.enabled, bpd.condition, bpd.hitCondition, bpd.logMessage); + } else if (bpd.type === 'data') { + bp = new DataBreakpoint(bpd.label, bpd.dataId, bpd.canPersist, bpd.enabled, bpd.hitCondition, bpd.condition, bpd.logMessage); } else { const uri = URI.revive(bpd.uri); bp = new SourceBreakpoint(new Location(uri, new Position(bpd.line, bpd.character)), bpd.enabled, bpd.condition, bpd.hitCondition, bpd.logMessage); diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 32b29633a6..df2e809994 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -46,6 +46,9 @@ enum Settings { PANEL_POSITION = 'workbench.panel.defaultLocation', ZEN_MODE_RESTORE = 'zenMode.restore', + + // TODO @misolori update this when finished + OCTICONS_UPDATE_ENABLED = 'workbench.octiconsUpdate.enabled', } enum Storage { @@ -173,6 +176,11 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi wasSideBarVisible: false, wasPanelVisible: false, transitionDisposables: new DisposableStore() + }, + + // TODO @misolori update this when finished + octiconsUpdate: { + enabled: false } }; @@ -314,6 +322,10 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const newMenubarVisibility = this.configurationService.getValue(Settings.MENUBAR_VISIBLE); this.setMenubarVisibility(newMenubarVisibility, !!skipLayout); + // TODO @misolori update this when finished + const newOcticonsUpdate = this.configurationService.getValue(Settings.OCTICONS_UPDATE_ENABLED); + this.setOcticonsUpdate(newOcticonsUpdate); + } private setSideBarPosition(position: Position): void { @@ -426,6 +438,10 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Zen mode enablement this.state.zenMode.restore = this.storageService.getBoolean(Storage.ZEN_MODE_ENABLED, StorageScope.WORKSPACE, false) && this.configurationService.getValue(Settings.ZEN_MODE_RESTORE); + // TODO @misolori update this when finished + this.state.octiconsUpdate.enabled = this.configurationService.getValue(Settings.OCTICONS_UPDATE_ENABLED); + this.setOcticonsUpdate(this.state.octiconsUpdate.enabled); + } private resolveEditorsToOpen(fileService: IFileService): Promise | IResourceEditor[] { @@ -729,6 +745,19 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } } + // TODO @misolori update this when finished + private setOcticonsUpdate(enabled: boolean): void { + this.state.octiconsUpdate.enabled = enabled; + + // Update DOM + if (enabled) { + document.body.dataset.octiconsUpdate = 'enabled'; + } else { + document.body.dataset.octiconsUpdate = ''; + } + + } + protected createWorkbenchLayout(instantiationService: IInstantiationService): void { const titleBar = this.getPart(Parts.TITLEBAR_PART); const editorPart = this.getPart(Parts.EDITOR_PART); diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index 1dc23193df..fc8ef59cb5 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -171,7 +171,7 @@ export class PlaceHolderViewletActivityAction extends ViewletActivityAction { super({ id, name: id, cssClass: `extensionViewlet-placeholder-${id.replace(/\./g, '-')}` }, viewletService, layoutService, telemetryService); const iconClass = `.monaco-workbench .activitybar .monaco-action-bar .action-label.${this.class}`; // Generate Placeholder CSS to show the icon in the activity bar - DOM.createCSSRule(iconClass, `-webkit-mask: url('${DOM.asDomUri(iconUrl) || ''}') no-repeat 50% 50%; -webkit-mask-size: 24px;`); + DOM.createCSSRule(iconClass, `-webkit-mask: ${DOM.asCSSUrl(iconUrl)} no-repeat 50% 50%; -webkit-mask-size: 24px;`); } setActivity(activity: IActivity): void { diff --git a/src/vs/workbench/browser/parts/quickinput/quickInputUtils.ts b/src/vs/workbench/browser/parts/quickinput/quickInputUtils.ts index b3e5100571..136755bd3a 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInputUtils.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInputUtils.ts @@ -22,8 +22,8 @@ export function getIconClass(iconPath: { dark: URI; light?: URI; } | undefined): iconClass = iconPathToClass[key]; } else { iconClass = iconClassGenerator.nextId(); - dom.createCSSRule(`.${iconClass}`, `background-image: url("${dom.asDomUri(iconPath.light || iconPath.dark).toString()}")`); - dom.createCSSRule(`.vs-dark .${iconClass}, .hc-black .${iconClass}`, `background-image: url("${dom.asDomUri(iconPath.dark).toString()}")`); + dom.createCSSRule(`.${iconClass}`, `background-image: ${dom.asCSSUrl(iconPath.light || iconPath.dark)}`); + dom.createCSSRule(`.vs-dark .${iconClass}, .hc-black .${iconClass}`, `background-image: ${dom.asCSSUrl(iconPath.dark)}`); iconPathToClass[key] = iconClass; } diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index 6979c02fbc..ece095c111 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -756,7 +756,7 @@ class TreeRenderer extends Disposable implements ITreeRenderer{ $treeViewId: this.treeViewId, $treeItemHandle: node.handle }; templateData.actionBar.push(this.menus.getResourceActions(node), { icon: true, label: false }); diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 6bee433e83..782c544fac 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -41,6 +41,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { getThemeTypeSelector, DARK, HIGH_CONTRAST, LIGHT } from 'vs/platform/theme/common/themeService'; import { InMemoryUserDataProvider } from 'vs/workbench/services/userData/common/inMemoryUserDataProvider'; import { registerWindowDriver } from 'vs/platform/driver/browser/driver'; +import { StaticExtensionsService, IStaticExtensionsService } from 'vs/workbench/services/extensions/common/staticExtensions'; class CodeRendererMain extends Disposable { @@ -145,6 +146,10 @@ class CodeRendererMain extends Disposable { const fileService = this._register(new FileService(logService)); serviceCollection.set(IFileService, fileService); + // Static Extensions + const staticExtensions = new StaticExtensionsService(this.configuration.staticExtensions || []); + serviceCollection.set(IStaticExtensionsService, staticExtensions); + let userDataProvider: IFileSystemProvider | undefined = this.configuration.userDataProvider; const connection = remoteAgentService.getConnection(); if (connection) { diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 19c9fba474..68492759aa 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -239,6 +239,11 @@ import { isMacintosh, isWindows, isLinux, isWeb } from 'vs/base/common/platform' 'description': nls.localize('workbench.useExperimentalGridLayout', "Enables the grid layout for the workbench. This setting may enable additional layout options for workbench components."), 'default': true, 'scope': ConfigurationScope.APPLICATION + }, + 'workbench.octiconsUpdate.enabled': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('workbench.octiconsUpdate.enabled', "Controls the visibility of the new Octicons style in the workbench.") } } }); diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index cea2d6ba2c..fa94de5165 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -8,7 +8,7 @@ import * as resources from 'vs/base/common/resources'; import * as dom from 'vs/base/browser/dom'; import { IAction, Action } from 'vs/base/common/actions'; import { IDebugService, IBreakpoint, CONTEXT_BREAKPOINTS_FOCUSED, EDITOR_CONTRIBUTION_ID, State, DEBUG_SCHEME, IFunctionBreakpoint, IExceptionBreakpoint, IEnablement, IDebugEditorContribution } from 'vs/workbench/contrib/debug/common/debug'; -import { ExceptionBreakpoint, FunctionBreakpoint, Breakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; +import { ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, DataBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; import { AddFunctionBreakpointAction, ToggleBreakpointsActivatedAction, RemoveAllBreakpointsAction, RemoveBreakpointAction, EnableAllBreakpointsAction, DisableAllBreakpointsAction, ReapplyBreakpointsAction } from 'vs/workbench/contrib/debug/browser/debugActions'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -74,6 +74,7 @@ export class BreakpointsView extends ViewletPanel { this.instantiationService.createInstance(BreakpointsRenderer), new ExceptionBreakpointsRenderer(this.debugService), this.instantiationService.createInstance(FunctionBreakpointsRenderer), + this.instantiationService.createInstance(DataBreakpointsRenderer), new FunctionBreakpointInputRenderer(this.debugService, this.contextViewService, this.themeService) ], { identityProvider: { getId: (element: IEnablement) => element.getId() }, @@ -91,7 +92,7 @@ export class BreakpointsView extends ViewletPanel { this._register(this.list.onContextMenu(this.onListContextMenu, this)); - this._register(this.list.onDidOpen(e => { + this._register(this.list.onDidOpen(async e => { let isSingleClick = false; let isDoubleClick = false; let isMiddleClick = false; @@ -110,9 +111,11 @@ export class BreakpointsView extends ViewletPanel { if (isMiddleClick) { if (element instanceof Breakpoint) { - this.debugService.removeBreakpoints(element.getId()); + await this.debugService.removeBreakpoints(element.getId()); } else if (element instanceof FunctionBreakpoint) { - this.debugService.removeFunctionBreakpoints(element.getId()); + await this.debugService.removeFunctionBreakpoints(element.getId()); + } else if (element instanceof DataBreakpoint) { + await this.debugService.removeDataBreakpoints(element.getId()); } return; } @@ -222,14 +225,14 @@ export class BreakpointsView extends ViewletPanel { private get elements(): IEnablement[] { const model = this.debugService.getModel(); - const elements = (>model.getExceptionBreakpoints()).concat(model.getFunctionBreakpoints()).concat(model.getBreakpoints()); + const elements = (>model.getExceptionBreakpoints()).concat(model.getFunctionBreakpoints()).concat(model.getDataBreakpoints()).concat(model.getBreakpoints()); return elements; } private getExpandedBodySize(): number { const model = this.debugService.getModel(); - const length = model.getBreakpoints().length + model.getExceptionBreakpoints().length + model.getFunctionBreakpoints().length; + const length = model.getBreakpoints().length + model.getExceptionBreakpoints().length + model.getFunctionBreakpoints().length + model.getDataBreakpoints().length; return Math.min(BreakpointsView.MAX_VISIBLE_FILES, length) * 22; } } @@ -259,6 +262,9 @@ class BreakpointsDelegate implements IListVirtualDelegate { if (element instanceof ExceptionBreakpoint) { return ExceptionBreakpointsRenderer.ID; } + if (element instanceof DataBreakpoint) { + return DataBreakpointsRenderer.ID; + } return ''; } @@ -454,6 +460,61 @@ class FunctionBreakpointsRenderer implements IListRenderer { + + constructor( + @IDebugService private readonly debugService: IDebugService + ) { + // noop + } + + static readonly ID = 'databreakpoints'; + + get templateId() { + return DataBreakpointsRenderer.ID; + } + + renderTemplate(container: HTMLElement): IBaseBreakpointWithIconTemplateData { + const data: IBreakpointTemplateData = Object.create(null); + data.breakpoint = dom.append(container, $('.breakpoint')); + + data.icon = $('.icon'); + data.checkbox = createCheckbox(); + data.toDispose = []; + data.toDispose.push(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => { + this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context); + })); + + dom.append(data.breakpoint, data.icon); + dom.append(data.breakpoint, data.checkbox); + + data.name = dom.append(data.breakpoint, $('span.name')); + + return data; + } + + renderElement(dataBreakpoint: DataBreakpoint, index: number, data: IBaseBreakpointWithIconTemplateData): void { + data.context = dataBreakpoint; + data.name.textContent = dataBreakpoint.label; + const { className, message } = getBreakpointMessageAndClassName(this.debugService, dataBreakpoint); + data.icon.className = className + ' icon'; + data.icon.title = message ? message : ''; + data.checkbox.checked = dataBreakpoint.enabled; + data.breakpoint.title = dataBreakpoint.label; + + // Mark function breakpoints as disabled if deactivated or if debug type does not support them #9099 + const session = this.debugService.getViewModel().focusedSession; + dom.toggleClass(data.breakpoint, 'disabled', (session && !session.capabilities.supportsDataBreakpoints) || !this.debugService.getModel().areBreakpointsActivated()); + if (session && !session.capabilities.supportsDataBreakpoints) { + data.breakpoint.title = nls.localize('dataBreakpointsNotSupported', "Data breakpoints are not supported by this debug type"); + } + } + + disposeTemplate(templateData: IBaseBreakpointWithIconTemplateData): void { + dispose(templateData.toDispose); + } +} + class FunctionBreakpointInputRenderer implements IListRenderer { constructor( @@ -572,7 +633,7 @@ export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolea }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP); } -export function getBreakpointMessageAndClassName(debugService: IDebugService, breakpoint: IBreakpoint | FunctionBreakpoint): { message?: string, className: string } { +export function getBreakpointMessageAndClassName(debugService: IDebugService, breakpoint: IBreakpoint | FunctionBreakpoint | DataBreakpoint): { message?: string, className: string } { const state = debugService.state; const debugActive = state === State.Running || state === State.Stopped; @@ -584,7 +645,7 @@ export function getBreakpointMessageAndClassName(debugService: IDebugService, br } const appendMessage = (text: string): string => { - return !(breakpoint instanceof FunctionBreakpoint) && breakpoint.message ? text.concat(', ' + breakpoint.message) : text; + return !(breakpoint instanceof FunctionBreakpoint) && !(breakpoint instanceof DataBreakpoint) && breakpoint.message ? text.concat(', ' + breakpoint.message) : text; }; if (debugActive && !breakpoint.verified) { return { @@ -607,6 +668,19 @@ export function getBreakpointMessageAndClassName(debugService: IDebugService, br }; } + if (breakpoint instanceof DataBreakpoint) { + if (session && !session.capabilities.supportsDataBreakpoints) { + return { + className: 'debug-data-breakpoint-unverified', + message: nls.localize('dataBreakpointUnsupported', "Data breakpoints not supported by this debug type"), + }; + } + + return { + className: 'debug-data-breakpoint', + }; + } + if (breakpoint.logMessage || breakpoint.condition || breakpoint.hitCondition) { const messages: string[] = []; if (breakpoint.logMessage) { diff --git a/src/vs/workbench/contrib/debug/browser/debugActions.ts b/src/vs/workbench/contrib/debug/browser/debugActions.ts index da8c678afc..308b45f8c5 100644 --- a/src/vs/workbench/contrib/debug/browser/debugActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugActions.ts @@ -9,7 +9,7 @@ import * as lifecycle from 'vs/base/common/lifecycle'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IDebugService, State, IEnablement, IBreakpoint, IDebugSession } from 'vs/workbench/contrib/debug/common/debug'; -import { Variable, Breakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; +import { Variable, Breakpoint, FunctionBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -191,7 +191,7 @@ export class RemoveBreakpointAction extends AbstractDebugAction { public run(breakpoint: IBreakpoint): Promise { return breakpoint instanceof Breakpoint ? this.debugService.removeBreakpoints(breakpoint.getId()) - : this.debugService.removeFunctionBreakpoints(breakpoint.getId()); + : breakpoint instanceof FunctionBreakpoint ? this.debugService.removeFunctionBreakpoints(breakpoint.getId()) : this.debugService.removeDataBreakpoints(breakpoint.getId()); } } @@ -205,7 +205,7 @@ export class RemoveAllBreakpointsAction extends AbstractDebugAction { } public run(): Promise { - return Promise.all([this.debugService.removeBreakpoints(), this.debugService.removeFunctionBreakpoints()]); + return Promise.all([this.debugService.removeBreakpoints(), this.debugService.removeFunctionBreakpoints(), this.debugService.removeDataBreakpoints()]); } protected isEnabled(state: State): boolean { diff --git a/src/vs/workbench/contrib/debug/browser/debugCommands.ts b/src/vs/workbench/contrib/debug/browser/debugCommands.ts index f317cf7f45..06fb7f05ed 100644 --- a/src/vs/workbench/contrib/debug/browser/debugCommands.ts +++ b/src/vs/workbench/contrib/debug/browser/debugCommands.ts @@ -10,7 +10,7 @@ import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/co import { IListService } from 'vs/platform/list/browser/listService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution, CONTEXT_IN_DEBUG_MODE, CONTEXT_EXPRESSION_SELECTED, CONTEXT_BREAKPOINT_SELECTED, IConfig, IStackFrame, IThread, IDebugSession, CONTEXT_DEBUG_STATE, REPL_ID, IDebugConfiguration, CONTEXT_JUMP_TO_CURSOR_SUPPORTED } from 'vs/workbench/contrib/debug/common/debug'; -import { Expression, Variable, Breakpoint, FunctionBreakpoint, Thread } from 'vs/workbench/contrib/debug/common/debugModel'; +import { Expression, Variable, Breakpoint, FunctionBreakpoint, Thread, DataBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; import { IExtensionsViewlet, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -410,6 +410,8 @@ export function registerCommands(): void { debugService.removeBreakpoints(element.getId()); } else if (element instanceof FunctionBreakpoint) { debugService.removeFunctionBreakpoints(element.getId()); + } else if (element instanceof DataBreakpoint) { + debugService.removeDataBreakpoints(element.getId()); } } } diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 065814633c..0199dd9ee8 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -18,7 +18,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/files/common/files'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { DebugModel, ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, Expression } from 'vs/workbench/contrib/debug/common/debugModel'; +import { DebugModel, ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, Expression, DataBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; import { ViewModel } from 'vs/workbench/contrib/debug/common/debugViewModel'; import * as debugactions from 'vs/workbench/contrib/debug/browser/debugActions'; import { ConfigurationManager } from 'vs/workbench/contrib/debug/browser/debugConfigurationManager'; @@ -52,6 +52,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint'; const DEBUG_BREAKPOINTS_ACTIVATED_KEY = 'debug.breakpointactivated'; const DEBUG_FUNCTION_BREAKPOINTS_KEY = 'debug.functionbreakpoint'; +const DEBUG_DATA_BREAKPOINTS_KEY = 'debug.databreakpoint'; const DEBUG_EXCEPTION_BREAKPOINTS_KEY = 'debug.exceptionbreakpoint'; const DEBUG_WATCH_EXPRESSIONS_KEY = 'debug.watchexpressions'; @@ -129,7 +130,7 @@ export class DebugService implements IDebugService { this.inDebugMode = CONTEXT_IN_DEBUG_MODE.bindTo(contextKeyService); this.model = new DebugModel(this.loadBreakpoints(), this.storageService.getBoolean(DEBUG_BREAKPOINTS_ACTIVATED_KEY, StorageScope.WORKSPACE, true), this.loadFunctionBreakpoints(), - this.loadExceptionBreakpoints(), this.loadWatchExpressions(), this.textFileService); + this.loadExceptionBreakpoints(), this.loadDataBreakpoints(), this.loadWatchExpressions(), this.textFileService); this.toDispose.push(this.model); this.viewModel = new ViewModel(contextKeyService); @@ -851,6 +852,8 @@ export class DebugService implements IDebugService { await this.sendBreakpoints(breakpoint.uri); } else if (breakpoint instanceof FunctionBreakpoint) { await this.sendFunctionBreakpoints(); + } else if (breakpoint instanceof DataBreakpoint) { + await this.sendDataBreakpoints(); } else { await this.sendExceptionBreakpoints(); } @@ -920,6 +923,19 @@ export class DebugService implements IDebugService { this.storeBreakpoints(); } + async addDataBreakpoint(label: string, dataId: string, canPersist: boolean): Promise { + this.model.addDataBreakpoint(label, dataId, canPersist); + await this.sendDataBreakpoints(); + + this.storeBreakpoints(); + } + + async removeDataBreakpoints(id?: string): Promise { + this.model.removeDataBreakpoints(id); + await this.sendDataBreakpoints(); + this.storeBreakpoints(); + } + sendAllBreakpoints(session?: IDebugSession): Promise { return Promise.all(distinct(this.model.getBreakpoints(), bp => bp.uri.toString()).map(bp => this.sendBreakpoints(bp.uri, false, session))) .then(() => this.sendFunctionBreakpoints(session)) @@ -943,6 +959,14 @@ export class DebugService implements IDebugService { }); } + private sendDataBreakpoints(session?: IDebugSession): Promise { + const breakpointsToSend = this.model.getDataBreakpoints().filter(fbp => fbp.enabled && this.model.areBreakpointsActivated()); + + return this.sendToOneOrAllSessions(session, s => { + return s.capabilities.supportsDataBreakpoints ? s.sendDataBreakpoints(breakpointsToSend) : Promise.resolve(undefined); + }); + } + private sendExceptionBreakpoints(session?: IDebugSession): Promise { const enabledExceptionBps = this.model.getExceptionBreakpoints().filter(exb => exb.enabled); @@ -1006,6 +1030,17 @@ export class DebugService implements IDebugService { return result || []; } + private loadDataBreakpoints(): DataBreakpoint[] { + let result: DataBreakpoint[] | undefined; + try { + result = JSON.parse(this.storageService.get(DEBUG_DATA_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((dbp: any) => { + return new DataBreakpoint(dbp.label, dbp.dataId, true, dbp.enabled, dbp.hitCondition, dbp.condition, dbp.logMessage); + }); + } catch (e) { } + + return result || []; + } + private loadWatchExpressions(): Expression[] { let result: Expression[] | undefined; try { @@ -1041,6 +1076,13 @@ export class DebugService implements IDebugService { this.storageService.remove(DEBUG_FUNCTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE); } + const dataBreakpoints = this.model.getDataBreakpoints().filter(dbp => dbp.canPersist); + if (dataBreakpoints.length) { + this.storageService.store(DEBUG_DATA_BREAKPOINTS_KEY, JSON.stringify(dataBreakpoints), StorageScope.WORKSPACE); + } else { + this.storageService.remove(DEBUG_DATA_BREAKPOINTS_KEY, StorageScope.WORKSPACE); + } + const exceptionBreakpoints = this.model.getExceptionBreakpoints(); if (exceptionBreakpoints.length) { this.storageService.store(DEBUG_EXCEPTION_BREAKPOINTS_KEY, JSON.stringify(exceptionBreakpoints), StorageScope.WORKSPACE); diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index b0572dd077..b33cc2cdb4 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -12,7 +12,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { CompletionItem, completionKindFromString } from 'vs/editor/common/modes'; import { Position } from 'vs/editor/common/core/position'; import * as aria from 'vs/base/browser/ui/aria/aria'; -import { IDebugSession, IConfig, IThread, IRawModelUpdate, IDebugService, IRawStoppedDetails, State, LoadedSourceEvent, IFunctionBreakpoint, IExceptionBreakpoint, IBreakpoint, IExceptionInfo, AdapterEndEvent, IDebugger, VIEWLET_ID, IDebugConfiguration, IReplElement, IStackFrame, IExpression, IReplElementSource } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugSession, IConfig, IThread, IRawModelUpdate, IDebugService, IRawStoppedDetails, State, LoadedSourceEvent, IFunctionBreakpoint, IExceptionBreakpoint, IBreakpoint, IExceptionInfo, AdapterEndEvent, IDebugger, VIEWLET_ID, IDebugConfiguration, IReplElement, IStackFrame, IExpression, IReplElementSource, IDataBreakpoint } from 'vs/workbench/contrib/debug/common/debug'; import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; import { mixin } from 'vs/base/common/objects'; import { Thread, ExpressionContainer, DebugModel } from 'vs/workbench/contrib/debug/common/debugModel'; @@ -327,6 +327,34 @@ export class DebugSession implements IDebugSession { return Promise.reject(new Error('no debug adapter')); } + dataBreakpointInfo(name: string, variablesReference?: number): Promise<{ dataId: string | null, description: string, canPersist?: boolean }> { + if (this.raw) { + if (this.raw.readyForBreakpoints) { + return this.raw.dataBreakpointInfo({ name, variablesReference }).then(response => response.body); + } + return Promise.reject(new Error(nls.localize('sessionNotReadyForBreakpoints', "Session is not ready for breakpoints"))); + } + return Promise.reject(new Error('no debug adapter')); + } + + sendDataBreakpoints(dataBreakpoints: IDataBreakpoint[]): Promise { + if (this.raw) { + if (this.raw.readyForBreakpoints) { + return this.raw.setDataBreakpoints({ breakpoints: dataBreakpoints }).then(response => { + if (response && response.body) { + const data = new Map(); + for (let i = 0; i < dataBreakpoints.length; i++) { + data.set(dataBreakpoints[i].getId(), response.body.breakpoints[i]); + } + this.model.setBreakpointSessionData(this.getId(), data); + } + }); + } + return Promise.resolve(undefined); + } + return Promise.reject(new Error('no debug adapter')); + } + customRequest(request: string, args: any): Promise { if (this.raw) { return this.raw.custom(request, args); diff --git a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts index 3e6771d145..e115330026 100644 --- a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts @@ -357,6 +357,20 @@ export class RawDebugSession { return Promise.reject(new Error('setFunctionBreakpoints not supported')); } + dataBreakpointInfo(args: DebugProtocol.DataBreakpointInfoArguments): Promise { + if (this.capabilities.supportsDataBreakpoints) { + return this.send('dataBreakpointInfo', args); + } + return Promise.reject(new Error('dataBreakpointInfo not supported')); + } + + setDataBreakpoints(args: DebugProtocol.SetDataBreakpointsArguments): Promise { + if (this.capabilities.supportsDataBreakpoints) { + return this.send('setDataBreakpoints', args); + } + return Promise.reject(new Error('setDataBreakpoints not supported')); + } + setExceptionBreakpoints(args: DebugProtocol.SetExceptionBreakpointsArguments): Promise { return this.send('setExceptionBreakpoints', args); } diff --git a/src/vs/workbench/contrib/debug/browser/variablesView.ts b/src/vs/workbench/contrib/debug/browser/variablesView.ts index 95942512b9..1f3761eec3 100644 --- a/src/vs/workbench/contrib/debug/browser/variablesView.ts +++ b/src/vs/workbench/contrib/debug/browser/variablesView.ts @@ -123,7 +123,7 @@ export class VariablesView extends ViewletPanel { this.tree.updateChildren(); })); this._register(this.tree.onMouseDblClick(e => this.onMouseDblClick(e))); - this._register(this.tree.onContextMenu(e => this.onContextMenu(e))); + this._register(this.tree.onContextMenu(async e => await this.onContextMenu(e))); this._register(this.onDidChangeBodyVisibility(visible => { if (visible && this.needsRefresh) { @@ -152,7 +152,7 @@ export class VariablesView extends ViewletPanel { } } - private onContextMenu(e: ITreeContextMenuEvent): void { + private async onContextMenu(e: ITreeContextMenuEvent): Promise { const variable = e.element; if (variable instanceof Variable && !!variable.value) { const actions: IAction[] = []; @@ -174,6 +174,16 @@ export class VariablesView extends ViewletPanel { return Promise.resolve(undefined); })); } + if (session && session.capabilities.supportsDataBreakpoints) { + const response = await session.dataBreakpointInfo(variable.name, variable.parent.reference); + const dataid = response.dataId; + if (dataid) { + actions.push(new Separator()); + actions.push(new Action('debug.addDataBreakpoint', nls.localize('setDataBreakpoint', "Set Data Breakpoint"), undefined, true, () => { + return this.debugService.addDataBreakpoint(response.description, dataid, !!response.canPersist); + })); + } + } this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 60d6bbfae5..b11ec4ccd1 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -103,6 +103,7 @@ export interface IReplElementSource { export interface IExpressionContainer extends ITreeElement { readonly hasChildren: boolean; getChildren(): Promise; + readonly reference?: number; } export interface IExpression extends IReplElement, IExpressionContainer { @@ -201,6 +202,8 @@ export interface IDebugSession extends ITreeElement { sendBreakpoints(modelUri: uri, bpts: IBreakpoint[], sourceModified: boolean): Promise; sendFunctionBreakpoints(fbps: IFunctionBreakpoint[]): Promise; + dataBreakpointInfo(name: string, variablesReference?: number): Promise<{ dataId: string | null, description: string, canPersist?: boolean }>; + sendDataBreakpoints(dbps: IDataBreakpoint[]): Promise; sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): Promise; stackTrace(threadId: number, startFrame: number, levels: number): Promise; @@ -357,6 +360,12 @@ export interface IExceptionBreakpoint extends IEnablement { readonly label: string; } +export interface IDataBreakpoint extends IBaseBreakpoint { + readonly label: string; + readonly dataId: string; + readonly canPersist: boolean; +} + export interface IExceptionInfo { readonly id?: string; readonly description?: string; @@ -404,6 +413,7 @@ export interface IDebugModel extends ITreeElement { getBreakpoints(filter?: { uri?: uri, lineNumber?: number, column?: number, enabledOnly?: boolean }): ReadonlyArray; areBreakpointsActivated(): boolean; getFunctionBreakpoints(): ReadonlyArray; + getDataBreakpoints(): ReadonlyArray; getExceptionBreakpoints(): ReadonlyArray; getWatchExpressions(): ReadonlyArray; @@ -416,9 +426,9 @@ export interface IDebugModel extends ITreeElement { * An event describing a change to the set of [breakpoints](#debug.Breakpoint). */ export interface IBreakpointsChangeEvent { - added?: Array; - removed?: Array; - changed?: Array; + added?: Array; + removed?: Array; + changed?: Array; sessionOnly?: boolean; } @@ -754,6 +764,17 @@ export interface IDebugService { */ removeFunctionBreakpoints(id?: string): Promise; + /** + * Adds a new data breakpoint. + */ + addDataBreakpoint(label: string, dataId: string, canPersist: boolean): Promise; + + /** + * Removes all data breakpoints. If id is passed only removes the data breakpoint with the passed id. + * Notifies debug adapter of breakpoint changes. + */ + removeDataBreakpoints(id?: string): Promise; + /** * Sends all breakpoints to the passed session. * If session is not passed, sends all breakpoints to each session. diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index d2390c4bcb..f778ae187c 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -16,7 +16,7 @@ import { distinct, lastIndex } from 'vs/base/common/arrays'; import { Range, IRange } from 'vs/editor/common/core/range'; import { ITreeElement, IExpression, IExpressionContainer, IDebugSession, IStackFrame, IExceptionBreakpoint, IBreakpoint, IFunctionBreakpoint, IDebugModel, IReplElementSource, - IThread, IRawModelUpdate, IScope, IRawStoppedDetails, IEnablement, IBreakpointData, IExceptionInfo, IReplElement, IBreakpointsChangeEvent, IBreakpointUpdateData, IBaseBreakpoint, State + IThread, IRawModelUpdate, IScope, IRawStoppedDetails, IEnablement, IBreakpointData, IExceptionInfo, IReplElement, IBreakpointsChangeEvent, IBreakpointUpdateData, IBaseBreakpoint, State, IDataBreakpoint } from 'vs/workbench/contrib/debug/common/debug'; import { Source, UNKNOWN_SOURCE_LABEL } from 'vs/workbench/contrib/debug/common/debugSource'; import { commonSuffixLength } from 'vs/base/common/strings'; @@ -735,6 +735,34 @@ export class FunctionBreakpoint extends BaseBreakpoint implements IFunctionBreak } } +export class DataBreakpoint extends BaseBreakpoint implements IDataBreakpoint { + + constructor( + public label: string, + public dataId: string, + public canPersist: boolean, + enabled: boolean, + hitCondition: string | undefined, + condition: string | undefined, + logMessage: string | undefined, + id = generateUuid() + ) { + super(enabled, hitCondition, condition, logMessage, id); + } + + toJSON(): any { + const result = super.toJSON(); + result.label = this.label; + result.dataid = this.dataId; + + return result; + } + + toString(): string { + return this.label; + } +} + export class ExceptionBreakpoint extends Enablement implements IExceptionBreakpoint { constructor(public filter: string, public label: string, enabled: boolean) { @@ -778,6 +806,7 @@ export class DebugModel implements IDebugModel { private breakpointsActivated: boolean, private functionBreakpoints: FunctionBreakpoint[], private exceptionBreakpoints: ExceptionBreakpoint[], + private dataBreakopints: DataBreakpoint[], private watchExpressions: Expression[], private textFileService: ITextFileService ) { @@ -918,6 +947,10 @@ export class DebugModel implements IDebugModel { return this.functionBreakpoints; } + getDataBreakpoints(): IDataBreakpoint[] { + return this.dataBreakopints; + } + getExceptionBreakpoints(): IExceptionBreakpoint[] { return this.exceptionBreakpoints; } @@ -991,6 +1024,12 @@ export class DebugModel implements IDebugModel { fbp.setSessionData(sessionId, fbpData); } }); + this.dataBreakopints.forEach(dbp => { + const dbpData = data.get(dbp.getId()); + if (dbpData) { + dbp.setSessionData(sessionId, dbpData); + } + }); this._onDidChangeBreakpoints.fire({ sessionOnly: true @@ -1001,6 +1040,7 @@ export class DebugModel implements IDebugModel { this.breakpointsSessionId = sessionId; this.breakpoints.forEach(bp => bp.setSessionId(sessionId)); this.functionBreakpoints.forEach(fbp => fbp.setSessionId(sessionId)); + this.dataBreakopints.forEach(dbp => dbp.setSessionId(sessionId)); this._onDidChangeBreakpoints.fire({ sessionOnly: true @@ -1038,7 +1078,7 @@ export class DebugModel implements IDebugModel { } enableOrDisableAllBreakpoints(enable: boolean): void { - const changed: Array = []; + const changed: Array = []; this.breakpoints.forEach(bp => { if (bp.enabled !== enable) { @@ -1052,6 +1092,12 @@ export class DebugModel implements IDebugModel { } fbp.enabled = enable; }); + this.dataBreakopints.forEach(dbp => { + if (dbp.enabled !== enable) { + changed.push(dbp); + } + dbp.enabled = enable; + }); this._onDidChangeBreakpoints.fire({ changed: changed }); } @@ -1073,7 +1119,6 @@ export class DebugModel implements IDebugModel { } removeFunctionBreakpoints(id?: string): void { - let removed: FunctionBreakpoint[]; if (id) { removed = this.functionBreakpoints.filter(fbp => fbp.getId() === id); @@ -1082,7 +1127,25 @@ export class DebugModel implements IDebugModel { removed = this.functionBreakpoints; this.functionBreakpoints = []; } - this._onDidChangeBreakpoints.fire({ removed: removed }); + this._onDidChangeBreakpoints.fire({ removed }); + } + + addDataBreakpoint(label: string, dataId: string, canPersist: boolean): void { + const newDataBreakpoint = new DataBreakpoint(label, dataId, canPersist, true, undefined, undefined, undefined); + this.dataBreakopints.push(newDataBreakpoint); + this._onDidChangeBreakpoints.fire({ added: [newDataBreakpoint] }); + } + + removeDataBreakpoints(id?: string): void { + let removed: DataBreakpoint[]; + if (id) { + removed = this.dataBreakopints.filter(fbp => fbp.getId() === id); + this.dataBreakopints = this.dataBreakopints.filter(fbp => fbp.getId() !== id); + } else { + removed = this.dataBreakopints; + this.dataBreakopints = []; + } + this._onDidChangeBreakpoints.fire({ removed }); } getWatchExpressions(): Expression[] { diff --git a/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts b/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts index 746770664f..e2385a1503 100644 --- a/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts @@ -23,7 +23,7 @@ suite('Debug - Model', () => { let rawSession: MockRawSession; setup(() => { - model = new DebugModel([], true, [], [], [], { isDirty: (e: any) => false }); + model = new DebugModel([], true, [], [], [], [], { isDirty: (e: any) => false }); rawSession = new MockRawSession(); }); diff --git a/src/vs/workbench/contrib/debug/test/common/mockDebug.ts b/src/vs/workbench/contrib/debug/test/common/mockDebug.ts index a36e1253ba..d592f33eec 100644 --- a/src/vs/workbench/contrib/debug/test/common/mockDebug.ts +++ b/src/vs/workbench/contrib/debug/test/common/mockDebug.ts @@ -7,7 +7,7 @@ import { URI as uri } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { Position } from 'vs/editor/common/core/position'; -import { ILaunch, IDebugService, State, IDebugSession, IConfigurationManager, IStackFrame, IBreakpointData, IBreakpointUpdateData, IConfig, IDebugModel, IViewModel, IBreakpoint, LoadedSourceEvent, IThread, IRawModelUpdate, IFunctionBreakpoint, IExceptionBreakpoint, IDebugger, IExceptionInfo, AdapterEndEvent, IReplElement, IExpression, IReplElementSource } from 'vs/workbench/contrib/debug/common/debug'; +import { ILaunch, IDebugService, State, IDebugSession, IConfigurationManager, IStackFrame, IBreakpointData, IBreakpointUpdateData, IConfig, IDebugModel, IViewModel, IBreakpoint, LoadedSourceEvent, IThread, IRawModelUpdate, IFunctionBreakpoint, IExceptionBreakpoint, IDebugger, IExceptionInfo, AdapterEndEvent, IReplElement, IExpression, IReplElementSource, IDataBreakpoint } from 'vs/workbench/contrib/debug/common/debug'; import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; import { CompletionItem } from 'vs/editor/common/modes'; import Severity from 'vs/base/common/severity'; @@ -79,6 +79,13 @@ export class MockDebugService implements IDebugService { throw new Error('not implemented'); } + addDataBreakpoint(label: string, dataId: string, canPersist: boolean): Promise { + throw new Error('Method not implemented.'); + } + removeDataBreakpoints(id?: string | undefined): Promise { + throw new Error('Method not implemented.'); + } + public addReplExpression(name: string): Promise { throw new Error('not implemented'); } @@ -125,6 +132,13 @@ export class MockDebugService implements IDebugService { } export class MockSession implements IDebugSession { + dataBreakpointInfo(name: string, variablesReference?: number | undefined): Promise<{ dataId: string | null; description: string; canPersist?: boolean | undefined; }> { + throw new Error('Method not implemented.'); + } + + sendDataBreakpoints(dbps: IDataBreakpoint[]): Promise { + throw new Error('Method not implemented.'); + } subId: string | undefined; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts index beb604b3ae..1b75151813 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts @@ -47,7 +47,7 @@ import { timeout } from 'vs/base/common/async'; import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry'; // {{SQL CARBON EDIT}} import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys'; // {{SQL CARBON EDIT}} import { IWorkspaceStatsService } from 'vs/workbench/contrib/stats/common/workspaceStats'; -import { Platform, setImmediate } from 'vs/base/common/platform'; +import { setImmediate, isWeb } from 'vs/base/common/platform'; import { platform, env as processEnv } from 'vs/base/common/process'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -996,7 +996,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe * If user has any of the tools listed in this.productService.exeBasedExtensionTips, fetch corresponding recommendations */ private async fetchExecutableRecommendations(important: boolean): Promise { - if (Platform.Web) { + if (isWeb) { return; } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 2650e05cfc..3a553d299a 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -144,7 +144,7 @@ class Extension implements IExtension { private get localIconUrl(): string | null { if (this.local && this.local.manifest.icon) { - return asDomUri(resources.joinPath(this.local.location, this.local.manifest.icon)).toString(); + return asDomUri(resources.joinPath(this.local.location, this.local.manifest.icon)).toString(true); } return null; } diff --git a/src/vs/workbench/contrib/files/browser/saveErrorHandler.ts b/src/vs/workbench/contrib/files/browser/saveErrorHandler.ts index af19d0a492..2fd0c89c52 100644 --- a/src/vs/workbench/contrib/files/browser/saveErrorHandler.ts +++ b/src/vs/workbench/contrib/files/browser/saveErrorHandler.ts @@ -236,7 +236,6 @@ class ResolveSaveConflictAction extends Action { @IEditorService private readonly editorService: IEditorService, @INotificationService private readonly notificationService: INotificationService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @IStorageService private readonly storageService: IStorageService, @IEnvironmentService private readonly environmentService: IEnvironmentService ) { super('workbench.files.action.resolveConflict', nls.localize('compareChanges', "Compare")); @@ -250,21 +249,15 @@ class ResolveSaveConflictAction extends Action { await TextFileContentProvider.open(resource, CONFLICT_RESOLUTION_SCHEME, editorLabel, this.editorService, { pinned: true }); - if (this.storageService.getBoolean(LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, StorageScope.GLOBAL)) { - return; // return if this message is ignored - } - // Show additional help how to resolve the save conflict - const primaryActions: IAction[] = [ - this.instantiationService.createInstance(ResolveConflictLearnMoreAction) - ]; - const secondaryActions: IAction[] = [ - this.instantiationService.createInstance(DoNotShowResolveConflictLearnMoreAction) - ]; - - const actions: INotificationActions = { primary: primaryActions, secondary: secondaryActions }; - const handle = this.notificationService.notify({ severity: Severity.Info, message: conflictEditorHelp, actions }); - Event.once(handle.onDidClose)(() => { dispose(primaryActions); dispose(secondaryActions); }); + const actions: INotificationActions = { primary: [this.instantiationService.createInstance(ResolveConflictLearnMoreAction)] }; + const handle = this.notificationService.notify({ + severity: Severity.Info, + message: conflictEditorHelp, + actions, + neverShowAgain: { id: LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, isSecondary: true } + }); + Event.once(handle.onDidClose)(() => dispose(actions.primary!)); pendingResolveSaveConflictMessages.push(handle); } diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 35a5c0a1e2..5a2b4a6fd3 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -218,56 +218,46 @@ export class FilesRenderer implements ITreeRenderer 0 && !stat.isDirectory ? lastDot : value.length }); - let isFinishableDisposeEvent = false; - setTimeout(() => { - // Check if disposed - if (!inputBox.inputElement) { - return; - } - inputBox.focus(); - inputBox.select({ start: 0, end: lastDot > 0 && !stat.isDirectory ? lastDot : value.length }); - isFinishableDisposeEvent = true; - }, 0); - - const done = once(async (success: boolean) => { + const done = once(async (success: boolean, blur: boolean) => { label.element.style.display = 'none'; const value = inputBox.value; dispose(toDispose); - label.element.remove(); - // Timeout: once done rendering only then re-render #70902 - setTimeout(() => editableData.onFinish(value, success), 0); + container.removeChild(label.element); + editableData.onFinish(value, success); }); - const blurDisposable = DOM.addDisposableListener(inputBox.inputElement, DOM.EventType.BLUR, () => { - done(inputBox.isInputValid()); - }); + // It can happen that the tree re-renders this node. When that happens, + // we're gonna get a blur event first and only after an element disposable. + // Because of that, we should setTimeout the blur handler to differentiate + // between the blur happening because of a unrender or because of a user action. + let ignoreBlur = false; const toDispose = [ inputBox, DOM.addStandardDisposableListener(inputBox.inputElement, DOM.EventType.KEY_DOWN, (e: IKeyboardEvent) => { if (e.equals(KeyCode.Enter)) { if (inputBox.validate()) { - done(true); + done(true, false); } } else if (e.equals(KeyCode.Escape)) { - done(false); + done(false, false); } }), - blurDisposable, + DOM.addDisposableListener(inputBox.inputElement, DOM.EventType.BLUR, () => { + setTimeout(() => { + if (!ignoreBlur) { + done(inputBox.isInputValid(), true); + } + }, 0); + }), label, styler ]; - return toDisposable(() => { - if (isFinishableDisposeEvent) { - done(false); - } - else { - dispose(toDispose); - label.element.remove(); - } - }); + return toDisposable(() => ignoreBlur = true); } disposeElement?(element: ITreeNode, index: number, templateData: IFileTemplateData): void { diff --git a/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts b/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts index a434182972..ad03b1a00b 100644 --- a/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts +++ b/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts @@ -68,8 +68,7 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo } private onDidInstallExtension(e: DidInstallExtensionEvent): void { - const donotAskUpdateKey = 'langugage.update.donotask'; - if (!this.storageService.getBoolean(donotAskUpdateKey, StorageScope.GLOBAL) && e.local && e.operation === InstallOperation.Install && e.local.manifest.contributes && e.local.manifest.contributes.localizations && e.local.manifest.contributes.localizations.length) { + if (e.local && e.operation === InstallOperation.Install && e.local.manifest.contributes && e.local.manifest.contributes.localizations && e.local.manifest.contributes.localizations.length) { const locale = e.local.manifest.contributes.localizations[0].languageId; if (platform.language !== locale) { const updateAndRestart = platform.locale !== locale; @@ -83,12 +82,11 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo const updatePromise = updateAndRestart ? this.jsonEditingService.write(this.environmentService.localeResource, { key: 'locale', value: locale }, true) : Promise.resolve(undefined); updatePromise.then(() => this.windowsService.relaunch({}), e => this.notificationService.error(e)); } - }, { - label: localize('neverAgain', "Don't Show Again"), - isSecondary: true, - run: () => this.storageService.store(donotAskUpdateKey, true, StorageScope.GLOBAL) }], - { sticky: true } + { + sticky: true, + neverShowAgain: { id: 'langugage.update.donotask', isSecondary: true } + } ); } } @@ -302,4 +300,4 @@ ExtensionsRegistry.registerExtensionPoint({ } } } -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts b/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts index 573888c13e..a05d14266c 100644 --- a/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts +++ b/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts @@ -168,13 +168,14 @@ class InsertSnippetAction extends EditorAction { return quickInputService.pick(picks, { matchOnDetail: true }).then(pick => resolve(pick && pick.snippet), reject); } }).then(async snippet => { + if (!snippet) { + return; + } let clipboardText: string | undefined; if (snippet.needsClipboard) { clipboardText = await clipboardService.readText(); } - if (snippet) { - SnippetController2.get(editor).insert(snippet.codeSnippet, { clipboardText }); - } + SnippetController2.get(editor).insert(snippet.codeSnippet, { clipboardText }); }); } } diff --git a/src/vs/workbench/contrib/stats/electron-browser/workspaceStatsService.ts b/src/vs/workbench/contrib/stats/electron-browser/workspaceStatsService.ts index 249fb6d95d..e0266b250e 100644 --- a/src/vs/workbench/contrib/stats/electron-browser/workspaceStatsService.ts +++ b/src/vs/workbench/contrib/stats/electron-browser/workspaceStatsService.ts @@ -8,9 +8,8 @@ import { IFileService, IResolveFileResult, IFileStat } from 'vs/platform/files/c import { IWorkspaceContextService, WorkbenchState, IWorkspace } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IWindowService, IWindowConfiguration } from 'vs/platform/windows/common/windows'; -import { INotificationService, IPromptChoice } from 'vs/platform/notification/common/notification'; +import { INotificationService, NeverShowAgainScope, INeverShowAgainOptions } from 'vs/platform/notification/common/notification'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITextFileService, ITextFileContent } from 'vs/workbench/services/textfile/common/textfiles'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; @@ -22,8 +21,6 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkspaceStatsService, Tags } from 'vs/workbench/contrib/stats/common/workspaceStats'; import { getHashedRemotesFromConfig } from 'vs/workbench/contrib/stats/electron-browser/workspaceStats'; -const DISABLE_WORKSPACE_PROMPT_KEY = 'workspaces.dontPromptToOpen'; - const ModulesToLookFor = [ // Packages that suggest a node server 'express', @@ -103,7 +100,6 @@ export class WorkspaceStatsService implements IWorkspaceStatsService { @IWindowService private readonly windowService: IWindowService, @INotificationService private readonly notificationService: INotificationService, @IQuickInputService private readonly quickInputService: IQuickInputService, - @IStorageService private readonly storageService: IStorageService, @ITextFileService private readonly textFileService: ITextFileService ) { } @@ -449,15 +445,7 @@ export class WorkspaceStatsService implements IWorkspaceStatsService { } private doHandleWorkspaceFiles(folder: URI, workspaces: string[]): void { - if (this.storageService.getBoolean(DISABLE_WORKSPACE_PROMPT_KEY, StorageScope.WORKSPACE)) { - return; // prompt disabled by user - } - - const doNotShowAgain: IPromptChoice = { - label: localize('never again', "Don't Show Again"), - isSecondary: true, - run: () => this.storageService.store(DISABLE_WORKSPACE_PROMPT_KEY, true, StorageScope.WORKSPACE) - }; + const neverShowAgain: INeverShowAgainOptions = { id: 'workspaces.dontPromptToOpen', scope: NeverShowAgainScope.WORKSPACE, isSecondary: true }; // Prompt to open one workspace if (workspaces.length === 1) { @@ -466,7 +454,7 @@ export class WorkspaceStatsService implements IWorkspaceStatsService { this.notificationService.prompt(Severity.Info, localize('workspaceFound', "This folder contains a workspace file '{0}'. Do you want to open it? [Learn more]({1}) about workspace files.", workspaceFile, 'https://go.microsoft.com/fwlink/?linkid=2025315'), [{ label: localize('openWorkspace', "Open Workspace"), run: () => this.windowService.openWindow([{ workspaceUri: joinPath(folder, workspaceFile) }]) - }, doNotShowAgain]); + }], { neverShowAgain }); } // Prompt to select a workspace from many @@ -482,7 +470,7 @@ export class WorkspaceStatsService implements IWorkspaceStatsService { } }); } - }, doNotShowAgain]); + }], { neverShowAgain }); } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts index e9797ea59c..cea1df3ca6 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts @@ -11,7 +11,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { ITerminalConfiguration, ITerminalFont, IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, TERMINAL_CONFIG_SECTION, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, MINIMUM_LETTER_SPACING, LinuxDistro, IShellLaunchConfig } from 'vs/workbench/contrib/terminal/common/terminal'; import Severity from 'vs/base/common/severity'; import { Terminal as XTermTerminal } from 'xterm'; -import { INotificationService } from 'vs/platform/notification/common/notification'; +import { INotificationService, NeverShowAgainScope } from 'vs/platform/notification/common/notification'; import { IBrowserTerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminal'; import { Emitter, Event } from 'vs/base/common/event'; import { basename } from 'vs/base/common/path'; @@ -254,7 +254,6 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { return r; } - private readonly NO_RECOMMENDATIONS_KEY = 'terminalConfigHelper/launchRecommendationsIgnore'; private recommendationsShown = false; public async showRecommendations(shellLaunchConfig: IShellLaunchConfig): Promise { @@ -264,10 +263,6 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { this.recommendationsShown = true; if (platform.isWindows && shellLaunchConfig.executable && basename(shellLaunchConfig.executable).toLowerCase() === 'wsl.exe') { - if (this._storageService.getBoolean(this.NO_RECOMMENDATIONS_KEY, StorageScope.WORKSPACE, false)) { - return; - } - if (! await this.isExtensionInstalled('ms-vscode-remote.remote-wsl')) { this._notificationService.prompt( Severity.Info, @@ -276,16 +271,10 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { "Check out the 'Visual Studio Code Remote - WSL' extension for a great development experience in WSL. Click [here]({0}) to learn more.", 'https://go.microsoft.com/fwlink/?linkid=2097212' ), - [ - { - label: nls.localize('doNotShowAgain', "Don't Show Again"), - run: () => { - this._storageService.store(this.NO_RECOMMENDATIONS_KEY, true, StorageScope.WORKSPACE); - } - } - ], + [], { - sticky: true + sticky: true, + neverShowAgain: { id: 'terminalConfigHelper/launchRecommendationsIgnore', scope: NeverShowAgainScope.WORKSPACE } } ); } diff --git a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts index d95984ab93..3ed0c6bc9a 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts @@ -144,18 +144,20 @@ export function getCwd( try { customCwd = configurationResolverService.resolve(lastActiveWorkspace, customCwd); } catch (e) { - // There was an issue resolving a variable, just use the unresolved customCwd which - // which will fail, and log the error in the console. + // There was an issue resolving a variable, log the error in the console and + // fallback to the default. if (logService) { logService.error('Could not resolve terminal.integrated.cwd', e); } - return customCwd; + customCwd = undefined; } } - if (path.isAbsolute(customCwd)) { - cwd = customCwd; - } else if (root) { - cwd = path.join(root.fsPath, customCwd); + if (customCwd) { + if (path.isAbsolute(customCwd)) { + cwd = customCwd; + } else if (root) { + cwd = path.join(root.fsPath, customCwd); + } } } @@ -208,33 +210,33 @@ export function getDefaultShell( if (!maybeExecutable) { maybeExecutable = getShellSetting(fetchSetting, isWorkspaceShellAllowed, 'shell', platformOverride); } - maybeExecutable = maybeExecutable || defaultShell; + let executable: string = maybeExecutable || defaultShell; // Change Sysnative to System32 if the OS is Windows but NOT WoW64. It's // safe to assume that this was used by accident as Sysnative does not // exist and will break the terminal in non-WoW64 environments. if ((platformOverride === platform.Platform.Windows) && !isWoW64 && windir) { const sysnativePath = path.join(windir, 'Sysnative').replace(/\//g, '\\').toLowerCase(); - if (maybeExecutable && maybeExecutable.toLowerCase().indexOf(sysnativePath) === 0) { - maybeExecutable = path.join(windir, 'System32', maybeExecutable.substr(sysnativePath.length + 1)); + if (executable && executable.toLowerCase().indexOf(sysnativePath) === 0) { + executable = path.join(windir, 'System32', executable.substr(sysnativePath.length + 1)); } } // Convert / to \ on Windows for convenience - if (maybeExecutable && platformOverride === platform.Platform.Windows) { - maybeExecutable = maybeExecutable.replace(/\//g, '\\'); + if (executable && platformOverride === platform.Platform.Windows) { + executable = executable.replace(/\//g, '\\'); } if (configurationResolverService) { try { - maybeExecutable = configurationResolverService.resolve(lastActiveWorkspace, maybeExecutable); + executable = configurationResolverService.resolve(lastActiveWorkspace, executable); } catch (e) { logService.error(`Could not resolve shell`, e); - maybeExecutable = maybeExecutable; + executable = executable; } } - return maybeExecutable; + return executable; } export function getDefaultShellArgs( diff --git a/src/vs/workbench/contrib/update/electron-browser/update.ts b/src/vs/workbench/contrib/update/electron-browser/update.ts index 6cfefbf834..0560712825 100644 --- a/src/vs/workbench/contrib/update/electron-browser/update.ts +++ b/src/vs/workbench/contrib/update/electron-browser/update.ts @@ -19,7 +19,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { IUpdateService, State as UpdateState, StateType, IUpdate } from 'vs/platform/update/common/update'; import * as semver from 'semver-umd'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { INotificationService, INotificationHandle, Severity } from 'vs/platform/notification/common/notification'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { ReleaseNotesManager } from './releaseNotesEditor'; @@ -165,32 +165,8 @@ export class ProductContribution implements IWorkbenchContribution { } } -class NeverShowAgain { - - private readonly key: string; - - readonly action = new Action(`neverShowAgain:${this.key}`, nls.localize('neveragain', "Don't Show Again"), undefined, true, (notification: INotificationHandle) => { - - // Hide notification - notification.close(); - - this.storageService.store(this.key, true, StorageScope.GLOBAL); - - return Promise.resolve(true); - }); - - constructor(key: string, @IStorageService private readonly storageService: IStorageService) { - this.key = `neverShowAgain:${key}`; - } - - shouldShow(): boolean { - return !this.storageService.getBoolean(this.key, StorageScope.GLOBAL, false); - } -} - export class Win3264BitContribution implements IWorkbenchContribution { - private static readonly KEY = 'update/win32-64bits'; private static readonly URL = 'https://code.visualstudio.com/updates/v1_15#_windows-64-bit'; private static readonly INSIDER_URL = 'https://github.com/Microsoft/vscode-docs/blob/vnext/release-notes/v1_15.md#windows-64-bit'; @@ -203,28 +179,18 @@ export class Win3264BitContribution implements IWorkbenchContribution { return; } - const neverShowAgain = new NeverShowAgain(Win3264BitContribution.KEY, storageService); - - if (!neverShowAgain.shouldShow()) { - return; - } - const url = product.quality === 'insider' ? Win3264BitContribution.INSIDER_URL : Win3264BitContribution.URL; - const handle = notificationService.prompt( + notificationService.prompt( severity.Info, nls.localize('64bitisavailable', "{0} for 64-bit Windows is now available! Click [here]({1}) to learn more.", product.nameShort, url), - [{ - label: nls.localize('neveragain', "Don't Show Again"), - isSecondary: true, - run: () => { - neverShowAgain.action.run(handle); - neverShowAgain.action.dispose(); - } - }], - { sticky: true } + [], + { + sticky: true, + neverShowAgain: { id: 'neverShowAgain:update/win32-64bits', isSecondary: true } + } ); } } @@ -401,23 +367,13 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu } // windows fast updates (target === system) - const neverShowAgain = new NeverShowAgain('update/win32-fast-updates', this.storageService); - - if (!neverShowAgain.shouldShow()) { - return; - } - - const handle = this.notificationService.prompt( + this.notificationService.prompt( severity.Info, nls.localize('updateInstalling', "{0} {1} is being installed in the background; we'll let you know when it's done.", product.nameLong, update.productVersion), - [{ - label: nls.localize('neveragain', "Don't Show Again"), - isSecondary: true, - run: () => { - neverShowAgain.action.run(handle); - neverShowAgain.action.dispose(); - } - }] + [], + { + neverShowAgain: { id: 'neverShowAgain:update/win32-fast-updates', isSecondary: true } + } ); } diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts b/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts index b460eeaf33..306aab5e10 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts @@ -39,11 +39,11 @@ class WebviewIconsManager { this._icons.forEach((value, key) => { const webviewSelector = `.show-file-icons .webview-${key}-name-file-icon::before`; if (URI.isUri(value)) { - cssRules.push(`${webviewSelector} { content: ""; background-image: url(${dom.asDomUri(value).toString()}); }`); + cssRules.push(`${webviewSelector} { content: ""; background-image: ${dom.asCSSUrl(value)}; }`); } else { - cssRules.push(`.vs ${webviewSelector} { content: ""; background-image: url(${dom.asDomUri(value.light).toString()}); }`); - cssRules.push(`.vs-dark ${webviewSelector} { content: ""; background-image: url(${dom.asDomUri(value.dark).toString()}); }`); + cssRules.push(`.vs ${webviewSelector} { content: ""; background-image: ${dom.asCSSUrl(value.light)}; }`); + cssRules.push(`.vs-dark ${webviewSelector} { content: ""; background-image: ${dom.asCSSUrl(value.dark)}; }`); } }); this._styleElement.innerHTML = cssRules.join('\n'); diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index 06b6fa0c9c..6471343fd1 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -22,10 +22,16 @@ import { WebWorkerExtensionHostStarter } from 'vs/workbench/services/extensions/ import { URI } from 'vs/base/common/uri'; import { isWebExtension } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { FetchFileSystemProvider } from 'vs/workbench/services/extensions/browser/webWorkerFileSystemProvider'; +import { Schemas } from 'vs/base/common/network'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { IStaticExtensionsService } from 'vs/workbench/services/extensions/common/staticExtensions'; export class ExtensionService extends AbstractExtensionService implements IExtensionService { - private _remoteExtensionsEnvironmentData: IRemoteAgentEnvironment | null; + private _disposables = new DisposableStore(); + private _remoteExtensionsEnvironmentData: IRemoteAgentEnvironment | null = null; constructor( @IInstantiationService instantiationService: IInstantiationService, @@ -37,6 +43,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten @IProductService productService: IProductService, @IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService, @IConfigurationService private readonly _configService: IConfigurationService, + @IStaticExtensionsService private readonly _staticExtensions: IStaticExtensionsService, ) { super( instantiationService, @@ -48,8 +55,19 @@ export class ExtensionService extends AbstractExtensionService implements IExten productService, ); - this._remoteExtensionsEnvironmentData = null; this._initialize(); + this._initFetchFileSystem(); + } + + dispose(): void { + this._disposables.dispose(); + super.dispose(); + } + + private _initFetchFileSystem(): void { + const provider = new FetchFileSystemProvider(); + this._disposables.add(this._fileService.registerProvider(Schemas.http, provider)); + this._disposables.add(this._fileService.registerProvider(Schemas.https, provider)); } private _createProvider(remoteAuthority: string): IInitDataProvider { @@ -84,23 +102,31 @@ export class ExtensionService extends AbstractExtensionService implements IExten protected async _scanAndHandleExtensions(): Promise { // fetch the remote environment - const remoteEnv = (await this._remoteAgentService.getEnvironment())!; + let [remoteEnv, localExtensions] = await Promise.all([ + >this._remoteAgentService.getEnvironment(), + this._staticExtensions.getExtensions() + ]); - // enable or disable proposed API per extension + // local: only enabled and web'ish extension + localExtensions = localExtensions.filter(ext => this._isEnabled(ext) && isWebExtension(ext, this._configService)); + this._checkEnableProposedApi(localExtensions); + + // remote: only enabled and none-web'ish extension + remoteEnv.extensions = remoteEnv.extensions.filter(extension => this._isEnabled(extension) && !isWebExtension(extension, this._configService)); this._checkEnableProposedApi(remoteEnv.extensions); - // remove disabled extensions - remoteEnv.extensions = remoteEnv.extensions.filter(extension => this._isEnabled(extension)); + // in case of overlap, the remote wins + const isRemoteExtension = new Set(); + remoteEnv.extensions.forEach(extension => isRemoteExtension.add(ExtensionIdentifier.toKey(extension.identifier))); + localExtensions = localExtensions.filter(extension => !isRemoteExtension.has(ExtensionIdentifier.toKey(extension.identifier))); // save for remote extension's init data this._remoteExtensionsEnvironmentData = remoteEnv; - // this._handleExtensionPoints(([]).concat(remoteEnv.extensions).concat(localExtensions)); - const result = this._registry.deltaExtensions(remoteEnv.extensions, []); + const result = this._registry.deltaExtensions(remoteEnv.extensions.concat(localExtensions), []); if (result.removedDueToLooping.length > 0) { this._logOrShowMessage(Severity.Error, nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', '))); } - this._doHandleExtensionPoints(this._registry.getAllExtensionDescriptions()); } diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts index 07220c8ad9..781741a056 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts @@ -65,8 +65,8 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { }; worker.onerror = (event) => { - console.error(event.error); - this._onDidExit.fire([81, event.error]); + console.error(event.message, event.error); + this._onDidExit.fire([81, event.message || event.error]); }; // keep for cleanup diff --git a/src/vs/workbench/services/extensions/browser/webWorkerFileSystemProvider.ts b/src/vs/workbench/services/extensions/browser/webWorkerFileSystemProvider.ts new file mode 100644 index 0000000000..80644cd17b --- /dev/null +++ b/src/vs/workbench/services/extensions/browser/webWorkerFileSystemProvider.ts @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IFileSystemProvider, FileSystemProviderCapabilities, IStat, FileType, FileDeleteOptions, FileOverwriteOptions, FileWriteOptions, FileSystemProviderError, FileSystemProviderErrorCode } from 'vs/platform/files/common/files'; + +import { Event } from 'vs/base/common/event'; +import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { NotImplementedError } from 'vs/base/common/errors'; + +export class FetchFileSystemProvider implements IFileSystemProvider { + + readonly capabilities = FileSystemProviderCapabilities.Readonly + FileSystemProviderCapabilities.FileReadWrite + FileSystemProviderCapabilities.PathCaseSensitive; + readonly onDidChangeCapabilities = Event.None; + readonly onDidChangeFile = Event.None; + + // working implementations + async readFile(resource: URI): Promise { + try { + const res = await fetch(resource.toString(true)); + if (res.status === 200) { + return new Uint8Array(await res.arrayBuffer()); + } + throw new FileSystemProviderError(res.statusText, FileSystemProviderErrorCode.Unknown); + } catch (err) { + throw new FileSystemProviderError(err, FileSystemProviderErrorCode.Unknown); + } + } + + // fake implementations + async stat(_resource: URI): Promise { + return { + type: FileType.File, + size: 0, + mtime: 0, + ctime: 0 + }; + } + + watch(): IDisposable { + return Disposable.None; + } + + // error implementations + writeFile(_resource: URI, _content: Uint8Array, _opts: FileWriteOptions): Promise { + throw new NotImplementedError(); + } + readdir(_resource: URI): Promise<[string, FileType][]> { + throw new NotImplementedError(); + } + mkdir(_resource: URI): Promise { + throw new NotImplementedError(); + } + delete(_resource: URI, _opts: FileDeleteOptions): Promise { + throw new NotImplementedError(); + } + rename(_from: URI, _to: URI, _opts: FileOverwriteOptions): Promise { + throw new NotImplementedError(); + } +} diff --git a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts index 8a546aa432..d4d1459b75 100644 --- a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts +++ b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts @@ -61,9 +61,7 @@ export interface IExtensionPointUser { collector: ExtensionMessageCollector; } -export interface IExtensionPointHandler { - (extensions: IExtensionPointUser[], delta: ExtensionPointUserDelta): void; -} +export type IExtensionPointHandler = (extensions: readonly IExtensionPointUser[], delta: ExtensionPointUserDelta) => void; export interface IExtensionPoint { name: string; @@ -73,7 +71,7 @@ export interface IExtensionPoint { export class ExtensionPointUserDelta { - private static _toSet(arr: IExtensionPointUser[]): Set { + private static _toSet(arr: readonly IExtensionPointUser[]): Set { const result = new Set(); for (let i = 0, len = arr.length; i < len; i++) { result.add(ExtensionIdentifier.toKey(arr[i].description.identifier)); @@ -81,7 +79,7 @@ export class ExtensionPointUserDelta { return result; } - public static compute(previous: IExtensionPointUser[] | null, current: IExtensionPointUser[]): ExtensionPointUserDelta { + public static compute(previous: readonly IExtensionPointUser[] | null, current: readonly IExtensionPointUser[]): ExtensionPointUserDelta { if (!previous || !previous.length) { return new ExtensionPointUserDelta(current, []); } @@ -99,8 +97,8 @@ export class ExtensionPointUserDelta { } constructor( - public readonly added: IExtensionPointUser[], - public readonly removed: IExtensionPointUser[], + public readonly added: readonly IExtensionPointUser[], + public readonly removed: readonly IExtensionPointUser[], ) { } } diff --git a/src/vs/workbench/services/extensions/common/staticExtensions.ts b/src/vs/workbench/services/extensions/common/staticExtensions.ts new file mode 100644 index 0000000000..64eda6d064 --- /dev/null +++ b/src/vs/workbench/services/extensions/common/staticExtensions.ts @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IExtensionDescription, IExtensionManifest, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { UriComponents, URI } from 'vs/base/common/uri'; + +export const IStaticExtensionsService = createDecorator('IStaticExtensionsService'); + +export interface IStaticExtensionsService { + _serviceBrand: any; + getExtensions(): Promise; +} + +export class StaticExtensionsService implements IStaticExtensionsService { + + _serviceBrand: any; + + private readonly _descriptions: IExtensionDescription[] = []; + + constructor(staticExtensions: { packageJSON: IExtensionManifest, extensionLocation: UriComponents }[]) { + this._descriptions = staticExtensions.map(data => { + identifier: new ExtensionIdentifier(`${data.packageJSON.publisher}.${data.packageJSON.name}`), + extensionLocation: URI.revive(data.extensionLocation), + ...data.packageJSON, + }); + } + + async getExtensions(): Promise { + return this._descriptions; + } +} diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index b345b439e2..4fef7fb208 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -34,6 +34,8 @@ import { IFileService } from 'vs/platform/files/common/files'; import { PersistentConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection'; import { IProductService } from 'vs/platform/product/common/product'; import { Logger } from 'vs/workbench/services/extensions/common/extensionPoints'; +import { flatten } from 'vs/base/common/arrays'; +import { IStaticExtensionsService } from 'vs/workbench/services/extensions/common/staticExtensions'; class DeltaExtensionsQueueItem { constructor( @@ -64,6 +66,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten @IConfigurationService private readonly _configurationService: IConfigurationService, @ILifecycleService private readonly _lifecycleService: ILifecycleService, @IWindowService protected readonly _windowService: IWindowService, + @IStaticExtensionsService private readonly _staticExtensions: IStaticExtensionsService, ) { super( instantiationService, @@ -72,7 +75,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten telemetryService, extensionEnablementService, fileService, - productService, + productService ); if (this._extensionEnablementService.allUserExtensionsDisabled) { @@ -437,7 +440,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten const remoteAuthority = this._environmentService.configuration.remoteAuthority; const extensionHost = this._extensionHostProcessManagers[0]; - let localExtensions = await this._extensionScanner.scannedExtensions; + let localExtensions = flatten(await Promise.all([this._extensionScanner.scannedExtensions, this._staticExtensions.getExtensions()])); // enable or disable proposed API per extension this._checkEnableProposedApi(localExtensions); @@ -463,7 +466,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten this._remoteAuthorityResolverService.setResolvedAuthorityError(remoteAuthority, err); // Proceed with the local extension host - await this._startLocalExtensionHost(extensionHost, localExtensions); + await this._startLocalExtensionHost(extensionHost, localExtensions, localExtensions.map(extension => extension.identifier)); return; } @@ -508,20 +511,18 @@ export class ExtensionService extends AbstractExtensionService implements IExten // save for remote extension's init data this._remoteExtensionsEnvironmentData.set(remoteAuthority, remoteEnv); - this._handleExtensionPoints(([]).concat(remoteEnv.extensions).concat(localExtensions)); - extensionHost.start(localExtensions.map(extension => extension.identifier)); - + await this._startLocalExtensionHost(extensionHost, remoteEnv.extensions.concat(localExtensions), localExtensions.map(extension => extension.identifier)); } else { - await this._startLocalExtensionHost(extensionHost, localExtensions); + await this._startLocalExtensionHost(extensionHost, localExtensions, localExtensions.map(extension => extension.identifier)); } } - private async _startLocalExtensionHost(extensionHost: ExtensionHostProcessManager, localExtensions: IExtensionDescription[]): Promise { - this._handleExtensionPoints(localExtensions); - extensionHost.start(localExtensions.map(extension => extension.identifier).filter(id => this._registry.containsExtension(id))); + private async _startLocalExtensionHost(extensionHost: ExtensionHostProcessManager, allExtensions: IExtensionDescription[], localExtensions: ExtensionIdentifier[]): Promise { + this._registerAndHandleExtensions(allExtensions); + extensionHost.start(localExtensions.filter(id => this._registry.containsExtension(id))); } - private _handleExtensionPoints(allExtensions: IExtensionDescription[]): void { + private _registerAndHandleExtensions(allExtensions: IExtensionDescription[]): void { const result = this._registry.deltaExtensions(allExtensions, []); if (result.removedDueToLooping.length > 0) { this._logOrShowMessage(Severity.Error, nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', '))); diff --git a/src/vs/workbench/services/files/common/workspaceWatcher.ts b/src/vs/workbench/services/files/common/workspaceWatcher.ts index dbc896aa52..f8595bd3b5 100644 --- a/src/vs/workbench/services/files/common/workspaceWatcher.ts +++ b/src/vs/workbench/services/files/common/workspaceWatcher.ts @@ -13,8 +13,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { ResourceMap } from 'vs/base/common/map'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { StorageScope, IStorageService } from 'vs/platform/storage/common/storage'; -import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { INotificationService, Severity, NeverShowAgainScope } from 'vs/platform/notification/common/notification'; import { localize } from 'vs/nls'; import { FileService } from 'vs/platform/files/common/fileService'; @@ -26,8 +25,7 @@ export class WorkspaceWatcher extends Disposable { @IFileService private readonly fileService: FileService, @IConfigurationService private readonly configurationService: IConfigurationService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @INotificationService private readonly notificationService: INotificationService, - @IStorageService private readonly storageService: IStorageService + @INotificationService private readonly notificationService: INotificationService ) { super(); @@ -73,38 +71,34 @@ export class WorkspaceWatcher extends Disposable { onUnexpectedError(msg); // Detect if we run < .NET Framework 4.5 - if (msg.indexOf('System.MissingMethodException') >= 0 && !this.storageService.getBoolean('ignoreNetVersionError', StorageScope.WORKSPACE)) { + if (msg.indexOf('System.MissingMethodException') >= 0) { this.notificationService.prompt( Severity.Warning, localize('netVersionError', "The Microsoft .NET Framework 4.5 is required. Please follow the link to install it."), [{ label: localize('installNet', "Download .NET Framework 4.5"), run: () => window.open('https://go.microsoft.com/fwlink/?LinkId=786533') - }, - { - label: localize('neverShowAgain', "Don't Show Again"), - isSecondary: true, - run: () => this.storageService.store('ignoreNetVersionError', true, StorageScope.WORKSPACE) }], - { sticky: true } + { + sticky: true, + neverShowAgain: { id: 'ignoreNetVersionError', isSecondary: true, scope: NeverShowAgainScope.WORKSPACE } + } ); } // Detect if we run into ENOSPC issues - if (msg.indexOf('ENOSPC') >= 0 && !this.storageService.getBoolean('ignoreEnospcError', StorageScope.WORKSPACE)) { + if (msg.indexOf('ENOSPC') >= 0) { this.notificationService.prompt( Severity.Warning, localize('enospcError', "Unable to watch for file changes in this large workspace. Please follow the instructions link to resolve this issue."), [{ label: localize('learnMore', "Instructions"), run: () => window.open('https://go.microsoft.com/fwlink/?linkid=867693') - }, - { - label: localize('neverShowAgain', "Don't Show Again"), - isSecondary: true, - run: () => this.storageService.store('ignoreEnospcError', true, StorageScope.WORKSPACE) }], - { sticky: true } + { + sticky: true, + neverShowAgain: { id: 'ignoreEnospcError', isSecondary: true, scope: NeverShowAgainScope.WORKSPACE } + } ); } } @@ -157,4 +151,4 @@ export class WorkspaceWatcher extends Disposable { } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(WorkspaceWatcher, LifecyclePhase.Restored); \ No newline at end of file +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(WorkspaceWatcher, LifecyclePhase.Restored); diff --git a/src/vs/workbench/services/notification/common/notificationService.ts b/src/vs/workbench/services/notification/common/notificationService.ts index 94f98e5efd..649b73c9bb 100644 --- a/src/vs/workbench/services/notification/common/notificationService.ts +++ b/src/vs/workbench/services/notification/common/notificationService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { INotificationService, INotification, INotificationHandle, Severity, NotificationMessage, INotificationActions, IPromptChoice, IPromptOptions, IStatusMessageOptions, NoOpNotification } from 'vs/platform/notification/common/notification'; +import { INotificationService, INotification, INotificationHandle, Severity, NotificationMessage, INotificationActions, IPromptChoice, IPromptOptions, IStatusMessageOptions, NoOpNotification, NeverShowAgainScope } from 'vs/platform/notification/common/notification'; import { INotificationsModel, NotificationsModel, ChoiceAction } from 'vs/workbench/common/notifications'; import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; @@ -63,11 +63,12 @@ export class NotificationService extends Disposable implements INotificationServ // Handle neverShowAgain option accordingly let handle: INotificationHandle; if (notification.neverShowAgain) { + const scope = notification.neverShowAgain.scope === NeverShowAgainScope.WORKSPACE ? StorageScope.WORKSPACE : StorageScope.GLOBAL; // If the user already picked to not show the notification // again, we return with a no-op notification here const id = notification.neverShowAgain.id; - if (this.storageService.getBoolean(id, StorageScope.GLOBAL)) { + if (this.storageService.getBoolean(id, scope)) { return new NoOpNotification(); } @@ -80,7 +81,7 @@ export class NotificationService extends Disposable implements INotificationServ handle.close(); // Remember choice - this.storageService.store(id, true, StorageScope.GLOBAL); + this.storageService.store(id, true, scope); return Promise.resolve(); })); @@ -110,17 +111,18 @@ export class NotificationService extends Disposable implements INotificationServ // Handle neverShowAgain option accordingly if (options && options.neverShowAgain) { + const scope = options.neverShowAgain.scope === NeverShowAgainScope.WORKSPACE ? StorageScope.WORKSPACE : StorageScope.GLOBAL; // If the user already picked to not show the notification // again, we return with a no-op notification here const id = options.neverShowAgain.id; - if (this.storageService.getBoolean(id, StorageScope.GLOBAL)) { + if (this.storageService.getBoolean(id, scope)) { return new NoOpNotification(); } const neverShowAgainChoice = { label: nls.localize('neverShowAgain', "Don't Show Again"), - run: () => this.storageService.store(id, true, StorageScope.GLOBAL), + run: () => this.storageService.store(id, true, scope), isSecondary: options.neverShowAgain.isSecondary }; diff --git a/src/vs/workbench/services/themes/browser/fileIconThemeData.ts b/src/vs/workbench/services/themes/browser/fileIconThemeData.ts index fb849ae2cc..5137574ccd 100644 --- a/src/vs/workbench/services/themes/browser/fileIconThemeData.ts +++ b/src/vs/workbench/services/themes/browser/fileIconThemeData.ts @@ -11,7 +11,7 @@ import * as Json from 'vs/base/common/json'; import { ExtensionData, IThemeExtensionPoint, IFileIconTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IFileService } from 'vs/platform/files/common/files'; import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages'; -import { asDomUri } from 'vs/base/browser/dom'; +import { asCSSUrl } from 'vs/base/browser/dom'; export class FileIconThemeData implements IFileIconTheme { id: string; @@ -332,7 +332,7 @@ function _processIconThemeDocument(id: string, iconThemeDocumentLocation: URI, i let fonts = iconThemeDocument.fonts; if (Array.isArray(fonts)) { fonts.forEach(font => { - let src = font.src.map(l => `url('${asDomUri(resolvePath(l.path))}') format('${l.format}')`).join(', '); + let src = font.src.map(l => `${asCSSUrl(resolvePath(l.path))} format('${l.format}')`).join(', '); cssRules.push(`@font-face { src: ${src}; font-family: '${font.id}'; font-weight: ${font.weight}; font-style: ${font.style}; }`); }); cssRules.push(`.show-file-icons .file-icon::before, .show-file-icons .folder-icon::before, .show-file-icons .rootfolder-icon::before { font-family: '${fonts[0].id}'; font-size: ${fonts[0].size || '150%'}}`); @@ -343,7 +343,7 @@ function _processIconThemeDocument(id: string, iconThemeDocumentLocation: URI, i let definition = iconThemeDocument.iconDefinitions[defId]; if (definition) { if (definition.iconPath) { - cssRules.push(`${selectors.join(', ')} { content: ' '; background-image: url("${asDomUri(resolvePath(definition.iconPath))}"); }`); + cssRules.push(`${selectors.join(', ')} { content: ' '; background-image: ${asCSSUrl(resolvePath(definition.iconPath))}; }`); } if (definition.fontCharacter || definition.fontColor) { let body = ''; diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index 57e91c5077..3b94d88dda 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -74,6 +74,7 @@ import { IMenubarService } from 'vs/platform/menubar/common/menubar'; import { MenubarService } from 'vs/platform/menubar/electron-browser/menubarService'; import { IURLService } from 'vs/platform/url/common/url'; import { RelayURLService } from 'vs/platform/url/electron-browser/urlService'; +import { StaticExtensionsService, IStaticExtensionsService } from 'vs/workbench/services/extensions/common/staticExtensions'; registerSingleton(IClipboardService, ClipboardService, true); registerSingleton(IRequestService, RequestService, true); @@ -86,6 +87,7 @@ registerSingleton(IIssueService, IssueService); registerSingleton(IWorkspacesService, WorkspacesService); registerSingleton(IMenubarService, MenubarService); registerSingleton(IURLService, RelayURLService); +registerSingleton(IStaticExtensionsService, class extends StaticExtensionsService { constructor() { super([]); } }); //#endregion diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index c319a043f6..94ca71746b 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -9,6 +9,7 @@ import { UriComponents } from 'vs/base/common/uri'; import { IFileSystemProvider } from 'vs/platform/files/common/files'; import { IWebSocketFactory } from 'vs/platform/remote/browser/browserSocketFactory'; import { ICredentialsProvider } from 'vs/workbench/services/credentials/browser/credentialsService'; +import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; export interface IWorkbenchConstructionOptions { @@ -59,6 +60,11 @@ export interface IWorkbenchConstructionOptions { * Experimental: The credentials provider to store and retrieve secrets. */ credentialsProvider?: ICredentialsProvider; + + /** + * Experimental: Add static extensions that cannot be uninstalled but only be disabled. + */ + staticExtensions?: { packageJSON: IExtensionManifest, extensionLocation: UriComponents }[]; } /** diff --git a/test/smoke/src/vscode/puppeteerDriver.ts b/test/smoke/src/vscode/puppeteerDriver.ts index 5c58fd556b..c049a23a7d 100644 --- a/test/smoke/src/vscode/puppeteerDriver.ts +++ b/test/smoke/src/vscode/puppeteerDriver.ts @@ -95,6 +95,7 @@ export async function launch(_args: string[]): Promise { await promisify(mkdir)(webUserDataDir); server = spawn(join(args[0], `resources/server/web.${process.platform === 'win32' ? 'bat' : 'sh'}`), ['--browser', 'none', '--driver', 'web', '--web-user-data-dir', webUserDataDir]); server.stderr.on('data', e => console.log('Server stderr: ' + e)); + server.stdout.on('data', e => console.log('Server stdout: ' + e)); process.on('exit', teardown); process.on('SIGINT', teardown); endpoint = await waitForEndpoint(); @@ -129,8 +130,7 @@ export function connect(headless: boolean, outPath: string, handle: string): Pro }); const page = (await browser.pages())[0]; await page.setViewport({ width, height }); - const endpointSplit = endpoint!.split('#'); - await page.goto(`${endpointSplit[0]}?folder=${args![1]}#${endpointSplit[1]}`); + await page.goto(`${endpoint}&folder=${args![1]}`); const result = { client: { dispose: () => teardown }, driver: buildDriver(browser, page) diff --git a/yarn.lock b/yarn.lock index 046ef4ad1d..2272f3f400 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10328,10 +10328,10 @@ xtend@~2.1.1: dependencies: object-keys "~0.4.0" -xterm-addon-search@0.2.0-beta3: - version "0.2.0-beta3" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.2.0-beta3.tgz#710ce14658e269c5a4791f5a9e2f520883a2e62b" - integrity sha512-KzVdkEtGbKJe9ER2TmrI7XjF/wUq1lir9U63vPJi0t2ymQvIECl1V63f9QtOp1vvpdhbZiXBxO+vGTj+y0tRow== +xterm-addon-search@0.2.0-beta5: + version "0.2.0-beta5" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.2.0-beta5.tgz#258d7cb1511d9060cd4520f0f82e408000fd4f53" + integrity sha512-Tg+d8scch0rYOVmzBbX35Y1GXtq+eu/YlzbXznmTo/yD83j3BQlXOhgECu/Yv8EX5JwFmzbfVRWC+JWnfigwGg== xterm-addon-web-links@0.1.0-beta10: version "0.1.0-beta10"