diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml
index 0d0b2608757..0d01ba8a608 100644
--- a/build/azure-pipelines/linux/product-build-linux.yml
+++ b/build/azure-pipelines/linux/product-build-linux.yml
@@ -100,7 +100,7 @@ steps:
- script: |
set -e
- DISPLAY=:10 ./scripts/test.sh --build --tfs --no-sandbox "Unit Tests"
+ DISPLAY=:10 ./scripts/test.sh --build --tfs "Unit Tests"
displayName: Run unit tests
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js
index 7bbf246a8dc..44f38953adb 100644
--- a/build/gulpfile.editor.js
+++ b/build/gulpfile.editor.js
@@ -45,6 +45,7 @@ var editorResources = [
'!out-build/vs/base/browser/ui/splitview/**/*',
'!out-build/vs/base/browser/ui/toolbar/**/*',
'!out-build/vs/base/browser/ui/octiconLabel/**/*',
+ '!out-build/vs/base/browser/ui/codiconLabel/**/*',
'!out-build/vs/workbench/**',
'!**/test/**'
];
@@ -91,6 +92,7 @@ const extractEditorSrcTask = task.define('extract-editor-src', () => {
],
redirects: {
'vs/base/browser/ui/octiconLabel/octiconLabel': 'vs/base/browser/ui/octiconLabel/octiconLabel.mock',
+ 'vs/base/browser/ui/codiconLabel/codiconLabel': 'vs/base/browser/ui/codiconLabel/codiconLabel.mock',
},
shakeLevel: 2, // 0-Files, 1-InnerFile, 2-ClassMembers
importIgnorePattern: /(^vs\/css!)|(promise-polyfill\/polyfill)/,
diff --git a/build/gulpfile.hygiene.js b/build/gulpfile.hygiene.js
index 5c60d5db221..30c98f95d06 100644
--- a/build/gulpfile.hygiene.js
+++ b/build/gulpfile.hygiene.js
@@ -72,6 +72,7 @@ const indentationFilter = [
// except multiple specific folders
'!**/octicons/**',
+ '!**/codicon/**',
'!**/fixtures/**',
'!**/lib/**',
'!extensions/**/out/**',
diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js
index 3e9e2276bfe..9a63fcec936 100644
--- a/build/gulpfile.vscode.js
+++ b/build/gulpfile.vscode.js
@@ -68,6 +68,7 @@ const vscodeResources = [
'out-build/vs/base/node/languagePacks.js',
'out-build/vs/base/node/{stdForkStart.js,terminateProcess.sh,cpuUsage.sh,ps.sh}',
'out-build/vs/base/browser/ui/octiconLabel/octicons/**',
+ 'out-build/vs/base/browser/ui/codiconLabel/codicon/**',
'out-build/vs/workbench/browser/media/*-theme.css',
'out-build/vs/workbench/contrib/debug/**/*.json',
'out-build/vs/workbench/contrib/externalTerminal/**/*.scpt',
diff --git a/extensions/cpp/test/colorize-results/test-23630_cpp.json b/extensions/cpp/test/colorize-results/test-23630_cpp.json
index 08d81e6afff..26da4333e92 100644
--- a/extensions/cpp/test/colorize-results/test-23630_cpp.json
+++ b/extensions/cpp/test/colorize-results/test-23630_cpp.json
@@ -3,8 +3,8 @@
"c": "#",
"t": "source.cpp keyword.control.directive.conditional.ifndef.cpp punctuation.definition.directive.cpp",
"r": {
- "dark_plus": "keyword.control: #C586C0",
- "light_plus": "keyword.control: #AF00DB",
+ "dark_plus": "source.cpp keyword.control.directive: #9B9B9B",
+ "light_plus": "source.cpp keyword.control.directive: #808080",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
@@ -14,8 +14,8 @@
"c": "ifndef",
"t": "source.cpp keyword.control.directive.conditional.ifndef.cpp",
"r": {
- "dark_plus": "keyword.control: #C586C0",
- "light_plus": "keyword.control: #AF00DB",
+ "dark_plus": "source.cpp keyword.control.directive: #9B9B9B",
+ "light_plus": "source.cpp keyword.control.directive: #808080",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
@@ -36,8 +36,8 @@
"c": "_UCRT",
"t": "source.cpp meta.preprocessor.conditional.cpp entity.name.function.preprocessor.cpp",
"r": {
- "dark_plus": "entity.name.function: #DCDCAA",
- "light_plus": "entity.name.function: #795E26",
+ "dark_plus": "source.cpp entity.name.function.preprocessor: #C586C0",
+ "light_plus": "source.cpp entity.name.function.preprocessor: #AF00DB",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "entity.name.function: #DCDCAA"
@@ -58,8 +58,8 @@
"c": "#",
"t": "source.cpp meta.preprocessor.macro.cpp keyword.control.directive.define.cpp punctuation.definition.directive.cpp",
"r": {
- "dark_plus": "keyword.control: #C586C0",
- "light_plus": "keyword.control: #AF00DB",
+ "dark_plus": "source.cpp keyword.control.directive: #9B9B9B",
+ "light_plus": "source.cpp keyword.control.directive: #808080",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
@@ -69,8 +69,8 @@
"c": "define",
"t": "source.cpp meta.preprocessor.macro.cpp keyword.control.directive.define.cpp",
"r": {
- "dark_plus": "keyword.control: #C586C0",
- "light_plus": "keyword.control: #AF00DB",
+ "dark_plus": "source.cpp keyword.control.directive: #9B9B9B",
+ "light_plus": "source.cpp keyword.control.directive: #808080",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
@@ -91,8 +91,8 @@
"c": "_UCRT",
"t": "source.cpp meta.preprocessor.macro.cpp entity.name.function.preprocessor.cpp",
"r": {
- "dark_plus": "entity.name.function: #DCDCAA",
- "light_plus": "entity.name.function: #795E26",
+ "dark_plus": "source.cpp entity.name.function.preprocessor: #C586C0",
+ "light_plus": "source.cpp entity.name.function.preprocessor: #AF00DB",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "entity.name.function: #DCDCAA"
@@ -102,8 +102,8 @@
"c": "#",
"t": "source.cpp keyword.control.directive.endif.cpp punctuation.definition.directive.cpp",
"r": {
- "dark_plus": "keyword.control: #C586C0",
- "light_plus": "keyword.control: #AF00DB",
+ "dark_plus": "source.cpp keyword.control.directive: #9B9B9B",
+ "light_plus": "source.cpp keyword.control.directive: #808080",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
@@ -113,8 +113,8 @@
"c": "endif",
"t": "source.cpp keyword.control.directive.endif.cpp",
"r": {
- "dark_plus": "keyword.control: #C586C0",
- "light_plus": "keyword.control: #AF00DB",
+ "dark_plus": "source.cpp keyword.control.directive: #9B9B9B",
+ "light_plus": "source.cpp keyword.control.directive: #808080",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
diff --git a/extensions/cpp/test/colorize-results/test-23850_cpp.json b/extensions/cpp/test/colorize-results/test-23850_cpp.json
index 4ba6b59dc9b..9a0466d8c29 100644
--- a/extensions/cpp/test/colorize-results/test-23850_cpp.json
+++ b/extensions/cpp/test/colorize-results/test-23850_cpp.json
@@ -3,8 +3,8 @@
"c": "#",
"t": "source.cpp keyword.control.directive.conditional.ifndef.cpp punctuation.definition.directive.cpp",
"r": {
- "dark_plus": "keyword.control: #C586C0",
- "light_plus": "keyword.control: #AF00DB",
+ "dark_plus": "source.cpp keyword.control.directive: #9B9B9B",
+ "light_plus": "source.cpp keyword.control.directive: #808080",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
@@ -14,8 +14,8 @@
"c": "ifndef",
"t": "source.cpp keyword.control.directive.conditional.ifndef.cpp",
"r": {
- "dark_plus": "keyword.control: #C586C0",
- "light_plus": "keyword.control: #AF00DB",
+ "dark_plus": "source.cpp keyword.control.directive: #9B9B9B",
+ "light_plus": "source.cpp keyword.control.directive: #808080",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
@@ -36,8 +36,8 @@
"c": "_UCRT",
"t": "source.cpp meta.preprocessor.conditional.cpp entity.name.function.preprocessor.cpp",
"r": {
- "dark_plus": "entity.name.function: #DCDCAA",
- "light_plus": "entity.name.function: #795E26",
+ "dark_plus": "source.cpp entity.name.function.preprocessor: #C586C0",
+ "light_plus": "source.cpp entity.name.function.preprocessor: #AF00DB",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "entity.name.function: #DCDCAA"
@@ -47,8 +47,8 @@
"c": "#",
"t": "source.cpp meta.preprocessor.macro.cpp keyword.control.directive.define.cpp punctuation.definition.directive.cpp",
"r": {
- "dark_plus": "keyword.control: #C586C0",
- "light_plus": "keyword.control: #AF00DB",
+ "dark_plus": "source.cpp keyword.control.directive: #9B9B9B",
+ "light_plus": "source.cpp keyword.control.directive: #808080",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
@@ -58,8 +58,8 @@
"c": "define",
"t": "source.cpp meta.preprocessor.macro.cpp keyword.control.directive.define.cpp",
"r": {
- "dark_plus": "keyword.control: #C586C0",
- "light_plus": "keyword.control: #AF00DB",
+ "dark_plus": "source.cpp keyword.control.directive: #9B9B9B",
+ "light_plus": "source.cpp keyword.control.directive: #808080",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
@@ -80,8 +80,8 @@
"c": "_UCRT",
"t": "source.cpp meta.preprocessor.macro.cpp entity.name.function.preprocessor.cpp",
"r": {
- "dark_plus": "entity.name.function: #DCDCAA",
- "light_plus": "entity.name.function: #795E26",
+ "dark_plus": "source.cpp entity.name.function.preprocessor: #C586C0",
+ "light_plus": "source.cpp entity.name.function.preprocessor: #AF00DB",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "entity.name.function: #DCDCAA"
@@ -91,8 +91,8 @@
"c": "#",
"t": "source.cpp keyword.control.directive.endif.cpp punctuation.definition.directive.cpp",
"r": {
- "dark_plus": "keyword.control: #C586C0",
- "light_plus": "keyword.control: #AF00DB",
+ "dark_plus": "source.cpp keyword.control.directive: #9B9B9B",
+ "light_plus": "source.cpp keyword.control.directive: #808080",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
@@ -102,8 +102,8 @@
"c": "endif",
"t": "source.cpp keyword.control.directive.endif.cpp",
"r": {
- "dark_plus": "keyword.control: #C586C0",
- "light_plus": "keyword.control: #AF00DB",
+ "dark_plus": "source.cpp keyword.control.directive: #9B9B9B",
+ "light_plus": "source.cpp keyword.control.directive: #808080",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
diff --git a/extensions/cpp/test/colorize-results/test-78769_cpp.json b/extensions/cpp/test/colorize-results/test-78769_cpp.json
index e714cf5e26b..da4cbdd99b2 100644
--- a/extensions/cpp/test/colorize-results/test-78769_cpp.json
+++ b/extensions/cpp/test/colorize-results/test-78769_cpp.json
@@ -3,8 +3,8 @@
"c": "#",
"t": "source.cpp meta.preprocessor.macro.cpp keyword.control.directive.define.cpp punctuation.definition.directive.cpp",
"r": {
- "dark_plus": "keyword.control: #C586C0",
- "light_plus": "keyword.control: #AF00DB",
+ "dark_plus": "source.cpp keyword.control.directive: #9B9B9B",
+ "light_plus": "source.cpp keyword.control.directive: #808080",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
@@ -14,8 +14,8 @@
"c": "define",
"t": "source.cpp meta.preprocessor.macro.cpp keyword.control.directive.define.cpp",
"r": {
- "dark_plus": "keyword.control: #C586C0",
- "light_plus": "keyword.control: #AF00DB",
+ "dark_plus": "source.cpp keyword.control.directive: #9B9B9B",
+ "light_plus": "source.cpp keyword.control.directive: #808080",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
@@ -36,8 +36,8 @@
"c": "DOCTEST_IMPLEMENT_FIXTURE",
"t": "source.cpp meta.preprocessor.macro.cpp entity.name.function.preprocessor.cpp",
"r": {
- "dark_plus": "entity.name.function: #DCDCAA",
- "light_plus": "entity.name.function: #795E26",
+ "dark_plus": "source.cpp entity.name.function.preprocessor: #C586C0",
+ "light_plus": "source.cpp entity.name.function.preprocessor: #AF00DB",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "entity.name.function: #DCDCAA"
@@ -58,8 +58,8 @@
"c": "der",
"t": "source.cpp meta.preprocessor.macro.cpp variable.parameter.preprocessor.cpp",
"r": {
- "dark_plus": "variable: #9CDCFE",
- "light_plus": "variable: #001080",
+ "dark_plus": "source.cpp variable.parameter: #7F7F7F",
+ "light_plus": "source.cpp variable.parameter: #808080",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "variable: #9CDCFE"
@@ -91,8 +91,8 @@
"c": "base",
"t": "source.cpp meta.preprocessor.macro.cpp variable.parameter.preprocessor.cpp",
"r": {
- "dark_plus": "variable: #9CDCFE",
- "light_plus": "variable: #001080",
+ "dark_plus": "source.cpp variable.parameter: #7F7F7F",
+ "light_plus": "source.cpp variable.parameter: #808080",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "variable: #9CDCFE"
@@ -124,8 +124,8 @@
"c": "func",
"t": "source.cpp meta.preprocessor.macro.cpp variable.parameter.preprocessor.cpp",
"r": {
- "dark_plus": "variable: #9CDCFE",
- "light_plus": "variable: #001080",
+ "dark_plus": "source.cpp variable.parameter: #7F7F7F",
+ "light_plus": "source.cpp variable.parameter: #808080",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "variable: #9CDCFE"
@@ -157,8 +157,8 @@
"c": "decorators",
"t": "source.cpp meta.preprocessor.macro.cpp variable.parameter.preprocessor.cpp",
"r": {
- "dark_plus": "variable: #9CDCFE",
- "light_plus": "variable: #001080",
+ "dark_plus": "source.cpp variable.parameter: #7F7F7F",
+ "light_plus": "source.cpp variable.parameter: #808080",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "variable: #9CDCFE"
diff --git a/extensions/cpp/test/colorize-results/test_cc.json b/extensions/cpp/test/colorize-results/test_cc.json
index 902ef645164..6c3e21a12a3 100644
--- a/extensions/cpp/test/colorize-results/test_cc.json
+++ b/extensions/cpp/test/colorize-results/test_cc.json
@@ -3,8 +3,8 @@
"c": "#",
"t": "source.cpp keyword.control.directive.conditional.if.cpp punctuation.definition.directive.cpp",
"r": {
- "dark_plus": "keyword.control: #C586C0",
- "light_plus": "keyword.control: #AF00DB",
+ "dark_plus": "source.cpp keyword.control.directive: #9B9B9B",
+ "light_plus": "source.cpp keyword.control.directive: #808080",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
@@ -14,8 +14,8 @@
"c": "if",
"t": "source.cpp keyword.control.directive.conditional.if.cpp",
"r": {
- "dark_plus": "keyword.control: #C586C0",
- "light_plus": "keyword.control: #AF00DB",
+ "dark_plus": "source.cpp keyword.control.directive: #9B9B9B",
+ "light_plus": "source.cpp keyword.control.directive: #808080",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
@@ -36,8 +36,8 @@
"c": "B4G_DEBUG_CHECK",
"t": "source.cpp meta.preprocessor.conditional.cpp entity.name.function.preprocessor.cpp",
"r": {
- "dark_plus": "entity.name.function: #DCDCAA",
- "light_plus": "entity.name.function: #795E26",
+ "dark_plus": "source.cpp entity.name.function.preprocessor: #C586C0",
+ "light_plus": "source.cpp entity.name.function.preprocessor: #AF00DB",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "entity.name.function: #DCDCAA"
@@ -652,8 +652,8 @@
"c": "#",
"t": "source.cpp keyword.control.directive.endif.cpp punctuation.definition.directive.cpp",
"r": {
- "dark_plus": "keyword.control: #C586C0",
- "light_plus": "keyword.control: #AF00DB",
+ "dark_plus": "source.cpp keyword.control.directive: #9B9B9B",
+ "light_plus": "source.cpp keyword.control.directive: #808080",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
@@ -663,8 +663,8 @@
"c": "endif",
"t": "source.cpp keyword.control.directive.endif.cpp",
"r": {
- "dark_plus": "keyword.control: #C586C0",
- "light_plus": "keyword.control: #AF00DB",
+ "dark_plus": "source.cpp keyword.control.directive: #9B9B9B",
+ "light_plus": "source.cpp keyword.control.directive: #808080",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
@@ -751,8 +751,8 @@
"c": "obj",
"t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp variable.parameter.cpp",
"r": {
- "dark_plus": "variable: #9CDCFE",
- "light_plus": "variable: #001080",
+ "dark_plus": "source.cpp variable.parameter: #7F7F7F",
+ "light_plus": "source.cpp variable.parameter: #808080",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "variable: #9CDCFE"
@@ -1037,7 +1037,7 @@
"c": "x",
"t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp variable.other.property.cpp",
"r": {
- "dark_plus": "variable: #9CDCFE",
+ "dark_plus": "source.cpp variable.other.property: #DADADA",
"light_plus": "variable: #001080",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
diff --git a/extensions/cpp/test/colorize-results/test_cpp.json b/extensions/cpp/test/colorize-results/test_cpp.json
index f84d916afa3..9a6fe977b57 100644
--- a/extensions/cpp/test/colorize-results/test_cpp.json
+++ b/extensions/cpp/test/colorize-results/test_cpp.json
@@ -25,8 +25,8 @@
"c": "#",
"t": "source.cpp meta.preprocessor.include.cpp keyword.control.directive.include.cpp punctuation.definition.directive.cpp",
"r": {
- "dark_plus": "keyword.control: #C586C0",
- "light_plus": "keyword.control: #AF00DB",
+ "dark_plus": "source.cpp keyword.control.directive: #9B9B9B",
+ "light_plus": "source.cpp keyword.control.directive: #808080",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
@@ -36,8 +36,8 @@
"c": "include",
"t": "source.cpp meta.preprocessor.include.cpp keyword.control.directive.include.cpp",
"r": {
- "dark_plus": "keyword.control: #C586C0",
- "light_plus": "keyword.control: #AF00DB",
+ "dark_plus": "source.cpp keyword.control.directive: #9B9B9B",
+ "light_plus": "source.cpp keyword.control.directive: #808080",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
@@ -157,8 +157,8 @@
"c": "#",
"t": "source.cpp meta.preprocessor.macro.cpp keyword.control.directive.define.cpp punctuation.definition.directive.cpp",
"r": {
- "dark_plus": "keyword.control: #C586C0",
- "light_plus": "keyword.control: #AF00DB",
+ "dark_plus": "source.cpp keyword.control.directive: #9B9B9B",
+ "light_plus": "source.cpp keyword.control.directive: #808080",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
@@ -168,8 +168,8 @@
"c": "define",
"t": "source.cpp meta.preprocessor.macro.cpp keyword.control.directive.define.cpp",
"r": {
- "dark_plus": "keyword.control: #C586C0",
- "light_plus": "keyword.control: #AF00DB",
+ "dark_plus": "source.cpp keyword.control.directive: #9B9B9B",
+ "light_plus": "source.cpp keyword.control.directive: #808080",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
@@ -190,8 +190,8 @@
"c": "EXTERN_C",
"t": "source.cpp meta.preprocessor.macro.cpp entity.name.function.preprocessor.cpp",
"r": {
- "dark_plus": "entity.name.function: #DCDCAA",
- "light_plus": "entity.name.function: #795E26",
+ "dark_plus": "source.cpp entity.name.function.preprocessor: #C586C0",
+ "light_plus": "source.cpp entity.name.function.preprocessor: #AF00DB",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "entity.name.function: #DCDCAA"
@@ -817,8 +817,8 @@
"c": "x",
"t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp variable.parameter.cpp",
"r": {
- "dark_plus": "variable: #9CDCFE",
- "light_plus": "variable: #001080",
+ "dark_plus": "source.cpp variable.parameter: #7F7F7F",
+ "light_plus": "source.cpp variable.parameter: #808080",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "variable: #9CDCFE"
@@ -872,8 +872,8 @@
"c": "y",
"t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp variable.parameter.cpp",
"r": {
- "dark_plus": "variable: #9CDCFE",
- "light_plus": "variable: #001080",
+ "dark_plus": "source.cpp variable.parameter: #7F7F7F",
+ "light_plus": "source.cpp variable.parameter: #808080",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "variable: #9CDCFE"
diff --git a/extensions/theme-defaults/themes/dark_plus.json b/extensions/theme-defaults/themes/dark_plus.json
index f12d3d7633e..3efcc124ff0 100644
--- a/extensions/theme-defaults/themes/dark_plus.json
+++ b/extensions/theme-defaults/themes/dark_plus.json
@@ -170,6 +170,73 @@
"settings": {
"foreground": "#d7ba7d"
}
+ },
+ // Scopes that are potentially C++ only follow
+ {
+ "scope": "source.cpp entity.name",
+ "settings": {
+ "foreground": "#C8C8C8"
+ }
+ },
+ {
+ "scope": "source.cpp keyword.control.directive",
+ "settings": {
+ "foreground": "#9B9B9B"
+ }
+ },
+ {
+ "scope": "source.cpp entity.name.function.operator",
+ "settings": {
+ "foreground": "#B4B4B4"
+ }
+ },
+ {
+ "scope": "source.cpp entity.name.function.preprocessor",
+ "settings": {
+ "foreground": "#C586C0"
+ }
+ },
+ {
+ "scope": "source.cpp entity.name.label",
+ "settings": {
+ "foreground": "#C8C8C8"
+ }
+ },
+ {
+ "scope": "source.cpp entity.name.operator.custom-literal",
+ "settings": {
+ "foreground": "#DADADA"
+ }
+ },
+ {
+ "scope": "source.cpp entity.name.operator.custom-literal.number",
+ "settings": {
+ "foreground": "#B5CEA8"
+ }
+ },
+ {
+ "scope": "source.cpp entity.name.operator.custom-literal.string",
+ "settings": {
+ "foreground": "#ce9178"
+ }
+ },
+ {
+ "scope": "source.cpp variable.other.enummember",
+ "settings": {
+ "foreground": "#B8D7A3"
+ }
+ },
+ {
+ "scope": "source.cpp variable.other.property",
+ "settings": {
+ "foreground": "#DADADA"
+ }
+ },
+ {
+ "scope": "source.cpp variable.parameter",
+ "settings": {
+ "foreground": "#7F7F7F"
+ }
}
]
}
diff --git a/extensions/theme-defaults/themes/light_plus.json b/extensions/theme-defaults/themes/light_plus.json
index 3d30c5d17fb..e6a4c922357 100644
--- a/extensions/theme-defaults/themes/light_plus.json
+++ b/extensions/theme-defaults/themes/light_plus.json
@@ -170,7 +170,55 @@
"settings": {
"foreground": "#ff0000"
}
+ },
+ // Scopes that are potentially C++ only follow
+ {
+ "scope": "source.cpp entity.name",
+ "settings": {
+ "foreground": "#000000"
+ }
+ },
+ {
+ "scope": "source.cpp keyword.control.directive",
+ "settings": {
+ "foreground": "#808080"
+ }
+ },
+ {
+ "scope": "source.cpp entity.name.function.operator",
+ "settings": {
+ "foreground": "#008080"
+ }
+ },
+ {
+ "scope": "source.cpp entity.name.function.preprocessor",
+ "settings": {
+ "foreground": "#AF00DB"
+ }
+ },
+ {
+ "scope": "source.cpp entity.name.label",
+ "settings": {
+ "foreground": "#000000"
+ }
+ },
+ {
+ "scope": "source.cpp entity.name.operator.custom-literal.string",
+ "settings": {
+ "foreground": "#0451a5"
+ }
+ },
+ {
+ "scope": "source.cpp variable.other.enummember",
+ "settings": {
+ "foreground": "#2F4F4F"
+ }
+ },
+ {
+ "scope": "source.cpp variable.parameter",
+ "settings": {
+ "foreground": "#808080"
+ }
}
-
]
}
diff --git a/package.json b/package.json
index 673662926a8..a03e9dd1f40 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "code-oss-dev",
"version": "1.39.0",
- "distro": "e892717dc1ae3cea3824697bc2c28d64f8476f93",
+ "distro": "8ec29a9878d2ca77f73efc02f382f177d200fbb9",
"author": {
"name": "Microsoft Corporation"
},
diff --git a/resources/linux/code-url-handler.desktop b/resources/linux/code-url-handler.desktop
index 7106e0e0969..b85525fbd04 100644
--- a/resources/linux/code-url-handler.desktop
+++ b/resources/linux/code-url-handler.desktop
@@ -2,7 +2,7 @@
Name=@@NAME_LONG@@ - URL Handler
Comment=Code Editing. Redefined.
GenericName=Text Editor
-Exec=@@EXEC@@ --open-url %U
+Exec=@@EXEC@@ --no-sandbox --open-url %U
Icon=@@ICON@@
Type=Application
NoDisplay=true
diff --git a/resources/linux/code.desktop b/resources/linux/code.desktop
index 1273bb2db7c..b975e1094a2 100644
--- a/resources/linux/code.desktop
+++ b/resources/linux/code.desktop
@@ -2,7 +2,7 @@
Name=@@NAME_LONG@@
Comment=Code Editing. Redefined.
GenericName=Text Editor
-Exec=@@EXEC@@ --unity-launch %F
+Exec=@@EXEC@@ --no-sandbox --unity-launch %F
Icon=@@ICON@@
Type=Application
StartupNotify=false
@@ -14,5 +14,5 @@ Keywords=vscode;
[Desktop Action new-empty-window]
Name=New Empty Window
-Exec=@@EXEC@@ --new-window %F
+Exec=@@EXEC@@ --no-sandbox --new-window %F
Icon=@@ICON@@
diff --git a/resources/win32/bin/code.sh b/resources/win32/bin/code.sh
index f925851fb95..43aaa25a96e 100644
--- a/resources/win32/bin/code.sh
+++ b/resources/win32/bin/code.sh
@@ -27,15 +27,15 @@ if grep -qi Microsoft /proc/version; then
WSL_EXT_ID="ms-vscode-remote.remote-wsl"
if [ $WSL_BUILD -ge 41955 -a $WSL_BUILD -lt 41959 ]; then
- # WSL2 in workaround for https://github.com/microsoft/WSL/issues/4337
+ # WSL2 workaround for https://github.com/microsoft/WSL/issues/4337
CWD="$(pwd)"
cd "$VSCODE_PATH"
- cmd.exe /C ".\\bin\\$APP_NAME.cmd --locate-extension $WSL_EXT_ID >remote-wsl-loc.txt"
- WSL_EXT_WLOC="$(cat ./remote-wsl-loc.txt)"
- rm remote-wsl-loc.txt
+ cmd.exe /C ".\\bin\\$APP_NAME.cmd --locate-extension $WSL_EXT_ID >%TEMP%\\remote-wsl-loc.txt"
+ WSL_EXT_WLOC=$(cmd.exe /C type %TEMP%\\remote-wsl-loc.txt)
cd "$CWD"
else
- WSL_EXT_WLOC=$(ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" --locate-extension $WSL_EXT_ID)
+ ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" --locate-extension $WSL_EXT_ID >/tmp/remote-wsl-loc.txt
+ WSL_EXT_WLOC=$(cat /tmp/remote-wsl-loc.txt)
fi
if [ -n "$WSL_EXT_WLOC" ]; then
# replace \r\n with \n in WSL_EXT_WLOC
diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh
index 143e2ff4eb4..2c3a571ffc7 100755
--- a/scripts/test-integration.sh
+++ b/scripts/test-integration.sh
@@ -8,7 +8,7 @@ if [[ "$OSTYPE" == "darwin"* ]]; then
else
ROOT=$(dirname $(dirname $(readlink -f $0)))
VSCODEUSERDATADIR=`mktemp -d 2>/dev/null`
- LINUX_NO_SANDBOX="--no-sandbox" # workaround Electron 6 issue on Linux when running tests in container
+ LINUX_NO_SANDBOX="--no-sandbox" # Electron 6 introduces a chrome-sandbox that requires root to run. This can fail. Disable sandbox via --no-sandbox.
fi
cd $ROOT
@@ -34,7 +34,7 @@ else
fi
# Integration tests in AMD
-./scripts/test.sh $LINUX_NO_SANDBOX --runGlob **/*.integrationTest.js "$@"
+./scripts/test.sh --runGlob **/*.integrationTest.js "$@"
# Tests in the extension host
"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-api-tests/testWorkspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR
diff --git a/scripts/test.sh b/scripts/test.sh
index e1ed5aa65c7..630af4e53e5 100755
--- a/scripts/test.sh
+++ b/scripts/test.sh
@@ -34,5 +34,5 @@ else
cd $ROOT ; \
ELECTRON_ENABLE_LOGGING=1 \
"$CODE" \
- test/electron/index.js "$@"
+ test/electron/index.js --no-sandbox "$@" # Electron 6 introduces a chrome-sandbox that requires root to run. This can fail. Disable sandbox via --no-sandbox.
fi
diff --git a/src/vs/base/browser/ui/actionbar/actionbar.css b/src/vs/base/browser/ui/actionbar/actionbar.css
index 25795ddb631..f2eff2c7f66 100644
--- a/src/vs/base/browser/ui/actionbar/actionbar.css
+++ b/src/vs/base/browser/ui/actionbar/actionbar.css
@@ -40,7 +40,8 @@
transform: scale(1.272019649, 1.272019649); /* 1.272019649 = √φ */
}
-.monaco-action-bar .action-item .icon {
+.monaco-action-bar .action-item .icon,
+.monaco-action-bar .action-item .codicon {
display: inline-block;
}
@@ -95,4 +96,4 @@
display: flex;
align-items: center;
justify-content: center;
-}
\ No newline at end of file
+}
diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts
index 538f7fa0dbe..38ec0a60a51 100644
--- a/src/vs/base/browser/ui/actionbar/actionbar.ts
+++ b/src/vs/base/browser/ui/actionbar/actionbar.ts
@@ -311,14 +311,14 @@ export class ActionViewItem extends BaseActionViewItem {
if (this.options.icon) {
this.cssClass = this.getAction().class;
- DOM.addClass(this.label, 'icon');
+ DOM.addClass(this.label, 'codicon');
if (this.cssClass) {
DOM.addClasses(this.label, this.cssClass);
}
this.updateEnabled();
} else {
- DOM.removeClass(this.label, 'icon');
+ DOM.removeClass(this.label, 'codicon');
}
}
diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon-animations.css b/src/vs/base/browser/ui/codiconLabel/codicon/codicon-animations.css
new file mode 100644
index 00000000000..86c223674ec
--- /dev/null
+++ b/src/vs/base/browser/ui/codiconLabel/codicon/codicon-animations.css
@@ -0,0 +1,14 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+@keyframes codicon-spin {
+ 100% {
+ transform:rotate(360deg);
+ }
+}
+
+.codicon-animation-spin {
+ animation: octicon-spin 1.5s linear infinite;
+}
diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css
new file mode 100644
index 00000000000..417afa0de5d
--- /dev/null
+++ b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css
@@ -0,0 +1,354 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+@font-face {
+ font-family: "codicon";
+ src: url("./codicon.ttf?e042d2dda15ef7b36b910e3edf539f26") format("truetype");
+}
+
+.codicon[class*='codicon-'] {
+ font: normal normal normal 16px/1 codicon;
+ display: inline-block;
+ text-decoration: none;
+ text-rendering: auto;
+ text-align: center;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+
+.codicon-add:before { content: "\ea60" }
+.codicon-plus:before { content: "\ea60" }
+.codicon-gist-new:before { content: "\ea60" }
+.codicon-repo-create:before { content: "\ea60" }
+.codicon-lightbulb:before { content: "\ea61" }
+.codicon-light-bulb:before { content: "\ea61" }
+.codicon-repo:before { content: "\ea62" }
+.codicon-repo-delete:before { content: "\ea62" }
+.codicon-gist-fork:before { content: "\ea63" }
+.codicon-repo-forked:before { content: "\ea63" }
+.codicon-git-pull-request:before { content: "\ea64" }
+.codicon-git-pull-request-abandoned:before { content: "\ea64" }
+.codicon-record-keys:before { content: "\ea65" }
+.codicon-keyboard:before { content: "\ea65" }
+.codicon-tag:before { content: "\ea66" }
+.codicon-tag-add:before { content: "\ea66" }
+.codicon-tag-remove:before { content: "\ea66" }
+.codicon-person:before { content: "\ea67" }
+.codicon-person-add:before { content: "\ea67" }
+.codicon-person-follow:before { content: "\ea67" }
+.codicon-person-outline:before { content: "\ea67" }
+.codicon-person-filled:before { content: "\ea67" }
+.codicon-git-branch:before { content: "\ea68" }
+.codicon-git-branch-create:before { content: "\ea68" }
+.codicon-git-branch-delete:before { content: "\ea68" }
+.codicon-source-control:before { content: "\ea68" }
+.codicon-mirror:before { content: "\ea69" }
+.codicon-mirror-public:before { content: "\ea69" }
+.codicon-star:before { content: "\ea6a" }
+.codicon-star-add:before { content: "\ea6a" }
+.codicon-star-delete:before { content: "\ea6a" }
+.codicon-comment:before { content: "\ea6b" }
+.codicon-comment-add:before { content: "\ea6b" }
+.codicon-alert:before { content: "\ea6c" }
+.codicon-warning:before { content: "\ea6c" }
+.codicon-search:before { content: "\ea6d" }
+.codicon-search-save:before { content: "\ea6d" }
+.codicon-log-out:before { content: "\ea6e" }
+.codicon-sign-out:before { content: "\ea6e" }
+.codicon-log-in:before { content: "\ea6f" }
+.codicon-sign-in:before { content: "\ea6f" }
+.codicon-eye:before { content: "\ea70" }
+.codicon-eye-unwatch:before { content: "\ea70" }
+.codicon-eye-watch:before { content: "\ea70" }
+.codicon-circle-filled:before { content: "\ea71" }
+.codicon-primitive-dot:before { content: "\ea71" }
+.codicon-stop:before { content: "\ea72" }
+.codicon-primitive-square:before { content: "\ea72" }
+.codicon-edit:before { content: "\ea73" }
+.codicon-pencil:before { content: "\ea73" }
+.codicon-info:before { content: "\ea74" }
+.codicon-issue-opened:before { content: "\ea74" }
+.codicon-gist-private:before { content: "\ea75" }
+.codicon-git-fork-private:before { content: "\ea75" }
+.codicon-lock:before { content: "\ea75" }
+.codicon-mirror-private:before { content: "\ea75" }
+.codicon-close:before { content: "\ea76" }
+.codicon-remove-close:before { content: "\ea76" }
+.codicon-x:before { content: "\ea76" }
+.codicon-repo-sync:before { content: "\ea77" }
+.codicon-sync:before { content: "\ea77" }
+.codicon-clone:before { content: "\ea78" }
+.codicon-desktop-download:before { content: "\ea78" }
+.codicon-beaker:before { content: "\ea79" }
+.codicon-microscope:before { content: "\ea79" }
+.codicon-vm:before { content: "\ea7a" }
+.codicon-device-desktop:before { content: "\ea7a" }
+.codicon-file:before { content: "\ea7b" }
+.codicon-file-text:before { content: "\ea7b" }
+.codicon-more:before { content: "\ea7c" }
+.codicon-kebab-horizontal:before { content: "\ea7c" }
+.codicon-mail-reply:before { content: "\ea7d" }
+.codicon-reply:before { content: "\ea7d" }
+.codicon-organization:before { content: "\ea7e" }
+.codicon-organization-filled:before { content: "\ea7e" }
+.codicon-organization-outline:before { content: "\ea7e" }
+.codicon-new-file:before { content: "\ea7f" }
+.codicon-file-add:before { content: "\ea7f" }
+.codicon-new-folder:before { content: "\ea80" }
+.codicon-file-directory-create:before { content: "\ea80" }
+.codicon-Vector:before { content: "\f101" }
+.codicon-activate-breakpoints:before { content: "\f102" }
+.codicon-archive:before { content: "\f103" }
+.codicon-array:before { content: "\f104" }
+.codicon-arrow-both:before { content: "\f105" }
+.codicon-arrow-down:before { content: "\f106" }
+.codicon-arrow-left:before { content: "\f107" }
+.codicon-arrow-right:before { content: "\f108" }
+.codicon-arrow-small-down:before { content: "\f109" }
+.codicon-arrow-small-left:before { content: "\f10a" }
+.codicon-arrow-small-right:before { content: "\f10b" }
+.codicon-arrow-small-up:before { content: "\f10c" }
+.codicon-arrow-up:before { content: "\f10d" }
+.codicon-bell:before { content: "\f10e" }
+.codicon-bold:before { content: "\f10f" }
+.codicon-book:before { content: "\f110" }
+.codicon-bookmark:before { content: "\f111" }
+.codicon-boolean:before { content: "\f112" }
+.codicon-breakpoint-conditional-unverified:before { content: "\f113" }
+.codicon-breakpoint-conditional:before { content: "\f114" }
+.codicon-breakpoint-data-unverified:before { content: "\f115" }
+.codicon-breakpoint-data:before { content: "\f116" }
+.codicon-breakpoint-log-unverified:before { content: "\f117" }
+.codicon-breakpoint-log:before { content: "\f118" }
+.codicon-briefcase:before { content: "\f119" }
+.codicon-broadcast:before { content: "\f11a" }
+.codicon-browser:before { content: "\f11b" }
+.codicon-bug:before { content: "\f11c" }
+.codicon-calendar:before { content: "\f11d" }
+.codicon-case-sensitive:before { content: "\f11e" }
+.codicon-check:before { content: "\f11f" }
+.codicon-checklist:before { content: "\f120" }
+.codicon-chevron-down:before { content: "\f121" }
+.codicon-chevron-left:before { content: "\f122" }
+.codicon-chevron-right:before { content: "\f123" }
+.codicon-chevron-up:before { content: "\f124" }
+.codicon-circle-outline:before { content: "\f125" }
+.codicon-circle-slash:before { content: "\f126" }
+.codicon-circuit-board:before { content: "\f127" }
+.codicon-class:before { content: "\f128" }
+.codicon-clear-all:before { content: "\f129" }
+.codicon-clippy:before { content: "\f12a" }
+.codicon-close-all:before { content: "\f12b" }
+.codicon-cloud-download:before { content: "\f12c" }
+.codicon-cloud-upload:before { content: "\f12d" }
+.codicon-code:before { content: "\f12e" }
+.codicon-collapse-all:before { content: "\f12f" }
+.codicon-color-mode:before { content: "\f130" }
+.codicon-color:before { content: "\f131" }
+.codicon-comment-discussion:before { content: "\f132" }
+.codicon-compare-changes:before { content: "\f133" }
+.codicon-console:before { content: "\f134" }
+.codicon-constant:before { content: "\f135" }
+.codicon-continue:before { content: "\f136" }
+.codicon-credit-card:before { content: "\f137" }
+.codicon-current-and-breakpoint:before { content: "\f138" }
+.codicon-current:before { content: "\f139" }
+.codicon-dash:before { content: "\f13a" }
+.codicon-dashboard:before { content: "\f13b" }
+.codicon-database:before { content: "\f13c" }
+.codicon-debug:before { content: "\f13d" }
+.codicon-device-camera-video:before { content: "\f13e" }
+.codicon-device-camera:before { content: "\f13f" }
+.codicon-device-mobile:before { content: "\f140" }
+.codicon-diff-added:before { content: "\f141" }
+.codicon-diff-ignored:before { content: "\f142" }
+.codicon-diff-modified:before { content: "\f143" }
+.codicon-diff-removed:before { content: "\f144" }
+.codicon-diff-renamed:before { content: "\f145" }
+.codicon-diff:before { content: "\f146" }
+.codicon-discard:before { content: "\f147" }
+.codicon-disconnect-:before { content: "\f148" }
+.codicon-editor-layout:before { content: "\f149" }
+.codicon-ellipsis:before { content: "\f14a" }
+.codicon-empty-window:before { content: "\f14b" }
+.codicon-enumerator-member:before { content: "\f14c" }
+.codicon-enumerator:before { content: "\f14d" }
+.codicon-error:before { content: "\f14e" }
+.codicon-event:before { content: "\f14f" }
+.codicon-exclude:before { content: "\f150" }
+.codicon-extensions:before { content: "\f151" }
+.codicon-eye-closed:before { content: "\f152" }
+.codicon-field:before { content: "\f153" }
+.codicon-file-binary:before { content: "\f154" }
+.codicon-file-code:before { content: "\f155" }
+.codicon-file-media:before { content: "\f156" }
+.codicon-file-pdf:before { content: "\f157" }
+.codicon-file-submodule:before { content: "\f158" }
+.codicon-file-symlink-directory:before { content: "\f159" }
+.codicon-file-symlink-file:before { content: "\f15a" }
+.codicon-file-zip:before { content: "\f15b" }
+.codicon-files:before { content: "\f15c" }
+.codicon-filter:before { content: "\f15d" }
+.codicon-flame:before { content: "\f15e" }
+.codicon-fold-down:before { content: "\f15f" }
+.codicon-fold-up:before { content: "\f160" }
+.codicon-fold:before { content: "\f161" }
+.codicon-folder-active:before { content: "\f162" }
+.codicon-folder-opened:before { content: "\f163" }
+.codicon-folder:before { content: "\f164" }
+.codicon-gift:before { content: "\f165" }
+.codicon-gist-secret:before { content: "\f166" }
+.codicon-gist:before { content: "\f167" }
+.codicon-git-commit:before { content: "\f168" }
+.codicon-git-compare:before { content: "\f169" }
+.codicon-git-merge:before { content: "\f16a" }
+.codicon-github-action:before { content: "\f16b" }
+.codicon-github-alt:before { content: "\f16c" }
+.codicon-github:before { content: "\f16d" }
+.codicon-globe:before { content: "\f16e" }
+.codicon-go-to-file:before { content: "\f16f" }
+.codicon-grabber:before { content: "\f170" }
+.codicon-graph:before { content: "\f171" }
+.codicon-gripper:before { content: "\f172" }
+.codicon-heart:before { content: "\f173" }
+.codicon-history:before { content: "\f174" }
+.codicon-home:before { content: "\f175" }
+.codicon-horizontal-rule:before { content: "\f176" }
+.codicon-hubot:before { content: "\f177" }
+.codicon-inbox:before { content: "\f178" }
+.codicon-interface:before { content: "\f179" }
+.codicon-issue-closed:before { content: "\f17a" }
+.codicon-issue-reopened:before { content: "\f17b" }
+.codicon-issues:before { content: "\f17c" }
+.codicon-italic:before { content: "\f17d" }
+.codicon-jersey:before { content: "\f17e" }
+.codicon-json:before { content: "\f17f" }
+.codicon-kebab-vertical:before { content: "\f180" }
+.codicon-key:before { content: "\f181" }
+.codicon-keyword:before { content: "\f182" }
+.codicon-law:before { content: "\f183" }
+.codicon-lightbulb-autofix:before { content: "\f184" }
+.codicon-link-external:before { content: "\f185" }
+.codicon-link:before { content: "\f186" }
+.codicon-list-ordered:before { content: "\f187" }
+.codicon-list-unordered:before { content: "\f188" }
+.codicon-live-share:before { content: "\f189" }
+.codicon-loading:before { content: "\f18a" }
+.codicon-location:before { content: "\f18b" }
+.codicon-mail-read:before { content: "\f18c" }
+.codicon-mail:before { content: "\f18d" }
+.codicon-markdown:before { content: "\f18e" }
+.codicon-megaphone:before { content: "\f18f" }
+.codicon-mention:before { content: "\f190" }
+.codicon-method:before { content: "\f191" }
+.codicon-milestone:before { content: "\f192" }
+.codicon-misc:before { content: "\f193" }
+.codicon-mortar-board:before { content: "\f194" }
+.codicon-move:before { content: "\f195" }
+.codicon-multiple-windows:before { content: "\f196" }
+.codicon-mute:before { content: "\f197" }
+.codicon-namespace:before { content: "\f198" }
+.codicon-no-newline:before { content: "\f199" }
+.codicon-note:before { content: "\f19a" }
+.codicon-numeric:before { content: "\f19b" }
+.codicon-octoface:before { content: "\f19c" }
+.codicon-open-preview:before { content: "\f19d" }
+.codicon-operator:before { content: "\f19e" }
+.codicon-package:before { content: "\f19f" }
+.codicon-paintcan:before { content: "\f1a0" }
+.codicon-parameter:before { content: "\f1a1" }
+.codicon-pause:before { content: "\f1a2" }
+.codicon-pin:before { content: "\f1a3" }
+.codicon-play:before { content: "\f1a4" }
+.codicon-plug:before { content: "\f1a5" }
+.codicon-preserve-case:before { content: "\f1a6" }
+.codicon-preview:before { content: "\f1a7" }
+.codicon-project:before { content: "\f1a8" }
+.codicon-property:before { content: "\f1a9" }
+.codicon-pulse:before { content: "\f1aa" }
+.codicon-question:before { content: "\f1ab" }
+.codicon-quote:before { content: "\f1ac" }
+.codicon-radio-tower:before { content: "\f1ad" }
+.codicon-reactions:before { content: "\f1ae" }
+.codicon-references:before { content: "\f1af" }
+.codicon-refresh:before { content: "\f1b0" }
+.codicon-regex:before { content: "\f1b1" }
+.codicon-remote:before { content: "\f1b2" }
+.codicon-remove:before { content: "\f1b3" }
+.codicon-replace-all:before { content: "\f1b4" }
+.codicon-replace:before { content: "\f1b5" }
+.codicon-repo-clone:before { content: "\f1b6" }
+.codicon-repo-force-push:before { content: "\f1b7" }
+.codicon-repo-pull:before { content: "\f1b8" }
+.codicon-repo-push:before { content: "\f1b9" }
+.codicon-report:before { content: "\f1ba" }
+.codicon-request-changes:before { content: "\f1bb" }
+.codicon-restart:before { content: "\f1bc" }
+.codicon-rocket:before { content: "\f1bd" }
+.codicon-root-folder-opened:before { content: "\f1be" }
+.codicon-root-folder:before { content: "\f1bf" }
+.codicon-rss:before { content: "\f1c0" }
+.codicon-ruby:before { content: "\f1c1" }
+.codicon-ruler:before { content: "\f1c2" }
+.codicon-save-all:before { content: "\f1c3" }
+.codicon-save-as:before { content: "\f1c4" }
+.codicon-save:before { content: "\f1c5" }
+.codicon-screen-full:before { content: "\f1c6" }
+.codicon-screen-normal:before { content: "\f1c7" }
+.codicon-search-stop:before { content: "\f1c8" }
+.codicon-selection:before { content: "\f1c9" }
+.codicon-server:before { content: "\f1ca" }
+.codicon-settings:before { content: "\f1cb" }
+.codicon-shield:before { content: "\f1cc" }
+.codicon-smiley:before { content: "\f1cd" }
+.codicon-snippet:before { content: "\f1ce" }
+.codicon-sort-precedence:before { content: "\f1cf" }
+.codicon-split-horizontal:before { content: "\f1d0" }
+.codicon-split-vertical:before { content: "\f1d1" }
+.codicon-squirrel:before { content: "\f1d2" }
+.codicon-star-empty:before { content: "\f1d3" }
+.codicon-star-full:before { content: "\f1d4" }
+.codicon-star-half:before { content: "\f1d5" }
+.codicon-start:before { content: "\f1d6" }
+.codicon-step-into:before { content: "\f1d7" }
+.codicon-step-out:before { content: "\f1d8" }
+.codicon-step-over:before { content: "\f1d9" }
+.codicon-string:before { content: "\f1da" }
+.codicon-structure:before { content: "\f1db" }
+.codicon-tasklist:before { content: "\f1dc" }
+.codicon-telescope:before { content: "\f1dd" }
+.codicon-text-size:before { content: "\f1de" }
+.codicon-three-bars:before { content: "\f1df" }
+.codicon-thumbsdown:before { content: "\f1e0" }
+.codicon-thumbsup:before { content: "\f1e1" }
+.codicon-tools:before { content: "\f1e2" }
+.codicon-trash:before { content: "\f1e3" }
+.codicon-triangle-down:before { content: "\f1e4" }
+.codicon-triangle-left:before { content: "\f1e5" }
+.codicon-triangle-right:before { content: "\f1e6" }
+.codicon-triangle-up:before { content: "\f1e7" }
+.codicon-twitter:before { content: "\f1e8" }
+.codicon-unfold:before { content: "\f1e9" }
+.codicon-unlock:before { content: "\f1ea" }
+.codicon-unmute:before { content: "\f1eb" }
+.codicon-unverified:before { content: "\f1ec" }
+.codicon-variable:before { content: "\f1ed" }
+.codicon-verified:before { content: "\f1ee" }
+.codicon-versions:before { content: "\f1ef" }
+.codicon-vm-active:before { content: "\f1f0" }
+.codicon-vm-outline:before { content: "\f1f1" }
+.codicon-vm-running:before { content: "\f1f2" }
+.codicon-watch:before { content: "\f1f3" }
+.codicon-whitespace:before { content: "\f1f4" }
+.codicon-whole-word:before { content: "\f1f5" }
+.codicon-window:before { content: "\f1f6" }
+.codicon-word-wrap:before { content: "\f1f7" }
+.codicon-zoom-in:before { content: "\f1f8" }
+.codicon-zoom-out:before { content: "\f1f9" }
diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf
new file mode 100644
index 00000000000..688b9ffd0bd
Binary files /dev/null and b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf differ
diff --git a/src/vs/base/browser/ui/codiconLabel/codiconLabel.mock.ts b/src/vs/base/browser/ui/codiconLabel/codiconLabel.mock.ts
new file mode 100644
index 00000000000..37862376c2c
--- /dev/null
+++ b/src/vs/base/browser/ui/codiconLabel/codiconLabel.mock.ts
@@ -0,0 +1,24 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { escape } from 'vs/base/common/strings';
+
+export function renderCodicons(text: string): string {
+ return escape(text);
+}
+
+export class CodiconLabel {
+
+ private _container: HTMLElement;
+
+ constructor(container: HTMLElement) {
+ this._container = container;
+ }
+
+ set text(text: string) {
+ this._container.innerHTML = renderCodicons(text || '');
+ }
+
+}
diff --git a/src/vs/base/browser/ui/codiconLabel/codiconLabel.ts b/src/vs/base/browser/ui/codiconLabel/codiconLabel.ts
new file mode 100644
index 00000000000..42d38948e88
--- /dev/null
+++ b/src/vs/base/browser/ui/codiconLabel/codiconLabel.ts
@@ -0,0 +1,33 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import 'vs/css!./codicon/codicon';
+import 'vs/css!./codicon/codicon-animations';
+import { escape } from 'vs/base/common/strings';
+
+function expand(text: string): string {
+ return text.replace(/\$\(((.+?)(~(.*?))?)\)/g, (_match, _g1, name, _g3, animation) => {
+ return ``;
+ });
+}
+
+export function renderCodicons(label: string): string {
+ return expand(escape(label));
+}
+
+export class CodiconLabel {
+
+ constructor(
+ private readonly _container: HTMLElement
+ ) { }
+
+ set text(text: string) {
+ this._container.innerHTML = renderCodicons(text || '');
+ }
+
+ set title(title: string) {
+ this._container.title = title;
+ }
+}
diff --git a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts
index 63c805b4237..e7dcf431b0d 100644
--- a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts
+++ b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as objects from 'vs/base/common/objects';
-import { renderOcticons } from 'vs/base/browser/ui/octiconLabel/octiconLabel';
+import { renderCodicons } from 'vs/base/browser/ui/codiconLabel/codiconLabel';
import { escape } from 'vs/base/common/strings';
export interface IHighlight {
@@ -65,13 +65,13 @@ export class HighlightedLabel {
if (pos < highlight.start) {
htmlContent += '';
const substring = this.text.substring(pos, highlight.start);
- htmlContent += this.supportOcticons ? renderOcticons(substring) : escape(substring);
+ htmlContent += this.supportOcticons ? renderCodicons(substring) : escape(substring);
htmlContent += '';
pos = highlight.end;
}
htmlContent += '';
const substring = this.text.substring(highlight.start, highlight.end);
- htmlContent += this.supportOcticons ? renderOcticons(substring) : escape(substring);
+ htmlContent += this.supportOcticons ? renderCodicons(substring) : escape(substring);
htmlContent += '';
pos = highlight.end;
}
@@ -79,7 +79,7 @@ export class HighlightedLabel {
if (pos < this.text.length) {
htmlContent += '';
const substring = this.text.substring(pos);
- htmlContent += this.supportOcticons ? renderOcticons(substring) : escape(substring);
+ htmlContent += this.supportOcticons ? renderCodicons(substring) : escape(substring);
htmlContent += '';
}
diff --git a/src/vs/base/browser/ui/inputbox/inputBox.css b/src/vs/base/browser/ui/inputbox/inputBox.css
index ae4cd429a07..939d0cb5f01 100644
--- a/src/vs/base/browser/ui/inputbox/inputBox.css
+++ b/src/vs/base/browser/ui/inputbox/inputBox.css
@@ -123,7 +123,7 @@
margin-left: 2px;
}
-.monaco-inputbox .monaco-action-bar .action-item .icon {
+.monaco-inputbox .monaco-action-bar .action-item .codicon {
background-repeat: no-repeat;
width: 16px;
height: 16px;
diff --git a/src/vs/base/browser/ui/splitview/panelview.css b/src/vs/base/browser/ui/splitview/panelview.css
index d4def58461f..194ef683029 100644
--- a/src/vs/base/browser/ui/splitview/panelview.css
+++ b/src/vs/base/browser/ui/splitview/panelview.css
@@ -68,7 +68,8 @@
}
/* TODO: actions should be part of the panel, but they aren't yet */
-.monaco-panel-view .panel > .panel-header > .actions .action-label.icon {
+.monaco-panel-view .panel > .panel-header > .actions .action-label.icon,
+.monaco-panel-view .panel > .panel-header > .actions .action-label.codicon {
width: 28px;
height: 22px;
background-size: 16px;
diff --git a/src/vs/base/parts/ipc/common/ipc.ts b/src/vs/base/parts/ipc/common/ipc.ts
index e0205092202..392cfeaeae7 100644
--- a/src/vs/base/parts/ipc/common/ipc.ts
+++ b/src/vs/base/parts/ipc/common/ipc.ts
@@ -32,7 +32,6 @@ export interface IServerChannel {
listen(ctx: TContext, event: string, arg?: any): Event;
}
-
export const enum RequestType {
Promise = 100,
PromiseCancel = 101,
diff --git a/src/vs/base/parts/tree/browser/collapse-all-dark.svg b/src/vs/base/parts/tree/browser/collapse-all-dark.svg
deleted file mode 100644
index 4862c55dbeb..00000000000
--- a/src/vs/base/parts/tree/browser/collapse-all-dark.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/src/vs/base/parts/tree/browser/collapse-all-hc.svg b/src/vs/base/parts/tree/browser/collapse-all-hc.svg
deleted file mode 100644
index 05f920b29b6..00000000000
--- a/src/vs/base/parts/tree/browser/collapse-all-hc.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/src/vs/base/parts/tree/browser/collapse-all-light.svg b/src/vs/base/parts/tree/browser/collapse-all-light.svg
deleted file mode 100644
index 6359b42e623..00000000000
--- a/src/vs/base/parts/tree/browser/collapse-all-light.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/src/vs/base/parts/tree/browser/tree.css b/src/vs/base/parts/tree/browser/tree.css
index 2a3cb47ddbb..6eb5f9b7aff 100644
--- a/src/vs/base/parts/tree/browser/tree.css
+++ b/src/vs/base/parts/tree/browser/tree.css
@@ -110,15 +110,3 @@
.hc-black .monaco-tree .monaco-tree-rows > .monaco-tree-row.has-children.loading > .content:before {
background-image: url('loading-hc.svg');
}
-
-.monaco-tree-action.collapse-all {
- background: url('collapse-all-light.svg') center center no-repeat;
-}
-
-.vs-dark .monaco-tree-action.collapse-all {
- background: url('collapse-all-dark.svg') center center no-repeat;
-}
-
-.hc-black .monaco-tree-action.collapse-all {
- background: url('collapse-all-hc.svg') center center no-repeat;
-}
diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts
index a0f146faa19..6d0693d7657 100644
--- a/src/vs/code/electron-browser/issue/issueReporterMain.ts
+++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts
@@ -32,7 +32,7 @@ import { EnvironmentService } from 'vs/platform/environment/node/environmentServ
import { IssueReporterModel, IssueReporterData as IssueReporterModelData } from 'vs/code/electron-browser/issue/issueReporterModel';
import { IssueReporterData, IssueReporterStyles, IssueType, ISettingsSearchIssueReporterData, IssueReporterFeatures, IssueReporterExtensionData } from 'vs/platform/issue/node/issue';
import BaseHtml from 'vs/code/electron-browser/issue/issueReporterPage';
-import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc';
+import { LoggerChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc';
import { ILogService, getLogLevel } from 'vs/platform/log/common/log';
import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel';
import { normalizeGitHubUrl } from 'vs/code/electron-browser/issue/issueReporterUtil';
@@ -300,8 +300,8 @@ export class IssueReporter extends Disposable {
this.environmentService = new EnvironmentService(configuration, configuration.execPath);
const logService = new SpdLogService(`issuereporter${configuration.windowId}`, this.environmentService.logsPath, getLogLevel(this.environmentService));
- const logLevelClient = new LogLevelSetterChannelClient(mainProcessService.getChannel('loglevel'));
- this.logService = new FollowerLogService(logLevelClient, logService);
+ const loggerClient = new LoggerChannelClient(mainProcessService.getChannel('logger'));
+ this.logService = new FollowerLogService(loggerClient, logService);
const sharedProcess = (serviceCollection.get(IWindowsService)).whenSharedProcessReady()
.then(() => connectNet(this.environmentService.sharedIPCHandle, `window:${configuration.windowId}`));
diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts
index 68b3606086f..dac1b8f7377 100644
--- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts
+++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts
@@ -30,7 +30,7 @@ import { IWindowsService, ActiveWindowManager } from 'vs/platform/windows/common
import { WindowsService } from 'vs/platform/windows/electron-browser/windowsService';
import { ipcRenderer } from 'electron';
import { ILogService, LogLevel } from 'vs/platform/log/common/log';
-import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc';
+import { LoggerChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc';
import { LocalizationsService } from 'vs/platform/localizations/node/localizations';
import { ILocalizationsService } from 'vs/platform/localizations/common/localizations';
import { LocalizationsChannel } from 'vs/platform/localizations/node/localizationsIpc';
@@ -101,8 +101,8 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
const environmentService = new EnvironmentService(initData.args, process.execPath);
const mainRouter = new StaticRouter(ctx => ctx === 'main');
- const logLevelClient = new LogLevelSetterChannelClient(server.getChannel('loglevel', mainRouter));
- const logService = new FollowerLogService(logLevelClient, new SpdLogService('sharedprocess', environmentService.logsPath, initData.logLevel));
+ const loggerClient = new LoggerChannelClient(server.getChannel('logger', mainRouter));
+ const logService = new FollowerLogService(loggerClient, new SpdLogService('sharedprocess', environmentService.logsPath, initData.logLevel));
disposables.add(logService);
logService.info('main', JSON.stringify(configuration));
@@ -145,7 +145,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
const services = new ServiceCollection();
const environmentService = accessor.get(IEnvironmentService);
const { appRoot, extensionsPath, extensionDevelopmentLocationURI: extensionDevelopmentLocationURI, isBuilt, installSourcePath } = environmentService;
- const telemetryLogService = new FollowerLogService(logLevelClient, new SpdLogService('telemetry', environmentService.logsPath, initData.logLevel));
+ const telemetryLogService = new FollowerLogService(loggerClient, new SpdLogService('telemetry', environmentService.logsPath, initData.logLevel));
telemetryLogService.info('The below are logs for every telemetry event sent from VS Code once the log level is set to trace.');
telemetryLogService.info('===========================================================');
diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts
index 4e2f8842a9e..e7eac8d5f3d 100644
--- a/src/vs/code/electron-main/app.ts
+++ b/src/vs/code/electron-main/app.ts
@@ -33,6 +33,7 @@ import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc
import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService';
import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties';
import { getDelayedChannel, StaticRouter } from 'vs/base/parts/ipc/common/ipc';
+import { SimpleServiceProxyChannel } from 'vs/platform/ipc/node/simpleIpcProxy';
import product from 'vs/platform/product/common/product';
import { ProxyAuthHandler } from 'vs/code/electron-main/auth';
import { Disposable } from 'vs/base/common/lifecycle';
@@ -45,9 +46,8 @@ import { Win32UpdateService } from 'vs/platform/update/electron-main/updateServi
import { LinuxUpdateService } from 'vs/platform/update/electron-main/updateService.linux';
import { DarwinUpdateService } from 'vs/platform/update/electron-main/updateService.darwin';
import { IIssueService } from 'vs/platform/issue/node/issue';
-import { IssueChannel } from 'vs/platform/issue/electron-main/issueIpc';
import { IssueMainService } from 'vs/platform/issue/electron-main/issueMainService';
-import { LogLevelSetterChannel } from 'vs/platform/log/common/logIpc';
+import { LoggerChannel } from 'vs/platform/log/common/logIpc';
import { setUnexpectedErrorHandler, onUnexpectedError } from 'vs/base/common/errors';
import { ElectronURLListener } from 'vs/platform/url/electron-main/electronUrlListener';
import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver';
@@ -77,7 +77,7 @@ import { IFileService } from 'vs/platform/files/common/files';
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
import { ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc';
import { IElectronService } from 'vs/platform/electron/node/electron';
-import { ElectronMainService, ElectronChannel } from 'vs/platform/electron/electron-main/electronMainService';
+import { ElectronMainService } from 'vs/platform/electron/electron-main/electronMainService';
export class CodeApplication extends Disposable {
@@ -92,7 +92,7 @@ export class CodeApplication extends Disposable {
@IInstantiationService private readonly instantiationService: IInstantiationService,
@ILogService private readonly logService: ILogService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
- @ILifecycleMainService private readonly lifecycleService: ILifecycleMainService,
+ @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IStateService private readonly stateService: IStateService
) {
@@ -109,7 +109,7 @@ export class CodeApplication extends Disposable {
process.on('unhandledRejection', (reason: unknown) => onUnexpectedError(reason));
// Dispose on shutdown
- this.lifecycleService.onWillShutdown(() => this.dispose());
+ this.lifecycleMainService.onWillShutdown(() => this.dispose());
// Contextmenu via IPC support
registerContextMenuListener();
@@ -255,7 +255,7 @@ export class CodeApplication extends Disposable {
this.logService.trace('IPC#vscode:exit', code);
this.dispose();
- this.lifecycleService.kill(code);
+ this.lifecycleMainService.kill(code);
});
ipc.on('vscode:fetchShellEnv', async (event: Electron.IpcMainEvent) => {
@@ -282,7 +282,7 @@ export class CodeApplication extends Disposable {
// Some listeners after window opened
(async () => {
- await this.lifecycleService.when(LifecycleMainPhase.AfterWindowOpen);
+ await this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen);
// After waking up from sleep (after window opened)
powerMonitor.on('resume', () => {
@@ -361,7 +361,7 @@ export class CodeApplication extends Disposable {
// Spawn shared process after the first window has opened and 3s have passed
const sharedProcess = this.instantiationService.createInstance(SharedProcess, machineId, this.userEnv);
const sharedProcessClient = sharedProcess.whenReady().then(() => connect(this.environmentService.sharedIPCHandle, 'main'));
- this.lifecycleService.when(LifecycleMainPhase.AfterWindowOpen).then(() => {
+ this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => {
this._register(new RunOnceScheduler(async () => {
const userEnv = await getShellEnvironment(this.logService, this.environmentService);
@@ -461,7 +461,7 @@ export class CodeApplication extends Disposable {
const storageMainService = new StorageMainService(this.logService, this.environmentService);
services.set(IStorageMainService, storageMainService);
- this.lifecycleService.onWillShutdown(e => e.join(storageMainService.close()));
+ this.lifecycleMainService.onWillShutdown(e => e.join(storageMainService.close()));
const backupMainService = new BackupMainService(this.environmentService, this.configurationService, this.logService);
services.set(IBackupMainService, backupMainService);
@@ -529,8 +529,8 @@ export class CodeApplication extends Disposable {
private openFirstWindow(accessor: ServicesAccessor, electronIpcServer: ElectronIPCServer, sharedProcessClient: Promise>): ICodeWindow[] {
// Register more Main IPC services
- const launchService = accessor.get(ILaunchMainService);
- const launchChannel = new LaunchChannel(launchService);
+ const launchMainService = accessor.get(ILaunchMainService);
+ const launchChannel = new LaunchChannel(launchMainService);
this.mainIpcServer.registerChannel('launch', launchChannel);
// Register more Electron IPC services
@@ -539,15 +539,15 @@ export class CodeApplication extends Disposable {
electronIpcServer.registerChannel('update', updateChannel);
const issueService = accessor.get(IIssueService);
- const issueChannel = new IssueChannel(issueService);
+ const issueChannel = new SimpleServiceProxyChannel(issueService);
electronIpcServer.registerChannel('issue', issueChannel);
const electronService = accessor.get(IElectronService);
- const electronChannel = new ElectronChannel(electronService);
+ const electronChannel = new SimpleServiceProxyChannel(electronService);
electronIpcServer.registerChannel('electron', electronChannel);
- const workspacesService = accessor.get(IWorkspacesMainService);
- const workspacesChannel = new WorkspacesChannel(workspacesService);
+ const workspacesMainService = accessor.get(IWorkspacesMainService);
+ const workspacesChannel = new WorkspacesChannel(workspacesMainService);
electronIpcServer.registerChannel('workspaces', workspacesChannel);
const windowsService = accessor.get(IWindowsService);
@@ -567,15 +567,15 @@ export class CodeApplication extends Disposable {
const storageChannel = this._register(new GlobalStorageDatabaseChannel(this.logService, storageMainService));
electronIpcServer.registerChannel('storage', storageChannel);
- const logLevelChannel = new LogLevelSetterChannel(accessor.get(ILogService));
- electronIpcServer.registerChannel('loglevel', logLevelChannel);
- sharedProcessClient.then(client => client.registerChannel('loglevel', logLevelChannel));
+ const loggerChannel = new LoggerChannel(accessor.get(ILogService));
+ electronIpcServer.registerChannel('logger', loggerChannel);
+ sharedProcessClient.then(client => client.registerChannel('logger', loggerChannel));
// ExtensionHost Debug broadcast service
electronIpcServer.registerChannel(ExtensionHostDebugBroadcastChannel.ChannelName, new ExtensionHostDebugBroadcastChannel());
// Signal phase: ready (services set)
- this.lifecycleService.phase = LifecycleMainPhase.Ready;
+ this.lifecycleMainService.phase = LifecycleMainPhase.Ready;
// Propagate to clients
const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService);
@@ -685,7 +685,7 @@ export class CodeApplication extends Disposable {
private afterWindowOpen(): void {
// Signal phase: after window open
- this.lifecycleService.phase = LifecycleMainPhase.AfterWindowOpen;
+ this.lifecycleMainService.phase = LifecycleMainPhase.AfterWindowOpen;
// Remote Authorities
this.handleRemoteAuthorities();
diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts
index f2d5cf7b88d..534cfddbe4c 100644
--- a/src/vs/code/electron-main/main.ts
+++ b/src/vs/code/electron-main/main.ts
@@ -116,13 +116,13 @@ class CodeMain {
await instantiationService.invokeFunction(async accessor => {
const environmentService = accessor.get(IEnvironmentService);
const logService = accessor.get(ILogService);
- const lifecycleService = accessor.get(ILifecycleMainService);
+ const lifecycleMainService = accessor.get(ILifecycleMainService);
const configurationService = accessor.get(IConfigurationService);
- const mainIpcServer = await this.doStartup(logService, environmentService, lifecycleService, instantiationService, true);
+ const mainIpcServer = await this.doStartup(logService, environmentService, lifecycleMainService, instantiationService, true);
bufferLogService.logger = new SpdLogService('main', environmentService.logsPath, bufferLogService.getLevel());
- once(lifecycleService.onWillShutdown)(() => (configurationService as ConfigurationService).dispose());
+ once(lifecycleMainService.onWillShutdown)(() => (configurationService as ConfigurationService).dispose());
return instantiationService.createInstance(CodeApplication, mainIpcServer, instanceEnvironment).startup();
});
@@ -189,7 +189,7 @@ class CodeMain {
return instanceEnvironment;
}
- private async doStartup(logService: ILogService, environmentService: IEnvironmentService, lifecycleService: ILifecycleMainService, instantiationService: IInstantiationService, retry: boolean): Promise {
+ private async doStartup(logService: ILogService, environmentService: IEnvironmentService, lifecycleMainService: ILifecycleMainService, instantiationService: IInstantiationService, retry: boolean): Promise {
// Try to setup a server for running. If that succeeds it means
// we are the first instance to startup. Otherwise it is likely
@@ -197,7 +197,7 @@ class CodeMain {
let server: Server;
try {
server = await serve(environmentService.mainIPCHandle);
- once(lifecycleService.onWillShutdown)(() => server.dispose());
+ once(lifecycleMainService.onWillShutdown)(() => server.dispose());
} catch (error) {
// Handle unexpected errors (the only expected error is EADDRINUSE that
@@ -245,7 +245,7 @@ class CodeMain {
throw error;
}
- return this.doStartup(logService, environmentService, lifecycleService, instantiationService, false);
+ return this.doStartup(logService, environmentService, lifecycleMainService, instantiationService, false);
}
// Tests from CLI require to be the only instance currently
@@ -374,7 +374,7 @@ class CodeMain {
private quit(accessor: ServicesAccessor, reason?: ExpectedError | Error): void {
const logService = accessor.get(ILogService);
- const lifecycleService = accessor.get(ILifecycleMainService);
+ const lifecycleMainService = accessor.get(ILifecycleMainService);
let exitCode = 0;
@@ -394,7 +394,7 @@ class CodeMain {
}
}
- lifecycleService.kill(exitCode);
+ lifecycleMainService.kill(exitCode);
}
}
diff --git a/src/vs/code/electron-main/sharedProcess.ts b/src/vs/code/electron-main/sharedProcess.ts
index 29ef3a38ddc..b59ecadb78f 100644
--- a/src/vs/code/electron-main/sharedProcess.ts
+++ b/src/vs/code/electron-main/sharedProcess.ts
@@ -24,7 +24,7 @@ export class SharedProcess implements ISharedProcess {
private readonly machineId: string,
private userEnv: NodeJS.ProcessEnv,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
- @ILifecycleMainService private readonly lifecycleService: ILifecycleMainService,
+ @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
@ILogService private readonly logService: ILogService,
@IThemeMainService private readonly themeMainService: IThemeMainService
) { }
@@ -68,7 +68,7 @@ export class SharedProcess implements ISharedProcess {
const disposables = new DisposableStore();
- this.lifecycleService.onWillShutdown(() => {
+ this.lifecycleMainService.onWillShutdown(() => {
disposables.dispose();
// Shut the shared process down when we are quitting
diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts
index d5ba7ef2ae2..3bb019cd961 100644
--- a/src/vs/code/electron-main/windows.ts
+++ b/src/vs/code/electron-main/windows.ts
@@ -188,7 +188,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
@ILogService private readonly logService: ILogService,
@IStateService private readonly stateService: IStateService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
- @ILifecycleMainService private readonly lifecycleService: ILifecycleMainService,
+ @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
@IBackupMainService private readonly backupMainService: IBackupMainService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@@ -207,8 +207,8 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
this.dialogs = new Dialogs(stateService, this);
this.workspacesManager = new WorkspacesManager(workspacesMainService, backupMainService, this);
- this.lifecycleService.when(LifecycleMainPhase.Ready).then(() => this.registerListeners());
- this.lifecycleService.when(LifecycleMainPhase.AfterWindowOpen).then(() => this.installWindowsMutex());
+ this.lifecycleMainService.when(LifecycleMainPhase.Ready).then(() => this.registerListeners());
+ this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => this.installWindowsMutex());
}
private installWindowsMutex(): void {
@@ -217,7 +217,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
try {
const WindowsMutex = (require.__$__nodeRequire('windows-mutex') as typeof import('windows-mutex')).Mutex;
const mutex = new WindowsMutex(win32MutexName);
- once(this.lifecycleService.onWillShutdown)(() => mutex.release());
+ once(this.lifecycleMainService.onWillShutdown)(() => mutex.release());
} catch (e) {
this.logService.error(e);
}
@@ -251,8 +251,8 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
}
// Handle various lifecycle events around windows
- this.lifecycleService.onBeforeWindowClose(window => this.onBeforeWindowClose(window));
- this.lifecycleService.onBeforeShutdown(() => this.onBeforeShutdown());
+ this.lifecycleMainService.onBeforeWindowClose(window => this.onBeforeWindowClose(window));
+ this.lifecycleMainService.onBeforeShutdown(() => this.onBeforeShutdown());
this.onWindowsCountChanged(e => {
if (e.newCount - e.oldCount > 0) {
// clear last closed window state when a new window opens. this helps on macOS where
@@ -340,7 +340,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
// See note on #onBeforeShutdown() for details how these events are flowing
private onBeforeWindowClose(win: ICodeWindow): void {
- if (this.lifecycleService.quitRequested) {
+ if (this.lifecycleMainService.quitRequested) {
return; // during quit, many windows close in parallel so let it be handled in the before-quit handler
}
@@ -985,7 +985,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
private getRestoreWindowsSetting(): RestoreWindowsSetting {
let restoreWindows: RestoreWindowsSetting;
- if (this.lifecycleService.wasRestarted) {
+ if (this.lifecycleMainService.wasRestarted) {
restoreWindows = 'all'; // always reopen all windows when an update was applied
} else {
const windowConfig = this.configurationService.getValue('window');
@@ -1328,7 +1328,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
// Window state is from a previous session: only allow fullscreen when we got updated or user wants to restore
else {
- allowFullscreen = this.lifecycleService.wasRestarted || (windowConfig && windowConfig.restoreFullscreen);
+ allowFullscreen = this.lifecycleMainService.wasRestarted || (windowConfig && windowConfig.restoreFullscreen);
}
if (state.mode === WindowMode.Fullscreen && !allowFullscreen) {
@@ -1364,7 +1364,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
window.win.on('closed', () => this.onWindowClosed(window!));
// Lifecycle
- (this.lifecycleService as LifecycleMainService).registerWindow(window);
+ (this.lifecycleMainService as LifecycleMainService).registerWindow(window);
}
// Existing window
@@ -1387,7 +1387,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
// first and only load the new configuration if that was
// not vetoed
if (window.isReady) {
- this.lifecycleService.unload(window, UnloadReason.LOAD).then(veto => {
+ this.lifecycleMainService.unload(window, UnloadReason.LOAD).then(veto => {
if (!veto) {
this.doOpenInBrowserWindow(window!, configuration, options);
}
@@ -1554,7 +1554,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
async reload(win: ICodeWindow, cli?: ParsedArgs): Promise {
// Only reload when the window has not vetoed this
- const veto = await this.lifecycleService.unload(win, UnloadReason.RELOAD);
+ const veto = await this.lifecycleMainService.unload(win, UnloadReason.RELOAD);
if (!veto) {
win.reload(undefined, cli);
}
@@ -1866,7 +1866,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
// Otherwise: normal quit
else {
setTimeout(() => {
- this.lifecycleService.quit();
+ this.lifecycleMainService.quit();
}, 10 /* delay to unwind callback stack (IPC) */);
}
}
diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts
index 84997b65040..6dc1c356bac 100644
--- a/src/vs/code/node/cli.ts
+++ b/src/vs/code/node/cli.ts
@@ -15,7 +15,7 @@ import { whenDeleted, writeFileSync } from 'vs/base/node/pfs';
import { findFreePort, randomPort } from 'vs/base/node/ports';
import { resolveTerminalEncoding } from 'vs/base/node/encoding';
import * as iconv from 'iconv-lite';
-import { isWindows } from 'vs/base/common/platform';
+import { isWindows, isLinux } from 'vs/base/common/platform';
import { ProfilingSession, Target } from 'v8-inspect-profiler';
import { isString } from 'vs/base/common/types';
@@ -360,6 +360,10 @@ export async function main(argv: string[]): Promise {
options['stdio'] = 'ignore';
}
+ if (isLinux) {
+ addArg(argv, '--no-sandbox'); // Electron 6 introduces a chrome-sandbox that requires root to run. This can fail. Disable sandbox via --no-sandbox
+ }
+
const child = spawn(process.execPath, argv.slice(2), options);
if (args.wait && waitMarkerFilePath) {
diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts
index c42f82fdb26..8a122c18fc5 100644
--- a/src/vs/editor/browser/services/openerService.ts
+++ b/src/vs/editor/browser/services/openerService.ts
@@ -13,7 +13,7 @@ import { equalsIgnoreCase } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
-import { IOpener, IOpenerService, IValidator } from 'vs/platform/opener/common/opener';
+import { IOpener, IOpenerService, IValidator, IExternalUriResolver } from 'vs/platform/opener/common/opener';
export class OpenerService extends Disposable implements IOpenerService {
@@ -21,6 +21,7 @@ export class OpenerService extends Disposable implements IOpenerService {
private readonly _openers = new LinkedList();
private readonly _validators = new LinkedList();
+ private readonly _resolvers = new LinkedList();
constructor(
@ICodeEditorService private readonly _editorService: ICodeEditorService,
@@ -39,6 +40,11 @@ export class OpenerService extends Disposable implements IOpenerService {
return { dispose: remove };
}
+ registerExternalUriResolver(resolver: IExternalUriResolver): IDisposable {
+ const remove = this._resolvers.push(resolver);
+ return { dispose: remove };
+ }
+
async open(resource: URI, options?: { openToSide?: boolean, openExternal?: boolean }): Promise {
// no scheme ?!?
if (!resource.scheme) {
@@ -118,7 +124,11 @@ export class OpenerService extends Disposable implements IOpenerService {
}
}
- private _doOpenExternal(resource: URI): Promise {
+ private async _doOpenExternal(resource: URI): Promise {
+ for (const resolver of this._resolvers.toArray()) {
+ resource = await resolver.resolveExternalUri(resource);
+ }
+
dom.windowOpenNoOpener(encodeURI(resource.toString(true)));
return Promise.resolve(true);
diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts
index 7a409b98a5e..c7bcd9b066c 100644
--- a/src/vs/platform/actions/common/actions.ts
+++ b/src/vs/platform/actions/common/actions.ts
@@ -93,6 +93,7 @@ export const enum MenuId {
SearchContext,
StatusBarWindowIndicatorMenu,
TouchBarContext,
+ TitleBarContext,
ViewItemContext,
ViewTitle,
CommentThreadTitle,
diff --git a/src/vs/platform/debug/common/extensionHostDebug.ts b/src/vs/platform/debug/common/extensionHostDebug.ts
index 3386e62b25d..3ccb12051b7 100644
--- a/src/vs/platform/debug/common/extensionHostDebug.ts
+++ b/src/vs/platform/debug/common/extensionHostDebug.ts
@@ -6,6 +6,8 @@
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Event } from 'vs/base/common/event';
import { IRemoteConsoleLog } from 'vs/base/common/console';
+import { ParsedArgs } from 'vs/platform/environment/common/environment';
+import { IProcessEnvironment } from 'vs/base/common/platform';
export const IExtensionHostDebugService = createDecorator('extensionHostDebugService');
@@ -37,17 +39,19 @@ export interface IExtensionHostDebugService {
_serviceBrand: undefined;
reload(sessionId: string): void;
- onReload: Event;
+ readonly onReload: Event;
close(sessionId: string): void;
- onClose: Event;
+ readonly onClose: Event;
attachSession(sessionId: string, port: number, subId?: string): void;
- onAttachSession: Event;
+ readonly onAttachSession: Event;
logToSession(sessionId: string, log: IRemoteConsoleLog): void;
- onLogToSession: Event;
+ readonly onLogToSession: Event;
terminateSession(sessionId: string, subId?: string): void;
- onTerminateSession: Event;
-}
\ No newline at end of file
+ readonly onTerminateSession: Event;
+
+ openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise;
+}
diff --git a/src/vs/platform/debug/common/extensionHostDebugIpc.ts b/src/vs/platform/debug/common/extensionHostDebugIpc.ts
index 3511be94950..555d90bfde0 100644
--- a/src/vs/platform/debug/common/extensionHostDebugIpc.ts
+++ b/src/vs/platform/debug/common/extensionHostDebugIpc.ts
@@ -8,6 +8,8 @@ import { IReloadSessionEvent, ICloseSessionEvent, IAttachSessionEvent, ILogToSes
import { Event, Emitter } from 'vs/base/common/event';
import { IRemoteConsoleLog } from 'vs/base/common/console';
import { Disposable } from 'vs/base/common/lifecycle';
+import { ParsedArgs } from 'vs/platform/environment/common/environment';
+import { IProcessEnvironment } from 'vs/base/common/platform';
export class ExtensionHostDebugBroadcastChannel implements IServerChannel {
@@ -99,4 +101,10 @@ export class ExtensionHostDebugChannelClient extends Disposable implements IExte
get onTerminateSession(): Event {
return this.channel.listen('terminate');
}
-}
\ No newline at end of file
+
+ openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise {
+ // TODO@Isidor
+ //return this.channel.call('openExtensionDevelopmentHostWindow', [args, env]);
+ return Promise.resolve();
+ }
+}
diff --git a/src/vs/platform/driver/electron-main/driver.ts b/src/vs/platform/driver/electron-main/driver.ts
index 49404125de3..8966f02836c 100644
--- a/src/vs/platform/driver/electron-main/driver.ts
+++ b/src/vs/platform/driver/electron-main/driver.ts
@@ -34,7 +34,7 @@ export class Driver implements IDriver, IWindowDriverRegistry {
constructor(
private windowServer: IPCServer,
private options: IDriverOptions,
- @IWindowsMainService private readonly windowsService: IWindowsMainService
+ @IWindowsMainService private readonly windowsMainService: IWindowsMainService
) { }
async registerWindowDriver(windowId: number): Promise {
@@ -49,7 +49,7 @@ export class Driver implements IDriver, IWindowDriverRegistry {
}
async getWindowIds(): Promise {
- return this.windowsService.getWindows()
+ return this.windowsMainService.getWindows()
.map(w => w.id)
.filter(id => this.registeredWindowIds.has(id) && !this.reloadingWindowIds.has(id));
}
@@ -57,7 +57,7 @@ export class Driver implements IDriver, IWindowDriverRegistry {
async capturePage(windowId: number): Promise {
await this.whenUnfrozen(windowId);
- const window = this.windowsService.getWindowById(windowId);
+ const window = this.windowsMainService.getWindowById(windowId);
if (!window) {
throw new Error('Invalid window');
}
@@ -69,16 +69,16 @@ export class Driver implements IDriver, IWindowDriverRegistry {
async reloadWindow(windowId: number): Promise {
await this.whenUnfrozen(windowId);
- const window = this.windowsService.getWindowById(windowId);
+ const window = this.windowsMainService.getWindowById(windowId);
if (!window) {
throw new Error('Invalid window');
}
this.reloadingWindowIds.add(windowId);
- this.windowsService.reload(window);
+ this.windowsMainService.reload(window);
}
async exitApplication(): Promise {
- return this.windowsService.quit();
+ return this.windowsMainService.quit();
}
async dispatchKeybinding(windowId: number, keybinding: string): Promise {
@@ -96,7 +96,7 @@ export class Driver implements IDriver, IWindowDriverRegistry {
throw new Error('ScanCodeBindings not supported');
}
- const window = this.windowsService.getWindowById(windowId);
+ const window = this.windowsMainService.getWindowById(windowId);
if (!window) {
throw new Error('Invalid window');
}
diff --git a/src/vs/platform/electron/electron-browser/electronService.ts b/src/vs/platform/electron/electron-browser/electronService.ts
index fa2b5f09b18..0a88c90efc5 100644
--- a/src/vs/platform/electron/electron-browser/electronService.ts
+++ b/src/vs/platform/electron/electron-browser/electronService.ts
@@ -3,26 +3,15 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
+import { IElectronService } from 'vs/platform/electron/node/electron';
import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
+import { createSimpleChannelProxy } from 'vs/platform/ipc/node/simpleIpcProxy';
export class ElectronService {
_serviceBrand: undefined;
constructor(@IMainProcessService mainProcessService: IMainProcessService) {
- const channel = mainProcessService.getChannel('electron');
-
- // Proxy: forward any property access to the channel
- return new Proxy({}, {
- get(_target, propKey, _receiver) {
- if (typeof propKey === 'string') {
- return function (...args: any[]) {
- return channel.call(propKey, ...args);
- };
- }
-
- throw new Error(`Not Implemented in ElectronService: ${String(propKey)}`);
- }
- }) as ElectronService;
+ return createSimpleChannelProxy(mainProcessService.getChannel('electron'));
}
}
diff --git a/src/vs/platform/electron/electron-main/electronMainService.ts b/src/vs/platform/electron/electron-main/electronMainService.ts
index a8c92d2414b..d51d4b65d3e 100644
--- a/src/vs/platform/electron/electron-main/electronMainService.ts
+++ b/src/vs/platform/electron/electron-main/electronMainService.ts
@@ -5,9 +5,7 @@
import { IElectronService } from 'vs/platform/electron/node/electron';
import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows';
-import { MessageBoxOptions, MessageBoxReturnValue } from 'electron';
-import { IServerChannel } from 'vs/base/parts/ipc/common/ipc';
-import { Event } from 'vs/base/common/event';
+import { MessageBoxOptions, MessageBoxReturnValue, shell } from 'electron';
export class ElectronMainService implements IElectronService {
@@ -18,10 +16,20 @@ export class ElectronMainService implements IElectronService {
) {
}
+ //#region Window
+
private get window(): ICodeWindow | undefined {
return this.windowsMainService.getFocusedWindow() || this.windowsMainService.getLastActiveWindow();
}
+ async windowCount(): Promise {
+ return this.windowsMainService.getWindowCount();
+ }
+
+ //#endregion
+
+ //#region Other
+
async showMessageBox(options: MessageBoxOptions): Promise {
const result = await this.windowsMainService.showMessageBox(options, this.window);
@@ -30,30 +38,10 @@ export class ElectronMainService implements IElectronService {
checkboxChecked: !!result.checkboxChecked
};
}
-}
-
-export class ElectronChannel implements IServerChannel {
-
- private service: { [key: string]: unknown };
-
- constructor(service: IElectronService) {
- this.service = service as unknown as { [key: string]: unknown };
- }
-
- listen(_: unknown, event: string): Event {
- throw new Error(`Event not found: ${event}`);
- }
-
- call(_: unknown, command: string, arg?: any): Promise {
- const target = this.service[command];
- if (typeof target === 'function') {
- if (Array.isArray(arg)) {
- return target.apply(this.service, arg);
- }
-
- return target.call(this.service, arg);
- }
-
- throw new Error(`Call Not Found in ElectronService: ${command}`);
- }
+
+ async showItemInFolder(path: string): Promise {
+ shell.showItemInFolder(path);
+ }
+
+ //#endregion
}
diff --git a/src/vs/platform/electron/node/electron.ts b/src/vs/platform/electron/node/electron.ts
index 0f6c3a83091..8257bf0eee2 100644
--- a/src/vs/platform/electron/node/electron.ts
+++ b/src/vs/platform/electron/node/electron.ts
@@ -12,6 +12,12 @@ export interface IElectronService {
_serviceBrand: undefined;
+ // Window
+ windowCount(): Promise;
+
// Dialogs
showMessageBox(options: MessageBoxOptions): Promise;
+
+ // OS
+ showItemInFolder(path: string): Promise;
}
diff --git a/src/vs/platform/history/electron-main/historyMainService.ts b/src/vs/platform/history/electron-main/historyMainService.ts
index b67c268b997..a76d56fcc21 100644
--- a/src/vs/platform/history/electron-main/historyMainService.ts
+++ b/src/vs/platform/history/electron-main/historyMainService.ts
@@ -29,6 +29,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
export const IHistoryMainService = createDecorator('historyMainService');
export interface IHistoryMainService {
+
_serviceBrand: undefined;
onRecentlyOpenedChange: CommonEvent;
@@ -68,11 +69,11 @@ export class HistoryMainService implements IHistoryMainService {
@ILogService private readonly logService: ILogService,
@IWorkspacesMainService private readonly workspacesMainService: IWorkspacesMainService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
- @ILifecycleMainService lifecycleService: ILifecycleMainService
+ @ILifecycleMainService lifecycleMainService: ILifecycleMainService
) {
this.macOSRecentDocumentsUpdater = new ThrottledDelayer(800);
- lifecycleService.when(LifecycleMainPhase.AfterWindowOpen).then(() => this.handleWindowsJumpList());
+ lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => this.handleWindowsJumpList());
}
private handleWindowsJumpList(): void {
diff --git a/src/vs/platform/ipc/node/simpleIpcProxy.ts b/src/vs/platform/ipc/node/simpleIpcProxy.ts
new file mode 100644
index 00000000000..43e4cdbc31f
--- /dev/null
+++ b/src/vs/platform/ipc/node/simpleIpcProxy.ts
@@ -0,0 +1,48 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { Event } from 'vs/base/common/event';
+import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
+
+//
+// Use both `SimpleServiceProxyChannel` and `createSimpleChannelProxy`
+// for a very basic process <=> process communication over methods.
+//
+
+export class SimpleServiceProxyChannel implements IServerChannel {
+
+ private service: { [key: string]: unknown };
+
+ constructor(service: unknown) {
+ this.service = service as { [key: string]: unknown };
+ }
+
+ listen(_: unknown, event: string): Event {
+ throw new Error(`Events are currently unsupported by SimpleServiceProxyChannel: ${event}`);
+ }
+
+ call(_: unknown, command: string, args: any[]): Promise {
+ const target = this.service[command];
+ if (typeof target === 'function') {
+ return target.apply(this.service, args);
+ }
+
+ throw new Error(`Method not found: ${command}`);
+ }
+}
+
+export function createSimpleChannelProxy(channel: IChannel): T {
+ return new Proxy({}, {
+ get(_target, propKey, _receiver) {
+ if (typeof propKey === 'string') {
+ return function (...args: any[]) {
+ return channel.call(propKey, args);
+ };
+ }
+
+ throw new Error(`Unable to provide main channel proxy implementation for: ${String(propKey)}`);
+ }
+ }) as T;
+}
diff --git a/src/vs/platform/issue/electron-browser/issueService.ts b/src/vs/platform/issue/electron-browser/issueService.ts
index 56068fab19e..72502019070 100644
--- a/src/vs/platform/issue/electron-browser/issueService.ts
+++ b/src/vs/platform/issue/electron-browser/issueService.ts
@@ -3,29 +3,15 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { IChannel } from 'vs/base/parts/ipc/common/ipc';
-import { IIssueService, IssueReporterData, ProcessExplorerData } from 'vs/platform/issue/node/issue';
+import { IIssueService } from 'vs/platform/issue/node/issue';
import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
+import { createSimpleChannelProxy } from 'vs/platform/ipc/node/simpleIpcProxy';
-export class IssueService implements IIssueService {
+export class IssueService {
_serviceBrand: undefined;
- private channel: IChannel;
-
constructor(@IMainProcessService mainProcessService: IMainProcessService) {
- this.channel = mainProcessService.getChannel('issue');
- }
-
- openReporter(data: IssueReporterData): Promise {
- return this.channel.call('openIssueReporter', data);
- }
-
- openProcessExplorer(data: ProcessExplorerData): Promise {
- return this.channel.call('openProcessExplorer', data);
- }
-
- getSystemStatus(): Promise {
- return this.channel.call('getSystemStatus');
+ return createSimpleChannelProxy(mainProcessService.getChannel('issue'));
}
}
diff --git a/src/vs/platform/issue/electron-main/issueIpc.ts b/src/vs/platform/issue/electron-main/issueIpc.ts
deleted file mode 100644
index 271bcf5ceee..00000000000
--- a/src/vs/platform/issue/electron-main/issueIpc.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import { IServerChannel } from 'vs/base/parts/ipc/common/ipc';
-import { Event } from 'vs/base/common/event';
-import { IIssueService } from 'vs/platform/issue/node/issue';
-
-export class IssueChannel implements IServerChannel {
-
- constructor(private service: IIssueService) { }
-
- listen(_: unknown, event: string): Event {
- throw new Error(`Event not found: ${event}`);
- }
-
- call(_: unknown, command: string, arg?: any): Promise {
- switch (command) {
- case 'openIssueReporter':
- return this.service.openReporter(arg);
- case 'openProcessExplorer':
- return this.service.openProcessExplorer(arg);
- case 'getSystemStatus':
- return this.service.getSystemStatus();
- }
-
- throw new Error(`Call not found: ${command}`);
- }
-}
\ No newline at end of file
diff --git a/src/vs/platform/issue/electron-main/issueMainService.ts b/src/vs/platform/issue/electron-main/issueMainService.ts
index 2278e311a04..55d332040c5 100644
--- a/src/vs/platform/issue/electron-main/issueMainService.ts
+++ b/src/vs/platform/issue/electron-main/issueMainService.ts
@@ -31,7 +31,7 @@ export class IssueMainService implements IIssueService {
private machineId: string,
private userEnv: IProcessEnvironment,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
- @ILaunchMainService private readonly launchService: ILaunchMainService,
+ @ILaunchMainService private readonly launchMainService: ILaunchMainService,
@ILogService private readonly logService: ILogService,
@IDiagnosticsService private readonly diagnosticsService: IDiagnosticsService,
@IWindowsService private readonly windowsService: IWindowsService
@@ -41,7 +41,7 @@ export class IssueMainService implements IIssueService {
private registerListeners(): void {
ipcMain.on('vscode:issueSystemInfoRequest', async (event: Electron.IpcMainEvent) => {
- Promise.all([this.launchService.getMainProcessInfo(), this.launchService.getRemoteDiagnostics({ includeProcesses: false, includeWorkspaceMetadata: false })])
+ Promise.all([this.launchMainService.getMainProcessInfo(), this.launchMainService.getRemoteDiagnostics({ includeProcesses: false, includeWorkspaceMetadata: false })])
.then(result => {
const [info, remoteData] = result;
this.diagnosticsService.getSystemInfo(info, remoteData).then(msg => {
@@ -54,9 +54,9 @@ export class IssueMainService implements IIssueService {
const processes = [];
try {
- const mainPid = await this.launchService.getMainProcessId();
+ const mainPid = await this.launchMainService.getMainProcessId();
processes.push({ name: localize('local', "Local"), rootProcess: await listProcesses(mainPid) });
- (await this.launchService.getRemoteDiagnostics({ includeProcesses: true }))
+ (await this.launchMainService.getRemoteDiagnostics({ includeProcesses: true }))
.forEach(data => {
if (isRemoteDiagnosticError(data)) {
processes.push({
@@ -157,7 +157,7 @@ export class IssueMainService implements IIssueService {
});
ipcMain.on('windowsInfoRequest', (event: Electron.IpcMainEvent) => {
- this.launchService.getMainProcessInfo().then(info => {
+ this.launchMainService.getMainProcessInfo().then(info => {
event.sender.send('vscode:windowsInfoResponse', info.windows);
});
});
@@ -268,7 +268,7 @@ export class IssueMainService implements IIssueService {
}
public async getSystemStatus(): Promise {
- return Promise.all([this.launchService.getMainProcessInfo(), this.launchService.getRemoteDiagnostics({ includeProcesses: false, includeWorkspaceMetadata: false })])
+ return Promise.all([this.launchMainService.getMainProcessInfo(), this.launchMainService.getRemoteDiagnostics({ includeProcesses: false, includeWorkspaceMetadata: false })])
.then(result => {
const [info, remoteData] = result;
return this.diagnosticsService.getDiagnostics(info, remoteData);
@@ -345,7 +345,7 @@ export class IssueMainService implements IIssueService {
private getPerformanceInfo(): Promise {
return new Promise(async (resolve, reject) => {
- Promise.all([this.launchService.getMainProcessInfo(), this.launchService.getRemoteDiagnostics({ includeProcesses: true, includeWorkspaceMetadata: true })])
+ Promise.all([this.launchMainService.getMainProcessInfo(), this.launchMainService.getRemoteDiagnostics({ includeProcesses: true, includeWorkspaceMetadata: true })])
.then(result => {
const [info, remoteData] = result;
this.diagnosticsService.getPerformanceInfo(info, remoteData)
diff --git a/src/vs/platform/log/common/log.ts b/src/vs/platform/log/common/log.ts
index 5e6b7300c49..0009b304e53 100644
--- a/src/vs/platform/log/common/log.ts
+++ b/src/vs/platform/log/common/log.ts
@@ -8,6 +8,7 @@ import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { isWindows } from 'vs/base/common/platform';
import { Event, Emitter } from 'vs/base/common/event';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
+import { LoggerChannelClient } from 'vs/platform/log/common/logIpc';
export const ILogService = createServiceDecorator('logService');
@@ -183,6 +184,54 @@ export class ConsoleLogService extends AbstractLogService implements ILogService
dispose(): void { }
}
+export class ConsoleLogInMainService extends AbstractLogService implements ILogService {
+
+ _serviceBrand: undefined;
+
+ constructor(private readonly client: LoggerChannelClient, logLevel: LogLevel = DEFAULT_LOG_LEVEL) {
+ super();
+ this.setLevel(logLevel);
+ }
+
+ trace(message: string, ...args: any[]): void {
+ if (this.getLevel() <= LogLevel.Trace) {
+ this.client.consoleLog('trace', [message, ...args]);
+ }
+ }
+
+ debug(message: string, ...args: any[]): void {
+ if (this.getLevel() <= LogLevel.Debug) {
+ this.client.consoleLog('debug', [message, ...args]);
+ }
+ }
+
+ info(message: string, ...args: any[]): void {
+ if (this.getLevel() <= LogLevel.Info) {
+ this.client.consoleLog('info', [message, ...args]);
+ }
+ }
+
+ warn(message: string | Error, ...args: any[]): void {
+ if (this.getLevel() <= LogLevel.Warning) {
+ this.client.consoleLog('warn', [message, ...args]);
+ }
+ }
+
+ error(message: string, ...args: any[]): void {
+ if (this.getLevel() <= LogLevel.Error) {
+ this.client.consoleLog('error', [message, ...args]);
+ }
+ }
+
+ critical(message: string, ...args: any[]): void {
+ if (this.getLevel() <= LogLevel.Critical) {
+ this.client.consoleLog('critical', [message, ...args]);
+ }
+ }
+
+ dispose(): void { }
+}
+
export class MultiplexLogService extends AbstractLogService implements ILogService {
_serviceBrand: undefined;
@@ -326,4 +375,4 @@ export function getLogLevel(environmentService: IEnvironmentService): LogLevel {
}
}
return DEFAULT_LOG_LEVEL;
-}
\ No newline at end of file
+}
diff --git a/src/vs/platform/log/common/logIpc.ts b/src/vs/platform/log/common/logIpc.ts
index 5f631b8b9d1..260922cedcb 100644
--- a/src/vs/platform/log/common/logIpc.ts
+++ b/src/vs/platform/log/common/logIpc.ts
@@ -7,7 +7,7 @@ import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { LogLevel, ILogService, DelegatedLogService } from 'vs/platform/log/common/log';
import { Event } from 'vs/base/common/event';
-export class LogLevelSetterChannel implements IServerChannel {
+export class LoggerChannel implements IServerChannel {
onDidChangeLogLevel: Event;
@@ -26,13 +26,32 @@ export class LogLevelSetterChannel implements IServerChannel {
call(_: unknown, command: string, arg?: any): Promise {
switch (command) {
case 'setLevel': this.service.setLevel(arg); return Promise.resolve();
+ case 'consoleLog': this.consoleLog(arg[0], arg[1]); return Promise.resolve();
}
throw new Error(`Call not found: ${command}`);
}
+
+ private consoleLog(severity: string, args: string[]): void {
+ let consoleFn = console.log;
+
+ switch (severity) {
+ case 'error':
+ consoleFn = console.error;
+ break;
+ case 'warn':
+ consoleFn = console.warn;
+ break;
+ case 'info':
+ consoleFn = console.info;
+ break;
+ }
+
+ consoleFn.call(console, ...args);
+ }
}
-export class LogLevelSetterChannelClient {
+export class LoggerChannelClient {
constructor(private channel: IChannel) { }
@@ -43,12 +62,16 @@ export class LogLevelSetterChannelClient {
setLevel(level: LogLevel): void {
this.channel.call('setLevel', level);
}
+
+ consoleLog(severity: string, args: string[]): void {
+ this.channel.call('consoleLog', [severity, args]);
+ }
}
export class FollowerLogService extends DelegatedLogService implements ILogService {
_serviceBrand: undefined;
- constructor(private master: LogLevelSetterChannelClient, logService: ILogService) {
+ constructor(private master: LoggerChannelClient, logService: ILogService) {
super(logService);
this._register(master.onDidChangeLogLevel(level => logService.setLevel(level)));
}
@@ -56,4 +79,4 @@ export class FollowerLogService extends DelegatedLogService implements ILogServi
setLevel(level: LogLevel): void {
this.master.setLevel(level);
}
-}
\ No newline at end of file
+}
diff --git a/src/vs/platform/menubar/electron-main/menubar.ts b/src/vs/platform/menubar/electron-main/menubar.ts
index 25139ec68e5..70caae25b47 100644
--- a/src/vs/platform/menubar/electron-main/menubar.ts
+++ b/src/vs/platform/menubar/electron-main/menubar.ts
@@ -68,7 +68,7 @@ export class Menubar {
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IHistoryMainService private readonly historyMainService: IHistoryMainService,
@IStateService private readonly stateService: IStateService,
- @ILifecycleMainService private readonly lifecycleService: ILifecycleMainService,
+ @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
@ILogService private readonly logService: ILogService
) {
this.menuUpdater = new RunOnceScheduler(() => this.doUpdateMenu(), 0);
@@ -160,7 +160,7 @@ export class Menubar {
private registerListeners(): void {
// Keep flag when app quits
- this.lifecycleService.onWillShutdown(() => this.willShutdown = true);
+ this.lifecycleMainService.onWillShutdown(() => this.willShutdown = true);
// // Listen to some events from window service to update menu
this.windowsMainService.onWindowsCountChanged(e => this.onWindowsCountChanged(e));
diff --git a/src/vs/platform/opener/common/opener.ts b/src/vs/platform/opener/common/opener.ts
index 8563f3fa2af..0c9a243a952 100644
--- a/src/vs/platform/opener/common/opener.ts
+++ b/src/vs/platform/opener/common/opener.ts
@@ -5,7 +5,7 @@
import { URI } from 'vs/base/common/uri';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
-import { IDisposable } from 'vs/base/common/lifecycle';
+import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
export const IOpenerService = createDecorator('openerService');
@@ -18,6 +18,10 @@ export interface IValidator {
shouldOpen(resource: URI): Promise;
}
+export interface IExternalUriResolver {
+ resolveExternalUri(resource: URI): Promise;
+}
+
export interface IOpenerService {
_serviceBrand: undefined;
@@ -29,10 +33,15 @@ export interface IOpenerService {
/**
* Register a participant that can validate if the URI resource be opened.
- * validators are run before openers.
+ * Validators are run before openers.
*/
registerValidator(validator: IValidator): IDisposable;
+ /**
+ * Register a participant that can resolve an external URI resource to be opened.
+ */
+ registerExternalUriResolver(resolver: IExternalUriResolver): IDisposable;
+
/**
* Opens a resource, like a webaddress, a document uri, or executes command.
*
@@ -45,7 +54,8 @@ export interface IOpenerService {
export const NullOpenerService: IOpenerService = Object.freeze({
_serviceBrand: undefined,
- registerOpener() { return { dispose() { } }; },
- registerValidator() { return { dispose() { } }; },
+ registerOpener() { return Disposable.None; },
+ registerValidator() { return Disposable.None; },
+ registerExternalUriResolver() { return Disposable.None; },
open() { return Promise.resolve(false); },
});
diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts
index f5fc75c9324..b6ec6f9e6f9 100644
--- a/src/vs/platform/theme/common/colorRegistry.ts
+++ b/src/vs/platform/theme/common/colorRegistry.ts
@@ -187,6 +187,8 @@ export const activeContrastBorder = registerColor('contrastActiveBorder', { ligh
export const selectionBackground = registerColor('selection.background', { light: null, dark: null, hc: null }, nls.localize('selectionBackground', "The background color of text selections in the workbench (e.g. for input fields or text areas). Note that this does not apply to selections within the editor."));
+export const iconForeground = registerColor('icon.foreground', { light: '#424242', dark: '#C5C5C5', hc: '#FFFFFF' }, nls.localize('iconForeground', "The default color for icons in the workbench."));
+
// ------ text colors
export const textSeparatorForeground = registerColor('textSeparator.foreground', { light: '#0000002e', dark: '#ffffff2e', hc: Color.black }, nls.localize('textSeparatorForeground', "Color for text separators."));
diff --git a/src/vs/platform/update/electron-main/abstractUpdateService.ts b/src/vs/platform/update/electron-main/abstractUpdateService.ts
index 8847db647a7..276c468a51a 100644
--- a/src/vs/platform/update/electron-main/abstractUpdateService.ts
+++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts
@@ -44,7 +44,7 @@ export abstract class AbstractUpdateService implements IUpdateService {
}
constructor(
- @ILifecycleMainService private readonly lifecycleService: ILifecycleMainService,
+ @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
@IConfigurationService protected configurationService: IConfigurationService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IRequestService protected requestService: IRequestService,
@@ -152,7 +152,7 @@ export abstract class AbstractUpdateService implements IUpdateService {
this.logService.trace('update#quitAndInstall(): before lifecycle quit()');
- this.lifecycleService.quit(true /* from update */).then(vetod => {
+ this.lifecycleMainService.quit(true /* from update */).then(vetod => {
this.logService.trace(`update#quitAndInstall(): after lifecycle quit() with veto: ${vetod}`);
if (vetod) {
return;
diff --git a/src/vs/platform/update/electron-main/updateService.darwin.ts b/src/vs/platform/update/electron-main/updateService.darwin.ts
index cf016a44280..cd73a5cd95a 100644
--- a/src/vs/platform/update/electron-main/updateService.darwin.ts
+++ b/src/vs/platform/update/electron-main/updateService.darwin.ts
@@ -28,14 +28,14 @@ export class DarwinUpdateService extends AbstractUpdateService {
@memoize private get onRawUpdateDownloaded(): Event { return Event.fromNodeEventEmitter(electron.autoUpdater, 'update-downloaded', (_, releaseNotes, version, date) => ({ releaseNotes, version, productVersion: version, date })); }
constructor(
- @ILifecycleMainService lifecycleService: ILifecycleMainService,
+ @ILifecycleMainService lifecycleMainService: ILifecycleMainService,
@IConfigurationService configurationService: IConfigurationService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IEnvironmentService environmentService: IEnvironmentService,
@IRequestService requestService: IRequestService,
@ILogService logService: ILogService
) {
- super(lifecycleService, configurationService, environmentService, requestService, logService);
+ super(lifecycleMainService, configurationService, environmentService, requestService, logService);
this.onRawError(this.onError, this, this.disposables);
this.onRawUpdateAvailable(this.onUpdateAvailable, this, this.disposables);
this.onRawUpdateDownloaded(this.onUpdateDownloaded, this, this.disposables);
diff --git a/src/vs/platform/update/electron-main/updateService.linux.ts b/src/vs/platform/update/electron-main/updateService.linux.ts
index 7adb7e00bf8..8b59c871d09 100644
--- a/src/vs/platform/update/electron-main/updateService.linux.ts
+++ b/src/vs/platform/update/electron-main/updateService.linux.ts
@@ -20,14 +20,14 @@ export class LinuxUpdateService extends AbstractUpdateService {
_serviceBrand: undefined;
constructor(
- @ILifecycleMainService lifecycleService: ILifecycleMainService,
+ @ILifecycleMainService lifecycleMainService: ILifecycleMainService,
@IConfigurationService configurationService: IConfigurationService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IEnvironmentService environmentService: IEnvironmentService,
@IRequestService requestService: IRequestService,
@ILogService logService: ILogService
) {
- super(lifecycleService, configurationService, environmentService, requestService, logService);
+ super(lifecycleMainService, configurationService, environmentService, requestService, logService);
}
protected buildUpdateFeedUrl(quality: string): string {
diff --git a/src/vs/platform/update/electron-main/updateService.snap.ts b/src/vs/platform/update/electron-main/updateService.snap.ts
index 36db202ff33..75d7c1e187e 100644
--- a/src/vs/platform/update/electron-main/updateService.snap.ts
+++ b/src/vs/platform/update/electron-main/updateService.snap.ts
@@ -35,7 +35,7 @@ abstract class AbstractUpdateService2 implements IUpdateService {
}
constructor(
- @ILifecycleMainService private readonly lifecycleService: ILifecycleMainService,
+ @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
@IEnvironmentService environmentService: IEnvironmentService,
@ILogService protected logService: ILogService,
) {
@@ -106,7 +106,7 @@ abstract class AbstractUpdateService2 implements IUpdateService {
this.logService.trace('update#quitAndInstall(): before lifecycle quit()');
- this.lifecycleService.quit(true /* from update */).then(vetod => {
+ this.lifecycleMainService.quit(true /* from update */).then(vetod => {
this.logService.trace(`update#quitAndInstall(): after lifecycle quit() with veto: ${vetod}`);
if (vetod) {
return;
@@ -139,12 +139,12 @@ export class SnapUpdateService extends AbstractUpdateService2 {
constructor(
private snap: string,
private snapRevision: string,
- @ILifecycleMainService lifecycleService: ILifecycleMainService,
+ @ILifecycleMainService lifecycleMainService: ILifecycleMainService,
@IEnvironmentService environmentService: IEnvironmentService,
@ILogService logService: ILogService,
@ITelemetryService private readonly telemetryService: ITelemetryService
) {
- super(lifecycleService, environmentService, logService);
+ super(lifecycleMainService, environmentService, logService);
const watcher = watch(path.dirname(this.snap));
const onChange = Event.fromNodeEventEmitter(watcher, 'change', (_, fileName: string) => fileName);
@@ -152,7 +152,7 @@ export class SnapUpdateService extends AbstractUpdateService2 {
const onDebouncedCurrentChange = Event.debounce(onCurrentChange, (_, e) => e, 2000);
const listener = onDebouncedCurrentChange(this.checkForUpdates, this);
- lifecycleService.onWillShutdown(() => {
+ lifecycleMainService.onWillShutdown(() => {
listener.dispose();
watcher.close();
});
diff --git a/src/vs/platform/update/electron-main/updateService.win32.ts b/src/vs/platform/update/electron-main/updateService.win32.ts
index 7395de466da..c053fda3904 100644
--- a/src/vs/platform/update/electron-main/updateService.win32.ts
+++ b/src/vs/platform/update/electron-main/updateService.win32.ts
@@ -60,7 +60,7 @@ export class Win32UpdateService extends AbstractUpdateService {
}
constructor(
- @ILifecycleMainService lifecycleService: ILifecycleMainService,
+ @ILifecycleMainService lifecycleMainService: ILifecycleMainService,
@IConfigurationService configurationService: IConfigurationService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IEnvironmentService environmentService: IEnvironmentService,
@@ -68,7 +68,7 @@ export class Win32UpdateService extends AbstractUpdateService {
@ILogService logService: ILogService,
@IFileService private readonly fileService: IFileService
) {
- super(lifecycleService, configurationService, environmentService, requestService, logService);
+ super(lifecycleMainService, configurationService, environmentService, requestService, logService);
if (getUpdateType() === UpdateType.Setup) {
/* __GDPR__
diff --git a/src/vs/platform/url/electron-main/electronUrlListener.ts b/src/vs/platform/url/electron-main/electronUrlListener.ts
index 6edd504f66c..f8db65d4e1e 100644
--- a/src/vs/platform/url/electron-main/electronUrlListener.ts
+++ b/src/vs/platform/url/electron-main/electronUrlListener.ts
@@ -28,7 +28,7 @@ export class ElectronURLListener {
constructor(
initial: string | string[],
@IURLService private readonly urlService: IURLService,
- @IWindowsMainService windowsService: IWindowsMainService
+ @IWindowsMainService windowsMainService: IWindowsMainService
) {
const globalBuffer = ((global).getOpenUrls() || []) as string[];
const rawBuffer = [
@@ -58,14 +58,14 @@ export class ElectronURLListener {
const onOpenUrl = Event.filter(Event.map(onOpenElectronUrl, uriFromRawUrl), uri => !!uri);
onOpenUrl(this.urlService.open, this.urlService, this.disposables);
- const isWindowReady = windowsService.getWindows()
+ const isWindowReady = windowsMainService.getWindows()
.filter(w => w.isReady)
.length > 0;
if (isWindowReady) {
flush();
} else {
- Event.once(windowsService.onWindowReady)(flush);
+ Event.once(windowsMainService.onWindowReady)(flush);
}
}
diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts
index 7f697c0d445..8d713a46ec6 100644
--- a/src/vs/platform/windows/common/windows.ts
+++ b/src/vs/platform/windows/common/windows.ts
@@ -155,9 +155,6 @@ export interface IWindowsService {
openNewWindow(options?: INewWindowOptions): Promise;
openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise;
getWindows(): Promise<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]>;
- getWindowCount(): Promise;
- log(severity: string, args: string[]): Promise;
- showItemInFolder(path: URI): Promise;
getActiveWindowId(): Promise;
// This needs to be handled from browser process to prevent
diff --git a/src/vs/platform/windows/common/windowsIpc.ts b/src/vs/platform/windows/common/windowsIpc.ts
index 5e3c0944d94..a17937f4455 100644
--- a/src/vs/platform/windows/common/windowsIpc.ts
+++ b/src/vs/platform/windows/common/windowsIpc.ts
@@ -104,13 +104,10 @@ export class WindowsChannel implements IServerChannel {
case 'openNewWindow': return this.service.openNewWindow(arg);
case 'openExtensionDevelopmentHostWindow': return this.service.openExtensionDevelopmentHostWindow(arg[0], arg[1]);
case 'getWindows': return this.service.getWindows();
- case 'getWindowCount': return this.service.getWindowCount();
case 'relaunch': return this.service.relaunch(arg[0]);
case 'whenSharedProcessReady': return this.service.whenSharedProcessReady();
case 'toggleSharedProcess': return this.service.toggleSharedProcess();
case 'quit': return this.service.quit();
- case 'log': return this.service.log(arg[0], arg[1]);
- case 'showItemInFolder': return this.service.showItemInFolder(URI.revive(arg));
case 'getActiveWindowId': return this.service.getActiveWindowId();
case 'openExternal': return this.service.openExternal(arg);
case 'startCrashReporter': return this.service.startCrashReporter(arg);
diff --git a/src/vs/platform/windows/electron-browser/windowsService.ts b/src/vs/platform/windows/electron-browser/windowsService.ts
index d6089d90776..92808158543 100644
--- a/src/vs/platform/windows/electron-browser/windowsService.ts
+++ b/src/vs/platform/windows/electron-browser/windowsService.ts
@@ -222,18 +222,6 @@ export class WindowsService implements IWindowsService {
return result;
}
- getWindowCount(): Promise {
- return this.channel.call('getWindowCount');
- }
-
- log(severity: string, args: string[]): Promise {
- return this.channel.call('log', [severity, args]);
- }
-
- showItemInFolder(path: URI): Promise {
- return this.channel.call('showItemInFolder', path);
- }
-
getActiveWindowId(): Promise {
return this.channel.call('getActiveWindowId');
}
diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts
index c6ee7cfc593..6cdde7c3da8 100644
--- a/src/vs/platform/windows/electron-main/windowsService.ts
+++ b/src/vs/platform/windows/electron-main/windowsService.ts
@@ -38,15 +38,15 @@ export class WindowsService extends Disposable implements IWindowsService, IURLH
Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-focus', (_, w: Electron.BrowserWindow) => w.id), id => !!this.windowsMainService.getWindowById(id))
);
- readonly onRecentlyOpenedChange: Event = this.historyService.onRecentlyOpenedChange;
+ readonly onRecentlyOpenedChange: Event = this.historyMainService.onRecentlyOpenedChange;
constructor(
private sharedProcess: ISharedProcess,
@IWindowsMainService private readonly windowsMainService: IWindowsMainService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IURLService urlService: IURLService,
- @ILifecycleMainService private readonly lifecycleService: ILifecycleMainService,
- @IHistoryMainService private readonly historyService: IHistoryMainService,
+ @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
+ @IHistoryMainService private readonly historyMainService: IHistoryMainService,
@ILogService private readonly logService: ILogService
) {
super();
@@ -157,25 +157,25 @@ export class WindowsService extends Disposable implements IWindowsService, IURLH
async addRecentlyOpened(recents: IRecent[]): Promise {
this.logService.trace('windowsService#addRecentlyOpened');
- this.historyService.addRecentlyOpened(recents);
+ this.historyMainService.addRecentlyOpened(recents);
}
async removeFromRecentlyOpened(paths: URI[]): Promise {
this.logService.trace('windowsService#removeFromRecentlyOpened');
- this.historyService.removeFromRecentlyOpened(paths);
+ this.historyMainService.removeFromRecentlyOpened(paths);
}
async clearRecentlyOpened(): Promise {
this.logService.trace('windowsService#clearRecentlyOpened');
- this.historyService.clearRecentlyOpened();
+ this.historyMainService.clearRecentlyOpened();
}
async getRecentlyOpened(windowId: number): Promise {
this.logService.trace('windowsService#getRecentlyOpened', windowId);
- return this.withWindow(windowId, codeWindow => this.historyService.getRecentlyOpened(codeWindow.config.workspace, codeWindow.config.folderUri, codeWindow.config.filesToOpenOrCreate), () => this.historyService.getRecentlyOpened())!;
+ return this.withWindow(windowId, codeWindow => this.historyMainService.getRecentlyOpened(codeWindow.config.workspace, codeWindow.config.folderUri, codeWindow.config.filesToOpenOrCreate), () => this.historyMainService.getRecentlyOpened())!;
}
async newWindowTab(): Promise {
@@ -336,32 +336,6 @@ export class WindowsService extends Disposable implements IWindowsService, IURLH
return this.windowsMainService.getWindows().length;
}
- async log(severity: string, args: string[]): Promise {
- let consoleFn = console.log;
-
- switch (severity) {
- case 'error':
- consoleFn = console.error;
- break;
- case 'warn':
- consoleFn = console.warn;
- break;
- case 'info':
- consoleFn = console.info;
- break;
- }
-
- consoleFn.call(console, ...args);
- }
-
- async showItemInFolder(resource: URI): Promise {
- this.logService.trace('windowsService#showItemInFolder');
-
- if (resource.scheme === Schemas.file) {
- shell.showItemInFolder(resource.fsPath);
- }
- }
-
async getActiveWindowId(): Promise {
return this._activeWindowId;
}
@@ -388,7 +362,7 @@ export class WindowsService extends Disposable implements IWindowsService, IURLH
async relaunch(options: { addArgs?: string[], removeArgs?: string[] }): Promise {
this.logService.trace('windowsService#relaunch');
- this.lifecycleService.relaunch(options);
+ this.lifecycleMainService.relaunch(options);
}
async whenSharedProcessReady(): Promise {
diff --git a/src/vs/workbench/api/browser/mainThreadConsole.ts b/src/vs/workbench/api/browser/mainThreadConsole.ts
index 1d7a7723404..4a244875f42 100644
--- a/src/vs/workbench/api/browser/mainThreadConsole.ts
+++ b/src/vs/workbench/api/browser/mainThreadConsole.ts
@@ -6,9 +6,10 @@
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { MainContext, MainThreadConsoleShape, IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
-import { IRemoteConsoleLog, log, parse } from 'vs/base/common/console';
+import { IRemoteConsoleLog, log } from 'vs/base/common/console';
+import { logRemoteEntry } from 'vs/workbench/services/extensions/common/remoteConsoleUtil';
import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions';
-import { IWindowsService } from 'vs/platform/windows/common/windows';
+import { ILogService } from 'vs/platform/log/common/log';
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
@extHostNamedCustomer(MainContext.MainThreadConsole)
@@ -20,7 +21,7 @@ export class MainThreadConsole implements MainThreadConsoleShape {
constructor(
extHostContext: IExtHostContext,
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
- @IWindowsService private readonly _windowsService: IWindowsService,
+ @ILogService private readonly _logService: ILogService,
@IExtensionHostDebugService private readonly _extensionHostDebugService: IExtensionHostDebugService,
) {
const devOpts = parseExtensionDevOptions(this._environmentService);
@@ -40,7 +41,7 @@ export class MainThreadConsole implements MainThreadConsoleShape {
// Log on main side if running tests from cli
if (this._isExtensionDevTestFromCli) {
- this._windowsService.log(entry.severity, parse(entry).args);
+ logRemoteEntry(this._logService, entry);
}
// Broadcast to other windows if we are in development mode
diff --git a/src/vs/workbench/api/common/extHostTask.ts b/src/vs/workbench/api/common/extHostTask.ts
index a2119063d3d..7667465f6bd 100644
--- a/src/vs/workbench/api/common/extHostTask.ts
+++ b/src/vs/workbench/api/common/extHostTask.ts
@@ -374,8 +374,9 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape {
protected _handlers: Map;
protected _taskExecutions: Map;
protected _providedCustomExecutions2: Map;
+ private _notProvidedCustomExecutions: Set; // Used for custom executions tasks that are created and run through executeTask.
protected _activeCustomExecutions2: Map;
-
+ private _lastStartedTask: string | undefined;
protected readonly _onDidExecuteTask: Emitter = new Emitter();
protected readonly _onDidTerminateTask: Emitter = new Emitter();
@@ -399,6 +400,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape {
this._handlers = new Map();
this._taskExecutions = new Map();
this._providedCustomExecutions2 = new Map();
+ this._notProvidedCustomExecutions = new Set();
this._activeCustomExecutions2 = new Map();
}
@@ -462,6 +464,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape {
this._activeCustomExecutions2.set(execution.id, execution2);
this._terminalService.attachPtyToTerminal(terminalId, await execution2.callback());
}
+ this._lastStartedTask = execution.id;
this._onDidExecuteTask.fire({
execution: await this.getTaskExecution(execution)
@@ -571,7 +574,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape {
}
if (CustomExecution2DTO.is(resolvedTaskDTO.execution)) {
- await this.addCustomExecution2(resolvedTaskDTO, resolvedTask);
+ await this.addCustomExecution2(resolvedTaskDTO, resolvedTask, true);
}
return await this.resolveTaskInternal(resolvedTaskDTO);
@@ -585,8 +588,11 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape {
return this._handleCounter++;
}
- protected async addCustomExecution2(taskDTO: tasks.TaskDTO, task: vscode.Task2): Promise {
+ protected async addCustomExecution2(taskDTO: tasks.TaskDTO, task: vscode.Task2, isProvided: boolean): Promise {
const taskId = await this._proxy.$createTaskId(taskDTO);
+ if (!isProvided && !this._providedCustomExecutions2.has(taskId)) {
+ this._notProvidedCustomExecutions.add(taskId);
+ }
this._providedCustomExecutions2.set(taskId, (task).execution2);
}
@@ -618,16 +624,22 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape {
this._activeCustomExecutions2.delete(execution.id);
}
- const lastCustomExecution = this._providedCustomExecutions2.get(execution.id);
// Technically we don't really need to do this, however, if an extension
// is executing a task through "executeTask" over and over again
- // with different properties in the task definition, then this list
+ // with different properties in the task definition, then the map of executions
// could grow indefinitely, something we don't want.
- this._providedCustomExecutions2.clear();
- // We do still need to hang on to the last custom execution so that the
- // Rerun Task command doesn't choke when it tries to rerun a custom execution
- if (lastCustomExecution) {
- this._providedCustomExecutions2.set(execution.id, lastCustomExecution);
+ if (this._notProvidedCustomExecutions.has(execution.id) && (this._lastStartedTask !== execution.id)) {
+ this._providedCustomExecutions2.delete(execution.id);
+ this._notProvidedCustomExecutions.delete(execution.id);
+ }
+ let iterator = this._notProvidedCustomExecutions.values();
+ let iteratorResult = iterator.next();
+ while (!iteratorResult.done) {
+ if (!this._activeCustomExecutions2.has(iteratorResult.value) && (this._lastStartedTask !== iteratorResult.value)) {
+ this._providedCustomExecutions2.delete(iteratorResult.value);
+ this._notProvidedCustomExecutions.delete(iteratorResult.value);
+ }
+ iteratorResult = iterator.next();
}
}
@@ -663,7 +675,7 @@ export class WorkerExtHostTask extends ExtHostTaskBase {
// in the provided custom execution map that is cleaned up after the
// task is executed.
if (CustomExecution2DTO.is(dto.execution)) {
- await this.addCustomExecution2(dto, task);
+ await this.addCustomExecution2(dto, task, false);
} else {
throw new Error('Not implemented');
}
@@ -685,7 +697,7 @@ export class WorkerExtHostTask extends ExtHostTaskBase {
// The ID is calculated on the main thread task side, so, let's call into it here.
// We need the task id's pre-computed for custom task executions because when OnDidStartTask
// is invoked, we have to be able to map it back to our data.
- taskIdPromises.push(this.addCustomExecution2(taskDTO, task));
+ taskIdPromises.push(this.addCustomExecution2(taskDTO, task, true));
} else {
console.warn('Only custom execution tasks supported.');
}
diff --git a/src/vs/workbench/api/node/extHostTask.ts b/src/vs/workbench/api/node/extHostTask.ts
index f1379251ab1..71d8b818b72 100644
--- a/src/vs/workbench/api/node/extHostTask.ts
+++ b/src/vs/workbench/api/node/extHostTask.ts
@@ -57,7 +57,7 @@ export class ExtHostTask extends ExtHostTaskBase {
// in the provided custom execution map that is cleaned up after the
// task is executed.
if (CustomExecution2DTO.is(dto.execution)) {
- await this.addCustomExecution2(dto, task);
+ await this.addCustomExecution2(dto, task, false);
}
return this._proxy.$executeTask(dto).then(value => this.getTaskExecution(value, task));
@@ -80,7 +80,7 @@ export class ExtHostTask extends ExtHostTaskBase {
// The ID is calculated on the main thread task side, so, let's call into it here.
// We need the task id's pre-computed for custom task executions because when OnDidStartTask
// is invoked, we have to be able to map it back to our data.
- taskIdPromises.push(this.addCustomExecution2(taskDTO, task));
+ taskIdPromises.push(this.addCustomExecution2(taskDTO, task, true));
}
}
}
diff --git a/src/vs/workbench/browser/parts/panel/media/chevron-down-dark.svg b/src/vs/workbench/browser/parts/panel/media/chevron-down-dark.svg
deleted file mode 100644
index a1df6a8d44a..00000000000
--- a/src/vs/workbench/browser/parts/panel/media/chevron-down-dark.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/browser/parts/panel/media/chevron-down-hc.svg b/src/vs/workbench/browser/parts/panel/media/chevron-down-hc.svg
deleted file mode 100644
index 4f2ec146927..00000000000
--- a/src/vs/workbench/browser/parts/panel/media/chevron-down-hc.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/browser/parts/panel/media/chevron-down-light.svg b/src/vs/workbench/browser/parts/panel/media/chevron-down-light.svg
deleted file mode 100644
index e60e357f573..00000000000
--- a/src/vs/workbench/browser/parts/panel/media/chevron-down-light.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/browser/parts/panel/media/chevron-left-dark.svg b/src/vs/workbench/browser/parts/panel/media/chevron-left-dark.svg
deleted file mode 100644
index dbc37784a70..00000000000
--- a/src/vs/workbench/browser/parts/panel/media/chevron-left-dark.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/browser/parts/panel/media/chevron-left-hc.svg b/src/vs/workbench/browser/parts/panel/media/chevron-left-hc.svg
deleted file mode 100644
index 3767c200513..00000000000
--- a/src/vs/workbench/browser/parts/panel/media/chevron-left-hc.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/browser/parts/panel/media/chevron-left-light.svg b/src/vs/workbench/browser/parts/panel/media/chevron-left-light.svg
deleted file mode 100644
index 2bd4c96832f..00000000000
--- a/src/vs/workbench/browser/parts/panel/media/chevron-left-light.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/browser/parts/panel/media/chevron-right-dark.svg b/src/vs/workbench/browser/parts/panel/media/chevron-right-dark.svg
deleted file mode 100644
index f518fc1632a..00000000000
--- a/src/vs/workbench/browser/parts/panel/media/chevron-right-dark.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/browser/parts/panel/media/chevron-right-hc.svg b/src/vs/workbench/browser/parts/panel/media/chevron-right-hc.svg
deleted file mode 100644
index 40ba72b7086..00000000000
--- a/src/vs/workbench/browser/parts/panel/media/chevron-right-hc.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/browser/parts/panel/media/chevron-right-light.svg b/src/vs/workbench/browser/parts/panel/media/chevron-right-light.svg
deleted file mode 100644
index 0d746558a4f..00000000000
--- a/src/vs/workbench/browser/parts/panel/media/chevron-right-light.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/browser/parts/panel/media/chevron-up-dark.svg b/src/vs/workbench/browser/parts/panel/media/chevron-up-dark.svg
deleted file mode 100644
index 5b9da8932e1..00000000000
--- a/src/vs/workbench/browser/parts/panel/media/chevron-up-dark.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/browser/parts/panel/media/chevron-up-hc.svg b/src/vs/workbench/browser/parts/panel/media/chevron-up-hc.svg
deleted file mode 100644
index 13bad537364..00000000000
--- a/src/vs/workbench/browser/parts/panel/media/chevron-up-hc.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/browser/parts/panel/media/chevron-up-light.svg b/src/vs/workbench/browser/parts/panel/media/chevron-up-light.svg
deleted file mode 100644
index 5498cda5bc4..00000000000
--- a/src/vs/workbench/browser/parts/panel/media/chevron-up-light.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/browser/parts/panel/media/close-dark.svg b/src/vs/workbench/browser/parts/panel/media/close-dark.svg
deleted file mode 100644
index e0475f7b85a..00000000000
--- a/src/vs/workbench/browser/parts/panel/media/close-dark.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/src/vs/workbench/browser/parts/panel/media/close-hc.svg b/src/vs/workbench/browser/parts/panel/media/close-hc.svg
deleted file mode 100644
index 64618b61760..00000000000
--- a/src/vs/workbench/browser/parts/panel/media/close-hc.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/src/vs/workbench/browser/parts/panel/media/close-light.svg b/src/vs/workbench/browser/parts/panel/media/close-light.svg
deleted file mode 100644
index 3bd44674699..00000000000
--- a/src/vs/workbench/browser/parts/panel/media/close-light.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/src/vs/workbench/browser/parts/panel/media/panelpart.css b/src/vs/workbench/browser/parts/panel/media/panelpart.css
index 9dc9bb2000f..fcb9fc8edfa 100644
--- a/src/vs/workbench/browser/parts/panel/media/panelpart.css
+++ b/src/vs/workbench/browser/parts/panel/media/panelpart.css
@@ -123,69 +123,3 @@
min-width: 110px;
margin-right: 10px;
}
-
-/* Close */
-.monaco-workbench .hide-panel-action {
- background: url('close-light.svg') center center no-repeat;
-}
-
-.vs-dark .monaco-workbench .hide-panel-action {
- background: url('close-dark.svg') center center no-repeat;
-}
-
-.hc-black .monaco-workbench .hide-panel-action {
- background: url('close-hc.svg') center center no-repeat;
-}
-
-
-/* Up */
-.monaco-workbench .maximize-panel-action {
- background-image: url('chevron-up-light.svg');
-}
-
-.vs-dark .monaco-workbench .maximize-panel-action {
- background-image: url('chevron-up-dark.svg');
-}
-
-.hc-black .monaco-workbench .maximize-panel-action {
- background-image: url('chevron-up-hc.svg');
-}
-
-/* Down */
-.monaco-workbench .minimize-panel-action {
- background-image: url('chevron-down-light.svg');
-}
-
-.vs-dark .monaco-workbench .minimize-panel-action {
- background-image: url('chevron-down-dark.svg');
-}
-
-.hc-black .monaco-workbench .minimize-panel-action {
- background-image: url('chevron-down-hc.svg');
-}
-
-/* Left */
-.monaco-workbench .panel.right .maximize-panel-action {
- background-image: url('chevron-left-light.svg');
-}
-
-.vs-dark .monaco-workbench .panel.right .maximize-panel-action {
- background-image: url('chevron-left-dark.svg');
-}
-
-.hc-black .monaco-workbench .panel.right .maximize-panel-action {
- background-image: url('chevron-left-hc.svg');
-}
-
-/* Right */
-.monaco-workbench .panel.right .minimize-panel-action {
- background-image: url('chevron-right-light.svg');
-}
-
-.vs-dark .monaco-workbench .panel.right .minimize-panel-action {
- background-image: url('chevron-right-dark.svg');
-}
-
-.hc-black .monaco-workbench .panel.right .minimize-panel-action {
- background-image: url('chevron-right-hc.svg');
-}
diff --git a/src/vs/workbench/browser/parts/panel/panelActions.ts b/src/vs/workbench/browser/parts/panel/panelActions.ts
index 904f8a364ba..c8419eed7e7 100644
--- a/src/vs/workbench/browser/parts/panel/panelActions.ts
+++ b/src/vs/workbench/browser/parts/panel/panelActions.ts
@@ -28,7 +28,7 @@ export class ClosePanelAction extends Action {
name: string,
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService
) {
- super(id, name, 'hide-panel-action');
+ super(id, name, 'codicon-close');
}
run(): Promise {
@@ -141,11 +141,11 @@ export class ToggleMaximizedPanelAction extends Action {
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
@IEditorGroupsService editorGroupsService: IEditorGroupsService
) {
- super(id, label, layoutService.isPanelMaximized() ? 'minimize-panel-action' : 'maximize-panel-action');
+ super(id, label, layoutService.isPanelMaximized() ? 'codicon-chevron-down' : 'codicon-chevron-up');
this.toDispose.add(editorGroupsService.onDidLayout(() => {
const maximized = this.layoutService.isPanelMaximized();
- this.class = maximized ? 'minimize-panel-action' : 'maximize-panel-action';
+ this.class = maximized ? 'codicon-chevron-down' : 'codicon-chevron-up';
this.label = maximized ? ToggleMaximizedPanelAction.RESTORE_LABEL : ToggleMaximizedPanelAction.MAXIMIZE_LABEL;
}));
}
diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts
index a934e456352..394f522bd67 100644
--- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts
+++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts
@@ -4,18 +4,17 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/titlebarpart';
-import { dirname, posix } from 'vs/base/common/path';
import * as resources from 'vs/base/common/resources';
import { Part } from 'vs/workbench/browser/part';
import { ITitleService, ITitleProperties } from 'vs/workbench/services/title/common/titleService';
import { getZoomFactor } from 'vs/base/browser/browser';
-import { IWindowService, IWindowsService, MenuBarVisibility, getTitleBarStyle } from 'vs/platform/windows/common/windows';
+import { IWindowService, MenuBarVisibility, getTitleBarStyle } from 'vs/platform/windows/common/windows';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
-import { IAction, Action } from 'vs/base/common/actions';
+import { IAction } from 'vs/base/common/actions';
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
-import { DisposableStore } from 'vs/base/common/lifecycle';
+import { DisposableStore, dispose } from 'vs/base/common/lifecycle';
import * as nls from 'vs/nls';
import { EditorInput, toResource, Verbosity, SideBySideEditor } from 'vs/workbench/common/editor';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
@@ -29,7 +28,7 @@ import { trim } from 'vs/base/common/strings';
import { EventType, EventHelper, Dimension, isAncestor, hide, show, removeClass, addClass, append, $, addDisposableListener, runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
import { CustomMenubarControl } from 'vs/workbench/browser/parts/titlebar/menubarControl';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
-import { template, getBaseLabel } from 'vs/base/common/labels';
+import { template } from 'vs/base/common/labels';
import { ILabelService } from 'vs/platform/label/common/label';
import { Event, Emitter } from 'vs/base/common/event';
import { IStorageService } from 'vs/platform/storage/common/storage';
@@ -37,6 +36,9 @@ import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/bro
import { RunOnceScheduler } from 'vs/base/common/async';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { Schemas } from 'vs/base/common/network';
+import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
+import { IMenuService, IMenu, MenuId } from 'vs/platform/actions/common/actions';
+import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
export class TitlebarPart extends Part implements ITitleService {
@@ -71,7 +73,6 @@ export class TitlebarPart extends Part implements ITitleService {
private lastLayoutDimensions: Dimension;
private pendingTitle: string;
- private representedFileName: string;
private isInactive: boolean;
@@ -80,11 +81,12 @@ export class TitlebarPart extends Part implements ITitleService {
private titleUpdater: RunOnceScheduler = this._register(new RunOnceScheduler(() => this.doUpdateTitle(), 0));
+ private contextMenu: IMenu;
+
constructor(
@IContextMenuService private readonly contextMenuService: IContextMenuService,
@IWindowService private readonly windowService: IWindowService,
@IConfigurationService private readonly configurationService: IConfigurationService,
- @IWindowsService private readonly windowsService: IWindowsService,
@IEditorService private readonly editorService: IEditorService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@@ -92,10 +94,14 @@ export class TitlebarPart extends Part implements ITitleService {
@IThemeService themeService: IThemeService,
@ILabelService private readonly labelService: ILabelService,
@IStorageService storageService: IStorageService,
- @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService
+ @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
+ @IMenuService menuService: IMenuService,
+ @IContextKeyService contextKeyService: IContextKeyService
) {
super(Parts.TITLEBAR_PART, { hasTitle: false }, themeService, storageService, layoutService);
+ this.contextMenu = this._register(menuService.createMenu(MenuId.TitleBarContext, contextKeyService));
+
this.registerListeners();
}
@@ -181,9 +187,6 @@ export class TitlebarPart extends Part implements ITitleService {
// Apply to window
this.windowService.setRepresentedFilename(path);
-
- // Keep for context menu
- this.representedFileName = path;
}
private doUpdateTitle(): void {
@@ -376,7 +379,6 @@ export class TitlebarPart extends Part implements ITitleService {
if (!isMacintosh && !isWeb) {
this.windowControls = append(this.element, $('div.window-controls-container'));
-
// Minimize
const minimizeIconContainer = append(this.windowControls, $('div.window-icon-bg'));
const minimizeIcon = append(minimizeIconContainer, $('div.window-icon'));
@@ -503,44 +505,16 @@ export class TitlebarPart extends Part implements ITitleService {
const event = new StandardMouseEvent(e);
const anchor = { x: event.posx, y: event.posy };
- // Show menu
- const actions = this.getContextMenuActions();
- if (actions.length) {
- this.contextMenuService.showContextMenu({
- getAnchor: () => anchor,
- getActions: () => actions,
- onHide: () => actions.forEach(a => a.dispose())
- });
- }
- }
-
- private getContextMenuActions(): IAction[] {
+ // Fill in contributed actions
const actions: IAction[] = [];
+ const actionsDisposable = createAndFillInContextMenuActions(this.contextMenu, undefined, actions, this.contextMenuService);
- if (this.representedFileName) {
- const segments = this.representedFileName.split(posix.sep);
- for (let i = segments.length; i > 0; i--) {
- const isFile = (i === segments.length);
-
- let pathOffset = i;
- if (!isFile) {
- pathOffset++; // for segments which are not the file name we want to open the folder
- }
-
- const path = segments.slice(0, pathOffset).join(posix.sep);
-
- let label: string;
- if (!isFile) {
- label = getBaseLabel(dirname(path));
- } else {
- label = getBaseLabel(path);
- }
-
- actions.push(new ShowItemInFolderAction(path, label || posix.sep, this.windowsService));
- }
- }
-
- return actions;
+ // Show it
+ this.contextMenuService.showContextMenu({
+ getAnchor: () => anchor,
+ getActions: () => actions,
+ onHide: () => dispose(actionsDisposable)
+ });
}
private adjustTitleMarginToCenter(): void {
@@ -605,17 +579,6 @@ export class TitlebarPart extends Part implements ITitleService {
}
}
-class ShowItemInFolderAction extends Action {
-
- constructor(private path: string, label: string, private windowsService: IWindowsService) {
- super('showItemInFolder.action.id', label);
- }
-
- run(): Promise {
- return this.windowsService.showItemInFolder(URI.file(this.path));
- }
-}
-
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
const titlebarActiveFg = theme.getColor(TITLE_BAR_ACTIVE_FOREGROUND);
if (titlebarActiveFg) {
diff --git a/src/vs/workbench/browser/style.ts b/src/vs/workbench/browser/style.ts
index dbb318045f5..deaaf4953ed 100644
--- a/src/vs/workbench/browser/style.ts
+++ b/src/vs/workbench/browser/style.ts
@@ -6,11 +6,17 @@
import 'vs/css!./media/style';
import { registerThemingParticipant, ITheme, ICssStyleCollector, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService';
-import { foreground, selectionBackground, focusBorder, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, listHighlightForeground, inputPlaceholderForeground } from 'vs/platform/theme/common/colorRegistry';
+import { iconForeground, foreground, selectionBackground, focusBorder, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, listHighlightForeground, inputPlaceholderForeground } from 'vs/platform/theme/common/colorRegistry';
import { WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme';
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
+ // Icon defaults
+ const iconForegroundColor = theme.getColor(iconForeground);
+ if (iconForegroundColor) {
+ collector.addRule(`.monaco-workbench .codicon { color: ${iconForegroundColor}; }`);
+ }
+
// Foreground
const windowForeground = theme.getColor(foreground);
if (windowForeground) {
@@ -136,4 +142,5 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
}
`);
}
+
});
diff --git a/src/vs/workbench/browser/web.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts
index 86e496ceedd..53fb58d10a2 100644
--- a/src/vs/workbench/browser/web.simpleservices.ts
+++ b/src/vs/workbench/browser/web.simpleservices.ts
@@ -318,8 +318,6 @@ registerSingleton(IWindowService, SimpleWindowService);
export class SimpleWindowsService implements IWindowsService {
_serviceBrand: undefined;
- windowCount = 1;
-
readonly onWindowOpen: Event = Event.None;
readonly onWindowFocus: Event = Event.None;
readonly onWindowBlur: Event = Event.None;
@@ -454,49 +452,6 @@ export class SimpleWindowsService implements IWindowsService {
}
openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise {
-
- // we pass the "ParsedArgs" as query parameters of the URL
-
- let newAddress = `${document.location.origin}/?`;
- let gotFolder = false;
-
- const addQueryParameter = (key: string, value: string) => {
- const lastChar = newAddress.charAt(newAddress.length - 1);
- if (lastChar !== '?' && lastChar !== '&') {
- newAddress += '&';
- }
- newAddress += `${key}=${encodeURIComponent(value)}`;
- };
-
- const f = args['folder-uri'];
- if (f) {
- const u = URI.parse(f[0]);
- gotFolder = true;
- addQueryParameter('folder', u.path);
- }
- if (!gotFolder) {
- // request empty window
- addQueryParameter('ew', 'true');
- }
-
- const ep = args['extensionDevelopmentPath'];
- if (ep) {
- let u = ep[0];
- addQueryParameter('edp', u);
- }
-
- const di = args['debugId'];
- if (di) {
- addQueryParameter('di', di);
- }
-
- const ibe = args['inspect-brk-extensions'];
- if (ibe) {
- addQueryParameter('ibe', ibe);
- }
-
- window.open(newAddress);
-
return Promise.resolve();
}
@@ -504,18 +459,6 @@ export class SimpleWindowsService implements IWindowsService {
return Promise.resolve([]);
}
- getWindowCount(): Promise {
- return Promise.resolve(this.windowCount);
- }
-
- log(_severity: string, _args: string[]): Promise {
- return Promise.resolve();
- }
-
- showItemInFolder(_path: URI): Promise {
- return Promise.resolve();
- }
-
newWindowTab(): Promise {
return Promise.resolve();
}
diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts
index 776eb29464f..2a5c59429a3 100644
--- a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts
+++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts
@@ -17,7 +17,7 @@ import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webvi
import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput';
import { IWebviewEditorService } from 'vs/workbench/contrib/webview/browser/webviewEditorService';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
-import { promptSave } from 'vs/workbench/services/textfile/common/textFileService';
+import { promptSave } from 'vs/workbench/services/textfile/browser/textFileService';
export class CustomFileEditorInput extends WebviewInput {
diff --git a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts
index 8b613a2058a..fda9ec6e6ea 100644
--- a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts
+++ b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts
@@ -28,12 +28,14 @@ import { getBreakpointMessageAndClassName } from 'vs/workbench/contrib/debug/bro
import { generateUuid } from 'vs/base/common/uuid';
import { memoize } from 'vs/base/common/decorators';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
+import { distinct } from 'vs/base/common/arrays';
+import { RunOnceScheduler } from 'vs/base/common/async';
const $ = dom.$;
interface IBreakpointDecoration {
decorationId: string;
- breakpointId: string;
+ breakpoint: IBreakpoint;
range: Range;
inlineWidget?: InlineBreakpointWidget;
}
@@ -84,6 +86,39 @@ function getBreakpointDecorationOptions(model: ITextModel, breakpoint: IBreakpoi
};
}
+async function createCandidateDecorations(model: ITextModel, breakpointDecorations: IBreakpointDecoration[], debugService: IDebugService): Promise<{ range: Range; options: IModelDecorationOptions; breakpoint: IBreakpoint | undefined }[]> {
+ const lineNumbers = distinct(breakpointDecorations.map(bpd => bpd.range.startLineNumber));
+ const result: { range: Range; options: IModelDecorationOptions; breakpoint: IBreakpoint | undefined }[] = [];
+ const session = debugService.getViewModel().focusedSession;
+ if (session && session.capabilities.supportsBreakpointLocationsRequest) {
+ await Promise.all(lineNumbers.map(async lineNumber => {
+ const positions = await session.breakpointsLocations(model.uri, lineNumber);
+ if (positions.length > 1) {
+ // Do not render candidates if there is only one, since it is already covered by the line breakpoint
+ positions.forEach(p => {
+ const range = new Range(p.lineNumber, p.column, p.lineNumber, p.column + 1);
+ const breakpointAtPosition = breakpointDecorations.filter(bpd => bpd.range.equalsRange(range)).pop();
+ if (breakpointAtPosition && breakpointAtPosition.inlineWidget) {
+ // Space already occupied, do not render candidate.
+ return;
+ }
+ result.push({
+ range,
+ options: {
+ stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
+ beforeContentClassName: `debug-breakpoint-placeholder`
+ },
+ breakpoint: breakpointAtPosition ? breakpointAtPosition.breakpoint : undefined
+ });
+ });
+ }
+ }));
+ }
+
+ return result;
+}
+
+
class BreakpointEditorContribution implements IBreakpointEditorContribution {
private breakpointHintDecoration: string[] = [];
@@ -91,8 +126,10 @@ class BreakpointEditorContribution implements IBreakpointEditorContribution {
private breakpointWidgetVisible: IContextKey;
private toDispose: IDisposable[] = [];
private ignoreDecorationsChangedEvent = false;
- private ignoreFirstBreakpointsChangeEvent = false;
+ private ignoreBreakpointsChangeEvent = false;
private breakpointDecorations: IBreakpointDecoration[] = [];
+ private candidateDecorations: { decorationId: string, inlineWidget: InlineBreakpointWidget }[] = [];
+ private setDecorationsScheduler: RunOnceScheduler;
constructor(
private readonly editor: ICodeEditor,
@@ -104,6 +141,7 @@ class BreakpointEditorContribution implements IBreakpointEditorContribution {
) {
this.breakpointWidgetVisible = CONTEXT_BREAKPOINT_WIDGET_VISIBLE.bindTo(contextKeyService);
this.registerListeners();
+ this.setDecorationsScheduler = new RunOnceScheduler(() => this.setDecorations(), 30);
}
getId(): string {
@@ -193,16 +231,14 @@ class BreakpointEditorContribution implements IBreakpointEditorContribution {
this.ensureBreakpointHintDecoration(-1);
}));
- this.toDispose.push(this.editor.onDidChangeModel(() => {
+ this.toDispose.push(this.editor.onDidChangeModel(async () => {
this.closeBreakpointWidget();
- this.setDecorations();
+ await this.setDecorations();
}));
- this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => {
- if (this.ignoreFirstBreakpointsChangeEvent) {
- this.ignoreFirstBreakpointsChangeEvent = false;
- return;
+ this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(async () => {
+ if (!this.ignoreBreakpointsChangeEvent && !this.setDecorationsScheduler.isScheduled()) {
+ this.setDecorationsScheduler.schedule();
}
- this.setDecorations();
}));
this.toDispose.push(this.editor.onDidChangeModelDecorations(() => this.onModelDecorationsChanged()));
}
@@ -274,7 +310,7 @@ class BreakpointEditorContribution implements IBreakpointEditorContribution {
nls.localize('addLogPoint', "Add Logpoint..."),
undefined,
true,
- () => Promise.resolve(this.showBreakpointWidget(lineNumber, BreakpointWidgetContext.LOG_MESSAGE))
+ () => Promise.resolve(this.showBreakpointWidget(lineNumber, column, BreakpointWidgetContext.LOG_MESSAGE))
));
}
@@ -311,7 +347,7 @@ class BreakpointEditorContribution implements IBreakpointEditorContribution {
this.breakpointHintDecoration = this.editor.deltaDecorations(this.breakpointHintDecoration, newDecoration);
}
- private setDecorations(): void {
+ private async setDecorations(): Promise {
if (!this.editor.hasModel()) {
return;
}
@@ -319,11 +355,13 @@ class BreakpointEditorContribution implements IBreakpointEditorContribution {
const activeCodeEditor = this.editor;
const model = activeCodeEditor.getModel();
const breakpoints = this.debugService.getModel().getBreakpoints({ uri: model.uri });
- const desiredDecorations = createBreakpointDecorations(model, breakpoints, this.debugService);
+ const desiredBreakpointDecorations = createBreakpointDecorations(model, breakpoints, this.debugService);
try {
this.ignoreDecorationsChangedEvent = true;
- const decorationIds = activeCodeEditor.deltaDecorations(this.breakpointDecorations.map(bpd => bpd.decorationId), desiredDecorations);
+
+ // Set breakpoint decorations
+ const decorationIds = activeCodeEditor.deltaDecorations(this.breakpointDecorations.map(bpd => bpd.decorationId), desiredBreakpointDecorations);
this.breakpointDecorations.forEach(bpd => {
if (bpd.inlineWidget) {
bpd.inlineWidget.dispose();
@@ -333,19 +371,37 @@ class BreakpointEditorContribution implements IBreakpointEditorContribution {
let inlineWidget: InlineBreakpointWidget | undefined = undefined;
const breakpoint = breakpoints[index];
if (breakpoint.column) {
- inlineWidget = new InlineBreakpointWidget(activeCodeEditor, decorationId, desiredDecorations[index].options.glyphMarginClassName, breakpoint, this.debugService, this.contextMenuService, () => this.getContextMenuActions([breakpoint], activeCodeEditor.getModel().uri, breakpoint.lineNumber, breakpoint.column));
+ inlineWidget = new InlineBreakpointWidget(activeCodeEditor, decorationId, desiredBreakpointDecorations[index].options.glyphMarginClassName, breakpoint, this.debugService, this.contextMenuService, () => this.getContextMenuActions([breakpoint], activeCodeEditor.getModel().uri, breakpoint.lineNumber, breakpoint.column));
}
return {
decorationId,
- breakpointId: breakpoint.getId(),
- range: desiredDecorations[index].range,
+ breakpoint,
+ range: desiredBreakpointDecorations[index].range,
inlineWidget
};
});
+
} finally {
this.ignoreDecorationsChangedEvent = false;
}
+
+ // Set breakpoint candidate decorations
+ const desiredCandidateDecorations = await createCandidateDecorations(this.editor.getModel(), this.breakpointDecorations, this.debugService);
+ const candidateDecorationIds = this.editor.deltaDecorations(this.candidateDecorations.map(c => c.decorationId), desiredCandidateDecorations);
+ this.candidateDecorations.forEach(candidate => {
+ candidate.inlineWidget.dispose();
+ });
+ this.candidateDecorations = candidateDecorationIds.map((decorationId, index) => {
+ const candidate = desiredCandidateDecorations[index];
+ const cssClass = candidate.breakpoint ? undefined : 'debug-breakpoint-disabled';
+ const inlineWidget = new InlineBreakpointWidget(activeCodeEditor, decorationId, cssClass, candidate.breakpoint, this.debugService, this.contextMenuService, () => this.getContextMenuActions([], activeCodeEditor.getModel().uri, candidate.range.startLineNumber, candidate.range.startColumn));
+
+ return {
+ decorationId,
+ inlineWidget
+ };
+ });
}
private async onModelDecorationsChanged(): Promise {
@@ -370,28 +426,26 @@ class BreakpointEditorContribution implements IBreakpointEditorContribution {
}
const data = new Map();
- const breakpoints = this.debugService.getModel().getBreakpoints();
for (let i = 0, len = this.breakpointDecorations.length; i < len; i++) {
const breakpointDecoration = this.breakpointDecorations[i];
const decorationRange = model.getDecorationRange(breakpointDecoration.decorationId);
// check if the line got deleted.
if (decorationRange) {
- const breakpoint = breakpoints.filter(bp => bp.getId() === breakpointDecoration.breakpointId).pop();
// since we know it is collapsed, it cannot grow to multiple lines
- if (breakpoint) {
- data.set(breakpoint.getId(), {
+ if (breakpointDecoration.breakpoint) {
+ data.set(breakpointDecoration.breakpoint.getId(), {
lineNumber: decorationRange.startLineNumber,
- column: breakpoint.column ? decorationRange.startColumn : undefined,
+ column: breakpointDecoration.breakpoint.column ? decorationRange.startColumn : undefined,
});
}
}
}
try {
- this.ignoreFirstBreakpointsChangeEvent = true;
+ this.ignoreBreakpointsChangeEvent = true;
await this.debugService.updateBreakpoints(model.uri, data, true);
} finally {
- this.ignoreFirstBreakpointsChangeEvent = false;
+ this.ignoreBreakpointsChangeEvent = false;
}
}
@@ -495,6 +549,8 @@ class InlineBreakpointWidget implements IContentWidget, IDisposable {
if (!this.range) {
return null;
}
+ // Workaround: since the content widget can not be placed before the first column we need to force the left position
+ dom.toggleClass(this.domNode, 'line-start', this.range.startColumn === 1);
return {
position: { lineNumber: this.range.startLineNumber, column: this.range.startColumn - 1 },
diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts
index 92c18c46873..928e9d322ed 100644
--- a/src/vs/workbench/contrib/debug/browser/debugSession.ts
+++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts
@@ -22,7 +22,8 @@ import { IWorkspaceFolder, IWorkspaceContextService } from 'vs/platform/workspac
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { RunOnceScheduler } from 'vs/base/common/async';
import { generateUuid } from 'vs/base/common/uuid';
-import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
+import { IWindowService } from 'vs/platform/windows/common/windows';
+import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { normalizeDriveLetter } from 'vs/base/common/labels';
import { Range } from 'vs/editor/common/core/range';
@@ -74,7 +75,7 @@ export class DebugSession implements IDebugSession {
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
@INotificationService private readonly notificationService: INotificationService,
@IProductService private readonly productService: IProductService,
- @IWindowsService private readonly windowsService: IWindowsService,
+ @IExtensionHostDebugService private readonly extensionHostDebugService: IExtensionHostDebugService,
@IOpenerService private readonly openerService: IOpenerService
) {
this.id = generateUuid();
@@ -186,7 +187,7 @@ export class DebugSession implements IDebugSession {
return dbgr.createDebugAdapter(this).then(debugAdapter => {
- this.raw = new RawDebugSession(debugAdapter, dbgr, this.telemetryService, customTelemetryService, this.windowsService, this.openerService);
+ this.raw = new RawDebugSession(debugAdapter, dbgr, this.telemetryService, customTelemetryService, this.extensionHostDebugService, this.openerService);
return this.raw.start().then(() => {
@@ -375,7 +376,7 @@ export class DebugSession implements IDebugSession {
const response = await this.raw.breakpointLocations({ source, line: lineNumber });
const positions = response.body.breakpoints.map(bp => ({ lineNumber: bp.line, column: bp.column || 1 }));
- return distinct(positions, p => p.toString());
+ return distinct(positions, p => `${p.lineNumber}:${p.column}`);
}
return Promise.reject(new Error('no debug adapter'));
}
diff --git a/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts b/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts
index 49b8815ca93..af19c1bbe2e 100644
--- a/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts
+++ b/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts
@@ -5,7 +5,7 @@
import { ExtensionHostDebugChannelClient, ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
-import { IEnvironmentService } from 'vs/platform/environment/common/environment';
+import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
import { IDebugHelperService } from 'vs/workbench/contrib/debug/common/debug';
@@ -13,8 +13,10 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService';
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
import { Event } from 'vs/base/common/event';
+import { IProcessEnvironment } from 'vs/base/common/platform';
+import { URI } from 'vs/base/common/uri';
-class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient {
+class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient implements IExtensionHostDebugService {
constructor(
@IRemoteAgentService remoteAgentService: IRemoteAgentService,
@@ -45,6 +47,52 @@ class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient {
}
}));
}
+
+ openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise {
+ // we pass the "ParsedArgs" as query parameters of the URL
+
+ let newAddress = `${document.location.origin}/?`;
+ let gotFolder = false;
+
+ const addQueryParameter = (key: string, value: string) => {
+ const lastChar = newAddress.charAt(newAddress.length - 1);
+ if (lastChar !== '?' && lastChar !== '&') {
+ newAddress += '&';
+ }
+ newAddress += `${key}=${encodeURIComponent(value)}`;
+ };
+
+ const f = args['folder-uri'];
+ if (f) {
+ const u = URI.parse(f[0]);
+ gotFolder = true;
+ addQueryParameter('folder', u.path);
+ }
+ if (!gotFolder) {
+ // request empty window
+ addQueryParameter('ew', 'true');
+ }
+
+ const ep = args['extensionDevelopmentPath'];
+ if (ep) {
+ let u = ep[0];
+ addQueryParameter('edp', u);
+ }
+
+ const di = args['debugId'];
+ if (di) {
+ addQueryParameter('di', di);
+ }
+
+ const ibe = args['inspect-brk-extensions'];
+ if (ibe) {
+ addQueryParameter('ibe', ibe);
+ }
+
+ window.open(newAddress);
+
+ return Promise.resolve();
+ }
}
registerSingleton(IExtensionHostDebugService, BrowserExtensionHostDebugService);
diff --git a/src/vs/workbench/contrib/debug/browser/media/clear-dark.svg b/src/vs/workbench/contrib/debug/browser/media/clear-dark.svg
deleted file mode 100644
index 04d64ab41ca..00000000000
--- a/src/vs/workbench/contrib/debug/browser/media/clear-dark.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/debug/browser/media/clear-hc.svg b/src/vs/workbench/contrib/debug/browser/media/clear-hc.svg
deleted file mode 100644
index 44a41edd3b3..00000000000
--- a/src/vs/workbench/contrib/debug/browser/media/clear-hc.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/debug/browser/media/clear-light.svg b/src/vs/workbench/contrib/debug/browser/media/clear-light.svg
deleted file mode 100644
index f6a51c856f0..00000000000
--- a/src/vs/workbench/contrib/debug/browser/media/clear-light.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css b/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css
index 69be074454c..4576abcbe9a 100644
--- a/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css
+++ b/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css
@@ -21,6 +21,14 @@
background: url('breakpoint-disabled.svg') center center no-repeat;
}
+.monaco-editor .inline-breakpoint-widget.debug-breakpoint-disabled:hover {
+ background: url('breakpoint-hint.svg') center center no-repeat;
+}
+
+.monaco-editor .inline-breakpoint-widget.line-start {
+ left: -0.45em !important;
+}
+
.debug-breakpoint-unverified,
.monaco-editor .inline-breakpoint-widget.debug-breakpoint-unverified {
background: url('breakpoint-unverified.svg') center center no-repeat;
@@ -57,6 +65,7 @@
width: 1.3em;
height: 1.3em;
margin-left: 0.61em;
+ cursor: pointer;
}
.debug-function-breakpoint {
diff --git a/src/vs/workbench/contrib/debug/browser/media/repl.css b/src/vs/workbench/contrib/debug/browser/media/repl.css
index 9d92c972d48..e7befa20952 100644
--- a/src/vs/workbench/contrib/debug/browser/media/repl.css
+++ b/src/vs/workbench/contrib/debug/browser/media/repl.css
@@ -88,19 +88,6 @@
font-size: 9px;
}
-/* Actions */
-.debug-action.clear-repl {
- background: url('clear-light.svg') center center no-repeat;
-}
-
-.vs-dark .debug-action.clear-repl {
- background: url('clear-dark.svg') center center no-repeat;
-}
-
-.hc-black .debug-action.clear-repl {
- background: url('clear-hc.svg') center center no-repeat;
-}
-
/* Output coloring and styling */
.repl .repl-tree .output.expression > .ignore {
font-style: italic;
diff --git a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts
index 53a10ed04c5..45ffb5776a8 100644
--- a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts
+++ b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts
@@ -13,7 +13,7 @@ import { formatPII, isUri } from 'vs/workbench/contrib/debug/common/debugUtils';
import { IDebugAdapter, IConfig, AdapterEndEvent, IDebugger } from 'vs/workbench/contrib/debug/common/debug';
import { createErrorWithActions } from 'vs/base/common/errorsWithActions';
import { ParsedArgs } from 'vs/platform/environment/common/environment';
-import { IWindowsService } from 'vs/platform/windows/common/windows';
+import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
import { URI } from 'vs/base/common/uri';
import { IProcessEnvironment } from 'vs/base/common/platform';
import { env as processEnv } from 'vs/base/common/process';
@@ -79,7 +79,7 @@ export class RawDebugSession implements IDisposable {
dbgr: IDebugger,
private readonly telemetryService: ITelemetryService,
public readonly customTelemetryService: ITelemetryService | undefined,
- private readonly windowsService: IWindowsService,
+ private readonly extensionHostDebugService: IExtensionHostDebugService,
private readonly openerService: IOpenerService
) {
@@ -632,7 +632,7 @@ export class RawDebugSession implements IDisposable {
Object.keys(env).filter(k => env[k] === null).forEach(key => delete env[key]);
}
- return this.windowsService.openExtensionDevelopmentHostWindow(args, env);
+ return this.extensionHostDebugService.openExtensionDevelopmentHostWindow(args, env);
}
private send(command: string, args: any, token?: CancellationToken, timeout?: number): Promise {
diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts
index ba438198881..ec21ca71f2c 100644
--- a/src/vs/workbench/contrib/debug/browser/repl.ts
+++ b/src/vs/workbench/contrib/debug/browser/repl.ts
@@ -1008,7 +1008,7 @@ export class ClearReplAction extends Action {
constructor(id: string, label: string,
@IPanelService private readonly panelService: IPanelService
) {
- super(id, label, 'debug-action clear-repl');
+ super(id, label, 'debug-action codicon-clear-all');
}
run(): Promise {
diff --git a/src/vs/workbench/contrib/debug/electron-browser/extensionHostDebugService.ts b/src/vs/workbench/contrib/debug/electron-browser/extensionHostDebugService.ts
index e292a9ecd31..dad63fd4fb0 100644
--- a/src/vs/workbench/contrib/debug/electron-browser/extensionHostDebugService.ts
+++ b/src/vs/workbench/contrib/debug/electron-browser/extensionHostDebugService.ts
@@ -7,13 +7,22 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
import { ExtensionHostDebugChannelClient, ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc';
+import { IWindowsService } from 'vs/platform/windows/common/windows';
+import { IProcessEnvironment } from 'vs/base/common/platform';
+import { ParsedArgs } from 'vs/platform/environment/common/environment';
export class ExtensionHostDebugService extends ExtensionHostDebugChannelClient {
constructor(
- @IMainProcessService readonly windowService: IMainProcessService,
+ @IMainProcessService readonly mainProcessService: IMainProcessService,
+ @IWindowsService private readonly windowsService: IWindowsService
) {
- super(windowService.getChannel(ExtensionHostDebugBroadcastChannel.ChannelName));
+ super(mainProcessService.getChannel(ExtensionHostDebugBroadcastChannel.ChannelName));
+ }
+
+ openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise {
+ // TODO@Isidor use debug IPC channel
+ return this.windowsService.openExtensionDevelopmentHostWindow(args, env);
}
}
diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts
index b7bed063f07..db05deb3e72 100644
--- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts
+++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts
@@ -20,7 +20,7 @@ import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/brow
import {
OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowPopularExtensionsAction,
ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, ShowBuiltInExtensionsAction, UpdateAllAction,
- EnableAllAction, EnableAllWorkpsaceAction, DisableAllAction, DisableAllWorkpsaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, ShowAzureExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction, ConfigureRecommendedExtensionsCommandsContributor, OpenExtensionsFolderAction, InstallVSIXAction, ReinstallAction, InstallSpecificVersionOfExtensionAction
+ EnableAllAction, EnableAllWorkpsaceAction, DisableAllAction, DisableAllWorkpsaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, ShowAzureExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction, ConfigureRecommendedExtensionsCommandsContributor, InstallVSIXAction, ReinstallAction, InstallSpecificVersionOfExtensionAction
} from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput';
import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor } from 'vs/workbench/browser/viewlet';
@@ -43,7 +43,6 @@ import { onUnexpectedError } from 'vs/base/common/errors';
import { ExtensionDependencyChecker } from 'vs/workbench/contrib/extensions/browser/extensionsDependencyChecker';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
-import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { RemoteExtensionsInstaller } from 'vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller';
import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/browser/extensionTipsService';
@@ -344,7 +343,6 @@ const workbenchRegistry = Registry.as(Workbench
class ExtensionsContributions implements IWorkbenchContribution {
constructor(
- @IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService,
@IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService
) {
@@ -362,14 +360,7 @@ class ExtensionsContributions implements IWorkbenchContribution {
)
);
}
-
- if (workbenchEnvironmentService.extensionsPath) {
- const openExtensionsFolderActionDescriptor = new SyncActionDescriptor(OpenExtensionsFolderAction, OpenExtensionsFolderAction.ID, OpenExtensionsFolderAction.LABEL);
- actionRegistry.registerWorkbenchAction(openExtensionsFolderActionDescriptor, 'Extensions: Open Extensions Folder', ExtensionsLabel);
- }
-
}
-
}
workbenchRegistry.registerWorkbenchContribution(ExtensionsContributions, LifecyclePhase.Starting);
diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts
index b9356f1b35e..a18bcf9b4ba 100644
--- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts
+++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts
@@ -25,7 +25,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { Query } from 'vs/workbench/contrib/extensions/common/extensionQuery';
import { IFileService, IFileContent } from 'vs/platform/files/common/files';
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
-import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
+import { IWindowService } from 'vs/platform/windows/common/windows';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { URI } from 'vs/base/common/uri';
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
@@ -44,7 +44,6 @@ import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/w
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { mnemonicButtonLabel } from 'vs/base/common/labels';
-import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput';
@@ -2777,41 +2776,6 @@ export class EnableAllWorkpsaceAction extends Action {
}
}
-export class OpenExtensionsFolderAction extends Action {
-
- static readonly ID = 'workbench.extensions.action.openExtensionsFolder';
- static LABEL = localize('openExtensionsFolder', "Open Extensions Folder");
-
- constructor(
- id: string,
- label: string,
- @IWindowsService private readonly windowsService: IWindowsService,
- @IFileService private readonly fileService: IFileService,
- @IEnvironmentService private readonly environmentService: IEnvironmentService
- ) {
- super(id, label, undefined, true);
- }
-
- run(): Promise {
- if (this.environmentService.extensionsPath) {
-
- const extensionsHome = URI.file(this.environmentService.extensionsPath);
-
- return Promise.resolve(this.fileService.resolve(extensionsHome)).then(file => {
- let itemToShow: URI;
- if (file.children && file.children.length > 0) {
- itemToShow = file.children[0].resource;
- } else {
- itemToShow = extensionsHome;
- }
-
- return this.windowsService.showItemInFolder(itemToShow);
- });
- }
- return Promise.resolve();
- }
-}
-
export class InstallVSIXAction extends Action {
static readonly ID = 'workbench.extensions.action.installVSIX';
diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css b/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css
index 79d7e01a69c..ed1ba80ba46 100644
--- a/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css
+++ b/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css
@@ -78,7 +78,7 @@
margin: 0.15em;
}
-.monaco-action-bar .action-item .action-label.system-disable.icon {
+.monaco-action-bar .action-item .action-label.system-disable.codicon {
opacity: 1;
height: 18px;
width: 10px;
diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts
index d47550c32a9..19e5153bb53 100644
--- a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts
+++ b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts
@@ -8,7 +8,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions';
-import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
+import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -22,6 +22,9 @@ import { URI } from 'vs/base/common/uri';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { ExtensionsAutoProfiler } from 'vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler';
import { registerAndGetAmdImageURL } from 'vs/base/common/amd';
+import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
+import { OpenExtensionsFolderAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions';
+import { ExtensionsLabel } from 'vs/platform/extensionManagement/common/extensionManagement';
// Singletons
registerSingleton(IExtensionHostProfileService, ExtensionHostProfileService, true);
@@ -57,6 +60,20 @@ const actionRegistry = Registry.as(WorkbenchActionExte
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowRuntimeExtensionsAction, ShowRuntimeExtensionsAction.ID, ShowRuntimeExtensionsAction.LABEL), 'Show Running Extensions', localize('developer', "Developer"));
+class ExtensionsContributions implements IWorkbenchContribution {
+
+ constructor(
+ @IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService
+ ) {
+ if (workbenchEnvironmentService.extensionsPath) {
+ const openExtensionsFolderActionDescriptor = new SyncActionDescriptor(OpenExtensionsFolderAction, OpenExtensionsFolderAction.ID, OpenExtensionsFolderAction.LABEL);
+ actionRegistry.registerWorkbenchAction(openExtensionsFolderActionDescriptor, 'Extensions: Open Extensions Folder', ExtensionsLabel);
+ }
+ }
+}
+
+workbenchRegistry.registerWorkbenchContribution(ExtensionsContributions, LifecyclePhase.Starting);
+
// Register Commands
CommandsRegistry.registerCommand(DebugExtensionHostAction.ID, (accessor: ServicesAccessor) => {
diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts
new file mode 100644
index 00000000000..b4185a6614f
--- /dev/null
+++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts
@@ -0,0 +1,47 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { localize } from 'vs/nls';
+import { Action } from 'vs/base/common/actions';
+import { IFileService } from 'vs/platform/files/common/files';
+import { URI } from 'vs/base/common/uri';
+import { IEnvironmentService } from 'vs/platform/environment/common/environment';
+import { IElectronService } from 'vs/platform/electron/node/electron';
+import { Schemas } from 'vs/base/common/network';
+
+export class OpenExtensionsFolderAction extends Action {
+
+ static readonly ID = 'workbench.extensions.action.openExtensionsFolder';
+ static LABEL = localize('openExtensionsFolder', "Open Extensions Folder");
+
+ constructor(
+ id: string,
+ label: string,
+ @IElectronService private readonly electronService: IElectronService,
+ @IFileService private readonly fileService: IFileService,
+ @IEnvironmentService private readonly environmentService: IEnvironmentService
+ ) {
+ super(id, label, undefined, true);
+ }
+
+ async run(): Promise {
+ if (this.environmentService.extensionsPath) {
+ const extensionsHome = URI.file(this.environmentService.extensionsPath);
+ const file = await this.fileService.resolve(extensionsHome);
+
+ let itemToShow: URI;
+ if (file.children && file.children.length > 0) {
+ itemToShow = file.children[0].resource;
+ } else {
+ itemToShow = extensionsHome;
+ }
+
+ if (itemToShow.scheme === Schemas.file) {
+ return this.electronService.showItemInFolder(itemToShow.fsPath);
+ }
+ }
+ }
+}
+
diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts
index ff484b1f19c..47c9912e899 100644
--- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts
+++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts
@@ -10,11 +10,11 @@ import { revertLocalChangesCommand, acceptLocalChangesCommand, CONFLICT_RESOLUTI
import { SyncActionDescriptor, MenuId, MenuRegistry, ILocalizedString } from 'vs/platform/actions/common/actions';
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes';
-import { openWindowCommand, REVEAL_IN_OS_COMMAND_ID, COPY_PATH_COMMAND_ID, REVEAL_IN_EXPLORER_COMMAND_ID, OPEN_TO_SIDE_COMMAND_ID, REVERT_FILE_COMMAND_ID, SAVE_FILE_COMMAND_ID, SAVE_FILE_LABEL, SAVE_FILE_AS_COMMAND_ID, SAVE_FILE_AS_LABEL, SAVE_ALL_IN_GROUP_COMMAND_ID, OpenEditorsGroupContext, COMPARE_WITH_SAVED_COMMAND_ID, COMPARE_RESOURCE_COMMAND_ID, SELECT_FOR_COMPARE_COMMAND_ID, ResourceSelectedForCompareContext, REVEAL_IN_OS_LABEL, DirtyEditorContext, COMPARE_SELECTED_COMMAND_ID, REMOVE_ROOT_FOLDER_COMMAND_ID, REMOVE_ROOT_FOLDER_LABEL, SAVE_FILES_COMMAND_ID, COPY_RELATIVE_PATH_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_LABEL, newWindowCommand } from 'vs/workbench/contrib/files/browser/fileCommands';
+import { openWindowCommand, COPY_PATH_COMMAND_ID, REVEAL_IN_EXPLORER_COMMAND_ID, OPEN_TO_SIDE_COMMAND_ID, REVERT_FILE_COMMAND_ID, SAVE_FILE_COMMAND_ID, SAVE_FILE_LABEL, SAVE_FILE_AS_COMMAND_ID, SAVE_FILE_AS_LABEL, SAVE_ALL_IN_GROUP_COMMAND_ID, OpenEditorsGroupContext, COMPARE_WITH_SAVED_COMMAND_ID, COMPARE_RESOURCE_COMMAND_ID, SELECT_FOR_COMPARE_COMMAND_ID, ResourceSelectedForCompareContext, DirtyEditorContext, COMPARE_SELECTED_COMMAND_ID, REMOVE_ROOT_FOLDER_COMMAND_ID, REMOVE_ROOT_FOLDER_LABEL, SAVE_FILES_COMMAND_ID, COPY_RELATIVE_PATH_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_LABEL, newWindowCommand } from 'vs/workbench/contrib/files/browser/fileCommands';
import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
-import { isWindows, isMacintosh } from 'vs/base/common/platform';
+import { isMacintosh } from 'vs/base/common/platform';
import { FilesExplorerFocusCondition, ExplorerRootContext, ExplorerFolderContext, ExplorerResourceNotReadonlyContext, ExplorerResourceCut, IExplorerService, ExplorerResourceMoveableToTrash, ExplorerViewletVisibleContext } from 'vs/workbench/contrib/files/common/files';
import { ADD_ROOT_FOLDER_COMMAND_ID, ADD_ROOT_FOLDER_LABEL } from 'vs/workbench/browser/actions/workspaceCommands';
import { CLOSE_SAVED_EDITORS_COMMAND_ID, CLOSE_EDITORS_IN_GROUP_COMMAND_ID, CLOSE_EDITOR_COMMAND_ID, CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands';
@@ -49,6 +49,14 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleAutoSaveAction,
const workspacesCategory = nls.localize('workspaces', "Workspaces");
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenWorkspaceAction, OpenWorkspaceAction.ID, OpenWorkspaceAction.LABEL), 'Workspaces: Open Workspace...', workspacesCategory);
+const fileCategory = nls.localize('file', "File");
+if (isMacintosh) {
+ registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileFolderAction, OpenFileFolderAction.ID, OpenFileFolderAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }), 'File: Open...', fileCategory);
+} else {
+ registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileAction, OpenFileAction.ID, OpenFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }), 'File: Open File...', fileCategory);
+ registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFolderAction, OpenFolderAction.ID, OpenFolderAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_O) }), 'File: Open Folder...', fileCategory);
+}
+
// Commands
CommandsRegistry.registerCommand('_files.windowOpen', openWindowCommand);
CommandsRegistry.registerCommand('_files.newWindow', newWindowCommand);
@@ -162,11 +170,9 @@ const copyRelativePathCommand = {
// Editor Title Context Menu
appendEditorTitleContextMenuItem(COPY_PATH_COMMAND_ID, copyPathCommand.title, ResourceContextKey.IsFileSystemResource, '1_cutcopypaste');
appendEditorTitleContextMenuItem(COPY_RELATIVE_PATH_COMMAND_ID, copyRelativePathCommand.title, ResourceContextKey.IsFileSystemResource, '1_cutcopypaste');
-appendEditorTitleContextMenuItem(REVEAL_IN_OS_COMMAND_ID, REVEAL_IN_OS_LABEL, ResourceContextKey.Scheme.isEqualTo(Schemas.file));
-appendEditorTitleContextMenuItem(REVEAL_IN_OS_COMMAND_ID, REVEAL_IN_OS_LABEL, ContextKeyExpr.and(IsWebContext.toNegated(), ResourceContextKey.Scheme.isEqualTo(Schemas.userData)));
appendEditorTitleContextMenuItem(REVEAL_IN_EXPLORER_COMMAND_ID, nls.localize('revealInSideBar', "Reveal in Side Bar"), ResourceContextKey.IsFileSystemResource);
-function appendEditorTitleContextMenuItem(id: string, title: string, when: ContextKeyExpr | undefined, group?: string): void {
+export function appendEditorTitleContextMenuItem(id: string, title: string, when: ContextKeyExpr | undefined, group?: string): void {
// Menu
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, {
@@ -202,7 +208,7 @@ function appendSaveConflictEditorTitleAction(id: string, title: string, iconLoca
// Menu registration - command palette
-function appendToCommandPalette(id: string, title: ILocalizedString, category: ILocalizedString, when?: ContextKeyExpr): void {
+export function appendToCommandPalette(id: string, title: ILocalizedString, category: ILocalizedString, when?: ContextKeyExpr): void {
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id,
@@ -222,7 +228,6 @@ appendToCommandPalette(SAVE_ALL_IN_GROUP_COMMAND_ID, { value: nls.localize('save
appendToCommandPalette(SAVE_FILES_COMMAND_ID, { value: nls.localize('saveFiles', "Save All Files"), original: 'Save All Files' }, category);
appendToCommandPalette(REVERT_FILE_COMMAND_ID, { value: nls.localize('revert', "Revert File"), original: 'Revert File' }, category);
appendToCommandPalette(COMPARE_WITH_SAVED_COMMAND_ID, { value: nls.localize('compareActiveWithSaved', "Compare Active File with Saved"), original: 'Compare Active File with Saved' }, category);
-appendToCommandPalette(REVEAL_IN_OS_COMMAND_ID, { value: REVEAL_IN_OS_LABEL, original: isWindows ? 'Reveal in Explorer' : isMacintosh ? 'Reveal in Finder' : 'Open Containing Folder' }, category);
appendToCommandPalette(SAVE_FILE_AS_COMMAND_ID, { value: SAVE_FILE_AS_LABEL, original: 'Save As...' }, category);
appendToCommandPalette(CLOSE_EDITOR_COMMAND_ID, { value: nls.localize('closeEditor', "Close Editor"), original: 'Close Editor' }, { value: nls.localize('view', "View"), original: 'View' });
appendToCommandPalette(NEW_FILE_COMMAND_ID, { value: NEW_FILE_LABEL, original: 'New File' }, category, WorkspaceFolderCountContext.notEqualsTo('0'));
@@ -242,17 +247,6 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, {
when: ResourceContextKey.IsFileSystemResource
});
-const revealInOsCommand = {
- id: REVEAL_IN_OS_COMMAND_ID,
- title: isWindows ? nls.localize('revealInWindows', "Reveal in Explorer") : isMacintosh ? nls.localize('revealInMac', "Reveal in Finder") : nls.localize('openContainer', "Open Containing Folder")
-};
-MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, {
- group: 'navigation',
- order: 20,
- command: revealInOsCommand,
- when: ResourceContextKey.IsFileSystemResource
-});
-
MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, {
group: '1_cutcopypaste',
order: 10,
@@ -420,13 +414,6 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
when: ContextKeyExpr.and(ExplorerFolderContext.toNegated(), ResourceContextKey.HasResource)
});
-MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
- group: 'navigation',
- order: 20,
- command: revealInOsCommand,
- when: ResourceContextKey.Scheme.isEqualTo(Schemas.file)
-});
-
MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
group: '3_compare',
order: 20,
diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts
index 2ee93fec213..98363ee5a05 100644
--- a/src/vs/workbench/contrib/files/browser/fileCommands.ts
+++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts
@@ -25,9 +25,8 @@ import { IEditorViewState } from 'vs/editor/common/editorCommon';
import { getCodeEditor } from 'vs/editor/browser/editorBrowser';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes';
-import { isWindows, isMacintosh } from 'vs/base/common/platform';
+import { isWindows } from 'vs/base/common/platform';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
-import { sequence } from 'vs/base/common/async';
import { getResourceForCommand, getMultiSelectedResources } from 'vs/workbench/contrib/files/browser/files';
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
import { getMultiSelectedEditorContexts } from 'vs/workbench/browser/parts/editor/editorCommands';
@@ -46,8 +45,6 @@ import { withUndefinedAsNull } from 'vs/base/common/types';
// Commands
-export const REVEAL_IN_OS_COMMAND_ID = 'revealFileInOS';
-export const REVEAL_IN_OS_LABEL = isWindows ? nls.localize('revealInWindows', "Reveal in Explorer") : isMacintosh ? nls.localize('revealInMac', "Reveal in Finder") : nls.localize('openContainer', "Open Containing Folder");
export const REVEAL_IN_EXPLORER_COMMAND_ID = 'revealInExplorer';
export const REVERT_FILE_COMMAND_ID = 'workbench.action.files.revert';
export const OPEN_TO_SIDE_COMMAND_ID = 'explorer.openToSide';
@@ -406,44 +403,6 @@ CommandsRegistry.registerCommand({
}
});
-function revealResourcesInOS(resources: URI[], windowsService: IWindowsService, notificationService: INotificationService, workspaceContextService: IWorkspaceContextService): void {
- if (resources.length) {
- sequence(resources.map(r => () => windowsService.showItemInFolder(r.scheme === Schemas.userData ? r.with({ scheme: Schemas.file }) : r)));
- } else if (workspaceContextService.getWorkspace().folders.length) {
- windowsService.showItemInFolder(workspaceContextService.getWorkspace().folders[0].uri);
- } else {
- notificationService.info(nls.localize('openFileToReveal', "Open a file first to reveal"));
- }
-}
-
-KeybindingsRegistry.registerCommandAndKeybindingRule({
- id: REVEAL_IN_OS_COMMAND_ID,
- weight: KeybindingWeight.WorkbenchContrib,
- when: EditorContextKeys.focus.toNegated(),
- primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_R,
- win: {
- primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_R
- },
- handler: (accessor: ServicesAccessor, resource: URI | object) => {
- const resources = getMultiSelectedResources(resource, accessor.get(IListService), accessor.get(IEditorService));
- revealResourcesInOS(resources, accessor.get(IWindowsService), accessor.get(INotificationService), accessor.get(IWorkspaceContextService));
- }
-});
-
-KeybindingsRegistry.registerCommandAndKeybindingRule({
- weight: KeybindingWeight.WorkbenchContrib,
- when: undefined,
- primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_R),
- id: 'workbench.action.files.revealActiveFileInWindows',
- handler: (accessor: ServicesAccessor) => {
- const editorService = accessor.get(IEditorService);
- const activeInput = editorService.activeEditor;
- const resource = activeInput ? activeInput.getResource() : null;
- const resources = resource ? [resource] : [];
- revealResourcesInOS(resources, accessor.get(IWindowsService), accessor.get(INotificationService), accessor.get(IWorkspaceContextService));
- }
-});
-
async function resourcesToClipboard(resources: URI[], relative: boolean, clipboardService: IClipboardService, notificationService: INotificationService, labelService: ILabelService): Promise {
if (resources.length) {
const lineDelimiter = isWindows ? '\r\n' : '\n';
diff --git a/src/vs/workbench/contrib/files/electron-browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/electron-browser/fileActions.contribution.ts
new file mode 100644
index 00000000000..20be6820e3f
--- /dev/null
+++ b/src/vs/workbench/contrib/files/electron-browser/fileActions.contribution.ts
@@ -0,0 +1,83 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import * as nls from 'vs/nls';
+import { URI } from 'vs/base/common/uri';
+import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
+import { isWindows, isMacintosh } from 'vs/base/common/platform';
+import { Schemas } from 'vs/base/common/network';
+import { INotificationService } from 'vs/platform/notification/common/notification';
+import { IElectronService } from 'vs/platform/electron/node/electron';
+import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
+import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
+import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes';
+import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
+import { getMultiSelectedResources } from 'vs/workbench/contrib/files/browser/files';
+import { IListService } from 'vs/platform/list/browser/listService';
+import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
+import { revealResourcesInOS } from 'vs/workbench/contrib/files/electron-browser/fileCommands';
+import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
+import { ResourceContextKey } from 'vs/workbench/common/resources';
+import { appendToCommandPalette, appendEditorTitleContextMenuItem } from 'vs/workbench/contrib/files/browser/fileActions.contribution';
+
+const REVEAL_IN_OS_COMMAND_ID = 'revealFileInOS';
+const REVEAL_IN_OS_LABEL = isWindows ? nls.localize('revealInWindows', "Reveal in Explorer") : isMacintosh ? nls.localize('revealInMac', "Reveal in Finder") : nls.localize('openContainer', "Open Containing Folder");
+
+KeybindingsRegistry.registerCommandAndKeybindingRule({
+ id: REVEAL_IN_OS_COMMAND_ID,
+ weight: KeybindingWeight.WorkbenchContrib,
+ when: EditorContextKeys.focus.toNegated(),
+ primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_R,
+ win: {
+ primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_R
+ },
+ handler: (accessor: ServicesAccessor, resource: URI | object) => {
+ const resources = getMultiSelectedResources(resource, accessor.get(IListService), accessor.get(IEditorService));
+ revealResourcesInOS(resources, accessor.get(IElectronService), accessor.get(INotificationService), accessor.get(IWorkspaceContextService));
+ }
+});
+
+KeybindingsRegistry.registerCommandAndKeybindingRule({
+ weight: KeybindingWeight.WorkbenchContrib,
+ when: undefined,
+ primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_R),
+ id: 'workbench.action.files.revealActiveFileInWindows',
+ handler: (accessor: ServicesAccessor) => {
+ const editorService = accessor.get(IEditorService);
+ const activeInput = editorService.activeEditor;
+ const resource = activeInput ? activeInput.getResource() : null;
+ const resources = resource ? [resource] : [];
+ revealResourcesInOS(resources, accessor.get(IElectronService), accessor.get(INotificationService), accessor.get(IWorkspaceContextService));
+ }
+});
+
+appendEditorTitleContextMenuItem(REVEAL_IN_OS_COMMAND_ID, REVEAL_IN_OS_LABEL, ResourceContextKey.Scheme.isEqualTo(Schemas.file));
+
+// Menu registration - open editors
+
+const revealInOsCommand = {
+ id: REVEAL_IN_OS_COMMAND_ID,
+ title: isWindows ? nls.localize('revealInWindows', "Reveal in Explorer") : isMacintosh ? nls.localize('revealInMac', "Reveal in Finder") : nls.localize('openContainer', "Open Containing Folder")
+};
+MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, {
+ group: 'navigation',
+ order: 20,
+ command: revealInOsCommand,
+ when: ResourceContextKey.IsFileSystemResource
+});
+
+// Menu registration - explorer
+
+MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
+ group: 'navigation',
+ order: 20,
+ command: revealInOsCommand,
+ when: ResourceContextKey.Scheme.isEqualTo(Schemas.file)
+});
+
+// Command Palette
+
+const category = { value: nls.localize('filesCategory', "File"), original: 'File' };
+appendToCommandPalette(REVEAL_IN_OS_COMMAND_ID, { value: REVEAL_IN_OS_LABEL, original: isWindows ? 'Reveal in Explorer' : isMacintosh ? 'Reveal in Finder' : 'Open Containing Folder' }, category);
diff --git a/src/vs/workbench/contrib/files/electron-browser/fileCommands.ts b/src/vs/workbench/contrib/files/electron-browser/fileCommands.ts
new file mode 100644
index 00000000000..0a6e710462c
--- /dev/null
+++ b/src/vs/workbench/contrib/files/electron-browser/fileCommands.ts
@@ -0,0 +1,31 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import * as nls from 'vs/nls';
+import { URI } from 'vs/base/common/uri';
+import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
+import { sequence } from 'vs/base/common/async';
+import { Schemas } from 'vs/base/common/network';
+import { INotificationService } from 'vs/platform/notification/common/notification';
+import { IElectronService } from 'vs/platform/electron/node/electron';
+
+// Commands
+
+export function revealResourcesInOS(resources: URI[], electronService: IElectronService, notificationService: INotificationService, workspaceContextService: IWorkspaceContextService): void {
+ if (resources.length) {
+ sequence(resources.map(r => async () => {
+ if (r.scheme === Schemas.file) {
+ electronService.showItemInFolder(r.fsPath);
+ }
+ }));
+ } else if (workspaceContextService.getWorkspace().folders.length) {
+ const uri = workspaceContextService.getWorkspace().folders[0].uri;
+ if (uri.scheme === Schemas.file) {
+ electronService.showItemInFolder(uri.fsPath);
+ }
+ } else {
+ notificationService.info(nls.localize('openFileToReveal', "Open a file first to reveal"));
+ }
+}
diff --git a/src/vs/workbench/contrib/logs/common/logs.contribution.ts b/src/vs/workbench/contrib/logs/common/logs.contribution.ts
index 3de9142da6b..ab29875c3c3 100644
--- a/src/vs/workbench/contrib/logs/common/logs.contribution.ts
+++ b/src/vs/workbench/contrib/logs/common/logs.contribution.ts
@@ -8,7 +8,7 @@ import { join } from 'vs/base/common/path';
import { Registry } from 'vs/platform/registry/common/platform';
import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
-import { SetLogLevelAction, OpenLogsFolderAction, OpenWindowSessionLogFileAction } from 'vs/workbench/contrib/logs/common/logsActions';
+import { SetLogLevelAction, OpenWindowSessionLogFileAction } from 'vs/workbench/contrib/logs/common/logsActions';
import * as Constants from 'vs/workbench/contrib/logs/common/logConstants';
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
@@ -64,10 +64,6 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution {
};
registerTelemetryChannel(this.logService.getLevel());
this.logService.onDidChangeLogLevel(registerTelemetryChannel);
-
- const workbenchActionsRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions);
- const devCategory = nls.localize('developer', "Developer");
- workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenLogsFolderAction, OpenLogsFolderAction.ID, OpenLogsFolderAction.LABEL), 'Developer: Open Logs Folder', devCategory);
}
private async registerLogChannel(id: string, label: string, file: URI): Promise {
diff --git a/src/vs/workbench/contrib/logs/common/logsActions.ts b/src/vs/workbench/contrib/logs/common/logsActions.ts
index 00a100d4c64..34e9eed4475 100644
--- a/src/vs/workbench/contrib/logs/common/logsActions.ts
+++ b/src/vs/workbench/contrib/logs/common/logsActions.ts
@@ -5,9 +5,6 @@
import * as nls from 'vs/nls';
import { Action } from 'vs/base/common/actions';
-import { join } from 'vs/base/common/path';
-import { IEnvironmentService } from 'vs/platform/environment/common/environment';
-import { IWindowsService } from 'vs/platform/windows/common/windows';
import { ILogService, LogLevel, DEFAULT_LOG_LEVEL } from 'vs/platform/log/common/log';
import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { URI } from 'vs/base/common/uri';
@@ -16,23 +13,6 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/
import { dirname, basename, isEqual } from 'vs/base/common/resources';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
-export class OpenLogsFolderAction extends Action {
-
- static ID = 'workbench.action.openLogsFolder';
- static LABEL = nls.localize('openLogsFolder', "Open Logs Folder");
-
- constructor(id: string, label: string,
- @IEnvironmentService private readonly environmentService: IEnvironmentService,
- @IWindowsService private readonly windowsService: IWindowsService,
- ) {
- super(id, label);
- }
-
- run(): Promise {
- return this.windowsService.showItemInFolder(URI.file(join(this.environmentService.logsPath, 'main.log')));
- }
-}
-
export class SetLogLevelAction extends Action {
static ID = 'workbench.action.setLogLevel';
diff --git a/src/vs/workbench/contrib/logs/electron-browser/logs.contribution.ts b/src/vs/workbench/contrib/logs/electron-browser/logs.contribution.ts
new file mode 100644
index 00000000000..f6ceef58f71
--- /dev/null
+++ b/src/vs/workbench/contrib/logs/electron-browser/logs.contribution.ts
@@ -0,0 +1,14 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import * as nls from 'vs/nls';
+import { Registry } from 'vs/platform/registry/common/platform';
+import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions';
+import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
+import { OpenLogsFolderAction } from 'vs/workbench/contrib/logs/electron-browser/logsActions';
+
+const workbenchActionsRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions);
+const devCategory = nls.localize('developer', "Developer");
+workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenLogsFolderAction, OpenLogsFolderAction.ID, OpenLogsFolderAction.LABEL), 'Developer: Open Logs Folder', devCategory);
diff --git a/src/vs/workbench/contrib/logs/electron-browser/logsActions.ts b/src/vs/workbench/contrib/logs/electron-browser/logsActions.ts
new file mode 100644
index 00000000000..547558b7deb
--- /dev/null
+++ b/src/vs/workbench/contrib/logs/electron-browser/logsActions.ts
@@ -0,0 +1,28 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import * as nls from 'vs/nls';
+import { Action } from 'vs/base/common/actions';
+import { join } from 'vs/base/common/path';
+import { IEnvironmentService } from 'vs/platform/environment/common/environment';
+import { URI } from 'vs/base/common/uri';
+import { IElectronService } from 'vs/platform/electron/node/electron';
+
+export class OpenLogsFolderAction extends Action {
+
+ static ID = 'workbench.action.openLogsFolder';
+ static LABEL = nls.localize('openLogsFolder', "Open Logs Folder");
+
+ constructor(id: string, label: string,
+ @IEnvironmentService private readonly environmentService: IEnvironmentService,
+ @IElectronService private readonly electronService: IElectronService,
+ ) {
+ super(id, label);
+ }
+
+ run(): Promise {
+ return this.electronService.showItemInFolder(URI.file(join(this.environmentService.logsPath, 'main.log')).fsPath);
+ }
+}
diff --git a/src/vs/workbench/contrib/markers/browser/markersPanel.ts b/src/vs/workbench/contrib/markers/browser/markersPanel.ts
index bbc4392c7f7..01ef11a2cd4 100644
--- a/src/vs/workbench/contrib/markers/browser/markersPanel.ts
+++ b/src/vs/workbench/contrib/markers/browser/markersPanel.ts
@@ -117,7 +117,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController {
this.rangeHighlightDecorations = this._register(this.instantiationService.createInstance(RangeHighlightDecorations));
// actions
- this.collapseAllAction = this._register(new Action('vs.tree.collapse', localize('collapseAll', "Collapse All"), 'monaco-tree-action collapse-all', true, async () => this.collapseAll()));
+ this.collapseAllAction = this._register(new Action('vs.tree.collapse', localize('collapseAll', "Collapse All"), 'monaco-tree-action codicon-collapse-all', true, async () => this.collapseAll()));
this.filterAction = this._register(this.instantiationService.createInstance(MarkersFilterAction, { filterText: this.panelState['filter'] || '', filterHistory: this.panelState['filterHistory'] || [], useFilesExclude: !!this.panelState['useFilesExclude'] }));
}
diff --git a/src/vs/workbench/contrib/markers/browser/markersPanelActions.ts b/src/vs/workbench/contrib/markers/browser/markersPanelActions.ts
index c45dee82e5d..1bc91c2d7cf 100644
--- a/src/vs/workbench/contrib/markers/browser/markersPanelActions.ts
+++ b/src/vs/workbench/contrib/markers/browser/markersPanelActions.ts
@@ -213,7 +213,7 @@ export class MarkersFilterActionViewItem extends BaseActionViewItem {
private createFilesExcludeCheckbox(container: HTMLElement): void {
const filesExcludeFilter = this._register(new Checkbox({
- actionClassName: 'markers-panel-filter-filesExclude',
+ actionClassName: 'codicon codicon-exclude',
title: this.action.useFilesExclude ? Messages.MARKERS_PANEL_ACTION_TOOLTIP_DO_NOT_USE_FILES_EXCLUDE : Messages.MARKERS_PANEL_ACTION_TOOLTIP_USE_FILES_EXCLUDE,
isChecked: this.action.useFilesExclude
}));
diff --git a/src/vs/workbench/contrib/markers/browser/media/exclude-settings-dark.svg b/src/vs/workbench/contrib/markers/browser/media/exclude-settings-dark.svg
deleted file mode 100644
index 0b1694dc2f1..00000000000
--- a/src/vs/workbench/contrib/markers/browser/media/exclude-settings-dark.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/markers/browser/media/exclude-settings-hc.svg b/src/vs/workbench/contrib/markers/browser/media/exclude-settings-hc.svg
deleted file mode 100644
index ba88235419a..00000000000
--- a/src/vs/workbench/contrib/markers/browser/media/exclude-settings-hc.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/markers/browser/media/exclude-settings-light.svg b/src/vs/workbench/contrib/markers/browser/media/exclude-settings-light.svg
deleted file mode 100644
index 114ec3f0fec..00000000000
--- a/src/vs/workbench/contrib/markers/browser/media/exclude-settings-light.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/markers/browser/media/markers.css b/src/vs/workbench/contrib/markers/browser/media/markers.css
index 42321b8dba4..c8e96443126 100644
--- a/src/vs/workbench/contrib/markers/browser/media/markers.css
+++ b/src/vs/workbench/contrib/markers/browser/media/markers.css
@@ -56,19 +56,6 @@
display: none;
}
-.vs .markers-panel-action-filter > .markers-panel-filter-controls > .markers-panel-filter-filesExclude {
- background: url('exclude-settings-light.svg') center center no-repeat;
-}
-
-.vs-dark .markers-panel-action-filter > .markers-panel-filter-controls > .markers-panel-filter-filesExclude,
-.hc-black .markers-panel-action-filter > .markers-panel-filter-controls > .markers-panel-filter-filesExclude {
- background: url('exclude-settings-dark.svg') center center no-repeat;
-}
-
-.hc-black .markers-panel-action-filter > .markers-panel-filter-controls > .markers-panel-filter-filesExclude {
- background: url('exclude-settings-hc.svg') center center no-repeat;
-}
-
.markers-panel .markers-panel-container {
height: 100%;
}
diff --git a/src/vs/workbench/contrib/output/browser/media/clear-dark.svg b/src/vs/workbench/contrib/output/browser/media/clear-dark.svg
deleted file mode 100644
index 04d64ab41ca..00000000000
--- a/src/vs/workbench/contrib/output/browser/media/clear-dark.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/output/browser/media/clear-hc.svg b/src/vs/workbench/contrib/output/browser/media/clear-hc.svg
deleted file mode 100644
index 44a41edd3b3..00000000000
--- a/src/vs/workbench/contrib/output/browser/media/clear-hc.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/output/browser/media/clear-light.svg b/src/vs/workbench/contrib/output/browser/media/clear-light.svg
deleted file mode 100644
index f6a51c856f0..00000000000
--- a/src/vs/workbench/contrib/output/browser/media/clear-light.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/output/browser/media/locked-dark.svg b/src/vs/workbench/contrib/output/browser/media/locked-dark.svg
deleted file mode 100644
index ebdc1c0078a..00000000000
--- a/src/vs/workbench/contrib/output/browser/media/locked-dark.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/output/browser/media/locked-hc.svg b/src/vs/workbench/contrib/output/browser/media/locked-hc.svg
deleted file mode 100644
index 350dc417710..00000000000
--- a/src/vs/workbench/contrib/output/browser/media/locked-hc.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/output/browser/media/locked-light.svg b/src/vs/workbench/contrib/output/browser/media/locked-light.svg
deleted file mode 100644
index 03b03513688..00000000000
--- a/src/vs/workbench/contrib/output/browser/media/locked-light.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/output/browser/media/open-file-dark.svg b/src/vs/workbench/contrib/output/browser/media/open-file-dark.svg
deleted file mode 100644
index ed302ae1398..00000000000
--- a/src/vs/workbench/contrib/output/browser/media/open-file-dark.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/output/browser/media/open-file-hc.svg b/src/vs/workbench/contrib/output/browser/media/open-file-hc.svg
deleted file mode 100644
index bba63494e6f..00000000000
--- a/src/vs/workbench/contrib/output/browser/media/open-file-hc.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/output/browser/media/open-file-light.svg b/src/vs/workbench/contrib/output/browser/media/open-file-light.svg
deleted file mode 100644
index 392a840c5ef..00000000000
--- a/src/vs/workbench/contrib/output/browser/media/open-file-light.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/output/browser/media/output.css b/src/vs/workbench/contrib/output/browser/media/output.css
deleted file mode 100644
index 0f19102cc38..00000000000
--- a/src/vs/workbench/contrib/output/browser/media/output.css
+++ /dev/null
@@ -1,52 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-.monaco-workbench .output-action.clear-output {
- background: url('clear-light.svg') center center no-repeat;
-}
-
-.vs-dark .monaco-workbench .output-action.clear-output {
- background: url('clear-dark.svg') center center no-repeat;
-}
-
-.hc-black .monaco-workbench .output-action.clear-output {
- background: url('clear-hc.svg') center center no-repeat;
-}
-
-.monaco-workbench .output-action.output-scroll-lock {
- background: url('locked-light.svg') center center no-repeat;
-}
-
-.vs-dark .monaco-workbench .output-action.output-scroll-lock {
- background: url('locked-dark.svg') center center no-repeat;
-}
-
-.hc-black .monaco-workbench .output-action.output-scroll-lock {
- background: url('locked-hc.svg') center center no-repeat;
-}
-
-.monaco-workbench .output-action.output-scroll-unlock {
- background: url('unlocked-light.svg') center center no-repeat;
-}
-
-.vs-dark .monaco-workbench .output-action.output-scroll-unlock {
- background: url('unlocked-dark.svg') center center no-repeat;
-}
-
-.hc-black .monaco-workbench .output-action.output-scroll-unlock {
- background: url('unlocked-hc.svg') center center no-repeat;
-}
-
-.monaco-workbench .output-action.open-log-file {
- background: url('open-file-light.svg') center center no-repeat;
-}
-
-.vs-dark .monaco-workbench .output-action.open-log-file {
- background: url('open-file-dark.svg') center center no-repeat;
-}
-
-.hc-black .monaco-workbench .output-action.open-log-file {
- background: url('open-file-hc.svg') center center no-repeat;
-}
diff --git a/src/vs/workbench/contrib/output/browser/media/unlocked-dark.svg b/src/vs/workbench/contrib/output/browser/media/unlocked-dark.svg
deleted file mode 100644
index 3b7947f9752..00000000000
--- a/src/vs/workbench/contrib/output/browser/media/unlocked-dark.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/output/browser/media/unlocked-hc.svg b/src/vs/workbench/contrib/output/browser/media/unlocked-hc.svg
deleted file mode 100644
index 100cfc1385a..00000000000
--- a/src/vs/workbench/contrib/output/browser/media/unlocked-hc.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/output/browser/media/unlocked-light.svg b/src/vs/workbench/contrib/output/browser/media/unlocked-light.svg
deleted file mode 100644
index 0c3cb4b93b0..00000000000
--- a/src/vs/workbench/contrib/output/browser/media/unlocked-light.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/output/browser/outputActions.ts b/src/vs/workbench/contrib/output/browser/outputActions.ts
index 561891fc62e..3760d018bf5 100644
--- a/src/vs/workbench/contrib/output/browser/outputActions.ts
+++ b/src/vs/workbench/contrib/output/browser/outputActions.ts
@@ -45,7 +45,7 @@ export class ClearOutputAction extends Action {
id: string, label: string,
@IOutputService private readonly outputService: IOutputService
) {
- super(id, label, 'output-action clear-output');
+ super(id, label, 'output-action codicon-clear-all');
}
public run(): Promise {
@@ -67,7 +67,7 @@ export class ToggleOrSetOutputScrollLockAction extends Action {
public static readonly LABEL = nls.localize({ key: 'toggleOutputScrollLock', comment: ['Turn on / off automatic output scrolling'] }, "Toggle Output Scroll Lock");
constructor(id: string, label: string, @IOutputService private readonly outputService: IOutputService) {
- super(id, label, 'output-action output-scroll-unlock');
+ super(id, label, 'output-action codicon-unlock');
this._register(this.outputService.onActiveOutputChannel(channel => {
const activeChannel = this.outputService.getActiveChannel();
if (activeChannel) {
@@ -94,10 +94,10 @@ export class ToggleOrSetOutputScrollLockAction extends Action {
private setClassAndLabel(locked: boolean) {
if (locked) {
- this.class = 'output-action output-scroll-lock';
+ this.class = 'output-action codicon-lock';
this.label = nls.localize('outputScrollOn', "Turn Auto Scrolling On");
} else {
- this.class = 'output-action output-scroll-unlock';
+ this.class = 'output-action codicon-unlock';
this.label = nls.localize('outputScrollOff', "Turn Auto Scrolling Off");
}
}
@@ -185,7 +185,7 @@ export class OpenLogOutputFile extends Action {
@IEditorService private readonly editorService: IEditorService,
@IInstantiationService private readonly instantiationService: IInstantiationService
) {
- super(OpenLogOutputFile.ID, OpenLogOutputFile.LABEL, 'output-action open-log-file');
+ super(OpenLogOutputFile.ID, OpenLogOutputFile.LABEL, 'output-action codicon-go-to-file');
this._register(this.outputService.onActiveOutputChannel(this.update, this));
this.update();
}
diff --git a/src/vs/workbench/contrib/output/browser/outputPanel.ts b/src/vs/workbench/contrib/output/browser/outputPanel.ts
index f1c0b211d32..9f58f043851 100644
--- a/src/vs/workbench/contrib/output/browser/outputPanel.ts
+++ b/src/vs/workbench/contrib/output/browser/outputPanel.ts
@@ -3,7 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import 'vs/css!./media/output';
import * as nls from 'vs/nls';
import { Action, IAction } from 'vs/base/common/actions';
import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
diff --git a/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts b/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts
index daa2adf2ccd..0509b63e0d3 100644
--- a/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts
+++ b/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts
@@ -18,6 +18,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { URI } from 'vs/base/common/uri';
import { IOpenerService } from 'vs/platform/opener/common/opener';
+import { IElectronService } from 'vs/platform/electron/node/electron';
export class StartupProfiler implements IWorkbenchContribution {
@@ -29,7 +30,8 @@ export class StartupProfiler implements IWorkbenchContribution {
@IClipboardService private readonly _clipboardService: IClipboardService,
@ILifecycleService lifecycleService: ILifecycleService,
@IExtensionService extensionService: IExtensionService,
- @IOpenerService private readonly _openerService: IOpenerService
+ @IOpenerService private readonly _openerService: IOpenerService,
+ @IElectronService private readonly _electronService: IElectronService
) {
// wait for everything to be ready
Promise.all([
@@ -81,7 +83,7 @@ export class StartupProfiler implements IWorkbenchContribution {
}).then(res => {
if (res.confirmed) {
Promise.all([
- this._windowsService.showItemInFolder(URI.file(join(dir, files[0]))),
+ this._electronService.showItemInFolder(URI.file(join(dir, files[0])).fsPath),
this._createPerfIssue(files)
]).then(() => {
// keep window stable until restart is selected
diff --git a/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts b/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts
index c4d1c7cd4c1..e0c7f7f6bd2 100644
--- a/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts
+++ b/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts
@@ -21,6 +21,7 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { didUseCachedData, ITimerService } from 'vs/workbench/services/timer/electron-browser/timerService';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { getEntries } from 'vs/base/common/performance';
+import { IHostService } from 'vs/workbench/services/host/browser/host';
export class StartupTimings implements IWorkbenchContribution {
@@ -34,6 +35,7 @@ export class StartupTimings implements IWorkbenchContribution {
@ILifecycleService private readonly _lifecycleService: ILifecycleService,
@IUpdateService private readonly _updateService: IUpdateService,
@IEnvironmentService private readonly _envService: IEnvironmentService,
+ @IHostService private readonly _hostService: IHostService
) {
//
this._report().catch(onUnexpectedError);
@@ -91,7 +93,7 @@ export class StartupTimings implements IWorkbenchContribution {
if (this._lifecycleService.startupKind !== StartupKind.NewWindow) {
return false;
}
- if (await this._windowsService.getWindowCount() !== 1) {
+ if (await this._hostService.windowCount !== 1) {
return false;
}
const activeViewlet = this._viewletService.getActiveViewlet();
diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts
index 8ec38b3795e..1f4297c039f 100644
--- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts
+++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts
@@ -11,7 +11,7 @@ import { OperatingSystem, isWeb } from 'vs/base/common/platform';
import { Schemas } from 'vs/base/common/network';
import { IRemoteAgentService, RemoteExtensionLogFileName } from 'vs/workbench/services/remote/common/remoteAgentService';
import { ILogService } from 'vs/platform/log/common/log';
-import { LogLevelSetterChannelClient } from 'vs/platform/log/common/logIpc';
+import { LoggerChannelClient } from 'vs/platform/log/common/logIpc';
import { IOutputChannelRegistry, Extensions as OutputExt, } from 'vs/workbench/contrib/output/common/output';
import { localize } from 'vs/nls';
import { joinPath } from 'vs/base/common/resources';
@@ -79,9 +79,9 @@ class RemoteChannelsContribution extends Disposable implements IWorkbenchContrib
super();
const connection = remoteAgentService.getConnection();
if (connection) {
- const logLevelClient = new LogLevelSetterChannelClient(connection.getChannel('loglevel'));
- logLevelClient.setLevel(logService.getLevel());
- this._register(logService.onDidChangeLogLevel(level => logLevelClient.setLevel(level)));
+ const loggerClient = new LoggerChannelClient(connection.getChannel('logger'));
+ loggerClient.setLevel(logService.getLevel());
+ this._register(logService.onDidChangeLogLevel(level => loggerClient.setLevel(level)));
}
}
}
diff --git a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts
index 37cae45ace2..3ba4103fd34 100644
--- a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts
+++ b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts
@@ -12,7 +12,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { isMacintosh } from 'vs/base/common/platform';
import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
-import { MenuId, IMenuService, MenuItemAction, IMenu, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions';
+import { MenuId, IMenuService, MenuItemAction, IMenu, MenuRegistry } from 'vs/platform/actions/common/actions';
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchContributionsExtensions } from 'vs/workbench/common/contributions';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { StatusbarAlignment, IStatusbarService, IStatusbarEntryAccessor, IStatusbarEntry } from 'vs/platform/statusbar/common/statusbar';
@@ -26,15 +26,13 @@ import { ILogService } from 'vs/platform/log/common/log';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { DialogChannel } from 'vs/platform/dialogs/electron-browser/dialogIpc';
import { DownloadServiceChannel } from 'vs/platform/download/common/downloadIpc';
-import { LogLevelSetterChannel } from 'vs/platform/log/common/logIpc';
+import { LoggerChannel } from 'vs/platform/log/common/logIpc';
import { ipcRenderer as ipc } from 'electron';
import { IDiagnosticInfoOptions, IRemoteDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnostics';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { PersistentConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
-import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
-import { OpenFileFolderAction, OpenFileAction, OpenFolderAction } from 'vs/workbench/browser/actions/workspaceActions';
import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { IWindowsService } from 'vs/platform/windows/common/windows';
import { RemoteConnectionState, Deprecated_RemoteAuthorityContext, RemoteFileDialogContext } from 'vs/workbench/browser/contextkeys';
@@ -229,7 +227,7 @@ class RemoteChannelsContribution implements IWorkbenchContribution {
if (connection) {
connection.registerChannel('dialog', new DialogChannel(dialogService));
connection.registerChannel('download', new DownloadServiceChannel(downloadService));
- connection.registerChannel('loglevel', new LogLevelSetterChannel(logService));
+ connection.registerChannel('logger', new LoggerChannel(logService));
}
}
}
@@ -377,11 +375,7 @@ Registry.as(ConfigurationExtensions.Configuration)
}
});
-const registry = Registry.as(ActionExtensions.WorkbenchActions);
-const fileCategory = nls.localize('file', "File");
-
if (isMacintosh) {
- registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileFolderAction, OpenFileFolderAction.ID, OpenFileFolderAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }), 'File: Open...', fileCategory);
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: OpenLocalFileFolderCommand.ID,
weight: KeybindingWeight.WorkbenchContrib,
@@ -391,8 +385,6 @@ if (isMacintosh) {
handler: OpenLocalFileFolderCommand.handler()
});
} else {
- registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileAction, OpenFileAction.ID, OpenFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }), 'File: Open File...', fileCategory);
- registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFolderAction, OpenFolderAction.ID, OpenFolderAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_O) }), 'File: Open Folder...', fileCategory);
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: OpenLocalFileCommand.ID,
weight: KeybindingWeight.WorkbenchContrib,
diff --git a/src/vs/workbench/contrib/search/browser/media/searchview.css b/src/vs/workbench/contrib/search/browser/media/searchview.css
index f530bce968a..decc6173962 100644
--- a/src/vs/workbench/contrib/search/browser/media/searchview.css
+++ b/src/vs/workbench/contrib/search/browser/media/searchview.css
@@ -86,7 +86,7 @@
height: 25px;
}
-.search-view .search-widget .replace-container .monaco-action-bar .action-item .icon {
+.search-view .search-widget .replace-container .monaco-action-bar .action-item .codicon {
background-repeat: no-repeat;
width: 20px;
height: 25px;
diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts
index 4a97413453c..254346cc052 100644
--- a/src/vs/workbench/contrib/search/browser/searchWidget.ts
+++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts
@@ -364,11 +364,12 @@ export class SearchWidget extends Widget {
}
}));
- let controls = document.createElement('div');
+ const controls = document.createElement('div');
controls.className = 'controls';
controls.style.display = 'block';
controls.appendChild(this._preserveCase.domNode);
replaceBox.appendChild(controls);
+ this.replaceInput.paddingRight = this._preserveCase.width();
this._register(attachInputBoxStyler(this.replaceInput, this.themeService));
this.onkeydown(this.replaceInput.inputElement, (keyboardEvent) => this.onReplaceInputKeyDown(keyboardEvent));
diff --git a/src/vs/workbench/contrib/terminal/browser/media/kill-dark.svg b/src/vs/workbench/contrib/terminal/browser/media/kill-dark.svg
deleted file mode 100644
index 9831c96a166..00000000000
--- a/src/vs/workbench/contrib/terminal/browser/media/kill-dark.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/terminal/browser/media/kill-hc.svg b/src/vs/workbench/contrib/terminal/browser/media/kill-hc.svg
deleted file mode 100644
index 656f3bd7a42..00000000000
--- a/src/vs/workbench/contrib/terminal/browser/media/kill-hc.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/terminal/browser/media/kill-light.svg b/src/vs/workbench/contrib/terminal/browser/media/kill-light.svg
deleted file mode 100644
index d5ac851860b..00000000000
--- a/src/vs/workbench/contrib/terminal/browser/media/kill-light.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/terminal/browser/media/new-dark.svg b/src/vs/workbench/contrib/terminal/browser/media/new-dark.svg
deleted file mode 100644
index 4d9389336b9..00000000000
--- a/src/vs/workbench/contrib/terminal/browser/media/new-dark.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/terminal/browser/media/new-hc.svg b/src/vs/workbench/contrib/terminal/browser/media/new-hc.svg
deleted file mode 100644
index fb50c6c2849..00000000000
--- a/src/vs/workbench/contrib/terminal/browser/media/new-hc.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/terminal/browser/media/new-light.svg b/src/vs/workbench/contrib/terminal/browser/media/new-light.svg
deleted file mode 100644
index 01a9de7d5ab..00000000000
--- a/src/vs/workbench/contrib/terminal/browser/media/new-light.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/terminal/browser/media/split-editor-horizontal-dark.svg b/src/vs/workbench/contrib/terminal/browser/media/split-editor-horizontal-dark.svg
deleted file mode 100644
index 8c22a7c5bfe..00000000000
--- a/src/vs/workbench/contrib/terminal/browser/media/split-editor-horizontal-dark.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/terminal/browser/media/split-editor-horizontal-hc.svg b/src/vs/workbench/contrib/terminal/browser/media/split-editor-horizontal-hc.svg
deleted file mode 100644
index 82c19d0c8fc..00000000000
--- a/src/vs/workbench/contrib/terminal/browser/media/split-editor-horizontal-hc.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/terminal/browser/media/split-editor-horizontal-light.svg b/src/vs/workbench/contrib/terminal/browser/media/split-editor-horizontal-light.svg
deleted file mode 100644
index 2d53ab6d3c2..00000000000
--- a/src/vs/workbench/contrib/terminal/browser/media/split-editor-horizontal-light.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/terminal/browser/media/split-editor-vertical-dark.svg b/src/vs/workbench/contrib/terminal/browser/media/split-editor-vertical-dark.svg
deleted file mode 100644
index 419c21be4f6..00000000000
--- a/src/vs/workbench/contrib/terminal/browser/media/split-editor-vertical-dark.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/terminal/browser/media/split-editor-vertical-hc.svg b/src/vs/workbench/contrib/terminal/browser/media/split-editor-vertical-hc.svg
deleted file mode 100644
index 7565fd3c168..00000000000
--- a/src/vs/workbench/contrib/terminal/browser/media/split-editor-vertical-hc.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/terminal/browser/media/split-editor-vertical-light.svg b/src/vs/workbench/contrib/terminal/browser/media/split-editor-vertical-light.svg
deleted file mode 100644
index 7e95763b463..00000000000
--- a/src/vs/workbench/contrib/terminal/browser/media/split-editor-vertical-light.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css
index be6edf706dc..d50dd4b9253 100644
--- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css
+++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css
@@ -144,24 +144,6 @@
opacity: 0 !important;
}
-/* Light theme */
-.monaco-workbench .terminal-action.kill { background: url('kill-light.svg') center center no-repeat; }
-.monaco-workbench .terminal-action.new { background: url('new-light.svg') center center no-repeat; }
-.monaco-workbench .terminal-action.split { background: url('split-editor-horizontal-light.svg') center center no-repeat; }
-.monaco-workbench .panel.right .terminal-action.split { background: url('split-editor-vertical-light.svg') center center no-repeat; }
-
-/* Dark theme */
-.vs-dark .monaco-workbench .terminal-action.kill, .hc-black .monaco-workbench .terminal-action.kill { background: url('kill-dark.svg') center center no-repeat; }
-.vs-dark .monaco-workbench .terminal-action.new, .hc-black .monaco-workbench .terminal-action.new { background: url('new-dark.svg') center center no-repeat; }
-.vs-dark .monaco-workbench .terminal-action.split, .hc-black .monaco-workbench .terminal-action.split { background: url('split-editor-horizontal-dark.svg') center center no-repeat; }
-.vs-dark .monaco-workbench .panel.right .terminal-action.split, .hc-black .monaco-workbench .panel.right .terminal-action.split { background: url('split-editor-vertical-dark.svg') center center no-repeat; }
-
-/* HC theme */
-.hc-black .monaco-workbench .terminal-action.kill, .hc-black .monaco-workbench .terminal-action.kill { background: url('kill-hc.svg') center center no-repeat; }
-.hc-black .monaco-workbench .terminal-action.new, .hc-black .monaco-workbench .terminal-action.new { background: url('new-hc.svg') center center no-repeat; }
-.hc-black .monaco-workbench .terminal-action.split, .hc-black .monaco-workbench .terminal-action.split { background: url('split-editor-horizontal-hc.svg') center center no-repeat; }
-.hc-black .monaco-workbench .panel.right .terminal-action.split, .hc-black .monaco-workbench .panel.right .terminal-action.split { background: url('split-editor-vertical-hc.svg') center center no-repeat; }
-
.vs-dark .monaco-workbench.mac .panel.integrated-terminal .terminal-outer-container:not(.alt-active) .terminal:not(.enable-mouse-events),
.hc-black .monaco-workbench.mac .panel.integrated-terminal .terminal-outer-container:not(.alt-active) .terminal:not(.enable-mouse-events) {
cursor: -webkit-image-set(url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAL0lEQVQoz2NgCD3x//9/BhBYBWdhgFVAiVW4JBFKGIa4AqD0//9D3pt4I4tAdAMAHTQ/j5Zom30AAAAASUVORK5CYII=') 1x, url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAAz0lEQVRIx2NgYGBY/R8I/vx5eelX3n82IJ9FxGf6tksvf/8FiTMQAcAGQMDvSwu09abffY8QYSAScNk45G198eX//yev73/4///701eh//kZSARckrNBRvz//+8+6ZohwCzjGNjdgQxkAg7B9WADeBjIBqtJCbhRA0YNoIkBSNmaPEMoNmA0FkYNoFKhapJ6FGyAH3nauaSmPfwI0v/3OukVi0CIZ+F25KrtYcx/CTIy0e+rC7R1Z4KMICVTQQ14feVXIbR695u14+Ir4gwAAD49E54wc1kWAAAAAElFTkSuQmCC') 2x) 5 8, text;
diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts
index 6357326ade2..5c6c751e720 100644
--- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts
+++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts
@@ -105,7 +105,7 @@ export class KillTerminalAction extends Action {
id: string, label: string,
@ITerminalService private readonly terminalService: ITerminalService
) {
- super(id, label, 'terminal-action kill');
+ super(id, label, 'terminal-action codicon-trash');
}
public run(event?: any): Promise {
@@ -336,7 +336,7 @@ export class CreateNewTerminalAction extends Action {
@ICommandService private readonly commandService: ICommandService,
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService
) {
- super(id, label, 'terminal-action new');
+ super(id, label, 'terminal-action codicon-add');
}
public run(event?: any): Promise {
@@ -412,7 +412,7 @@ export class SplitTerminalAction extends Action {
@ICommandService private readonly commandService: ICommandService,
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService
) {
- super(id, label, 'terminal-action split');
+ super(id, label, 'terminal-action codicon-split-horizontal');
}
public run(event?: any): Promise {
diff --git a/src/vs/workbench/contrib/url/common/externalUriResolver.ts b/src/vs/workbench/contrib/url/common/externalUriResolver.ts
new file mode 100644
index 00000000000..454d76644b6
--- /dev/null
+++ b/src/vs/workbench/contrib/url/common/externalUriResolver.ts
@@ -0,0 +1,26 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { Disposable } from 'vs/base/common/lifecycle';
+import { IOpenerService } from 'vs/platform/opener/common/opener';
+import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
+import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
+
+export class ExternalUriResolverContribution extends Disposable implements IWorkbenchContribution {
+ constructor(
+ @IOpenerService _openerService: IOpenerService,
+ @IWorkbenchEnvironmentService _workbenchEnvironmentService: IWorkbenchEnvironmentService,
+ ) {
+ super();
+
+ if (_workbenchEnvironmentService.options && _workbenchEnvironmentService.options.resolveExternalUri) {
+ this._register(_openerService.registerExternalUriResolver({
+ resolveExternalUri: async (resource) => {
+ return _workbenchEnvironmentService.options!.resolveExternalUri!(resource);
+ }
+ }));
+ }
+ }
+}
diff --git a/src/vs/workbench/contrib/url/common/url.contribution.ts b/src/vs/workbench/contrib/url/common/url.contribution.ts
index f51bd6d3f03..e299262de05 100644
--- a/src/vs/workbench/contrib/url/common/url.contribution.ts
+++ b/src/vs/workbench/contrib/url/common/url.contribution.ts
@@ -14,9 +14,10 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { IURLService } from 'vs/platform/url/common/url';
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
+import { ExternalUriResolverContribution } from 'vs/workbench/contrib/url/common/externalUriResolver';
import { configureTrustedDomainSettingsCommand } from 'vs/workbench/contrib/url/common/trustedDomains';
-import { OpenerValidatorContributions } from 'vs/workbench/contrib/url/common/trustedDomainsValidator';
import { TrustedDomainsFileSystemProvider } from 'vs/workbench/contrib/url/common/trustedDomainsFileSystemProvider';
+import { OpenerValidatorContributions } from 'vs/workbench/contrib/url/common/trustedDomainsValidator';
export class OpenUrlAction extends Action {
static readonly ID = 'workbench.action.url.openUrl';
@@ -65,3 +66,7 @@ Registry.as(WorkbenchExtensions.Workbench).regi
TrustedDomainsFileSystemProvider,
LifecyclePhase.Ready
);
+Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(
+ ExternalUriResolverContribution,
+ LifecyclePhase.Ready
+);
diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/browser/telemetryOptOut.ts b/src/vs/workbench/contrib/welcome/gettingStarted/browser/telemetryOptOut.ts
index 9752407f3f8..c942edef9c9 100644
--- a/src/vs/workbench/contrib/welcome/gettingStarted/browser/telemetryOptOut.ts
+++ b/src/vs/workbench/contrib/welcome/gettingStarted/browser/telemetryOptOut.ts
@@ -18,6 +18,7 @@ import { language, locale } from 'vs/base/common/platform';
import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IProductService } from 'vs/platform/product/common/productService';
+import { IHostService } from 'vs/workbench/services/host/browser/host';
export class TelemetryOptOut implements IWorkbenchContribution {
@@ -30,6 +31,7 @@ export class TelemetryOptOut implements IWorkbenchContribution {
@INotificationService private readonly notificationService: INotificationService,
@IWindowService windowService: IWindowService,
@IWindowsService windowsService: IWindowsService,
+ @IHostService hostService: IHostService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IExperimentService private readonly experimentService: IExperimentService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@@ -42,7 +44,7 @@ export class TelemetryOptOut implements IWorkbenchContribution {
const experimentId = 'telemetryOptOut';
Promise.all([
windowService.isFocused(),
- windowsService.getWindowCount(),
+ hostService.windowCount,
experimentService.getExperimentById(experimentId)
]).then(([focused, count, experimentState]) => {
if (!focused && count > 1) {
diff --git a/src/vs/workbench/electron-browser/desktop.main.ts b/src/vs/workbench/electron-browser/desktop.main.ts
index ebf89cda089..076d03a5b1e 100644
--- a/src/vs/workbench/electron-browser/desktop.main.ts
+++ b/src/vs/workbench/electron-browser/desktop.main.ts
@@ -23,9 +23,9 @@ import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron
import { IWindowConfiguration } from 'vs/platform/windows/common/windows';
import { webFrame } from 'electron';
import { ISingleFolderWorkspaceIdentifier, IWorkspaceInitializationPayload, ISingleFolderWorkspaceInitializationPayload, reviveWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
-import { ConsoleLogService, MultiplexLogService, ILogService } from 'vs/platform/log/common/log';
+import { ConsoleLogService, MultiplexLogService, ILogService, ConsoleLogInMainService } from 'vs/platform/log/common/log';
import { StorageService } from 'vs/platform/storage/node/storageService';
-import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc';
+import { LoggerChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc';
import { Schemas } from 'vs/base/common/network';
import { sanitizeFilePath } from 'vs/base/common/extpath';
import { GlobalStorageDatabaseChannelClient } from 'vs/platform/storage/node/storageIpc';
@@ -345,12 +345,25 @@ class CodeRendererMain extends Disposable {
}
private createLogService(mainProcessService: IMainProcessService, environmentService: IWorkbenchEnvironmentService): ILogService {
- const spdlogService = new SpdLogService(`renderer${this.environmentService.configuration.windowId}`, environmentService.logsPath, this.environmentService.configuration.logLevel);
- const consoleLogService = new ConsoleLogService(this.environmentService.configuration.logLevel);
- const logService = new MultiplexLogService([consoleLogService, spdlogService]);
- const logLevelClient = new LogLevelSetterChannelClient(mainProcessService.getChannel('loglevel'));
+ const loggerClient = new LoggerChannelClient(mainProcessService.getChannel('logger'));
- return new FollowerLogService(logLevelClient, logService);
+ // Extension development test CLI: forward everything to main side
+ const loggers: ILogService[] = [];
+ if (environmentService.isExtensionDevelopment && !!environmentService.extensionTestsLocationURI) {
+ loggers.push(
+ new ConsoleLogInMainService(loggerClient, this.environmentService.configuration.logLevel)
+ );
+ }
+
+ // Normal logger: spdylog and console
+ else {
+ loggers.push(
+ new ConsoleLogService(this.environmentService.configuration.logLevel),
+ new SpdLogService(`renderer${this.environmentService.configuration.windowId}`, environmentService.logsPath, this.environmentService.configuration.logLevel)
+ );
+ }
+
+ return new FollowerLogService(loggerClient, new MultiplexLogService(loggers));
}
}
diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts
index d650e0a1594..31cb64f2d5b 100644
--- a/src/vs/workbench/electron-browser/window.ts
+++ b/src/vs/workbench/electron-browser/window.ts
@@ -19,12 +19,12 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView
import { ITitleService } from 'vs/workbench/services/title/common/titleService';
import { IWorkbenchThemeService, VS_HC_THEME } from 'vs/workbench/services/themes/common/workbenchThemeService';
import * as browser from 'vs/base/browser/browser';
-import { ICommandService } from 'vs/platform/commands/common/commands';
+import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands';
import { IResourceInput } from 'vs/platform/editor/common/editor';
import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/nativeKeymapService';
import { ipcRenderer as ipc, webFrame, crashReporter, Event } from 'electron';
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
-import { IMenuService, MenuId, IMenu, MenuItemAction, ICommandAction, SubmenuItemAction } from 'vs/platform/actions/common/actions';
+import { IMenuService, MenuId, IMenu, MenuItemAction, ICommandAction, SubmenuItemAction, MenuRegistry } from 'vs/platform/actions/common/actions';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { RunOnceScheduler } from 'vs/base/common/async';
@@ -55,6 +55,9 @@ import { IMenubarService, IMenubarData, IMenubarMenu, IMenubarKeybinding, IMenub
import { withNullAsUndefined } from 'vs/base/common/types';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { Schemas } from 'vs/base/common/network';
+import { IElectronService } from 'vs/platform/electron/node/electron';
+import { posix, dirname } from 'vs/base/common/path';
+import { getBaseLabel } from 'vs/base/common/labels';
const TextInputActions: IAction[] = [
new Action('undo', nls.localize('undo', "Undo"), undefined, true, () => Promise.resolve(document.execCommand('undo'))),
@@ -73,6 +76,8 @@ export class ElectronWindow extends Disposable {
private readonly touchBarDisposables = this._register(new DisposableStore());
private lastInstalledTouchedBar: ICommandAction[][] | undefined;
+ private customTitleContextMenuDisposable = this._register(new DisposableStore());
+
private previousConfiguredZoomLevel: number | undefined;
private addFoldersScheduler: RunOnceScheduler;
@@ -102,7 +107,8 @@ export class ElectronWindow extends Disposable {
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@ITextFileService private readonly textFileService: ITextFileService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
- @IOpenerService private readonly openerService: IOpenerService
+ @IOpenerService private readonly openerService: IOpenerService,
+ @IElectronService private readonly electronService: IElectronService
) {
super();
@@ -243,6 +249,11 @@ export class ElectronWindow extends Disposable {
this._register(this.trackClosedWaitFiles(waitMarkerFile, resourcesToWaitFor));
}
+
+ // macOS custom title menu
+ if (isMacintosh) {
+ this._register(this.editorService.onDidActiveEditorChange(() => this.provideCustomTitleContextMenu()));
+ }
}
private onDidVisibleEditorsChange(): void {
@@ -306,6 +317,43 @@ export class ElectronWindow extends Disposable {
}
}
+ private provideCustomTitleContextMenu(): void {
+
+ // Clear old menu
+ this.customTitleContextMenuDisposable.clear();
+
+ // Provide new menu if a file is opened and we are on a custom title
+ const fileResource = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER, filterByScheme: Schemas.file });
+ if (!fileResource || getTitleBarStyle(this.configurationService, this.environmentService) !== 'custom') {
+ return;
+ }
+
+ // Split up filepath into segments
+ const filePath = fileResource.fsPath;
+ const segments = filePath.split(posix.sep);
+ for (let i = segments.length; i > 0; i--) {
+ const isFile = (i === segments.length);
+
+ let pathOffset = i;
+ if (!isFile) {
+ pathOffset++; // for segments which are not the file name we want to open the folder
+ }
+
+ const path = segments.slice(0, pathOffset).join(posix.sep);
+
+ let label: string;
+ if (!isFile) {
+ label = getBaseLabel(dirname(path));
+ } else {
+ label = getBaseLabel(path);
+ }
+
+ const commandId = `workbench.action.revealPathInFinder${i}`;
+ this.customTitleContextMenuDisposable.add(CommandsRegistry.registerCommand(commandId, () => this.electronService.showItemInFolder(path)));
+ this.customTitleContextMenuDisposable.add(MenuRegistry.appendMenuItem(MenuId.TitleBarContext, { command: { id: commandId, title: label || posix.sep }, order: -i }));
+ }
+ }
+
private create(): void {
// Native menu controller
@@ -371,7 +419,7 @@ export class ElectronWindow extends Disposable {
const success = await $this.windowsService.openExternal(encodeURI(resource.toString(true)));
if (!success && resource.scheme === Schemas.file) {
// if opening failed, and this is a file, we can still try to reveal it
- await $this.windowsService.showItemInFolder(resource);
+ await $this.electronService.showItemInFolder(resource.fsPath);
}
return true;
diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts
index 6ccc6ffcbbf..b31602466f5 100644
--- a/src/vs/workbench/services/environment/browser/environmentService.ts
+++ b/src/vs/workbench/services/environment/browser/environmentService.ts
@@ -183,16 +183,17 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment
get webviewExternalEndpoint(): string {
// TODO: get fallback from product.json
- return this.options.webviewEndpoint || 'https://{{uuid}}.vscode-webview-test.com';
- }
-
- get webviewResourceRoot(): string {
- return `${this.webviewExternalEndpoint}/{{commit}}/vscode-resource{{resource}}`
+ return (this.options.webviewEndpoint || 'https://{{uuid}}.vscode-webview-test.com/{{commit}}')
.replace('{{commit}}', product.commit || '211fa02efe8c041fd7baa8ec3dce199d5185aa44');
}
+ get webviewResourceRoot(): string {
+ return `${this.webviewExternalEndpoint}/vscode-resource{{resource}}`;
+ }
+
get webviewCspSource(): string {
- return this.options.webviewEndpoint || `https://*.vscode-webview-test.com`;
+ return this.webviewExternalEndpoint
+ .replace('{{uuid}}', '*');
}
}
diff --git a/src/vs/workbench/services/extensions/common/remoteConsoleUtil.ts b/src/vs/workbench/services/extensions/common/remoteConsoleUtil.ts
new file mode 100644
index 00000000000..58ca081f88f
--- /dev/null
+++ b/src/vs/workbench/services/extensions/common/remoteConsoleUtil.ts
@@ -0,0 +1,32 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { IRemoteConsoleLog, parse } from 'vs/base/common/console';
+import { ILogService } from 'vs/platform/log/common/log';
+
+export function logRemoteEntry(logService: ILogService, entry: IRemoteConsoleLog): void {
+ const args = parse(entry).args;
+ const firstArg = args.shift();
+ if (typeof firstArg !== 'string') {
+ return;
+ }
+
+ if (!entry.severity) {
+ entry.severity = 'info';
+ }
+
+ switch (entry.severity) {
+ case 'log':
+ case 'info':
+ logService.info(firstArg, ...args);
+ break;
+ case 'warn':
+ logService.warn(firstArg, ...args);
+ break;
+ case 'error':
+ logService.error(firstArg, ...args);
+ break;
+ }
+}
diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts
index 5d5e96479a2..4ceaca310a8 100644
--- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts
+++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts
@@ -14,7 +14,8 @@ import { toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import * as objects from 'vs/base/common/objects';
import * as platform from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
-import { IRemoteConsoleLog, log, parse } from 'vs/base/common/console';
+import { IRemoteConsoleLog, log } from 'vs/base/common/console';
+import { logRemoteEntry } from 'vs/workbench/services/extensions/common/remoteConsoleUtil';
import { findFreePort, randomPort } from 'vs/base/node/ports';
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net';
@@ -26,7 +27,7 @@ import { ILogService } from 'vs/platform/log/common/log';
import product from 'vs/platform/product/common/product';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
-import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
+import { IWindowService } from 'vs/platform/windows/common/windows';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IInitData, UIKind } from 'vs/workbench/api/common/extHost.protocol';
import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
@@ -67,7 +68,6 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
private readonly _extensionHostLogsLocation: URI,
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
@INotificationService private readonly _notificationService: INotificationService,
- @IWindowsService private readonly _windowsService: IWindowsService,
@IWindowService private readonly _windowService: IWindowService,
@ILifecycleService private readonly _lifecycleService: ILifecycleService,
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
@@ -435,7 +435,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
// Log on main side if running tests from cli
if (this._isExtensionDevTestFromCli) {
- this._windowsService.log(entry.severity, parse(entry).args);
+ logRemoteEntry(this._logService, entry);
}
// Broadcast to other windows if we are in development mode
diff --git a/src/vs/workbench/services/host/browser/browserHostService.ts b/src/vs/workbench/services/host/browser/browserHostService.ts
new file mode 100644
index 00000000000..a452c20287c
--- /dev/null
+++ b/src/vs/workbench/services/host/browser/browserHostService.ts
@@ -0,0 +1,20 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { IHostService } from 'vs/workbench/services/host/browser/host';
+import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
+
+export class BrowserHostService implements IHostService {
+
+ _serviceBrand: undefined;
+
+ //#region Window
+
+ readonly windowCount = Promise.resolve(1);
+
+ //#endregion
+}
+
+registerSingleton(IHostService, BrowserHostService, true);
diff --git a/src/vs/workbench/services/host/browser/host.ts b/src/vs/workbench/services/host/browser/host.ts
new file mode 100644
index 00000000000..735b21869e9
--- /dev/null
+++ b/src/vs/workbench/services/host/browser/host.ts
@@ -0,0 +1,22 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
+
+export const IHostService = createDecorator('hostService');
+
+export interface IHostService {
+
+ _serviceBrand: undefined;
+
+ //#region Window
+
+ /**
+ * The number of windows that belong to the current client session.
+ */
+ readonly windowCount: Promise;
+
+ //#endregion
+}
diff --git a/src/vs/workbench/services/host/electron-browser/desktopHostService.ts b/src/vs/workbench/services/host/electron-browser/desktopHostService.ts
new file mode 100644
index 00000000000..833310b965c
--- /dev/null
+++ b/src/vs/workbench/services/host/electron-browser/desktopHostService.ts
@@ -0,0 +1,23 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { IHostService } from 'vs/workbench/services/host/browser/host';
+import { IElectronService } from 'vs/platform/electron/node/electron';
+import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
+
+export class DesktopHostService implements IHostService {
+
+ _serviceBrand: undefined;
+
+ constructor(@IElectronService private readonly electronService: IElectronService) { }
+
+ //#region Window
+
+ get windowCount() { return this.electronService.windowCount(); }
+
+ //#endregion
+}
+
+registerSingleton(IHostService, DesktopHostService, true);
diff --git a/src/vs/workbench/services/textfile/browser/browserTextFileService.ts b/src/vs/workbench/services/textfile/browser/browserTextFileService.ts
new file mode 100644
index 00000000000..4cdfa523db0
--- /dev/null
+++ b/src/vs/workbench/services/textfile/browser/browserTextFileService.ts
@@ -0,0 +1,62 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { AbstractTextFileService } from 'vs/workbench/services/textfile/browser/textFileService';
+import { ITextFileService, IResourceEncodings, IResourceEncoding, ModelState } from 'vs/workbench/services/textfile/common/textfiles';
+import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
+import { ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle';
+import { Schemas } from 'vs/base/common/network';
+
+export class BrowserTextFileService extends AbstractTextFileService {
+
+ readonly encoding: IResourceEncodings = {
+ getPreferredWriteEncoding(): IResourceEncoding {
+ return { encoding: 'utf8', hasBOM: false };
+ }
+ };
+
+ protected onBeforeShutdown(reason: ShutdownReason): boolean {
+ // Web: we cannot perform long running in the shutdown phase
+ // As such we need to check sync if there are any dirty files
+ // that have not been backed up yet and then prevent the shutdown
+ // if that is the case.
+ return this.doBeforeShutdownSync();
+ }
+
+ private doBeforeShutdownSync(): boolean {
+ if (this.models.getAll().some(model => model.hasState(ModelState.PENDING_SAVE) || model.hasState(ModelState.PENDING_AUTO_SAVE))) {
+ return true; // files are pending to be saved: veto
+ }
+
+ const dirtyResources = this.getDirty();
+ if (!dirtyResources.length) {
+ return false; // no dirty: no veto
+ }
+
+ if (!this.isHotExitEnabled) {
+ return true; // dirty without backup: veto
+ }
+
+ for (const dirtyResource of dirtyResources) {
+ let hasBackup = false;
+
+ if (this.fileService.canHandleResource(dirtyResource)) {
+ const model = this.models.get(dirtyResource);
+ hasBackup = !!(model && model.hasBackup());
+ } else if (dirtyResource.scheme === Schemas.untitled) {
+ hasBackup = this.untitledEditorService.hasBackup(dirtyResource);
+ }
+
+ if (!hasBackup) {
+ console.warn('Unload prevented: pending backups');
+ return true; // dirty without backup: veto
+ }
+ }
+
+ return false; // dirty with backups: no veto
+ }
+}
+
+registerSingleton(ITextFileService, BrowserTextFileService);
diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts
index ed7f5273046..0925a9f3504 100644
--- a/src/vs/workbench/services/textfile/browser/textFileService.ts
+++ b/src/vs/workbench/services/textfile/browser/textFileService.ts
@@ -3,60 +3,1067 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { TextFileService } from 'vs/workbench/services/textfile/common/textFileService';
-import { ITextFileService, IResourceEncodings, IResourceEncoding, ModelState } from 'vs/workbench/services/textfile/common/textfiles';
-import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
-import { ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle';
+import * as nls from 'vs/nls';
+import { URI } from 'vs/base/common/uri';
+import * as errors from 'vs/base/common/errors';
+import * as objects from 'vs/base/common/objects';
+import { Event, Emitter } from 'vs/base/common/event';
+import * as platform from 'vs/base/common/platform';
+import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
+import { IResult, ITextFileOperationResult, ITextFileService, ITextFileStreamContent, IAutoSaveConfiguration, AutoSaveMode, SaveReason, ITextFileEditorModelManager, ITextFileEditorModel, ModelState, ISaveOptions, AutoSaveContext, IWillMoveEvent, ITextFileContent, IResourceEncodings, IReadTextFileOptions, IWriteTextFileOptions, toBufferOrReadable, TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles';
+import { ConfirmResult, IRevertOptions } from 'vs/workbench/common/editor';
+import { ILifecycleService, ShutdownReason, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
+import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
+import { IFileService, IFilesConfiguration, FileOperationError, FileOperationResult, AutoSaveConfiguration, HotExitConfiguration, IFileStatWithMetadata, ICreateFileOptions } from 'vs/platform/files/common/files';
+import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
+import { Disposable } from 'vs/base/common/lifecycle';
+import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
+import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
+import { UntitledEditorModel } from 'vs/workbench/common/editor/untitledEditorModel';
+import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager';
+import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
+import { ResourceMap } from 'vs/base/common/map';
import { Schemas } from 'vs/base/common/network';
+import { IHistoryService } from 'vs/workbench/services/history/common/history';
+import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
+import { createTextBufferFactoryFromSnapshot, createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel';
+import { IModelService } from 'vs/editor/common/services/modelService';
+import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
+import { isEqualOrParent, isEqual, joinPath, dirname, extname, basename, toLocalResource } from 'vs/base/common/resources';
+import { getConfirmMessage, IDialogService, IFileDialogService, ISaveDialogOptions, IConfirmation } from 'vs/platform/dialogs/common/dialogs';
+import { IModeService } from 'vs/editor/common/services/modeService';
+import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
+import { coalesce } from 'vs/base/common/arrays';
+import { trim } from 'vs/base/common/strings';
+import { VSBuffer } from 'vs/base/common/buffer';
+import { ITextSnapshot } from 'vs/editor/common/model';
+import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
+import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry';
+import { IHostService } from 'vs/workbench/services/host/browser/host';
-export class BrowserTextFileService extends TextFileService {
+/**
+ * The workbench file service implementation implements the raw file service spec and adds additional methods on top.
+ */
+export abstract class AbstractTextFileService extends Disposable implements ITextFileService {
- readonly encoding: IResourceEncodings = {
- getPreferredWriteEncoding(): IResourceEncoding {
- return { encoding: 'utf8', hasBOM: false };
- }
- };
+ _serviceBrand: undefined;
- protected onBeforeShutdown(reason: ShutdownReason): boolean {
- // Web: we cannot perform long running in the shutdown phase
- // As such we need to check sync if there are any dirty files
- // that have not been backed up yet and then prevent the shutdown
- // if that is the case.
- return this.doBeforeShutdownSync();
+ private readonly _onAutoSaveConfigurationChange: Emitter = this._register(new Emitter());
+ readonly onAutoSaveConfigurationChange: Event = this._onAutoSaveConfigurationChange.event;
+
+ private readonly _onFilesAssociationChange: Emitter = this._register(new Emitter());
+ readonly onFilesAssociationChange: Event = this._onFilesAssociationChange.event;
+
+ private readonly _onWillMove = this._register(new Emitter());
+ readonly onWillMove: Event = this._onWillMove.event;
+
+ private _models: TextFileEditorModelManager;
+ get models(): ITextFileEditorModelManager { return this._models; }
+
+ abstract get encoding(): IResourceEncodings;
+
+ private currentFilesAssociationConfig: { [key: string]: string; };
+ private configuredAutoSaveDelay?: number;
+ private configuredAutoSaveOnFocusChange: boolean | undefined;
+ private configuredAutoSaveOnWindowChange: boolean | undefined;
+ private configuredHotExit: string | undefined;
+ private autoSaveContext: IContextKey;
+
+ constructor(
+ @IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
+ @IFileService protected readonly fileService: IFileService,
+ @IUntitledEditorService protected readonly untitledEditorService: IUntitledEditorService,
+ @ILifecycleService private readonly lifecycleService: ILifecycleService,
+ @IInstantiationService protected instantiationService: IInstantiationService,
+ @IConfigurationService private readonly configurationService: IConfigurationService,
+ @IModeService private readonly modeService: IModeService,
+ @IModelService private readonly modelService: IModelService,
+ @IWorkbenchEnvironmentService protected readonly environmentService: IWorkbenchEnvironmentService,
+ @INotificationService private readonly notificationService: INotificationService,
+ @IBackupFileService private readonly backupFileService: IBackupFileService,
+ @IHostService private readonly hostService: IHostService,
+ @IHistoryService private readonly historyService: IHistoryService,
+ @IContextKeyService contextKeyService: IContextKeyService,
+ @IDialogService private readonly dialogService: IDialogService,
+ @IFileDialogService private readonly fileDialogService: IFileDialogService,
+ @IEditorService private readonly editorService: IEditorService,
+ @ITextResourceConfigurationService protected readonly textResourceConfigurationService: ITextResourceConfigurationService
+ ) {
+ super();
+
+ this._models = this._register(instantiationService.createInstance(TextFileEditorModelManager));
+ this.autoSaveContext = AutoSaveContext.bindTo(contextKeyService);
+
+ const configuration = configurationService.getValue();
+ this.currentFilesAssociationConfig = configuration && configuration.files && configuration.files.associations;
+
+ this.onFilesConfigurationChange(configuration);
+
+ this.registerListeners();
}
- private doBeforeShutdownSync(): boolean {
- if (this.models.getAll().some(model => model.hasState(ModelState.PENDING_SAVE) || model.hasState(ModelState.PENDING_AUTO_SAVE))) {
- return true; // files are pending to be saved: veto
- }
+ //#region event handling
- const dirtyResources = this.getDirty();
- if (!dirtyResources.length) {
- return false; // no dirty: no veto
- }
+ private registerListeners(): void {
- if (!this.isHotExitEnabled) {
- return true; // dirty without backup: veto
- }
+ // Lifecycle
+ this.lifecycleService.onBeforeShutdown(event => event.veto(this.onBeforeShutdown(event.reason)));
+ this.lifecycleService.onShutdown(this.dispose, this);
- for (const dirtyResource of dirtyResources) {
- let hasBackup = false;
+ // Files configuration changes
+ this._register(this.configurationService.onDidChangeConfiguration(e => {
+ if (e.affectsConfiguration('files')) {
+ this.onFilesConfigurationChange(this.configurationService.getValue());
+ }
+ }));
+ }
- if (this.fileService.canHandleResource(dirtyResource)) {
- const model = this.models.get(dirtyResource);
- hasBackup = !!(model && model.hasBackup());
- } else if (dirtyResource.scheme === Schemas.untitled) {
- hasBackup = this.untitledEditorService.hasBackup(dirtyResource);
+ protected onBeforeShutdown(reason: ShutdownReason): boolean | Promise {
+
+ // Dirty files need treatment on shutdown
+ const dirty = this.getDirty();
+ if (dirty.length) {
+
+ // If auto save is enabled, save all files and then check again for dirty files
+ // We DO NOT run any save participant if we are in the shutdown phase for performance reasons
+ if (this.getAutoSaveMode() !== AutoSaveMode.OFF) {
+ return this.saveAll(false /* files only */, { skipSaveParticipants: true }).then(() => {
+
+ // If we still have dirty files, we either have untitled ones or files that cannot be saved
+ const remainingDirty = this.getDirty();
+ if (remainingDirty.length) {
+ return this.handleDirtyBeforeShutdown(remainingDirty, reason);
+ }
+
+ return false;
+ });
}
- if (!hasBackup) {
- console.warn('Unload prevented: pending backups');
- return true; // dirty without backup: veto
+ // Auto save is not enabled
+ return this.handleDirtyBeforeShutdown(dirty, reason);
+ }
+
+ // No dirty files: no veto
+ return this.noVeto({ cleanUpBackups: true });
+ }
+
+ private handleDirtyBeforeShutdown(dirty: URI[], reason: ShutdownReason): boolean | Promise {
+
+ // If hot exit is enabled, backup dirty files and allow to exit without confirmation
+ if (this.isHotExitEnabled) {
+ return this.backupBeforeShutdown(dirty, reason).then(didBackup => {
+ if (didBackup) {
+ return this.noVeto({ cleanUpBackups: false }); // no veto and no backup cleanup (since backup was successful)
+ }
+
+ // since a backup did not happen, we have to confirm for the dirty files now
+ return this.confirmBeforeShutdown();
+ }, error => {
+ this.notificationService.error(nls.localize('files.backup.failSave', "Files that are dirty could not be written to the backup location (Error: {0}). Try saving your files first and then exit.", error.message));
+
+ return true; // veto, the backups failed
+ });
+ }
+
+ // Otherwise just confirm from the user what to do with the dirty files
+ return this.confirmBeforeShutdown();
+ }
+
+ private async backupBeforeShutdown(dirtyToBackup: URI[], reason: ShutdownReason): Promise {
+ const windowCount = await this.hostService.windowCount;
+
+ // When quit is requested skip the confirm callback and attempt to backup all workspaces.
+ // When quit is not requested the confirm callback should be shown when the window being
+ // closed is the only VS Code window open, except for on Mac where hot exit is only
+ // ever activated when quit is requested.
+
+ let doBackup: boolean | undefined;
+ switch (reason) {
+ case ShutdownReason.CLOSE:
+ if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.configuredHotExit === HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE) {
+ doBackup = true; // backup if a folder is open and onExitAndWindowClose is configured
+ } else if (windowCount > 1 || platform.isMacintosh) {
+ doBackup = false; // do not backup if a window is closed that does not cause quitting of the application
+ } else {
+ doBackup = true; // backup if last window is closed on win/linux where the application quits right after
+ }
+ break;
+
+ case ShutdownReason.QUIT:
+ doBackup = true; // backup because next start we restore all backups
+ break;
+
+ case ShutdownReason.RELOAD:
+ doBackup = true; // backup because after window reload, backups restore
+ break;
+
+ case ShutdownReason.LOAD:
+ if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.configuredHotExit === HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE) {
+ doBackup = true; // backup if a folder is open and onExitAndWindowClose is configured
+ } else {
+ doBackup = false; // do not backup because we are switching contexts
+ }
+ break;
+ }
+
+ if (!doBackup) {
+ return false;
+ }
+
+ await this.backupAll(dirtyToBackup);
+
+ return true;
+ }
+
+ private backupAll(dirtyToBackup: URI[]): Promise {
+
+ // split up between files and untitled
+ const filesToBackup: ITextFileEditorModel[] = [];
+ const untitledToBackup: URI[] = [];
+ dirtyToBackup.forEach(dirty => {
+ if (this.fileService.canHandleResource(dirty)) {
+ const model = this.models.get(dirty);
+ if (model) {
+ filesToBackup.push(model);
+ }
+ } else if (dirty.scheme === Schemas.untitled) {
+ untitledToBackup.push(dirty);
+ }
+ });
+
+ return this.doBackupAll(filesToBackup, untitledToBackup);
+ }
+
+ private async doBackupAll(dirtyFileModels: ITextFileEditorModel[], untitledResources: URI[]): Promise {
+
+ // Handle file resources first
+ await Promise.all(dirtyFileModels.map(model => model.backup()));
+
+ // Handle untitled resources
+ await Promise.all(untitledResources
+ .filter(untitled => this.untitledEditorService.exists(untitled))
+ .map(async untitled => (await this.untitledEditorService.loadOrCreate({ resource: untitled })).backup()));
+ }
+
+ private async confirmBeforeShutdown(): Promise {
+ const confirm = await this.confirmSave();
+
+ // Save
+ if (confirm === ConfirmResult.SAVE) {
+ const result = await this.saveAll(true /* includeUntitled */, { skipSaveParticipants: true });
+
+ if (result.results.some(r => !r.success)) {
+ return true; // veto if some saves failed
+ }
+
+ return this.noVeto({ cleanUpBackups: true });
+ }
+
+ // Don't Save
+ else if (confirm === ConfirmResult.DONT_SAVE) {
+
+ // Make sure to revert untitled so that they do not restore
+ // see https://github.com/Microsoft/vscode/issues/29572
+ this.untitledEditorService.revertAll();
+
+ return this.noVeto({ cleanUpBackups: true });
+ }
+
+ // Cancel
+ else if (confirm === ConfirmResult.CANCEL) {
+ return true; // veto
+ }
+
+ return false;
+ }
+
+ private noVeto(options: { cleanUpBackups: boolean }): boolean | Promise {
+ if (!options.cleanUpBackups) {
+ return false;
+ }
+
+ if (this.lifecycleService.phase < LifecyclePhase.Restored) {
+ return false; // if editors have not restored, we are not up to speed with backups and thus should not clean them
+ }
+
+ return this.cleanupBackupsBeforeShutdown().then(() => false, () => false);
+ }
+
+ protected async cleanupBackupsBeforeShutdown(): Promise {
+ if (this.environmentService.isExtensionDevelopment) {
+ return;
+ }
+
+ await this.backupFileService.discardAllWorkspaceBackups();
+ }
+
+ protected onFilesConfigurationChange(configuration: IFilesConfiguration): void {
+ const wasAutoSaveEnabled = (this.getAutoSaveMode() !== AutoSaveMode.OFF);
+
+ const autoSaveMode = (configuration && configuration.files && configuration.files.autoSave) || AutoSaveConfiguration.OFF;
+ this.autoSaveContext.set(autoSaveMode);
+ switch (autoSaveMode) {
+ case AutoSaveConfiguration.AFTER_DELAY:
+ this.configuredAutoSaveDelay = configuration && configuration.files && configuration.files.autoSaveDelay;
+ this.configuredAutoSaveOnFocusChange = false;
+ this.configuredAutoSaveOnWindowChange = false;
+ break;
+
+ case AutoSaveConfiguration.ON_FOCUS_CHANGE:
+ this.configuredAutoSaveDelay = undefined;
+ this.configuredAutoSaveOnFocusChange = true;
+ this.configuredAutoSaveOnWindowChange = false;
+ break;
+
+ case AutoSaveConfiguration.ON_WINDOW_CHANGE:
+ this.configuredAutoSaveDelay = undefined;
+ this.configuredAutoSaveOnFocusChange = false;
+ this.configuredAutoSaveOnWindowChange = true;
+ break;
+
+ default:
+ this.configuredAutoSaveDelay = undefined;
+ this.configuredAutoSaveOnFocusChange = false;
+ this.configuredAutoSaveOnWindowChange = false;
+ break;
+ }
+
+ // Emit as event
+ this._onAutoSaveConfigurationChange.fire(this.getAutoSaveConfiguration());
+
+ // save all dirty when enabling auto save
+ if (!wasAutoSaveEnabled && this.getAutoSaveMode() !== AutoSaveMode.OFF) {
+ this.saveAll();
+ }
+
+ // Check for change in files associations
+ const filesAssociation = configuration && configuration.files && configuration.files.associations;
+ if (!objects.equals(this.currentFilesAssociationConfig, filesAssociation)) {
+ this.currentFilesAssociationConfig = filesAssociation;
+ this._onFilesAssociationChange.fire();
+ }
+
+ // Hot exit
+ const hotExitMode = configuration && configuration.files && configuration.files.hotExit;
+ if (hotExitMode === HotExitConfiguration.OFF || hotExitMode === HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE) {
+ this.configuredHotExit = hotExitMode;
+ } else {
+ this.configuredHotExit = HotExitConfiguration.ON_EXIT;
+ }
+ }
+
+ //#endregion
+
+ //#region primitives (read, create, move, delete, update)
+
+ async read(resource: URI, options?: IReadTextFileOptions): Promise {
+ const content = await this.fileService.readFile(resource, options);
+
+ // in case of acceptTextOnly: true, we check the first
+ // chunk for possibly being binary by looking for 0-bytes
+ // we limit this check to the first 512 bytes
+ this.validateBinary(content.value, options);
+
+ return {
+ ...content,
+ encoding: 'utf8',
+ value: content.value.toString()
+ };
+ }
+
+ async readStream(resource: URI, options?: IReadTextFileOptions): Promise {
+ const stream = await this.fileService.readFileStream(resource, options);
+
+ // in case of acceptTextOnly: true, we check the first
+ // chunk for possibly being binary by looking for 0-bytes
+ // we limit this check to the first 512 bytes
+ let checkedForBinary = false;
+ const throwOnBinary = (data: VSBuffer): Error | undefined => {
+ if (!checkedForBinary) {
+ checkedForBinary = true;
+
+ this.validateBinary(data, options);
+ }
+
+ return undefined;
+ };
+
+ return {
+ ...stream,
+ encoding: 'utf8',
+ value: await createTextBufferFactoryFromStream(stream.value, undefined, options && options.acceptTextOnly ? throwOnBinary : undefined)
+ };
+ }
+
+ private validateBinary(buffer: VSBuffer, options?: IReadTextFileOptions): void {
+ if (!options || !options.acceptTextOnly) {
+ return; // no validation needed
+ }
+
+ // in case of acceptTextOnly: true, we check the first
+ // chunk for possibly being binary by looking for 0-bytes
+ // we limit this check to the first 512 bytes
+ for (let i = 0; i < buffer.byteLength && i < 512; i++) {
+ if (buffer.readUInt8(i) === 0) {
+ throw new TextFileOperationError(nls.localize('fileBinaryError', "File seems to be binary and cannot be opened as text"), TextFileOperationResult.FILE_IS_BINARY, options);
+ }
+ }
+ }
+
+ async create(resource: URI, value?: string | ITextSnapshot, options?: ICreateFileOptions): Promise {
+ const stat = await this.doCreate(resource, value, options);
+
+ // If we had an existing model for the given resource, load
+ // it again to make sure it is up to date with the contents
+ // we just wrote into the underlying resource by calling
+ // revert()
+ const existingModel = this.models.get(resource);
+ if (existingModel && !existingModel.isDisposed()) {
+ await existingModel.revert();
+ }
+
+ return stat;
+ }
+
+ protected doCreate(resource: URI, value?: string | ITextSnapshot, options?: ICreateFileOptions): Promise {
+ return this.fileService.createFile(resource, toBufferOrReadable(value), options);
+ }
+
+ async write(resource: URI, value: string | ITextSnapshot, options?: IWriteTextFileOptions): Promise {
+ return this.fileService.writeFile(resource, toBufferOrReadable(value), options);
+ }
+
+ async delete(resource: URI, options?: { useTrash?: boolean, recursive?: boolean }): Promise {
+ const dirtyFiles = this.getDirty().filter(dirty => isEqualOrParent(dirty, resource));
+
+ await this.revertAll(dirtyFiles, { soft: true });
+
+ return this.fileService.del(resource, options);
+ }
+
+ async move(source: URI, target: URI, overwrite?: boolean): Promise {
+
+ // await onWillMove event joiners
+ await this.notifyOnWillMove(source, target);
+
+ // find all models that related to either source or target (can be many if resource is a folder)
+ const sourceModels: ITextFileEditorModel[] = [];
+ const conflictingModels: ITextFileEditorModel[] = [];
+ for (const model of this.getFileModels()) {
+ const resource = model.getResource();
+
+ if (isEqualOrParent(resource, target, false /* do not ignorecase, see https://github.com/Microsoft/vscode/issues/56384 */)) {
+ conflictingModels.push(model);
+ }
+
+ if (isEqualOrParent(resource, source)) {
+ sourceModels.push(model);
}
}
- return false; // dirty with backups: no veto
+ // remember each source model to load again after move is done
+ // with optional content to restore if it was dirty
+ type ModelToRestore = { resource: URI; snapshot?: ITextSnapshot };
+ const modelsToRestore: ModelToRestore[] = [];
+ for (const sourceModel of sourceModels) {
+ const sourceModelResource = sourceModel.getResource();
+
+ // If the source is the actual model, just use target as new resource
+ let modelToRestoreResource: URI;
+ if (isEqual(sourceModelResource, source)) {
+ modelToRestoreResource = target;
+ }
+
+ // Otherwise a parent folder of the source is being moved, so we need
+ // to compute the target resource based on that
+ else {
+ modelToRestoreResource = joinPath(target, sourceModelResource.path.substr(source.path.length + 1));
+ }
+
+ const modelToRestore: ModelToRestore = { resource: modelToRestoreResource };
+ if (sourceModel.isDirty()) {
+ modelToRestore.snapshot = sourceModel.createSnapshot();
+ }
+
+ modelsToRestore.push(modelToRestore);
+ }
+
+ // in order to move, we need to soft revert all dirty models,
+ // both from the source as well as the target if any
+ const dirtyModels = [...sourceModels, ...conflictingModels].filter(model => model.isDirty());
+ await this.revertAll(dirtyModels.map(dirtyModel => dirtyModel.getResource()), { soft: true });
+
+ // now we can rename the source to target via file operation
+ let stat: IFileStatWithMetadata;
+ try {
+ stat = await this.fileService.move(source, target, overwrite);
+ } catch (error) {
+
+ // in case of any error, ensure to set dirty flag back
+ dirtyModels.forEach(dirtyModel => dirtyModel.makeDirty());
+
+ throw error;
+ }
+
+ // finally, restore models that we had loaded previously
+ await Promise.all(modelsToRestore.map(async modelToRestore => {
+
+ // restore the model, forcing a reload. this is important because
+ // we know the file has changed on disk after the move and the
+ // model might have still existed with the previous state. this
+ // ensures we are not tracking a stale state.
+ const restoredModel = await this.models.loadOrCreate(modelToRestore.resource, { reload: { async: false } });
+
+ // restore previous dirty content if any and ensure to mark
+ // the model as dirty
+ if (modelToRestore.snapshot && restoredModel.isResolved()) {
+ this.modelService.updateModel(restoredModel.textEditorModel, createTextBufferFactoryFromSnapshot(modelToRestore.snapshot));
+
+ restoredModel.makeDirty();
+ }
+ }));
+
+ return stat;
+ }
+
+ private async notifyOnWillMove(source: URI, target: URI): Promise {
+ const waitForPromises: Promise[] = [];
+
+ // fire event
+ this._onWillMove.fire({
+ oldResource: source,
+ newResource: target,
+ waitUntil(promise: Promise) {
+ waitForPromises.push(promise.then(undefined, errors.onUnexpectedError));
+ }
+ });
+
+ // prevent async waitUntil-calls
+ Object.freeze(waitForPromises);
+
+ await Promise.all(waitForPromises);
+ }
+
+ //#endregion
+
+ //#region save/revert
+
+ async save(resource: URI, options?: ISaveOptions): Promise {
+
+ // Run a forced save if we detect the file is not dirty so that save participants can still run
+ if (options && options.force && this.fileService.canHandleResource(resource) && !this.isDirty(resource)) {
+ const model = this._models.get(resource);
+ if (model) {
+ options.reason = SaveReason.EXPLICIT;
+
+ await model.save(options);
+
+ return !model.isDirty();
+ }
+ }
+
+ const result = await this.saveAll([resource], options);
+
+ return result.results.length === 1 && !!result.results[0].success;
+ }
+
+ async confirmSave(resources?: URI[]): Promise {
+ if (this.environmentService.isExtensionDevelopment) {
+ if (!this.environmentService.args['extension-development-confirm-save']) {
+ return ConfirmResult.DONT_SAVE; // no veto when we are in extension dev mode because we cannot assume we run interactive (e.g. tests)
+ }
+ }
+
+ const resourcesToConfirm = this.getDirty(resources);
+ if (resourcesToConfirm.length === 0) {
+ return ConfirmResult.DONT_SAVE;
+ }
+ return promptSave(this.dialogService, resourcesToConfirm);
+ }
+
+ async confirmOverwrite(resource: URI): Promise {
+ const confirm: IConfirmation = {
+ message: nls.localize('confirmOverwrite', "'{0}' already exists. Do you want to replace it?", basename(resource)),
+ detail: nls.localize('irreversible', "A file or folder with the same name already exists in the folder {0}. Replacing it will overwrite its current contents.", basename(dirname(resource))),
+ primaryButton: nls.localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace"),
+ type: 'warning'
+ };
+
+ return (await this.dialogService.confirm(confirm)).confirmed;
+ }
+
+ saveAll(includeUntitled?: boolean, options?: ISaveOptions): Promise;
+ saveAll(resources: URI[], options?: ISaveOptions): Promise;
+ saveAll(arg1?: boolean | URI[], options?: ISaveOptions): Promise {
+
+ // get all dirty
+ let toSave: URI[] = [];
+ if (Array.isArray(arg1)) {
+ toSave = this.getDirty(arg1);
+ } else {
+ toSave = this.getDirty();
+ }
+
+ // split up between files and untitled
+ const filesToSave: URI[] = [];
+ const untitledToSave: URI[] = [];
+ toSave.forEach(resourceToSave => {
+ if ((Array.isArray(arg1) || arg1 === true /* includeUntitled */) && resourceToSave.scheme === Schemas.untitled) {
+ untitledToSave.push(resourceToSave);
+ } else {
+ filesToSave.push(resourceToSave);
+ }
+ });
+
+ return this.doSaveAll(filesToSave, untitledToSave, options);
+ }
+
+ private async doSaveAll(fileResources: URI[], untitledResources: URI[], options?: ISaveOptions): Promise {
+
+ // Handle files first that can just be saved
+ const result = await this.doSaveAllFiles(fileResources, options);
+
+ // Preflight for untitled to handle cancellation from the dialog
+ const targetsForUntitled: URI[] = [];
+ for (const untitled of untitledResources) {
+ if (this.untitledEditorService.exists(untitled)) {
+ let targetUri: URI;
+
+ // Untitled with associated file path don't need to prompt
+ if (this.untitledEditorService.hasAssociatedFilePath(untitled)) {
+ targetUri = toLocalResource(untitled, this.environmentService.configuration.remoteAuthority);
+ }
+
+ // Otherwise ask user
+ else {
+ const targetPath = await this.promptForPath(untitled, this.suggestFileName(untitled));
+ if (!targetPath) {
+ return { results: [...fileResources, ...untitledResources].map(r => ({ source: r })) };
+ }
+
+ targetUri = targetPath;
+ }
+
+ targetsForUntitled.push(targetUri);
+ }
+ }
+
+ // Handle untitled
+ await Promise.all(targetsForUntitled.map(async (target, index) => {
+ const uri = await this.saveAs(untitledResources[index], target);
+
+ result.results.push({
+ source: untitledResources[index],
+ target: uri,
+ success: !!uri
+ });
+ }));
+
+ return result;
+ }
+
+ protected async promptForPath(resource: URI, defaultUri: URI, availableFileSystems?: string[]): Promise {
+
+ // Help user to find a name for the file by opening it first
+ await this.editorService.openEditor({ resource, options: { revealIfOpened: true, preserveFocus: true } });
+
+ return this.fileDialogService.pickFileToSave(this.getSaveDialogOptions(defaultUri, availableFileSystems));
+ }
+
+ private getSaveDialogOptions(defaultUri: URI, availableFileSystems?: string[]): ISaveDialogOptions {
+ const options: ISaveDialogOptions = {
+ defaultUri,
+ title: nls.localize('saveAsTitle', "Save As"),
+ availableFileSystems,
+ };
+
+ // Filters are only enabled on Windows where they work properly
+ if (!platform.isWindows) {
+ return options;
+ }
+
+ interface IFilter { name: string; extensions: string[]; }
+
+ // Build the file filter by using our known languages
+ const ext: string | undefined = defaultUri ? extname(defaultUri) : undefined;
+ let matchingFilter: IFilter | undefined;
+ const filters: IFilter[] = coalesce(this.modeService.getRegisteredLanguageNames().map(languageName => {
+ const extensions = this.modeService.getExtensions(languageName);
+ if (!extensions || !extensions.length) {
+ return null;
+ }
+
+ const filter: IFilter = { name: languageName, extensions: extensions.slice(0, 10).map(e => trim(e, '.')) };
+
+ if (ext && extensions.indexOf(ext) >= 0) {
+ matchingFilter = filter;
+
+ return null; // matching filter will be added last to the top
+ }
+
+ return filter;
+ }));
+
+ // Filters are a bit weird on Windows, based on having a match or not:
+ // Match: we put the matching filter first so that it shows up selected and the all files last
+ // No match: we put the all files filter first
+ const allFilesFilter = { name: nls.localize('allFiles', "All Files"), extensions: ['*'] };
+ if (matchingFilter) {
+ filters.unshift(matchingFilter);
+ filters.unshift(allFilesFilter);
+ } else {
+ filters.unshift(allFilesFilter);
+ }
+
+ // Allow to save file without extension
+ filters.push({ name: nls.localize('noExt', "No Extension"), extensions: [''] });
+
+ options.filters = filters;
+
+ return options;
+ }
+
+ private async doSaveAllFiles(resources?: URI[], options: ISaveOptions = Object.create(null)): Promise {
+ const dirtyFileModels = this.getDirtyFileModels(Array.isArray(resources) ? resources : undefined /* Save All */)
+ .filter(model => {
+ if ((model.hasState(ModelState.CONFLICT) || model.hasState(ModelState.ERROR)) && (options.reason === SaveReason.AUTO || options.reason === SaveReason.FOCUS_CHANGE || options.reason === SaveReason.WINDOW_CHANGE)) {
+ return false; // if model is in save conflict or error, do not save unless save reason is explicit or not provided at all
+ }
+
+ return true;
+ });
+
+ const mapResourceToResult = new ResourceMap();
+ dirtyFileModels.forEach(m => {
+ mapResourceToResult.set(m.getResource(), {
+ source: m.getResource()
+ });
+ });
+
+ await Promise.all(dirtyFileModels.map(async model => {
+ await model.save(options);
+
+ if (!model.isDirty()) {
+ const result = mapResourceToResult.get(model.getResource());
+ if (result) {
+ result.success = true;
+ }
+ }
+ }));
+
+ return { results: mapResourceToResult.values() };
+ }
+
+ private getFileModels(arg1?: URI | URI[]): ITextFileEditorModel[] {
+ if (Array.isArray(arg1)) {
+ const models: ITextFileEditorModel[] = [];
+ arg1.forEach(resource => {
+ models.push(...this.getFileModels(resource));
+ });
+
+ return models;
+ }
+
+ return this._models.getAll(arg1);
+ }
+
+ private getDirtyFileModels(resources?: URI | URI[]): ITextFileEditorModel[] {
+ return this.getFileModels(resources).filter(model => model.isDirty());
+ }
+
+ async saveAs(resource: URI, targetResource?: URI, options?: ISaveOptions): Promise {
+
+ // Get to target resource
+ if (!targetResource) {
+ let dialogPath = resource;
+ if (resource.scheme === Schemas.untitled) {
+ dialogPath = this.suggestFileName(resource);
+ }
+
+ targetResource = await this.promptForPath(resource, dialogPath, options ? options.availableFileSystems : undefined);
+ }
+
+ if (!targetResource) {
+ return; // user canceled
+ }
+
+ // Just save if target is same as models own resource
+ if (resource.toString() === targetResource.toString()) {
+ await this.save(resource, options);
+
+ return resource;
+ }
+
+ // Do it
+ return this.doSaveAs(resource, targetResource, options);
+ }
+
+ private async doSaveAs(resource: URI, target: URI, options?: ISaveOptions): Promise {
+
+ // Retrieve text model from provided resource if any
+ let model: ITextFileEditorModel | UntitledEditorModel | undefined;
+ if (this.fileService.canHandleResource(resource)) {
+ model = this._models.get(resource);
+ } else if (resource.scheme === Schemas.untitled && this.untitledEditorService.exists(resource)) {
+ model = await this.untitledEditorService.loadOrCreate({ resource });
+ }
+
+ // We have a model: Use it (can be null e.g. if this file is binary and not a text file or was never opened before)
+ let result: boolean;
+ if (model) {
+ result = await this.doSaveTextFileAs(model, resource, target, options);
+ }
+
+ // Otherwise we can only copy
+ else {
+ await this.fileService.copy(resource, target);
+
+ result = true;
+ }
+
+ // Return early if the operation was not running
+ if (!result) {
+ return target;
+ }
+
+ // Revert the source
+ await this.revert(resource);
+
+ return target;
+ }
+
+ private async doSaveTextFileAs(sourceModel: ITextFileEditorModel | UntitledEditorModel, resource: URI, target: URI, options?: ISaveOptions): Promise {
+
+ // Prefer an existing model if it is already loaded for the given target resource
+ let targetExists: boolean = false;
+ let targetModel = this.models.get(target);
+ if (targetModel && targetModel.isResolved()) {
+ targetExists = true;
+ }
+
+ // Otherwise create the target file empty if it does not exist already and resolve it from there
+ else {
+ targetExists = await this.fileService.exists(target);
+
+ // create target model adhoc if file does not exist yet
+ if (!targetExists) {
+ await this.create(target, '');
+ }
+
+ targetModel = await this.models.loadOrCreate(target);
+ }
+
+ try {
+
+ // Confirm to overwrite if we have an untitled file with associated file where
+ // the file actually exists on disk and we are instructed to save to that file
+ // path. This can happen if the file was created after the untitled file was opened.
+ // See https://github.com/Microsoft/vscode/issues/67946
+ let write: boolean;
+ if (sourceModel instanceof UntitledEditorModel && sourceModel.hasAssociatedFilePath && targetExists && isEqual(target, toLocalResource(sourceModel.getResource(), this.environmentService.configuration.remoteAuthority))) {
+ write = await this.confirmOverwrite(target);
+ } else {
+ write = true;
+ }
+
+ if (!write) {
+ return false;
+ }
+
+ // take over model value, encoding and mode (only if more specific) from source model
+ targetModel.updatePreferredEncoding(sourceModel.getEncoding());
+ if (sourceModel.isResolved() && targetModel.isResolved()) {
+ this.modelService.updateModel(targetModel.textEditorModel, createTextBufferFactoryFromSnapshot(sourceModel.createSnapshot()));
+
+ const sourceMode = sourceModel.textEditorModel.getLanguageIdentifier();
+ const targetMode = targetModel.textEditorModel.getLanguageIdentifier();
+ if (sourceMode.language !== PLAINTEXT_MODE_ID && targetMode.language === PLAINTEXT_MODE_ID) {
+ targetModel.textEditorModel.setMode(sourceMode); // only use if more specific than plain/text
+ }
+ }
+
+ // save model
+ await targetModel.save(options);
+
+ return true;
+ } catch (error) {
+
+ // binary model: delete the file and run the operation again
+ if (
+ (error).textFileOperationResult === TextFileOperationResult.FILE_IS_BINARY ||
+ (error).fileOperationResult === FileOperationResult.FILE_TOO_LARGE
+ ) {
+ await this.fileService.del(target);
+
+ return this.doSaveTextFileAs(sourceModel, resource, target, options);
+ }
+
+ throw error;
+ }
+ }
+
+ private suggestFileName(untitledResource: URI): URI {
+ const untitledFileName = this.untitledEditorService.suggestFileName(untitledResource);
+ const remoteAuthority = this.environmentService.configuration.remoteAuthority;
+ const schemeFilter = remoteAuthority ? Schemas.vscodeRemote : Schemas.file;
+
+ const lastActiveFile = this.historyService.getLastActiveFile(schemeFilter);
+ if (lastActiveFile) {
+ const lastDir = dirname(lastActiveFile);
+ return joinPath(lastDir, untitledFileName);
+ }
+
+ const lastActiveFolder = this.historyService.getLastActiveWorkspaceRoot(schemeFilter);
+ if (lastActiveFolder) {
+ return joinPath(lastActiveFolder, untitledFileName);
+ }
+
+ return untitledResource.with({ path: untitledFileName });
+ }
+
+ async revert(resource: URI, options?: IRevertOptions): Promise {
+ const result = await this.revertAll([resource], options);
+
+ return result.results.length === 1 && !!result.results[0].success;
+ }
+
+ async revertAll(resources?: URI[], options?: IRevertOptions): Promise {
+
+ // Revert files first
+ const revertOperationResult = await this.doRevertAllFiles(resources, options);
+
+ // Revert untitled
+ const untitledReverted = this.untitledEditorService.revertAll(resources);
+ untitledReverted.forEach(untitled => revertOperationResult.results.push({ source: untitled, success: true }));
+
+ return revertOperationResult;
+ }
+
+ private async doRevertAllFiles(resources?: URI[], options?: IRevertOptions): Promise {
+ const fileModels = options && options.force ? this.getFileModels(resources) : this.getDirtyFileModels(resources);
+
+ const mapResourceToResult = new ResourceMap();
+ fileModels.forEach(m => {
+ mapResourceToResult.set(m.getResource(), {
+ source: m.getResource()
+ });
+ });
+
+ await Promise.all(fileModels.map(async model => {
+ try {
+ await model.revert(options && options.soft);
+
+ if (!model.isDirty()) {
+ const result = mapResourceToResult.get(model.getResource());
+ if (result) {
+ result.success = true;
+ }
+ }
+ } catch (error) {
+
+ // FileNotFound means the file got deleted meanwhile, so still record as successful revert
+ if ((error).fileOperationResult === FileOperationResult.FILE_NOT_FOUND) {
+ const result = mapResourceToResult.get(model.getResource());
+ if (result) {
+ result.success = true;
+ }
+ }
+
+ // Otherwise bubble up the error
+ else {
+ throw error;
+ }
+ }
+ }));
+
+ return { results: mapResourceToResult.values() };
+ }
+
+ getDirty(resources?: URI[]): URI[] {
+
+ // Collect files
+ const dirty = this.getDirtyFileModels(resources).map(m => m.getResource());
+
+ // Add untitled ones
+ dirty.push(...this.untitledEditorService.getDirty(resources));
+
+ return dirty;
+ }
+
+ isDirty(resource?: URI): boolean {
+
+ // Check for dirty file
+ if (this._models.getAll(resource).some(model => model.isDirty())) {
+ return true;
+ }
+
+ // Check for dirty untitled
+ return this.untitledEditorService.getDirty().some(dirty => !resource || dirty.toString() === resource.toString());
+ }
+
+ //#endregion
+
+ //#region config
+
+ getAutoSaveMode(): AutoSaveMode {
+ if (this.configuredAutoSaveOnFocusChange) {
+ return AutoSaveMode.ON_FOCUS_CHANGE;
+ }
+
+ if (this.configuredAutoSaveOnWindowChange) {
+ return AutoSaveMode.ON_WINDOW_CHANGE;
+ }
+
+ if (this.configuredAutoSaveDelay && this.configuredAutoSaveDelay > 0) {
+ return this.configuredAutoSaveDelay <= 1000 ? AutoSaveMode.AFTER_SHORT_DELAY : AutoSaveMode.AFTER_LONG_DELAY;
+ }
+
+ return AutoSaveMode.OFF;
+ }
+
+ getAutoSaveConfiguration(): IAutoSaveConfiguration {
+ return {
+ autoSaveDelay: this.configuredAutoSaveDelay && this.configuredAutoSaveDelay > 0 ? this.configuredAutoSaveDelay : undefined,
+ autoSaveFocusChange: !!this.configuredAutoSaveOnFocusChange,
+ autoSaveApplicationChange: !!this.configuredAutoSaveOnWindowChange
+ };
+ }
+
+ get isHotExitEnabled(): boolean {
+ return !this.environmentService.isExtensionDevelopment && this.configuredHotExit !== HotExitConfiguration.OFF;
+ }
+
+ //#endregion
+
+ dispose(): void {
+
+ // Clear all caches
+ this._models.clear();
+
+ super.dispose();
+ }
+}
+
+export async function promptSave(dialogService: IDialogService, resourcesToConfirm: readonly URI[]) {
+ const message = resourcesToConfirm.length === 1
+ ? nls.localize('saveChangesMessage', "Do you want to save the changes you made to {0}?", basename(resourcesToConfirm[0]))
+ : getConfirmMessage(nls.localize('saveChangesMessages', "Do you want to save the changes to the following {0} files?", resourcesToConfirm.length), resourcesToConfirm);
+
+ const buttons: string[] = [
+ resourcesToConfirm.length > 1 ? nls.localize({ key: 'saveAll', comment: ['&& denotes a mnemonic'] }, "&&Save All") : nls.localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save"),
+ nls.localize({ key: 'dontSave', comment: ['&& denotes a mnemonic'] }, "Do&&n't Save"),
+ nls.localize('cancel', "Cancel")
+ ];
+
+ const { choice } = await dialogService.show(Severity.Warning, message, buttons, {
+ cancelId: 2,
+ detail: nls.localize('saveChangesDetail', "Your changes will be lost if you don't save them.")
+ });
+
+ switch (choice) {
+ case 0: return ConfirmResult.SAVE;
+ case 1: return ConfirmResult.DONT_SAVE;
+ default: return ConfirmResult.CANCEL;
}
}
-registerSingleton(ITextFileService, BrowserTextFileService);
diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts
deleted file mode 100644
index 8c619f82fb8..00000000000
--- a/src/vs/workbench/services/textfile/common/textFileService.ts
+++ /dev/null
@@ -1,1069 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import * as nls from 'vs/nls';
-import { URI } from 'vs/base/common/uri';
-import * as errors from 'vs/base/common/errors';
-import * as objects from 'vs/base/common/objects';
-import { Event, Emitter } from 'vs/base/common/event';
-import * as platform from 'vs/base/common/platform';
-import { IWindowsService } from 'vs/platform/windows/common/windows';
-import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
-import { IResult, ITextFileOperationResult, ITextFileService, ITextFileStreamContent, IAutoSaveConfiguration, AutoSaveMode, SaveReason, ITextFileEditorModelManager, ITextFileEditorModel, ModelState, ISaveOptions, AutoSaveContext, IWillMoveEvent, ITextFileContent, IResourceEncodings, IReadTextFileOptions, IWriteTextFileOptions, toBufferOrReadable, TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles';
-import { ConfirmResult, IRevertOptions } from 'vs/workbench/common/editor';
-import { ILifecycleService, ShutdownReason, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
-import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
-import { IFileService, IFilesConfiguration, FileOperationError, FileOperationResult, AutoSaveConfiguration, HotExitConfiguration, IFileStatWithMetadata, ICreateFileOptions } from 'vs/platform/files/common/files';
-import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
-import { Disposable } from 'vs/base/common/lifecycle';
-import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
-import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
-import { UntitledEditorModel } from 'vs/workbench/common/editor/untitledEditorModel';
-import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager';
-import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
-import { ResourceMap } from 'vs/base/common/map';
-import { Schemas } from 'vs/base/common/network';
-import { IHistoryService } from 'vs/workbench/services/history/common/history';
-import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
-import { createTextBufferFactoryFromSnapshot, createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel';
-import { IModelService } from 'vs/editor/common/services/modelService';
-import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
-import { isEqualOrParent, isEqual, joinPath, dirname, extname, basename, toLocalResource } from 'vs/base/common/resources';
-import { getConfirmMessage, IDialogService, IFileDialogService, ISaveDialogOptions, IConfirmation } from 'vs/platform/dialogs/common/dialogs';
-import { IModeService } from 'vs/editor/common/services/modeService';
-import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
-import { coalesce } from 'vs/base/common/arrays';
-import { trim } from 'vs/base/common/strings';
-import { VSBuffer } from 'vs/base/common/buffer';
-import { ITextSnapshot } from 'vs/editor/common/model';
-import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
-import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry';
-
-/**
- * The workbench file service implementation implements the raw file service spec and adds additional methods on top.
- */
-export abstract class TextFileService extends Disposable implements ITextFileService {
-
- _serviceBrand: undefined;
-
- private readonly _onAutoSaveConfigurationChange: Emitter = this._register(new Emitter());
- readonly onAutoSaveConfigurationChange: Event = this._onAutoSaveConfigurationChange.event;
-
- private readonly _onFilesAssociationChange: Emitter = this._register(new Emitter());
- readonly onFilesAssociationChange: Event = this._onFilesAssociationChange.event;
-
- private readonly _onWillMove = this._register(new Emitter());
- readonly onWillMove: Event = this._onWillMove.event;
-
- private _models: TextFileEditorModelManager;
- get models(): ITextFileEditorModelManager { return this._models; }
-
- abstract get encoding(): IResourceEncodings;
-
- private currentFilesAssociationConfig: { [key: string]: string; };
- private configuredAutoSaveDelay?: number;
- private configuredAutoSaveOnFocusChange: boolean | undefined;
- private configuredAutoSaveOnWindowChange: boolean | undefined;
- private configuredHotExit: string | undefined;
- private autoSaveContext: IContextKey;
-
- constructor(
- @IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
- @IFileService protected readonly fileService: IFileService,
- @IUntitledEditorService protected readonly untitledEditorService: IUntitledEditorService,
- @ILifecycleService private readonly lifecycleService: ILifecycleService,
- @IInstantiationService protected instantiationService: IInstantiationService,
- @IConfigurationService private readonly configurationService: IConfigurationService,
- @IModeService private readonly modeService: IModeService,
- @IModelService private readonly modelService: IModelService,
- @IWorkbenchEnvironmentService protected readonly environmentService: IWorkbenchEnvironmentService,
- @INotificationService private readonly notificationService: INotificationService,
- @IBackupFileService private readonly backupFileService: IBackupFileService,
- @IWindowsService private readonly windowsService: IWindowsService,
- @IHistoryService private readonly historyService: IHistoryService,
- @IContextKeyService contextKeyService: IContextKeyService,
- @IDialogService private readonly dialogService: IDialogService,
- @IFileDialogService private readonly fileDialogService: IFileDialogService,
- @IEditorService private readonly editorService: IEditorService,
- @ITextResourceConfigurationService protected readonly textResourceConfigurationService: ITextResourceConfigurationService
- ) {
- super();
-
- this._models = this._register(instantiationService.createInstance(TextFileEditorModelManager));
- this.autoSaveContext = AutoSaveContext.bindTo(contextKeyService);
-
- const configuration = configurationService.getValue();
- this.currentFilesAssociationConfig = configuration && configuration.files && configuration.files.associations;
-
- this.onFilesConfigurationChange(configuration);
-
- this.registerListeners();
- }
-
- //#region event handling
-
- private registerListeners(): void {
-
- // Lifecycle
- this.lifecycleService.onBeforeShutdown(event => event.veto(this.onBeforeShutdown(event.reason)));
- this.lifecycleService.onShutdown(this.dispose, this);
-
- // Files configuration changes
- this._register(this.configurationService.onDidChangeConfiguration(e => {
- if (e.affectsConfiguration('files')) {
- this.onFilesConfigurationChange(this.configurationService.getValue());
- }
- }));
- }
-
- protected onBeforeShutdown(reason: ShutdownReason): boolean | Promise {
-
- // Dirty files need treatment on shutdown
- const dirty = this.getDirty();
- if (dirty.length) {
-
- // If auto save is enabled, save all files and then check again for dirty files
- // We DO NOT run any save participant if we are in the shutdown phase for performance reasons
- if (this.getAutoSaveMode() !== AutoSaveMode.OFF) {
- return this.saveAll(false /* files only */, { skipSaveParticipants: true }).then(() => {
-
- // If we still have dirty files, we either have untitled ones or files that cannot be saved
- const remainingDirty = this.getDirty();
- if (remainingDirty.length) {
- return this.handleDirtyBeforeShutdown(remainingDirty, reason);
- }
-
- return false;
- });
- }
-
- // Auto save is not enabled
- return this.handleDirtyBeforeShutdown(dirty, reason);
- }
-
- // No dirty files: no veto
- return this.noVeto({ cleanUpBackups: true });
- }
-
- private handleDirtyBeforeShutdown(dirty: URI[], reason: ShutdownReason): boolean | Promise {
-
- // If hot exit is enabled, backup dirty files and allow to exit without confirmation
- if (this.isHotExitEnabled) {
- return this.backupBeforeShutdown(dirty, reason).then(didBackup => {
- if (didBackup) {
- return this.noVeto({ cleanUpBackups: false }); // no veto and no backup cleanup (since backup was successful)
- }
-
- // since a backup did not happen, we have to confirm for the dirty files now
- return this.confirmBeforeShutdown();
- }, error => {
- this.notificationService.error(nls.localize('files.backup.failSave', "Files that are dirty could not be written to the backup location (Error: {0}). Try saving your files first and then exit.", error.message));
-
- return true; // veto, the backups failed
- });
- }
-
- // Otherwise just confirm from the user what to do with the dirty files
- return this.confirmBeforeShutdown();
- }
-
- private async backupBeforeShutdown(dirtyToBackup: URI[], reason: ShutdownReason): Promise {
- const windowCount = await this.windowsService.getWindowCount();
-
- // When quit is requested skip the confirm callback and attempt to backup all workspaces.
- // When quit is not requested the confirm callback should be shown when the window being
- // closed is the only VS Code window open, except for on Mac where hot exit is only
- // ever activated when quit is requested.
-
- let doBackup: boolean | undefined;
- switch (reason) {
- case ShutdownReason.CLOSE:
- if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.configuredHotExit === HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE) {
- doBackup = true; // backup if a folder is open and onExitAndWindowClose is configured
- } else if (windowCount > 1 || platform.isMacintosh) {
- doBackup = false; // do not backup if a window is closed that does not cause quitting of the application
- } else {
- doBackup = true; // backup if last window is closed on win/linux where the application quits right after
- }
- break;
-
- case ShutdownReason.QUIT:
- doBackup = true; // backup because next start we restore all backups
- break;
-
- case ShutdownReason.RELOAD:
- doBackup = true; // backup because after window reload, backups restore
- break;
-
- case ShutdownReason.LOAD:
- if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.configuredHotExit === HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE) {
- doBackup = true; // backup if a folder is open and onExitAndWindowClose is configured
- } else {
- doBackup = false; // do not backup because we are switching contexts
- }
- break;
- }
-
- if (!doBackup) {
- return false;
- }
-
- await this.backupAll(dirtyToBackup);
-
- return true;
- }
-
- private backupAll(dirtyToBackup: URI[]): Promise {
-
- // split up between files and untitled
- const filesToBackup: ITextFileEditorModel[] = [];
- const untitledToBackup: URI[] = [];
- dirtyToBackup.forEach(dirty => {
- if (this.fileService.canHandleResource(dirty)) {
- const model = this.models.get(dirty);
- if (model) {
- filesToBackup.push(model);
- }
- } else if (dirty.scheme === Schemas.untitled) {
- untitledToBackup.push(dirty);
- }
- });
-
- return this.doBackupAll(filesToBackup, untitledToBackup);
- }
-
- private async doBackupAll(dirtyFileModels: ITextFileEditorModel[], untitledResources: URI[]): Promise {
-
- // Handle file resources first
- await Promise.all(dirtyFileModels.map(model => model.backup()));
-
- // Handle untitled resources
- await Promise.all(untitledResources
- .filter(untitled => this.untitledEditorService.exists(untitled))
- .map(async untitled => (await this.untitledEditorService.loadOrCreate({ resource: untitled })).backup()));
- }
-
- private async confirmBeforeShutdown(): Promise {
- const confirm = await this.confirmSave();
-
- // Save
- if (confirm === ConfirmResult.SAVE) {
- const result = await this.saveAll(true /* includeUntitled */, { skipSaveParticipants: true });
-
- if (result.results.some(r => !r.success)) {
- return true; // veto if some saves failed
- }
-
- return this.noVeto({ cleanUpBackups: true });
- }
-
- // Don't Save
- else if (confirm === ConfirmResult.DONT_SAVE) {
-
- // Make sure to revert untitled so that they do not restore
- // see https://github.com/Microsoft/vscode/issues/29572
- this.untitledEditorService.revertAll();
-
- return this.noVeto({ cleanUpBackups: true });
- }
-
- // Cancel
- else if (confirm === ConfirmResult.CANCEL) {
- return true; // veto
- }
-
- return false;
- }
-
- private noVeto(options: { cleanUpBackups: boolean }): boolean | Promise {
- if (!options.cleanUpBackups) {
- return false;
- }
-
- if (this.lifecycleService.phase < LifecyclePhase.Restored) {
- return false; // if editors have not restored, we are not up to speed with backups and thus should not clean them
- }
-
- return this.cleanupBackupsBeforeShutdown().then(() => false, () => false);
- }
-
- protected async cleanupBackupsBeforeShutdown(): Promise {
- if (this.environmentService.isExtensionDevelopment) {
- return;
- }
-
- await this.backupFileService.discardAllWorkspaceBackups();
- }
-
- protected onFilesConfigurationChange(configuration: IFilesConfiguration): void {
- const wasAutoSaveEnabled = (this.getAutoSaveMode() !== AutoSaveMode.OFF);
-
- const autoSaveMode = (configuration && configuration.files && configuration.files.autoSave) || AutoSaveConfiguration.OFF;
- this.autoSaveContext.set(autoSaveMode);
- switch (autoSaveMode) {
- case AutoSaveConfiguration.AFTER_DELAY:
- this.configuredAutoSaveDelay = configuration && configuration.files && configuration.files.autoSaveDelay;
- this.configuredAutoSaveOnFocusChange = false;
- this.configuredAutoSaveOnWindowChange = false;
- break;
-
- case AutoSaveConfiguration.ON_FOCUS_CHANGE:
- this.configuredAutoSaveDelay = undefined;
- this.configuredAutoSaveOnFocusChange = true;
- this.configuredAutoSaveOnWindowChange = false;
- break;
-
- case AutoSaveConfiguration.ON_WINDOW_CHANGE:
- this.configuredAutoSaveDelay = undefined;
- this.configuredAutoSaveOnFocusChange = false;
- this.configuredAutoSaveOnWindowChange = true;
- break;
-
- default:
- this.configuredAutoSaveDelay = undefined;
- this.configuredAutoSaveOnFocusChange = false;
- this.configuredAutoSaveOnWindowChange = false;
- break;
- }
-
- // Emit as event
- this._onAutoSaveConfigurationChange.fire(this.getAutoSaveConfiguration());
-
- // save all dirty when enabling auto save
- if (!wasAutoSaveEnabled && this.getAutoSaveMode() !== AutoSaveMode.OFF) {
- this.saveAll();
- }
-
- // Check for change in files associations
- const filesAssociation = configuration && configuration.files && configuration.files.associations;
- if (!objects.equals(this.currentFilesAssociationConfig, filesAssociation)) {
- this.currentFilesAssociationConfig = filesAssociation;
- this._onFilesAssociationChange.fire();
- }
-
- // Hot exit
- const hotExitMode = configuration && configuration.files && configuration.files.hotExit;
- if (hotExitMode === HotExitConfiguration.OFF || hotExitMode === HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE) {
- this.configuredHotExit = hotExitMode;
- } else {
- this.configuredHotExit = HotExitConfiguration.ON_EXIT;
- }
- }
-
- //#endregion
-
- //#region primitives (read, create, move, delete, update)
-
- async read(resource: URI, options?: IReadTextFileOptions): Promise {
- const content = await this.fileService.readFile(resource, options);
-
- // in case of acceptTextOnly: true, we check the first
- // chunk for possibly being binary by looking for 0-bytes
- // we limit this check to the first 512 bytes
- this.validateBinary(content.value, options);
-
- return {
- ...content,
- encoding: 'utf8',
- value: content.value.toString()
- };
- }
-
- async readStream(resource: URI, options?: IReadTextFileOptions): Promise {
- const stream = await this.fileService.readFileStream(resource, options);
-
- // in case of acceptTextOnly: true, we check the first
- // chunk for possibly being binary by looking for 0-bytes
- // we limit this check to the first 512 bytes
- let checkedForBinary = false;
- const throwOnBinary = (data: VSBuffer): Error | undefined => {
- if (!checkedForBinary) {
- checkedForBinary = true;
-
- this.validateBinary(data, options);
- }
-
- return undefined;
- };
-
- return {
- ...stream,
- encoding: 'utf8',
- value: await createTextBufferFactoryFromStream(stream.value, undefined, options && options.acceptTextOnly ? throwOnBinary : undefined)
- };
- }
-
- private validateBinary(buffer: VSBuffer, options?: IReadTextFileOptions): void {
- if (!options || !options.acceptTextOnly) {
- return; // no validation needed
- }
-
- // in case of acceptTextOnly: true, we check the first
- // chunk for possibly being binary by looking for 0-bytes
- // we limit this check to the first 512 bytes
- for (let i = 0; i < buffer.byteLength && i < 512; i++) {
- if (buffer.readUInt8(i) === 0) {
- throw new TextFileOperationError(nls.localize('fileBinaryError', "File seems to be binary and cannot be opened as text"), TextFileOperationResult.FILE_IS_BINARY, options);
- }
- }
- }
-
- async create(resource: URI, value?: string | ITextSnapshot, options?: ICreateFileOptions): Promise {
- const stat = await this.doCreate(resource, value, options);
-
- // If we had an existing model for the given resource, load
- // it again to make sure it is up to date with the contents
- // we just wrote into the underlying resource by calling
- // revert()
- const existingModel = this.models.get(resource);
- if (existingModel && !existingModel.isDisposed()) {
- await existingModel.revert();
- }
-
- return stat;
- }
-
- protected doCreate(resource: URI, value?: string | ITextSnapshot, options?: ICreateFileOptions): Promise {
- return this.fileService.createFile(resource, toBufferOrReadable(value), options);
- }
-
- async write(resource: URI, value: string | ITextSnapshot, options?: IWriteTextFileOptions): Promise {
- return this.fileService.writeFile(resource, toBufferOrReadable(value), options);
- }
-
- async delete(resource: URI, options?: { useTrash?: boolean, recursive?: boolean }): Promise {
- const dirtyFiles = this.getDirty().filter(dirty => isEqualOrParent(dirty, resource));
-
- await this.revertAll(dirtyFiles, { soft: true });
-
- return this.fileService.del(resource, options);
- }
-
- async move(source: URI, target: URI, overwrite?: boolean): Promise {
-
- // await onWillMove event joiners
- await this.notifyOnWillMove(source, target);
-
- // find all models that related to either source or target (can be many if resource is a folder)
- const sourceModels: ITextFileEditorModel[] = [];
- const conflictingModels: ITextFileEditorModel[] = [];
- for (const model of this.getFileModels()) {
- const resource = model.getResource();
-
- if (isEqualOrParent(resource, target, false /* do not ignorecase, see https://github.com/Microsoft/vscode/issues/56384 */)) {
- conflictingModels.push(model);
- }
-
- if (isEqualOrParent(resource, source)) {
- sourceModels.push(model);
- }
- }
-
- // remember each source model to load again after move is done
- // with optional content to restore if it was dirty
- type ModelToRestore = { resource: URI; snapshot?: ITextSnapshot };
- const modelsToRestore: ModelToRestore[] = [];
- for (const sourceModel of sourceModels) {
- const sourceModelResource = sourceModel.getResource();
-
- // If the source is the actual model, just use target as new resource
- let modelToRestoreResource: URI;
- if (isEqual(sourceModelResource, source)) {
- modelToRestoreResource = target;
- }
-
- // Otherwise a parent folder of the source is being moved, so we need
- // to compute the target resource based on that
- else {
- modelToRestoreResource = joinPath(target, sourceModelResource.path.substr(source.path.length + 1));
- }
-
- const modelToRestore: ModelToRestore = { resource: modelToRestoreResource };
- if (sourceModel.isDirty()) {
- modelToRestore.snapshot = sourceModel.createSnapshot();
- }
-
- modelsToRestore.push(modelToRestore);
- }
-
- // in order to move, we need to soft revert all dirty models,
- // both from the source as well as the target if any
- const dirtyModels = [...sourceModels, ...conflictingModels].filter(model => model.isDirty());
- await this.revertAll(dirtyModels.map(dirtyModel => dirtyModel.getResource()), { soft: true });
-
- // now we can rename the source to target via file operation
- let stat: IFileStatWithMetadata;
- try {
- stat = await this.fileService.move(source, target, overwrite);
- } catch (error) {
-
- // in case of any error, ensure to set dirty flag back
- dirtyModels.forEach(dirtyModel => dirtyModel.makeDirty());
-
- throw error;
- }
-
- // finally, restore models that we had loaded previously
- await Promise.all(modelsToRestore.map(async modelToRestore => {
-
- // restore the model, forcing a reload. this is important because
- // we know the file has changed on disk after the move and the
- // model might have still existed with the previous state. this
- // ensures we are not tracking a stale state.
- const restoredModel = await this.models.loadOrCreate(modelToRestore.resource, { reload: { async: false } });
-
- // restore previous dirty content if any and ensure to mark
- // the model as dirty
- if (modelToRestore.snapshot && restoredModel.isResolved()) {
- this.modelService.updateModel(restoredModel.textEditorModel, createTextBufferFactoryFromSnapshot(modelToRestore.snapshot));
-
- restoredModel.makeDirty();
- }
- }));
-
- return stat;
- }
-
- private async notifyOnWillMove(source: URI, target: URI): Promise {
- const waitForPromises: Promise[] = [];
-
- // fire event
- this._onWillMove.fire({
- oldResource: source,
- newResource: target,
- waitUntil(promise: Promise) {
- waitForPromises.push(promise.then(undefined, errors.onUnexpectedError));
- }
- });
-
- // prevent async waitUntil-calls
- Object.freeze(waitForPromises);
-
- await Promise.all(waitForPromises);
- }
-
- //#endregion
-
- //#region save/revert
-
- async save(resource: URI, options?: ISaveOptions): Promise {
-
- // Run a forced save if we detect the file is not dirty so that save participants can still run
- if (options && options.force && this.fileService.canHandleResource(resource) && !this.isDirty(resource)) {
- const model = this._models.get(resource);
- if (model) {
- options.reason = SaveReason.EXPLICIT;
-
- await model.save(options);
-
- return !model.isDirty();
- }
- }
-
- const result = await this.saveAll([resource], options);
-
- return result.results.length === 1 && !!result.results[0].success;
- }
-
- async confirmSave(resources?: URI[]): Promise {
- if (this.environmentService.isExtensionDevelopment) {
- if (!this.environmentService.args['extension-development-confirm-save']) {
- return ConfirmResult.DONT_SAVE; // no veto when we are in extension dev mode because we cannot assume we run interactive (e.g. tests)
- }
- }
-
- const resourcesToConfirm = this.getDirty(resources);
- if (resourcesToConfirm.length === 0) {
- return ConfirmResult.DONT_SAVE;
- }
- return promptSave(this.dialogService, resourcesToConfirm);
- }
-
- async confirmOverwrite(resource: URI): Promise {
- const confirm: IConfirmation = {
- message: nls.localize('confirmOverwrite', "'{0}' already exists. Do you want to replace it?", basename(resource)),
- detail: nls.localize('irreversible', "A file or folder with the same name already exists in the folder {0}. Replacing it will overwrite its current contents.", basename(dirname(resource))),
- primaryButton: nls.localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace"),
- type: 'warning'
- };
-
- return (await this.dialogService.confirm(confirm)).confirmed;
- }
-
- saveAll(includeUntitled?: boolean, options?: ISaveOptions): Promise;
- saveAll(resources: URI[], options?: ISaveOptions): Promise;
- saveAll(arg1?: boolean | URI[], options?: ISaveOptions): Promise {
-
- // get all dirty
- let toSave: URI[] = [];
- if (Array.isArray(arg1)) {
- toSave = this.getDirty(arg1);
- } else {
- toSave = this.getDirty();
- }
-
- // split up between files and untitled
- const filesToSave: URI[] = [];
- const untitledToSave: URI[] = [];
- toSave.forEach(resourceToSave => {
- if ((Array.isArray(arg1) || arg1 === true /* includeUntitled */) && resourceToSave.scheme === Schemas.untitled) {
- untitledToSave.push(resourceToSave);
- } else {
- filesToSave.push(resourceToSave);
- }
- });
-
- return this.doSaveAll(filesToSave, untitledToSave, options);
- }
-
- private async doSaveAll(fileResources: URI[], untitledResources: URI[], options?: ISaveOptions): Promise {
-
- // Handle files first that can just be saved
- const result = await this.doSaveAllFiles(fileResources, options);
-
- // Preflight for untitled to handle cancellation from the dialog
- const targetsForUntitled: URI[] = [];
- for (const untitled of untitledResources) {
- if (this.untitledEditorService.exists(untitled)) {
- let targetUri: URI;
-
- // Untitled with associated file path don't need to prompt
- if (this.untitledEditorService.hasAssociatedFilePath(untitled)) {
- targetUri = toLocalResource(untitled, this.environmentService.configuration.remoteAuthority);
- }
-
- // Otherwise ask user
- else {
- const targetPath = await this.promptForPath(untitled, this.suggestFileName(untitled));
- if (!targetPath) {
- return { results: [...fileResources, ...untitledResources].map(r => ({ source: r })) };
- }
-
- targetUri = targetPath;
- }
-
- targetsForUntitled.push(targetUri);
- }
- }
-
- // Handle untitled
- await Promise.all(targetsForUntitled.map(async (target, index) => {
- const uri = await this.saveAs(untitledResources[index], target);
-
- result.results.push({
- source: untitledResources[index],
- target: uri,
- success: !!uri
- });
- }));
-
- return result;
- }
-
- protected async promptForPath(resource: URI, defaultUri: URI, availableFileSystems?: string[]): Promise {
-
- // Help user to find a name for the file by opening it first
- await this.editorService.openEditor({ resource, options: { revealIfOpened: true, preserveFocus: true } });
-
- return this.fileDialogService.pickFileToSave(this.getSaveDialogOptions(defaultUri, availableFileSystems));
- }
-
- private getSaveDialogOptions(defaultUri: URI, availableFileSystems?: string[]): ISaveDialogOptions {
- const options: ISaveDialogOptions = {
- defaultUri,
- title: nls.localize('saveAsTitle', "Save As"),
- availableFileSystems,
- };
-
- // Filters are only enabled on Windows where they work properly
- if (!platform.isWindows) {
- return options;
- }
-
- interface IFilter { name: string; extensions: string[]; }
-
- // Build the file filter by using our known languages
- const ext: string | undefined = defaultUri ? extname(defaultUri) : undefined;
- let matchingFilter: IFilter | undefined;
- const filters: IFilter[] = coalesce(this.modeService.getRegisteredLanguageNames().map(languageName => {
- const extensions = this.modeService.getExtensions(languageName);
- if (!extensions || !extensions.length) {
- return null;
- }
-
- const filter: IFilter = { name: languageName, extensions: extensions.slice(0, 10).map(e => trim(e, '.')) };
-
- if (ext && extensions.indexOf(ext) >= 0) {
- matchingFilter = filter;
-
- return null; // matching filter will be added last to the top
- }
-
- return filter;
- }));
-
- // Filters are a bit weird on Windows, based on having a match or not:
- // Match: we put the matching filter first so that it shows up selected and the all files last
- // No match: we put the all files filter first
- const allFilesFilter = { name: nls.localize('allFiles', "All Files"), extensions: ['*'] };
- if (matchingFilter) {
- filters.unshift(matchingFilter);
- filters.unshift(allFilesFilter);
- } else {
- filters.unshift(allFilesFilter);
- }
-
- // Allow to save file without extension
- filters.push({ name: nls.localize('noExt', "No Extension"), extensions: [''] });
-
- options.filters = filters;
-
- return options;
- }
-
- private async doSaveAllFiles(resources?: URI[], options: ISaveOptions = Object.create(null)): Promise {
- const dirtyFileModels = this.getDirtyFileModels(Array.isArray(resources) ? resources : undefined /* Save All */)
- .filter(model => {
- if ((model.hasState(ModelState.CONFLICT) || model.hasState(ModelState.ERROR)) && (options.reason === SaveReason.AUTO || options.reason === SaveReason.FOCUS_CHANGE || options.reason === SaveReason.WINDOW_CHANGE)) {
- return false; // if model is in save conflict or error, do not save unless save reason is explicit or not provided at all
- }
-
- return true;
- });
-
- const mapResourceToResult = new ResourceMap();
- dirtyFileModels.forEach(m => {
- mapResourceToResult.set(m.getResource(), {
- source: m.getResource()
- });
- });
-
- await Promise.all(dirtyFileModels.map(async model => {
- await model.save(options);
-
- if (!model.isDirty()) {
- const result = mapResourceToResult.get(model.getResource());
- if (result) {
- result.success = true;
- }
- }
- }));
-
- return { results: mapResourceToResult.values() };
- }
-
- private getFileModels(arg1?: URI | URI[]): ITextFileEditorModel[] {
- if (Array.isArray(arg1)) {
- const models: ITextFileEditorModel[] = [];
- arg1.forEach(resource => {
- models.push(...this.getFileModels(resource));
- });
-
- return models;
- }
-
- return this._models.getAll(arg1);
- }
-
- private getDirtyFileModels(resources?: URI | URI[]): ITextFileEditorModel[] {
- return this.getFileModels(resources).filter(model => model.isDirty());
- }
-
- async saveAs(resource: URI, targetResource?: URI, options?: ISaveOptions): Promise {
-
- // Get to target resource
- if (!targetResource) {
- let dialogPath = resource;
- if (resource.scheme === Schemas.untitled) {
- dialogPath = this.suggestFileName(resource);
- }
-
- targetResource = await this.promptForPath(resource, dialogPath, options ? options.availableFileSystems : undefined);
- }
-
- if (!targetResource) {
- return; // user canceled
- }
-
- // Just save if target is same as models own resource
- if (resource.toString() === targetResource.toString()) {
- await this.save(resource, options);
-
- return resource;
- }
-
- // Do it
- return this.doSaveAs(resource, targetResource, options);
- }
-
- private async doSaveAs(resource: URI, target: URI, options?: ISaveOptions): Promise {
-
- // Retrieve text model from provided resource if any
- let model: ITextFileEditorModel | UntitledEditorModel | undefined;
- if (this.fileService.canHandleResource(resource)) {
- model = this._models.get(resource);
- } else if (resource.scheme === Schemas.untitled && this.untitledEditorService.exists(resource)) {
- model = await this.untitledEditorService.loadOrCreate({ resource });
- }
-
- // We have a model: Use it (can be null e.g. if this file is binary and not a text file or was never opened before)
- let result: boolean;
- if (model) {
- result = await this.doSaveTextFileAs(model, resource, target, options);
- }
-
- // Otherwise we can only copy
- else {
- await this.fileService.copy(resource, target);
-
- result = true;
- }
-
- // Return early if the operation was not running
- if (!result) {
- return target;
- }
-
- // Revert the source
- await this.revert(resource);
-
- return target;
- }
-
- private async doSaveTextFileAs(sourceModel: ITextFileEditorModel | UntitledEditorModel, resource: URI, target: URI, options?: ISaveOptions): Promise {
-
- // Prefer an existing model if it is already loaded for the given target resource
- let targetExists: boolean = false;
- let targetModel = this.models.get(target);
- if (targetModel && targetModel.isResolved()) {
- targetExists = true;
- }
-
- // Otherwise create the target file empty if it does not exist already and resolve it from there
- else {
- targetExists = await this.fileService.exists(target);
-
- // create target model adhoc if file does not exist yet
- if (!targetExists) {
- await this.create(target, '');
- }
-
- targetModel = await this.models.loadOrCreate(target);
- }
-
- try {
-
- // Confirm to overwrite if we have an untitled file with associated file where
- // the file actually exists on disk and we are instructed to save to that file
- // path. This can happen if the file was created after the untitled file was opened.
- // See https://github.com/Microsoft/vscode/issues/67946
- let write: boolean;
- if (sourceModel instanceof UntitledEditorModel && sourceModel.hasAssociatedFilePath && targetExists && isEqual(target, toLocalResource(sourceModel.getResource(), this.environmentService.configuration.remoteAuthority))) {
- write = await this.confirmOverwrite(target);
- } else {
- write = true;
- }
-
- if (!write) {
- return false;
- }
-
- // take over model value, encoding and mode (only if more specific) from source model
- targetModel.updatePreferredEncoding(sourceModel.getEncoding());
- if (sourceModel.isResolved() && targetModel.isResolved()) {
- this.modelService.updateModel(targetModel.textEditorModel, createTextBufferFactoryFromSnapshot(sourceModel.createSnapshot()));
-
- const sourceMode = sourceModel.textEditorModel.getLanguageIdentifier();
- const targetMode = targetModel.textEditorModel.getLanguageIdentifier();
- if (sourceMode.language !== PLAINTEXT_MODE_ID && targetMode.language === PLAINTEXT_MODE_ID) {
- targetModel.textEditorModel.setMode(sourceMode); // only use if more specific than plain/text
- }
- }
-
- // save model
- await targetModel.save(options);
-
- return true;
- } catch (error) {
-
- // binary model: delete the file and run the operation again
- if (
- (error).textFileOperationResult === TextFileOperationResult.FILE_IS_BINARY ||
- (