From 58bfba4b472cfa482c42582fd2d118ad0cfc1441 Mon Sep 17 00:00:00 2001 From: Anthony Dresser Date: Thu, 15 Aug 2019 02:19:31 -0700 Subject: [PATCH] Merge from vscode b12f623603e2fc1c5b3037115fa37c1a6acc4165 (#6760) --- build/gulpfile.vscode.web.js | 3 +- build/win32/code.iss | 4 +- .../syntaxes/markdown.tmLanguage.json | 37 +- .../test/colorize-results/test-33886_md.json | 20 +- package.json | 4 +- resources/win32/inno-big-100.bmp | Bin 0 -> 154544 bytes .../win32/{inno-big.bmp => inno-big-125.bmp} | Bin resources/win32/inno-big-150.bmp | Bin 0 -> 339716 bytes resources/win32/inno-big-175.bmp | Bin 0 -> 455976 bytes resources/win32/inno-big-200.bmp | Bin 0 -> 594392 bytes resources/win32/inno-big-225.bmp | Bin 0 -> 747656 bytes resources/win32/inno-big-250.bmp | Bin 0 -> 1307136 bytes resources/win32/inno-small-100.bmp | Bin 0 -> 9296 bytes resources/win32/inno-small-125.bmp | Bin 0 -> 13112 bytes resources/win32/inno-small-150.bmp | Bin 0 -> 20216 bytes resources/win32/inno-small-175.bmp | Bin 0 -> 26828 bytes resources/win32/inno-small-200.bmp | Bin 0 -> 35248 bytes resources/win32/inno-small-225.bmp | Bin 0 -> 44336 bytes resources/win32/inno-small-250.bmp | Bin 0 -> 58296 bytes resources/win32/inno-small.bmp | Bin 12816 -> 0 bytes src/buildfile.js | 10 +- src/sql/base/browser/ui/listBox/listBox.ts | 4 +- .../base/browser/ui/selectBox/selectBox.ts | 4 +- .../browser/parts/views/customView.ts | 4 +- .../outputs/notebookMarkdown.ts | 6 +- src/vs/base/browser/formattedTextRenderer.ts | 220 +++++++++ ...ContentRenderer.ts => markdownRenderer.ts} | 233 +--------- src/vs/base/browser/ui/inputbox/inputBox.ts | 5 +- .../browser/ui/selectBox/selectBoxCustom.ts | 2 +- src/vs/base/common/strings.ts | 15 + ....test.ts => formattedTextRenderer.test.ts} | 38 +- .../test/browser/markdownRenderer.test.ts | 47 ++ src/vs/base/worker/defaultWorkerFactory.ts | 33 +- src/vs/code/electron-main/app.ts | 2 +- .../editor/browser/controller/mouseHandler.ts | 2 +- .../editor/browser/controller/mouseTarget.ts | 2 +- src/vs/editor/browser/editorBrowser.ts | 8 +- .../editor/browser/services/openerService.ts | 9 +- src/vs/editor/browser/view/viewImpl.ts | 8 +- .../browser/viewParts/viewZones/viewZones.ts | 34 +- .../editor/browser/widget/diffEditorWidget.ts | 2 +- src/vs/editor/common/model/textModel.ts | 17 +- .../editor/common/viewLayout/linesLayout.ts | 6 +- src/vs/editor/common/viewLayout/viewLayout.ts | 6 +- .../common/viewLayout/whitespaceComputer.ts | 53 ++- src/vs/editor/common/viewModel/viewModel.ts | 8 +- .../editor/contrib/codelens/codelensWidget.ts | 2 +- src/vs/editor/contrib/find/findWidget.ts | 12 +- .../contrib/markdown/markdownRenderer.ts | 4 +- .../contrib/snippet/snippetVariables.ts | 3 + .../snippet/test/snippetVariables.test.ts | 1 + .../editor/contrib/zoneWidget/zoneWidget.ts | 2 +- .../accessibilityHelp/accessibilityHelp.ts | 2 +- src/vs/monaco.d.ts | 6 +- .../common/extensionGalleryService.ts | 14 +- .../platform/extensions/common/extensions.ts | 2 +- src/vs/platform/opener/common/opener.ts | 14 +- .../product/browser/productService.ts | 44 +- src/vs/platform/product/common/product.ts | 47 +- .../platform/product/node/productService.ts | 37 +- .../storage/browser/storageService.ts | 8 +- .../windows/{node => common}/windowsIpc.ts | 0 .../electron-browser/windowsService.ts | 11 +- .../api/browser/mainThreadCodeInsets.ts | 2 +- .../api/browser/mainThreadWebview.ts | 2 +- .../workbench/api/browser/mainThreadWindow.ts | 7 +- .../api/browser/viewsExtensionPoint.ts | 41 +- .../api/common/extHostExtensionService.ts | 8 +- .../api/node/extHostExtensionService.ts | 12 +- .../api/worker/extHostExtensionService.ts | 64 ++- src/vs/workbench/browser/layout.ts | 10 +- .../workbench/browser/parts/editor/editor.ts | 2 +- src/vs/workbench/browser/parts/views/views.ts | 14 +- .../workbench/browser/web.simpleservices.ts | 41 +- src/vs/workbench/common/views.ts | 14 +- .../browser/accessibility/accessibility.ts | 2 +- .../comments/browser/commentsTreeViewer.ts | 2 +- .../contrib/debug/browser/breakpointsView.ts | 4 +- .../browser/debugConfigurationManager.ts | 7 +- .../contrib/debug/browser/debugSession.ts | 2 +- .../contrib/debug/common/debugSource.ts | 3 +- .../experimentalPrompt.ts | 0 .../experiments.contribution.ts | 5 +- .../experiments/common/experimentService.ts | 404 ++++++++++++++++- .../electron-browser/experimentService.ts | 407 ----------------- .../experimentService.test.ts | 3 +- .../experimentalPrompts.test.ts | 2 +- .../extensions/browser/extensionsActions.ts | 4 +- .../extensions/browser/extensionsViews.ts | 6 +- .../browser/extensionsWorkbenchService.ts | 17 +- .../electron-browser/extensionTipsService.ts | 50 +-- .../extensionsTipsService.test.ts | 1 + .../electron-browser/extensionsViews.test.ts | 3 +- .../contrib/feedback/browser/feedback.ts | 4 +- .../feedback/browser/feedbackStatusbarItem.ts | 2 +- .../files/browser/editors/binaryFileEditor.ts | 14 +- .../contrib/files/browser/explorerViewlet.ts | 5 +- .../contrib/files/browser/fileActions.ts | 2 +- .../contrib/files/common/explorerService.ts | 6 +- .../contrib/markers/browser/markersPanel.ts | 4 +- .../browser/keyboardLayoutPicker.ts | 4 +- .../preferences/browser/preferencesActions.ts | 2 +- .../preferences/browser/preferencesSearch.ts | 6 +- .../preferences/browser/preferencesWidgets.ts | 4 +- .../preferences/browser/settingsEditor2.ts | 4 +- .../preferences/browser/settingsTree.ts | 2 +- .../browser/help-documentation-dark.svg | 11 + .../remote/browser/help-documentation-hc.svg | 11 + .../browser/help-documentation-light.svg | 11 + .../remote/browser/help-feedback-dark.svg | 4 + .../remote/browser/help-feedback-hc.svg | 4 + .../remote/browser/help-feedback-light.svg | 4 + .../browser/help-getting-started-dark.svg | 4 + .../browser/help-getting-started-hc.svg | 4 + .../browser/help-getting-started-light.svg | 4 + .../remote/browser/help-report-issue-dark.svg | 4 + .../remote/browser/help-report-issue-hc.svg | 4 + .../browser/help-report-issue-light.svg | 4 + .../browser/help-review-issues-dark.svg | 4 + .../remote/browser/help-review-issues-hc.svg | 4 + .../browser/help-review-issues-light.svg | 4 + .../remote/browser/remote-activity-bar.svg | 13 + .../contrib/remote/browser/remote.ts | 419 ++++++++++++++++++ .../contrib/remote/browser/remoteViewlet.css | 92 ++++ .../remote/common/remote.contribution.ts | 28 ++ .../contrib/search/browser/searchView.ts | 2 +- .../contrib/stats/common/workspaceStats.ts | 26 ++ .../stats/electron-browser/workspaceStats.ts | 18 +- .../electron-browser/workspaceStatsService.ts | 33 +- .../terminal/browser/terminal.contribution.ts | 5 +- .../terminal/browser/terminalActions.ts | 29 +- .../browser/terminalProcessManager.ts | 2 +- .../contrib/terminal/common/terminal.ts | 2 +- .../terminal/common/terminalCommands.ts | 3 +- .../terminal/common/terminalService.ts | 20 +- .../contrib/webview/browser/pre/main.js | 7 +- .../editor/vs_code_editor_walkthrough.md | 5 +- .../services/editor/browser/editorService.ts | 18 +- .../services/editor/common/editorService.ts | 12 +- .../extensions/browser/extensionService.ts | 10 +- .../browser/webWorkerExtensionHostStarter.ts | 46 +- .../common/abstractExtensionService.ts | 6 +- .../extensions/common/extensionsUtil.ts | 7 +- .../common/remoteExtensionHostClient.ts | 12 +- .../remoteExtensionManagementIpc.ts | 4 +- .../extensions/node/extensionPoints.ts | 5 - .../services/extensions/node/proxyResolver.ts | 36 +- .../extensions/worker/extensionHostWorker.ts | 20 +- .../worker/extensionHostWorkerMain.ts | 21 + .../services/history/browser/history.ts | 2 +- .../opener/electron-browser/openerService.ts | 40 ++ .../preferences/browser/preferencesService.ts | 33 +- .../preferences/common/preferences.ts | 14 +- .../remote/browser/remoteAgentServiceImpl.ts | 2 +- .../telemetry/browser/telemetryService.ts | 6 +- .../electron-browser/telemetryService.ts | 4 +- .../textfile/test/textFileService.io.test.ts | 14 +- src/vs/workbench/workbench.common.main.ts | 7 +- src/vs/workbench/workbench.desktop.main.ts | 4 +- src/vs/workbench/workbench.web.main.ts | 3 + yarn.lock | 25 +- 161 files changed, 2072 insertions(+), 1317 deletions(-) create mode 100644 resources/win32/inno-big-100.bmp rename resources/win32/{inno-big.bmp => inno-big-125.bmp} (100%) mode change 100644 => 100755 create mode 100755 resources/win32/inno-big-150.bmp create mode 100755 resources/win32/inno-big-175.bmp create mode 100755 resources/win32/inno-big-200.bmp create mode 100755 resources/win32/inno-big-225.bmp create mode 100644 resources/win32/inno-big-250.bmp create mode 100755 resources/win32/inno-small-100.bmp create mode 100755 resources/win32/inno-small-125.bmp create mode 100755 resources/win32/inno-small-150.bmp create mode 100755 resources/win32/inno-small-175.bmp create mode 100755 resources/win32/inno-small-200.bmp create mode 100755 resources/win32/inno-small-225.bmp create mode 100755 resources/win32/inno-small-250.bmp delete mode 100644 resources/win32/inno-small.bmp create mode 100644 src/vs/base/browser/formattedTextRenderer.ts rename src/vs/base/browser/{htmlContentRenderer.ts => markdownRenderer.ts} (50%) rename src/vs/base/test/browser/{htmlContent.test.ts => formattedTextRenderer.test.ts} (65%) create mode 100644 src/vs/base/test/browser/markdownRenderer.test.ts rename src/vs/platform/windows/{node => common}/windowsIpc.ts (100%) rename src/vs/workbench/contrib/experiments/{electron-browser => browser}/experimentalPrompt.ts (100%) rename src/vs/workbench/contrib/experiments/{electron-browser => browser}/experiments.contribution.ts (79%) delete mode 100644 src/vs/workbench/contrib/experiments/electron-browser/experimentService.ts create mode 100644 src/vs/workbench/contrib/remote/browser/help-documentation-dark.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-documentation-hc.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-documentation-light.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-feedback-dark.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-feedback-hc.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-feedback-light.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-getting-started-dark.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-getting-started-hc.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-getting-started-light.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-report-issue-dark.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-report-issue-hc.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-report-issue-light.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-review-issues-dark.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-review-issues-hc.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-review-issues-light.svg create mode 100644 src/vs/workbench/contrib/remote/browser/remote-activity-bar.svg create mode 100644 src/vs/workbench/contrib/remote/browser/remote.ts create mode 100644 src/vs/workbench/contrib/remote/browser/remoteViewlet.css create mode 100644 src/vs/workbench/contrib/stats/common/workspaceStats.ts create mode 100644 src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts create mode 100644 src/vs/workbench/services/opener/electron-browser/openerService.ts diff --git a/build/gulpfile.vscode.web.js b/build/gulpfile.vscode.web.js index cddd86631d..a40c461aa9 100644 --- a/build/gulpfile.vscode.web.js +++ b/build/gulpfile.vscode.web.js @@ -53,6 +53,7 @@ const buildfile = require('../src/buildfile'); const vscodeWebEntryPoints = [ buildfile.workbenchWeb, buildfile.serviceWorker, + buildfile.workerExtensionHost, buildfile.keyboardMaps, buildfile.base ]; @@ -148,4 +149,4 @@ const dashed = (str) => (str ? `-${str}` : ``); vscodeWebTaskCI )); gulp.task(vscodeWebTask); -}); \ No newline at end of file +}); diff --git a/build/win32/code.iss b/build/win32/code.iss index d13e4014e5..66b2053053 100644 --- a/build/win32/code.iss +++ b/build/win32/code.iss @@ -20,8 +20,8 @@ Compression=lzma SolidCompression=yes AppMutex={code:GetAppMutex} SetupMutex={#AppMutex}setup -WizardImageFile={#RepoDir}\resources\win32\inno-big.bmp -WizardSmallImageFile={#RepoDir}\resources\win32\inno-small.bmp +WizardImageFile="{#RepoDir}\resources\win32\inno-big-100.bmp,{#RepoDir}\resources\win32\inno-big-125.bmp,{#RepoDir}\resources\win32\inno-big-150.bmp,{#RepoDir}\resources\win32\inno-big-175.bmp,{#RepoDir}\resources\win32\inno-big-200.bmp,{#RepoDir}\resources\win32\inno-big-225.bmp,{#RepoDir}\resources\win32\inno-big-250.bmp" +WizardSmallImageFile="{#RepoDir}\resources\win32\inno-small-100.bmp,{#RepoDir}\resources\win32\inno-small-125.bmp,{#RepoDir}\resources\win32\inno-small-150.bmp,{#RepoDir}\resources\win32\inno-small-175.bmp,{#RepoDir}\resources\win32\inno-small-200.bmp,{#RepoDir}\resources\win32\inno-small-225.bmp,{#RepoDir}\resources\win32\inno-small-250.bmp" SetupIconFile={#RepoDir}\resources\win32\code.ico UninstallDisplayIcon={app}\{#ExeBasename}.exe ChangesEnvironment=true diff --git a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json index f63c46a878..6fb25ae6ce 100644 --- a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json +++ b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/a595d8ba2ae9ce8864435d33db2afa0fe68b1487", + "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/05ccfa3db6edbd357390431f9e316adb38ba41d8", "name": "Markdown", "scopeName": "text.html.markdown", "patterns": [ @@ -716,7 +716,7 @@ { "begin": "(^|\\G)(\\s*)(.*)", "while": "(^|\\G)(?!\\s*([`~]{3,})\\s*$)", - "contentName": "meta.embedded.block.cpp source.cpp", + "contentName": "meta.embedded.block.cpp", "patterns": [ { "include": "source.cpp" @@ -1987,8 +1987,29 @@ "name": "comment.block.html" }, { - "begin": "(^|\\G)\\s*(?=<(script|style|pre)(\\s|$|>)(?!.*?))", - "end": "(?=.*)", + "begin": "(?i)(^|\\G)\\s*(?=<(script|style|pre)(\\s|$|>)(?!.*?))", + "end": "(?i)(.*)(())", + "endCaptures": { + "1": { + "patterns": [ + { + "include": "text.html.basic" + } + ] + }, + "2": { + "name": "meta.tag.structure.$4.end.html" + }, + "3": { + "name": "punctuation.definition.tag.begin.html" + }, + "4": { + "name": "entity.name.tag.html" + }, + "5": { + "name": "punctuation.definition.tag.end.html" + } + }, "patterns": [ { "begin": "(\\s*|$)", @@ -1997,12 +2018,12 @@ "include": "text.html.basic" } ], - "while": "^(?!.*)" + "while": "(?i)^(?!.*)" } ] }, { - "begin": "(^|\\G)\\s*(?=))", + "begin": "(?i)(^|\\G)\\s*(?=))", "patterns": [ { "include": "text.html.basic" @@ -2275,7 +2296,7 @@ }, "bracket": { "comment": "Markdown will convert this for us. We match it so that the HTML grammar will not mark it up as invalid.", - "match": "<(?![a-z/?\\$!])", + "match": "<(?![a-zA-Z/?\\$!])", "name": "meta.other.valid-bracket.markdown" }, "escape": { @@ -2566,4 +2587,4 @@ "name": "markup.inline.raw.string.markdown" } } -} +} \ No newline at end of file diff --git a/extensions/markdown-basics/test/colorize-results/test-33886_md.json b/extensions/markdown-basics/test/colorize-results/test-33886_md.json index 179172a573..25799e89a3 100644 --- a/extensions/markdown-basics/test/colorize-results/test-33886_md.json +++ b/extensions/markdown-basics/test/colorize-results/test-33886_md.json @@ -111,7 +111,7 @@ }, { "c": "", - "t": "text.html.markdown meta.paragraph.markdown meta.tag.inline.code.end.html punctuation.definition.tag.end.html", + "t": "text.html.markdown meta.tag.inline.code.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -144,7 +144,7 @@ }, { "c": "", - "t": "text.html.markdown meta.paragraph.markdown meta.tag.structure.pre.end.html punctuation.definition.tag.end.html", + "t": "text.html.markdown meta.tag.structure.pre.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -254,7 +254,7 @@ }, { "c": "a", - "t": "text.html.markdown meta.paragraph.markdown", + "t": "text.html.markdown", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -265,7 +265,7 @@ }, { "c": "", - "t": "text.html.markdown meta.paragraph.markdown meta.tag.structure.pre.end.html punctuation.definition.tag.end.html", + "t": "text.html.markdown meta.tag.structure.pre.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", diff --git a/package.json b/package.json index 94213a79a4..168a40f189 100644 --- a/package.json +++ b/package.json @@ -131,7 +131,7 @@ "gulp-shell": "^0.6.5", "gulp-tsb": "2.0.7", "gulp-tslint": "^8.1.3", - "gulp-uglify": "^3.0.0", + "gulp-uglify": "^3.0.2", "gulp-untar": "^0.0.7", "gulp-vinyl-zip": "^2.1.2", "http-server": "^0.11.1", @@ -168,7 +168,7 @@ "typemoq": "^0.3.2", "typescript": "3.5.2", "typescript-formatter": "7.1.0", - "uglify-es": "^3.0.18", + "uglify-es": "^3.3.9", "vinyl": "^2.0.0", "vinyl-fs": "^3.0.0", "vsce": "1.48.0", diff --git a/resources/win32/inno-big-100.bmp b/resources/win32/inno-big-100.bmp new file mode 100644 index 0000000000000000000000000000000000000000..99cf4ba66683ae1ade11909259216a5e59177c36 GIT binary patch literal 154544 zcmeI*v8p817RTWpAHYz21fRgbK+ssh+&7uG5F;@)G%zwSF%T1l(L~IQ1qICv40JmU zO+nXLQ>XU7Rypv=nM+f-qWnuRn_q z-t!qw+@&~s+fv){wQg(r&_-JK2t4H9dA;YPV;9eLXV%rdwC)jD#c#gYb5hTBbJo@E zwC)jD#c#gYb5hTBbJo@EwC)jD#c#gYb5hTBbJo@EwC)jD#c#gYbJEG@`qv*n|LvzQ zn}5cpL7e8StJ?*SQ;(YY-%{#(SI;$naco+j=uxZlpJ#r;qdxG=o9kZns@ISgP+`Ej52p>pDuYTK6h$)O!AVd!0}t2UoRDCEGnGShF6b_3lQg8|y&9 z)tT16|NPY-zkJ*LGd2w#Kh1<9GUhzWeCyS~6R0-?aJ@23OnunQNcJ+1As@9QSGed$k>rq-qL}E=2sRIR9+geY8?7MxOuD04YHhWk734^OzXT$B77OYv1(t1ao z>@9Vm;HuWya66_2Yu2N*-ccueOC2b@x}e8I_QelCsw|0eNfr^v07^#|H_?0>hIypr`vOV%9n7t=`zQmueDyX zpYr<9zRTfSpVD`^=`zQmuiIMp|ANY~qu)`v{PMK5UT*sTdA)qT&U)RZq4imbZ~JQ9 zBX9#h@kP%`$1a}h6Dwc0*1AXF^w8r2^qkalt@TZy?JIZIMz@om_@eFX{`>mG%Ga&6 z?h!aW^!NZhCmp-YJlFm|kt1J~m!ft3J&8En4;h;Vaqj2$UkM(k9#!|6x%VJ6t?MYo zs@4a6C%2C+>SUL+-X7(kcV>eY)3iRQ;P$aao$Qj<+oK%x&TPx5MLForZRgecpn}`SYTYBSy~uIz>^bS!#dCdJ z#Ep}+?h)9S5!jf++XxUKK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF m5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAh1#3a`_i*_`;w7 literal 0 HcmV?d00001 diff --git a/resources/win32/inno-big.bmp b/resources/win32/inno-big-125.bmp old mode 100644 new mode 100755 similarity index 100% rename from resources/win32/inno-big.bmp rename to resources/win32/inno-big-125.bmp diff --git a/resources/win32/inno-big-150.bmp b/resources/win32/inno-big-150.bmp new file mode 100755 index 0000000000000000000000000000000000000000..554461982f01c8b8d2c8943fc683587aa46a39e5 GIT binary patch literal 339716 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 literal 0 HcmV?d00001 diff --git a/resources/win32/inno-big-175.bmp b/resources/win32/inno-big-175.bmp new file mode 100755 index 0000000000000000000000000000000000000000..be0e7df91cf7cab1b3caff6ddb4180b85cbcb89f GIT binary patch literal 455976 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 OK!5-N0$qWdoBspz9(5A{ literal 0 HcmV?d00001 diff --git a/resources/win32/inno-big-225.bmp b/resources/win32/inno-big-225.bmp new file mode 100755 index 0000000000000000000000000000000000000000..89cf4efb1e0b855e145ec7708bad421cf1096f8a GIT binary patch literal 747656 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 zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly 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 literal 0 HcmV?d00001 diff --git a/resources/win32/inno-small-150.bmp b/resources/win32/inno-small-150.bmp new file mode 100755 index 0000000000000000000000000000000000000000..3f65efc5f4c33f7c8dee74826756505f186848a7 GIT binary patch literal 20216 zcmeHNy-EW?5Wc*CrT7SzB4}e1>=nV@GO2t4eFB4^MH*{cOG`mfG+L*yun0l04p=By z=wM)3hMSwYS;Fn!PS`tU=Vtci`@Xrm{B7@i?-u{BP1vu(wh!ALY@K2S_C;|I#~+sF z+rI;Lzq!5)d!EXh?xuTg3|P2A8`#(?#FZi?HzPT2FeN6U4`+x4;EPLrEK z-4z1k{j-Hzmbaw<=&nx;zo#JSxjHa&l=hKbx<$-QMu7nT@vhz2E9o-FF zJrV9BK=^2re1=W=9sLn-94SNLxrh5fB z^FxZBtiIkA3)W3xLB*!~`{RXfOz+MSImuyX3#I~ozGii?_j)~QHQ$exl}vR#@1L}m zt-#*xb|t5ffJHI!V;BDXj&Mn)I_#stx^6t3tf8t{O=H83>JOQ+*gKsm8v_A~8g#lV zPDG`!xQ4?a8`DV%dwW=h=d(fEMVk`=S!ETJT+^-?V`?)gjIwTh9mL_0t_j z$}sE{GtTexDYSLZIN{=wFjqWfG^Kmazd@s}prk4FLvGT#8}?FJS!e3H=lmZt$psr2 z!}d>8_xV6vl61pf!GP&rLC*Y;q{mPUdWxa{*P-A=Oj%Gu-Pv_g3^42|suo_e=^oBu riYu7zDXJD;v*{krVTvo5?kTDkUbE>Q&S8oxnC>a67G87ax);SC>jLOQ literal 0 HcmV?d00001 diff --git a/resources/win32/inno-small-175.bmp b/resources/win32/inno-small-175.bmp new file mode 100755 index 0000000000000000000000000000000000000000..a11b53bcfe15b9cedeac90bcb8bc286de7037b11 GIT binary patch literal 26828 zcmeI3F-{v%6h$W+phQ^$E1*PzI!IYSnsiwMyFig5McD#%3YruVO^65$qN7Giq@+th z5x$J%!Y6+7zTemOh4n6CN33Qndj!b6HaJ*SGuYGZw~`Gp{hMODXnAzRtkeYGBQQe{_gx zU5ZSIOD2;u`F(d$-+tYmv#`+%Brq_oODXnA*SWgP!CM|3>N*k4GLZ=hP_a*BLIPBZ zeNwz}{WSf*x35PR(!jvC*`*Yj1cl{?S`Dl@Kb=mdbty6-E}6)L1gI4I#5I;kpHJb+ znOBNc75k)k_F#IR0G&Z>W=JNaBompC0F`2&)Nd%Ygqht_;`+9M(moe{SR3Rlr!cKc zDfY>-jLH+4klxIg)PL}52{Zf6tLr-LGP9lXO4o^KF0LmGoJz?PptD$mObAdWG9dvf znLhDv{4(6Qtc@xSK9d7wG%zp(v6-PvpU@N3vu;QxBqNjAGx2{_GON%QJnXc*QY??_ zXEAW9>3ITl7Hg0R0m?)sBtT`BeZmz+MFJ9#fCMBU0SQPz0uqpb1SB8<2}nQ!5+H$L Fcm!k1tRnyb literal 0 HcmV?d00001 diff --git a/resources/win32/inno-small-200.bmp b/resources/win32/inno-small-200.bmp new file mode 100755 index 0000000000000000000000000000000000000000..c0ad5436b66a9ab14e87d1a9ca1198b1ce51c505 GIT binary patch literal 35248 zcmeI4J&P1U5QfJ;Ac%073M$4TCYKrO?+^qvFk3T?yu`@heu0U~1(yhdaLXbWc*%hW z3JNEFfS|!0h#;KpY%m4IOubuE<95%z%goYLSJ(7D`&NIwE)sP~ zmT?5)B2kxQ8Al*45_L(IaRlNbQI}*HM<6Z|bxD?S1mYqsqRWFXyZw*DZ*O()|NK7e zA9kXMOeO?tn@#o_s{9R{QNNMA9i>va|%!Mo!zwk}Ly88*og}`fIaL zm(*pBx_rLXrdVwUlcL*=^LP!m8xmNHv)q2P`hN3X;Ia4Y4%kWQrZv@O$#}fndwQ#_ z*FO8NANvv#SjMk)c@_5sFW&8j>#{hO)MeZ}zA>XNyAuiL^BH$N>b@pei|6>x`n9ib zo&+BITaUp`N;e!(#w_IvT4EVznelkJM1JzpWNFPPl2&(_ESJHOE`UbY5?rvB9Frfik;bsUx`T{WkH-4~(N#!^$dc zZd)Ery`v+#33XWm)nFW!5wk3It?qameNCFYv`S=Y?c6PbO?|N}(^wI^8BfttOs(C9 zl~pR1CD-bs-uFjj5p#QPrJ+lX7MlOAd3KC6pPLAfyn8w20&3wmRzgnxQC=J<8=A% zKqS0T)MJUm!nvoW>>#I`;4F*#4PVAN&fh#+!w;@YvLs7m)|pr9!OvkCu`V;df64zY ziOX?^=!a+mrEKk#@jN~R7)MO9w9*5IN|t2#|L#~&mvL$%%ki)b`Ez6b_cA`29b?Z$ z)|bZ^6ZF!_62b&IktKu)aw1Cz6XZme5GKfpEFnyg6Inu-ASbefFhNda31Nbq$P&T? sIgurV334J!2ovN)mJlY$i7X*ZkP}%#m>?&zgfKx)WC>w{oT~Kr3n0c86951J literal 0 HcmV?d00001 diff --git a/resources/win32/inno-small-225.bmp b/resources/win32/inno-small-225.bmp new file mode 100755 index 0000000000000000000000000000000000000000..9a974c4d9c07c817000d700d83f4131103e60d33 GIT binary patch literal 44336 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 literal 0 HcmV?d00001 diff --git a/resources/win32/inno-small.bmp b/resources/win32/inno-small.bmp deleted file mode 100644 index 789fff066ecccdde41c56cc5878326faa8aad1ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12816 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 Ti~7^3&+*O_#C{K2UMBlL1$TLB diff --git a/src/buildfile.js b/src/buildfile.js index da10314e0a..fc52d8acb5 100644 --- a/src/buildfile.js +++ b/src/buildfile.js @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -function entrypoint (name) { +function entrypoint(name) { return [{ name: name, include: [], exclude: ['vs/css', 'vs/nls'] }]; } @@ -23,6 +23,14 @@ exports.serviceWorker = [{ dest: 'vs/workbench/contrib/resources/browser/resourceServiceWorkerMain.js' }]; +exports.workerExtensionHost = [{ + name: 'vs/workbench/services/extensions/worker/extensionHostWorker', + // include: [], + prepend: ['vs/loader.js'], + append: ['vs/workbench/services/extensions/worker/extensionHostWorkerMain'], + dest: 'vs/workbench/services/extensions/worker/extensionHostWorkerMain.js' +}]; + exports.workbench = require('./vs/workbench/buildfile').collectModules(['vs/workbench/workbench.desktop.main']); exports.workbenchWeb = entrypoint('vs/workbench/workbench.web.api'); diff --git a/src/sql/base/browser/ui/listBox/listBox.ts b/src/sql/base/browser/ui/listBox/listBox.ts index 244003bf60..7e22fa50c7 100644 --- a/src/sql/base/browser/ui/listBox/listBox.ts +++ b/src/sql/base/browser/ui/listBox/listBox.ts @@ -10,8 +10,8 @@ import { IMessage, MessageType, defaultOpts } from 'vs/base/browser/ui/inputbox/ import * as dom from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IContextViewProvider, AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; -import { RenderOptions, renderFormattedText, renderText } from 'vs/base/browser/htmlContentRenderer'; import { Emitter } from 'vs/base/common/event'; +import { renderFormattedText, renderText, FormattedTextRenderOptions } from 'vs/base/browser/formattedTextRenderer'; const $ = dom.$; @@ -212,7 +212,7 @@ export class ListBox extends SelectBox { div = dom.append(container, $('.monaco-inputbox-container')); layout(); - const renderOptions: RenderOptions = { + const renderOptions: FormattedTextRenderOptions = { inline: true, className: 'monaco-inputbox-message' }; diff --git a/src/sql/base/browser/ui/selectBox/selectBox.ts b/src/sql/base/browser/ui/selectBox/selectBox.ts index 9a637af19a..2fb8f58cac 100644 --- a/src/sql/base/browser/ui/selectBox/selectBox.ts +++ b/src/sql/base/browser/ui/selectBox/selectBox.ts @@ -9,10 +9,10 @@ import { SelectBox as vsSelectBox, ISelectBoxStyles as vsISelectBoxStyles, ISele import { Color } from 'vs/base/common/color'; import { IContextViewProvider, AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import * as dom from 'vs/base/browser/dom'; -import { RenderOptions, renderFormattedText, renderText } from 'vs/base/browser/htmlContentRenderer'; import { IMessage, MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; import * as aria from 'vs/base/browser/ui/aria/aria'; import * as nls from 'vs/nls'; +import { renderFormattedText, renderText, FormattedTextRenderOptions } from 'vs/base/browser/formattedTextRenderer'; const $ = dom.$; @@ -209,7 +209,7 @@ export class SelectBox extends vsSelectBox { div = dom.append(container, $('.monaco-inputbox-container')); layout(); - const renderOptions: RenderOptions = { + const renderOptions: FormattedTextRenderOptions = { inline: true, className: 'monaco-inputbox-message' }; diff --git a/src/sql/workbench/browser/parts/views/customView.ts b/src/sql/workbench/browser/parts/views/customView.ts index da2ff41961..99be7ab34c 100644 --- a/src/sql/workbench/browser/parts/views/customView.ts +++ b/src/sql/workbench/browser/parts/views/customView.ts @@ -34,7 +34,7 @@ import { timeout } from 'vs/base/common/async'; import { editorFindMatchHighlight, editorFindMatchHighlightBorder, textLinkForeground, textCodeBlockBackground, focusBorder } from 'vs/platform/theme/common/colorRegistry'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { isString } from 'vs/base/common/types'; -import { renderMarkdown, RenderOptions } from 'vs/base/browser/htmlContentRenderer'; +import { renderMarkdown, MarkdownRenderOptions } from 'vs/base/browser/markdownRenderer'; import { onUnexpectedError } from 'vs/base/common/errors'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IMarkdownRenderResult } from 'vs/editor/contrib/markdown/markdownRenderer'; @@ -934,7 +934,7 @@ class MarkdownRenderer { ) { } - private getOptions(disposeables: DisposableStore): RenderOptions { + private getOptions(disposeables: DisposableStore): MarkdownRenderOptions { return { actionHandler: { callback: (content) => { diff --git a/src/sql/workbench/parts/notebook/electron-browser/outputs/notebookMarkdown.ts b/src/sql/workbench/parts/notebook/electron-browser/outputs/notebookMarkdown.ts index d2de8f9d4c..5358c099a7 100644 --- a/src/sql/workbench/parts/notebook/electron-browser/outputs/notebookMarkdown.ts +++ b/src/sql/workbench/parts/notebook/electron-browser/outputs/notebookMarkdown.ts @@ -7,12 +7,12 @@ import * as fs from 'fs'; import { URI } from 'vs/base/common/uri'; -import { RenderOptions } from 'vs/base/browser/htmlContentRenderer'; import { IMarkdownString, removeMarkdownEscapes } from 'vs/base/common/htmlContent'; import { IMarkdownRenderResult } from 'vs/editor/contrib/markdown/markdownRenderer'; import * as marked from 'vs/base/common/marked/marked'; import { defaultGenerator } from 'vs/base/common/idGenerator'; import { revive } from 'vs/base/common/marshalling'; +import { MarkdownRenderOptions } from 'vs/base/browser/markdownRenderer'; // Based off of HtmlContentRenderer export class NotebookMarkdownRenderer { @@ -30,7 +30,7 @@ export class NotebookMarkdownRenderer { }; } - createElement(options: RenderOptions): HTMLElement { + createElement(options: MarkdownRenderOptions): HTMLElement { const tagName = options.inline ? 'span' : 'div'; const element = document.createElement(tagName); if (options.className) { @@ -52,7 +52,7 @@ export class NotebookMarkdownRenderer { * respects the trusted state of a notebook, and allows command links to * be clickable. */ - renderMarkdown(markdown: IMarkdownString, options: RenderOptions = {}): HTMLElement { + renderMarkdown(markdown: IMarkdownString, options: MarkdownRenderOptions = {}): HTMLElement { const element = this.createElement(options); // signal to code-block render that the element has been created diff --git a/src/vs/base/browser/formattedTextRenderer.ts b/src/vs/base/browser/formattedTextRenderer.ts new file mode 100644 index 0000000000..4d46725f78 --- /dev/null +++ b/src/vs/base/browser/formattedTextRenderer.ts @@ -0,0 +1,220 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as DOM from 'vs/base/browser/dom'; +import { IMouseEvent } from 'vs/base/browser/mouseEvent'; +import { DisposableStore } from 'vs/base/common/lifecycle'; + +export interface IContentActionHandler { + callback: (content: string, event?: IMouseEvent) => void; + readonly disposeables: DisposableStore; +} + +export interface FormattedTextRenderOptions { + readonly className?: string; + readonly inline?: boolean; + readonly actionHandler?: IContentActionHandler; +} + +export function renderText(text: string, options: FormattedTextRenderOptions = {}): HTMLElement { + const element = createElement(options); + element.textContent = text; + return element; +} + +export function renderFormattedText(formattedText: string, options: FormattedTextRenderOptions = {}): HTMLElement { + const element = createElement(options); + _renderFormattedText(element, parseFormattedText(formattedText), options.actionHandler); + return element; +} + +export function createElement(options: FormattedTextRenderOptions): HTMLElement { + const tagName = options.inline ? 'span' : 'div'; + const element = document.createElement(tagName); + if (options.className) { + element.className = options.className; + } + return element; +} + +class StringStream { + private source: string; + private index: number; + + constructor(source: string) { + this.source = source; + this.index = 0; + } + + public eos(): boolean { + return this.index >= this.source.length; + } + + public next(): string { + const next = this.peek(); + this.advance(); + return next; + } + + public peek(): string { + return this.source[this.index]; + } + + public advance(): void { + this.index++; + } +} + +const enum FormatType { + Invalid, + Root, + Text, + Bold, + Italics, + Action, + ActionClose, + NewLine +} + +interface IFormatParseTree { + type: FormatType; + content?: string; + index?: number; + children?: IFormatParseTree[]; +} + +function _renderFormattedText(element: Node, treeNode: IFormatParseTree, actionHandler?: IContentActionHandler) { + let child: Node | undefined; + + if (treeNode.type === FormatType.Text) { + child = document.createTextNode(treeNode.content || ''); + } else if (treeNode.type === FormatType.Bold) { + child = document.createElement('b'); + } else if (treeNode.type === FormatType.Italics) { + child = document.createElement('i'); + } else if (treeNode.type === FormatType.Action && actionHandler) { + const a = document.createElement('a'); + a.href = '#'; + actionHandler.disposeables.add(DOM.addStandardDisposableListener(a, 'click', (event) => { + actionHandler.callback(String(treeNode.index), event); + })); + + child = a; + } else if (treeNode.type === FormatType.NewLine) { + child = document.createElement('br'); + } else if (treeNode.type === FormatType.Root) { + child = element; + } + + if (child && element !== child) { + element.appendChild(child); + } + + if (child && Array.isArray(treeNode.children)) { + treeNode.children.forEach((nodeChild) => { + _renderFormattedText(child!, nodeChild, actionHandler); + }); + } +} + +function parseFormattedText(content: string): IFormatParseTree { + + const root: IFormatParseTree = { + type: FormatType.Root, + children: [] + }; + + let actionViewItemIndex = 0; + let current = root; + const stack: IFormatParseTree[] = []; + const stream = new StringStream(content); + + while (!stream.eos()) { + let next = stream.next(); + + const isEscapedFormatType = (next === '\\' && formatTagType(stream.peek()) !== FormatType.Invalid); + if (isEscapedFormatType) { + next = stream.next(); // unread the backslash if it escapes a format tag type + } + + if (!isEscapedFormatType && isFormatTag(next) && next === stream.peek()) { + stream.advance(); + + if (current.type === FormatType.Text) { + current = stack.pop()!; + } + + const type = formatTagType(next); + if (current.type === type || (current.type === FormatType.Action && type === FormatType.ActionClose)) { + current = stack.pop()!; + } else { + const newCurrent: IFormatParseTree = { + type: type, + children: [] + }; + + if (type === FormatType.Action) { + newCurrent.index = actionViewItemIndex; + actionViewItemIndex++; + } + + current.children!.push(newCurrent); + stack.push(current); + current = newCurrent; + } + } else if (next === '\n') { + if (current.type === FormatType.Text) { + current = stack.pop()!; + } + + current.children!.push({ + type: FormatType.NewLine + }); + + } else { + if (current.type !== FormatType.Text) { + const textCurrent: IFormatParseTree = { + type: FormatType.Text, + content: next + }; + current.children!.push(textCurrent); + stack.push(current); + current = textCurrent; + + } else { + current.content += next; + } + } + } + + if (current.type === FormatType.Text) { + current = stack.pop()!; + } + + if (stack.length) { + // incorrectly formatted string literal + } + + return root; +} + +function isFormatTag(char: string): boolean { + return formatTagType(char) !== FormatType.Invalid; +} + +function formatTagType(char: string): FormatType { + switch (char) { + case '*': + return FormatType.Bold; + case '_': + return FormatType.Italics; + case '[': + return FormatType.Action; + case ']': + return FormatType.ActionClose; + default: + return FormatType.Invalid; + } +} diff --git a/src/vs/base/browser/htmlContentRenderer.ts b/src/vs/base/browser/markdownRenderer.ts similarity index 50% rename from src/vs/base/browser/htmlContentRenderer.ts rename to src/vs/base/browser/markdownRenderer.ts index 631038c5b5..2878142f0b 100644 --- a/src/vs/base/browser/htmlContentRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -4,55 +4,25 @@ *--------------------------------------------------------------------------------------------*/ import * as DOM from 'vs/base/browser/dom'; -import { defaultGenerator } from 'vs/base/common/idGenerator'; -import { escape } from 'vs/base/common/strings'; -import { removeMarkdownEscapes, IMarkdownString, parseHrefAndDimensions } from 'vs/base/common/htmlContent'; -import * as marked from 'vs/base/common/marked/marked'; -import { IMouseEvent } from 'vs/base/browser/mouseEvent'; -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { createElement, FormattedTextRenderOptions } from 'vs/base/browser/formattedTextRenderer'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { URI } from 'vs/base/common/uri'; +import { IMarkdownString, parseHrefAndDimensions, removeMarkdownEscapes } from 'vs/base/common/htmlContent'; +import { defaultGenerator } from 'vs/base/common/idGenerator'; +import * as marked from 'vs/base/common/marked/marked'; import { parse } from 'vs/base/common/marshalling'; import { cloneAndChange } from 'vs/base/common/objects'; +import { escape } from 'vs/base/common/strings'; +import { URI } from 'vs/base/common/uri'; -export interface IContentActionHandler { - callback: (content: string, event?: IMouseEvent) => void; - readonly disposeables: DisposableStore; -} - -export interface RenderOptions { - className?: string; - inline?: boolean; - actionHandler?: IContentActionHandler; +export interface MarkdownRenderOptions extends FormattedTextRenderOptions { codeBlockRenderer?: (modeId: string, value: string) => Promise; codeBlockRenderCallback?: () => void; } -function createElement(options: RenderOptions): HTMLElement { - const tagName = options.inline ? 'span' : 'div'; - const element = document.createElement(tagName); - if (options.className) { - element.className = options.className; - } - return element; -} - -export function renderText(text: string, options: RenderOptions = {}): HTMLElement { - const element = createElement(options); - element.textContent = text; - return element; -} - -export function renderFormattedText(formattedText: string, options: RenderOptions = {}): HTMLElement { - const element = createElement(options); - _renderFormattedText(element, parseFormattedText(formattedText), options.actionHandler); - return element; -} - /** * Create html nodes for the given content element. */ -export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions = {}): HTMLElement { +export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRenderOptions = {}): HTMLElement { const element = createElement(options); const _uriMassage = function (part: string): string { @@ -219,190 +189,3 @@ export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions return element; } - -// --- formatted string parsing - -class StringStream { - private source: string; - private index: number; - - constructor(source: string) { - this.source = source; - this.index = 0; - } - - public eos(): boolean { - return this.index >= this.source.length; - } - - public next(): string { - const next = this.peek(); - this.advance(); - return next; - } - - public peek(): string { - return this.source[this.index]; - } - - public advance(): void { - this.index++; - } -} - -const enum FormatType { - Invalid, - Root, - Text, - Bold, - Italics, - Action, - ActionClose, - NewLine -} - -interface IFormatParseTree { - type: FormatType; - content?: string; - index?: number; - children?: IFormatParseTree[]; -} - -function _renderFormattedText(element: Node, treeNode: IFormatParseTree, actionHandler?: IContentActionHandler) { - let child: Node | undefined; - - if (treeNode.type === FormatType.Text) { - child = document.createTextNode(treeNode.content || ''); - } - else if (treeNode.type === FormatType.Bold) { - child = document.createElement('b'); - } - else if (treeNode.type === FormatType.Italics) { - child = document.createElement('i'); - } - else if (treeNode.type === FormatType.Action && actionHandler) { - const a = document.createElement('a'); - a.href = '#'; - actionHandler.disposeables.add(DOM.addStandardDisposableListener(a, 'click', (event) => { - actionHandler.callback(String(treeNode.index), event); - })); - - child = a; - } - else if (treeNode.type === FormatType.NewLine) { - child = document.createElement('br'); - } - else if (treeNode.type === FormatType.Root) { - child = element; - } - - if (child && element !== child) { - element.appendChild(child); - } - - if (child && Array.isArray(treeNode.children)) { - treeNode.children.forEach((nodeChild) => { - _renderFormattedText(child!, nodeChild, actionHandler); - }); - } -} - -function parseFormattedText(content: string): IFormatParseTree { - - const root: IFormatParseTree = { - type: FormatType.Root, - children: [] - }; - - let actionViewItemIndex = 0; - let current = root; - const stack: IFormatParseTree[] = []; - const stream = new StringStream(content); - - while (!stream.eos()) { - let next = stream.next(); - - const isEscapedFormatType = (next === '\\' && formatTagType(stream.peek()) !== FormatType.Invalid); - if (isEscapedFormatType) { - next = stream.next(); // unread the backslash if it escapes a format tag type - } - - if (!isEscapedFormatType && isFormatTag(next) && next === stream.peek()) { - stream.advance(); - - if (current.type === FormatType.Text) { - current = stack.pop()!; - } - - const type = formatTagType(next); - if (current.type === type || (current.type === FormatType.Action && type === FormatType.ActionClose)) { - current = stack.pop()!; - } else { - const newCurrent: IFormatParseTree = { - type: type, - children: [] - }; - - if (type === FormatType.Action) { - newCurrent.index = actionViewItemIndex; - actionViewItemIndex++; - } - - current.children!.push(newCurrent); - stack.push(current); - current = newCurrent; - } - } else if (next === '\n') { - if (current.type === FormatType.Text) { - current = stack.pop()!; - } - - current.children!.push({ - type: FormatType.NewLine - }); - - } else { - if (current.type !== FormatType.Text) { - const textCurrent: IFormatParseTree = { - type: FormatType.Text, - content: next - }; - current.children!.push(textCurrent); - stack.push(current); - current = textCurrent; - - } else { - current.content += next; - } - } - } - - if (current.type === FormatType.Text) { - current = stack.pop()!; - } - - if (stack.length) { - // incorrectly formatted string literal - } - - return root; -} - -function isFormatTag(char: string): boolean { - return formatTagType(char) !== FormatType.Invalid; -} - -function formatTagType(char: string): FormatType { - switch (char) { - case '*': - return FormatType.Bold; - case '_': - return FormatType.Italics; - case '[': - return FormatType.Action; - case ']': - return FormatType.ActionClose; - default: - return FormatType.Invalid; - } -} diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index e3d30c3ad5..b78389a77b 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -8,7 +8,8 @@ import 'vs/css!./inputBox'; import * as nls from 'vs/nls'; import * as Bal from 'vs/base/browser/browser'; import * as dom from 'vs/base/browser/dom'; -import { RenderOptions, renderFormattedText, renderText } from 'vs/base/browser/htmlContentRenderer'; +import { MarkdownRenderOptions } from 'vs/base/browser/markdownRenderer'; +import { renderFormattedText, renderText } from 'vs/base/browser/formattedTextRenderer'; import * as aria from 'vs/base/browser/ui/aria/aria'; import { IAction } from 'vs/base/common/actions'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; @@ -472,7 +473,7 @@ export class InputBox extends Widget { div = dom.append(container, $('.monaco-inputbox-container')); layout(); - const renderOptions: RenderOptions = { + const renderOptions: MarkdownRenderOptions = { inline: true, className: 'monaco-inputbox-message' }; diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index ae7f5aa370..936d83f9df 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -18,7 +18,7 @@ import { domEvent } from 'vs/base/browser/event'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { ISelectBoxDelegate, ISelectOptionItem, ISelectBoxOptions, ISelectBoxStyles, ISelectData } from 'vs/base/browser/ui/selectBox/selectBox'; import { isMacintosh } from 'vs/base/common/platform'; -import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer'; +import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; // {{SQL CARBON EDIT}} import color import { Color } from 'vs/base/common/color'; diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index 058a2f59ee..9305b25cae 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -729,3 +729,18 @@ export function getNLines(str: string, n = 1): string { str.substr(0, idx) : str; } + +/** + * Produces 'a'-'z', followed by 'A'-'Z'... followed by 'a'-'z', etc. + */ +export function singleLetterHash(n: number): string { + const LETTERS_CNT = (CharCode.Z - CharCode.A + 1); + + n = n % (2 * LETTERS_CNT); + + if (n < LETTERS_CNT) { + return String.fromCharCode(CharCode.a + n); + } + + return String.fromCharCode(CharCode.A + n - LETTERS_CNT); +} diff --git a/src/vs/base/test/browser/htmlContent.test.ts b/src/vs/base/test/browser/formattedTextRenderer.test.ts similarity index 65% rename from src/vs/base/test/browser/htmlContent.test.ts rename to src/vs/base/test/browser/formattedTextRenderer.test.ts index 707e1bd5e2..baededd138 100644 --- a/src/vs/base/test/browser/htmlContent.test.ts +++ b/src/vs/base/test/browser/formattedTextRenderer.test.ts @@ -2,12 +2,12 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; -import * as marked from 'vs/base/common/marked/marked'; -import { renderMarkdown, renderText, renderFormattedText } from 'vs/base/browser/htmlContentRenderer'; +import { renderText, renderFormattedText } from 'vs/base/browser/formattedTextRenderer'; import { DisposableStore } from 'vs/base/common/lifecycle'; -suite('HtmlContent', () => { +suite('FormattedTextRenderer', () => { const store = new DisposableStore(); setup(() => { @@ -101,36 +101,4 @@ suite('HtmlContent', () => { assert.strictEqual(result.children.length, 0); assert.strictEqual(result.innerHTML, '**bold**'); }); - test('image rendering conforms to default', () => { - const markdown = { value: `![image](someimageurl 'caption')` }; - const result: HTMLElement = renderMarkdown(markdown); - const renderer = new marked.Renderer(); - const imageFromMarked = marked(markdown.value, { - sanitize: true, - renderer - }).trim(); - assert.strictEqual(result.innerHTML, imageFromMarked); - }); - test('image rendering conforms to default without title', () => { - const markdown = { value: `![image](someimageurl)` }; - const result: HTMLElement = renderMarkdown(markdown); - const renderer = new marked.Renderer(); - const imageFromMarked = marked(markdown.value, { - sanitize: true, - renderer - }).trim(); - assert.strictEqual(result.innerHTML, imageFromMarked); - }); - test('image width from title params', () => { - let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|width=100 'caption')` }); - assert.strictEqual(result.innerHTML, `

image

`); - }); - test('image height from title params', () => { - let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=100 'caption')` }); - assert.strictEqual(result.innerHTML, `

image

`); - }); - test('image width and height from title params', () => { - let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=200,width=100 'caption')` }); - assert.strictEqual(result.innerHTML, `

image

`); - }); }); diff --git a/src/vs/base/test/browser/markdownRenderer.test.ts b/src/vs/base/test/browser/markdownRenderer.test.ts new file mode 100644 index 0000000000..8f48b0d690 --- /dev/null +++ b/src/vs/base/test/browser/markdownRenderer.test.ts @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import * as marked from 'vs/base/common/marked/marked'; +import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; + +suite('MarkdownRenderer', () => { + test('image rendering conforms to default', () => { + const markdown = { value: `![image](someimageurl 'caption')` }; + const result: HTMLElement = renderMarkdown(markdown); + const renderer = new marked.Renderer(); + const imageFromMarked = marked(markdown.value, { + sanitize: true, + renderer + }).trim(); + assert.strictEqual(result.innerHTML, imageFromMarked); + }); + + test('image rendering conforms to default without title', () => { + const markdown = { value: `![image](someimageurl)` }; + const result: HTMLElement = renderMarkdown(markdown); + const renderer = new marked.Renderer(); + const imageFromMarked = marked(markdown.value, { + sanitize: true, + renderer + }).trim(); + assert.strictEqual(result.innerHTML, imageFromMarked); + }); + + test('image width from title params', () => { + let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|width=100 'caption')` }); + assert.strictEqual(result.innerHTML, `

image

`); + }); + + test('image height from title params', () => { + let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=100 'caption')` }); + assert.strictEqual(result.innerHTML, `

image

`); + }); + + test('image width and height from title params', () => { + let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=200,width=100 'caption')` }); + assert.strictEqual(result.innerHTML, `

image

`); + }); +}); diff --git a/src/vs/base/worker/defaultWorkerFactory.ts b/src/vs/base/worker/defaultWorkerFactory.ts index 290b7a29f5..98f42104ce 100644 --- a/src/vs/base/worker/defaultWorkerFactory.ts +++ b/src/vs/base/worker/defaultWorkerFactory.ts @@ -19,26 +19,31 @@ function getWorker(workerId: string, label: string): Worker | Promise { // ESM-comment-begin if (typeof require === 'function') { // check if the JS lives on a different origin - const workerMain = require.toUrl('./' + workerId); - if (/^(http:)|(https:)|(file:)/.test(workerMain)) { - const currentUrl = String(window.location); - const currentOrigin = currentUrl.substr(0, currentUrl.length - window.location.hash.length - window.location.search.length - window.location.pathname.length); - if (workerMain.substring(0, currentOrigin.length) !== currentOrigin) { - // this is the cross-origin case - // i.e. the webpage is running at a different origin than where the scripts are loaded from - const workerBaseUrl = workerMain.substr(0, workerMain.length - 'vs/base/worker/workerMain.js'.length); - const js = `/*${label}*/self.MonacoEnvironment={baseUrl: '${workerBaseUrl}'};importScripts('${workerMain}');/*${label}*/`; - const url = `data:text/javascript;charset=utf-8,${encodeURIComponent(js)}`; - return new Worker(url); - } - } - return new Worker(workerMain + '#' + label); + const workerUrl = getWorkerBootstrapUrl(workerMain, label); + return new Worker(workerUrl, { name: label }); } // ESM-comment-end throw new Error(`You must define a function MonacoEnvironment.getWorkerUrl or MonacoEnvironment.getWorker`); } +export function getWorkerBootstrapUrl(scriptPath: string, label: string): string { + if (/^(http:)|(https:)|(file:)/.test(scriptPath)) { + const currentUrl = String(window.location); + const currentOrigin = currentUrl.substr(0, currentUrl.length - window.location.hash.length - window.location.search.length - window.location.pathname.length); + if (scriptPath.substring(0, currentOrigin.length) !== currentOrigin) { + // this is the cross-origin case + // i.e. the webpage is running at a different origin than where the scripts are loaded from + const myPath = 'vs/base/worker/defaultWorkerFactory.js'; + const workerBaseUrl = require.toUrl(myPath).slice(0, -myPath.length); + const js = `/*${label}*/self.MonacoEnvironment={baseUrl: '${workerBaseUrl}'};importScripts('${scriptPath}');/*${label}*/`; + const url = `data:text/javascript;charset=utf-8,${encodeURIComponent(js)}`; + return url; + } + } + return scriptPath + '#' + label; +} + function isPromiseLike(obj: any): obj is PromiseLike { if (typeof obj.then === 'function') { return true; diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index a77e6a61eb..b9cac66a00 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -7,7 +7,7 @@ import { app, ipcMain as ipc, systemPreferences, shell, Event, contentTracing, p import { IProcessEnvironment, isWindows, isMacintosh } from 'vs/base/common/platform'; import { WindowsManager } from 'vs/code/electron-main/windows'; import { IWindowsService, OpenContext, ActiveWindowManager, IURIToOpen } from 'vs/platform/windows/common/windows'; -import { WindowsChannel } from 'vs/platform/windows/node/windowsIpc'; +import { WindowsChannel } from 'vs/platform/windows/common/windowsIpc'; import { WindowsService } from 'vs/platform/windows/electron-main/windowsService'; import { ILifecycleService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; import { getShellEnvironment } from 'vs/code/node/shellEnv'; diff --git a/src/vs/editor/browser/controller/mouseHandler.ts b/src/vs/editor/browser/controller/mouseHandler.ts index 7871f66421..e9696bbe29 100644 --- a/src/vs/editor/browser/controller/mouseHandler.ts +++ b/src/vs/editor/browser/controller/mouseHandler.ts @@ -49,7 +49,7 @@ export interface IPointerHandlerHelper { */ getLastViewCursorsRenderData(): IViewCursorRenderData[]; - shouldSuppressMouseDownOnViewZone(viewZoneId: number): boolean; + shouldSuppressMouseDownOnViewZone(viewZoneId: string): boolean; shouldSuppressMouseDownOnWidget(widgetId: string): boolean; /** diff --git a/src/vs/editor/browser/controller/mouseTarget.ts b/src/vs/editor/browser/controller/mouseTarget.ts index 06776f19f0..c681bf6732 100644 --- a/src/vs/editor/browser/controller/mouseTarget.ts +++ b/src/vs/editor/browser/controller/mouseTarget.ts @@ -19,7 +19,7 @@ import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; import { CursorColumns } from 'vs/editor/common/controller/cursorCommon'; export interface IViewZoneData { - viewZoneId: number; + viewZoneId: string; positionBefore: Position | null; positionAfter: Position | null; position: Position; diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index cba6e57afd..7439ec0622 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -83,17 +83,17 @@ export interface IViewZoneChangeAccessor { * @param zone Zone to create * @return A unique identifier to the view zone. */ - addZone(zone: IViewZone): number; + addZone(zone: IViewZone): string; /** * Remove a zone * @param id A unique identifier to the view zone, as returned by the `addZone` call. */ - removeZone(id: number): void; + removeZone(id: string): void; /** * Change a zone's position. * The editor will rescan the `afterLineNumber` and `afterColumn` properties of a view zone. */ - layoutZone(id: number): void; + layoutZone(id: string): void; } /** @@ -399,7 +399,7 @@ export interface ICodeEditor extends editorCommon.IEditor { */ onWillType(listener: (text: string) => void): IDisposable; /** - * An event emitted before interpreting typed characters (on the keyboard). + * An event emitted after interpreting typed characters (on the keyboard). * @event * @internal */ diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index b69abee0ba..d537b9ea9e 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -55,8 +55,7 @@ export class OpenerService implements IOpenerService { if (equalsIgnoreCase(scheme, Schemas.http) || equalsIgnoreCase(scheme, Schemas.https) || equalsIgnoreCase(scheme, Schemas.mailto)) { // open http or default mail application - dom.windowOpenNoOpener(encodeURI(resource.toString(true))); - return Promise.resolve(true); + return this.openExternal(resource); } else if (equalsIgnoreCase(scheme, Schemas.command)) { // run command or bail out if command isn't known @@ -100,4 +99,10 @@ export class OpenerService implements IOpenerService { ).then(() => true); } } + + openExternal(resource: URI): Promise { + dom.windowOpenNoOpener(encodeURI(resource.toString(true))); + + return Promise.resolve(true); + } } diff --git a/src/vs/editor/browser/view/viewImpl.ts b/src/vs/editor/browser/view/viewImpl.ts index 1f5f2286b9..2837bfe182 100644 --- a/src/vs/editor/browser/view/viewImpl.ts +++ b/src/vs/editor/browser/view/viewImpl.ts @@ -248,7 +248,7 @@ export class View extends ViewEventHandler { getLastViewCursorsRenderData: () => { return this.viewCursors.getLastRenderData() || []; }, - shouldSuppressMouseDownOnViewZone: (viewZoneId: number) => { + shouldSuppressMouseDownOnViewZone: (viewZoneId: string) => { return this.viewZones.shouldSuppressMouseDownOnViewZone(viewZoneId); }, shouldSuppressMouseDownOnWidget: (widgetId: string) => { @@ -473,17 +473,17 @@ export class View extends ViewEventHandler { this._renderOnce(() => { const changeAccessor: editorBrowser.IViewZoneChangeAccessor = { - addZone: (zone: editorBrowser.IViewZone): number => { + addZone: (zone: editorBrowser.IViewZone): string => { zonesHaveChanged = true; return this.viewZones.addZone(zone); }, - removeZone: (id: number): void => { + removeZone: (id: string): void => { if (!id) { return; } zonesHaveChanged = this.viewZones.removeZone(id) || zonesHaveChanged; }, - layoutZone: (id: number): void => { + layoutZone: (id: string): void => { if (!id) { return; } diff --git a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts index b86eabea57..143a5a291c 100644 --- a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts +++ b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts @@ -14,7 +14,7 @@ import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { IViewWhitespaceViewportData } from 'vs/editor/common/viewModel/viewModel'; export interface IMyViewZone { - whitespaceId: number; + whitespaceId: string; delegate: IViewZone; isVisible: boolean; domNode: FastDomNode; @@ -74,7 +74,7 @@ export class ViewZones extends ViewPart { const id = keys[i]; const zone = this._zones[id]; const props = this._computeWhitespaceProps(zone.delegate); - if (this._context.viewLayout.changeWhitespace(parseInt(id, 10), props.afterViewLineNumber, props.heightInPx)) { + if (this._context.viewLayout.changeWhitespace(id, props.afterViewLineNumber, props.heightInPx)) { this._safeCallOnComputedHeight(zone.delegate, props.heightInPx); hadAChange = true; } @@ -183,7 +183,7 @@ export class ViewZones extends ViewPart { }; } - public addZone(zone: IViewZone): number { + public addZone(zone: IViewZone): string { const props = this._computeWhitespaceProps(zone); const whitespaceId = this._context.viewLayout.addWhitespace(props.afterViewLineNumber, this._getZoneOrdinal(zone), props.heightInPx, props.minWidthInPx); @@ -200,18 +200,18 @@ export class ViewZones extends ViewPart { myZone.domNode.setPosition('absolute'); myZone.domNode.domNode.style.width = '100%'; myZone.domNode.setDisplay('none'); - myZone.domNode.setAttribute('monaco-view-zone', myZone.whitespaceId.toString()); + myZone.domNode.setAttribute('monaco-view-zone', myZone.whitespaceId); this.domNode.appendChild(myZone.domNode); if (myZone.marginDomNode) { myZone.marginDomNode.setPosition('absolute'); myZone.marginDomNode.domNode.style.width = '100%'; myZone.marginDomNode.setDisplay('none'); - myZone.marginDomNode.setAttribute('monaco-view-zone', myZone.whitespaceId.toString()); + myZone.marginDomNode.setAttribute('monaco-view-zone', myZone.whitespaceId); this.marginDomNode.appendChild(myZone.marginDomNode); } - this._zones[myZone.whitespaceId.toString()] = myZone; + this._zones[myZone.whitespaceId] = myZone; this.setShouldRender(); @@ -219,10 +219,10 @@ export class ViewZones extends ViewPart { return myZone.whitespaceId; } - public removeZone(id: number): boolean { - if (this._zones.hasOwnProperty(id.toString())) { - const zone = this._zones[id.toString()]; - delete this._zones[id.toString()]; + public removeZone(id: string): boolean { + if (this._zones.hasOwnProperty(id)) { + const zone = this._zones[id]; + delete this._zones[id]; this._context.viewLayout.removeWhitespace(zone.whitespaceId); zone.domNode.removeAttribute('monaco-visible-view-zone'); @@ -242,10 +242,10 @@ export class ViewZones extends ViewPart { return false; } - public layoutZone(id: number): boolean { + public layoutZone(id: string): boolean { let changed = false; - if (this._zones.hasOwnProperty(id.toString())) { - const zone = this._zones[id.toString()]; + if (this._zones.hasOwnProperty(id)) { + const zone = this._zones[id]; const props = this._computeWhitespaceProps(zone.delegate); // const newOrdinal = this._getZoneOrdinal(zone.delegate); changed = this._context.viewLayout.changeWhitespace(zone.whitespaceId, props.afterViewLineNumber, props.heightInPx) || changed; @@ -259,9 +259,9 @@ export class ViewZones extends ViewPart { return changed; } - public shouldSuppressMouseDownOnViewZone(id: number): boolean { - if (this._zones.hasOwnProperty(id.toString())) { - const zone = this._zones[id.toString()]; + public shouldSuppressMouseDownOnViewZone(id: string): boolean { + if (this._zones.hasOwnProperty(id)) { + const zone = this._zones[id]; return Boolean(zone.delegate.suppressMouseDown); } return false; @@ -314,7 +314,7 @@ export class ViewZones extends ViewPart { let hasVisibleZone = false; for (let i = 0, len = visibleWhitespaces.length; i < len; i++) { - visibleZones[visibleWhitespaces[i].id.toString()] = visibleWhitespaces[i]; + visibleZones[visibleWhitespaces[i].id] = visibleWhitespaces[i]; hasVisibleZone = true; } diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index 2255370655..131acb5bc1 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -72,7 +72,7 @@ interface IDiffEditorWidgetStyle { } class VisualEditorState { - private _zones: number[]; + private _zones: string[]; private _zonesMap: { [zoneId: string]: boolean; }; private _decorations: string[]; diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index c070748b1e..a918fd0591 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -110,21 +110,6 @@ export function createTextBuffer(value: string | model.ITextBufferFactory, defau let MODEL_ID = 0; -/** - * Produces 'a'-'z', followed by 'A'-'Z'... followed by 'a'-'z', etc. - */ -function singleLetter(result: number): string { - const LETTERS_CNT = (CharCode.Z - CharCode.A + 1); - - result = result % (2 * LETTERS_CNT); - - if (result < LETTERS_CNT) { - return String.fromCharCode(CharCode.a + result); - } - - return String.fromCharCode(CharCode.A + result - LETTERS_CNT); -} - const LIMIT_FIND_COUNT = 999; export const LONG_LINE_BOUNDARY = 10000; @@ -343,7 +328,7 @@ export class TextModel extends Disposable implements model.ITextModel { } }); - this._instanceId = singleLetter(MODEL_ID); + this._instanceId = strings.singleLetterHash(MODEL_ID); this._lastDecorationId = 0; this._decorations = Object.create(null); this._decorationsTree = new DecorationsTrees(); diff --git a/src/vs/editor/common/viewLayout/linesLayout.ts b/src/vs/editor/common/viewLayout/linesLayout.ts index 822be751ea..673c178331 100644 --- a/src/vs/editor/common/viewLayout/linesLayout.ts +++ b/src/vs/editor/common/viewLayout/linesLayout.ts @@ -63,14 +63,14 @@ export class LinesLayout { * @param heightInPx The height of the whitespace, in pixels. * @return An id that can be used later to mutate or delete the whitespace */ - public insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): number { + public insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string { return this._whitespaces.insertWhitespace(afterLineNumber, ordinal, heightInPx, minWidth); } /** * Change properties associated with a certain whitespace. */ - public changeWhitespace(id: number, newAfterLineNumber: number, newHeight: number): boolean { + public changeWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean { return this._whitespaces.changeWhitespace(id, newAfterLineNumber, newHeight); } @@ -80,7 +80,7 @@ export class LinesLayout { * @param id The whitespace to remove * @return Returns true if the whitespace is found and it is removed. */ - public removeWhitespace(id: number): boolean { + public removeWhitespace(id: string): boolean { return this._whitespaces.removeWhitespace(id); } diff --git a/src/vs/editor/common/viewLayout/viewLayout.ts b/src/vs/editor/common/viewLayout/viewLayout.ts index b9e2f8995b..22635b7f4f 100644 --- a/src/vs/editor/common/viewLayout/viewLayout.ts +++ b/src/vs/editor/common/viewLayout/viewLayout.ts @@ -173,13 +173,13 @@ export class ViewLayout extends Disposable implements IViewLayout { // ---- IVerticalLayoutProvider - public addWhitespace(afterLineNumber: number, ordinal: number, height: number, minWidth: number): number { + public addWhitespace(afterLineNumber: number, ordinal: number, height: number, minWidth: number): string { return this._linesLayout.insertWhitespace(afterLineNumber, ordinal, height, minWidth); } - public changeWhitespace(id: number, newAfterLineNumber: number, newHeight: number): boolean { + public changeWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean { return this._linesLayout.changeWhitespace(id, newAfterLineNumber, newHeight); } - public removeWhitespace(id: number): boolean { + public removeWhitespace(id: string): boolean { return this._linesLayout.removeWhitespace(id); } public getVerticalOffsetForLineNumber(lineNumber: number): number { diff --git a/src/vs/editor/common/viewLayout/whitespaceComputer.ts b/src/vs/editor/common/viewLayout/whitespaceComputer.ts index c920ea5a7e..914aaecd5e 100644 --- a/src/vs/editor/common/viewLayout/whitespaceComputer.ts +++ b/src/vs/editor/common/viewLayout/whitespaceComputer.ts @@ -3,8 +3,10 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as strings from 'vs/base/common/strings'; + export interface IEditorWhitespace { - readonly id: number; + readonly id: string; readonly afterLineNumber: number; readonly heightInLines: number; } @@ -15,6 +17,10 @@ export interface IEditorWhitespace { */ export class WhitespaceComputer { + private static INSTANCE_COUNT = 0; + + private readonly _instanceId: string; + /** * heights[i] is the height in pixels for whitespace at index i */ @@ -48,7 +54,7 @@ export class WhitespaceComputer { /** * ids[i] is the whitespace id of whitespace at index i */ - private readonly _ids: number[]; + private readonly _ids: string[]; /** * index at which a whitespace is positioned (inside heights, afterLineNumbers, prefixSum members) @@ -65,6 +71,7 @@ export class WhitespaceComputer { private _minWidth: number; constructor() { + this._instanceId = strings.singleLetterHash(++WhitespaceComputer.INSTANCE_COUNT); this._heights = []; this._minWidths = []; this._ids = []; @@ -113,21 +120,20 @@ export class WhitespaceComputer { * @param heightInPx The height of the whitespace, in pixels. * @return An id that can be used later to mutate or delete the whitespace */ - public insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): number { + public insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string { afterLineNumber = afterLineNumber | 0; ordinal = ordinal | 0; heightInPx = heightInPx | 0; minWidth = minWidth | 0; - let id = (++this._lastWhitespaceId); + let id = this._instanceId + (++this._lastWhitespaceId); let insertionIndex = WhitespaceComputer.findInsertionIndex(this._afterLineNumbers, afterLineNumber, this._ordinals, ordinal); this._insertWhitespaceAtIndex(id, insertionIndex, afterLineNumber, ordinal, heightInPx, minWidth); this._minWidth = -1; /* marker for not being computed */ return id; } - private _insertWhitespaceAtIndex(id: number, insertIndex: number, afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): void { - id = id | 0; + private _insertWhitespaceAtIndex(id: string, insertIndex: number, afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): void { insertIndex = insertIndex | 0; afterLineNumber = afterLineNumber | 0; ordinal = ordinal | 0; @@ -150,15 +156,14 @@ export class WhitespaceComputer { } } - this._whitespaceId2Index[id.toString()] = insertIndex; + this._whitespaceId2Index[id] = insertIndex; this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, insertIndex - 1); } /** * Change properties associated with a certain whitespace. */ - public changeWhitespace(id: number, newAfterLineNumber: number, newHeight: number): boolean { - id = id | 0; + public changeWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean { newAfterLineNumber = newAfterLineNumber | 0; newHeight = newHeight | 0; @@ -175,13 +180,11 @@ export class WhitespaceComputer { * @param newHeightInPx The new height of the whitespace, in pixels * @return Returns true if the whitespace is found and if the new height is different than the old height */ - public changeWhitespaceHeight(id: number, newHeightInPx: number): boolean { - id = id | 0; + public changeWhitespaceHeight(id: string, newHeightInPx: number): boolean { newHeightInPx = newHeightInPx | 0; - let sid = id.toString(); - if (this._whitespaceId2Index.hasOwnProperty(sid)) { - let index = this._whitespaceId2Index[sid]; + if (this._whitespaceId2Index.hasOwnProperty(id)) { + let index = this._whitespaceId2Index[id]; if (this._heights[index] !== newHeightInPx) { this._heights[index] = newHeightInPx; this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, index - 1); @@ -198,13 +201,11 @@ export class WhitespaceComputer { * @param newAfterLineNumber The new line number the whitespace will follow * @return Returns true if the whitespace is found and if the new line number is different than the old line number */ - public changeWhitespaceAfterLineNumber(id: number, newAfterLineNumber: number): boolean { - id = id | 0; + public changeWhitespaceAfterLineNumber(id: string, newAfterLineNumber: number): boolean { newAfterLineNumber = newAfterLineNumber | 0; - let sid = id.toString(); - if (this._whitespaceId2Index.hasOwnProperty(sid)) { - let index = this._whitespaceId2Index[sid]; + if (this._whitespaceId2Index.hasOwnProperty(id)) { + let index = this._whitespaceId2Index[id]; if (this._afterLineNumbers[index] !== newAfterLineNumber) { // `afterLineNumber` changed for this whitespace @@ -236,14 +237,10 @@ export class WhitespaceComputer { * @param id The whitespace to remove * @return Returns true if the whitespace is found and it is removed. */ - public removeWhitespace(id: number): boolean { - id = id | 0; - - let sid = id.toString(); - - if (this._whitespaceId2Index.hasOwnProperty(sid)) { - let index = this._whitespaceId2Index[sid]; - delete this._whitespaceId2Index[sid]; + public removeWhitespace(id: string): boolean { + if (this._whitespaceId2Index.hasOwnProperty(id)) { + let index = this._whitespaceId2Index[id]; + delete this._whitespaceId2Index[id]; this._removeWhitespaceAtIndex(index); this._minWidth = -1; /* marker for not being computed */ return true; @@ -459,7 +456,7 @@ export class WhitespaceComputer { * @param index The index of the whitespace. * @return `id` of whitespace at `index`. */ - public getIdForWhitespaceIndex(index: number): number { + public getIdForWhitespaceIndex(index: number): string { index = index | 0; return this._ids[index]; diff --git a/src/vs/editor/common/viewModel/viewModel.ts b/src/vs/editor/common/viewModel/viewModel.ts index 3b15be4d78..18be24c1eb 100644 --- a/src/vs/editor/common/viewModel/viewModel.ts +++ b/src/vs/editor/common/viewModel/viewModel.ts @@ -17,7 +17,7 @@ import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceCompute import { ITheme } from 'vs/platform/theme/common/themeService'; export interface IViewWhitespaceViewportData { - readonly id: number; + readonly id: string; readonly afterLineNumber: number; readonly verticalOffset: number; readonly height: number; @@ -74,15 +74,15 @@ export interface IViewLayout { * Reserve rendering space. * @return an identifier that can be later used to remove or change the whitespace. */ - addWhitespace(afterLineNumber: number, ordinal: number, height: number, minWidth: number): number; + addWhitespace(afterLineNumber: number, ordinal: number, height: number, minWidth: number): string; /** * Change the properties of a whitespace. */ - changeWhitespace(id: number, newAfterLineNumber: number, newHeight: number): boolean; + changeWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean; /** * Remove rendering space */ - removeWhitespace(id: number): boolean; + removeWhitespace(id: string): boolean; /** * Get the layout information for whitespaces currently in the viewport */ diff --git a/src/vs/editor/contrib/codelens/codelensWidget.ts b/src/vs/editor/contrib/codelens/codelensWidget.ts index d12738caf7..6a95fba8fb 100644 --- a/src/vs/editor/contrib/codelens/codelensWidget.ts +++ b/src/vs/editor/contrib/codelens/codelensWidget.ts @@ -193,7 +193,7 @@ export class CodeLensWidget { private readonly _editor: editorBrowser.ICodeEditor; private readonly _viewZone!: CodeLensViewZone; - private readonly _viewZoneId!: number; + private readonly _viewZoneId!: string; private readonly _contentWidget!: CodeLensContentWidget; private _decorationIds: string[]; private _data: CodeLensItem[]; diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index 553f43a9de..24bc0f88ea 100644 --- a/src/vs/editor/contrib/find/findWidget.ts +++ b/src/vs/editor/contrib/find/findWidget.ts @@ -116,7 +116,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas private readonly _replaceFocusTracker: dom.IFocusTracker; private readonly _replaceInputFocused: IContextKey; private _viewZone?: FindWidgetViewZone; - private _viewZoneId?: number; + private _viewZoneId?: string; private _resizeSash!: Sash; private _resized!: boolean; @@ -224,15 +224,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas if (!this._isVisible) { return; } - if (this._viewZoneId === undefined) { - return; - } - this._codeEditor.changeViewZones((accessor) => { - if (this._viewZoneId) { - accessor.removeZone(this._viewZoneId); - } - this._viewZoneId = undefined; - }); + this._viewZoneId = undefined; })); diff --git a/src/vs/editor/contrib/markdown/markdownRenderer.ts b/src/vs/editor/contrib/markdown/markdownRenderer.ts index a2d4690521..5a94e63fb8 100644 --- a/src/vs/editor/contrib/markdown/markdownRenderer.ts +++ b/src/vs/editor/contrib/markdown/markdownRenderer.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IMarkdownString } from 'vs/base/common/htmlContent'; -import { renderMarkdown, RenderOptions } from 'vs/base/browser/htmlContentRenderer'; +import { renderMarkdown, MarkdownRenderOptions } from 'vs/base/browser/markdownRenderer'; import { IOpenerService, NullOpenerService } from 'vs/platform/opener/common/opener'; import { IModeService } from 'vs/editor/common/services/modeService'; import { URI } from 'vs/base/common/uri'; @@ -33,7 +33,7 @@ export class MarkdownRenderer extends Disposable { super(); } - private getOptions(disposeables: DisposableStore): RenderOptions { + private getOptions(disposeables: DisposableStore): MarkdownRenderOptions { return { codeBlockRenderer: (languageAlias, value) => { // In markdown, diff --git a/src/vs/editor/contrib/snippet/snippetVariables.ts b/src/vs/editor/contrib/snippet/snippetVariables.ts index 7ef44d80ad..91b7d162ec 100644 --- a/src/vs/editor/contrib/snippet/snippetVariables.ts +++ b/src/vs/editor/contrib/snippet/snippetVariables.ts @@ -27,6 +27,7 @@ export const KnownSnippetVariableNames: { [key: string]: true } = Object.freeze( 'CURRENT_DAY_NAME_SHORT': true, 'CURRENT_MONTH_NAME': true, 'CURRENT_MONTH_NAME_SHORT': true, + 'CURRENT_SECONDS_UNIX': true, 'SELECTION': true, 'CLIPBOARD': true, 'TM_SELECTED_TEXT': true, @@ -245,6 +246,8 @@ export class TimeBasedVariableResolver implements VariableResolver { return TimeBasedVariableResolver.monthNames[new Date().getMonth()]; } else if (name === 'CURRENT_MONTH_NAME_SHORT') { return TimeBasedVariableResolver.monthNamesShort[new Date().getMonth()]; + } else if (name === 'CURRENT_SECONDS_UNIX') { + return String(Math.floor(Date.now() / 1000)); } return undefined; diff --git a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts index c67a35d98a..ff3455173e 100644 --- a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts @@ -281,6 +281,7 @@ suite('Snippet Variables Resolver', function () { assertVariableResolve3(resolver, 'CURRENT_DAY_NAME_SHORT'); assertVariableResolve3(resolver, 'CURRENT_MONTH_NAME'); assertVariableResolve3(resolver, 'CURRENT_MONTH_NAME_SHORT'); + assertVariableResolve3(resolver, 'CURRENT_SECONDS_UNIX'); }); test('creating snippet - format-condition doesn\'t work #53617', function () { diff --git a/src/vs/editor/contrib/zoneWidget/zoneWidget.ts b/src/vs/editor/contrib/zoneWidget/zoneWidget.ts index 91e54ed2de..c63c66611d 100644 --- a/src/vs/editor/contrib/zoneWidget/zoneWidget.ts +++ b/src/vs/editor/contrib/zoneWidget/zoneWidget.ts @@ -51,7 +51,7 @@ const WIDGET_ID = 'vs.editor.contrib.zoneWidget'; export class ViewZoneDelegate implements IViewZone { public domNode: HTMLElement; - public id: number = 0; // A valid zone id should be greater than 0 + public id: string = ''; // A valid zone id should be greater than 0 public afterLineNumber: number; public afterColumn: number; public heightInLines: number; diff --git a/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts b/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts index ef85a863ac..a118257414 100644 --- a/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts +++ b/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts @@ -7,7 +7,7 @@ import 'vs/css!./accessibilityHelp'; import * as browser from 'vs/base/browser/browser'; import * as dom from 'vs/base/browser/dom'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; -import { renderFormattedText } from 'vs/base/browser/htmlContentRenderer'; +import { renderFormattedText } from 'vs/base/browser/formattedTextRenderer'; import { alert } from 'vs/base/browser/ui/aria/aria'; import { Widget } from 'vs/base/browser/ui/widget'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 43d11160fb..f32c6bc9d8 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3609,17 +3609,17 @@ declare namespace monaco.editor { * @param zone Zone to create * @return A unique identifier to the view zone. */ - addZone(zone: IViewZone): number; + addZone(zone: IViewZone): string; /** * Remove a zone * @param id A unique identifier to the view zone, as returned by the `addZone` call. */ - removeZone(id: number): void; + removeZone(id: string): void; /** * Change a zone's position. * The editor will rescan the `afterLineNumber` and `afterColumn` properties of a view zone. */ - layoutZone(id: number): void; + layoutZone(id: string): void; } /** diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index 571f0c6513..c5d4278be3 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -392,10 +392,10 @@ export class ExtensionGalleryService implements IExtensionGalleryService { @IProductService private readonly productService: IProductService, @optional(IStorageService) private readonly storageService: IStorageService, ) { - const config = productService.extensionsGallery; + const config = productService.productConfiguration.extensionsGallery; this.extensionsGalleryUrl = config && config.serviceUrl; this.extensionsControlUrl = config && config.controlUrl; - this.commonHeadersPromise = resolveMarketplaceHeaders(productService.version, this.environmentService, this.fileService, this.storageService); + this.commonHeadersPromise = resolveMarketplaceHeaders(productService.productConfiguration.version, this.environmentService, this.fileService, this.storageService); } private api(path = ''): string { @@ -440,7 +440,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService { const versionAsset = rawExtension.versions.filter(v => v.version === version)[0]; if (versionAsset) { const extension = toExtension(rawExtension, versionAsset, 0, query); - if (extension.properties.engine && isEngineValid(extension.properties.engine, this.productService.version)) { + if (extension.properties.engine && isEngineValid(extension.properties.engine, this.productService.productConfiguration.version)) { return extension; } } @@ -788,7 +788,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService { return this.queryGallery(query, CancellationToken.None).then(({ galleryExtensions }) => { if (galleryExtensions.length) { if (compatible) { - return Promise.all(galleryExtensions[0].versions.map(v => this.getEngine(v).then(engine => isEngineValid(engine, this.productService.version) ? v : null))) + return Promise.all(galleryExtensions[0].versions.map(v => this.getEngine(v).then(engine => isEngineValid(engine, this.productService.productConfiguration.version) ? v : null))) .then(versions => versions .filter(v => !!v) .map(v => ({ version: v!.version, date: v!.lastUpdated }))); @@ -874,7 +874,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService { if (!engine) { return null; } - if (isEngineValid(engine, this.productService.version)) { + if (isEngineValid(engine, this.productService.productConfiguration.version)) { return Promise.resolve(version); } } @@ -906,7 +906,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService { const version = versions[0]; return this.getEngine(version) .then(engine => { - if (!isEngineValid(engine, this.productService.version)) { + if (!isEngineValid(engine, this.productService.productConfiguration.version)) { return this.getLastValidExtensionVersionRecursively(extension, versions.slice(1)); } @@ -986,4 +986,4 @@ export async function resolveMarketplaceHeaders(version: string, environmentServ return headers; -} \ No newline at end of file +} diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index a1fc24c8fe..d89410fb46 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -106,7 +106,7 @@ export interface IExtensionContributions { localizations?: ILocalization[]; } -export type ExtensionKind = 'ui' | 'workspace'; +export type ExtensionKind = 'ui' | 'workspace' | 'web'; export function isIExtensionIdentifier(thing: any): thing is IExtensionIdentifier { return thing diff --git a/src/vs/platform/opener/common/opener.ts b/src/vs/platform/opener/common/opener.ts index 1bc294ec2a..e5e67e3543 100644 --- a/src/vs/platform/opener/common/opener.ts +++ b/src/vs/platform/opener/common/opener.ts @@ -9,7 +9,6 @@ import { IDisposable } from 'vs/base/common/lifecycle'; export const IOpenerService = createDecorator('openerService'); - export interface IOpener { open(resource: URI, options?: { openToSide?: boolean }): Promise; } @@ -18,6 +17,9 @@ export interface IOpenerService { _serviceBrand: any; + /** + * Register a participant that can handle the open() call. + */ registerOpener(opener: IOpener): IDisposable; /** @@ -27,10 +29,18 @@ export interface IOpenerService { * @return A promise that resolves when the opening is done. */ open(resource: URI, options?: { openToSide?: boolean }): Promise; + + /** + * Opens a URL externally. + * + * @param url A resource to open externally. + */ + openExternal(resource: URI): Promise; } export const NullOpenerService: IOpenerService = Object.freeze({ _serviceBrand: undefined, registerOpener() { return { dispose() { } }; }, - open() { return Promise.resolve(false); } + open() { return Promise.resolve(false); }, + openExternal() { return Promise.resolve(false); } }); diff --git a/src/vs/platform/product/browser/productService.ts b/src/vs/platform/product/browser/productService.ts index 1a46cd6412..fd2cf7d966 100644 --- a/src/vs/platform/product/browser/productService.ts +++ b/src/vs/platform/product/browser/productService.ts @@ -10,44 +10,18 @@ export class ProductService implements IProductService { _serviceBrand!: ServiceIdentifier; - private readonly productConfiguration: IProductConfiguration | null; + readonly productConfiguration: IProductConfiguration; constructor() { const element = document.getElementById('vscode-remote-product-configuration'); - this.productConfiguration = element ? JSON.parse(element.getAttribute('data-settings')!) : null; + this.productConfiguration = { + ...element ? JSON.parse(element.getAttribute('data-settings')!) : { + version: '1.38.0-unknown', + nameLong: 'Unknown', + extensionAllowedProposedApi: [], + }, ...{ urlProtocol: '', enableTelemetry: false }, + ...{ vscodeVersion: '1.35.0' } // {{SQL CARBON EDIT}} add vscodeversion + }; } - get version(): string { return this.productConfiguration && this.productConfiguration.version ? this.productConfiguration.version : '1.38.0-unknown'; } - - get vscodeVersion(): string { return '1.35.0'; } // {{SQL CARBON EDIT}} add vscodeversion - - get recommendedExtensionsByScenario(): { [area: string]: Array } { return this.productConfiguration ? this.productConfiguration.recommendedExtensionsByScenario : {}; }// {{SQL CARBON EDIT}} add getter - - get commit(): string | undefined { return this.productConfiguration ? this.productConfiguration.commit : undefined; } - - get nameLong(): string { return this.productConfiguration ? this.productConfiguration.nameLong : 'Unknown'; } - - get urlProtocol(): string { return ''; } - - get extensionAllowedProposedApi(): readonly string[] { return this.productConfiguration ? this.productConfiguration.extensionAllowedProposedApi : []; } - - get uiExtensions(): readonly string[] | undefined { return this.productConfiguration ? this.productConfiguration.uiExtensions : undefined; } - - get enableTelemetry(): boolean { return false; } - - get sendASmile(): { reportIssueUrl: string, requestFeatureUrl: string } | undefined { return this.productConfiguration ? this.productConfiguration.sendASmile : undefined; } - - get extensionsGallery() { return this.productConfiguration ? this.productConfiguration.extensionsGallery : undefined; } - - get settingsSearchBuildId(): number | undefined { return this.productConfiguration ? this.productConfiguration.settingsSearchBuildId : undefined; } - - get settingsSearchUrl(): string | undefined { return this.productConfiguration ? this.productConfiguration.settingsSearchUrl : undefined; } - - get experimentsUrl(): string | undefined { return this.productConfiguration ? this.productConfiguration.experimentsUrl : undefined; } - - get extensionKeywords(): { [extension: string]: readonly string[]; } | undefined { return this.productConfiguration ? this.productConfiguration.extensionKeywords : undefined; } - - get extensionAllowedBadgeProviders(): readonly string[] | undefined { return this.productConfiguration ? this.productConfiguration.extensionAllowedBadgeProviders : undefined; } - - get aiConfig() { return this.productConfiguration ? this.productConfiguration.aiConfig : undefined; } } diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts index b2ead4e836..d8eaf3de15 100644 --- a/src/vs/platform/product/common/product.ts +++ b/src/vs/platform/product/common/product.ts @@ -11,40 +11,7 @@ export interface IProductService { _serviceBrand: ServiceIdentifier; - readonly version: string; - readonly vscodeVersion: string; // {{SQL CARBON EDIT}} add vscode version - readonly recommendedExtensionsByScenario: { [area: string]: Array }; // {{SQL CARBON EDIT}} add getter - readonly commit?: string; - readonly date?: string; - - readonly nameLong: string; - readonly urlProtocol: string; - readonly extensionAllowedProposedApi: readonly string[]; - readonly uiExtensions?: readonly string[]; - - readonly enableTelemetry: boolean; - readonly extensionsGallery?: { - readonly serviceUrl: string; - readonly itemUrl: string; - readonly controlUrl: string; - readonly recommendationsUrl: string; - }; - - readonly sendASmile?: { - readonly reportIssueUrl: string; - readonly requestFeatureUrl: string; - }; - - readonly settingsSearchBuildId?: number; - readonly settingsSearchUrl?: string; - - readonly experimentsUrl?: string; - readonly extensionKeywords?: { [extension: string]: readonly string[]; }; - readonly extensionAllowedBadgeProviders?: readonly string[]; - - readonly aiConfig?: { - readonly asimovKey: string; - }; + readonly productConfiguration: IProductConfiguration; } export interface IProductConfiguration { @@ -76,10 +43,12 @@ export interface IProductConfiguration { readonly controlUrl: string; readonly recommendationsUrl: string; }; - extensionTips: { [id: string]: string; }; - recommendedExtensions: string[]; // {{SQL CARBON EDIT}} - recommendedExtensionsByScenario: { [area: string]: Array }; // {{SQL CARBON EDIT}} - extensionImportantTips: { [id: string]: { name: string; pattern: string; isExtensionPack?: boolean }; }; + readonly extensionTips: { [id: string]: string; }; + readonly recommendedExtensions: string[]; // {{SQL CARBON EDIT}} + readonly recommendedExtensionsByScenario: { [area: string]: Array }; // {{SQL CARBON EDIT}} + readonly vscodeVersion: string; // {{SQL CARBON EDIT}} add vscode version + readonly gettingStartedUrl: string; // {SQL CARBON EDIT} + readonly extensionImportantTips: { [id: string]: { name: string; pattern: string; isExtensionPack?: boolean }; }; readonly exeBasedExtensionTips: { [id: string]: IExeBasedExtensionTip; }; readonly extensionKeywords: { [extension: string]: readonly string[]; }; readonly extensionAllowedBadgeProviders: readonly string[]; @@ -100,8 +69,6 @@ export interface IProductConfiguration { }; readonly documentationUrl: string; readonly releaseNotesUrl: string; - readonly gettingStartedUrl: string; // {SQL CARBON EDIT} - readonly vscodeVersion: string; // {SQL CARBON EDIT} readonly keyboardShortcutsUrlMac: string; readonly keyboardShortcutsUrlLinux: string; readonly keyboardShortcutsUrlWin: string; diff --git a/src/vs/platform/product/node/productService.ts b/src/vs/platform/product/node/productService.ts index 6e701ae2dd..9721daf86c 100644 --- a/src/vs/platform/product/node/productService.ts +++ b/src/vs/platform/product/node/productService.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService, IProductConfiguration } from 'vs/platform/product/common/product'; import product from 'vs/platform/product/node/product'; import pkg from 'vs/platform/product/node/package'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; @@ -12,35 +12,12 @@ export class ProductService implements IProductService { _serviceBrand!: ServiceIdentifier; - get version(): string { return pkg.version; } + readonly productConfiguration: IProductConfiguration; - get vscodeVersion(): string { return '1.35.0'; } // {{SQL CARBON EDIT}} add vscodeversion + constructor() { + this.productConfiguration = { + ...product, ...{ version: pkg.version }, ...{ vscodeVersion: '1.35.0' } // {{SQL CARBON EDIT}} add vscodeversion} + }; + } - get recommendedExtensionsByScenario(): { [area: string]: Array } { return product.recommendedExtensionsByScenario; }// {{SQL CARBON EDIT}} add getter - - get commit(): string | undefined { return product.commit; } - - get nameLong(): string { return product.nameLong; } - - get urlProtocol(): string { return product.urlProtocol; } - - get extensionAllowedProposedApi(): readonly string[] { return product.extensionAllowedProposedApi; } - - get uiExtensions(): readonly string[] | undefined { return product.uiExtensions; } - - get enableTelemetry(): boolean { return product.enableTelemetry; } - - get sendASmile(): { reportIssueUrl: string, requestFeatureUrl: string } { return product.sendASmile; } - - get extensionsGallery() { return product.extensionsGallery; } - - get settingsSearchBuildId(): number | undefined { return product.settingsSearchBuildId; } - - get settingsSearchUrl(): string | undefined { return product.settingsSearchUrl; } - - get experimentsUrl(): string | undefined { return product.experimentsUrl; } - - get extensionKeywords(): { [extension: string]: readonly string[]; } | undefined { return product.extensionKeywords; } - - get extensionAllowedBadgeProviders(): readonly string[] | undefined { return product.extensionAllowedBadgeProviders; } } diff --git a/src/vs/platform/storage/browser/storageService.ts b/src/vs/platform/storage/browser/storageService.ts index 20801da3e5..560e93dca5 100644 --- a/src/vs/platform/storage/browser/storageService.ts +++ b/src/vs/platform/storage/browser/storageService.ts @@ -144,8 +144,10 @@ export class BrowserStorageService extends Disposable implements IStorageService // Signal as event so that clients can still store data this._onWillSaveState.fire({ reason: WillSaveStateReason.SHUTDOWN }); - // Close DBs - this.globalStorage.close(); - this.workspaceStorage.close(); + // 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. } } diff --git a/src/vs/platform/windows/node/windowsIpc.ts b/src/vs/platform/windows/common/windowsIpc.ts similarity index 100% rename from src/vs/platform/windows/node/windowsIpc.ts rename to src/vs/platform/windows/common/windowsIpc.ts diff --git a/src/vs/platform/windows/electron-browser/windowsService.ts b/src/vs/platform/windows/electron-browser/windowsService.ts index c53d69a0ae..1a910cd8c9 100644 --- a/src/vs/platform/windows/electron-browser/windowsService.ts +++ b/src/vs/platform/windows/electron-browser/windowsService.ts @@ -13,17 +13,14 @@ import { URI } from 'vs/base/common/uri'; import { ParsedArgs } from 'vs/platform/environment/common/environment'; import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; import { IProcessEnvironment } from 'vs/base/common/platform'; +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; export class WindowsService implements IWindowsService { - _serviceBrand: any; + _serviceBrand!: ServiceIdentifier; private channel: IChannel; - constructor(@IMainProcessService mainProcessService: IMainProcessService) { - this.channel = mainProcessService.getChannel('windows'); - } - get onWindowOpen(): Event { return this.channel.listen('onWindowOpen'); } get onWindowFocus(): Event { return this.channel.listen('onWindowFocus'); } get onWindowBlur(): Event { return this.channel.listen('onWindowBlur'); } @@ -31,6 +28,10 @@ export class WindowsService implements IWindowsService { get onWindowUnmaximize(): Event { return this.channel.listen('onWindowUnmaximize'); } get onRecentlyOpenedChange(): Event { return this.channel.listen('onRecentlyOpenedChange'); } + constructor(@IMainProcessService mainProcessService: IMainProcessService) { + this.channel = mainProcessService.getChannel('windows'); + } + pickFileFolderAndOpen(options: INativeOpenDialogOptions): Promise { return this.channel.call('pickFileFolderAndOpen', options); } diff --git a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts index b8e38f8f8a..ac02f14f9a 100644 --- a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts +++ b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts @@ -21,7 +21,7 @@ class EditorWebviewZone implements IViewZone { readonly afterColumn: number; readonly heightInLines: number; - private _id?: number; + private _id?: string; // suppressMouseDown?: boolean | undefined; // heightInPx?: number | undefined; // minWidthInPx?: number | undefined; diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index d181d029dd..c718af569e 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -327,7 +327,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews if (MainThreadWebviews.standardSupportedLinkSchemes.has(link.scheme)) { return true; } - if (this._productService.urlProtocol === link.scheme) { + if (this._productService.productConfiguration.urlProtocol === link.scheme) { return true; } return !!webview.webview.contentOptions.enableCommandUris && link.scheme === 'command'; diff --git a/src/vs/workbench/api/browser/mainThreadWindow.ts b/src/vs/workbench/api/browser/mainThreadWindow.ts index 9dd58173fc..e473d27452 100644 --- a/src/vs/workbench/api/browser/mainThreadWindow.ts +++ b/src/vs/workbench/api/browser/mainThreadWindow.ts @@ -6,12 +6,13 @@ import { Event } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; +import { IWindowService } from 'vs/platform/windows/common/windows'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { ExtHostContext, ExtHostWindowShape, IExtHostContext, MainContext, MainThreadWindowShape, IOpenUriOptions } from '../common/extHost.protocol'; import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { extractLocalHostUriMetaDataForPortMapping } from 'vs/workbench/contrib/webview/common/portMapping'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; @extHostNamedCustomer(MainContext.MainThreadWindow) export class MainThreadWindow implements MainThreadWindowShape { @@ -23,7 +24,7 @@ export class MainThreadWindow implements MainThreadWindowShape { constructor( extHostContext: IExtHostContext, @IWindowService private readonly windowService: IWindowService, - @IWindowsService private readonly windowsService: IWindowsService, + @IOpenerService private readonly openerService: IOpenerService, @ITunnelService private readonly tunnelService: ITunnelService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService ) { @@ -58,7 +59,7 @@ export class MainThreadWindow implements MainThreadWindowShape { } } - return this.windowsService.openExternal(encodeURI(uri.toString(true))); + return this.openerService.openExternal(uri); } private getOrCreateTunnel(remotePort: number): Promise | undefined { diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 965359c212..077a47ea29 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -19,6 +19,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { VIEWLET_ID as EXPLORER } from 'vs/workbench/contrib/files/common/files'; import { VIEWLET_ID as SCM } from 'vs/workbench/contrib/scm/common/scm'; import { VIEWLET_ID as DEBUG } from 'vs/workbench/contrib/debug/common/debug'; +import { VIEWLET_ID as REMOTE } from 'vs/workbench/contrib/remote/common/remote.contribution'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { URI } from 'vs/base/common/uri'; import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor, ShowViewletAction } from 'vs/workbench/browser/viewlet'; @@ -79,6 +80,7 @@ interface IUserFriendlyViewDescriptor { id: string; name: string; when?: string; + group?: string; } const viewDescriptor: IJSONSchema = { @@ -99,6 +101,27 @@ const viewDescriptor: IJSONSchema = { } }; +const nestableViewDescriptor: IJSONSchema = { + type: 'object', + properties: { + id: { + description: localize('vscode.extension.contributes.view.id', 'Identifier of the view. Use this to register a data provider through `vscode.window.registerTreeDataProviderForView` API. Also to trigger activating your extension by registering `onView:${id}` event to `activationEvents`.'), + type: 'string' + }, + name: { + description: localize('vscode.extension.contributes.view.name', 'The human-readable name of the view. Will be shown'), + type: 'string' + }, + when: { + description: localize('vscode.extension.contributes.view.when', 'Condition which must be true to show this view'), + type: 'string' + }, + group: { + description: localize('vscode.extension.contributes.view.group', 'Nested group in the viewlet'), + type: 'string' + } + } +}; const viewsContribution: IJSONSchema = { description: localize('vscode.extension.contributes.views', "Contributes views to the editor"), type: 'object', @@ -126,6 +149,12 @@ const viewsContribution: IJSONSchema = { type: 'array', items: viewDescriptor, default: [] + }, + 'remote': { + description: localize('views.remote', "Contributes views to Remote container in the Activity bar"), + type: 'array', + items: nestableViewDescriptor, + default: [] } }, additionalProperties: { @@ -376,6 +405,12 @@ class ViewsExtensionHandler implements IWorkbenchContribution { return null; } + const order = ExtensionIdentifier.equals(extension.description.identifier, container.extensionId) + ? index + 1 + : container.orderDelegate + ? container.orderDelegate.getOrder(item.group) + : undefined; + const viewDescriptor = { id: item.id, name: item.name, @@ -384,9 +419,10 @@ class ViewsExtensionHandler implements IWorkbenchContribution { canToggleVisibility: true, collapsed: this.showCollapsed(container), treeView: this.instantiationService.createInstance(CustomTreeView, item.id, item.name, container), - order: ExtensionIdentifier.equals(extension.description.identifier, container.extensionId) ? index + 1 : undefined, + order: order, extensionId: extension.description.identifier, - originalContainerId: entry.key + originalContainerId: entry.key, + group: item.group }; viewIds.push(viewDescriptor.id); @@ -440,6 +476,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { case 'explorer': return this.viewContainersRegistry.get(EXPLORER); case 'debug': return this.viewContainersRegistry.get(DEBUG); case 'scm': return this.viewContainersRegistry.get(SCM); + case 'remote': return this.viewContainersRegistry.get(REMOTE); default: return this.viewContainersRegistry.get(`workbench.view.extension.${value}`); } } diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 3ffbd8b2f6..8836b32bee 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import * as path from 'vs/base/common/path'; -import { originalFSPath } from 'vs/base/common/resources'; +import { originalFSPath, joinPath } from 'vs/base/common/resources'; import { Barrier } from 'vs/base/common/async'; import { dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { TernarySearchTree } from 'vs/base/common/map'; @@ -332,14 +332,14 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup); return Promise.all([ - this._loadCommonJSModule(extensionDescription.main, activationTimesBuilder), + this._loadCommonJSModule(joinPath(extensionDescription.extensionLocation, extensionDescription.main), activationTimesBuilder), this._loadExtensionContext(extensionDescription) ]).then(values => { return AbstractExtHostExtensionService._callActivate(this._logService, extensionDescription.identifier, values[0], values[1], activationTimesBuilder); }); } - protected abstract _loadCommonJSModule(modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise; + protected abstract _loadCommonJSModule(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise; private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise { @@ -536,7 +536,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio let testRunner: ITestRunner | INewTestRunner | undefined; let requireError: Error | undefined; try { - testRunner = await this._loadCommonJSModule(extensionTestsPath, new ExtensionActivationTimesBuilder(false)); + testRunner = await this._loadCommonJSModule(URI.file(extensionTestsPath), new ExtensionActivationTimesBuilder(false)); } catch (error) { requireError = error; } diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index edaa74be2f..3c3d0f010f 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -11,6 +11,9 @@ import { connectProxyResolver } from 'vs/workbench/services/extensions/node/prox import { AbstractExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; import { ExtHostDownloadService } from 'vs/workbench/api/node/extHostDownloadService'; import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer'; +import { URI } from 'vs/base/common/uri'; +import { Schemas } from 'vs/base/common/network'; + import { createAzdataApiFactory, createSqlopsApiFactory } from 'sql/workbench/api/common/sqlExtHost.api.impl'; // {{SQL CARBON EDIT}} use our extension initalizer import { AzdataNodeModuleFactory, SqlopsNodeModuleFactory } from 'sql/workbench/api/node/extHostRequireInterceptor'; // {{SQL CARBON EDIT}} use our extension initalizer @@ -61,12 +64,15 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { }; } - protected _loadCommonJSModule(modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { + protected _loadCommonJSModule(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { + if (module.scheme !== Schemas.file) { + throw new Error(`Cannot load URI: '${module}', must be of file-scheme`); + } let r: T | null = null; activationTimesBuilder.codeLoadingStart(); - this._logService.info(`ExtensionService#loadCommonJSModule ${modulePath}`); + this._logService.info(`ExtensionService#loadCommonJSModule ${module.toString(true)}`); try { - r = require.__$__nodeRequire(modulePath); + r = require.__$__nodeRequire(module.fsPath); } catch (e) { return Promise.reject(e); } finally { diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts index f2667aeea4..0ad4982620 100644 --- a/src/vs/workbench/api/worker/extHostExtensionService.ts +++ b/src/vs/workbench/api/worker/extHostExtensionService.ts @@ -6,13 +6,16 @@ import { createApiFactoryAndRegisterActors, IExtensionApiFactory } from 'vs/workbench/api/common/extHost.api.impl'; import { ExtensionActivationTimesBuilder } from 'vs/workbench/api/common/extHostExtensionActivator'; import { AbstractExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; -import { endsWith } from 'vs/base/common/strings'; +import { endsWith, startsWith } from 'vs/base/common/strings'; import { IExtensionDescription, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration'; import * as vscode from 'vscode'; import { TernarySearchTree } from 'vs/base/common/map'; import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; +import { URI } from 'vs/base/common/uri'; +import { Schemas } from 'vs/base/common/network'; +import { joinPath } from 'vs/base/common/resources'; class ApiInstances { @@ -52,11 +55,8 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { this._apiInstances = new ApiInstances(apiFactory, extensionPath, this._registry, configProvider); } - protected _loadCommonJSModule(modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { + protected _loadCommonJSModule(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { - // make sure modulePath ends with `.js` - const suffix = '.js'; - modulePath = endsWith(modulePath, suffix) ? modulePath : modulePath + suffix; interface FakeCommonJSSelf { module?: object; @@ -69,30 +69,66 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { // FAKE commonjs world that only collects exports const patchSelf: FakeCommonJSSelf = self; - const module = { exports: {} }; - patchSelf.module = module; - patchSelf.exports = module.exports; patchSelf.window = self; // <- that's improper but might help extensions that aren't authored correctly // FAKE require function that only works for the vscode-module - patchSelf.require = (module: string) => { - if (module !== 'vscode') { - throw new Error(`Cannot load module '${module}'`); + const moduleStack: URI[] = []; + patchSelf.require = (mod: string) => { + const parent = moduleStack[moduleStack.length - 1]; + if (mod === 'vscode') { + return this._apiInstances!.get(parent.fsPath); } - return this._apiInstances!.get(modulePath); + if (!startsWith(mod, '.')) { + throw new Error(`Cannot load module '${mod}'`); + } + + const exports = Object.create(null); + patchSelf.module = { exports }; + patchSelf.exports = exports; + + const next = joinPath(parent, '..', ensureSuffix(mod, '.js')); + moduleStack.push(next); + importScripts(asDomUri(next).toString(true)); + moduleStack.pop(); + + return exports; }; try { activationTimesBuilder.codeLoadingStart(); - importScripts(modulePath); + + const exports = Object.create(null); + patchSelf.module = { exports }; + patchSelf.exports = exports; + + module = module.with({ path: ensureSuffix(module.path, '.js') }); + moduleStack.push(module); + + importScripts(asDomUri(module).toString(true)); + moduleStack.pop(); + } finally { activationTimesBuilder.codeLoadingStop(); } - return Promise.resolve(module.exports as T); + return Promise.resolve(exports); } async $setRemoteEnvironment(env: { [key: string]: string | null }): Promise { throw new Error('Not supported'); } } + +// todo@joh this is a copy of `dom.ts#asDomUri` +function asDomUri(uri: URI): 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 URI.parse(window.location.href).with({ path: '/vscode-remote', query: JSON.stringify(uri) }); + } + return uri; +} + +function ensureSuffix(path: string, suffix: string): string { + return endsWith(path, suffix) ? path : path + suffix; +} diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 2a00a80c24..32b29633a6 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -1177,10 +1177,12 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } private createGridDescriptor(): ISerializedGrid { - const width = this.storageService.getNumber(Storage.GRID_WIDTH, StorageScope.GLOBAL, 600); - const height = this.storageService.getNumber(Storage.GRID_HEIGHT, StorageScope.GLOBAL, 400); - const sideBarSize = this.storageService.getNumber(Storage.SIDEBAR_SIZE, StorageScope.GLOBAL, 300); - const panelSize = this.storageService.getNumber(Storage.PANEL_SIZE, StorageScope.GLOBAL, 300); + const workbenchDimensions = getClientArea(this.parent); + const width = this.storageService.getNumber(Storage.GRID_WIDTH, StorageScope.GLOBAL, workbenchDimensions.width); + const height = this.storageService.getNumber(Storage.GRID_HEIGHT, StorageScope.GLOBAL, workbenchDimensions.height); + // At some point, we will not fall back to old keys from legacy layout, but for now, let's migrate the keys + const sideBarSize = this.storageService.getNumber(Storage.SIDEBAR_SIZE, StorageScope.GLOBAL, this.storageService.getNumber('workbench.sidebar.width', StorageScope.GLOBAL, Math.min(workbenchDimensions.width / 4, 300))!); + const panelSize = this.storageService.getNumber(Storage.PANEL_SIZE, StorageScope.GLOBAL, this.storageService.getNumber(this.state.panel.position === Position.BOTTOM ? 'workbench.panel.height' : 'workbench.panel.width', StorageScope.GLOBAL, workbenchDimensions.height / 3)); const titleBarHeight = this.titleBarPartView.minimumHeight; const statusBarHeight = this.statusBarPartView.minimumHeight; diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index 21b70abd80..9babf58813 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -74,7 +74,7 @@ export interface IEditorOpeningEvent extends IEditorIdentifier { * Allows to prevent the opening of an editor by providing a callback * that will be executed instead. By returning another editor promise * it is possible to override the opening with another editor. It is ok - * to return a promise that resolves to NULL to prevent the opening + * to return a promise that resolves to `undefined` to prevent the opening * alltogether. */ prevent(callback: () => undefined | Promise): void; diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index 1788c6e4b2..edffcc7172 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -382,7 +382,19 @@ export class ContributableViewsModel extends Disposable { return 0; } - return (this.getViewOrder(a) - this.getViewOrder(b)) || (a.id < b.id ? -1 : 1); + return (this.getViewOrder(a) - this.getViewOrder(b)) || this.getGroupOrderResult(a, b) || (a.id < b.id ? -1 : 1); + } + + private getGroupOrderResult(a: IViewDescriptor, b: IViewDescriptor) { + if (!a.group || !b.group) { + return 0; + } + + if (a.group === b.group) { + return 0; + } + + return a.group < b.group ? -1 : 1; } private getViewOrder(viewDescriptor: IViewDescriptor): number { diff --git a/src/vs/workbench/browser/web.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts index af235b633b..d6132b940f 100644 --- a/src/vs/workbench/browser/web.simpleservices.ts +++ b/src/vs/workbench/browser/web.simpleservices.ts @@ -22,7 +22,7 @@ import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; import { ITunnelService } from 'vs/platform/remote/common/tunnel'; // tslint:disable-next-line: import-patterns -import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState, IWorkspace } from 'vs/platform/workspace/common/workspace'; import { addDisposableListener, EventType, windowOpenNoOpener } from 'vs/base/browser/dom'; import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService'; import { pathsToEditors } from 'vs/workbench/common/editor'; @@ -31,14 +31,14 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { ParsedArgs } from 'vs/platform/environment/common/environment'; import { IProcessEnvironment } from 'vs/base/common/platform'; import { toStoreData, restoreRecentlyOpened } from 'vs/platform/history/common/historyStorage'; -// tslint:disable-next-line: import-patterns -import { IExperimentService, IExperiment, ExperimentActionType, ExperimentState } from 'vs/workbench/contrib/experiments/common/experimentService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IProductService } from 'vs/platform/product/common/product'; import Severity from 'vs/base/common/severity'; import { localize } from 'vs/nls'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +// tslint:disable-next-line: import-patterns +import { IWorkspaceStatsService, Tags } from 'vs/workbench/contrib/stats/common/workspaceStats'; //#region Extension Tips @@ -751,13 +751,13 @@ export class SimpleWindowsService implements IWindowsService { async openAboutDialog(): Promise { const detail = localize('aboutDetail', "Version: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}", - this.productService.version || 'Unknown', - this.productService.commit || 'Unknown', - this.productService.date || 'Unknown', + this.productService.productConfiguration.version || 'Unknown', + this.productService.productConfiguration.commit || 'Unknown', + this.productService.productConfiguration.date || 'Unknown', navigator.userAgent ); - const result = await this.dialogService.show(Severity.Info, this.productService.nameLong, [localize('copy', "Copy"), localize('ok', "OK")], { detail }); + const result = await this.dialogService.show(Severity.Info, this.productService.productConfiguration.nameLong, [localize('copy', "Copy"), localize('ok', "OK")], { detail }); if (result === 0) { this.clipboardService.writeText(detail); @@ -855,33 +855,26 @@ registerSingleton(ITunnelService, SimpleTunnelService); //#endregion -//#region experiments +//#region workspace stats + +class WorkspaceStatsService implements IWorkspaceStatsService { -class ExperimentService implements IExperimentService { _serviceBrand: any; - async getExperimentById(id: string): Promise { - return { - enabled: false, - id: '', - state: ExperimentState.NoRun - }; + getTags(): Promise { + return Promise.resolve({}); } - async getExperimentsByType(type: ExperimentActionType): Promise { - return []; + getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): string | undefined { + return undefined; } - async getCuratedExtensionsList(curatedExtensionsKey: string): Promise { - return []; + getHashedRemotesFromUri(workspaceUri: URI, stripEndingDotGit?: boolean): Promise { + return Promise.resolve([]); } - markAsCompleted(experimentId: string): void { } - - onExperimentEnabled: Event = Event.None; - } -registerSingleton(IExperimentService, ExperimentService); +registerSingleton(IWorkspaceStatsService, WorkspaceStatsService); //#endregion diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 4716abf4b4..78308d5ba6 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -51,7 +51,7 @@ export interface IViewContainersRegistry { * * @returns the registered ViewContainer. */ - registerViewContainer(id: string, hideIfEmpty?: boolean, extensionId?: ExtensionIdentifier): ViewContainer; + registerViewContainer(id: string, hideIfEmpty?: boolean, extensionId?: ExtensionIdentifier, viewOrderDelegate?: ViewOrderDelegate): ViewContainer; /** * Deregisters the given view container @@ -67,8 +67,12 @@ export interface IViewContainersRegistry { get(id: string): ViewContainer | undefined; } +interface ViewOrderDelegate { + getOrder(group?: string): number | undefined; +} + export class ViewContainer { - protected constructor(readonly id: string, readonly hideIfEmpty: boolean, readonly extensionId?: ExtensionIdentifier) { } + protected constructor(readonly id: string, readonly hideIfEmpty: boolean, readonly extensionId?: ExtensionIdentifier, readonly orderDelegate?: ViewOrderDelegate) { } } class ViewContainersRegistryImpl extends Disposable implements IViewContainersRegistry { @@ -85,7 +89,7 @@ class ViewContainersRegistryImpl extends Disposable implements IViewContainersRe return values(this.viewContainers); } - registerViewContainer(id: string, hideIfEmpty?: boolean, extensionId?: ExtensionIdentifier): ViewContainer { + registerViewContainer(id: string, hideIfEmpty?: boolean, extensionId?: ExtensionIdentifier, viewOrderDelegate?: ViewOrderDelegate): ViewContainer { const existing = this.viewContainers.get(id); if (existing) { return existing; @@ -93,7 +97,7 @@ class ViewContainersRegistryImpl extends Disposable implements IViewContainersRe const viewContainer = new class extends ViewContainer { constructor() { - super(id, !!hideIfEmpty, extensionId); + super(id, !!hideIfEmpty, extensionId, viewOrderDelegate); } }; this.viewContainers.set(id, viewContainer); @@ -126,6 +130,8 @@ export interface IViewDescriptor { readonly when?: ContextKeyExpr; + readonly group?: string; + readonly order?: number; readonly weight?: number; diff --git a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts index cc54ccbac8..90b06f46d5 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts @@ -7,7 +7,7 @@ import 'vs/css!./accessibility'; import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; -import { renderFormattedText } from 'vs/base/browser/htmlContentRenderer'; +import { renderFormattedText } from 'vs/base/browser/formattedTextRenderer'; import { alert } from 'vs/base/browser/ui/aria/aria'; import { Widget } from 'vs/base/browser/ui/widget'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; diff --git a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts index 99c38a87c6..b3851bcb9a 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts @@ -5,7 +5,7 @@ import * as dom from 'vs/base/browser/dom'; import * as nls from 'vs/nls'; -import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer'; +import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; import { onUnexpectedError } from 'vs/base/common/errors'; import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index f6ec5df703..cea2d6ba2c 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -543,9 +543,9 @@ class FunctionBreakpointInputRenderer implements IListRenderer { +export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolean, preserveFocus: boolean, debugService: IDebugService, editorService: IEditorService): Promise { if (breakpoint.uri.scheme === DEBUG_SCHEME && debugService.state === State.Inactive) { - return Promise.resolve(null); + return Promise.resolve(undefined); } const selection = breakpoint.endLineNumber ? { diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index cf0997c6e6..66004f31e2 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -35,6 +35,7 @@ import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/c import { onUnexpectedError } from 'vs/base/common/errors'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { withUndefinedAsNull } from 'vs/base/common/types'; const jsonRegistry = Registry.as(JSONExtensions.JSONContribution); jsonRegistry.registerSchema(launchSchemaId, launchSchema); @@ -577,7 +578,7 @@ class Launch extends AbstractLaunch implements ILaunch { pinned: created, revealIfVisible: true }, - }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(editor => ({ editor, created }))); + }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(editor => ({ editor: withUndefinedAsNull(editor), created }))); }, (error: Error) => { throw new Error(nls.localize('DebugConfig.failed', "Unable to create 'launch.json' file inside the '.vscode' folder ({0}).", error.message)); }); @@ -613,7 +614,7 @@ class WorkspaceLaunch extends AbstractLaunch implements ILaunch { return this.editorService.openEditor({ resource: this.contextService.getWorkspace().configuration!, options: { preserveFocus } - }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(editor => ({ editor, created: false })); + }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(editor => ({ editor: withUndefinedAsNull(editor), created: false })); } } @@ -647,6 +648,6 @@ class UserLaunch extends AbstractLaunch implements ILaunch { } openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): Promise<{ editor: IEditor | null, created: boolean }> { - return this.preferencesService.openGlobalSettings(false, { preserveFocus }).then(editor => ({ editor, created: false })); + return this.preferencesService.openGlobalSettings(false, { preserveFocus }).then(editor => ({ editor: withUndefinedAsNull(editor), created: false })); } } diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index b0572dd077..75a80f0ee5 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -175,7 +175,7 @@ export class DebugSession implements IDebugSession { return this.raw!.initialize({ clientID: 'vscode', - clientName: this.productService.nameLong, + clientName: this.productService.productConfiguration.nameLong, adapterID: this.configuration.type, pathFormat: 'path', linesStartAt1: true, diff --git a/src/vs/workbench/contrib/debug/common/debugSource.ts b/src/vs/workbench/contrib/debug/common/debugSource.ts index fe4fd729d4..66588efa42 100644 --- a/src/vs/workbench/contrib/debug/common/debugSource.ts +++ b/src/vs/workbench/contrib/debug/common/debugSource.ts @@ -13,6 +13,7 @@ import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/ import { Schemas } from 'vs/base/common/network'; import { isUri } from 'vs/workbench/contrib/debug/common/debugUtils'; import { ITextEditor } from 'vs/workbench/common/editor'; +import { withUndefinedAsNull } from 'vs/base/common/types'; export const UNKNOWN_SOURCE_LABEL = nls.localize('unknownSource', "Unknown Source"); @@ -104,7 +105,7 @@ export class Source { revealInCenterIfOutsideViewport: true, pinned: pinned || (!preserveFocus && !this.inMemory) } - }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP); + }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(withUndefinedAsNull); } static getEncodedDebugData(modelUri: uri): { name: string, path: string, sessionId?: string, sourceReference?: number } { diff --git a/src/vs/workbench/contrib/experiments/electron-browser/experimentalPrompt.ts b/src/vs/workbench/contrib/experiments/browser/experimentalPrompt.ts similarity index 100% rename from src/vs/workbench/contrib/experiments/electron-browser/experimentalPrompt.ts rename to src/vs/workbench/contrib/experiments/browser/experimentalPrompt.ts diff --git a/src/vs/workbench/contrib/experiments/electron-browser/experiments.contribution.ts b/src/vs/workbench/contrib/experiments/browser/experiments.contribution.ts similarity index 79% rename from src/vs/workbench/contrib/experiments/electron-browser/experiments.contribution.ts rename to src/vs/workbench/contrib/experiments/browser/experiments.contribution.ts index f7e0730926..fd13afb805 100644 --- a/src/vs/workbench/contrib/experiments/electron-browser/experiments.contribution.ts +++ b/src/vs/workbench/contrib/experiments/browser/experiments.contribution.ts @@ -4,12 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService'; -import { ExperimentService } from 'vs/workbench/contrib/experiments/electron-browser/experimentService'; +import { IExperimentService, ExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { ExperimentalPrompts } from 'vs/workbench/contrib/experiments/electron-browser/experimentalPrompt'; +import { ExperimentalPrompts } from 'vs/workbench/contrib/experiments/browser/experimentalPrompt'; registerSingleton(IExperimentService, ExperimentService, true); diff --git a/src/vs/workbench/contrib/experiments/common/experimentService.ts b/src/vs/workbench/contrib/experiments/common/experimentService.ts index 449c4bc4d8..d88fffd288 100644 --- a/src/vs/workbench/contrib/experiments/common/experimentService.ts +++ b/src/vs/workbench/contrib/experiments/common/experimentService.ts @@ -4,7 +4,23 @@ *--------------------------------------------------------------------------------------------*/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { Event } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ITelemetryService, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; +import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { language } from 'vs/base/common/platform'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { match } from 'vs/base/common/glob'; +import { IRequestService, asJson } from 'vs/platform/request/common/request'; +import { ITextFileService, StateChange } from 'vs/workbench/services/textfile/common/textfiles'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { distinct } from 'vs/base/common/arrays'; +import { ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { IProductService } from 'vs/platform/product/common/product'; +import { IWorkspaceStatsService } from 'vs/workbench/contrib/stats/common/workspaceStats'; export const enum ExperimentState { Evaluating, @@ -56,4 +72,388 @@ export interface IExperimentService { onExperimentEnabled: Event; } -export const IExperimentService = createDecorator('experimentService'); \ No newline at end of file +export const IExperimentService = createDecorator('experimentService'); + +interface IExperimentStorageState { + enabled: boolean; + state: ExperimentState; + editCount?: number; + lastEditedDate?: string; +} + +interface IRawExperiment { + id: string; + enabled?: boolean; + condition?: { + insidersOnly?: boolean; + newUser?: boolean; + displayLanguage?: string; + installedExtensions?: { + excludes?: string[]; + includes?: string[]; + }, + fileEdits?: { + filePathPattern?: string; + workspaceIncludes?: string[]; + workspaceExcludes?: string[]; + minEditCount: number; + }, + experimentsPreviouslyRun?: { + excludes?: string[]; + includes?: string[]; + } + userProbability?: number; + }; + action?: IExperimentAction; +} + +export class ExperimentService extends Disposable implements IExperimentService { + _serviceBrand: any; + private _experiments: IExperiment[] = []; + private _loadExperimentsPromise: Promise; + private _curatedMapping = Object.create(null); + + private readonly _onExperimentEnabled = this._register(new Emitter()); + onExperimentEnabled: Event = this._onExperimentEnabled.event; + + constructor( + @IStorageService private readonly storageService: IStorageService, + @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, + @ITextFileService private readonly textFileService: ITextFileService, + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @ITelemetryService private readonly telemetryService: ITelemetryService, + @ILifecycleService private readonly lifecycleService: ILifecycleService, + @IRequestService private readonly requestService: IRequestService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @IProductService private readonly productService: IProductService, + @IWorkspaceStatsService private readonly workspaceStatsService: IWorkspaceStatsService + ) { + super(); + + this._loadExperimentsPromise = Promise.resolve(this.lifecycleService.when(LifecyclePhase.Eventually)).then(() => this.loadExperiments()); + } + + public getExperimentById(id: string): Promise { + return this._loadExperimentsPromise.then(() => { + return this._experiments.filter(x => x.id === id)[0]; + }); + } + + public getExperimentsByType(type: ExperimentActionType): Promise { + return this._loadExperimentsPromise.then(() => { + if (type === ExperimentActionType.Custom) { + return this._experiments.filter(x => x.enabled && (!x.action || x.action.type === type)); + } + return this._experiments.filter(x => x.enabled && x.action && x.action.type === type); + }); + } + + public getCuratedExtensionsList(curatedExtensionsKey: string): Promise { + return this._loadExperimentsPromise.then(() => { + for (const experiment of this._experiments) { + if (experiment.enabled + && experiment.state === ExperimentState.Run + && this._curatedMapping[experiment.id] + && this._curatedMapping[experiment.id].curatedExtensionsKey === curatedExtensionsKey) { + return this._curatedMapping[experiment.id].curatedExtensionsList; + } + } + return []; + }); + } + + public markAsCompleted(experimentId: string): void { + const storageKey = 'experiments.' + experimentId; + const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); + experimentState.state = ExperimentState.Complete; + this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL); + } + + protected getExperiments(): Promise { + if (!this.productService.productConfiguration.experimentsUrl || this.configurationService.getValue('workbench.enableExperiments') === false) { + return Promise.resolve([]); + } + return this.requestService.request({ type: 'GET', url: this.productService.productConfiguration.experimentsUrl }, CancellationToken.None).then(context => { + if (context.res.statusCode !== 200) { + return Promise.resolve(null); + } + return asJson(context).then((result: any) => { + return result && Array.isArray(result['experiments']) ? result['experiments'] : []; + }); + }, () => Promise.resolve(null)); + } + + private loadExperiments(): Promise { + return this.getExperiments().then(rawExperiments => { + // Offline mode + if (!rawExperiments) { + const allExperimentIdsFromStorage = safeParse(this.storageService.get('allExperiments', StorageScope.GLOBAL), []); + if (Array.isArray(allExperimentIdsFromStorage)) { + allExperimentIdsFromStorage.forEach(experimentId => { + const storageKey = 'experiments.' + experimentId; + const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), null); + if (experimentState) { + this._experiments.push({ + id: experimentId, + enabled: experimentState.enabled, + state: experimentState.state + }); + } + }); + } + return Promise.resolve(null); + } + + // Clear disbaled/deleted experiments from storage + const allExperimentIdsFromStorage = safeParse(this.storageService.get('allExperiments', StorageScope.GLOBAL), []); + const enabledExperiments = rawExperiments.filter(experiment => !!experiment.enabled).map(experiment => experiment.id.toLowerCase()); + if (Array.isArray(allExperimentIdsFromStorage)) { + allExperimentIdsFromStorage.forEach(experiment => { + if (enabledExperiments.indexOf(experiment) === -1) { + this.storageService.remove(`experiments.${experiment}`, StorageScope.GLOBAL); + } + }); + } + if (enabledExperiments.length) { + this.storageService.store('allExperiments', JSON.stringify(enabledExperiments), StorageScope.GLOBAL); + } else { + this.storageService.remove('allExperiments', StorageScope.GLOBAL); + } + + const promises = rawExperiments.map(experiment => { + const processedExperiment: IExperiment = { + id: experiment.id, + enabled: !!experiment.enabled, + state: !!experiment.enabled ? ExperimentState.Evaluating : ExperimentState.NoRun + }; + + if (experiment.action) { + processedExperiment.action = { + type: ExperimentActionType[experiment.action.type] || ExperimentActionType.Custom, + properties: experiment.action.properties + }; + if (processedExperiment.action.type === ExperimentActionType.Prompt) { + ((processedExperiment.action.properties).commands || []).forEach(x => { + if (x.curatedExtensionsKey && Array.isArray(x.curatedExtensionsList)) { + this._curatedMapping[experiment.id] = x; + } + }); + } + if (!processedExperiment.action.properties) { + processedExperiment.action.properties = {}; + } + } + this._experiments.push(processedExperiment); + + if (!processedExperiment.enabled) { + return Promise.resolve(null); + } + + const storageKey = 'experiments.' + experiment.id; + const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); + if (!experimentState.hasOwnProperty('enabled')) { + experimentState.enabled = processedExperiment.enabled; + } + if (!experimentState.hasOwnProperty('state')) { + experimentState.state = processedExperiment.enabled ? ExperimentState.Evaluating : ExperimentState.NoRun; + } else { + processedExperiment.state = experimentState.state; + } + + return this.shouldRunExperiment(experiment, processedExperiment).then((state: ExperimentState) => { + experimentState.state = processedExperiment.state = state; + this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL); + + if (state === ExperimentState.Run) { + this.fireRunExperiment(processedExperiment); + } + return Promise.resolve(null); + }); + + }); + return Promise.all(promises).then(() => { + type ExperimentsClassification = { + experiments: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + }; + this.telemetryService.publicLog2<{ experiments: IExperiment[] }, ExperimentsClassification>('experiments', { experiments: this._experiments }); + }); + }); + } + + private fireRunExperiment(experiment: IExperiment) { + this._onExperimentEnabled.fire(experiment); + const runExperimentIdsFromStorage: string[] = safeParse(this.storageService.get('currentOrPreviouslyRunExperiments', StorageScope.GLOBAL), []); + if (runExperimentIdsFromStorage.indexOf(experiment.id) === -1) { + runExperimentIdsFromStorage.push(experiment.id); + } + + // Ensure we dont store duplicates + const distinctExperiments = distinct(runExperimentIdsFromStorage); + if (runExperimentIdsFromStorage.length !== distinctExperiments.length) { + this.storageService.store('currentOrPreviouslyRunExperiments', JSON.stringify(distinctExperiments), StorageScope.GLOBAL); + } + } + + private checkExperimentDependencies(experiment: IRawExperiment): boolean { + const experimentsPreviouslyRun = experiment.condition ? experiment.condition.experimentsPreviouslyRun : undefined; + if (experimentsPreviouslyRun) { + const runExperimentIdsFromStorage: string[] = safeParse(this.storageService.get('currentOrPreviouslyRunExperiments', StorageScope.GLOBAL), []); + let includeCheck = true; + let excludeCheck = true; + const includes = experimentsPreviouslyRun.includes; + if (Array.isArray(includes)) { + includeCheck = runExperimentIdsFromStorage.some(x => includes.indexOf(x) > -1); + } + const excludes = experimentsPreviouslyRun.excludes; + if (includeCheck && Array.isArray(excludes)) { + excludeCheck = !runExperimentIdsFromStorage.some(x => excludes.indexOf(x) > -1); + } + if (!includeCheck || !excludeCheck) { + return false; + } + } + return true; + } + + private shouldRunExperiment(experiment: IRawExperiment, processedExperiment: IExperiment): Promise { + if (processedExperiment.state !== ExperimentState.Evaluating) { + return Promise.resolve(processedExperiment.state); + } + + if (!experiment.enabled) { + return Promise.resolve(ExperimentState.NoRun); + } + + const condition = experiment.condition; + if (!condition) { + return Promise.resolve(ExperimentState.Run); + } + + if (!this.checkExperimentDependencies(experiment)) { + return Promise.resolve(ExperimentState.NoRun); + } + + if (this.environmentService.appQuality === 'stable' && condition.insidersOnly === true) { + return Promise.resolve(ExperimentState.NoRun); + } + + const isNewUser = !this.storageService.get(lastSessionDateStorageKey, StorageScope.GLOBAL); + if ((condition.newUser === true && !isNewUser) + || (condition.newUser === false && isNewUser)) { + return Promise.resolve(ExperimentState.NoRun); + } + + if (typeof condition.displayLanguage === 'string') { + let localeToCheck = condition.displayLanguage.toLowerCase(); + let displayLanguage = language!.toLowerCase(); + + if (localeToCheck !== displayLanguage) { + const a = displayLanguage.indexOf('-'); + const b = localeToCheck.indexOf('-'); + if (a > -1) { + displayLanguage = displayLanguage.substr(0, a); + } + if (b > -1) { + localeToCheck = localeToCheck.substr(0, b); + } + if (displayLanguage !== localeToCheck) { + return Promise.resolve(ExperimentState.NoRun); + } + } + } + + if (!condition.userProbability) { + condition.userProbability = 1; + } + + let extensionsCheckPromise = Promise.resolve(true); + const installedExtensions = condition.installedExtensions; + if (installedExtensions) { + extensionsCheckPromise = this.extensionManagementService.getInstalled(ExtensionType.User).then(locals => { + let includesCheck = true; + let excludesCheck = true; + const localExtensions = locals.map(local => `${local.manifest.publisher.toLowerCase()}.${local.manifest.name.toLowerCase()}`); + if (Array.isArray(installedExtensions.includes) && installedExtensions.includes.length) { + const extensionIncludes = installedExtensions.includes.map(e => e.toLowerCase()); + includesCheck = localExtensions.some(e => extensionIncludes.indexOf(e) > -1); + } + if (Array.isArray(installedExtensions.excludes) && installedExtensions.excludes.length) { + const extensionExcludes = installedExtensions.excludes.map(e => e.toLowerCase()); + excludesCheck = !localExtensions.some(e => extensionExcludes.indexOf(e) > -1); + } + return includesCheck && excludesCheck; + }); + } + + const storageKey = 'experiments.' + experiment.id; + const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); + + return extensionsCheckPromise.then(success => { + const fileEdits = condition.fileEdits; + if (!success || !fileEdits || typeof fileEdits.minEditCount !== 'number') { + const runExperiment = success && typeof condition.userProbability === 'number' && Math.random() < condition.userProbability; + return runExperiment ? ExperimentState.Run : ExperimentState.NoRun; + } + + experimentState.editCount = experimentState.editCount || 0; + if (experimentState.editCount >= fileEdits.minEditCount) { + return ExperimentState.Run; + } + + const onSaveHandler = this.textFileService.models.onModelsSaved(e => { + const date = new Date().toDateString(); + const latestExperimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); + if (latestExperimentState.state !== ExperimentState.Evaluating) { + onSaveHandler.dispose(); + return; + } + e.forEach(async event => { + if (event.kind !== StateChange.SAVED + || latestExperimentState.state !== ExperimentState.Evaluating + || date === latestExperimentState.lastEditedDate + || (typeof latestExperimentState.editCount === 'number' && latestExperimentState.editCount >= fileEdits.minEditCount) + ) { + return; + } + let filePathCheck = true; + let workspaceCheck = true; + + if (typeof fileEdits.filePathPattern === 'string') { + filePathCheck = match(fileEdits.filePathPattern, event.resource.fsPath); + } + if (Array.isArray(fileEdits.workspaceIncludes) && fileEdits.workspaceIncludes.length) { + const tags = await this.workspaceStatsService.getTags(); + workspaceCheck = !!tags && fileEdits.workspaceIncludes.some(x => !!tags[x]); + } + if (workspaceCheck && Array.isArray(fileEdits.workspaceExcludes) && fileEdits.workspaceExcludes.length) { + const tags = await this.workspaceStatsService.getTags(); + workspaceCheck = !!tags && !fileEdits.workspaceExcludes.some(x => !!tags[x]); + } + if (filePathCheck && workspaceCheck) { + latestExperimentState.editCount = (latestExperimentState.editCount || 0) + 1; + latestExperimentState.lastEditedDate = date; + this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL); + } + }); + if (typeof latestExperimentState.editCount === 'number' && latestExperimentState.editCount >= fileEdits.minEditCount) { + processedExperiment.state = latestExperimentState.state = (typeof condition.userProbability === 'number' && Math.random() < condition.userProbability && this.checkExperimentDependencies(experiment)) ? ExperimentState.Run : ExperimentState.NoRun; + this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL); + if (latestExperimentState.state === ExperimentState.Run && experiment.action && ExperimentActionType[experiment.action.type] === ExperimentActionType.Prompt) { + this.fireRunExperiment(processedExperiment); + } + } + }); + this._register(onSaveHandler); + return ExperimentState.Evaluating; + }); + } +} + + +function safeParse(text: string | undefined, defaultObject: any) { + try { + return text ? JSON.parse(text) || defaultObject : defaultObject; + } catch (e) { + return defaultObject; + } +} diff --git a/src/vs/workbench/contrib/experiments/electron-browser/experimentService.ts b/src/vs/workbench/contrib/experiments/electron-browser/experimentService.ts deleted file mode 100644 index a24a9f35fd..0000000000 --- a/src/vs/workbench/contrib/experiments/electron-browser/experimentService.ts +++ /dev/null @@ -1,407 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { ITelemetryService, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; -import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { language } from 'vs/base/common/platform'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { match } from 'vs/base/common/glob'; -import { IRequestService, asJson } from 'vs/platform/request/common/request'; -import { Emitter, Event } from 'vs/base/common/event'; -import { ITextFileService, StateChange } from 'vs/workbench/services/textfile/common/textfiles'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { distinct } from 'vs/base/common/arrays'; -import { ExtensionType } from 'vs/platform/extensions/common/extensions'; -import { ExperimentState, IExperimentAction, IExperimentService, IExperiment, ExperimentActionType, IExperimentActionPromptProperties } from 'vs/workbench/contrib/experiments/common/experimentService'; -import { IProductService } from 'vs/platform/product/common/product'; -import { IWorkspaceStatsService } from 'vs/workbench/contrib/stats/electron-browser/workspaceStatsService'; - -interface IExperimentStorageState { - enabled: boolean; - state: ExperimentState; - editCount?: number; - lastEditedDate?: string; -} - -interface IRawExperiment { - id: string; - enabled?: boolean; - condition?: { - insidersOnly?: boolean; - newUser?: boolean; - displayLanguage?: string; - installedExtensions?: { - excludes?: string[]; - includes?: string[]; - }, - fileEdits?: { - filePathPattern?: string; - workspaceIncludes?: string[]; - workspaceExcludes?: string[]; - minEditCount: number; - }, - experimentsPreviouslyRun?: { - excludes?: string[]; - includes?: string[]; - } - userProbability?: number; - }; - action?: IExperimentAction; -} - -export class ExperimentService extends Disposable implements IExperimentService { - _serviceBrand: any; - private _experiments: IExperiment[] = []; - private _loadExperimentsPromise: Promise; - private _curatedMapping = Object.create(null); - - private readonly _onExperimentEnabled = this._register(new Emitter()); - onExperimentEnabled: Event = this._onExperimentEnabled.event; - - constructor( - @IStorageService private readonly storageService: IStorageService, - @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, - @ITextFileService private readonly textFileService: ITextFileService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, - @ITelemetryService private readonly telemetryService: ITelemetryService, - @ILifecycleService private readonly lifecycleService: ILifecycleService, - @IRequestService private readonly requestService: IRequestService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @IProductService private readonly productService: IProductService, - @IWorkspaceStatsService private readonly workspaceStatsService: IWorkspaceStatsService - ) { - super(); - - this._loadExperimentsPromise = Promise.resolve(this.lifecycleService.when(LifecyclePhase.Eventually)).then(() => this.loadExperiments()); - } - - public getExperimentById(id: string): Promise { - return this._loadExperimentsPromise.then(() => { - return this._experiments.filter(x => x.id === id)[0]; - }); - } - - public getExperimentsByType(type: ExperimentActionType): Promise { - return this._loadExperimentsPromise.then(() => { - if (type === ExperimentActionType.Custom) { - return this._experiments.filter(x => x.enabled && (!x.action || x.action.type === type)); - } - return this._experiments.filter(x => x.enabled && x.action && x.action.type === type); - }); - } - - public getCuratedExtensionsList(curatedExtensionsKey: string): Promise { - return this._loadExperimentsPromise.then(() => { - for (const experiment of this._experiments) { - if (experiment.enabled - && experiment.state === ExperimentState.Run - && this._curatedMapping[experiment.id] - && this._curatedMapping[experiment.id].curatedExtensionsKey === curatedExtensionsKey) { - return this._curatedMapping[experiment.id].curatedExtensionsList; - } - } - return []; - }); - } - - public markAsCompleted(experimentId: string): void { - const storageKey = 'experiments.' + experimentId; - const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); - experimentState.state = ExperimentState.Complete; - this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL); - } - - protected getExperiments(): Promise { - if (!this.productService.experimentsUrl || this.configurationService.getValue('workbench.enableExperiments') === false) { - return Promise.resolve([]); - } - return this.requestService.request({ type: 'GET', url: this.productService.experimentsUrl }, CancellationToken.None).then(context => { - if (context.res.statusCode !== 200) { - return Promise.resolve(null); - } - return asJson(context).then((result: any) => { - return result && Array.isArray(result['experiments']) ? result['experiments'] : []; - }); - }, () => Promise.resolve(null)); - } - - private loadExperiments(): Promise { - return this.getExperiments().then(rawExperiments => { - // Offline mode - if (!rawExperiments) { - const allExperimentIdsFromStorage = safeParse(this.storageService.get('allExperiments', StorageScope.GLOBAL), []); - if (Array.isArray(allExperimentIdsFromStorage)) { - allExperimentIdsFromStorage.forEach(experimentId => { - const storageKey = 'experiments.' + experimentId; - const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), null); - if (experimentState) { - this._experiments.push({ - id: experimentId, - enabled: experimentState.enabled, - state: experimentState.state - }); - } - }); - } - return Promise.resolve(null); - } - - // Clear disbaled/deleted experiments from storage - const allExperimentIdsFromStorage = safeParse(this.storageService.get('allExperiments', StorageScope.GLOBAL), []); - const enabledExperiments = rawExperiments.filter(experiment => !!experiment.enabled).map(experiment => experiment.id.toLowerCase()); - if (Array.isArray(allExperimentIdsFromStorage)) { - allExperimentIdsFromStorage.forEach(experiment => { - if (enabledExperiments.indexOf(experiment) === -1) { - this.storageService.remove(`experiments.${experiment}`, StorageScope.GLOBAL); - } - }); - } - if (enabledExperiments.length) { - this.storageService.store('allExperiments', JSON.stringify(enabledExperiments), StorageScope.GLOBAL); - } else { - this.storageService.remove('allExperiments', StorageScope.GLOBAL); - } - - const promises = rawExperiments.map(experiment => { - const processedExperiment: IExperiment = { - id: experiment.id, - enabled: !!experiment.enabled, - state: !!experiment.enabled ? ExperimentState.Evaluating : ExperimentState.NoRun - }; - - if (experiment.action) { - processedExperiment.action = { - type: ExperimentActionType[experiment.action.type] || ExperimentActionType.Custom, - properties: experiment.action.properties - }; - if (processedExperiment.action.type === ExperimentActionType.Prompt) { - ((processedExperiment.action.properties).commands || []).forEach(x => { - if (x.curatedExtensionsKey && Array.isArray(x.curatedExtensionsList)) { - this._curatedMapping[experiment.id] = x; - } - }); - } - if (!processedExperiment.action.properties) { - processedExperiment.action.properties = {}; - } - } - this._experiments.push(processedExperiment); - - if (!processedExperiment.enabled) { - return Promise.resolve(null); - } - - const storageKey = 'experiments.' + experiment.id; - const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); - if (!experimentState.hasOwnProperty('enabled')) { - experimentState.enabled = processedExperiment.enabled; - } - if (!experimentState.hasOwnProperty('state')) { - experimentState.state = processedExperiment.enabled ? ExperimentState.Evaluating : ExperimentState.NoRun; - } else { - processedExperiment.state = experimentState.state; - } - - return this.shouldRunExperiment(experiment, processedExperiment).then((state: ExperimentState) => { - experimentState.state = processedExperiment.state = state; - this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL); - - if (state === ExperimentState.Run) { - this.fireRunExperiment(processedExperiment); - } - return Promise.resolve(null); - }); - - }); - return Promise.all(promises).then(() => { - type ExperimentsClassification = { - experiments: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - }; - this.telemetryService.publicLog2<{ experiments: IExperiment[] }, ExperimentsClassification>('experiments', { experiments: this._experiments }); - }); - }); - } - - private fireRunExperiment(experiment: IExperiment) { - this._onExperimentEnabled.fire(experiment); - const runExperimentIdsFromStorage: string[] = safeParse(this.storageService.get('currentOrPreviouslyRunExperiments', StorageScope.GLOBAL), []); - if (runExperimentIdsFromStorage.indexOf(experiment.id) === -1) { - runExperimentIdsFromStorage.push(experiment.id); - } - - // Ensure we dont store duplicates - const distinctExperiments = distinct(runExperimentIdsFromStorage); - if (runExperimentIdsFromStorage.length !== distinctExperiments.length) { - this.storageService.store('currentOrPreviouslyRunExperiments', JSON.stringify(distinctExperiments), StorageScope.GLOBAL); - } - } - - private checkExperimentDependencies(experiment: IRawExperiment): boolean { - const experimentsPreviouslyRun = experiment.condition ? experiment.condition.experimentsPreviouslyRun : undefined; - if (experimentsPreviouslyRun) { - const runExperimentIdsFromStorage: string[] = safeParse(this.storageService.get('currentOrPreviouslyRunExperiments', StorageScope.GLOBAL), []); - let includeCheck = true; - let excludeCheck = true; - const includes = experimentsPreviouslyRun.includes; - if (Array.isArray(includes)) { - includeCheck = runExperimentIdsFromStorage.some(x => includes.indexOf(x) > -1); - } - const excludes = experimentsPreviouslyRun.excludes; - if (includeCheck && Array.isArray(excludes)) { - excludeCheck = !runExperimentIdsFromStorage.some(x => excludes.indexOf(x) > -1); - } - if (!includeCheck || !excludeCheck) { - return false; - } - } - return true; - } - - private shouldRunExperiment(experiment: IRawExperiment, processedExperiment: IExperiment): Promise { - if (processedExperiment.state !== ExperimentState.Evaluating) { - return Promise.resolve(processedExperiment.state); - } - - if (!experiment.enabled) { - return Promise.resolve(ExperimentState.NoRun); - } - - const condition = experiment.condition; - if (!condition) { - return Promise.resolve(ExperimentState.Run); - } - - if (!this.checkExperimentDependencies(experiment)) { - return Promise.resolve(ExperimentState.NoRun); - } - - if (this.environmentService.appQuality === 'stable' && condition.insidersOnly === true) { - return Promise.resolve(ExperimentState.NoRun); - } - - const isNewUser = !this.storageService.get(lastSessionDateStorageKey, StorageScope.GLOBAL); - if ((condition.newUser === true && !isNewUser) - || (condition.newUser === false && isNewUser)) { - return Promise.resolve(ExperimentState.NoRun); - } - - if (typeof condition.displayLanguage === 'string') { - let localeToCheck = condition.displayLanguage.toLowerCase(); - let displayLanguage = language!.toLowerCase(); - - if (localeToCheck !== displayLanguage) { - const a = displayLanguage.indexOf('-'); - const b = localeToCheck.indexOf('-'); - if (a > -1) { - displayLanguage = displayLanguage.substr(0, a); - } - if (b > -1) { - localeToCheck = localeToCheck.substr(0, b); - } - if (displayLanguage !== localeToCheck) { - return Promise.resolve(ExperimentState.NoRun); - } - } - } - - if (!condition.userProbability) { - condition.userProbability = 1; - } - - let extensionsCheckPromise = Promise.resolve(true); - const installedExtensions = condition.installedExtensions; - if (installedExtensions) { - extensionsCheckPromise = this.extensionManagementService.getInstalled(ExtensionType.User).then(locals => { - let includesCheck = true; - let excludesCheck = true; - const localExtensions = locals.map(local => `${local.manifest.publisher.toLowerCase()}.${local.manifest.name.toLowerCase()}`); - if (Array.isArray(installedExtensions.includes) && installedExtensions.includes.length) { - const extensionIncludes = installedExtensions.includes.map(e => e.toLowerCase()); - includesCheck = localExtensions.some(e => extensionIncludes.indexOf(e) > -1); - } - if (Array.isArray(installedExtensions.excludes) && installedExtensions.excludes.length) { - const extensionExcludes = installedExtensions.excludes.map(e => e.toLowerCase()); - excludesCheck = !localExtensions.some(e => extensionExcludes.indexOf(e) > -1); - } - return includesCheck && excludesCheck; - }); - } - - const storageKey = 'experiments.' + experiment.id; - const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); - - return extensionsCheckPromise.then(success => { - const fileEdits = condition.fileEdits; - if (!success || !fileEdits || typeof fileEdits.minEditCount !== 'number') { - const runExperiment = success && typeof condition.userProbability === 'number' && Math.random() < condition.userProbability; - return runExperiment ? ExperimentState.Run : ExperimentState.NoRun; - } - - experimentState.editCount = experimentState.editCount || 0; - if (experimentState.editCount >= fileEdits.minEditCount) { - return ExperimentState.Run; - } - - const onSaveHandler = this.textFileService.models.onModelsSaved(e => { - const date = new Date().toDateString(); - const latestExperimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); - if (latestExperimentState.state !== ExperimentState.Evaluating) { - onSaveHandler.dispose(); - return; - } - e.forEach(async event => { - if (event.kind !== StateChange.SAVED - || latestExperimentState.state !== ExperimentState.Evaluating - || date === latestExperimentState.lastEditedDate - || (typeof latestExperimentState.editCount === 'number' && latestExperimentState.editCount >= fileEdits.minEditCount) - ) { - return; - } - let filePathCheck = true; - let workspaceCheck = true; - - if (typeof fileEdits.filePathPattern === 'string') { - filePathCheck = match(fileEdits.filePathPattern, event.resource.fsPath); - } - if (Array.isArray(fileEdits.workspaceIncludes) && fileEdits.workspaceIncludes.length) { - const tags = await this.workspaceStatsService.getTags(); - workspaceCheck = !!tags && fileEdits.workspaceIncludes.some(x => !!tags[x]); - } - if (workspaceCheck && Array.isArray(fileEdits.workspaceExcludes) && fileEdits.workspaceExcludes.length) { - const tags = await this.workspaceStatsService.getTags(); - workspaceCheck = !!tags && !fileEdits.workspaceExcludes.some(x => !!tags[x]); - } - if (filePathCheck && workspaceCheck) { - latestExperimentState.editCount = (latestExperimentState.editCount || 0) + 1; - latestExperimentState.lastEditedDate = date; - this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL); - } - }); - if (typeof latestExperimentState.editCount === 'number' && latestExperimentState.editCount >= fileEdits.minEditCount) { - processedExperiment.state = latestExperimentState.state = (typeof condition.userProbability === 'number' && Math.random() < condition.userProbability && this.checkExperimentDependencies(experiment)) ? ExperimentState.Run : ExperimentState.NoRun; - this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL); - if (latestExperimentState.state === ExperimentState.Run && experiment.action && ExperimentActionType[experiment.action.type] === ExperimentActionType.Prompt) { - this.fireRunExperiment(processedExperiment); - } - } - }); - this._register(onSaveHandler); - return ExperimentState.Evaluating; - }); - } -} - - -function safeParse(text: string | undefined, defaultObject: any) { - try { - return text ? JSON.parse(text) || defaultObject : defaultObject; - } catch (e) { - return defaultObject; - } -} diff --git a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts index 0c91a2337f..23207c76dd 100644 --- a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts +++ b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts @@ -4,8 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { ExperimentActionType, ExperimentState, IExperiment } from 'vs/workbench/contrib/experiments/common/experimentService'; -import { ExperimentService } from 'vs/workbench/contrib/experiments/electron-browser/experimentService'; +import { ExperimentActionType, ExperimentState, IExperiment, ExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { TestLifecycleService } from 'vs/workbench/test/workbenchTestServices'; diff --git a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts index f1f89fce44..8817cecef2 100644 --- a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts +++ b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts @@ -12,7 +12,7 @@ import { TestNotificationService } from 'vs/platform/notification/test/common/te import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; -import { ExperimentalPrompts } from 'vs/workbench/contrib/experiments/electron-browser/experimentalPrompt'; +import { ExperimentalPrompts } from 'vs/workbench/contrib/experiments/browser/experimentalPrompt'; import { ExperimentActionType, ExperimentState, IExperiment, IExperimentActionPromptProperties, IExperimentService, LocalizedPromptText } from 'vs/workbench/contrib/experiments/common/experimentService'; import { TestExperimentService } from 'vs/workbench/contrib/experiments/test/electron-browser/experimentService.test'; import { TestLifecycleService } from 'vs/workbench/test/workbenchTestServices'; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index c03daece76..a836fcc061 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -79,10 +79,10 @@ export function toExtensionDescription(local: ILocalExtension): IExtensionDescri const promptDownloadManually = (extension: IGalleryExtension | undefined, message: string, error: Error, instantiationService: IInstantiationService, notificationService: INotificationService, openerService: IOpenerService, productService: IProductService) => { - if (!extension || error.name === INSTALL_ERROR_INCOMPATIBLE || error.name === INSTALL_ERROR_MALICIOUS || !productService.extensionsGallery) { + if (!extension || error.name === INSTALL_ERROR_INCOMPATIBLE || error.name === INSTALL_ERROR_MALICIOUS || !productService.productConfiguration.extensionsGallery) { return Promise.reject(error); } else { - const downloadUrl = `${productService.extensionsGallery.serviceUrl}/publishers/${extension.publisher}/vsextensions/${extension.name}/${extension.version}/vspackage`; + const downloadUrl = `${productService.productConfiguration.extensionsGallery.serviceUrl}/publishers/${extension.publisher}/vsextensions/${extension.name}/${extension.version}/vspackage`; notificationService.prompt(Severity.Error, message, [{ label: localize('download', "Download Manually"), run: () => openerService.open(URI.parse(downloadUrl)).then(() => { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index 79cc19ddc6..4e411ce202 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -435,7 +435,7 @@ export class ExtensionsListView extends ViewletPanel { // {{SQL CARBON EDIT}} if (this.productService) { let promiseRecommendedExtensionsByScenario: Promise> | undefined; - Object.keys(this.productService.recommendedExtensionsByScenario).forEach(scenarioType => { + Object.keys(this.productService.productConfiguration.recommendedExtensionsByScenario).forEach(scenarioType => { let re = new RegExp('@' + scenarioType, 'i'); if (re.test(query.value)) { promiseRecommendedExtensionsByScenario = this.getRecommendedExtensionsByScenario(token, scenarioType); @@ -944,7 +944,7 @@ export class ServerExtensionsView extends ExtensionsListView { @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, @IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService, @IProductService productService: IProductService, - @IContextKeyService contextKeyService: IContextKeyService, + @IContextKeyService contextKeyService: IContextKeyService ) { options.server = server; super(options, notificationService, keybindingService, contextMenuService, instantiationService, themeService, extensionService, extensionsWorkbenchService, editorService, tipsService, telemetryService, configurationService, contextService, experimentService, workbenchThemeService, extensionManagementServerService, productService, contextKeyService); @@ -960,7 +960,7 @@ export class ServerExtensionsView extends ExtensionsListView { } getActions(): IAction[] { - if (this.extensionManagementServerService.localExtensionManagementServer === this.server) { + if (this.extensionManagementServerService.remoteExtensionManagementServer && this.extensionManagementServerService.localExtensionManagementServer === this.server) { const installLocalExtensionsInRemoteAction = this._register(this.instantiationService.createInstance(InstallLocalExtensionsInRemoteAction, false)); installLocalExtensionsInRemoteAction.class = 'octicon octicon-cloud-download'; return [installLocalExtensionsInRemoteAction]; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index c72d62938f..f178284e92 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -118,20 +118,19 @@ class Extension implements IExtension { } get url(): string | undefined { - if (!this.productService.extensionsGallery || !this.gallery) { + if (!this.productService.productConfiguration.extensionsGallery || !this.gallery) { return undefined; } - return this.productService.extensionsGallery.itemUrl && `${this.productService.extensionsGallery.itemUrl}?itemName=${this.publisher}.${this.name}`; // {{SQL CARBON EDIT}} add check for itemurl + return `${this.productService.productConfiguration.extensionsGallery.itemUrl}?itemName=${this.publisher}.${this.name}`; } // {{SQL CARBON EDIT}} get downloadPage(): string { - if (!this.productService.extensionsGallery) { + if (!this.productService.productConfiguration.extensionsGallery) { return null; } - // {{SQL CARBON EDIT}} return this.gallery && this.gallery.assets && this.gallery.assets.downloadPage && this.gallery.assets.downloadPage.uri; } @@ -632,7 +631,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension text = text.replace(extensionRegex, (m, ext) => { // Get curated keywords - const lookup = this.productService.extensionKeywords || {}; + const lookup = this.productService.productConfiguration.extensionKeywords || {}; const keywords = lookup[ext] || []; // Get mode name @@ -832,9 +831,9 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension // This is the execution path for install/update extension from marketplace. // Check both the vscode version and azure data studio version // The check is added here because we want to fail fast instead of downloading the VSIX and then fail. - if (gallery.properties.engine && (!isEngineValid(gallery.properties.engine, this.productService.vscodeVersion) - || (gallery.properties.azDataEngine && !isEngineValid(gallery.properties.azDataEngine, this.productService.version)))) { - return Promise.reject(new Error(nls.localize('incompatible2', "Unable to install version '{2}' of extension '{0}' as it is not compatible with Azure Data Studio '{1}'.", extension.gallery!.identifier.id, this.productService.version, gallery.version))); + if (gallery.properties.engine && (!isEngineValid(gallery.properties.engine, this.productService.productConfiguration.vscodeVersion) + || (gallery.properties.azDataEngine && !isEngineValid(gallery.properties.azDataEngine, this.productService.productConfiguration.version)))) { + return Promise.reject(new Error(nls.localize('incompatible2', "Unable to install version '{2}' of extension '{0}' as it is not compatible with Azure Data Studio '{1}'.", extension.gallery!.identifier.id, this.productService.productConfiguration.version, gallery.version))); } return this.installWithProgress(async () => { @@ -1077,7 +1076,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension get allowedBadgeProviders(): string[] { if (!this._extensionAllowedBadgeProviders) { - this._extensionAllowedBadgeProviders = (this.productService.extensionAllowedBadgeProviders || []).map(s => s.toLowerCase()); + this._extensionAllowedBadgeProviders = (this.productService.productConfiguration.extensionAllowedBadgeProviders || []).map(s => s.toLowerCase()); } return this._extensionAllowedBadgeProviders; } diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts index 05922a0800..ddc6e307d7 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts @@ -14,7 +14,6 @@ import { IExtensionTipsService, ExtensionRecommendationReason, IExtensionsConfig import { IModelService } from 'vs/editor/common/services/modelService'; import { ITextModel } from 'vs/editor/common/model'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import product from 'vs/platform/product/node/product'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; // {{SQL CARBON EDIT}} import { ShowRecommendedExtensionsAction, InstallWorkspaceRecommendedExtensionsAction, InstallRecommendedExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; @@ -28,13 +27,11 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IExtensionsConfiguration, ConfigurationKey, ShowRecommendationsOnlyOnDemandKey, IExtensionsViewlet, IExtensionsWorkbenchService, EXTENSIONS_CONFIG, ExtensionsPolicyKey, ExtensionsPolicy } from 'vs/workbench/contrib/extensions/common/extensions'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import * as pfs from 'vs/base/node/pfs'; import * as os from 'os'; import { flatten, distinct, shuffle, coalesce } from 'vs/base/common/arrays'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { guessMimeTypes, MIME_UNKNOWN } from 'vs/base/common/mime'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { getHashedRemotesFromUri } from 'vs/workbench/contrib/stats/electron-browser/workspaceStats'; import { IRequestService, asJson } from 'vs/platform/request/common/request'; import { isNumber } from 'vs/base/common/types'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -47,11 +44,11 @@ import { IExperimentService, ExperimentActionType, ExperimentState } from 'vs/wo import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { extname } from 'vs/base/common/resources'; -import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { IExeBasedExtensionTip } from 'vs/platform/product/common/product'; +import { IExeBasedExtensionTip, IProductService } from 'vs/platform/product/common/product'; import { timeout } from 'vs/base/common/async'; -import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry'; -import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys'; +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'; const milliSecondsInADay = 1000 * 60 * 60 * 24; const choiceNever = localize('neverShowAgain', "Don't Show Again"); @@ -117,8 +114,9 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, @IExtensionsWorkbenchService private readonly extensionWorkbenchService: IExtensionsWorkbenchService, @IExperimentService private readonly experimentService: IExperimentService, - @ITextFileService private readonly textFileService: ITextFileService, - @IAdsTelemetryService private readonly adsTelemetryService: IAdsTelemetryService + @IAdsTelemetryService private readonly adsTelemetryService: IAdsTelemetryService, // {{SQL CARBON EDIT}} + @IWorkspaceStatsService private readonly workspaceStatsService: IWorkspaceStatsService, + @IProductService private readonly productService: IProductService ) { super(); @@ -128,8 +126,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe return; } - if (product.extensionsGallery && product.extensionsGallery.recommendationsUrl) { - this._extensionsRecommendationsUrl = product.extensionsGallery.recommendationsUrl; + if (this.productService.productConfiguration.extensionsGallery && this.productService.productConfiguration.extensionsGallery.recommendationsUrl) { + this._extensionsRecommendationsUrl = this.productService.productConfiguration.extensionsGallery.recommendationsUrl; } this.sessionSeed = +new Date(); @@ -258,7 +256,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe } getKeymapRecommendations(): IExtensionRecommendation[] { - return (product.keymapExtensionTips || []) + return (this.productService.productConfiguration.keymapExtensionTips || []) .filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId)) .map(extensionId => ({ extensionId, sources: ['application'] })); } @@ -615,10 +613,10 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe return Object.keys(this._fileBasedRecommendations) .sort((a, b) => { if (this._fileBasedRecommendations[a].recommendedTime === this._fileBasedRecommendations[b].recommendedTime) { - if (!product.extensionImportantTips || caseInsensitiveGet(product.extensionImportantTips, a)) { + if (!this.productService.productConfiguration.extensionImportantTips || caseInsensitiveGet(this.productService.productConfiguration.extensionImportantTips, a)) { return -1; } - if (caseInsensitiveGet(product.extensionImportantTips, b)) { + if (caseInsensitiveGet(this.productService.productConfiguration.extensionImportantTips, b)) { return 1; } } @@ -629,13 +627,13 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe } /** - * Parse all file based recommendations from product.extensionTips - * Retire existing recommendations if they are older than a week or are not part of product.extensionTips anymore + * Parse all file based recommendations from this.productService.productConfiguration.extensionTips + * Retire existing recommendations if they are older than a week or are not part of this.productService.productConfiguration.extensionTips anymore */ private fetchFileBasedRecommendations() { - const extensionTips = product.extensionTips; + const extensionTips = this.productService.productConfiguration.extensionTips; // {{SQL CARBON EDIT}} - this._recommendations = product.recommendedExtensions; + this._recommendations = this.productService.productConfiguration.recommendedExtensions; if (!extensionTips) { return; } @@ -652,7 +650,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe } }); - forEach(product.extensionImportantTips, entry => { + forEach(this.productService.productConfiguration.extensionImportantTips, entry => { let { key: id, value } = entry; const { pattern } = value; let ids = this._availableRecommendations[pattern]; @@ -714,7 +712,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe let { key: pattern, value: ids } = entry; if (match(pattern, model.uri.toString())) { for (let id of ids) { - if (caseInsensitiveGet(product.extensionImportantTips, id)) { + if (caseInsensitiveGet(this.productService.productConfiguration.extensionImportantTips, id)) { recommendationsToSuggest.push(id); } const filedBasedRecommendation = this._fileBasedRecommendations[id.toLowerCase()] || { recommendedTime: now, sources: [] }; @@ -768,7 +766,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe } const id = recommendationsToSuggest[0]; - const entry = caseInsensitiveGet(product.extensionImportantTips, id); + const entry = caseInsensitiveGet(this.productService.productConfiguration.extensionImportantTips, id); if (!entry) { return false; } @@ -994,14 +992,14 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe } /** - * If user has any of the tools listed in product.exeBasedExtensionTips, fetch corresponding recommendations + * If user has any of the tools listed in this.productService.productConfiguration.exeBasedExtensionTips, fetch corresponding recommendations */ private fetchExecutableRecommendations(important: boolean): Promise { const homeDir = os.homedir(); let foundExecutables: Set = new Set(); let findExecutable = (exeName: string, tip: IExeBasedExtensionTip, path: string) => { - return pfs.fileExists(path).then(exists => { + return this.fileService.exists(URI.file(path)).then(exists => { if (exists && !foundExecutables.has(exeName)) { foundExecutables.add(exeName); (tip['recommendations'] || []).forEach(extensionId => { @@ -1018,7 +1016,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe let promises: Promise[] = []; // Loop through recommended extensions - forEach(product.exeBasedExtensionTips, entry => { + forEach(this.productService.productConfiguration.exeBasedExtensionTips, entry => { if (typeof entry.value !== 'object' || !Array.isArray(entry.value['recommendations'])) { return; } @@ -1090,7 +1088,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe const storageKey = 'extensionsAssistant/dynamicWorkspaceRecommendations'; const workspaceUri = this.contextService.getWorkspace().folders[0].uri; - return Promise.all([getHashedRemotesFromUri(workspaceUri, this.fileService, this.textFileService, false), getHashedRemotesFromUri(workspaceUri, this.fileService, this.textFileService, true)]).then(([hashedRemotes1, hashedRemotes2]) => { + return Promise.all([this.workspaceStatsService.getHashedRemotesFromUri(workspaceUri, false), this.workspaceStatsService.getHashedRemotesFromUri(workspaceUri, true)]).then(([hashedRemotes1, hashedRemotes2]) => { const hashedRemotes = (hashedRemotes1 || []).concat(hashedRemotes2 || []); if (!hashedRemotes.length) { return undefined; @@ -1248,7 +1246,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe return Promise.reject(new Error(localize('scenarioTypeUndefined', 'The scenario type for extension recommendations must be provided.'))); } - return Promise.resolve((product.recommendedExtensionsByScenario[scenarioType] || []) + return Promise.resolve((this.productService.productConfiguration.recommendedExtensionsByScenario[scenarioType] || []) .filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId)) .map(extensionId => ({ extensionId, sources: ['application'] }))); } diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts index 88308a656c..1f48f715f4 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts @@ -52,6 +52,7 @@ import { NullLogService } from 'vs/platform/log/common/log'; import { Schemas } from 'vs/base/common/network'; import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; import { IFileService } from 'vs/platform/files/common/files'; +import { IProductService } from 'vs/platform/product/common/product'; const mockExtensionGallery: IGalleryExtension[] = [ aGalleryExtension('MockExtension1', { diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts index f389a5ea57..c63e9f4a05 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts @@ -35,8 +35,7 @@ import { URLService } from 'vs/platform/url/common/urlService'; import { URI } from 'vs/base/common/uri'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { SinonStub } from 'sinon'; -import { IExperimentService, ExperimentState, ExperimentActionType } from 'vs/workbench/contrib/experiments/common/experimentService'; -import { ExperimentService } from 'vs/workbench/contrib/experiments/electron-browser/experimentService'; +import { IExperimentService, ExperimentState, ExperimentActionType, ExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; import { ExtensionIdentifier, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; diff --git a/src/vs/workbench/contrib/feedback/browser/feedback.ts b/src/vs/workbench/contrib/feedback/browser/feedback.ts index 76ad9b4801..aa9d9e015b 100644 --- a/src/vs/workbench/contrib/feedback/browser/feedback.ts +++ b/src/vs/workbench/contrib/feedback/browser/feedback.ts @@ -73,8 +73,8 @@ export class FeedbackDropdown extends Dropdown { this.feedbackDelegate = options.feedbackService; this.maxFeedbackCharacters = this.feedbackDelegate.getCharacterLimit(this.sentiment); - if (productService.sendASmile) { - this.requestFeatureLink = productService.sendASmile.requestFeatureUrl; + if (productService.productConfiguration.sendASmile) { + this.requestFeatureLink = productService.productConfiguration.sendASmile.requestFeatureUrl; } this.integrityService.isPure().then(result => { diff --git a/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts b/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts index e725cab005..5b10eb3202 100644 --- a/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts +++ b/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts @@ -58,7 +58,7 @@ export class FeedbackStatusbarConribution extends Disposable implements IWorkben ) { super(); - if (productService.sendASmile) { + if (productService.productConfiguration.sendASmile) { this.entry = this._register(statusbarService.addEntry(this.getStatusEntry(), 'status.feedback', localize('status.feedback', "Tweet Feedback"), StatusbarAlignment.RIGHT, -100 /* towards the end of the right hand side */)); CommandsRegistry.registerCommand('_feedback.open', () => this.toggleFeedback()); diff --git a/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts b/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts index 22107f7dfe..10a258a03a 100644 --- a/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts +++ b/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts @@ -7,16 +7,15 @@ import * as nls from 'vs/nls'; import { BaseBinaryResourceEditor } from 'vs/workbench/browser/parts/editor/binaryEditor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IWindowsService } from 'vs/platform/windows/common/windows'; import { EditorInput, EditorOptions } from 'vs/workbench/common/editor'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; -import { URI } from 'vs/base/common/uri'; import { BINARY_FILE_EDITOR_ID } from 'vs/workbench/contrib/files/common/files'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; /** * An implementation of editor for binary files like images. @@ -28,7 +27,7 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor { constructor( @ITelemetryService telemetryService: ITelemetryService, @IThemeService themeService: IThemeService, - @IWindowsService private readonly windowsService: IWindowsService, + @IOpenerService private readonly openerService: IOpenerService, @IEditorService private readonly editorService: IEditorService, @IStorageService storageService: IStorageService, @IFileService fileService: IFileService, @@ -39,7 +38,7 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor { BinaryFileEditor.ID, { openInternal: (input, options) => this.openInternal(input, options), - openExternal: resource => this.openExternal(resource) + openExternal: resource => this.openerService.openExternal(resource) }, telemetryService, themeService, @@ -58,13 +57,6 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor { } } - private async openExternal(resource: URI): Promise { - const didOpen = await this.windowsService.openExternal(resource.toString()); - if (!didOpen) { - return this.windowsService.showItemInFolder(resource); - } - } - getTitle(): string | null { return this.input ? this.input.getName() : nls.localize('binaryFileEditor', "Binary File Viewer"); } diff --git a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts index b883d0c4d0..4df07f409b 100644 --- a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts +++ b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts @@ -34,6 +34,7 @@ import { ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet'; import { KeyChord, KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { Registry } from 'vs/platform/registry/common/platform'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; +import { withUndefinedAsNull } from 'vs/base/common/types'; export class ExplorerViewletViewsContribution extends Disposable implements IWorkbenchContribution { @@ -202,7 +203,7 @@ export class ExplorerViewlet extends ViewContainerViewlet { openEditorsView.setStructuralRefreshDelay(delay); } - let openedEditor: IEditor | null = null; + let openedEditor: IEditor | undefined; try { openedEditor = await this.editorService.openEditor(editor, options, group); } catch (error) { @@ -214,7 +215,7 @@ export class ExplorerViewlet extends ViewContainerViewlet { } } - return openedEditor; + return withUndefinedAsNull(openedEditor); }); const explorerInstantiator = this.instantiationService.createChild(new ServiceCollection([IEditorService, delegatingEditorService])); diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index 157395c93e..2056a95568 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -503,7 +503,7 @@ export class GlobalCompareResourcesAction extends Action { override: this.editorService.openEditor({ leftResource: activeResource, rightResource: resource - }).then(() => null) + }).then(() => undefined) }; } diff --git a/src/vs/workbench/contrib/files/common/explorerService.ts b/src/vs/workbench/contrib/files/common/explorerService.ts index 40741dded7..38874db536 100644 --- a/src/vs/workbench/contrib/files/common/explorerService.ts +++ b/src/vs/workbench/contrib/files/common/explorerService.ts @@ -158,7 +158,11 @@ export class ExplorerService implements IExplorerService { // Stat needs to be resolved first and then revealed const options: IResolveFileOptions = { resolveTo: [resource], resolveMetadata: this.sortOrder === 'modified' }; const workspaceFolder = this.contextService.getWorkspaceFolder(resource); - const rootUri = workspaceFolder ? workspaceFolder.uri : this.roots[0].resource; + if (workspaceFolder === null) { + return Promise.resolve(undefined); + } + const rootUri = workspaceFolder.uri; + const root = this.roots.filter(r => r.resource.toString() === rootUri.toString()).pop()!; try { diff --git a/src/vs/workbench/contrib/markers/browser/markersPanel.ts b/src/vs/workbench/contrib/markers/browser/markersPanel.ts index 3702b34c62..89719cb974 100644 --- a/src/vs/workbench/contrib/markers/browser/markersPanel.ts +++ b/src/vs/workbench/contrib/markers/browser/markersPanel.ts @@ -534,8 +534,10 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { const span1 = dom.append(container, dom.$('span')); span1.textContent = Messages.MARKERS_PANEL_NO_PROBLEMS_FILTERS; const link = dom.append(container, dom.$('a.messageAction')); - link.textContent = localize('clearFilter', "Clear Filter."); + link.textContent = localize('clearFilter', "Clear Filter"); link.setAttribute('tabIndex', '0'); + const span2 = dom.append(container, dom.$('span')); + span2.textContent = '.'; dom.addStandardDisposableListener(link, dom.EventType.CLICK, () => this.filterAction.filterText = ''); dom.addStandardDisposableListener(link, dom.EventType.KEY_DOWN, (e: IKeyboardEvent) => { if (e.equals(KeyCode.Enter) || e.equals(KeyCode.Space)) { diff --git a/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts b/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts index f450babf49..79b1948318 100644 --- a/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts +++ b/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts @@ -158,9 +158,9 @@ export class KeyboardLayoutPickerAction extends Action { await this.fileService.resolve(file).then(undefined, (error) => { return this.fileService.createFile(file, VSBuffer.fromString(KeyboardLayoutPickerAction.DEFAULT_CONTENT)); - }).then((stat): Promise | null => { + }).then((stat): Promise | undefined => { if (!stat) { - return null; + return undefined; } return this.editorService.openEditor({ resource: stat.resource, diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts b/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts index e428e6df32..278357d9f7 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts @@ -225,7 +225,7 @@ export class OpenFolderSettingsAction extends Action { return this.preferencesService.openFolderSettings(workspaceFolder.uri); } - return null; + return undefined; }); } diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts index f1eab6c64d..31d3292bf7 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts @@ -73,7 +73,7 @@ export class PreferencesSearchService extends Disposable implements IPreferences }; } else { return { - urlBase: this.productService.settingsSearchUrl + urlBase: this.productService.productConfiguration.settingsSearchUrl }; } } @@ -364,7 +364,7 @@ class RemoteSearchProvider implements ISearchProvider { const extensions = await this.installedExtensions; const filters = this.options.newExtensionsOnly ? [`diminish eq 'latest'`] : - this.getVersionFilters(extensions, this.productService.settingsSearchBuildId); + this.getVersionFilters(extensions, this.productService.productConfiguration.settingsSearchBuildId); const filterStr = filters .slice(filterPage * RemoteSearchProvider.MAX_REQUEST_FILTERS, (filterPage + 1) * RemoteSearchProvider.MAX_REQUEST_FILTERS) @@ -563,4 +563,4 @@ export class SettingMatches { endColumn: setting.valueRange.startColumn + match.end + 1 }; } -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts index 0f68a89da9..ca50155931 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts @@ -36,7 +36,7 @@ import { ISettingsGroup } from 'vs/workbench/services/preferences/common/prefere export class SettingsHeaderWidget extends Widget implements IViewZone { - private id: number; + private id: string; private _domNode: HTMLElement; protected titleContainer: HTMLElement; @@ -121,7 +121,7 @@ export class DefaultSettingsHeaderWidget extends SettingsHeaderWidget { export class SettingsGroupTitleWidget extends Widget implements IViewZone { - private id: number; + private id: string; private _afterLineNumber: number; private _domNode: HTMLElement; diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 4eead4fe9a..b01c9291cc 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -466,12 +466,12 @@ export class SettingsEditor2 extends BaseEditor { } } - switchToSettingsFile(): Promise { + switchToSettingsFile(): Promise { const query = parseQuery(this.searchWidget.getValue()); return this.openSettingsFile(query.query); } - private openSettingsFile(query?: string): Promise { + private openSettingsFile(query?: string): Promise { const currentSettingsTarget = this.settingsTargetsWidget.settingsTarget; const options: ISettingsEditorOptions = { query }; diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 0e612186e0..71c234b6ce 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as DOM from 'vs/base/browser/dom'; -import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer'; +import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; diff --git a/src/vs/workbench/contrib/remote/browser/help-documentation-dark.svg b/src/vs/workbench/contrib/remote/browser/help-documentation-dark.svg new file mode 100644 index 0000000000..2673902c68 --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-documentation-dark.svg @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-documentation-hc.svg b/src/vs/workbench/contrib/remote/browser/help-documentation-hc.svg new file mode 100644 index 0000000000..e8dc8205ba --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-documentation-hc.svg @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-documentation-light.svg b/src/vs/workbench/contrib/remote/browser/help-documentation-light.svg new file mode 100644 index 0000000000..4a3009baee --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-documentation-light.svg @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-feedback-dark.svg b/src/vs/workbench/contrib/remote/browser/help-feedback-dark.svg new file mode 100644 index 0000000000..5d99408934 --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-feedback-dark.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-feedback-hc.svg b/src/vs/workbench/contrib/remote/browser/help-feedback-hc.svg new file mode 100644 index 0000000000..941430e9dd --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-feedback-hc.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-feedback-light.svg b/src/vs/workbench/contrib/remote/browser/help-feedback-light.svg new file mode 100644 index 0000000000..72437202b7 --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-feedback-light.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-getting-started-dark.svg b/src/vs/workbench/contrib/remote/browser/help-getting-started-dark.svg new file mode 100644 index 0000000000..0ea65d8319 --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-getting-started-dark.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-getting-started-hc.svg b/src/vs/workbench/contrib/remote/browser/help-getting-started-hc.svg new file mode 100644 index 0000000000..5bb05d3d8c --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-getting-started-hc.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-getting-started-light.svg b/src/vs/workbench/contrib/remote/browser/help-getting-started-light.svg new file mode 100644 index 0000000000..46cde7f7cc --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-getting-started-light.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-report-issue-dark.svg b/src/vs/workbench/contrib/remote/browser/help-report-issue-dark.svg new file mode 100644 index 0000000000..0117ceb7de --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-report-issue-dark.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-report-issue-hc.svg b/src/vs/workbench/contrib/remote/browser/help-report-issue-hc.svg new file mode 100644 index 0000000000..b0c521b7dc --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-report-issue-hc.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-report-issue-light.svg b/src/vs/workbench/contrib/remote/browser/help-report-issue-light.svg new file mode 100644 index 0000000000..5da9322b6a --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-report-issue-light.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-review-issues-dark.svg b/src/vs/workbench/contrib/remote/browser/help-review-issues-dark.svg new file mode 100644 index 0000000000..21eec9cbcb --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-review-issues-dark.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-review-issues-hc.svg b/src/vs/workbench/contrib/remote/browser/help-review-issues-hc.svg new file mode 100644 index 0000000000..94013ea52a --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-review-issues-hc.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-review-issues-light.svg b/src/vs/workbench/contrib/remote/browser/help-review-issues-light.svg new file mode 100644 index 0000000000..826d0eefbf --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-review-issues-light.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/vs/workbench/contrib/remote/browser/remote-activity-bar.svg b/src/vs/workbench/contrib/remote/browser/remote-activity-bar.svg new file mode 100644 index 0000000000..029e6b051c --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/remote-activity-bar.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts new file mode 100644 index 0000000000..c367144aa5 --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -0,0 +1,419 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./remoteViewlet'; +import * as nls from 'vs/nls'; +import * as dom from 'vs/base/browser/dom'; +import { URI } from 'vs/base/common/uri'; +import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { ViewContainerViewlet } from 'vs/workbench/browser/parts/views/viewsViewlet'; +import { VIEWLET_ID, VIEW_CONTAINER } from 'vs/workbench/contrib/remote/common/remote.contribution'; +import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; +import { IAddedViewDescriptorRef } from 'vs/workbench/browser/parts/views/views'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IViewDescriptor, IViewsRegistry, Extensions } from 'vs/workbench/common/views'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import { WorkbenchAsyncDataTree, TreeResourceNavigator2 } from 'vs/platform/list/browser/listService'; +import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; +import { ITreeRenderer, ITreeNode, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; +import { Event } from 'vs/base/common/event'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor } from 'vs/workbench/browser/viewlet'; + +interface HelpInformation { + extensionDescription: IExtensionDescription; + getStarted?: string; + documentation?: string; + feedback?: string; + issues?: string; +} + +const remoteHelpExtPoint = ExtensionsRegistry.registerExtensionPoint({ + extensionPoint: 'remoteHelp', + jsonSchema: { + description: nls.localize('RemoteHelpInformationExtPoint', 'Contributes help information for Remote'), + type: 'object', + properties: { + 'getStarted': { + description: nls.localize('RemoteHelpInformationExtPoint.getStarted', "The url to your project's Getting Started page"), + type: 'string' + }, + 'documentation': { + description: nls.localize('RemoteHelpInformationExtPoint.documentation', "The url to your project's documentation page"), + type: 'string' + }, + 'feedback': { + description: nls.localize('RemoteHelpInformationExtPoint.feedback', "The url to your project's feedback reporter"), + type: 'string' + }, + 'issues': { + description: nls.localize('RemoteHelpInformationExtPoint.issues', "The url to your project's issues list"), + type: 'string' + } + } + } +}); + +interface IViewModel { + helpInformations: HelpInformation[]; +} + +class HelpTreeVirtualDelegate implements IListVirtualDelegate { + getHeight(element: IHelpItem): number { + return 22; + } + + getTemplateId(element: IHelpItem): string { + return 'HelpItemTemplate'; + } +} + +interface IHelpItemTemplateData { + parent: HTMLElement; + icon: HTMLElement; +} + +class HelpTreeRenderer implements ITreeRenderer { + templateId: string = 'HelpItemTemplate'; + + renderTemplate(container: HTMLElement): IHelpItemTemplateData { + dom.addClass(container, 'remote-help-tree-node-item'); + + const icon = dom.append(container, dom.$('.remote-help-tree-node-item-icon')); + + const data = Object.create(null); + data.parent = container; + data.icon = icon; + + return data; + } + + renderElement(element: ITreeNode, index: number, templateData: IHelpItemTemplateData, height: number | undefined): void { + const container = templateData.parent; + dom.append(container, templateData.icon); + dom.addClass(templateData.icon, element.element.key); + const labelContainer = dom.append(container, dom.$('.help-item-label')); + labelContainer.innerText = element.element.label; + } + + disposeTemplate(templateData: IHelpItemTemplateData): void { + + } +} + +class HelpDataSource implements IAsyncDataSource { + hasChildren(element: any) { + return element instanceof HelpModel; + } + + getChildren(element: any) { + if (element instanceof HelpModel) { + return element.items; + } + + return []; + } +} + +interface IHelpItem { + key: string; + label: string; + handleClick(): Promise; +} + +class HelpItem implements IHelpItem { + constructor( + public key: string, + public label: string, + public values: { extensionDescription: IExtensionDescription; url: string }[], + private openerService: IOpenerService, + private quickInputService: IQuickInputService + ) { + } + + async handleClick() { + if (this.values.length > 1) { + let actions = this.values.map(value => { + return { + label: value.extensionDescription.displayName || value.extensionDescription.identifier.value, + description: value.url + }; + }); + + const action = await this.quickInputService.pick(actions, { placeHolder: nls.localize('pickRemoteExtension', "Select url to open") }); + + if (action) { + await this.openerService.open(URI.parse(action.label)); + } + } else { + await this.openerService.open(URI.parse(this.values[0].url)); + } + } +} + +class IssueReporterItem implements IHelpItem { + constructor( + public key: string, + public label: string, + public extensionDescriptions: IExtensionDescription[], + private quickInputService: IQuickInputService, + private commandService: ICommandService + ) { + } + + async handleClick() { + if (this.extensionDescriptions.length > 1) { + let actions = this.extensionDescriptions.map(extension => { + return { + label: extension.displayName || extension.identifier.value, + identifier: extension.identifier + }; + }); + + const action = await this.quickInputService.pick(actions, { placeHolder: nls.localize('pickRemoteExtensionToReportIssue', "Select an extension to report issue") }); + + if (action) { + await this.commandService.executeCommand('workbench.action.openIssueReporter', [action.identifier.value]); + } + } else { + await this.commandService.executeCommand('workbench.action.openIssueReporter', [this.extensionDescriptions[0].identifier.value]); + } + } +} + +class HelpModel { + items: IHelpItem[]; + + constructor( + viewModel: IViewModel, + openerService: IOpenerService, + quickInputService: IQuickInputService, + commandService: ICommandService + ) { + let helpItems: IHelpItem[] = []; + const getStarted = viewModel.helpInformations.filter(info => info.getStarted); + + if (getStarted.length) { + helpItems.push(new HelpItem( + 'getStarted', + nls.localize('remote.help.getStarted', "Get Started"), + getStarted.map((info: HelpInformation) => ({ + extensionDescription: info.extensionDescription, + url: info.getStarted! + })), + openerService, + quickInputService + )); + } + + const documentation = viewModel.helpInformations.filter(info => info.documentation); + + if (documentation.length) { + helpItems.push(new HelpItem( + 'documentation', + nls.localize('remote.help.documentation', "Read Documentation"), + documentation.map((info: HelpInformation) => ({ + extensionDescription: info.extensionDescription, + url: info.documentation! + })), + openerService, + quickInputService + )); + } + + const feedback = viewModel.helpInformations.filter(info => info.feedback); + + if (feedback.length) { + helpItems.push(new HelpItem( + 'feedback', + nls.localize('remote.help.feedback', "Provide Feedback"), + feedback.map((info: HelpInformation) => ({ + extensionDescription: info.extensionDescription, + url: info.feedback! + })), + openerService, + quickInputService + )); + } + + const issues = viewModel.helpInformations.filter(info => info.issues); + + if (issues.length) { + helpItems.push(new HelpItem( + 'issues', + nls.localize('remote.help.issues', "Review Issues"), + issues.map((info: HelpInformation) => ({ + extensionDescription: info.extensionDescription, + url: info.issues! + })), + openerService, + quickInputService + )); + } + + if (helpItems.length) { + helpItems.push(new IssueReporterItem( + 'issueReporter', + nls.localize('remote.help.report', "Report Issue"), + viewModel.helpInformations.map(info => info.extensionDescription), + quickInputService, + commandService + )); + } + + if (helpItems.length) { + this.items = helpItems; + } + } +} + +class HelpPanel extends ViewletPanel { + static readonly ID = '~remote.helpPanel'; + static readonly TITLE = nls.localize('remote.help', "Help and feedback"); + private tree!: WorkbenchAsyncDataTree; + + constructor( + protected viewModel: IViewModel, + options: IViewletPanelOptions, + @IKeybindingService protected keybindingService: IKeybindingService, + @IContextMenuService protected contextMenuService: IContextMenuService, + @IContextKeyService protected contextKeyService: IContextKeyService, + @IConfigurationService protected configurationService: IConfigurationService, + @IInstantiationService protected readonly instantiationService: IInstantiationService, + @IOpenerService protected openerService: IOpenerService, + @IQuickInputService protected quickInputService: IQuickInputService, + @ICommandService protected commandService: ICommandService + + + ) { + super(options, keybindingService, contextMenuService, configurationService, contextKeyService); + } + + protected renderBody(container: HTMLElement): void { + dom.addClass(container, 'remote-help'); + const treeContainer = document.createElement('div'); + dom.addClass(treeContainer, 'remote-help-content'); + container.appendChild(treeContainer); + + this.tree = this.instantiationService.createInstance(WorkbenchAsyncDataTree, + treeContainer, + new HelpTreeVirtualDelegate(), + [new HelpTreeRenderer()], + new HelpDataSource(), + { + keyboardSupport: true, + } + ); + + const model = new HelpModel(this.viewModel, this.openerService, this.quickInputService, this.commandService); + + this.tree.setInput(model); + + const helpItemNavigator = this._register(new TreeResourceNavigator2(this.tree, { openOnFocus: false, openOnSelection: false })); + + this._register(Event.debounce(helpItemNavigator.onDidOpenResource, (last, event) => event, 75, true)(e => { + e.element.handleClick(); + })); + } + + protected layoutBody(height: number, width: number): void { + this.tree.layout(height, width); + } +} + +class HelpPanelDescriptor implements IViewDescriptor { + readonly id = HelpPanel.ID; + readonly name = HelpPanel.TITLE; + readonly ctorDescriptor: { ctor: any, arguments?: any[] }; + readonly canToggleVisibility = true; + readonly hideByDefault = false; + readonly workspace = true; + + constructor(viewModel: IViewModel) { + this.ctorDescriptor = { ctor: HelpPanel, arguments: [viewModel] }; + } +} + + +export class RemoteViewlet extends ViewContainerViewlet implements IViewModel { + private helpPanelDescriptor = new HelpPanelDescriptor(this); + + helpInformations: HelpInformation[] = []; + + constructor( + @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, + @ITelemetryService telemetryService: ITelemetryService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IStorageService storageService: IStorageService, + @IConfigurationService configurationService: IConfigurationService, + @IInstantiationService instantiationService: IInstantiationService, + @IThemeService themeService: IThemeService, + @IContextMenuService contextMenuService: IContextMenuService, + @IExtensionService extensionService: IExtensionService + ) { + super(VIEWLET_ID, `${VIEWLET_ID}.state`, true, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService); + + remoteHelpExtPoint.setHandler((extensions) => { + let helpInformation: HelpInformation[] = []; + for (let extension of extensions) { + this._handleRemoteInfoExtensionPoint(extension, helpInformation); + } + + this.helpInformations = helpInformation; + + const viewsRegistry = Registry.as(Extensions.ViewsRegistry); + if (this.helpInformations.length) { + viewsRegistry.registerViews([this.helpPanelDescriptor], VIEW_CONTAINER); + } else { + viewsRegistry.deregisterViews([this.helpPanelDescriptor], VIEW_CONTAINER); + } + }); + } + + private _handleRemoteInfoExtensionPoint(extension: IExtensionPointUser, helpInformation: HelpInformation[]) { + if (!extension.value.documentation && !extension.value.feedback && !extension.value.getStarted && !extension.value.issues) { + return; + } + + helpInformation.push({ + extensionDescription: extension.description, + getStarted: extension.value.getStarted, + documentation: extension.value.documentation, + feedback: extension.value.feedback, + issues: extension.value.issues + }); + } + + onDidAddViews(added: IAddedViewDescriptorRef[]): ViewletPanel[] { + // too late, already added to the view model + return super.onDidAddViews(added); + } + + getTitle(): string { + const title = nls.localize('remote.explorer', "Remote Explorer"); + return title; + } +} + +Registry.as(ViewletExtensions.Viewlets).registerViewlet(new ViewletDescriptor( + RemoteViewlet, + VIEWLET_ID, + nls.localize('remote.explorer', "Remote Explorer"), + 'remote', + 4 +)); diff --git a/src/vs/workbench/contrib/remote/browser/remoteViewlet.css b/src/vs/workbench/contrib/remote/browser/remoteViewlet.css new file mode 100644 index 0000000000..a278b060ec --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/remoteViewlet.css @@ -0,0 +1,92 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench .activitybar>.content .monaco-action-bar .action-label.remote { + -webkit-mask: url('remote-activity-bar.svg') no-repeat 50% 50%; +} + +.remote-help-content .monaco-list .monaco-list-row .remote-help-tree-node-item { + display: flex; + height: 22px; + line-height: 22px; + flex: 1; + text-overflow: ellipsis; + overflow: hidden; + flex-wrap: nowrap; +} + +.remote-help-content .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon { + background-size: 16px; + background-position: left center; + background-repeat: no-repeat; + padding-right: 6px; + width: 16px; + height: 22px; + -webkit-font-smoothing: antialiased; +} + +.remote-help-content .monaco-list .monaco-list-row .monaco-tl-twistie { + width: 0px !important; +} + +.vs .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.getStarted { + background-image: url('help-getting-started-light.svg') +} + +.vs .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.documentation { + background-image: url('help-documentation-light.svg') +} + +.vs .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.feedback { + background-image: url('help-feedback-light.svg') +} + +.vs .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.issues { + background-image: url('help-review-issues-light.svg') +} + +.vs .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.issueReporter { + background-image: url('help-report-issue-light.svg') +} + +.vs-dark .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.getStarted { + background-image: url('help-getting-started-dark.svg') +} + +.vs-dark .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.documentation { + background-image: url('help-documentation-dark.svg') +} + +.vs-dark .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.feedback { + background-image: url('help-feedback-dark.svg') +} + +.vs-dark .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.issues { + background-image: url('help-review-issues-dark.svg') +} + +.vs-dark .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.issueReporter { + background-image: url('help-report-issue-dark.svg') +} + +.hc-black .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.getStarted { + background-image: url('help-getting-started-hc.svg') +} + +.hc-black .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.documentation { + background-image: url('help-documentation-hc.svg') +} + +.hc-black .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.feedback { + background-image: url('help-feedback-hc.svg') +} + +.hc-black .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.issues { + background-image: url('help-review-issues-hc.svg') +} + +.hc-black .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.issueReporter { + background-image: url('help-report-issue-hc.svg') +} diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts index 7be158d995..e20289161a 100644 --- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts @@ -17,6 +17,34 @@ import { IOutputChannelRegistry, Extensions as OutputExt, } from 'vs/workbench/c import { localize } from 'vs/nls'; import { joinPath } from 'vs/base/common/resources'; import { Disposable } from 'vs/base/common/lifecycle'; +import { ViewContainer, IViewContainersRegistry, Extensions as ViewContainerExtensions } from 'vs/workbench/common/views'; + +export const VIEWLET_ID = 'workbench.view.remote'; +export const VIEW_CONTAINER: ViewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer( + VIEWLET_ID, + true, + undefined, + { + getOrder: (group?: string) => { + if (!group) { + return undefined; // {{SQL CARBON EDIT}} strict-null-checks + } + + let matches = /^targets@(\d+)$/.exec(group); + if (matches) { + return -1000; + } + + matches = /^details@(\d+)$/.exec(group); + + if (matches) { + return -500; + } + + return undefined; // {{SQL CARBON EDIT}} strict-null-checks + } + } +); export class LabelContribution implements IWorkbenchContribution { constructor( diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index acb42d434d..38c5d1e192 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -1455,7 +1455,7 @@ export class SearchView extends ViewletPanel { this.openSettings('.exclude'); } - private openSettings(query: string): Promise { + private openSettings(query: string): Promise { const options: ISettingsEditorOptions = { query }; return this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? this.preferencesService.openWorkspaceSettings(undefined, options) : diff --git a/src/vs/workbench/contrib/stats/common/workspaceStats.ts b/src/vs/workbench/contrib/stats/common/workspaceStats.ts new file mode 100644 index 0000000000..6b7016863e --- /dev/null +++ b/src/vs/workbench/contrib/stats/common/workspaceStats.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { WorkbenchState, IWorkspace } from 'vs/platform/workspace/common/workspace'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { URI } from 'vs/base/common/uri'; + +export type Tags = { [index: string]: boolean | number | string | undefined }; + +export const IWorkspaceStatsService = createDecorator('workspaceStatsService'); + +export interface IWorkspaceStatsService { + _serviceBrand: any; + + getTags(): Promise; + + /** + * Returns an id for the workspace, different from the id returned by the context service. A hash based + * on the folder uri or workspace configuration, not time-based, and undefined for empty workspaces. + */ + getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): string | undefined; + + getHashedRemotesFromUri(workspaceUri: URI, stripEndingDotGit?: boolean): Promise; +} diff --git a/src/vs/workbench/contrib/stats/electron-browser/workspaceStats.ts b/src/vs/workbench/contrib/stats/electron-browser/workspaceStats.ts index 256ff6fb24..ced540f7ee 100644 --- a/src/vs/workbench/contrib/stats/electron-browser/workspaceStats.ts +++ b/src/vs/workbench/contrib/stats/electron-browser/workspaceStats.ts @@ -14,7 +14,7 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { endsWith } from 'vs/base/common/strings'; import { ITextFileService, } from 'vs/workbench/services/textfile/common/textfiles'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; -import { IWorkspaceStatsService, Tags } from 'vs/workbench/contrib/stats/electron-browser/workspaceStatsService'; +import { IWorkspaceStatsService, Tags } from 'vs/workbench/contrib/stats/common/workspaceStats'; import { IWorkspaceInformation } from 'vs/platform/diagnostics/common/diagnosticsService'; const SshProtocolMatcher = /^([^@:]+@)?([^:]+):/; @@ -136,20 +136,6 @@ export function getHashedRemotesFromConfig(text: string, stripEndingDotGit: bool }); } -export function getHashedRemotesFromUri(workspaceUri: URI, fileService: IFileService, textFileService: ITextFileService, stripEndingDotGit: boolean = false): Promise { - const path = workspaceUri.path; - const uri = workspaceUri.with({ path: `${path !== '/' ? path : ''}/.git/config` }); - return fileService.exists(uri).then(exists => { - if (!exists) { - return []; - } - return textFileService.read(uri, { acceptTextOnly: true }).then( - content => getHashedRemotesFromConfig(content.value, stripEndingDotGit), - err => [] // ignore missing or binary file - ); - }); -} - export class WorkspaceStats implements IWorkbenchContribution { constructor( @@ -230,7 +216,7 @@ export class WorkspaceStats implements IWorkbenchContribution { private reportRemotes(workspaceUris: URI[]): void { Promise.all(workspaceUris.map(workspaceUri => { - return getHashedRemotesFromUri(workspaceUri, this.fileService, this.textFileService, true); + return this.workspaceStatsService.getHashedRemotesFromUri(workspaceUri, true); })).then(hashedRemotes => { /* __GDPR__ "workspace.hashedRemotes" : { diff --git a/src/vs/workbench/contrib/stats/electron-browser/workspaceStatsService.ts b/src/vs/workbench/contrib/stats/electron-browser/workspaceStatsService.ts index a646548255..249fb6d95d 100644 --- a/src/vs/workbench/contrib/stats/electron-browser/workspaceStatsService.ts +++ b/src/vs/workbench/contrib/stats/electron-browser/workspaceStatsService.ts @@ -18,10 +18,9 @@ import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspa import { localize } from 'vs/nls'; import Severity from 'vs/base/common/severity'; import { joinPath } from 'vs/base/common/resources'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; - -export type Tags = { [index: string]: boolean | number | string | undefined }; +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'; @@ -93,20 +92,6 @@ const PyModulesToLookFor = [ 'botframework-connector' ]; -export const IWorkspaceStatsService = createDecorator('workspaceStatsService'); - -export interface IWorkspaceStatsService { - _serviceBrand: any; - getTags(): Promise; - - /** - * Returns an id for the workspace, different from the id returned by the context service. A hash based - * on the folder uri or workspace configuration, not time-based, and undefined for empty workspaces. - */ - getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): string | undefined; -} - - export class WorkspaceStatsService implements IWorkspaceStatsService { _serviceBrand: any; private _tags: Tags; @@ -152,6 +137,20 @@ export class WorkspaceStatsService implements IWorkspaceStatsService { return workspaceId; } + getHashedRemotesFromUri(workspaceUri: URI, stripEndingDotGit: boolean = false): Promise { + const path = workspaceUri.path; + const uri = workspaceUri.with({ path: `${path !== '/' ? path : ''}/.git/config` }); + return this.fileService.exists(uri).then(exists => { + if (!exists) { + return []; + } + return this.textFileService.read(uri, { acceptTextOnly: true }).then( + content => getHashedRemotesFromConfig(content.value, stripEndingDotGit), + err => [] // ignore missing or binary file + ); + }); + } + /* __GDPR__FRAGMENT__ "WorkspaceTags" : { "workbench.filesToOpenOrCreate" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 9ced7b2156..d14a2dd864 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -20,7 +20,7 @@ import * as panel from 'vs/workbench/browser/panel'; import { getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen'; import { Extensions as QuickOpenExtensions, IQuickOpenRegistry, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; -import { AllowWorkspaceShellTerminalCommand, ClearSelectionTerminalAction, ClearTerminalAction, CopyTerminalSelectionAction, CreateNewInActiveWorkspaceTerminalAction, CreateNewTerminalAction, DeleteToLineStartTerminalAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, DisallowWorkspaceShellTerminalCommand, FindNext, FindPrevious, FocusActiveTerminalAction, FocusNextPaneTerminalAction, FocusNextTerminalAction, FocusPreviousPaneTerminalAction, FocusPreviousTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, KillTerminalAction, MoveToLineEndTerminalAction, MoveToLineStartTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, RenameTerminalAction, ResizePaneDownTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, RunActiveFileInTerminalAction, RunSelectedTextInTerminalAction, ScrollDownPageTerminalAction, ScrollDownTerminalAction, ScrollToBottomTerminalAction, ScrollToNextCommandAction, ScrollToPreviousCommandAction, ScrollToTopTerminalAction, ScrollUpPageTerminalAction, ScrollUpTerminalAction, SelectAllTerminalAction, SelectDefaultShellWindowsTerminalAction, SelectToNextCommandAction, SelectToNextLineAction, SelectToPreviousCommandAction, SelectToPreviousLineAction, SendSequenceTerminalCommand, SplitInActiveWorkspaceTerminalAction, SplitTerminalAction, TerminalPasteAction, TERMINAL_PICKER_PREFIX, ToggleCaseSensitiveCommand, ToggleEscapeSequenceLoggingAction, ToggleRegexCommand, ToggleTerminalAction, ToggleWholeWordCommand, NavigationModeFocusPreviousTerminalAction, NavigationModeFocusNextTerminalAction, NavigationModeExitTerminalAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; +import { ClearSelectionTerminalAction, ClearTerminalAction, CopyTerminalSelectionAction, CreateNewInActiveWorkspaceTerminalAction, CreateNewTerminalAction, DeleteToLineStartTerminalAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, FindNext, FindPrevious, FocusActiveTerminalAction, FocusNextPaneTerminalAction, FocusNextTerminalAction, FocusPreviousPaneTerminalAction, FocusPreviousTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, KillTerminalAction, MoveToLineEndTerminalAction, MoveToLineStartTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, RenameTerminalAction, ResizePaneDownTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, RunActiveFileInTerminalAction, RunSelectedTextInTerminalAction, ScrollDownPageTerminalAction, ScrollDownTerminalAction, ScrollToBottomTerminalAction, ScrollToNextCommandAction, ScrollToPreviousCommandAction, ScrollToTopTerminalAction, ScrollUpPageTerminalAction, ScrollUpTerminalAction, SelectAllTerminalAction, SelectDefaultShellWindowsTerminalAction, SelectToNextCommandAction, SelectToNextLineAction, SelectToPreviousCommandAction, SelectToPreviousLineAction, SendSequenceTerminalCommand, SplitInActiveWorkspaceTerminalAction, SplitTerminalAction, TerminalPasteAction, TERMINAL_PICKER_PREFIX, ToggleCaseSensitiveCommand, ToggleEscapeSequenceLoggingAction, ToggleRegexCommand, ToggleTerminalAction, ToggleWholeWordCommand, NavigationModeFocusPreviousTerminalAction, NavigationModeFocusNextTerminalAction, NavigationModeExitTerminalAction, ManageWorkspaceShellPermissionsTerminalCommand } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { TerminalPanel } from 'vs/workbench/contrib/terminal/browser/terminalPanel'; import { TerminalPickerHandler } from 'vs/workbench/contrib/terminal/browser/terminalQuickOpen'; import { KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle, ITerminalService, TERMINAL_ACTION_CATEGORY, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS } from 'vs/workbench/contrib/terminal/common/terminal'; @@ -387,8 +387,7 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClearTerminalAct mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_K } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KeybindingWeight.WorkbenchContrib + 1), 'Terminal: Clear', category); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectDefaultShellWindowsTerminalAction, SelectDefaultShellWindowsTerminalAction.ID, SelectDefaultShellWindowsTerminalAction.LABEL), 'Terminal: Select Default Shell', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(AllowWorkspaceShellTerminalCommand, AllowWorkspaceShellTerminalCommand.ID, AllowWorkspaceShellTerminalCommand.LABEL), 'Terminal: Allow Workspace Shell Configuration', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DisallowWorkspaceShellTerminalCommand, DisallowWorkspaceShellTerminalCommand.ID, DisallowWorkspaceShellTerminalCommand.LABEL), 'Terminal: Disallow Workspace Shell Configuration', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ManageWorkspaceShellPermissionsTerminalCommand, ManageWorkspaceShellPermissionsTerminalCommand.ID, ManageWorkspaceShellPermissionsTerminalCommand.LABEL), 'Terminal: Manage Workspace Shell Permissions', category); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(RenameTerminalAction, RenameTerminalAction.ID, RenameTerminalAction.LABEL), 'Terminal: Rename', category); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusTerminalFindWidgetAction, FocusTerminalFindWidgetAction.ID, FocusTerminalFindWidgetAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_F diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index ea096daffc..6f2c3348ac 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -993,10 +993,10 @@ export class ClearSelectionTerminalAction extends Action { } } -export class AllowWorkspaceShellTerminalCommand extends Action { +export class ManageWorkspaceShellPermissionsTerminalCommand extends Action { - public static readonly ID = TERMINAL_COMMAND_ID.WORKSPACE_SHELL_ALLOW; - public static readonly LABEL = nls.localize('workbench.action.terminal.allowWorkspaceShell', "Allow Workspace Shell Configuration"); + public static readonly ID = TERMINAL_COMMAND_ID.MANAGE_WORKSPACE_SHELL_PERMISSIONS; + public static readonly LABEL = nls.localize('workbench.action.terminal.manageWorkspaceShellPermissions', "Manage Workspace Shell Permissions"); constructor( id: string, label: string, @@ -1005,27 +1005,8 @@ export class AllowWorkspaceShellTerminalCommand extends Action { super(id, label); } - public run(event?: any): Promise { - this.terminalService.setWorkspaceShellAllowed(true); - return Promise.resolve(undefined); - } -} - -export class DisallowWorkspaceShellTerminalCommand extends Action { - - public static readonly ID = TERMINAL_COMMAND_ID.WORKSPACE_SHELL_DISALLOW; - public static readonly LABEL = nls.localize('workbench.action.terminal.disallowWorkspaceShell', "Disallow Workspace Shell Configuration"); - - constructor( - id: string, label: string, - @ITerminalService private readonly terminalService: ITerminalService - ) { - super(id, label); - } - - public run(event?: any): Promise { - this.terminalService.setWorkspaceShellAllowed(false); - return Promise.resolve(undefined); + public async run(event?: any): Promise { + await this.terminalService.manageWorkspaceShellPermissions(); } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index 3ae3694719..76327565de 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -226,7 +226,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce const isWorkspaceShellAllowed = this._configHelper.checkWorkspaceShellPermissions(); this._configHelper.showRecommendations(shellLaunchConfig); const baseEnv = this._configHelper.config.inheritEnv ? process.env as platform.IProcessEnvironment : await this._terminalInstanceService.getMainProcessParentEnv(); - const env = terminalEnvironment.createTerminalEnvironment(shellLaunchConfig, lastActiveWorkspace, envFromConfigValue, this._configurationResolverService, isWorkspaceShellAllowed, this._productService.version, this._configHelper.config.setLocaleVariables, baseEnv); + const env = terminalEnvironment.createTerminalEnvironment(shellLaunchConfig, lastActiveWorkspace, envFromConfigValue, this._configurationResolverService, isWorkspaceShellAllowed, this._productService.productConfiguration.version, this._configHelper.config.setLocaleVariables, baseEnv); const useConpty = this._configHelper.config.windowsEnableConpty && !isScreenReaderModeEnabled; return this._terminalInstanceService.createTerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, useConpty); diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index c1518c06e2..6418393afd 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -279,7 +279,7 @@ export interface ITerminalService { selectDefaultWindowsShell(): Promise; setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void; - setWorkspaceShellAllowed(isAllowed: boolean): void; + manageWorkspaceShellPermissions(): void; /** * Takes a path and returns the properly escaped path to send to the terminal. diff --git a/src/vs/workbench/contrib/terminal/common/terminalCommands.ts b/src/vs/workbench/contrib/terminal/common/terminalCommands.ts index 347d0b3072..6cbf276e25 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalCommands.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalCommands.ts @@ -48,8 +48,7 @@ export const enum TERMINAL_COMMAND_ID { SCROLL_TO_TOP = 'workbench.action.terminal.scrollToTop', CLEAR = 'workbench.action.terminal.clear', CLEAR_SELECTION = 'workbench.action.terminal.clearSelection', - WORKSPACE_SHELL_ALLOW = 'workbench.action.terminal.allowWorkspaceShell', - WORKSPACE_SHELL_DISALLOW = 'workbench.action.terminal.disallowWorkspaceShell', + MANAGE_WORKSPACE_SHELL_PERMISSIONS = 'workbench.action.terminal.manageWorkspaceShellPermissions', RENAME = 'workbench.action.terminal.rename', FIND_WIDGET_FOCUS = 'workbench.action.terminal.focusFindWidget', FIND_WIDGET_HIDE = 'workbench.action.terminal.hideFindWidget', diff --git a/src/vs/workbench/contrib/terminal/common/terminalService.ts b/src/vs/workbench/contrib/terminal/common/terminalService.ts index e343191a67..2ffbf54bab 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalService.ts @@ -479,22 +479,28 @@ export abstract class TerminalService implements ITerminalService { return terminalIndex; } - public setWorkspaceShellAllowed(isAllowed: boolean): void { - this.configHelper.setWorkspaceShellAllowed(isAllowed); + public async manageWorkspaceShellPermissions(): Promise { + const allowItem: IQuickPickItem = { label: nls.localize('workbench.action.terminal.allowWorkspaceShell', "Allow Workspace Shell Configuration") }; + const disallowItem: IQuickPickItem = { label: nls.localize('workbench.action.terminal.disallowWorkspaceShell', "Disallow Workspace Shell Configuration") }; + const value = await this._quickInputService.pick([allowItem, disallowItem], { canPickMany: false }); + if (!value) { + return; + } + this.configHelper.setWorkspaceShellAllowed(value === allowItem); } - protected _showTerminalCloseConfirmation(): Promise { - let message; + protected async _showTerminalCloseConfirmation(): Promise { + let message: string; if (this.terminalInstances.length === 1) { message = nls.localize('terminalService.terminalCloseConfirmationSingular', "There is an active terminal session, do you want to kill it?"); } else { message = nls.localize('terminalService.terminalCloseConfirmationPlural', "There are {0} active terminal sessions, do you want to kill them?", this.terminalInstances.length); } - - return this._dialogService.confirm({ + const res = await this._dialogService.confirm({ message, type: 'warning', - }).then(res => !res.confirmed); + }); + return !res.confirmed; } protected _showNotEnoughSpaceToast(): void { diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index bd52911061..e8ba6f6e22 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -188,6 +188,11 @@ return; } + // Prevent middle clicks opening a broken link in the browser + if (event.button == 1) { + event.preventDefault(); + } + let baseElement = event.view.document.getElementsByTagName('base')[0]; /** @type {any} */ let node = event.target; @@ -443,7 +448,7 @@ }); // Bubble out link clicks - newFrame.contentWindow.addEventListener('click', handleInnerClick); + newFrame.contentWindow.addEventListener('mousedown', handleInnerClick); if (host.onIframeLoaded) { host.onIframeLoaded(newFrame); diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/vs_code_editor_walkthrough.md b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/vs_code_editor_walkthrough.md index 43e8bc83a8..ffd95c8971 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/vs_code_editor_walkthrough.md +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/vs_code_editor_walkthrough.md @@ -77,6 +77,9 @@ new Book("The Martian", "Andy Weir"); /** * Represents a book. + * + * @param {string} title Title of the book + * @param {string} author Who wrote the book */ function Book(title, author) { this.title = title; @@ -84,7 +87,7 @@ function Book(title, author) { } ``` -> **JSDoc Tip:** The example above also showcased another way to get IntelliSense hints by using `JSDoc` comments. You can try this out by invoking the `Book` function and seeing the enhanced context in the IntelliSense menu for the function as well as parameters. +> **JSDoc Tip:** VS Code's IntelliSense uses JSDoc comments to provide richer suggestions. The types and documentation from JSDoc comments show up when you hover over a reference to `Book` or in IntelliSense when you create a new instance of `Book`. ### Refactoring via Extraction diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index b148745ef7..0cd0d5e7f7 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -220,11 +220,11 @@ export class EditorService extends Disposable implements EditorServiceImpl { //#region openEditor() - openEditor(editor: IEditorInput, options?: IEditorOptions | ITextEditorOptions, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - openEditor(editor: IResourceInput | IUntitledResourceInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - openEditor(editor: IResourceDiffInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - openEditor(editor: IResourceSideBySideInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - openEditor(editor: IEditorInput | IResourceEditor, optionsOrGroup?: IEditorOptions | ITextEditorOptions | IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE, group?: GroupIdentifier): Promise { + openEditor(editor: IEditorInput, options?: IEditorOptions | ITextEditorOptions, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + openEditor(editor: IResourceInput | IUntitledResourceInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + openEditor(editor: IResourceDiffInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + openEditor(editor: IResourceSideBySideInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + openEditor(editor: IEditorInput | IResourceEditor, optionsOrGroup?: IEditorOptions | ITextEditorOptions | IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE, group?: GroupIdentifier): Promise { // Typed Editor Support if (editor instanceof EditorInput) { @@ -243,11 +243,11 @@ export class EditorService extends Disposable implements EditorServiceImpl { return this.doOpenEditor(targetGroup, typedInput, editorOptions); } - return Promise.resolve(null); + return Promise.resolve(undefined); } - protected doOpenEditor(group: IEditorGroup, editor: IEditorInput, options?: IEditorOptions): Promise { - return group.openEditor(editor, options); + protected doOpenEditor(group: IEditorGroup, editor: IEditorInput, options?: IEditorOptions): Promise { + return group.openEditor(editor, options).then(withNullAsUndefined); } private findTargetGroup(input: IEditorInput, options?: IEditorOptions, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): IEditorGroup { @@ -659,7 +659,7 @@ export class DelegatingEditorService extends EditorService { this.editorOpenHandler = handler; } - protected async doOpenEditor(group: IEditorGroup, editor: IEditorInput, options?: IEditorOptions): Promise { + protected async doOpenEditor(group: IEditorGroup, editor: IEditorInput, options?: IEditorOptions): Promise { if (!this.editorOpenHandler) { return super.doOpenEditor(group, editor, options); } diff --git a/src/vs/workbench/services/editor/common/editorService.ts b/src/vs/workbench/services/editor/common/editorService.ts index 0824b4ed29..0a2c4d6af6 100644 --- a/src/vs/workbench/services/editor/common/editorService.ts +++ b/src/vs/workbench/services/editor/common/editorService.ts @@ -36,7 +36,7 @@ export interface IOpenEditorOverride { * If defined, will prevent the opening of an editor and replace the resulting * promise with the provided promise for the openEditor() call. */ - override?: Promise; + override?: Promise; } export interface IVisibleEditor extends IEditor { @@ -117,13 +117,13 @@ export interface IEditorService { * active group. Use `SIDE_GROUP_TYPE` to open the editor in a new editor group to the side * of the currently active group. * - * @returns the editor that opened or NULL if the operation failed or the editor was not + * @returns the editor that opened or `undefined` if the operation failed or the editor was not * opened to be active. */ - openEditor(editor: IEditorInput, options?: IEditorOptions | ITextEditorOptions, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - openEditor(editor: IResourceInput | IUntitledResourceInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - openEditor(editor: IResourceDiffInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - openEditor(editor: IResourceSideBySideInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + openEditor(editor: IEditorInput, options?: IEditorOptions | ITextEditorOptions, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + openEditor(editor: IResourceInput | IUntitledResourceInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + openEditor(editor: IResourceDiffInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + openEditor(editor: IResourceSideBySideInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; /** * Open editors in an editor group. diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index 9e8e09f10b..06b6fa0c9c 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -20,6 +20,8 @@ import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEn import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { WebWorkerExtensionHostStarter } from 'vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter'; import { URI } from 'vs/base/common/uri'; +import { isWebExtension } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export class ExtensionService extends AbstractExtensionService implements IExtensionService { @@ -34,6 +36,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten @IFileService fileService: IFileService, @IProductService productService: IProductService, @IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService, + @IConfigurationService private readonly _configService: IConfigurationService, ) { super( instantiationService, @@ -65,11 +68,14 @@ export class ExtensionService extends AbstractExtensionService implements IExten const remoteAgentConnection = this._remoteAgentService.getConnection()!; - const webHostProcessWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, true, Promise.resolve([]), URI.parse('empty:value')); //todo@joh + const webExtensions = this.getExtensions().then(extensions => extensions.filter(ext => isWebExtension(ext, this._configService))); + const remoteExtensions = this.getExtensions().then(extensions => extensions.filter(ext => !isWebExtension(ext, this._configService))); + + const webHostProcessWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, true, webExtensions, URI.parse('empty:value')); //todo@joh const webHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, webHostProcessWorker, remoteAgentConnection.remoteAuthority, initialActivationEvents); result.push(webHostProcessManager); - const remoteExtHostProcessWorker = this._instantiationService.createInstance(RemoteExtensionHostClient, this.getExtensions(), this._createProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory); + const remoteExtHostProcessWorker = this._instantiationService.createInstance(RemoteExtensionHostClient, remoteExtensions, this._createProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory); const remoteExtHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, remoteExtHostProcessWorker, remoteAgentConnection.remoteAuthority, initialActivationEvents); result.push(remoteExtHostProcessManager); diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts index d3e2491589..2ae647ca37 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts @@ -3,9 +3,9 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { DefaultWorkerFactory } from 'vs/base/worker/defaultWorkerFactory'; +import { getWorkerBootstrapUrl } from 'vs/base/worker/defaultWorkerFactory'; import { Emitter, Event } from 'vs/base/common/event'; -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { VSBuffer } from 'vs/base/common/buffer'; import { createMessageOfType, MessageType, isMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; @@ -49,23 +49,29 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { if (!this._protocol) { const emitter = new Emitter(); - const worker = new DefaultWorkerFactory('WorkerExtensionHost').create( - 'vs/workbench/services/extensions/worker/extensionHostWorker', data => { - if (data instanceof ArrayBuffer) { - emitter.fire(VSBuffer.wrap(new Uint8Array(data, 0, data.byteLength))); - } else { - console.warn('UNKNOWN data received', data); - this._onDidExit.fire([77, 'UNKNOWN data received']); - } - }, err => { - this._onDidExit.fire([81, err]); - console.error(err); + + const url = getWorkerBootstrapUrl(require.toUrl('../worker/extensionHostWorkerMain.js'), 'WorkerExtensionHost'); + const worker = new Worker(url); + + worker.onmessage = (event) => { + const { data } = event; + if (!(data instanceof ArrayBuffer)) { + console.warn('UNKNOWN data received', data); + this._onDidExit.fire([77, 'UNKNOWN data received']); + return; } - ); + + emitter.fire(VSBuffer.wrap(new Uint8Array(data, 0, data.byteLength))); + }; + + worker.onerror = (event) => { + console.error(event.error); + this._onDidExit.fire([81, event.error]); + }; // keep for cleanup this._toDispose.add(emitter); - this._toDispose.add(worker); + this._toDispose.add(toDisposable(() => worker.terminate())); const protocol: IMessagePassingProtocol = { onMessage: emitter.event, @@ -111,16 +117,16 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { const [telemetryInfo, extensionDescriptions] = await Promise.all([this._telemetryService.getTelemetryInfo(), this._extensions]); const workspace = this._contextService.getWorkspace(); return { - commit: this._productService.commit, - version: this._productService.version, - vscodeVersion: this._productService.vscodeVersion, + commit: this._productService.productConfiguration.commit, + version: this._productService.productConfiguration.version, + vscodeVersion: this._productService.productConfiguration.vscodeVersion, // {{SQL CARBON EDIT}} add vscode version parentPid: -1, environment: { isExtensionDevelopmentDebug: false, appRoot: this._environmentService.appRoot ? URI.file(this._environmentService.appRoot) : undefined, appSettingsHome: this._environmentService.appSettingsHome ? this._environmentService.appSettingsHome : undefined, - appName: this._productService.nameLong, - appUriScheme: this._productService.urlProtocol, + appName: this._productService.productConfiguration.nameLong, + appUriScheme: this._productService.productConfiguration.urlProtocol, appLanguage: platform.language, extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index 9ffd299f00..bb16bfcd8a 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -462,12 +462,12 @@ class ProposedApiController { } this.enableProposedApiForAll = !environmentService.isBuilt || - (!!environmentService.extensionDevelopmentLocationURI && productService.nameLong !== 'Visual Studio Code') || + (!!environmentService.extensionDevelopmentLocationURI && productService.productConfiguration.nameLong !== 'Visual Studio Code') || (this.enableProposedApiFor.length === 0 && 'enable-proposed-api' in environmentService.args); this.productAllowProposedApi = new Set(); - if (isNonEmptyArray(productService.extensionAllowedProposedApi)) { - productService.extensionAllowedProposedApi.forEach((id) => this.productAllowProposedApi.add(ExtensionIdentifier.toKey(id))); + if (isNonEmptyArray(productService.productConfiguration.extensionAllowedProposedApi)) { + productService.productConfiguration.extensionAllowedProposedApi.forEach((id) => this.productAllowProposedApi.add(ExtensionIdentifier.toKey(id))); } } diff --git a/src/vs/workbench/services/extensions/common/extensionsUtil.ts b/src/vs/workbench/services/extensions/common/extensionsUtil.ts index 9b4699fc75..9b902d4246 100644 --- a/src/vs/workbench/services/extensions/common/extensionsUtil.ts +++ b/src/vs/workbench/services/extensions/common/extensionsUtil.ts @@ -10,6 +10,11 @@ import { getGalleryExtensionId, areSameExtensions } from 'vs/platform/extensionM import { isNonEmptyArray } from 'vs/base/common/arrays'; import { IProductService } from 'vs/platform/product/common/product'; +export function isWebExtension(manifest: IExtensionManifest, configurationService: IConfigurationService): boolean { + const extensionKind = getExtensionKind(manifest, configurationService); + return extensionKind === 'web'; +} + export function isUIExtension(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean { const uiContributions = ExtensionsRegistry.getExtensionPoints().filter(e => e.defaultExtensionKind !== 'workspace').map(e => e.name); const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name); @@ -19,7 +24,7 @@ export function isUIExtension(manifest: IExtensionManifest, productService: IPro case 'workspace': return false; default: { // Tagged as UI extension in product - if (isNonEmptyArray(productService.uiExtensions) && productService.uiExtensions.some(id => areSameExtensions({ id }, { id: extensionId }))) { + if (isNonEmptyArray(productService.productConfiguration.uiExtensions) && productService.productConfiguration.uiExtensions.some(id => areSameExtensions({ id }, { id: extensionId }))) { return true; } // Not an UI extension if it has main diff --git a/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts b/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts index 80a0df64f3..b159eb5299 100644 --- a/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts +++ b/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts @@ -71,7 +71,7 @@ export class RemoteExtensionHostClient extends Disposable implements IExtensionH public start(): Promise { const options: IConnectionOptions = { - commit: this._productService.commit, + commit: this._productService.productConfiguration.commit, socketFactory: this._socketFactory, addressProvider: { getAddress: async () => { @@ -181,16 +181,16 @@ export class RemoteExtensionHostClient extends Disposable implements IExtensionH const hostExtensions = allExtensions.filter(extension => extension.main && extension.api === 'none').map(extension => extension.identifier); const workspace = this._contextService.getWorkspace(); const r: IInitData = { - commit: this._productService.commit, - version: this._productService.version, - vscodeVersion: this._productService.vscodeVersion, // {{SQL CARBON EDIT}} add vscode version + commit: this._productService.productConfiguration.commit, + version: this._productService.productConfiguration.version, + vscodeVersion: this._productService.productConfiguration.vscodeVersion, // {{SQL CARBON EDIT}} add vscode version parentPid: remoteExtensionHostData.pid, environment: { isExtensionDevelopmentDebug, appRoot: remoteExtensionHostData.appRoot, appSettingsHome: remoteExtensionHostData.appSettingsHome, - appName: this._productService.nameLong, - appUriScheme: this._productService.urlProtocol, + appName: this._productService.productConfiguration.nameLong, + appUriScheme: this._productService.productConfiguration.urlProtocol, appLanguage: platform.language, extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, diff --git a/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts b/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts index 8366a02073..c767f79187 100644 --- a/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts +++ b/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts @@ -69,7 +69,7 @@ export class RemoteExtensionManagementChannelClient extends ExtensionManagementC const installed = await this.getInstalled(ExtensionType.User); const compatible = await this.galleryService.getCompatibleExtension(extension); if (!compatible) { - return Promise.reject(new Error(localize('incompatible', "Unable to install extension '{0}' as it is not compatible with VS Code '{1}'.", extension.identifier.id, this.productService.version))); + return Promise.reject(new Error(localize('incompatible', "Unable to install extension '{0}' as it is not compatible with VS Code '{1}'.", extension.identifier.id, this.productService.productConfiguration.version))); } const manifest = await this.galleryService.getManifest(compatible, CancellationToken.None); if (manifest) { @@ -140,4 +140,4 @@ export class RemoteExtensionManagementChannelClient extends ExtensionManagementC } return this.getDependenciesAndPackedExtensionsRecursively(toGet, result, uiExtension, token); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/services/extensions/node/extensionPoints.ts b/src/vs/workbench/services/extensions/node/extensionPoints.ts index e56f942196..84254e6761 100644 --- a/src/vs/workbench/services/extensions/node/extensionPoints.ts +++ b/src/vs/workbench/services/extensions/node/extensionPoints.ts @@ -313,11 +313,6 @@ class ExtensionManifestValidator extends ExtensionManifestHandler { extensionDescription.id = `${extensionDescription.publisher}.${extensionDescription.name}`; extensionDescription.identifier = new ExtensionIdentifier(extensionDescription.id); - // main := absolutePath(`main`) - if (extensionDescription.main) { - extensionDescription.main = path.join(this._absoluteFolderPath, extensionDescription.main); - } - extensionDescription.extensionLocation = URI.file(this._absoluteFolderPath); return extensionDescription; diff --git a/src/vs/workbench/services/extensions/node/proxyResolver.ts b/src/vs/workbench/services/extensions/node/proxyResolver.ts index 1fa3ce99ce..81812dfc29 100644 --- a/src/vs/workbench/services/extensions/node/proxyResolver.ts +++ b/src/vs/workbench/services/extensions/node/proxyResolver.ts @@ -469,26 +469,24 @@ async function readCaCertificates() { } async function readWindowsCaCertificates() { - // Not using await to work around minifier bug (https://github.com/microsoft/vscode/issues/79044). - return import('vscode-windows-ca-certs') - .then(winCA => { - let ders: any[] = []; - const store = winCA(); - try { - let der: any; - while (der = store.next()) { - ders.push(der); - } - } finally { - store.done(); - } + const winCA = await import('vscode-windows-ca-certs'); - const certs = new Set(ders.map(derToPem)); - return { - certs: Array.from(certs), - append: true - }; - }); + let ders: any[] = []; + const store = winCA(); + try { + let der: any; + while (der = store.next()) { + ders.push(der); + } + } finally { + store.done(); + } + + const certs = new Set(ders.map(derToPem)); + return { + certs: Array.from(certs), + append: true + }; } async function readMacCaCertificates() { diff --git a/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts b/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts index 6906d62a21..d767e85362 100644 --- a/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts +++ b/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts @@ -3,7 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IRequestHandler } from 'vs/base/common/worker/simpleWorker'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { VSBuffer } from 'vs/base/common/buffer'; import { Emitter } from 'vs/base/common/event'; @@ -38,21 +37,18 @@ const hostUtil = new class implements IHostUtils { //todo@joh do not allow extensions to call postMessage and other globals... -class ExtensionWorker implements IRequestHandler { - - // worker-contract - readonly _requestHandlerBrand: any; - readonly onmessage: (data: any) => any; +class ExtensionWorker { // protocol readonly protocol: IMessagePassingProtocol; - constructor(postMessage: (message: any, transfer?: Transferable[]) => any) { + constructor() { let emitter = new Emitter(); let terminating = false; - this.onmessage = data => { + onmessage = event => { + const { data } = event; if (!(data instanceof ArrayBuffer)) { console.warn('UNKNOWN data received', data); return; @@ -98,8 +94,8 @@ function connectToRenderer(protocol: IMessagePassingProtocol): Promise any): IRequestHandler { - const res = new ExtensionWorker(postMessage); +(function create(): void { + const res = new ExtensionWorker(); connectToRenderer(res.protocol).then(data => { @@ -112,6 +108,4 @@ export function create(postMessage: (message: any, transfer?: Transferable[]) => onTerminate = () => extHostMain.terminate(); }); - - return res; -} +})(); diff --git a/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts b/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts new file mode 100644 index 0000000000..66cc5c3f1a --- /dev/null +++ b/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +(function () { + + let MonacoEnvironment = (self).MonacoEnvironment; + let monacoBaseUrl = MonacoEnvironment && MonacoEnvironment.baseUrl ? MonacoEnvironment.baseUrl : '../../../../../'; + + if (typeof (self).define !== 'function' || !(self).define.amd) { + importScripts(monacoBaseUrl + 'vs/loader.js'); + } + + require.config({ + baseUrl: monacoBaseUrl, + catchError: true + }); + + require(['vs/workbench/services/extensions/worker/extensionHostWorker'], () => { }, err => console.error(err)); +})(); diff --git a/src/vs/workbench/services/history/browser/history.ts b/src/vs/workbench/services/history/browser/history.ts index ac4ee5e312..b298364c60 100644 --- a/src/vs/workbench/services/history/browser/history.ts +++ b/src/vs/workbench/services/history/browser/history.ts @@ -453,7 +453,7 @@ export class HistoryService extends Disposable implements IHistoryService { this.doNavigate(this.stack[this.index], !acrossEditors).finally(() => this.navigatingInStack = false); } - private doNavigate(location: IStackEntry, withSelection: boolean): Promise { + private doNavigate(location: IStackEntry, withSelection: boolean): Promise { const options: ITextEditorOptions = { revealIfOpened: true // support to navigate across editor groups }; diff --git a/src/vs/workbench/services/opener/electron-browser/openerService.ts b/src/vs/workbench/services/opener/electron-browser/openerService.ts new file mode 100644 index 0000000000..efba9695d2 --- /dev/null +++ b/src/vs/workbench/services/opener/electron-browser/openerService.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { OpenerService as BaseOpenerService } from 'vs/editor/browser/services/openerService'; +import { IWindowsService } from 'vs/platform/windows/common/windows'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { Schemas } from 'vs/base/common/network'; +import { URI } from 'vs/base/common/uri'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; + +export class OpenerService extends BaseOpenerService { + + _serviceBrand!: ServiceIdentifier; + + constructor( + @ICodeEditorService codeEditorService: ICodeEditorService, + @ICommandService commandService: ICommandService, + @IWindowsService private readonly windowsService: IWindowsService + ) { + super(codeEditorService, commandService); + } + + async openExternal(resource: URI): Promise { + const success = this.windowsService.openExternal(encodeURI(resource.toString(true))); + if (!success && resource.scheme === Schemas.file) { + await this.windowsService.showItemInFolder(resource); + + return true; + } + + return success; + } +} + +registerSingleton(IOpenerService, OpenerService, true); diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index c9d92d39dc..42bdc80cd8 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -38,6 +38,7 @@ import { defaultKeybindingsContents, DefaultKeybindingsEditorModel, DefaultSetti import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { withNullAsUndefined } from 'vs/base/common/types'; const emptyEditableSettingsContent = '{\n}'; @@ -188,15 +189,15 @@ export class PreferencesService extends Disposable implements IPreferencesServic return null; } - openRawDefaultSettings(): Promise { + openRawDefaultSettings(): Promise { return this.editorService.openEditor({ resource: this.defaultSettingsRawResource }); } - openRawUserSettings(): Promise { + openRawUserSettings(): Promise { return this.editorService.openEditor({ resource: this.userSettingsResource }); } - openSettings(jsonEditor: boolean | undefined, query: string | undefined): Promise { + openSettings(jsonEditor: boolean | undefined, query: string | undefined): Promise { jsonEditor = typeof jsonEditor === 'undefined' ? this.configurationService.getValue('workbench.settings.editor') === 'json' : jsonEditor; @@ -217,7 +218,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic .then(() => this.editorGroupService.activeGroup.activeControl!); } - openGlobalSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { + openGlobalSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { jsonEditor = typeof jsonEditor === 'undefined' ? this.configurationService.getValue('workbench.settings.editor') === 'json' : jsonEditor; @@ -227,16 +228,16 @@ export class PreferencesService extends Disposable implements IPreferencesServic this.openOrSwitchSettings2(ConfigurationTarget.USER_LOCAL, undefined, options, group); } - async openRemoteSettings(): Promise { + async openRemoteSettings(): Promise { const environment = await this.remoteAgentService.getEnvironment(); if (environment) { await this.createIfNotExists(environment.settingsPath, emptyEditableSettingsContent); return this.editorService.openEditor({ resource: environment.settingsPath, options: { pinned: true, revealIfOpened: true } }); } - return null; + return undefined; } - openWorkspaceSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { + openWorkspaceSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { jsonEditor = typeof jsonEditor === 'undefined' ? this.configurationService.getValue('workbench.settings.editor') === 'json' : jsonEditor; @@ -251,7 +252,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic this.openOrSwitchSettings2(ConfigurationTarget.WORKSPACE, undefined, options, group); } - async openFolderSettings(folder: URI, jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { + async openFolderSettings(folder: URI, jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { jsonEditor = typeof jsonEditor === 'undefined' ? this.configurationService.getValue('workbench.settings.editor') === 'json' : jsonEditor; @@ -306,7 +307,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.editorService.openEditor(this.instantiationService.createInstance(KeybindingsEditorInput), { pinned: true, revealIfOpened: true }).then(() => undefined); } - openDefaultKeybindingsFile(): Promise { + openDefaultKeybindingsFile(): Promise { return this.editorService.openEditor({ resource: this.defaultKeybindingsResource, label: nls.localize('defaultKeybindings', "Default Keybindings") }); } @@ -328,7 +329,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic })); } - private openOrSwitchSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): Promise { + private openOrSwitchSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): Promise { const editorInput = this.getActiveSettingsEditorInput(group); if (editorInput) { const editorInputResource = editorInput.master.getResource(); @@ -339,11 +340,11 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.doOpenSettings(configurationTarget, resource, options, group); } - private openOrSwitchSettings2(configurationTarget: ConfigurationTarget, folderUri?: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): Promise { + private openOrSwitchSettings2(configurationTarget: ConfigurationTarget, folderUri?: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): Promise { return this.doOpenSettings2(configurationTarget, folderUri, options, group); } - private doOpenSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { + private doOpenSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { const openSplitJSON = !!this.configurationService.getValue(USE_SPLIT_JSON_SETTING); if (openSplitJSON) { return this.doOpenSplitJSON(configurationTarget, resource, options, group); @@ -365,14 +366,14 @@ export class PreferencesService extends Disposable implements IPreferencesServic return Promise.all([ this.editorService.openEditor({ resource: this.defaultSettingsRawResource, options: { pinned: true, preserveFocus: true, revealIfOpened: true }, label: nls.localize('defaultSettings', "Default Settings"), description: '' }), this.editorService.openEditor(editableSettingsEditorInput, { pinned: true, revealIfOpened: true }, sideEditorGroup.id) - ]).then(([defaultEditor, editor]) => editor); + ]).then(([defaultEditor, editor]) => withNullAsUndefined(editor)); } else { return this.editorService.openEditor(editableSettingsEditorInput, SettingsEditorOptions.create(options), group); } }); } - private doOpenSplitJSON(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { + private doOpenSplitJSON(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { return this.getOrCreateEditableSettingsEditorInput(configurationTarget, resource) .then(editableSettingsEditorInput => { if (!options) { @@ -392,7 +393,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.instantiationService.createInstance(Settings2EditorModel, this.getDefaultSettings(ConfigurationTarget.USER_LOCAL)); } - private doOpenSettings2(target: ConfigurationTarget, folderUri: URI | undefined, options?: IEditorOptions, group?: IEditorGroup): Promise { + private doOpenSettings2(target: ConfigurationTarget, folderUri: URI | undefined, options?: IEditorOptions, group?: IEditorGroup): Promise { const input = this.settingsEditor2Input; const settingsOptions: ISettingsEditorOptions = { ...options, @@ -633,4 +634,4 @@ export class PreferencesService extends Disposable implements IPreferencesServic } } -registerSingleton(IPreferencesService, PreferencesService); \ No newline at end of file +registerSingleton(IPreferencesService, PreferencesService); diff --git a/src/vs/workbench/services/preferences/common/preferences.ts b/src/vs/workbench/services/preferences/common/preferences.ts index 854188deb9..d536a475e9 100644 --- a/src/vs/workbench/services/preferences/common/preferences.ts +++ b/src/vs/workbench/services/preferences/common/preferences.ts @@ -201,15 +201,15 @@ export interface IPreferencesService { createPreferencesEditorModel(uri: URI): Promise | null>; createSettings2EditorModel(): Settings2EditorModel; // TODO - openRawDefaultSettings(): Promise; - openSettings(jsonEditor: boolean | undefined, query: string | undefined): Promise; - openGlobalSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise; - openRemoteSettings(): Promise; - openWorkspaceSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise; - openFolderSettings(folder: URI, jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise; + openRawDefaultSettings(): Promise; + openSettings(jsonEditor: boolean | undefined, query: string | undefined): Promise; + openGlobalSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise; + openRemoteSettings(): Promise; + openWorkspaceSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise; + openFolderSettings(folder: URI, jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise; switchSettings(target: ConfigurationTarget, resource: URI, jsonEditor?: boolean): Promise; openGlobalKeybindingSettings(textual: boolean): Promise; - openDefaultKeybindingsFile(): Promise; + openDefaultKeybindingsFile(): Promise; configureSettingsForLanguage(language: string | null): void; } diff --git a/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts b/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts index b5345d054f..58f7d8e255 100644 --- a/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts +++ b/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts @@ -28,7 +28,7 @@ export class RemoteAgentService extends AbstractRemoteAgentService implements IR super(environmentService); this.socketFactory = new BrowserSocketFactory(webSocketFactory); - this._connection = this._register(new RemoteAgentConnection(environmentService.configuration.remoteAuthority!, productService.commit, this.socketFactory, remoteAuthorityResolverService, signService)); + this._connection = this._register(new RemoteAgentConnection(environmentService.configuration.remoteAuthority!, productService.productConfiguration.commit, this.socketFactory, remoteAuthorityResolverService, signService)); } getConnection(): IRemoteAgentConnection | null { diff --git a/src/vs/workbench/services/telemetry/browser/telemetryService.ts b/src/vs/workbench/services/telemetry/browser/telemetryService.ts index fb15d82c28..bc7f61b01b 100644 --- a/src/vs/workbench/services/telemetry/browser/telemetryService.ts +++ b/src/vs/workbench/services/telemetry/browser/telemetryService.ts @@ -79,11 +79,11 @@ export class TelemetryService extends Disposable implements ITelemetryService { ) { super(); - const aiKey = productService.aiConfig && productService.aiConfig.asimovKey; - if (!environmentService.isExtensionDevelopment && !environmentService.args['disable-telemetry'] && !!productService.enableTelemetry && !!aiKey) { + const aiKey = productService.productConfiguration.aiConfig && productService.productConfiguration.aiConfig.asimovKey; + if (!environmentService.isExtensionDevelopment && !environmentService.args['disable-telemetry'] && !!productService.productConfiguration.enableTelemetry && !!aiKey) { const config: ITelemetryServiceConfig = { appender: combinedAppender(new WebTelemetryAppender(aiKey, logService), new LogAppender(logService)), - commonProperties: resolveWorkbenchCommonProperties(storageService, productService.commit, productService.version, environmentService.configuration.machineId, environmentService.configuration.remoteAuthority), + commonProperties: resolveWorkbenchCommonProperties(storageService, productService.productConfiguration.commit, productService.productConfiguration.version, environmentService.configuration.machineId, environmentService.configuration.remoteAuthority), piiPaths: [environmentService.appRoot] }; diff --git a/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts b/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts index 91e12fbf2f..eef5d71bab 100644 --- a/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts +++ b/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts @@ -34,11 +34,11 @@ export class TelemetryService extends Disposable implements ITelemetryService { ) { super(); - if (!environmentService.isExtensionDevelopment && !environmentService.args['disable-telemetry'] && !!productService.enableTelemetry) { + if (!environmentService.isExtensionDevelopment && !environmentService.args['disable-telemetry'] && !!productService.productConfiguration.enableTelemetry) { const channel = sharedProcessService.getChannel('telemetryAppender'); const config: ITelemetryServiceConfig = { appender: combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(logService)), - commonProperties: resolveWorkbenchCommonProperties(storageService, productService.commit, productService.version, environmentService.configuration.machineId, environmentService.installSourcePath, environmentService.configuration.remoteAuthority), + commonProperties: resolveWorkbenchCommonProperties(storageService, productService.productConfiguration.commit, productService.productConfiguration.version, environmentService.configuration.machineId, environmentService.installSourcePath, environmentService.configuration.remoteAuthority), piiPaths: environmentService.extensionsPath ? [environmentService.appRoot, environmentService.extensionsPath] : [environmentService.appRoot] }; diff --git a/src/vs/workbench/services/textfile/test/textFileService.io.test.ts b/src/vs/workbench/services/textfile/test/textFileService.io.test.ts index d597e4538c..63bcab0664 100644 --- a/src/vs/workbench/services/textfile/test/textFileService.io.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileService.io.test.ts @@ -4,16 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; -import { workbenchInstantiationService, TestLifecycleService, TestTextFileService, TestWindowsService, TestContextService, TestFileService } from 'vs/workbench/test/workbenchTestServices'; -import { IWindowsService } from 'vs/platform/windows/common/windows'; +import { workbenchInstantiationService, TestTextFileService } from 'vs/workbench/test/workbenchTestServices'; import { ITextFileService, snapshotToString, TextFileOperationResult, TextFileOperationError } from 'vs/workbench/services/textfile/common/textfiles'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { IFileService } from 'vs/platform/files/common/files'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { IModelService } from 'vs/editor/common/services/modelService'; -import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { Schemas } from 'vs/base/common/network'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { rimraf, RimRafMode, copy, readFile, exists } from 'vs/base/node/pfs'; @@ -36,13 +31,8 @@ import { detectEncodingByBOM } from 'vs/base/test/node/encoding/encoding.test'; class ServiceAccessor { constructor( - @ILifecycleService public lifecycleService: TestLifecycleService, @ITextFileService public textFileService: TestTextFileService, - @IUntitledEditorService public untitledEditorService: IUntitledEditorService, - @IWindowsService public windowsService: TestWindowsService, - @IWorkspaceContextService public contextService: TestContextService, - @IModelService public modelService: ModelServiceImpl, - @IFileService public fileService: TestFileService + @IUntitledEditorService public untitledEditorService: IUntitledEditorService ) { } } diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 5edebd3b79..ed9319b161 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -81,8 +81,6 @@ import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IListService, ListService } from 'vs/platform/list/browser/listService'; -import { OpenerService } from 'vs/editor/browser/services/openerService'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { EditorWorkerServiceImpl } from 'vs/editor/common/services/editorWorkerServiceImpl'; import { MarkerDecorationsService } from 'vs/editor/common/services/markerDecorationsServiceImpl'; @@ -103,7 +101,6 @@ import { DownloadService } from 'vs/platform/download/common/downloadService'; registerSingleton(IExtensionGalleryService, ExtensionGalleryService, true); registerSingleton(IContextViewService, ContextViewService, true); registerSingleton(IListService, ListService, true); -registerSingleton(IOpenerService, OpenerService, true); registerSingleton(IEditorWorkerService, EditorWorkerServiceImpl); registerSingleton(IMarkerDecorationsService, MarkerDecorationsService); registerSingleton(IMarkerService, MarkerService, true); @@ -196,6 +193,7 @@ import 'vs/workbench/contrib/relauncher/common/relauncher.contribution'; // Remote import 'vs/workbench/contrib/remote/common/remote.contribution'; +import 'vs/workbench/contrib/remote/browser/remote'; // Emmet // import 'vs/workbench/contrib/emmet/browser/emmet.contribution'; {{SQL CARBON EDIT}} @@ -232,4 +230,7 @@ import 'vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution'; // Outline import 'vs/workbench/contrib/outline/browser/outline.contribution'; +// Experiments +import 'vs/workbench/contrib/experiments/browser/experiments.contribution'; + //#endregion diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index f26571a796..20823844d4 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -49,6 +49,7 @@ import 'vs/workbench/services/extensionManagement/node/extensionManagementServic import 'vs/workbench/services/accessibility/node/accessibilityService'; import 'vs/workbench/services/remote/node/tunnelService'; import 'vs/workbench/services/backup/node/backupFileService'; +import 'vs/workbench/services/opener/electron-browser/openerService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; @@ -263,9 +264,6 @@ import 'vs/workbench/contrib/themes/test/electron-browser/themes.test.contributi import 'vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.contribution'; import 'vs/workbench/contrib/welcome/page/browser/welcomePage.contribution'; -// Experiments -import 'vs/workbench/contrib/experiments/electron-browser/experiments.contribution'; - // Issues import 'vs/workbench/contrib/issue/electron-browser/issue.contribution'; diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index b1d44d3599..812a61c2ce 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -54,6 +54,8 @@ import { ContextMenuService } from 'vs/platform/contextview/browser/contextMenuS import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { BackupFileService } from 'vs/workbench/services/backup/common/backupFileService'; import { ExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagementService'; +import { OpenerService } from 'vs/editor/browser/services/openerService'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; registerSingleton(IRequestService, RequestService, true); registerSingleton(IExtensionManagementService, ExtensionManagementService); @@ -63,6 +65,7 @@ registerSingleton(IClipboardService, BrowserClipboardService, true); registerSingleton(IAccessibilityService, BrowserAccessibilityService, true); registerSingleton(ILifecycleService, BrowserLifecycleService); registerSingleton(IContextMenuService, ContextMenuService); +registerSingleton(IOpenerService, OpenerService, true); //#endregion diff --git a/yarn.lock b/yarn.lock index 38deacd755..6016c1acfc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4154,15 +4154,18 @@ gulp-tslint@^8.1.3: plugin-error "1.0.1" through "~2.3.8" -gulp-uglify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/gulp-uglify/-/gulp-uglify-3.0.0.tgz#0df0331d72a0d302e3e37e109485dddf33c6d1ca" - integrity sha1-DfAzHXKg0wLj434QlIXd3zPG0co= +gulp-uglify@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/gulp-uglify/-/gulp-uglify-3.0.2.tgz#5f5b2e8337f879ca9dec971feb1b82a5a87850b0" + integrity sha512-gk1dhB74AkV2kzqPMQBLA3jPoIAPd/nlNzP2XMDSG8XZrqnlCiDGAqC+rZOumzFvB5zOphlFh6yr3lgcAb/OOg== dependencies: + array-each "^1.0.1" + extend-shallow "^3.0.2" gulplog "^1.0.0" has-gulplog "^0.1.0" - lodash "^4.13.1" + isobject "^3.0.1" make-error-cause "^1.1.1" + safe-buffer "^5.1.2" through2 "^2.0.0" uglify-js "^3.0.5" vinyl-sourcemaps-apply "^0.2.0" @@ -5622,7 +5625,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@^4.13.1, lodash@^4.15.0, lodash@^4.3.0: +lodash@^4.15.0, lodash@^4.3.0: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" integrity sha1-eCA6TRwyiuHYbcpkYONptX9AVa4= @@ -9469,15 +9472,7 @@ uc.micro@^1.0.1, uc.micro@^1.0.3: resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.3.tgz#7ed50d5e0f9a9fb0a573379259f2a77458d50192" integrity sha1-ftUNXg+an7ClczeSWfKndFjVAZI= -uglify-es@^3.0.18: - version "3.1.9" - resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.1.9.tgz#6c82df628ac9eb7af9c61fd70c744a084abe6161" - integrity sha512-wVSiJKHDgDDFmxTVVvnbAH6IpamAFHYDI+5JvwPdaqIMnk8kRTX2JKwq1Fx7gb2+Jj5Dus8kzvIpKkWOMNU51w== - dependencies: - commander "~2.11.0" - source-map "~0.6.1" - -uglify-es@^3.3.4: +uglify-es@^3.3.4, uglify-es@^3.3.9: version "3.3.9" resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" integrity sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==