diff --git a/README.md b/README.md index 670195c3c92..b0a86b728ae 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ a code editor with what developers need for their core edit-build-debug cycle. Code provides comprehensive editing and debugging support, an extensibility model, and lightweight integration with existing tools. -VS Code is updated monthly with new features and bug fixes. You can download it for Windows, macOS, and Linux on [VS Code's website](https://code.visualstudio.com/Download). To get the latest releases everyday, you can install the [Insiders version of VS Code](https://code.visualstudio.com/insiders). This builds from the master branch and is updated at least daily. +VS Code is updated monthly with new features and bug fixes. You can download it for Windows, macOS, and Linux on [VS Code's website](https://code.visualstudio.com/Download). To get the latest releases every day, you can install the [Insiders version of VS Code](https://code.visualstudio.com/insiders). This builds from the master branch and is updated at least daily.

VS Code in action diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 8915457164b..f1807d26252 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -43,7 +43,7 @@ const nodeModules = ['electron', 'original-fs'] // Build const builtInExtensions = [ - { name: 'ms-vscode.node-debug', version: '1.17.3' }, + { name: 'ms-vscode.node-debug', version: '1.17.5' }, { name: 'ms-vscode.node-debug2', version: '1.17.1' } ]; diff --git a/build/tfs/win32/smoketest.ps1 b/build/tfs/win32/smoketest.ps1 index 072707d0432..65f6896806d 100644 --- a/build/tfs/win32/smoketest.ps1 +++ b/build/tfs/win32/smoketest.ps1 @@ -38,7 +38,7 @@ step "Build minified" { } step "Run smoke test" { - $Screenshots = "$env:AGENT_BUILDDIRECTORY\smoketests-screenshots" + $Screenshots = "$env:AGENT_BUILDDIRECTORY\smoketest-screenshots" Remove-Item -Recurse -Force -ErrorAction Ignore $Screenshots exec { & Push-Location test\smoke } diff --git a/extensions/coffeescript/test/colorize-results/test-regex_coffee.json b/extensions/coffeescript/test/colorize-results/test-regex_coffee.json index b6a7a06b854..445fab1f1c2 100644 --- a/extensions/coffeescript/test/colorize-results/test-regex_coffee.json +++ b/extensions/coffeescript/test/colorize-results/test-regex_coffee.json @@ -69,8 +69,8 @@ "c": "(", "t": "source.coffee string.regexp.coffee meta.group.regexp punctuation.definition.group.regexp", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "punctuation.definition.group.regexp: #CE9178", + "light_plus": "punctuation.definition.group.regexp: #D16969", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "string.regexp: #D16969" @@ -80,19 +80,19 @@ "c": "\\d", "t": "source.coffee string.regexp.coffee meta.group.regexp constant.character.character-class.regexp", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "constant.character.character-class.regexp: #D16969", + "light_plus": "constant.character.character-class.regexp: #811F3F", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", - "hc_black": "string.regexp: #D16969" + "hc_black": "constant.character: #569CD6" } }, { "c": "+", "t": "source.coffee string.regexp.coffee meta.group.regexp keyword.operator.quantifier.regexp", "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", + "dark_plus": "keyword.operator.quantifier.regexp: #D7BA7D", + "light_plus": "keyword.operator.quantifier.regexp: #000000", "dark_vs": "keyword.operator: #D4D4D4", "light_vs": "keyword.operator: #000000", "hc_black": "keyword.operator: #D4D4D4" @@ -102,8 +102,8 @@ "c": ")", "t": "source.coffee string.regexp.coffee meta.group.regexp punctuation.definition.group.regexp", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "punctuation.definition.group.regexp: #CE9178", + "light_plus": "punctuation.definition.group.regexp: #D16969", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "string.regexp: #D16969" @@ -454,11 +454,11 @@ "c": "#{", "t": "source.coffee string.regexp.coffee source.coffee.embedded.source punctuation.section.embedded.coffee", "r": { - "dark_plus": "punctuation.section.embedded.coffee: #569CD6", - "light_plus": "punctuation.section.embedded.coffee: #0000FF", - "dark_vs": "punctuation.section.embedded.coffee: #569CD6", - "light_vs": "punctuation.section.embedded.coffee: #0000FF", - "hc_black": "punctuation.section.embedded.coffee: #569CD6" + "dark_plus": "punctuation.section.embedded: #569CD6", + "light_plus": "punctuation.section.embedded: #0000FF", + "dark_vs": "punctuation.section.embedded: #569CD6", + "light_vs": "punctuation.section.embedded: #0000FF", + "hc_black": "punctuation.section.embedded: #569CD6" } }, { @@ -476,11 +476,11 @@ "c": "}", "t": "source.coffee string.regexp.coffee source.coffee.embedded.source punctuation.section.embedded.coffee", "r": { - "dark_plus": "punctuation.section.embedded.coffee: #569CD6", - "light_plus": "punctuation.section.embedded.coffee: #0000FF", - "dark_vs": "punctuation.section.embedded.coffee: #569CD6", - "light_vs": "punctuation.section.embedded.coffee: #0000FF", - "hc_black": "punctuation.section.embedded.coffee: #569CD6" + "dark_plus": "punctuation.section.embedded: #569CD6", + "light_plus": "punctuation.section.embedded: #0000FF", + "dark_vs": "punctuation.section.embedded: #569CD6", + "light_vs": "punctuation.section.embedded: #0000FF", + "hc_black": "punctuation.section.embedded: #569CD6" } }, { diff --git a/extensions/coffeescript/test/colorize-results/test_coffee.json b/extensions/coffeescript/test/colorize-results/test_coffee.json index 55a97e8b7ef..a36d10633bf 100644 --- a/extensions/coffeescript/test/colorize-results/test_coffee.json +++ b/extensions/coffeescript/test/colorize-results/test_coffee.json @@ -311,11 +311,11 @@ "c": "#{", "t": "source.coffee meta.function-call.coffee meta.arguments.coffee string.quoted.double.coffee source.coffee.embedded.source punctuation.section.embedded.coffee", "r": { - "dark_plus": "punctuation.section.embedded.coffee: #569CD6", - "light_plus": "punctuation.section.embedded.coffee: #0000FF", - "dark_vs": "punctuation.section.embedded.coffee: #569CD6", - "light_vs": "punctuation.section.embedded.coffee: #0000FF", - "hc_black": "punctuation.section.embedded.coffee: #569CD6" + "dark_plus": "punctuation.section.embedded: #569CD6", + "light_plus": "punctuation.section.embedded: #0000FF", + "dark_vs": "punctuation.section.embedded: #569CD6", + "light_vs": "punctuation.section.embedded: #0000FF", + "hc_black": "punctuation.section.embedded: #569CD6" } }, { @@ -333,11 +333,11 @@ "c": "}", "t": "source.coffee meta.function-call.coffee meta.arguments.coffee string.quoted.double.coffee source.coffee.embedded.source punctuation.section.embedded.coffee", "r": { - "dark_plus": "punctuation.section.embedded.coffee: #569CD6", - "light_plus": "punctuation.section.embedded.coffee: #0000FF", - "dark_vs": "punctuation.section.embedded.coffee: #569CD6", - "light_vs": "punctuation.section.embedded.coffee: #0000FF", - "hc_black": "punctuation.section.embedded.coffee: #569CD6" + "dark_plus": "punctuation.section.embedded: #569CD6", + "light_plus": "punctuation.section.embedded: #0000FF", + "dark_vs": "punctuation.section.embedded: #569CD6", + "light_vs": "punctuation.section.embedded: #0000FF", + "hc_black": "punctuation.section.embedded: #569CD6" } }, { @@ -575,11 +575,11 @@ "c": "#{", "t": "source.coffee meta.function-call.coffee meta.arguments.coffee string.quoted.double.coffee source.coffee.embedded.source punctuation.section.embedded.coffee", "r": { - "dark_plus": "punctuation.section.embedded.coffee: #569CD6", - "light_plus": "punctuation.section.embedded.coffee: #0000FF", - "dark_vs": "punctuation.section.embedded.coffee: #569CD6", - "light_vs": "punctuation.section.embedded.coffee: #0000FF", - "hc_black": "punctuation.section.embedded.coffee: #569CD6" + "dark_plus": "punctuation.section.embedded: #569CD6", + "light_plus": "punctuation.section.embedded: #0000FF", + "dark_vs": "punctuation.section.embedded: #569CD6", + "light_vs": "punctuation.section.embedded: #0000FF", + "hc_black": "punctuation.section.embedded: #569CD6" } }, { @@ -597,11 +597,11 @@ "c": "}", "t": "source.coffee meta.function-call.coffee meta.arguments.coffee string.quoted.double.coffee source.coffee.embedded.source punctuation.section.embedded.coffee", "r": { - "dark_plus": "punctuation.section.embedded.coffee: #569CD6", - "light_plus": "punctuation.section.embedded.coffee: #0000FF", - "dark_vs": "punctuation.section.embedded.coffee: #569CD6", - "light_vs": "punctuation.section.embedded.coffee: #0000FF", - "hc_black": "punctuation.section.embedded.coffee: #569CD6" + "dark_plus": "punctuation.section.embedded: #569CD6", + "light_plus": "punctuation.section.embedded: #0000FF", + "dark_vs": "punctuation.section.embedded: #569CD6", + "light_vs": "punctuation.section.embedded: #0000FF", + "hc_black": "punctuation.section.embedded: #569CD6" } }, { @@ -1378,8 +1378,8 @@ "c": "(", "t": "source.coffee string.regexp.coffee meta.group.regexp punctuation.definition.group.regexp", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "punctuation.definition.group.regexp: #CE9178", + "light_plus": "punctuation.definition.group.regexp: #D16969", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "string.regexp: #D16969" @@ -1389,19 +1389,19 @@ "c": "\\d", "t": "source.coffee string.regexp.coffee meta.group.regexp constant.character.character-class.regexp", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "constant.character.character-class.regexp: #D16969", + "light_plus": "constant.character.character-class.regexp: #811F3F", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", - "hc_black": "string.regexp: #D16969" + "hc_black": "constant.character: #569CD6" } }, { "c": "+", "t": "source.coffee string.regexp.coffee meta.group.regexp keyword.operator.quantifier.regexp", "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", + "dark_plus": "keyword.operator.quantifier.regexp: #D7BA7D", + "light_plus": "keyword.operator.quantifier.regexp: #000000", "dark_vs": "keyword.operator: #D4D4D4", "light_vs": "keyword.operator: #000000", "hc_black": "keyword.operator: #D4D4D4" @@ -1411,8 +1411,8 @@ "c": ")", "t": "source.coffee string.regexp.coffee meta.group.regexp punctuation.definition.group.regexp", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "punctuation.definition.group.regexp: #CE9178", + "light_plus": "punctuation.definition.group.regexp: #D16969", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "string.regexp: #D16969" @@ -1466,8 +1466,8 @@ "c": "(", "t": "source.coffee string.regexp.coffee meta.group.regexp punctuation.definition.group.regexp", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "punctuation.definition.group.regexp: #CE9178", + "light_plus": "punctuation.definition.group.regexp: #D16969", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "string.regexp: #D16969" @@ -1477,19 +1477,19 @@ "c": "\\w", "t": "source.coffee string.regexp.coffee meta.group.regexp constant.character.character-class.regexp", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "constant.character.character-class.regexp: #D16969", + "light_plus": "constant.character.character-class.regexp: #811F3F", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", - "hc_black": "string.regexp: #D16969" + "hc_black": "constant.character: #569CD6" } }, { "c": "*", "t": "source.coffee string.regexp.coffee meta.group.regexp keyword.operator.quantifier.regexp", "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", + "dark_plus": "keyword.operator.quantifier.regexp: #D7BA7D", + "light_plus": "keyword.operator.quantifier.regexp: #000000", "dark_vs": "keyword.operator: #D4D4D4", "light_vs": "keyword.operator: #000000", "hc_black": "keyword.operator: #D4D4D4" @@ -1499,8 +1499,8 @@ "c": ")", "t": "source.coffee string.regexp.coffee meta.group.regexp punctuation.definition.group.regexp", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "punctuation.definition.group.regexp: #CE9178", + "light_plus": "punctuation.definition.group.regexp: #D16969", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "string.regexp: #D16969" @@ -1554,8 +1554,8 @@ "c": "$", "t": "source.coffee string.regexp.coffee keyword.control.anchor.regexp", "r": { - "dark_plus": "keyword.control: #C586C0", - "light_plus": "keyword.control: #AF00DB", + "dark_plus": "keyword.control.anchor.regexp: #DCDCAA", + "light_plus": "keyword.control.anchor.regexp: #FF0000", "dark_vs": "keyword.control: #569CD6", "light_vs": "keyword.control: #0000FF", "hc_black": "keyword.control: #C586C0" diff --git a/extensions/css/package.json b/extensions/css/package.json index 6ace6ad649a..1f7e0681b1e 100644 --- a/extensions/css/package.json +++ b/extensions/css/package.json @@ -50,694 +50,677 @@ "path": "./snippets/css.json" } ], - "configuration": { - "order": 20, - "allOf": [ - { - "id": "css", - "title": "CSS", - "allOf": [ - { - "title": "%css.validate.title%", - "properties": { - "css.validate": { - "type": "boolean", - "scope": "resource", - "default": true, - "description": "%css.validate.desc%" - }, - "css.colorDecorators.enable": { - "type": "boolean", - "scope": "window", - "default": true, - "description": "%css.colorDecorators.enable.desc%", - "deprecationMessage": "%css.colorDecorators.enable.deprecationMessage%" - }, - "css.lint.compatibleVendorPrefixes": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%css.lint.compatibleVendorPrefixes.desc%" - }, - "css.lint.vendorPrefix": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "warning", - "description": "%css.lint.vendorPrefix.desc%" - }, - "css.lint.duplicateProperties": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%css.lint.duplicateProperties.desc%" - }, - "css.lint.emptyRules": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "warning", - "description": "%css.lint.emptyRules.desc%" - }, - "css.lint.importStatement": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%css.lint.importStatement.desc%" - }, - "css.lint.boxModel": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%css.lint.boxModel.desc%" - }, - "css.lint.universalSelector": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%css.lint.universalSelector.desc%" - }, - "css.lint.zeroUnits": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%css.lint.zeroUnits.desc%" - }, - "css.lint.fontFaceProperties": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "warning", - "description": "%css.lint.fontFaceProperties.desc%" - }, - "css.lint.hexColorLength": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "error", - "description": "%css.lint.hexColorLength.desc%" - }, - "css.lint.argumentsInColorFunction": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "error", - "description": "%css.lint.argumentsInColorFunction.desc%" - }, - "css.lint.unknownProperties": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "warning", - "description": "%css.lint.unknownProperties.desc%" - }, - "css.lint.ieHack": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%css.lint.ieHack.desc%" - }, - "css.lint.unknownVendorSpecificProperties": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%css.lint.unknownVendorSpecificProperties.desc%" - }, - "css.lint.propertyIgnoredDueToDisplay": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "warning", - "description": "%css.lint.propertyIgnoredDueToDisplay.desc%" - }, - "css.lint.important": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%css.lint.important.desc%" - }, - "css.lint.float": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%css.lint.float.desc%" - }, - "css.lint.idSelector": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%css.lint.idSelector.desc%" - }, - "css.trace.server": { - "type": "string", - "scope": "window", - "enum": [ - "off", - "messages", - "verbose" - ], - "default": "off", - "description": "%css.trace.server.desc%" - } - } - } - ] - }, - { - "id": "scss", - "order": 24, - "title": "SCSS (Sass)", - "allOf": [ - { - "title": "%scss.validate.title%", - "properties": { - "scss.validate": { - "type": "boolean", - "scope": "resource", - "default": true, - "description": "%scss.validate.desc%" - }, - "scss.colorDecorators.enable": { - "type": "boolean", - "scope": "window", - "default": true, - "description": "%scss.colorDecorators.enable.desc%", - "deprecationMessage": "%scss.colorDecorators.enable.deprecationMessage%" - }, - "scss.lint.compatibleVendorPrefixes": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%scss.lint.compatibleVendorPrefixes.desc%" - }, - "scss.lint.vendorPrefix": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "warning", - "description": "%scss.lint.vendorPrefix.desc%" - }, - "scss.lint.duplicateProperties": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%scss.lint.duplicateProperties.desc%" - }, - "scss.lint.emptyRules": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "warning", - "description": "%scss.lint.emptyRules.desc%" - }, - "scss.lint.importStatement": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%scss.lint.importStatement.desc%" - }, - "scss.lint.boxModel": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%scss.lint.boxModel.desc%" - }, - "scss.lint.universalSelector": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%scss.lint.universalSelector.desc%" - }, - "scss.lint.zeroUnits": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%scss.lint.zeroUnits.desc%" - }, - "scss.lint.fontFaceProperties": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "warning", - "description": "%scss.lint.fontFaceProperties.desc%" - }, - "scss.lint.hexColorLength": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "error", - "description": "%scss.lint.hexColorLength.desc%" - }, - "scss.lint.argumentsInColorFunction": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "error", - "description": "%scss.lint.argumentsInColorFunction.desc%" - }, - "scss.lint.unknownProperties": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "warning", - "description": "%scss.lint.unknownProperties.desc%" - }, - "scss.lint.ieHack": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%scss.lint.ieHack.desc%" - }, - "scss.lint.unknownVendorSpecificProperties": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%scss.lint.unknownVendorSpecificProperties.desc%" - }, - "scss.lint.propertyIgnoredDueToDisplay": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "warning", - "description": "%scss.lint.propertyIgnoredDueToDisplay.desc%" - }, - "scss.lint.important": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%scss.lint.important.desc%" - }, - "scss.lint.float": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%scss.lint.float.desc%" - }, - "scss.lint.idSelector": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%scss.lint.idSelector.desc%" - } - } - } - ] - }, - { - "id": "less", - "order": 22, - "type": "object", - "title": "LESS", - "allOf": [ - { - "title": "%less.validate.title%", - "properties": { - "less.validate": { - "type": "boolean", - "scope": "resource", - "default": true, - "description": "%less.validate.desc%" - }, - "less.colorDecorators.enable": { - "type": "boolean", - "scope": "window", - "default": true, - "description": "%less.colorDecorators.enable.desc%", - "deprecationMessage": "%less.colorDecorators.enable.deprecationMessage%" - }, - "less.lint.compatibleVendorPrefixes": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%less.lint.compatibleVendorPrefixes.desc%" - }, - "less.lint.vendorPrefix": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "warning", - "description": "%less.lint.vendorPrefix.desc%" - }, - "less.lint.duplicateProperties": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%less.lint.duplicateProperties.desc%" - }, - "less.lint.emptyRules": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "warning", - "description": "%less.lint.emptyRules.desc%" - }, - "less.lint.importStatement": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%less.lint.importStatement.desc%" - }, - "less.lint.boxModel": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%less.lint.boxModel.desc%" - }, - "less.lint.universalSelector": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%less.lint.universalSelector.desc%" - }, - "less.lint.zeroUnits": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%less.lint.zeroUnits.desc%" - }, - "less.lint.fontFaceProperties": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "warning", - "description": "%less.lint.fontFaceProperties.desc%" - }, - "less.lint.hexColorLength": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "error", - "description": "%less.lint.hexColorLength.desc%" - }, - "less.lint.argumentsInColorFunction": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "error", - "description": "%less.lint.argumentsInColorFunction.desc%" - }, - "less.lint.unknownProperties": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "warning", - "description": "%less.lint.unknownProperties.desc%" - }, - "less.lint.ieHack": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%less.lint.ieHack.desc%" - }, - "less.lint.unknownVendorSpecificProperties": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%less.lint.unknownVendorSpecificProperties.desc%" - }, - "less.lint.propertyIgnoredDueToDisplay": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "warning", - "description": "%less.lint.propertyIgnoredDueToDisplay.desc%" - }, - "less.lint.important": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%less.lint.important.desc%" - }, - "less.lint.float": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%less.lint.float.desc%" - }, - "less.lint.idSelector": { - "type": "string", - "scope": "resource", - "enum": [ - "ignore", - "warning", - "error" - ], - "default": "ignore", - "description": "%less.lint.idSelector.desc%" - } - } - } - ] + "configuration": [ + { + "order": 22, + "id": "css", + "title": "%css.title%", + "properties": { + "css.validate": { + "type": "boolean", + "scope": "resource", + "default": true, + "description": "%css.validate.desc%" + }, + "css.colorDecorators.enable": { + "type": "boolean", + "scope": "window", + "default": true, + "description": "%css.colorDecorators.enable.desc%", + "deprecationMessage": "%css.colorDecorators.enable.deprecationMessage%" + }, + "css.lint.compatibleVendorPrefixes": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%css.lint.compatibleVendorPrefixes.desc%" + }, + "css.lint.vendorPrefix": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "%css.lint.vendorPrefix.desc%" + }, + "css.lint.duplicateProperties": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%css.lint.duplicateProperties.desc%" + }, + "css.lint.emptyRules": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "%css.lint.emptyRules.desc%" + }, + "css.lint.importStatement": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%css.lint.importStatement.desc%" + }, + "css.lint.boxModel": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%css.lint.boxModel.desc%" + }, + "css.lint.universalSelector": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%css.lint.universalSelector.desc%" + }, + "css.lint.zeroUnits": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%css.lint.zeroUnits.desc%" + }, + "css.lint.fontFaceProperties": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "%css.lint.fontFaceProperties.desc%" + }, + "css.lint.hexColorLength": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "error", + "description": "%css.lint.hexColorLength.desc%" + }, + "css.lint.argumentsInColorFunction": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "error", + "description": "%css.lint.argumentsInColorFunction.desc%" + }, + "css.lint.unknownProperties": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "%css.lint.unknownProperties.desc%" + }, + "css.lint.ieHack": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%css.lint.ieHack.desc%" + }, + "css.lint.unknownVendorSpecificProperties": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%css.lint.unknownVendorSpecificProperties.desc%" + }, + "css.lint.propertyIgnoredDueToDisplay": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "%css.lint.propertyIgnoredDueToDisplay.desc%" + }, + "css.lint.important": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%css.lint.important.desc%" + }, + "css.lint.float": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%css.lint.float.desc%" + }, + "css.lint.idSelector": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%css.lint.idSelector.desc%" + }, + "css.trace.server": { + "type": "string", + "scope": "window", + "enum": [ + "off", + "messages", + "verbose" + ], + "default": "off", + "description": "%css.trace.server.desc%" + } } - ] - } + }, + { + "id": "scss", + "order": 24, + "title": "%scss.title%", + "properties": { + "scss.validate": { + "type": "boolean", + "scope": "resource", + "default": true, + "description": "%scss.validate.desc%" + }, + "scss.colorDecorators.enable": { + "type": "boolean", + "scope": "window", + "default": true, + "description": "%scss.colorDecorators.enable.desc%", + "deprecationMessage": "%scss.colorDecorators.enable.deprecationMessage%" + }, + "scss.lint.compatibleVendorPrefixes": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%scss.lint.compatibleVendorPrefixes.desc%" + }, + "scss.lint.vendorPrefix": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "%scss.lint.vendorPrefix.desc%" + }, + "scss.lint.duplicateProperties": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%scss.lint.duplicateProperties.desc%" + }, + "scss.lint.emptyRules": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "%scss.lint.emptyRules.desc%" + }, + "scss.lint.importStatement": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%scss.lint.importStatement.desc%" + }, + "scss.lint.boxModel": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%scss.lint.boxModel.desc%" + }, + "scss.lint.universalSelector": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%scss.lint.universalSelector.desc%" + }, + "scss.lint.zeroUnits": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%scss.lint.zeroUnits.desc%" + }, + "scss.lint.fontFaceProperties": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "%scss.lint.fontFaceProperties.desc%" + }, + "scss.lint.hexColorLength": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "error", + "description": "%scss.lint.hexColorLength.desc%" + }, + "scss.lint.argumentsInColorFunction": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "error", + "description": "%scss.lint.argumentsInColorFunction.desc%" + }, + "scss.lint.unknownProperties": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "%scss.lint.unknownProperties.desc%" + }, + "scss.lint.ieHack": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%scss.lint.ieHack.desc%" + }, + "scss.lint.unknownVendorSpecificProperties": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%scss.lint.unknownVendorSpecificProperties.desc%" + }, + "scss.lint.propertyIgnoredDueToDisplay": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "%scss.lint.propertyIgnoredDueToDisplay.desc%" + }, + "scss.lint.important": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%scss.lint.important.desc%" + }, + "scss.lint.float": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%scss.lint.float.desc%" + }, + "scss.lint.idSelector": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%scss.lint.idSelector.desc%" + } + } + }, + { + "id": "less", + "order": 23, + "type": "object", + "title": "%less.title%", + "properties": { + "less.validate": { + "type": "boolean", + "scope": "resource", + "default": true, + "description": "%less.validate.desc%" + }, + "less.colorDecorators.enable": { + "type": "boolean", + "scope": "window", + "default": true, + "description": "%less.colorDecorators.enable.desc%", + "deprecationMessage": "%less.colorDecorators.enable.deprecationMessage%" + }, + "less.lint.compatibleVendorPrefixes": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%less.lint.compatibleVendorPrefixes.desc%" + }, + "less.lint.vendorPrefix": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "%less.lint.vendorPrefix.desc%" + }, + "less.lint.duplicateProperties": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%less.lint.duplicateProperties.desc%" + }, + "less.lint.emptyRules": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "%less.lint.emptyRules.desc%" + }, + "less.lint.importStatement": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%less.lint.importStatement.desc%" + }, + "less.lint.boxModel": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%less.lint.boxModel.desc%" + }, + "less.lint.universalSelector": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%less.lint.universalSelector.desc%" + }, + "less.lint.zeroUnits": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%less.lint.zeroUnits.desc%" + }, + "less.lint.fontFaceProperties": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "%less.lint.fontFaceProperties.desc%" + }, + "less.lint.hexColorLength": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "error", + "description": "%less.lint.hexColorLength.desc%" + }, + "less.lint.argumentsInColorFunction": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "error", + "description": "%less.lint.argumentsInColorFunction.desc%" + }, + "less.lint.unknownProperties": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "%less.lint.unknownProperties.desc%" + }, + "less.lint.ieHack": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%less.lint.ieHack.desc%" + }, + "less.lint.unknownVendorSpecificProperties": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%less.lint.unknownVendorSpecificProperties.desc%" + }, + "less.lint.propertyIgnoredDueToDisplay": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "%less.lint.propertyIgnoredDueToDisplay.desc%" + }, + "less.lint.important": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%less.lint.important.desc%" + }, + "less.lint.float": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%less.lint.float.desc%" + }, + "less.lint.idSelector": { + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "%less.lint.idSelector.desc%" + } + } + } + ] }, "dependencies": { - "color-convert": "^0.5.3", + "color-convert": "^0.5.3", "vscode-languageclient": "3.4.0-next.17", "vscode-languageserver-protocol": "^3.1.1", "vscode-nls": "^2.0.2" @@ -745,4 +728,4 @@ "devDependencies": { "@types/node": "^6.0.51" } -} +} \ No newline at end of file diff --git a/extensions/css/package.nls.json b/extensions/css/package.nls.json index b2a7126c30d..33fbf6d5c28 100644 --- a/extensions/css/package.nls.json +++ b/extensions/css/package.nls.json @@ -1,4 +1,5 @@ { + "css.title": "CSS", "css.lint.argumentsInColorFunction.desc": "Invalid number of parameters", "css.lint.boxModel.desc": "Do not use width or height when using padding or border", "css.lint.compatibleVendorPrefixes.desc": "When using a vendor-specific prefix make sure to also include all other vendor-specific properties", @@ -20,6 +21,7 @@ "css.trace.server.desc": "Traces the communication between VS Code and the CSS language server.", "css.validate.title": "Controls CSS validation and problem severities.", "css.validate.desc": "Enables or disables all validations", + "less.title": "LESS", "less.lint.argumentsInColorFunction.desc": "Invalid number of parameters", "less.lint.boxModel.desc": "Do not use width or height when using padding or border", "less.lint.compatibleVendorPrefixes.desc": "When using a vendor-specific prefix make sure to also include all other vendor-specific properties", @@ -40,6 +42,7 @@ "less.lint.zeroUnits.desc": "No unit for zero needed", "less.validate.title": "Controls LESS validation and problem severities.", "less.validate.desc": "Enables or disables all validations", + "scss.title": "SCSS (Sass)", "scss.lint.argumentsInColorFunction.desc": "Invalid number of parameters", "scss.lint.boxModel.desc": "Do not use width or height when using padding or border", "scss.lint.compatibleVendorPrefixes.desc": "When using a vendor-specific prefix make sure to also include all other vendor-specific properties", diff --git a/extensions/css/server/npm-shrinkwrap.json b/extensions/css/server/npm-shrinkwrap.json index eeafae21433..2c2d280b1a2 100644 --- a/extensions/css/server/npm-shrinkwrap.json +++ b/extensions/css/server/npm-shrinkwrap.json @@ -3,9 +3,9 @@ "version": "1.0.0", "dependencies": { "vscode-css-languageservice": { - "version": "2.1.4", + "version": "2.1.6", "from": "vscode-css-languageservice@next", - "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-2.1.4.tgz" + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-2.1.6.tgz" }, "vscode-jsonrpc": { "version": "3.3.1", diff --git a/extensions/css/server/package.json b/extensions/css/server/package.json index ad6c972e571..b62b50928bf 100644 --- a/extensions/css/server/package.json +++ b/extensions/css/server/package.json @@ -8,7 +8,7 @@ "node": "*" }, "dependencies": { - "vscode-css-languageservice": "^2.1.4", + "vscode-css-languageservice": "^2.1.6", "vscode-languageserver": "3.4.0-next.6", "vscode-languageserver-protocol": "^3.1.1" }, diff --git a/extensions/emmet/npm-shrinkwrap.json b/extensions/emmet/npm-shrinkwrap.json index 28091803b29..e91e4e9c33b 100644 --- a/extensions/emmet/npm-shrinkwrap.json +++ b/extensions/emmet/npm-shrinkwrap.json @@ -38,9 +38,9 @@ "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz" }, "vscode-emmet-helper": { - "version": "1.1.7", - "from": "vscode-emmet-helper@>=1.0.17 <2.0.0", - "resolved": "https://registry.npmjs.org/vscode-emmet-helper/-/vscode-emmet-helper-1.1.7.tgz" + "version": "1.1.8", + "from": "vscode-emmet-helper@>=1.0.8 <2.0.0", + "resolved": "https://registry.npmjs.org/vscode-emmet-helper/-/vscode-emmet-helper-1.1.8.tgz" }, "vscode-languageserver-types": { "version": "3.3.0", diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index 31de43eb8bf..7e6d7155d31 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -265,7 +265,7 @@ "@emmetio/html-matcher": "^0.3.1", "@emmetio/css-parser": "ramya-rao-a/css-parser#vscode", "@emmetio/math-expression": "^0.1.1", - "vscode-emmet-helper": "^1.1.7", + "vscode-emmet-helper": "^1.1.8", "vscode-languageserver-types": "^3.0.3", "image-size": "^0.5.2", "vscode-nls": "2.0.2" diff --git a/extensions/emmet/src/defaultCompletionProvider.ts b/extensions/emmet/src/defaultCompletionProvider.ts index 5c78aea06a0..f01fb6db568 100644 --- a/extensions/emmet/src/defaultCompletionProvider.ts +++ b/extensions/emmet/src/defaultCompletionProvider.ts @@ -55,7 +55,7 @@ export class DefaultCompletionItemProvider implements vscode.CompletionItemProvi return; } - let result: vscode.CompletionList = doComplete(document, position, syntax, getEmmetConfiguration(syntax)); + let result = doComplete(document, position, syntax, getEmmetConfiguration(syntax)); let newItems: vscode.CompletionItem[] = []; if (result && result.items) { result.items.forEach(item => { diff --git a/extensions/fsharp/test/colorize-results/test_fs.json b/extensions/fsharp/test/colorize-results/test_fs.json index 2f161a6fccc..ee0ae2ca9c1 100644 --- a/extensions/fsharp/test/colorize-results/test_fs.json +++ b/extensions/fsharp/test/colorize-results/test_fs.json @@ -1026,11 +1026,11 @@ "c": "\\n", "t": "source.fsharp string.quoted.double.fsharp constant.character.string.escape.fsharp", "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", + "dark_plus": "constant.character: #569CD6", + "light_plus": "constant.character: #0000FF", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" + "hc_black": "constant.character: #569CD6" } }, { diff --git a/extensions/git/package.json b/extensions/git/package.json index bd646d7f8a6..995ac785f74 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -529,6 +529,11 @@ "group": "3_commit", "when": "config.git.enabled && scmProvider == git" }, + { + "command": "git.stageAll", + "group": "4_stage", + "when": "config.git.enabled && scmProvider == git" + }, { "command": "git.unstageAll", "group": "4_stage", diff --git a/extensions/git/src/askpass.ts b/extensions/git/src/askpass.ts index 597cc043d4e..e241ced459a 100644 --- a/extensions/git/src/askpass.ts +++ b/extensions/git/src/askpass.ts @@ -27,10 +27,6 @@ function getIPCHandlePath(nonce: string): string { return `\\\\.\\pipe\\vscode-git-askpass-${nonce}-sock`; } - if (process.env['XDG_RUNTIME_DIR']) { - return path.join(process.env['XDG_RUNTIME_DIR'], `vscode-git-askpass-${nonce}.sock`); - } - return path.join(os.tmpdir(), `vscode-git-askpass-${nonce}.sock`); } diff --git a/extensions/groovy/test/colorize-results/test_groovy.json b/extensions/groovy/test/colorize-results/test_groovy.json index ee78a65becc..8d1191aa367 100644 --- a/extensions/groovy/test/colorize-results/test_groovy.json +++ b/extensions/groovy/test/colorize-results/test_groovy.json @@ -9232,33 +9232,33 @@ "c": "${", "t": "source.groovy meta.definition.method.groovy meta.method.body.java string.quoted.double.groovy source.groovy.embedded.source punctuation.section.embedded.groovy", "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" + "dark_plus": "punctuation.section.embedded: #569CD6", + "light_plus": "punctuation.section.embedded: #0000FF", + "dark_vs": "punctuation.section.embedded: #569CD6", + "light_vs": "punctuation.section.embedded: #0000FF", + "hc_black": "punctuation.section.embedded: #569CD6" } }, { "c": "System.currentTimeMillis() - start", "t": "source.groovy meta.definition.method.groovy meta.method.body.java string.quoted.double.groovy source.groovy.embedded.source", "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" + "dark_plus": "source.groovy.embedded: #D4D4D4", + "light_plus": "source.groovy.embedded: #000000", + "dark_vs": "source.groovy.embedded: #D4D4D4", + "light_vs": "source.groovy.embedded: #000000", + "hc_black": "source.groovy.embedded: #FFFFFF" } }, { "c": "}", "t": "source.groovy meta.definition.method.groovy meta.method.body.java string.quoted.double.groovy source.groovy.embedded.source punctuation.section.embedded.groovy", "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" + "dark_plus": "punctuation.section.embedded: #569CD6", + "light_plus": "punctuation.section.embedded: #0000FF", + "dark_vs": "punctuation.section.embedded: #569CD6", + "light_vs": "punctuation.section.embedded: #0000FF", + "hc_black": "punctuation.section.embedded: #569CD6" } }, { diff --git a/extensions/grunt/package.json b/extensions/grunt/package.json index 0758d440b08..4c028b73312 100644 --- a/extensions/grunt/package.json +++ b/extensions/grunt/package.json @@ -31,6 +31,7 @@ "title": "Grunt", "properties": { "grunt.autoDetect": { + "scope": "resource", "type": "string", "enum": [ "off", diff --git a/extensions/grunt/src/main.ts b/extensions/grunt/src/main.ts index 554bb4963e6..a5e74585e8e 100644 --- a/extensions/grunt/src/main.ts +++ b/extensions/grunt/src/main.ts @@ -13,49 +13,6 @@ import * as nls from 'vscode-nls'; const localize = nls.config(process.env.VSCODE_NLS_CONFIG)(); type AutoDetect = 'on' | 'off'; -let taskProvider: vscode.Disposable | undefined; - -export function activate(_context: vscode.ExtensionContext): void { - let workspaceRoot = vscode.workspace.rootPath; - if (!workspaceRoot) { - return; - } - let pattern = path.join(workspaceRoot, '[Gg]runtfile.js'); - let detectorPromise: Thenable | undefined = undefined; - let fileWatcher = vscode.workspace.createFileSystemWatcher(pattern); - fileWatcher.onDidChange(() => detectorPromise = undefined); - fileWatcher.onDidCreate(() => detectorPromise = undefined); - fileWatcher.onDidDelete(() => detectorPromise = undefined); - - function onConfigurationChanged() { - let autoDetect = vscode.workspace.getConfiguration('grunt').get('autoDetect'); - if (taskProvider && autoDetect === 'off') { - detectorPromise = undefined; - taskProvider.dispose(); - taskProvider = undefined; - } else if (!taskProvider && autoDetect === 'on') { - taskProvider = vscode.workspace.registerTaskProvider('grunt', { - provideTasks: () => { - if (!detectorPromise) { - detectorPromise = getGruntTasks(); - } - return detectorPromise; - }, - resolveTask(_task: vscode.Task): vscode.Task | undefined { - return undefined; - } - }); - } - } - vscode.workspace.onDidChangeConfiguration(onConfigurationChanged); - onConfigurationChanged(); -} - -export function deactivate(): void { - if (taskProvider) { - taskProvider.dispose(); - } -} function exists(file: string): Promise { return new Promise((resolve, _reject) => { @@ -76,19 +33,6 @@ function exec(command: string, options: cp.ExecOptions): Promise<{ stdout: strin }); } -let _channel: vscode.OutputChannel; -function getOutputChannel(): vscode.OutputChannel { - if (!_channel) { - _channel = vscode.window.createOutputChannel('Grunt Auto Detection'); - } - return _channel; -} - -interface GruntTaskDefinition extends vscode.TaskDefinition { - task: string; - file?: string; -} - const buildNames: string[] = ['build', 'compile', 'watch']; function isBuildTask(name: string): boolean { for (let buildName of buildNames) { @@ -109,97 +53,287 @@ function isTestTask(name: string): boolean { return false; } -async function getGruntTasks(): Promise { - let workspaceRoot = vscode.workspace.rootPath; - let emptyTasks: vscode.Task[] = []; - if (!workspaceRoot) { - return emptyTasks; +let _channel: vscode.OutputChannel; +function getOutputChannel(): vscode.OutputChannel { + if (!_channel) { + _channel = vscode.window.createOutputChannel('Gulp Auto Detection'); } - if (!await exists(path.join(workspaceRoot, 'gruntfile.js')) && !await exists(path.join(workspaceRoot, 'Gruntfile.js'))) { - return emptyTasks; + return _channel; +} + +interface GruntTaskDefinition extends vscode.TaskDefinition { + task: string; + file?: string; +} + +class FolderDetector { + + private fileWatcher: vscode.FileSystemWatcher; + private promise: Thenable | undefined; + + constructor(private _workspaceFolder: vscode.WorkspaceFolder) { } - let command: string; - let platform = process.platform; - if (platform === 'win32' && await exists(path.join(workspaceRoot!, 'node_modules', '.bin', 'grunt.cmd'))) { - command = path.join('.', 'node_modules', '.bin', 'grunt.cmd'); - } else if ((platform === 'linux' || platform === 'darwin') && await exists(path.join(workspaceRoot!, 'node_modules', '.bin', 'grunt'))) { - command = path.join('.', 'node_modules', '.bin', 'grunt'); - } else { - command = 'grunt'; + public get workspaceFolder(): vscode.WorkspaceFolder { + return this._workspaceFolder; } - let commandLine = `${command} --help --no-color`; - try { - let { stdout, stderr } = await exec(commandLine, { cwd: workspaceRoot }); - if (stderr) { - getOutputChannel().appendLine(stderr); - getOutputChannel().show(true); + public isEnabled(): boolean { + return vscode.workspace.getConfiguration('grunt', this._workspaceFolder.uri).get('autoDetect') === 'on'; + } + + public start(): void { + let pattern = path.join(this._workspaceFolder.uri.fsPath, '[Gg]runtfile.js'); + this.fileWatcher = vscode.workspace.createFileSystemWatcher(pattern); + this.fileWatcher.onDidChange(() => this.promise = undefined); + this.fileWatcher.onDidCreate(() => this.promise = undefined); + this.fileWatcher.onDidDelete(() => this.promise = undefined); + } + + public async getTasks(): Promise { + if (!this.promise) { + this.promise = this.computeTasks(); } - let result: vscode.Task[] = []; - if (stdout) { - // grunt lists tasks as follows (description is wrapped into a new line if too long): - // ... - // Available tasks - // uglify Minify files with UglifyJS. * - // jshint Validate files with JSHint. * - // test Alias for "jshint", "qunit" tasks. - // default Alias for "jshint", "qunit", "concat", "uglify" tasks. - // long Alias for "eslint", "qunit", "browserify", "sass", - // "autoprefixer", "uglify", tasks. - // - // Tasks run in the order specified + return this.promise; + } - let lines = stdout.split(/\r{0,1}\n/); - let tasksStart = false; - let tasksEnd = false; - for (let line of lines) { - if (line.length === 0) { - continue; - } - if (!tasksStart && !tasksEnd) { - if (line.indexOf('Available tasks') === 0) { - tasksStart = true; + private async computeTasks(): Promise { + let rootPath = this._workspaceFolder.uri.scheme === 'file' ? this._workspaceFolder.uri.fsPath : undefined; + let emptyTasks: vscode.Task[] = []; + if (!rootPath) { + return emptyTasks; + } + if (!await exists(path.join(rootPath, 'gruntfile.js')) && !await exists(path.join(rootPath, 'Gruntfile.js'))) { + return emptyTasks; + } + + let command: string; + let platform = process.platform; + if (platform === 'win32' && await exists(path.join(rootPath!, 'node_modules', '.bin', 'grunt.cmd'))) { + command = path.join('.', 'node_modules', '.bin', 'grunt.cmd'); + } else if ((platform === 'linux' || platform === 'darwin') && await exists(path.join(rootPath!, 'node_modules', '.bin', 'grunt'))) { + command = path.join('.', 'node_modules', '.bin', 'grunt'); + } else { + command = 'grunt'; + } + + let commandLine = `${command} --help --no-color`; + try { + let { stdout, stderr } = await exec(commandLine, { cwd: rootPath }); + if (stderr) { + getOutputChannel().appendLine(stderr); + getOutputChannel().show(true); + } + let result: vscode.Task[] = []; + if (stdout) { + // grunt lists tasks as follows (description is wrapped into a new line if too long): + // ... + // Available tasks + // uglify Minify files with UglifyJS. * + // jshint Validate files with JSHint. * + // test Alias for "jshint", "qunit" tasks. + // default Alias for "jshint", "qunit", "concat", "uglify" tasks. + // long Alias for "eslint", "qunit", "browserify", "sass", + // "autoprefixer", "uglify", tasks. + // + // Tasks run in the order specified + + let lines = stdout.split(/\r{0,1}\n/); + let tasksStart = false; + let tasksEnd = false; + for (let line of lines) { + if (line.length === 0) { + continue; } - } else if (tasksStart && !tasksEnd) { - if (line.indexOf('Tasks run in the order specified') === 0) { - tasksEnd = true; - } else { - let regExp = /^\s*(\S.*\S) \S/g; - let matches = regExp.exec(line); - if (matches && matches.length === 2) { - let name = matches[1]; - let kind: GruntTaskDefinition = { - type: 'grunt', - task: name - }; - let source = 'grunt'; - let task = name.indexOf(' ') === -1 - ? new vscode.Task(kind, name, source, new vscode.ShellExecution(`${command} ${name}`)) - : new vscode.Task(kind, name, source, new vscode.ShellExecution(`${command} "${name}"`)); - result.push(task); - let lowerCaseTaskName = name.toLowerCase(); - if (isBuildTask(lowerCaseTaskName)) { - task.group = vscode.TaskGroup.Build; - } else if (isTestTask(lowerCaseTaskName)) { - task.group = vscode.TaskGroup.Test; + if (!tasksStart && !tasksEnd) { + if (line.indexOf('Available tasks') === 0) { + tasksStart = true; + } + } else if (tasksStart && !tasksEnd) { + if (line.indexOf('Tasks run in the order specified') === 0) { + tasksEnd = true; + } else { + let regExp = /^\s*(\S.*\S) \S/g; + let matches = regExp.exec(line); + if (matches && matches.length === 2) { + let name = matches[1]; + let kind: GruntTaskDefinition = { + type: 'grunt', + task: name + }; + let source = 'grunt'; + let options: vscode.ShellExecutionOptions = { cwd: this.workspaceFolder.uri.fsPath }; + let task = name.indexOf(' ') === -1 + ? new vscode.Task(kind, this.workspaceFolder, name, source, new vscode.ShellExecution(`${command} ${name}`, options)) + : new vscode.Task(kind, this.workspaceFolder, name, source, new vscode.ShellExecution(`${command} "${name}"`, options)); + result.push(task); + let lowerCaseTaskName = name.toLowerCase(); + if (isBuildTask(lowerCaseTaskName)) { + task.group = vscode.TaskGroup.Build; + } else if (isTestTask(lowerCaseTaskName)) { + task.group = vscode.TaskGroup.Test; + } } } } } } + return result; + } catch (err) { + let channel = getOutputChannel(); + if (err.stderr) { + channel.appendLine(err.stderr); + } + if (err.stdout) { + channel.appendLine(err.stdout); + } + channel.appendLine(localize('execFailed', 'Auto detecting Grunt failed with error: {0}', err.error ? err.error.toString() : 'unknown')); + channel.show(true); + return emptyTasks; } - return result; - } catch (err) { - let channel = getOutputChannel(); - if (err.stderr) { - channel.appendLine(err.stderr); - } - if (err.stdout) { - channel.appendLine(err.stdout); - } - channel.appendLine(localize('execFailed', 'Auto detecting Grunt failed with error: {0}', err.error ? err.error.toString() : 'unknown')); - channel.show(true); - return emptyTasks; } + + public dispose() { + this.promise = undefined; + if (this.fileWatcher) { + this.fileWatcher.dispose(); + } + } +} + +class TaskDetector { + + private taskProvider: vscode.Disposable | undefined; + private detectors: Map = new Map(); + private promise: Promise | undefined; + + constructor() { + } + + public start(): void { + let folders = vscode.workspace.workspaceFolders; + if (folders) { + this.updateWorkspaceFolders(folders, []); + } + vscode.workspace.onDidChangeWorkspaceFolders((event) => this.updateWorkspaceFolders(event.added, event.removed)); + vscode.workspace.onDidChangeConfiguration(this.updateConfiguration, this); + } + + public dispose(): void { + if (this.taskProvider) { + this.taskProvider.dispose(); + this.taskProvider = undefined; + } + this.detectors.clear(); + this.promise = undefined; + } + + private updateWorkspaceFolders(added: vscode.WorkspaceFolder[], removed: vscode.WorkspaceFolder[]): void { + let changed = false; + for (let remove of removed) { + let detector = this.detectors.get(remove.uri.toString()); + if (detector) { + changed = true; + detector.dispose(); + this.detectors.delete(remove.uri.toString()); + } + } + for (let add of added) { + let detector = new FolderDetector(add); + if (detector.isEnabled()) { + changed = true; + this.detectors.set(add.uri.toString(), detector); + detector.start(); + } + } + if (changed) { + this.promise = undefined; + } + this.updateProvider(); + } + + private updateConfiguration(): void { + let changed = false; + for (let detector of this.detectors.values()) { + if (!detector.isEnabled()) { + changed = true; + detector.dispose(); + this.detectors.delete(detector.workspaceFolder.uri.toString()); + } + } + let folders = vscode.workspace.workspaceFolders; + if (folders) { + for (let folder of folders) { + if (!this.detectors.has(folder.uri.toString())) { + let detector = new FolderDetector(folder); + if (detector.isEnabled()) { + changed = true; + this.detectors.set(folder.uri.toString(), detector); + detector.start(); + } + } + } + } + if (changed) { + this.promise = undefined; + } + this.updateProvider(); + } + + private updateProvider(): void { + if (!this.taskProvider && this.detectors.size > 0) { + this.taskProvider = vscode.workspace.registerTaskProvider('gulp', { + provideTasks: () => { + return this.getTasks(); + }, + resolveTask(_task: vscode.Task): vscode.Task | undefined { + return undefined; + } + }); + } + else if (this.taskProvider && this.detectors.size === 0) { + this.taskProvider.dispose(); + this.taskProvider = undefined; + this.promise = undefined; + } + } + + public getTasks(): Promise { + if (!this.promise) { + this.promise = this.computeTasks(); + } + return this.promise; + } + + private computeTasks(): Promise { + if (this.detectors.size === 0) { + return Promise.resolve([]); + } else if (this.detectors.size === 1) { + return this.detectors.values().next().value.getTasks(); + } else { + let promises: Promise[] = []; + for (let detector of this.detectors.values()) { + promises.push(detector.getTasks().then((value) => value, () => [])); + } + return Promise.all(promises).then((values) => { + let result: vscode.Task[] = []; + for (let tasks of values) { + if (tasks && tasks.length > 0) { + result.push(...tasks); + } + } + return result; + }); + } + } +} + +let detector: TaskDetector; +export function activate(_context: vscode.ExtensionContext): void { + detector = new TaskDetector(); + detector.start(); +} + +export function deactivate(): void { + detector.dispose(); } \ No newline at end of file diff --git a/extensions/gulp/package.json b/extensions/gulp/package.json index 235f3e2d138..498384f3fa7 100644 --- a/extensions/gulp/package.json +++ b/extensions/gulp/package.json @@ -31,6 +31,7 @@ "title": "Gulp", "properties": { "gulp.autoDetect": { + "scope": "resource", "type": "string", "enum": [ "off", diff --git a/extensions/gulp/src/main.ts b/extensions/gulp/src/main.ts index 24d7370cf13..127e07afd64 100644 --- a/extensions/gulp/src/main.ts +++ b/extensions/gulp/src/main.ts @@ -13,49 +13,6 @@ import * as nls from 'vscode-nls'; const localize = nls.config(process.env.VSCODE_NLS_CONFIG)(); type AutoDetect = 'on' | 'off'; -let taskProvider: vscode.Disposable | undefined; - -export function activate(_context: vscode.ExtensionContext): void { - let workspaceRoot = vscode.workspace.rootPath; - if (!workspaceRoot) { - return; - } - let pattern = path.join(workspaceRoot, 'gulpfile{.babel.js,.js}'); - let gulpPromise: Thenable | undefined = undefined; - let fileWatcher = vscode.workspace.createFileSystemWatcher(pattern); - fileWatcher.onDidChange(() => gulpPromise = undefined); - fileWatcher.onDidCreate(() => gulpPromise = undefined); - fileWatcher.onDidDelete(() => gulpPromise = undefined); - - function onConfigurationChanged() { - let autoDetect = vscode.workspace.getConfiguration('gulp').get('autoDetect'); - if (taskProvider && autoDetect === 'off') { - gulpPromise = undefined; - taskProvider.dispose(); - taskProvider = undefined; - } else if (!taskProvider && autoDetect === 'on') { - taskProvider = vscode.workspace.registerTaskProvider('gulp', { - provideTasks: () => { - if (!gulpPromise) { - gulpPromise = getGulpTasks(); - } - return gulpPromise; - }, - resolveTask(_task: vscode.Task): vscode.Task | undefined { - return undefined; - } - }); - } - } - vscode.workspace.onDidChangeConfiguration(onConfigurationChanged); - onConfigurationChanged(); -} - -export function deactivate(): void { - if (taskProvider) { - taskProvider.dispose(); - } -} function exists(file: string): Promise { return new Promise((resolve, _reject) => { @@ -76,19 +33,6 @@ function exec(command: string, options: cp.ExecOptions): Promise<{ stdout: strin }); } -let _channel: vscode.OutputChannel; -function getOutputChannel(): vscode.OutputChannel { - if (!_channel) { - _channel = vscode.window.createOutputChannel('Gulp Auto Detection'); - } - return _channel; -} - -interface GulpTaskDefinition extends vscode.TaskDefinition { - task: string; - file?: string; -} - const buildNames: string[] = ['build', 'compile', 'watch']; function isBuildTask(name: string): boolean { for (let buildName of buildNames) { @@ -109,69 +53,259 @@ function isTestTask(name: string): boolean { return false; } -async function getGulpTasks(): Promise { - let workspaceRoot = vscode.workspace.rootPath; - let emptyTasks: vscode.Task[] = []; - if (!workspaceRoot) { - return emptyTasks; +let _channel: vscode.OutputChannel; +function getOutputChannel(): vscode.OutputChannel { + if (!_channel) { + _channel = vscode.window.createOutputChannel('Gulp Auto Detection'); } - let gulpfile = path.join(workspaceRoot, 'gulpfile.js'); - if (!await exists(gulpfile)) { - gulpfile = path.join(workspaceRoot, 'gulpfile.babel.js'); - if (! await exists(gulpfile)) { + return _channel; +} + +interface GulpTaskDefinition extends vscode.TaskDefinition { + task: string; + file?: string; +} + +class FolderDetector { + + private fileWatcher: vscode.FileSystemWatcher; + private promise: Thenable | undefined; + + constructor(private _workspaceFolder: vscode.WorkspaceFolder) { + } + + public get workspaceFolder(): vscode.WorkspaceFolder { + return this._workspaceFolder; + } + + public isEnabled(): boolean { + return vscode.workspace.getConfiguration('gulp', this._workspaceFolder.uri).get('autoDetect') === 'on'; + } + + public start(): void { + let pattern = path.join(this._workspaceFolder.uri.fsPath, 'gulpfile{.babel.js,.js}'); + this.fileWatcher = vscode.workspace.createFileSystemWatcher(pattern); + this.fileWatcher.onDidChange(() => this.promise = undefined); + this.fileWatcher.onDidCreate(() => this.promise = undefined); + this.fileWatcher.onDidDelete(() => this.promise = undefined); + } + + public async getTasks(): Promise { + if (!this.promise) { + this.promise = this.computeTasks(); + } + return this.promise; + } + + private async computeTasks(): Promise { + let rootPath = this._workspaceFolder.uri.scheme === 'file' ? this._workspaceFolder.uri.fsPath : undefined; + let emptyTasks: vscode.Task[] = []; + if (!rootPath) { + return emptyTasks; + } + let gulpfile = path.join(rootPath, 'gulpfile.js'); + if (!await exists(gulpfile)) { + gulpfile = path.join(rootPath, 'gulpfile.babel.js'); + if (! await exists(gulpfile)) { + return emptyTasks; + } + } + + let gulpCommand: string; + let platform = process.platform; + if (platform === 'win32' && await exists(path.join(rootPath!, 'node_modules', '.bin', 'gulp.cmd'))) { + gulpCommand = path.join('.', 'node_modules', '.bin', 'gulp.cmd'); + } else if ((platform === 'linux' || platform === 'darwin') && await exists(path.join(rootPath!, 'node_modules', '.bin', 'gulp'))) { + gulpCommand = path.join('.', 'node_modules', '.bin', 'gulp'); + } else { + gulpCommand = 'gulp'; + } + + let commandLine = `${gulpCommand} --tasks-simple --no-color`; + try { + let { stdout, stderr } = await exec(commandLine, { cwd: rootPath }); + if (stderr && stderr.length > 0) { + getOutputChannel().appendLine(stderr); + getOutputChannel().show(true); + } + let result: vscode.Task[] = []; + if (stdout) { + let lines = stdout.split(/\r{0,1}\n/); + for (let line of lines) { + if (line.length === 0) { + continue; + } + let kind: GulpTaskDefinition = { + type: 'gulp', + task: line + }; + let options: vscode.ShellExecutionOptions = { cwd: this.workspaceFolder.uri.fsPath }; + let task = new vscode.Task(kind, this.workspaceFolder, line, 'gulp', new vscode.ShellExecution(`${gulpCommand} ${line}`, options)); + result.push(task); + let lowerCaseLine = line.toLowerCase(); + if (isBuildTask(lowerCaseLine)) { + task.group = vscode.TaskGroup.Build; + } else if (isTestTask(lowerCaseLine)) { + task.group = vscode.TaskGroup.Test; + } + } + } + return result; + } catch (err) { + let channel = getOutputChannel(); + if (err.stderr) { + channel.appendLine(err.stderr); + } + if (err.stdout) { + channel.appendLine(err.stdout); + } + channel.appendLine(localize('execFailed', 'Auto detecting gulp failed with error: {0}', err.error ? err.error.toString() : 'unknown')); + channel.show(true); return emptyTasks; } } - let gulpCommand: string; - let platform = process.platform; - if (platform === 'win32' && await exists(path.join(workspaceRoot!, 'node_modules', '.bin', 'gulp.cmd'))) { - gulpCommand = path.join('.', 'node_modules', '.bin', 'gulp.cmd'); - } else if ((platform === 'linux' || platform === 'darwin') && await exists(path.join(workspaceRoot!, 'node_modules', '.bin', 'gulp'))) { - gulpCommand = path.join('.', 'node_modules', '.bin', 'gulp'); - } else { - gulpCommand = 'gulp'; + public dispose() { + this.promise = undefined; + if (this.fileWatcher) { + this.fileWatcher.dispose(); + } + } +} + +class TaskDetector { + + private taskProvider: vscode.Disposable | undefined; + private detectors: Map = new Map(); + private promise: Promise | undefined; + + constructor() { } - let commandLine = `${gulpCommand} --tasks-simple --no-color`; - try { - let { stdout, stderr } = await exec(commandLine, { cwd: workspaceRoot }); - if (stderr && stderr.length > 0) { - getOutputChannel().appendLine(stderr); - getOutputChannel().show(true); + public start(): void { + let folders = vscode.workspace.workspaceFolders; + if (folders) { + this.updateWorkspaceFolders(folders, []); } - let result: vscode.Task[] = []; - if (stdout) { - let lines = stdout.split(/\r{0,1}\n/); - for (let line of lines) { - if (line.length === 0) { - continue; - } - let kind: GulpTaskDefinition = { - type: 'gulp', - task: line - }; - let task = new vscode.Task(kind, line, 'gulp', new vscode.ShellExecution(`${gulpCommand} ${line}`)); - result.push(task); - let lowerCaseLine = line.toLowerCase(); - if (isBuildTask(lowerCaseLine)) { - task.group = vscode.TaskGroup.Build; - } else if (isTestTask(lowerCaseLine)) { - task.group = vscode.TaskGroup.Test; + vscode.workspace.onDidChangeWorkspaceFolders((event) => this.updateWorkspaceFolders(event.added, event.removed)); + vscode.workspace.onDidChangeConfiguration(this.updateConfiguration, this); + } + + public dispose(): void { + if (this.taskProvider) { + this.taskProvider.dispose(); + this.taskProvider = undefined; + } + this.detectors.clear(); + this.promise = undefined; + } + + private updateWorkspaceFolders(added: vscode.WorkspaceFolder[], removed: vscode.WorkspaceFolder[]): void { + let changed = false; + for (let remove of removed) { + let detector = this.detectors.get(remove.uri.toString()); + if (detector) { + changed = true; + detector.dispose(); + this.detectors.delete(remove.uri.toString()); + } + } + for (let add of added) { + let detector = new FolderDetector(add); + if (detector.isEnabled()) { + changed = true; + this.detectors.set(add.uri.toString(), detector); + detector.start(); + } + } + if (changed) { + this.promise = undefined; + } + this.updateProvider(); + } + + private updateConfiguration(): void { + let changed = false; + for (let detector of this.detectors.values()) { + if (!detector.isEnabled()) { + changed = true; + detector.dispose(); + this.detectors.delete(detector.workspaceFolder.uri.toString()); + } + } + let folders = vscode.workspace.workspaceFolders; + if (folders) { + for (let folder of folders) { + if (!this.detectors.has(folder.uri.toString())) { + let detector = new FolderDetector(folder); + if (detector.isEnabled()) { + changed = true; + this.detectors.set(folder.uri.toString(), detector); + detector.start(); + } } } } - return result; - } catch (err) { - let channel = getOutputChannel(); - if (err.stderr) { - channel.appendLine(err.stderr); + if (changed) { + this.promise = undefined; } - if (err.stdout) { - channel.appendLine(err.stdout); - } - channel.appendLine(localize('execFailed', 'Auto detecting gulp failed with error: {0}', err.error ? err.error.toString() : 'unknown')); - channel.show(true); - return emptyTasks; + this.updateProvider(); } + + private updateProvider(): void { + if (!this.taskProvider && this.detectors.size > 0) { + this.taskProvider = vscode.workspace.registerTaskProvider('gulp', { + provideTasks: () => { + return this.getTasks(); + }, + resolveTask(_task: vscode.Task): vscode.Task | undefined { + return undefined; + } + }); + } + else if (this.taskProvider && this.detectors.size === 0) { + this.taskProvider.dispose(); + this.taskProvider = undefined; + this.promise = undefined; + } + } + + public getTasks(): Promise { + if (!this.promise) { + this.promise = this.computeTasks(); + } + return this.promise; + } + + private computeTasks(): Promise { + if (this.detectors.size === 0) { + return Promise.resolve([]); + } else if (this.detectors.size === 1) { + return this.detectors.values().next().value.getTasks(); + } else { + let promises: Promise[] = []; + for (let detector of this.detectors.values()) { + promises.push(detector.getTasks().then((value) => value, () => [])); + } + return Promise.all(promises).then((values) => { + let result: vscode.Task[] = []; + for (let tasks of values) { + if (tasks && tasks.length > 0) { + result.push(...tasks); + } + } + return result; + }); + } + } +} + +let detector: TaskDetector; +export function activate(_context: vscode.ExtensionContext): void { + detector = new TaskDetector(); + detector.start(); +} + +export function deactivate(): void { + detector.dispose(); } \ No newline at end of file diff --git a/extensions/html/package.json b/extensions/html/package.json index a5ebee09dd3..240cb7d8d9f 100644 --- a/extensions/html/package.json +++ b/extensions/html/package.json @@ -92,7 +92,7 @@ "null" ], "scope": "resource", - "default": "a, abbr, acronym, b, bdo, big, br, button, cite, code, dfn, em, i, img, input, kbd, label, map, object, q, samp, select, small, span, strong, sub, sup, textarea, tt, var", + "default": "", "description": "%html.format.unformatted.desc%" }, "html.format.contentUnformatted": { @@ -101,7 +101,7 @@ "null" ], "scope": "resource", - "default": "pre", + "default": "pre,code,textarea", "description": "%html.format.contentUnformatted.desc%" }, "html.format.indentInnerHtml": { diff --git a/extensions/html/server/npm-shrinkwrap.json b/extensions/html/server/npm-shrinkwrap.json index 62a5bf9b691..52f9b38450f 100644 --- a/extensions/html/server/npm-shrinkwrap.json +++ b/extensions/html/server/npm-shrinkwrap.json @@ -3,14 +3,14 @@ "version": "1.0.0", "dependencies": { "vscode-css-languageservice": { - "version": "2.1.4", + "version": "2.1.6", "from": "vscode-css-languageservice@next", - "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-2.1.4.tgz" + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-2.1.6.tgz" }, "vscode-html-languageservice": { - "version": "2.0.7", + "version": "2.0.8", "from": "vscode-html-languageservice@next", - "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-2.0.7.tgz" + "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-2.0.8.tgz" }, "vscode-jsonrpc": { "version": "3.3.1", diff --git a/extensions/html/server/package.json b/extensions/html/server/package.json index 3cac05b7da9..8bcae7d7d90 100644 --- a/extensions/html/server/package.json +++ b/extensions/html/server/package.json @@ -8,8 +8,8 @@ "node": "*" }, "dependencies": { - "vscode-css-languageservice": "^2.1.4", - "vscode-html-languageservice": "^2.0.7", + "vscode-css-languageservice": "^2.1.6", + "vscode-html-languageservice": "^2.0.8", "vscode-languageserver": "3.4.0-next.6", "vscode-languageserver-protocol": "^3.1.1", "vscode-languageserver-types": "^3.3.0", diff --git a/extensions/html/server/src/htmlServerMain.ts b/extensions/html/server/src/htmlServerMain.ts index 9b53da722a2..14ea13ba25c 100644 --- a/extensions/html/server/src/htmlServerMain.ts +++ b/extensions/html/server/src/htmlServerMain.ts @@ -54,7 +54,6 @@ documents.onDidClose(e => { }); function getDocumentSettings(textDocument: TextDocument, needsDocumentSettings: () => boolean): Thenable { - console.log('scopedSettingsSupport ' + scopedSettingsSupport + 'needsSettings ' + needsDocumentSettings()); if (scopedSettingsSupport && needsDocumentSettings()) { let promise = documentSettings[textDocument.uri]; if (!promise) { diff --git a/extensions/html/test/colorize-results/12750_html.json b/extensions/html/test/colorize-results/12750_html.json index 571e60f4ee5..a89b03aa84f 100644 --- a/extensions/html/test/colorize-results/12750_html.json +++ b/extensions/html/test/colorize-results/12750_html.json @@ -29,7 +29,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -51,7 +51,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -106,7 +106,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -128,7 +128,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -150,7 +150,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -194,7 +194,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -205,7 +205,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -293,7 +293,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -315,7 +315,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -337,7 +337,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -381,7 +381,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -392,7 +392,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { diff --git a/extensions/html/test/colorize-results/25920_html.json b/extensions/html/test/colorize-results/25920_html.json index 88dc0cc895c..16b71acbe88 100644 --- a/extensions/html/test/colorize-results/25920_html.json +++ b/extensions/html/test/colorize-results/25920_html.json @@ -62,7 +62,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -84,7 +84,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -139,7 +139,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -172,7 +172,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -194,7 +194,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -359,7 +359,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -381,7 +381,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -436,7 +436,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -458,7 +458,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -480,7 +480,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -502,7 +502,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -524,7 +524,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -601,7 +601,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -623,7 +623,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -678,7 +678,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -711,7 +711,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -733,7 +733,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { diff --git a/extensions/html/test/colorize-results/test_html.json b/extensions/html/test/colorize-results/test_html.json index 1609f7270c1..a1da5d2c393 100644 --- a/extensions/html/test/colorize-results/test_html.json +++ b/extensions/html/test/colorize-results/test_html.json @@ -480,7 +480,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -502,7 +502,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -557,7 +557,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -579,7 +579,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -590,7 +590,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -601,7 +601,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -623,7 +623,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -634,7 +634,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -656,7 +656,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -667,7 +667,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -689,7 +689,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -700,7 +700,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -733,7 +733,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -744,7 +744,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -755,7 +755,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -766,7 +766,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1129,7 +1129,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1151,7 +1151,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1283,7 +1283,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1305,7 +1305,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1448,7 +1448,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1470,7 +1470,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1492,7 +1492,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1536,7 +1536,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1547,7 +1547,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1558,7 +1558,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1580,7 +1580,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1602,7 +1602,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1613,7 +1613,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1624,7 +1624,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1657,7 +1657,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1701,7 +1701,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1712,7 +1712,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1745,7 +1745,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1756,7 +1756,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1767,7 +1767,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1800,7 +1800,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1844,7 +1844,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1855,7 +1855,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1866,7 +1866,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1877,7 +1877,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1888,7 +1888,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1899,7 +1899,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1910,7 +1910,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1932,7 +1932,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1943,7 +1943,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1954,7 +1954,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1976,7 +1976,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1987,7 +1987,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1998,7 +1998,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2009,7 +2009,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2020,7 +2020,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2042,7 +2042,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2053,7 +2053,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2064,7 +2064,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2075,7 +2075,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2086,7 +2086,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2097,7 +2097,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2119,7 +2119,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2141,7 +2141,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2152,7 +2152,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2163,7 +2163,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2174,7 +2174,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2185,7 +2185,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2196,7 +2196,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2207,7 +2207,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { diff --git a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json index 6a9a32ac03c..de12bfe729c 100644 --- a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/648a036db2bad78ee93463269ec49ed91ee5aa91", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/30d95ebb561fc57e784b43d08f0a6f46ad0c3e5d", "name": "JavaScript (with React support)", "scopeName": "source.js", "fileTypes": [ @@ -94,6 +94,29 @@ { "include": "#comment" }, + { + "begin": "(,)\\s*(?!\\S)", + "beginCaptures": { + "1": { + "name": "punctuation.separator.comma.js" + } + }, + "end": "(?)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n )) |\n (:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n ))\n)", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n (=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n )) |\n (:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n ))\n)", "beginCaptures": { "1": { "name": "meta.definition.variable.js entity.name.function.js" @@ -727,7 +750,7 @@ }, "import-declaration": { "name": "meta.import.js", - "begin": "(?)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n )) |\n (:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n ))\n)" + "match": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=(\\?\\s*)?\\s*\n (=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n )) |\n (:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n ))\n)" }, { "name": "meta.definition.property.js variable.object.property.js", @@ -1572,7 +1595,7 @@ } }, { - "match": "(?x)(?:\\s*\\b(public|private|protected|readonly)\\s+)?(\\.\\.\\.)?\\s*(?)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n )) |\n (:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n ))\n)", + "match": "(?x)(?:\\s*\\b(public|private|protected|readonly)\\s+)?(\\.\\.\\.)?\\s*(?)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n )) |\n (:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n ))\n)", "captures": { "1": { "name": "storage.modifier.js" @@ -2410,7 +2433,7 @@ } }, { - "match": "(?x) (\\.) \\s* (?:\n (ATTRIBUTE_NODE|CDATA_SECTION_NODE|COMMENT_NODE|DOCUMENT_FRAGMENT_NODE|DOCUMENT_NODE|DOCUMENT_TYPE_NODE\n |DOMSTRING_SIZE_ERR|ELEMENT_NODE|ENTITY_NODE|ENTITY_REFERENCE_NODE|HIERARCHY_REQUEST_ERR|INDEX_SIZE_ERR\n |INUSE_ATTRIBUTE_ERR|INVALID_CHARACTER_ERR|NO_DATA_ALLOWED_ERR|NO_MODIFICATION_ALLOWED_ERR|NOT_FOUND_ERR\n |NOT_SUPPORTED_ERR|NOTATION_NODE|PROCESSING_INSTRUCTION_NODE|TEXT_NODE|WRONG_DOCUMENT_ERR)\n |\n (_content|[xyz]|abbr|above|accept|acceptCharset|accessKey|action|align|[av]Link(?:color)?|all|alt|anchors|appCodeName\n |appCore|applets|appMinorVersion|appName|appVersion|archive|areas|arguments|attributes|availHeight|availLeft|availTop\n |availWidth|axis|background|backgroundColor|backgroundImage|below|bgColor|body|border|borderBottomWidth|borderColor\n |borderLeftWidth|borderRightWidth|borderStyle|borderTopWidth|borderWidth|bottom|bufferDepth|callee|caller|caption\n |cellPadding|cells|cellSpacing|ch|characterSet|charset|checked|childNodes|chOff|cite|classes|className|clear\n |clientInformation|clip|clipBoardData|closed|code|codeBase|codeType|color|colorDepth|cols|colSpan|compact|complete\n |components|content|controllers|cookie|cookieEnabled|cords|cpuClass|crypto|current|data|dateTime|declare|defaultCharset\n |defaultChecked|defaultSelected|defaultStatus|defaultValue|defaultView|defer|description|dialogArguments|dialogHeight\n |dialogLeft|dialogTop|dialogWidth|dir|directories|disabled|display|docmain|doctype|documentElement|elements|embeds\n |enabledPlugin|encoding|enctype|entities|event|expando|external|face|fgColor|filename|firstChild|fontFamily|fontSize\n |fontWeight|form|formName|forms|frame|frameBorder|frameElement|frames|hasFocus|hash|headers|height|history|host\n |hostname|href|hreflang|hspace|htmlFor|httpEquiv|id|ids|ignoreCase|images|implementation|index|innerHeight|innerWidth\n |input|isMap|label|lang|language|lastChild|lastIndex|lastMatch|lastModified|lastParen|layer[sXY]|left|leftContext\n |lineHeight|link|linkColor|links|listStyleType|localName|location|locationbar|longDesc|lowsrc|lowSrc|marginBottom\n |marginHeight|marginLeft|marginRight|marginTop|marginWidth|maxLength|media|menubar|method|mimeTypes|multiline|multiple\n |name|nameProp|namespaces|namespaceURI|next|nextSibling|nodeName|nodeType|nodeValue|noHref|noResize|noShade|notationName\n |notations|noWrap|object|offscreenBuffering|onLine|onreadystatechange|opener|opsProfile|options|oscpu|outerHeight\n |outerWidth|ownerDocument|paddingBottom|paddingLeft|paddingRight|paddingTop|page[XY]|page[XY]Offset|parent|parentLayer\n |parentNode|parentWindow|pathname|personalbar|pixelDepth|pkcs11|platform|plugins|port|prefix|previous|previousDibling\n |product|productSub|profile|profileend|prompt|prompter|protocol|publicId|readOnly|readyState|referrer|rel|responseText\n |responseXML|rev|right|rightContext|rowIndex|rows|rowSpan|rules|scheme|scope|screen[XY]|screenLeft|screenTop|scripts\n |scrollbars|scrolling|sectionRowIndex|security|securityPolicy|selected|selectedIndex|selection|self|shape|siblingAbove\n |siblingBelow|size|source|specified|standby|start|status|statusbar|statusText|style|styleSheets|suffixes|summary\n |systemId|systemLanguage|tagName|tags|target|tBodies|text|textAlign|textDecoration|textIndent|textTransform|tFoot|tHead\n |title|toolbar|top|type|undefined|uniqueID|updateInterval|URL|URLUnencoded|useMap|userAgent|userLanguage|userProfile\n |vAlign|value|valueType|vendor|vendorSub|version|visibility|vspace|whiteSpace|width|X[MS]LDocument|zIndex))\\b(?!\\$|\\s*(<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "match": "(?x) (\\.) \\s* (?:\n (ATTRIBUTE_NODE|CDATA_SECTION_NODE|COMMENT_NODE|DOCUMENT_FRAGMENT_NODE|DOCUMENT_NODE|DOCUMENT_TYPE_NODE\n |DOMSTRING_SIZE_ERR|ELEMENT_NODE|ENTITY_NODE|ENTITY_REFERENCE_NODE|HIERARCHY_REQUEST_ERR|INDEX_SIZE_ERR\n |INUSE_ATTRIBUTE_ERR|INVALID_CHARACTER_ERR|NO_DATA_ALLOWED_ERR|NO_MODIFICATION_ALLOWED_ERR|NOT_FOUND_ERR\n |NOT_SUPPORTED_ERR|NOTATION_NODE|PROCESSING_INSTRUCTION_NODE|TEXT_NODE|WRONG_DOCUMENT_ERR)\n |\n (_content|[xyz]|abbr|above|accept|acceptCharset|accessKey|action|align|[av]Link(?:color)?|all|alt|anchors|appCodeName\n |appCore|applets|appMinorVersion|appName|appVersion|archive|areas|arguments|attributes|availHeight|availLeft|availTop\n |availWidth|axis|background|backgroundColor|backgroundImage|below|bgColor|body|border|borderBottomWidth|borderColor\n |borderLeftWidth|borderRightWidth|borderStyle|borderTopWidth|borderWidth|bottom|bufferDepth|callee|caller|caption\n |cellPadding|cells|cellSpacing|ch|characterSet|charset|checked|childNodes|chOff|cite|classes|className|clear\n |clientInformation|clip|clipBoardData|closed|code|codeBase|codeType|color|colorDepth|cols|colSpan|compact|complete\n |components|content|controllers|cookie|cookieEnabled|cords|cpuClass|crypto|current|data|dateTime|declare|defaultCharset\n |defaultChecked|defaultSelected|defaultStatus|defaultValue|defaultView|defer|description|dialogArguments|dialogHeight\n |dialogLeft|dialogTop|dialogWidth|dir|directories|disabled|display|docmain|doctype|documentElement|elements|embeds\n |enabledPlugin|encoding|enctype|entities|event|expando|external|face|fgColor|filename|firstChild|fontFamily|fontSize\n |fontWeight|form|formName|forms|frame|frameBorder|frameElement|frames|hasFocus|hash|headers|height|history|host\n |hostname|href|hreflang|hspace|htmlFor|httpEquiv|id|ids|ignoreCase|images|implementation|index|innerHeight|innerWidth\n |input|isMap|label|lang|language|lastChild|lastIndex|lastMatch|lastModified|lastParen|layer[sXY]|left|leftContext\n |lineHeight|link|linkColor|links|listStyleType|localName|location|locationbar|longDesc|lowsrc|lowSrc|marginBottom\n |marginHeight|marginLeft|marginRight|marginTop|marginWidth|maxLength|media|menubar|method|mimeTypes|multiline|multiple\n |name|nameProp|namespaces|namespaceURI|next|nextSibling|nodeName|nodeType|nodeValue|noHref|noResize|noShade|notationName\n |notations|noWrap|object|offscreenBuffering|onLine|onreadystatechange|opener|opsProfile|options|oscpu|outerHeight\n |outerWidth|ownerDocument|paddingBottom|paddingLeft|paddingRight|paddingTop|page[XY]|page[XY]Offset|parent|parentLayer\n |parentNode|parentWindow|pathname|personalbar|pixelDepth|pkcs11|platform|plugins|port|prefix|previous|previousDibling\n |product|productSub|profile|profileend|prompt|prompter|protocol|publicId|readOnly|readyState|referrer|rel|responseText\n |responseXML|rev|right|rightContext|rowIndex|rows|rowSpan|rules|scheme|scope|screen[XY]|screenLeft|screenTop|scripts\n |scrollbars|scrolling|sectionRowIndex|security|securityPolicy|selected|selectedIndex|selection|self|shape|siblingAbove\n |siblingBelow|size|source|specified|standby|start|status|statusbar|statusText|style|styleSheets|suffixes|summary\n |systemId|systemLanguage|tagName|tags|target|tBodies|text|textAlign|textDecoration|textIndent|textTransform|tFoot|tHead\n |title|toolbar|top|type|undefined|uniqueID|updateInterval|URL|URLUnencoded|useMap|userAgent|userLanguage|userProfile\n |vAlign|value|valueType|vendor|vendorSub|version|visibility|vspace|whiteSpace|width|X[MS]LDocument|zIndex))\\b(?!\\$|\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", "captures": { "1": { "name": "punctuation.accessor.js" @@ -2485,13 +2508,13 @@ ] }, "function-call": { - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)?\\()", - "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", "patterns": [ { "name": "meta.function-call.js", "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "end": "(?=\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", "patterns": [ { "include": "#literal" @@ -2552,7 +2575,7 @@ "include": "#object-identifiers" }, { - "match": "(?x)(?:(\\.)\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n))", + "match": "(?x)(?:(\\.)\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n))", "captures": { "1": { "name": "punctuation.accessor.js" @@ -2697,7 +2720,7 @@ }, { "name": "meta.object.member.js", - "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=:\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n)))", + "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=:\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n)))", "captures": { "0": { "name": "meta.object-literal.key.js" @@ -2714,8 +2737,7 @@ "0": { "name": "meta.object-literal.key.js" } - }, - "end": "(?=,|\\})" + } }, { "name": "meta.object.member.js", @@ -2741,6 +2763,16 @@ } } }, + { + "name": "meta.object.member.js", + "begin": "(?=[_$[:alpha:]][_$[:alnum:]]*\\s*=)", + "end": "(?=,|\\}|$)", + "patterns": [ + { + "include": "#expression" + } + ] + }, { "include": "#object-member-body" }, @@ -2815,7 +2847,7 @@ "name": "keyword.control.as.js" } }, - "end": "(?=$|^|[;,:})\\]])", + "end": "(?=$|^|[;,:})\\]]|((? is on new line\n (\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n ) |\n (\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends \n ) |\n # arrow function possible to detect only with => on same line\n (\n (<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\(([^()]|\\([^()]*\\))*\\) # parameteres\n (\\s*:\\s*(.)*)? # return type\n \\s*=> # arrow operator\n )\n )\n)", + "begin": "(?x) (?:\n (? is on new line\n (\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n ) |\n (\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends \n ) |\n # arrow function possible to detect only with => on same line\n (\n (<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\(([^()]|\\([^()]*\\))*\\) # parameteres\n (\\s*:\\s*(.)*)? # return type\n \\s*=> # arrow operator\n )\n )\n)", "beginCaptures": { "1": { "name": "storage.modifier.async.js" @@ -3053,7 +3085,7 @@ "patterns": [ { "name": "string.regexp.js", - "begin": "(?<=[=(:,\\[?+!]|return|case|=>|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/(?![\\/*])[gimy]*(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?<=[=(:,\\[?+!]|return|case|=>|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/(?![\\/*])[gimuy]*(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "1": { "name": "punctuation.definition.string.begin.js" @@ -3076,7 +3108,7 @@ }, { "name": "string.regexp.js", - "begin": "(? { + for (let setting of schemaSettings) { + let url = getSchemaId(setting, rootPath); + if (!url) { + continue; + } + let schemaSetting = schemaSettingsById[url]; + if (!schemaSetting) { + schemaSetting = schemaSettingsById[url] = { url, fileMatch: [] }; + settings.json.schemas.push(schemaSetting); + } + let fileMatches = setting.fileMatch; + if (Array.isArray(fileMatches)) { + if (fileMatchPrefix) { + fileMatches = fileMatches.map(m => fileMatchPrefix + m); + } + schemaSetting.fileMatch.push(...fileMatches); + } + if (setting.schema) { + schemaSetting.schema = setting.schema; + } } }; - let settingsSchemas = jsonSettings.get('schemas'); - if (Array.isArray(settingsSchemas)) { - schemas.push(...settingsSchemas); - } + // merge global and folder settings. Qualify all file matches with the folder path. + let globalSettings = jsonSettings.get('schemas'); + if (Array.isArray(globalSettings)) { + collectSchemaSettings(globalSettings, workspace.rootPath); + } let folders = workspace.workspaceFolders; if (folders) { - folders.forEach(folder => { - let jsonConfig = workspace.getConfiguration('json', folder.uri); - let schemaConfigInfo = jsonConfig.inspect('schemas'); + for (let folder of folders) { + let folderUri = folder.uri; + let schemaConfigInfo = workspace.getConfiguration('json', folderUri).inspect('schemas'); let folderSchemas = schemaConfigInfo.workspaceFolderValue; if (Array.isArray(folderSchemas)) { - folderSchemas.forEach(schema => { - let url = schema.url; - if (!url && schema.schema) { - url = schema.schema.id; - } - if (url && url[0] === '.') { - url = Uri.file(path.normalize(path.join(folder.uri.fsPath, url))).toString(); - } - let fileMatch = schema.fileMatch; - if (fileMatch) { - fileMatch = fileMatch.map(m => folder.uri.toString() + '*' + m); - } - schemas.push({ url, fileMatch, schema: schema.schema }); - }); + let folderPath = folderUri.toString(); + if (folderPath[folderPath.length - 1] !== '/') { + folderPath = folderPath + '/'; + } + collectSchemaSettings(folderSchemas, folderUri.fsPath, folderPath + '*'); }; - }); + }; } return settings; } +function getSchemaId(schema: JSONSchemaSettings, rootPath?: string) { + let url = schema.url; + if (!url) { + if (schema.schema) { + url = schema.schema.id || `vscode://schemas/custom/${encodeURIComponent(hash(schema.schema).toString(16))}`; + } + } else if (rootPath && (url[0] === '.' || url[0] === '/')) { + url = Uri.file(path.normalize(path.join(rootPath, url))).toString(); + } + return url; +} + function getPackageInfo(context: ExtensionContext): IPackageInfo { let extensionPackage = require(context.asAbsolutePath('./package.json')); if (extensionPackage) { diff --git a/extensions/json/client/src/utils/hash.ts b/extensions/json/client/src/utils/hash.ts new file mode 100644 index 00000000000..e7149e43cd7 --- /dev/null +++ b/extensions/json/client/src/utils/hash.ts @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +/** + * Return a hash value for an object. + */ +export function hash(obj: any, hashVal = 0): number { + switch (typeof obj) { + case 'object': + if (obj === null) { + return numberHash(349, hashVal); + } else if (Array.isArray(obj)) { + return arrayHash(obj, hashVal); + } + return objectHash(obj, hashVal); + case 'string': + return stringHash(obj, hashVal); + case 'boolean': + return booleanHash(obj, hashVal); + case 'number': + return numberHash(obj, hashVal); + case 'undefined': + return numberHash(obj, 937); + default: + return numberHash(obj, 617); + } +} + +function numberHash(val: number, initialHashVal: number): number { + return (((initialHashVal << 5) - initialHashVal) + val) | 0; // hashVal * 31 + ch, keep as int32 +} + +function booleanHash(b: boolean, initialHashVal: number): number { + return numberHash(b ? 433 : 863, initialHashVal); +} + +function stringHash(s: string, hashVal: number) { + hashVal = numberHash(149417, hashVal); + for (let i = 0, length = s.length; i < length; i++) { + hashVal = numberHash(s.charCodeAt(i), hashVal); + } + return hashVal; +} + +function arrayHash(arr: any[], initialHashVal: number): number { + initialHashVal = numberHash(104579, initialHashVal); + return arr.reduce((hashVal, item) => hash(item, hashVal), initialHashVal); +} + +function objectHash(obj: any, initialHashVal: number): number { + initialHashVal = numberHash(181387, initialHashVal); + return Object.keys(obj).sort().reduce((hashVal, key) => { + hashVal = stringHash(key, hashVal); + return hash(obj[key], hashVal); + }, initialHashVal); +} diff --git a/extensions/json/server/npm-shrinkwrap.json b/extensions/json/server/npm-shrinkwrap.json index 517efec26a3..3bcdb83008a 100644 --- a/extensions/json/server/npm-shrinkwrap.json +++ b/extensions/json/server/npm-shrinkwrap.json @@ -43,9 +43,9 @@ "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.2.1.tgz" }, "vscode-json-languageservice": { - "version": "2.0.16", + "version": "2.0.18", "from": "vscode-json-languageservice@next", - "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-2.0.16.tgz" + "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-2.0.18.tgz" }, "vscode-jsonrpc": { "version": "3.3.1", diff --git a/extensions/json/server/package.json b/extensions/json/server/package.json index 3b6e892cc42..684e9c35d28 100644 --- a/extensions/json/server/package.json +++ b/extensions/json/server/package.json @@ -10,10 +10,11 @@ "dependencies": { "jsonc-parser": "^1.0.0", "request-light": "^0.2.1", - "vscode-json-languageservice": "^2.0.16", + "vscode-json-languageservice": "^2.0.18", "vscode-languageserver": "3.4.0-next.6", "vscode-languageserver-protocol": "^3.1.1", - "vscode-nls": "^2.0.2" + "vscode-nls": "^2.0.2", + "vscode-uri": "^1.0.1" }, "devDependencies": { "@types/node": "^6.0.51" diff --git a/extensions/json/server/src/jsonServerMain.ts b/extensions/json/server/src/jsonServerMain.ts index 2fe428f717f..4d1c416c41c 100644 --- a/extensions/json/server/src/jsonServerMain.ts +++ b/extensions/json/server/src/jsonServerMain.ts @@ -13,9 +13,8 @@ import { import { DocumentColorRequest, ServerCapabilities as CPServerCapabilities } from 'vscode-languageserver-protocol/lib/protocol.colorProvider.proposed'; import { xhr, XHRResponse, configure as configureHttpRequests, getErrorStatusDescription } from 'request-light'; -import path = require('path'); import fs = require('fs'); -import URI from './utils/uri'; +import URI from 'vscode-uri'; import * as URL from 'url'; import Strings = require('./utils/strings'); import { JSONDocument, JSONSchema, LanguageSettings, getLanguageService } from 'vscode-json-languageservice'; @@ -54,9 +53,7 @@ let clientDynamicRegisterSupport = false; // After the server has started the client sends an initilize request. The server receives // in the passed params the rootPath of the workspace plus the client capabilities. -let workspaceRoot: URI; connection.onInitialize((params: InitializeParams): InitializeResult => { - workspaceRoot = URI.parse(params.rootPath); function hasClientCapability(...keys: string[]) { let c = params.capabilities; @@ -192,19 +189,12 @@ function updateConfiguration() { } } if (jsonConfigurationSettings) { - jsonConfigurationSettings.forEach(schema => { + jsonConfigurationSettings.forEach((schema, index) => { let uri = schema.url; if (!uri && schema.schema) { - uri = schema.schema.id; - } - if (!uri && schema.fileMatch) { - uri = 'vscode://schemas/custom/' + encodeURIComponent(schema.fileMatch.join('&')); + uri = schema.schema.id || `vscode://schemas/custom/${index}`; } if (uri) { - if (uri[0] === '.' && workspaceRoot) { - // workspace relative path - uri = URI.file(path.normalize(path.join(workspaceRoot.fsPath, uri))).toString(); - } languageSettings.schemas.push({ uri, fileMatch: schema.fileMatch, schema: schema.schema }); } }); diff --git a/extensions/json/server/src/utils/uri.ts b/extensions/json/server/src/utils/uri.ts deleted file mode 100644 index 5cce7590977..00000000000 --- a/extensions/json/server/src/utils/uri.ts +++ /dev/null @@ -1,351 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -function _encode(ch: string): string { - return '%' + ch.charCodeAt(0).toString(16).toUpperCase(); -} - -// see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent -function encodeURIComponent2(str: string): string { - return encodeURIComponent(str).replace(/[!'()*]/g, _encode); -} - -function encodeNoop(str: string): string { - return str; -} - - -/** - * Uniform Resource Identifier (URI) http://tools.ietf.org/html/rfc3986. - * This class is a simple parser which creates the basic component paths - * (http://tools.ietf.org/html/rfc3986#section-3) with minimal validation - * and encoding. - * - * foo://example.com:8042/over/there?name=ferret#nose - * \_/ \______________/\_________/ \_________/ \__/ - * | | | | | - * scheme authority path query fragment - * | _____________________|__ - * / \ / \ - * urn:example:animal:ferret:nose - * - * - */ -export default class URI { - - private static _empty = ''; - private static _slash = '/'; - private static _regexp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/; - private static _driveLetterPath = /^\/[a-zA-z]:/; - private static _upperCaseDrive = /^(\/)?([A-Z]:)/; - - private _scheme: string; - private _authority: string; - private _path: string; - private _query: string; - private _fragment: string; - private _formatted: string; - private _fsPath: string; - - constructor() { - this._scheme = URI._empty; - this._authority = URI._empty; - this._path = URI._empty; - this._query = URI._empty; - this._fragment = URI._empty; - - this._formatted = null; - this._fsPath = null; - } - - /** - * scheme is the 'http' part of 'http://www.msft.com/some/path?query#fragment'. - * The part before the first colon. - */ - get scheme() { - return this._scheme; - } - - /** - * authority is the 'www.msft.com' part of 'http://www.msft.com/some/path?query#fragment'. - * The part between the first double slashes and the next slash. - */ - get authority() { - return this._authority; - } - - /** - * path is the '/some/path' part of 'http://www.msft.com/some/path?query#fragment'. - */ - get path() { - return this._path; - } - - /** - * query is the 'query' part of 'http://www.msft.com/some/path?query#fragment'. - */ - get query() { - return this._query; - } - - /** - * fragment is the 'fragment' part of 'http://www.msft.com/some/path?query#fragment'. - */ - get fragment() { - return this._fragment; - } - - // ---- filesystem path ----------------------- - - /** - * Returns a string representing the corresponding file system path of this URI. - * Will handle UNC paths and normalize windows drive letters to lower-case. Also - * uses the platform specific path separator. Will *not* validate the path for - * invalid characters and semantics. Will *not* look at the scheme of this URI. - */ - get fsPath() { - if (!this._fsPath) { - var value: string; - if (this._authority && this.scheme === 'file') { - // unc path: file://shares/c$/far/boo - value = `//${this._authority}${this._path}`; - } else if (URI._driveLetterPath.test(this._path)) { - // windows drive letter: file:///c:/far/boo - value = this._path[1].toLowerCase() + this._path.substr(2); - } else { - // other path - value = this._path; - } - if (process.platform === 'win32') { - value = value.replace(/\//g, '\\'); - } - this._fsPath = value; - } - return this._fsPath; - } - - // ---- modify to new ------------------------- - - public with(scheme: string, authority: string, path: string, query: string, fragment: string): URI { - var ret = new URI(); - ret._scheme = scheme || this.scheme; - ret._authority = authority || this.authority; - ret._path = path || this.path; - ret._query = query || this.query; - ret._fragment = fragment || this.fragment; - URI._validate(ret); - return ret; - } - - public withScheme(value: string): URI { - return this.with(value, undefined, undefined, undefined, undefined); - } - - public withAuthority(value: string): URI { - return this.with(undefined, value, undefined, undefined, undefined); - } - - public withPath(value: string): URI { - return this.with(undefined, undefined, value, undefined, undefined); - } - - public withQuery(value: string): URI { - return this.with(undefined, undefined, undefined, value, undefined); - } - - public withFragment(value: string): URI { - return this.with(undefined, undefined, undefined, undefined, value); - } - - // ---- parse & validate ------------------------ - - public static parse(value: string): URI { - const ret = new URI(); - const data = URI._parseComponents(value); - ret._scheme = data.scheme; - ret._authority = decodeURIComponent(data.authority); - ret._path = decodeURIComponent(data.path); - ret._query = decodeURIComponent(data.query); - ret._fragment = decodeURIComponent(data.fragment); - URI._validate(ret); - return ret; - } - - public static file(path: string): URI { - - const ret = new URI(); - ret._scheme = 'file'; - - // normalize to fwd-slashes - path = path.replace(/\\/g, URI._slash); - - // check for authority as used in UNC shares - // or use the path as given - if (path[0] === URI._slash && path[0] === path[1]) { - let idx = path.indexOf(URI._slash, 2); - if (idx === -1) { - ret._authority = path.substring(2); - } else { - ret._authority = path.substring(2, idx); - ret._path = path.substring(idx); - } - } else { - ret._path = path; - } - - // Ensure that path starts with a slash - // or that it is at least a slash - if (ret._path[0] !== URI._slash) { - ret._path = URI._slash + ret._path; - } - - URI._validate(ret); - - return ret; - } - - private static _parseComponents(value: string): UriComponents { - - const ret: UriComponents = { - scheme: URI._empty, - authority: URI._empty, - path: URI._empty, - query: URI._empty, - fragment: URI._empty, - }; - - const match = URI._regexp.exec(value); - if (match) { - ret.scheme = match[2] || ret.scheme; - ret.authority = match[4] || ret.authority; - ret.path = match[5] || ret.path; - ret.query = match[7] || ret.query; - ret.fragment = match[9] || ret.fragment; - } - return ret; - } - - public static create(scheme?: string, authority?: string, path?: string, query?: string, fragment?: string): URI { - return new URI().with(scheme, authority, path, query, fragment); - } - - private static _validate(ret: URI): void { - - // validation - // path, http://tools.ietf.org/html/rfc3986#section-3.3 - // If a URI contains an authority component, then the path component - // must either be empty or begin with a slash ("/") character. If a URI - // does not contain an authority component, then the path cannot begin - // with two slash characters ("//"). - if (ret.authority && ret.path && ret.path[0] !== '/') { - throw new Error('[UriError]: If a URI contains an authority component, then the path component must either be empty or begin with a slash ("/") character'); - } - if (!ret.authority && ret.path.indexOf('//') === 0) { - throw new Error('[UriError]: If a URI does not contain an authority component, then the path cannot begin with two slash characters ("//")'); - } - } - - // ---- printing/externalize --------------------------- - - /** - * - * @param skipEncoding Do not encode the result, default is `false` - */ - public toString(skipEncoding: boolean = false): string { - if (!skipEncoding) { - if (!this._formatted) { - this._formatted = URI._asFormatted(this, false); - } - return this._formatted; - } else { - // we don't cache that - return URI._asFormatted(this, true); - } - } - - private static _asFormatted(uri: URI, skipEncoding: boolean): string { - - const encoder = !skipEncoding - ? encodeURIComponent2 - : encodeNoop; - - const parts: string[] = []; - - let { scheme, authority, path, query, fragment } = uri; - if (scheme) { - parts.push(scheme, ':'); - } - if (authority || scheme === 'file') { - parts.push('//'); - } - if (authority) { - authority = authority.toLowerCase(); - let idx = authority.indexOf(':'); - if (idx === -1) { - parts.push(encoder(authority)); - } else { - parts.push(encoder(authority.substr(0, idx)), authority.substr(idx)); - } - } - if (path) { - // lower-case windown drive letters in /C:/fff - const m = URI._upperCaseDrive.exec(path); - if (m) { - path = m[1] + m[2].toLowerCase() + path.substr(m[1].length + m[2].length); - } - - // encode every segement but not slashes - // make sure that # and ? are always encoded - // when occurring in paths - otherwise the result - // cannot be parsed back again - let lastIdx = 0; - while (true) { - let idx = path.indexOf(URI._slash, lastIdx); - if (idx === -1) { - parts.push(encoder(path.substring(lastIdx)).replace(/[#?]/, _encode)); - break; - } - parts.push(encoder(path.substring(lastIdx, idx)).replace(/[#?]/, _encode), URI._slash); - lastIdx = idx + 1; - }; - } - if (query) { - parts.push('?', encoder(query)); - } - if (fragment) { - parts.push('#', encoder(fragment)); - } - - return parts.join(URI._empty); - } - - public toJSON(): any { - return { - scheme: this.scheme, - authority: this.authority, - path: this.path, - fsPath: this.fsPath, - query: this.query, - fragment: this.fragment, - external: this.toString(), - $mid: 1 - }; - } -} - -interface UriComponents { - scheme: string; - authority: string; - path: string; - query: string; - fragment: string; -} - -interface UriState extends UriComponents { - $mid: number; - fsPath: string; - external: string; -} diff --git a/extensions/json/test/colorize-results/test_json.json b/extensions/json/test/colorize-results/test_json.json index 5f9b48b1ea0..f5295bcf70a 100644 --- a/extensions/json/test/colorize-results/test_json.json +++ b/extensions/json/test/colorize-results/test_json.json @@ -388,11 +388,11 @@ "c": "\\u0056", "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json string.quoted.double.json constant.character.escape.json", "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" + "hc_black": "constant.character: #569CD6" } }, { diff --git a/extensions/make/test/colorize-results/makefile.json b/extensions/make/test/colorize-results/makefile.json index b91562aaa2f..94d9b456703 100644 --- a/extensions/make/test/colorize-results/makefile.json +++ b/extensions/make/test/colorize-results/makefile.json @@ -135,11 +135,11 @@ "c": "\\", "t": "source.makefile meta.scope.target.makefile meta.scope.prerequisites.makefile constant.character.escape.continuation.makefile", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "constant.character: #569CD6" } }, { @@ -278,11 +278,11 @@ "c": "\\", "t": "source.makefile meta.scope.target.makefile meta.scope.prerequisites.makefile constant.character.escape.continuation.makefile", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "constant.character: #569CD6" } }, { @@ -322,11 +322,11 @@ "c": "\\", "t": "source.makefile meta.scope.target.makefile meta.scope.prerequisites.makefile comment.line.number-sign.makefile constant.character.escape.continuation.makefile", "r": { - "dark_plus": "comment: #608B4E", - "light_plus": "comment: #008000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "comment: #608B4E", "light_vs": "comment: #008000", - "hc_black": "comment: #7CA668" + "hc_black": "constant.character: #569CD6" } }, { @@ -377,11 +377,11 @@ "c": "\\", "t": "source.makefile comment.line.number-sign.makefile constant.character.escape.continuation.makefile", "r": { - "dark_plus": "comment: #608B4E", - "light_plus": "comment: #008000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "comment: #608B4E", "light_vs": "comment: #008000", - "hc_black": "comment: #7CA668" + "hc_black": "constant.character: #569CD6" } }, { @@ -553,11 +553,11 @@ "c": "\\", "t": "source.makefile meta.scope.target.makefile meta.scope.prerequisites.makefile constant.character.escape.continuation.makefile", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "constant.character: #569CD6" } }, { diff --git a/extensions/markdown/test/colorize-results/test_md.json b/extensions/markdown/test/colorize-results/test_md.json index 4779949f7ae..c07d8316525 100644 --- a/extensions/markdown/test/colorize-results/test_md.json +++ b/extensions/markdown/test/colorize-results/test_md.json @@ -656,7 +656,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -678,7 +678,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -733,7 +733,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -744,7 +744,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1030,7 +1030,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1052,7 +1052,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1063,7 +1063,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1074,7 +1074,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1096,7 +1096,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1107,7 +1107,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1151,7 +1151,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1162,7 +1162,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1173,7 +1173,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { diff --git a/extensions/perl/test/colorize-results/test2_pl.json b/extensions/perl/test/colorize-results/test2_pl.json index ef4bcabf439..bf690458af4 100644 --- a/extensions/perl/test/colorize-results/test2_pl.json +++ b/extensions/perl/test/colorize-results/test2_pl.json @@ -1301,11 +1301,11 @@ "c": "\\d\\d\\d", "t": "source.perl string.regexp.find.perl constant.character.escape.perl", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", - "hc_black": "string.regexp: #D16969" + "hc_black": "constant.character: #569CD6" } }, { @@ -1323,11 +1323,11 @@ "c": "\\d\\d", "t": "source.perl string.regexp.find.perl constant.character.escape.perl", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", - "hc_black": "string.regexp: #D16969" + "hc_black": "constant.character: #569CD6" } }, { @@ -1345,11 +1345,11 @@ "c": "\\d\\d", "t": "source.perl string.regexp.find.perl constant.character.escape.perl", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", - "hc_black": "string.regexp: #D16969" + "hc_black": "constant.character: #569CD6" } }, { @@ -1774,11 +1774,11 @@ "c": "\\d", "t": "source.perl string.regexp.find.perl constant.character.escape.perl", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", - "hc_black": "string.regexp: #D16969" + "hc_black": "constant.character: #569CD6" } }, { diff --git a/extensions/perl/test/colorize-results/test_pl.json b/extensions/perl/test/colorize-results/test_pl.json index dd6dc702ac1..e6551c2c639 100644 --- a/extensions/perl/test/colorize-results/test_pl.json +++ b/extensions/perl/test/colorize-results/test_pl.json @@ -388,11 +388,11 @@ "c": "\\s", "t": "source.perl string.regexp.find.perl constant.character.escape.perl", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", - "hc_black": "string.regexp: #D16969" + "hc_black": "constant.character: #569CD6" } }, { @@ -410,11 +410,11 @@ "c": "\\s", "t": "source.perl string.regexp.find.perl constant.character.escape.perl", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", - "hc_black": "string.regexp: #D16969" + "hc_black": "constant.character: #569CD6" } }, { @@ -432,11 +432,11 @@ "c": "\\(", "t": "source.perl string.regexp.find.perl constant.character.escape.perl", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", - "hc_black": "string.regexp: #D16969" + "hc_black": "constant.character: #569CD6" } }, { @@ -454,11 +454,11 @@ "c": "\\)", "t": "source.perl string.regexp.find.perl constant.character.escape.perl", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", - "hc_black": "string.regexp: #D16969" + "hc_black": "constant.character: #569CD6" } }, { @@ -476,11 +476,11 @@ "c": "\\)", "t": "source.perl string.regexp.find.perl constant.character.escape.perl", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", - "hc_black": "string.regexp: #D16969" + "hc_black": "constant.character: #569CD6" } }, { @@ -674,11 +674,11 @@ "c": "\\n", "t": "source.perl string.quoted.double.perl constant.character.escape.perl", "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" + "hc_black": "constant.character: #569CD6" } }, { @@ -949,11 +949,11 @@ "c": "\\n", "t": "source.perl string.quoted.double.perl constant.character.escape.perl", "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" + "hc_black": "constant.character: #569CD6" } }, { @@ -1444,11 +1444,11 @@ "c": "\\n", "t": "source.perl string.quoted.double.perl constant.character.escape.perl", "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" + "hc_black": "constant.character: #569CD6" } }, { @@ -2280,11 +2280,11 @@ "c": "\\n", "t": "source.perl string.quoted.double.perl constant.character.escape.perl", "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" + "hc_black": "constant.character: #569CD6" } }, { diff --git a/extensions/php/package.json b/extensions/php/package.json index e3b313bb321..3f50c17669b 100644 --- a/extensions/php/package.json +++ b/extensions/php/package.json @@ -27,7 +27,7 @@ "PHP", "php" ], - "firstLine": "^#!/.*\\bphp\\b", + "firstLine": "^#!/.*\\bphp\\b", "mimetypes": [ "application/x-php" ], diff --git a/extensions/php/syntaxes/php.tmLanguage.json b/extensions/php/syntaxes/php.tmLanguage.json index 0a36eb0b2f5..3f4d3f537f6 100644 --- a/extensions/php/syntaxes/php.tmLanguage.json +++ b/extensions/php/syntaxes/php.tmLanguage.json @@ -1,10 +1,10 @@ { "information_for_contributors": [ - "This file has been converted from https://github.com/atom/language-php/blob/master/grammars/php.cson", + "This file has been converted from https://github.com/roblourens/language-php/blob/revertBraceMatching/grammars/php.cson", "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/atom/language-php/commit/efab9c83b91c02f04b8cfd0236e0239e14c4af52", + "version": "https://github.com/roblourens/language-php/commit/91d50ab5f871ea2d11b4c511dc0b9a972e4ac5ce", "scopeName": "text.html.php", "name": "PHP", "fileTypes": [ @@ -1428,6 +1428,14 @@ { "include": "#comments" }, + { + "match": "{", + "name": "punctuation.section.scope.begin.php" + }, + { + "match": "}", + "name": "punctuation.section.scope.end.php" + }, { "begin": "(?i)^\\s*(interface)\\s+([a-z_\\x{7f}-\\x{ff}][a-z0-9_\\x{7f}-\\x{ff}]*)\\s*(extends)?\\s*", "beginCaptures": { @@ -2086,25 +2094,6 @@ { "include": "#string-backtick" }, - { - "begin": "{", - "beginCaptures": { - "0": { - "name": "punctuation.definition.begin.bracket.curly.php" - } - }, - "end": "}", - "endCaptures": { - "0": { - "name": "punctuation.definition.end.bracket.curly.php" - } - }, - "patterns": [ - { - "include": "#language" - } - ] - }, { "begin": "\\[", "beginCaptures": { diff --git a/extensions/php/test/colorize-results/issue-28354_php.json b/extensions/php/test/colorize-results/issue-28354_php.json index 669388a960e..10314265464 100644 --- a/extensions/php/test/colorize-results/issue-28354_php.json +++ b/extensions/php/test/colorize-results/issue-28354_php.json @@ -40,7 +40,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -62,7 +62,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -73,7 +73,7 @@ "light_plus": "punctuation.section.embedded.begin.php: #800000", "dark_vs": "punctuation.section.embedded.begin.php: #569CD6", "light_vs": "punctuation.section.embedded.begin.php: #800000", - "hc_black": "default: #FFFFFF" + "hc_black": "punctuation.section.embedded: #569CD6" } }, { @@ -84,7 +84,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -106,7 +106,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -139,7 +139,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -161,7 +161,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -194,7 +194,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -205,18 +205,18 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { "c": "{", - "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php punctuation.definition.begin.bracket.curly.php", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php punctuation.section.scope.begin.php", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -227,7 +227,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -249,7 +249,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -278,11 +278,11 @@ "c": "\\'", "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php string.quoted.single.php constant.character.escape.php", "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" + "hc_black": "constant.character: #569CD6" } }, { @@ -377,11 +377,11 @@ "c": "\\'", "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php string.quoted.single.php constant.character.escape.php", "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" + "hc_black": "constant.character: #569CD6" } }, { @@ -414,7 +414,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -425,18 +425,18 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { "c": "}", - "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php punctuation.definition.end.bracket.curly.php", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php punctuation.section.scope.end.php", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -447,7 +447,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -458,7 +458,7 @@ "light_plus": "punctuation.section.embedded.end.php: #800000", "dark_vs": "punctuation.section.embedded.end.php: #569CD6", "light_vs": "punctuation.section.embedded.end.php: #800000", - "hc_black": "default: #FFFFFF" + "hc_black": "punctuation.section.embedded: #569CD6" } }, { @@ -469,7 +469,7 @@ "light_plus": "punctuation.section.embedded.end.php: #800000", "dark_vs": "punctuation.section.embedded.end.php: #569CD6", "light_vs": "punctuation.section.embedded.end.php: #800000", - "hc_black": "default: #FFFFFF" + "hc_black": "punctuation.section.embedded: #569CD6" } }, { @@ -480,7 +480,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { diff --git a/extensions/php/test/colorize-results/test_php.json b/extensions/php/test/colorize-results/test_php.json index 5e1b41ed8e1..5f8cff26aa6 100644 --- a/extensions/php/test/colorize-results/test_php.json +++ b/extensions/php/test/colorize-results/test_php.json @@ -227,7 +227,7 @@ "light_plus": "punctuation.section.embedded.begin.php: #800000", "dark_vs": "punctuation.section.embedded.begin.php: #569CD6", "light_vs": "punctuation.section.embedded.begin.php: #800000", - "hc_black": "default: #FFFFFF" + "hc_black": "punctuation.section.embedded: #569CD6" } }, { @@ -238,7 +238,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -260,7 +260,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -282,7 +282,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -293,18 +293,18 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { "c": "{", - "t": "text.html.php meta.embedded.block.php source.php punctuation.definition.begin.bracket.curly.php", + "t": "text.html.php meta.embedded.block.php source.php punctuation.section.scope.begin.php", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -315,7 +315,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -348,18 +348,18 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { "c": "}", - "t": "text.html.php meta.embedded.block.php source.php punctuation.definition.end.bracket.curly.php", + "t": "text.html.php meta.embedded.block.php source.php punctuation.section.scope.end.php", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -370,7 +370,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -436,7 +436,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -469,7 +469,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -491,7 +491,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -513,7 +513,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -557,7 +557,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -568,7 +568,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -612,7 +612,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -623,7 +623,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -667,7 +667,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -678,7 +678,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -722,7 +722,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -733,7 +733,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -777,7 +777,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -788,7 +788,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -832,7 +832,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -843,7 +843,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -887,7 +887,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -898,7 +898,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -942,7 +942,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -953,7 +953,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -964,7 +964,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -986,7 +986,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1008,7 +1008,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1019,7 +1019,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1030,7 +1030,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1041,7 +1041,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1052,7 +1052,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1074,7 +1074,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1107,7 +1107,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1129,7 +1129,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1151,7 +1151,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1162,7 +1162,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1195,7 +1195,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1217,7 +1217,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1239,7 +1239,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1250,7 +1250,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1294,7 +1294,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1305,18 +1305,18 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { "c": "{", - "t": "text.html.php meta.embedded.block.php source.php punctuation.definition.begin.bracket.curly.php", + "t": "text.html.php meta.embedded.block.php source.php punctuation.section.scope.begin.php", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1327,7 +1327,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1360,7 +1360,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1382,7 +1382,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1404,7 +1404,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1437,7 +1437,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1448,7 +1448,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1459,7 +1459,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1492,7 +1492,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1514,7 +1514,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1525,7 +1525,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1547,7 +1547,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1558,7 +1558,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1602,7 +1602,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1613,7 +1613,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1624,7 +1624,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1646,7 +1646,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1679,7 +1679,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1712,7 +1712,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1723,7 +1723,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1745,7 +1745,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1778,7 +1778,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1789,18 +1789,18 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { "c": "{", - "t": "text.html.php meta.embedded.block.php source.php punctuation.definition.begin.bracket.curly.php", + "t": "text.html.php meta.embedded.block.php source.php punctuation.section.scope.begin.php", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1811,7 +1811,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1855,7 +1855,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1866,18 +1866,18 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { "c": "}", - "t": "text.html.php meta.embedded.block.php source.php punctuation.definition.end.bracket.curly.php", + "t": "text.html.php meta.embedded.block.php source.php punctuation.section.scope.end.php", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1888,7 +1888,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1910,18 +1910,18 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { "c": "{", - "t": "text.html.php meta.embedded.block.php source.php punctuation.definition.begin.bracket.curly.php", + "t": "text.html.php meta.embedded.block.php source.php punctuation.section.scope.begin.php", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1932,7 +1932,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1965,7 +1965,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1976,7 +1976,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -1987,7 +1987,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2009,7 +2009,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2042,7 +2042,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2075,7 +2075,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2086,7 +2086,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2097,7 +2097,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2130,7 +2130,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2163,7 +2163,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2174,7 +2174,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2196,7 +2196,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2229,7 +2229,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2240,18 +2240,18 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { "c": "}", - "t": "text.html.php meta.embedded.block.php source.php punctuation.definition.end.bracket.curly.php", + "t": "text.html.php meta.embedded.block.php source.php punctuation.section.scope.end.php", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2262,18 +2262,18 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { "c": "}", - "t": "text.html.php meta.embedded.block.php source.php punctuation.definition.end.bracket.curly.php", + "t": "text.html.php meta.embedded.block.php source.php punctuation.section.scope.end.php", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2284,7 +2284,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2306,7 +2306,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2328,7 +2328,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2339,7 +2339,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2350,7 +2350,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2361,7 +2361,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2372,7 +2372,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2405,7 +2405,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2427,7 +2427,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2438,7 +2438,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2460,7 +2460,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2471,7 +2471,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2504,7 +2504,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2515,7 +2515,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2526,7 +2526,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2548,7 +2548,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2625,7 +2625,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2636,7 +2636,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2647,7 +2647,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2680,7 +2680,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2702,7 +2702,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2713,7 +2713,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2746,7 +2746,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2768,7 +2768,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2790,7 +2790,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2801,7 +2801,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2834,7 +2834,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2856,7 +2856,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2878,7 +2878,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2889,7 +2889,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2933,7 +2933,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2944,18 +2944,18 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { "c": "{", - "t": "text.html.php meta.embedded.block.php source.php punctuation.definition.begin.bracket.curly.php", + "t": "text.html.php meta.embedded.block.php source.php punctuation.section.scope.begin.php", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2966,7 +2966,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2988,7 +2988,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -2999,7 +2999,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -3032,7 +3032,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -3054,7 +3054,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -3076,7 +3076,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -3087,18 +3087,18 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { "c": "{", - "t": "text.html.php meta.embedded.block.php source.php punctuation.definition.begin.bracket.curly.php", + "t": "text.html.php meta.embedded.block.php source.php punctuation.section.scope.begin.php", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -3109,7 +3109,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -3142,7 +3142,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -3164,7 +3164,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -3186,7 +3186,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -3197,18 +3197,18 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { "c": "}", - "t": "text.html.php meta.embedded.block.php source.php punctuation.definition.end.bracket.curly.php", + "t": "text.html.php meta.embedded.block.php source.php punctuation.section.scope.end.php", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -3219,7 +3219,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -3241,7 +3241,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -3362,7 +3362,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -3373,7 +3373,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -3384,7 +3384,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -3428,7 +3428,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -3439,18 +3439,18 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { "c": "}", - "t": "text.html.php meta.embedded.block.php source.php punctuation.definition.end.bracket.curly.php", + "t": "text.html.php meta.embedded.block.php source.php punctuation.section.scope.end.php", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -3461,7 +3461,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -3483,7 +3483,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -3505,7 +3505,7 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -3516,29 +3516,29 @@ "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { "c": "{", - "t": "text.html.php meta.embedded.block.php source.php punctuation.definition.begin.bracket.curly.php", + "t": "text.html.php meta.embedded.block.php source.php punctuation.section.scope.begin.php", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { "c": "}", - "t": "text.html.php meta.embedded.block.php source.php punctuation.definition.end.bracket.curly.php", + "t": "text.html.php meta.embedded.block.php source.php punctuation.section.scope.end.php", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "meta.embedded: #FFFFFF" } }, { @@ -3549,7 +3549,7 @@ "light_plus": "punctuation.section.embedded.end.php: #800000", "dark_vs": "punctuation.section.embedded.end.php: #569CD6", "light_vs": "punctuation.section.embedded.end.php: #800000", - "hc_black": "default: #FFFFFF" + "hc_black": "punctuation.section.embedded: #569CD6" } }, { @@ -3560,7 +3560,7 @@ "light_plus": "punctuation.section.embedded.end.php: #800000", "dark_vs": "punctuation.section.embedded.end.php: #569CD6", "light_vs": "punctuation.section.embedded.end.php: #800000", - "hc_black": "default: #FFFFFF" + "hc_black": "punctuation.section.embedded: #569CD6" } }, { diff --git a/extensions/python/test/colorize-results/test_py.json b/extensions/python/test/colorize-results/test_py.json index b456114b1ed..235958f078f 100644 --- a/extensions/python/test/colorize-results/test_py.json +++ b/extensions/python/test/colorize-results/test_py.json @@ -5294,8 +5294,8 @@ "c": "(", "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python support.other.parenthesis.regexp punctuation.parenthesis.begin.regexp", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "support.other.parenthesis.regexp: #CE9178", + "light_plus": "support.other.parenthesis.regexp: #D16969", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "string.regexp: #D16969" @@ -5305,8 +5305,8 @@ "c": "[", "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python meta.character.set.regexp punctuation.character.set.begin.regexp constant.other.set.regexp", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "punctuation.character.set.begin.regexp: #CE9178", + "light_plus": "punctuation.character.set.begin.regexp: #D16969", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "string.regexp: #D16969" @@ -5316,19 +5316,19 @@ "c": "0-9-", "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python meta.character.set.regexp constant.character.set.regexp", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "constant.character.set.regexp: #D16969", + "light_plus": "constant.character.set.regexp: #811F3F", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", - "hc_black": "string.regexp: #D16969" + "hc_black": "constant.character: #569CD6" } }, { "c": "]", "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python meta.character.set.regexp punctuation.character.set.end.regexp constant.other.set.regexp", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "punctuation.character.set.end.regexp: #CE9178", + "light_plus": "punctuation.character.set.end.regexp: #D16969", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "string.regexp: #D16969" @@ -5338,8 +5338,8 @@ "c": "*", "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python keyword.operator.quantifier.regexp", "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", + "dark_plus": "keyword.operator.quantifier.regexp: #D7BA7D", + "light_plus": "keyword.operator.quantifier.regexp: #000000", "dark_vs": "keyword.operator: #D4D4D4", "light_vs": "keyword.operator: #000000", "hc_black": "keyword.operator: #D4D4D4" @@ -5349,8 +5349,8 @@ "c": ")", "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python support.other.parenthesis.regexp punctuation.parenthesis.end.regexp", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "support.other.parenthesis.regexp: #CE9178", + "light_plus": "support.other.parenthesis.regexp: #D16969", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "string.regexp: #D16969" @@ -5371,8 +5371,8 @@ "c": "*", "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python keyword.operator.quantifier.regexp", "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", + "dark_plus": "keyword.operator.quantifier.regexp: #D7BA7D", + "light_plus": "keyword.operator.quantifier.regexp: #000000", "dark_vs": "keyword.operator: #D4D4D4", "light_vs": "keyword.operator: #000000", "hc_black": "keyword.operator: #D4D4D4" @@ -5382,8 +5382,8 @@ "c": "(", "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python support.other.parenthesis.regexp punctuation.parenthesis.begin.regexp", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "support.other.parenthesis.regexp: #CE9178", + "light_plus": "support.other.parenthesis.regexp: #D16969", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "string.regexp: #D16969" @@ -5393,8 +5393,8 @@ "c": "[", "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python meta.character.set.regexp punctuation.character.set.begin.regexp constant.other.set.regexp", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "punctuation.character.set.begin.regexp: #CE9178", + "light_plus": "punctuation.character.set.begin.regexp: #D16969", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "string.regexp: #D16969" @@ -5404,19 +5404,19 @@ "c": "A-Za-z", "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python meta.character.set.regexp constant.character.set.regexp", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "constant.character.set.regexp: #D16969", + "light_plus": "constant.character.set.regexp: #811F3F", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", - "hc_black": "string.regexp: #D16969" + "hc_black": "constant.character: #569CD6" } }, { "c": "]", "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python meta.character.set.regexp punctuation.character.set.end.regexp constant.other.set.regexp", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "punctuation.character.set.end.regexp: #CE9178", + "light_plus": "punctuation.character.set.end.regexp: #D16969", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "string.regexp: #D16969" @@ -5426,8 +5426,8 @@ "c": "+", "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python keyword.operator.quantifier.regexp", "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", + "dark_plus": "keyword.operator.quantifier.regexp: #D7BA7D", + "light_plus": "keyword.operator.quantifier.regexp: #000000", "dark_vs": "keyword.operator: #D4D4D4", "light_vs": "keyword.operator: #000000", "hc_black": "keyword.operator: #D4D4D4" @@ -5437,8 +5437,8 @@ "c": ")", "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python support.other.parenthesis.regexp punctuation.parenthesis.end.regexp", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "support.other.parenthesis.regexp: #CE9178", + "light_plus": "support.other.parenthesis.regexp: #D16969", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "string.regexp: #D16969" @@ -5470,8 +5470,8 @@ "c": "+", "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python keyword.operator.quantifier.regexp", "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", + "dark_plus": "keyword.operator.quantifier.regexp: #D7BA7D", + "light_plus": "keyword.operator.quantifier.regexp: #000000", "dark_vs": "keyword.operator: #D4D4D4", "light_vs": "keyword.operator: #000000", "hc_black": "keyword.operator: #D4D4D4" @@ -5481,8 +5481,8 @@ "c": "(", "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python support.other.parenthesis.regexp punctuation.parenthesis.begin.regexp", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "support.other.parenthesis.regexp: #CE9178", + "light_plus": "support.other.parenthesis.regexp: #D16969", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "string.regexp: #D16969" @@ -5503,8 +5503,8 @@ "c": "*", "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python keyword.operator.quantifier.regexp", "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", + "dark_plus": "keyword.operator.quantifier.regexp: #D7BA7D", + "light_plus": "keyword.operator.quantifier.regexp: #000000", "dark_vs": "keyword.operator: #D4D4D4", "light_vs": "keyword.operator: #000000", "hc_black": "keyword.operator: #D4D4D4" @@ -5514,8 +5514,8 @@ "c": ")", "t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python support.other.parenthesis.regexp punctuation.parenthesis.end.regexp", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "support.other.parenthesis.regexp: #CE9178", + "light_plus": "support.other.parenthesis.regexp: #D16969", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "string.regexp: #D16969" @@ -6504,8 +6504,8 @@ "c": "[", "t": "source.python string.regexp.quoted.multi.python meta.character.set.regexp punctuation.character.set.begin.regexp constant.other.set.regexp", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "punctuation.character.set.begin.regexp: #CE9178", + "light_plus": "punctuation.character.set.begin.regexp: #D16969", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "string.regexp: #D16969" @@ -6515,11 +6515,11 @@ "c": "1,2)`` leads to", "t": "source.python string.regexp.quoted.multi.python meta.character.set.regexp constant.character.set.regexp", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "constant.character.set.regexp: #D16969", + "light_plus": "constant.character.set.regexp: #811F3F", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", - "hc_black": "string.regexp: #D16969" + "hc_black": "constant.character: #569CD6" } }, { diff --git a/extensions/razor/test/colorize-results/test_cshtml.json b/extensions/razor/test/colorize-results/test_cshtml.json index 2e3dbe31e97..e7eead2eeeb 100644 --- a/extensions/razor/test/colorize-results/test_cshtml.json +++ b/extensions/razor/test/colorize-results/test_cshtml.json @@ -14,11 +14,11 @@ "c": "{", "t": "text.html.cshtml section.embedded.source.cshtml punctuation.section.embedded.begin.cshtml", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "dark_plus": "punctuation.section.embedded: #569CD6", + "light_plus": "punctuation.section.embedded: #0000FF", + "dark_vs": "punctuation.section.embedded: #569CD6", + "light_vs": "punctuation.section.embedded: #0000FF", + "hc_black": "punctuation.section.embedded: #569CD6" } }, { @@ -333,11 +333,11 @@ "c": "{", "t": "text.html.cshtml section.embedded.source.cshtml section.embedded.source.cshtml punctuation.section.embedded.begin.cshtml", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "dark_plus": "punctuation.section.embedded: #569CD6", + "light_plus": "punctuation.section.embedded: #0000FF", + "dark_vs": "punctuation.section.embedded: #569CD6", + "light_vs": "punctuation.section.embedded: #0000FF", + "hc_black": "punctuation.section.embedded: #569CD6" } }, { @@ -1125,22 +1125,22 @@ "c": "}", "t": "text.html.cshtml section.embedded.source.cshtml section.embedded.source.cshtml punctuation.section.embedded.begin.cshtml", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "dark_plus": "punctuation.section.embedded: #569CD6", + "light_plus": "punctuation.section.embedded: #0000FF", + "dark_vs": "punctuation.section.embedded: #569CD6", + "light_vs": "punctuation.section.embedded: #0000FF", + "hc_black": "punctuation.section.embedded: #569CD6" } }, { "c": "}", "t": "text.html.cshtml section.embedded.source.cshtml punctuation.section.embedded.begin.cshtml", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "dark_plus": "punctuation.section.embedded: #569CD6", + "light_plus": "punctuation.section.embedded: #0000FF", + "dark_vs": "punctuation.section.embedded: #569CD6", + "light_vs": "punctuation.section.embedded: #0000FF", + "hc_black": "punctuation.section.embedded: #569CD6" } }, { @@ -3226,11 +3226,11 @@ "c": "@(", "t": "text.html.cshtml punctuation.section.embedded.begin.cshtml", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "dark_plus": "punctuation.section.embedded: #569CD6", + "light_plus": "punctuation.section.embedded: #0000FF", + "dark_vs": "punctuation.section.embedded: #569CD6", + "light_vs": "punctuation.section.embedded: #0000FF", + "hc_black": "punctuation.section.embedded: #569CD6" } }, { @@ -3270,11 +3270,11 @@ "c": ")", "t": "text.html.cshtml punctuation.section.embedded.begin.cshtml", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "dark_plus": "punctuation.section.embedded: #569CD6", + "light_plus": "punctuation.section.embedded: #0000FF", + "dark_vs": "punctuation.section.embedded: #569CD6", + "light_vs": "punctuation.section.embedded: #0000FF", + "hc_black": "punctuation.section.embedded: #569CD6" } }, { diff --git a/extensions/ruby/test/colorize-results/test_rb.json b/extensions/ruby/test/colorize-results/test_rb.json index a3c5163218e..996dbbf0192 100644 --- a/extensions/ruby/test/colorize-results/test_rb.json +++ b/extensions/ruby/test/colorize-results/test_rb.json @@ -2258,11 +2258,11 @@ "c": "#{", "t": "source.ruby string.interpolated.ruby meta.embedded.line.ruby punctuation.section.embedded.begin.ruby", "r": { - "dark_plus": "meta.embedded: #D4D4D4", - "light_plus": "meta.embedded: #000000", - "dark_vs": "meta.embedded: #D4D4D4", - "light_vs": "meta.embedded: #000000", - "hc_black": "string: #CE9178" + "dark_plus": "punctuation.section.embedded: #569CD6", + "light_plus": "punctuation.section.embedded: #0000FF", + "dark_vs": "punctuation.section.embedded: #569CD6", + "light_vs": "punctuation.section.embedded: #0000FF", + "hc_black": "punctuation.section.embedded: #569CD6" } }, { @@ -2280,11 +2280,11 @@ "c": "}", "t": "source.ruby string.interpolated.ruby meta.embedded.line.ruby punctuation.section.embedded.end.ruby source.ruby", "r": { - "dark_plus": "meta.embedded: #D4D4D4", - "light_plus": "meta.embedded: #000000", - "dark_vs": "meta.embedded: #D4D4D4", - "light_vs": "meta.embedded: #000000", - "hc_black": "string: #CE9178" + "dark_plus": "punctuation.section.embedded: #569CD6", + "light_plus": "punctuation.section.embedded: #0000FF", + "dark_vs": "punctuation.section.embedded: #569CD6", + "light_vs": "punctuation.section.embedded: #0000FF", + "hc_black": "punctuation.section.embedded: #569CD6" } }, { @@ -2500,11 +2500,11 @@ "c": "\\d", "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby constant.character.escape.ruby", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", - "hc_black": "string.regexp: #D16969" + "hc_black": "constant.character: #569CD6" } }, { @@ -2522,11 +2522,11 @@ "c": "\\.\\d", "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby constant.character.escape.ruby", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", - "hc_black": "string.regexp: #D16969" + "hc_black": "constant.character: #569CD6" } }, { @@ -2544,11 +2544,11 @@ "c": "\\.\\d", "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby constant.character.escape.ruby", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", - "hc_black": "string.regexp: #D16969" + "hc_black": "constant.character: #569CD6" } }, { @@ -2577,11 +2577,11 @@ "c": "\\.\\d", "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby meta.group.regexp.ruby constant.character.escape.ruby", "r": { - "dark_plus": "string.regexp: #D16969", - "light_plus": "string.regexp: #811F3F", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", - "hc_black": "string.regexp: #D16969" + "hc_black": "constant.character: #569CD6" } }, { diff --git a/extensions/scss/test/colorize-results/test_scss.json b/extensions/scss/test/colorize-results/test_scss.json index 961c9da0912..cc598d4c7a2 100644 --- a/extensions/scss/test/colorize-results/test_scss.json +++ b/extensions/scss/test/colorize-results/test_scss.json @@ -20716,11 +20716,11 @@ "c": "\\\\", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss string.quoted.single.scss constant.character.escape.scss", "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" + "hc_black": "constant.character: #569CD6" } }, { @@ -20837,11 +20837,11 @@ "c": "\\'", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-value.scss string.quoted.single.scss constant.character.escape.scss", "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" + "hc_black": "constant.character: #569CD6" } }, { @@ -20914,11 +20914,11 @@ "c": "\\\"", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-value.scss string.quoted.double.scss constant.character.escape.scss", "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #A31515", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" + "hc_black": "constant.character: #569CD6" } }, { diff --git a/extensions/theme-abyss/themes/abyss-color-theme.json b/extensions/theme-abyss/themes/abyss-color-theme.json index 6b1a0f1c474..bae5fd48804 100644 --- a/extensions/theme-abyss/themes/abyss-color-theme.json +++ b/extensions/theme-abyss/themes/abyss-color-theme.json @@ -8,7 +8,7 @@ } }, { - "scope": "meta.embedded", + "scope": ["meta.embedded", "source.groovy.embedded"], "settings": { "background": "#000c18", "foreground": "#6688cc" diff --git a/extensions/theme-defaults/themes/dark_plus.json b/extensions/theme-defaults/themes/dark_plus.json index 972481b7e22..6fdfb2263ce 100644 --- a/extensions/theme-defaults/themes/dark_plus.json +++ b/extensions/theme-defaults/themes/dark_plus.json @@ -101,6 +101,59 @@ "settings": { "foreground": "#CE9178" } + }, + { + "name": "Regular expression groups", + "scope": [ + "punctuation.definition.group.regexp", + "punctuation.definition.group.assertion.regexp", + "punctuation.definition.character-class.regexp", + "punctuation.character.set.begin.regexp", + "punctuation.character.set.end.regexp", + "keyword.operator.negation.regexp", + "support.other.parenthesis.regexp" + ], + "settings": { + "foreground": "#CE9178" + } + }, + { + "scope": [ + "constant.character.character-class.regexp", + "constant.other.character-class.set.regexp", + "constant.other.character-class.regexp", + "constant.character.set.regexp" + ], + "settings": { + "foreground": "#d16969" + } + }, + { + "scope": [ + "keyword.operator.or.regexp", + "keyword.control.anchor.regexp" + ], + "settings": { + "foreground": "#DCDCAA" + } + }, + { + "scope": "keyword.operator.quantifier.regexp", + "settings": { + "foreground": "#d7ba7d" + } + }, + { + "scope": "constant.character", + "settings": { + "foreground": "#569cd6" + } + }, + { + "scope": "constant.character.escape", + "settings": { + "foreground": "#d7ba7d" + } } ] } \ No newline at end of file diff --git a/extensions/theme-defaults/themes/dark_vs.json b/extensions/theme-defaults/themes/dark_vs.json index 57dd332be52..084720561d0 100644 --- a/extensions/theme-defaults/themes/dark_vs.json +++ b/extensions/theme-defaults/themes/dark_vs.json @@ -10,7 +10,10 @@ } }, { - "scope": "meta.embedded", + "scope": [ + "meta.embedded", + "source.groovy.embedded" + ], "settings": { "foreground": "#D4D4D4", "background": "#1E1E1E" @@ -248,11 +251,11 @@ } }, { - "name": "JavaScript string interpolation ${}", + "name": "String interpolation", "scope": [ "punctuation.definition.template-expression.begin", "punctuation.definition.template-expression.end", - "punctuation.section.embedded.coffee" + "punctuation.section.embedded" ], "settings": { "foreground": "#569cd6" diff --git a/extensions/theme-defaults/themes/hc_black_defaults.json b/extensions/theme-defaults/themes/hc_black_defaults.json index 986fa7cf615..8833ff32c1f 100644 --- a/extensions/theme-defaults/themes/hc_black_defaults.json +++ b/extensions/theme-defaults/themes/hc_black_defaults.json @@ -14,6 +14,16 @@ "background": "#000000" } }, + { + "scope": [ + "meta.embedded", + "source.groovy.embedded" + ], + "settings": { + "foreground": "#FFFFFF", + "background": "#000000" + } + }, { "scope": "emphasis", "settings": { @@ -61,6 +71,12 @@ "foreground": "#b46695" } }, + { + "scope": "constant.character", + "settings": { + "foreground": "#569cd6" + } + }, { "scope": "entity.name.tag", "settings": { @@ -227,11 +243,11 @@ } }, { - "name": "JavaScript string interpolation ${}", + "name": "String interpolation", "scope": [ "punctuation.definition.template-expression.begin", "punctuation.definition.template-expression.end", - "punctuation.section.embedded.coffee" + "punctuation.section.embedded" ], "settings": { "foreground": "#569cd6" diff --git a/extensions/theme-defaults/themes/light_plus.json b/extensions/theme-defaults/themes/light_plus.json index c087a8f6ca1..3d5775ecec5 100644 --- a/extensions/theme-defaults/themes/light_plus.json +++ b/extensions/theme-defaults/themes/light_plus.json @@ -101,6 +101,60 @@ "settings": { "foreground": "#0451a5" } + }, + { + "name": "Regular expression groups", + "scope": [ + "punctuation.definition.group.regexp", + "punctuation.definition.group.assertion.regexp", + "punctuation.definition.character-class.regexp", + "punctuation.character.set.begin.regexp", + "punctuation.character.set.end.regexp", + "keyword.operator.negation.regexp", + "support.other.parenthesis.regexp" + ], + "settings": { + "foreground": "#d16969" + } + }, + { + "scope": [ + "constant.character.character-class.regexp", + "constant.other.character-class.set.regexp", + "constant.other.character-class.regexp", + "constant.character.set.regexp" + ], + "settings": { + "foreground": "#811f3f" + } + }, + { + "scope": "keyword.operator.quantifier.regexp", + "settings": { + "foreground": "#000000" + } + }, + { + "scope": [ + "keyword.operator.or.regexp", + "keyword.control.anchor.regexp" + ], + "settings": { + "foreground": "#ff0000" + } + }, + { + "scope": "constant.character", + "settings": { + "foreground": "#0000ff" + } + }, + { + "scope": "constant.character.escape", + "settings": { + "foreground": "#a31515" + } } + ] } \ No newline at end of file diff --git a/extensions/theme-defaults/themes/light_vs.json b/extensions/theme-defaults/themes/light_vs.json index 91f311a80a8..883d88347dc 100644 --- a/extensions/theme-defaults/themes/light_vs.json +++ b/extensions/theme-defaults/themes/light_vs.json @@ -4,7 +4,7 @@ "include": "./light_defaults.json", "tokenColors": [ { - "scope": "meta.embedded", + "scope": ["meta.embedded", "source.groovy.embedded"], "settings": { "foreground": "#000000ff", "background":"#ffffffff" @@ -244,11 +244,11 @@ } }, { - "name": "JavaScript string interpolation ${}", + "name": "String interpolation", "scope": [ "punctuation.definition.template-expression.begin", "punctuation.definition.template-expression.end", - "punctuation.section.embedded.coffee" + "punctuation.section.embedded" ], "settings": { "foreground": "#0000ff" diff --git a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json index 3a14293383f..bad21aface9 100644 --- a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json +++ b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json @@ -55,7 +55,7 @@ } }, { - "scope": "meta.embedded", + "scope": ["meta.embedded", "source.groovy.embedded"], "settings": { "background": "#221a0f", "foreground": "#d3af86" diff --git a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json index 31cd63a47bc..398853e9988 100644 --- a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json +++ b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json @@ -52,7 +52,7 @@ } }, { - "scope": "meta.embedded", + "scope": ["meta.embedded", "source.groovy.embedded"], "settings": { "background": "#1e1e1e", "foreground": "#C5C8C6" @@ -453,7 +453,9 @@ }, { "name": "Ruby Embedded Source", - "scope": "source.ruby.embedded.source", + "scope": [ + "punctuation.section.embedded.begin.ruby", + "punctuation.section.embedded.end.ruby"], "settings": { "foreground": "#D08442" } diff --git a/extensions/theme-monokai/themes/monokai-color-theme.json b/extensions/theme-monokai/themes/monokai-color-theme.json index a3152125248..f7e45a6daf0 100644 --- a/extensions/theme-monokai/themes/monokai-color-theme.json +++ b/extensions/theme-monokai/themes/monokai-color-theme.json @@ -97,7 +97,7 @@ } }, { - "scope": "meta.embedded", + "scope": ["meta.embedded", "source.groovy.embedded"], "settings": { "background": "#272822", "foreground": "#F8F8F2" @@ -121,7 +121,7 @@ "name": "Template Definition", "scope": [ "punctuation.definition.template-expression", - "punctuation.section.embedded.coffee" + "punctuation.section.embedded" ], "settings": { "foreground": "#F92672" diff --git a/extensions/theme-quietlight/themes/quietlight-color-theme.json b/extensions/theme-quietlight/themes/quietlight-color-theme.json index 59c6834293f..9fd5ad62cee 100644 --- a/extensions/theme-quietlight/themes/quietlight-color-theme.json +++ b/extensions/theme-quietlight/themes/quietlight-color-theme.json @@ -8,7 +8,7 @@ } }, { - "scope": "meta.embedded", + "scope": ["meta.embedded", "source.groovy.embedded"], "settings": { "background": "#F5F5F5", "foreground": "#333333" diff --git a/extensions/theme-red/themes/Red-color-theme.json b/extensions/theme-red/themes/Red-color-theme.json index 414ad2fb5e8..df3b63b3792 100644 --- a/extensions/theme-red/themes/Red-color-theme.json +++ b/extensions/theme-red/themes/Red-color-theme.json @@ -69,7 +69,7 @@ } }, { - "scope": "meta.embedded", + "scope": ["meta.embedded", "source.groovy.embedded"], "settings": { "background": "#390000", "foreground": "#F8F8F8" @@ -170,16 +170,16 @@ } }, { - "name": "String embedded-source", - "scope": "string.quoted source", + "scope": "constant.character", "settings": { - "fontStyle": "", - "foreground": "#9df39fff" + "foreground": "#ec0d1e" } }, { - "name": "String constant", - "scope": "string constant", + "scope": [ + "string constant", + "constant.character.escape" + ], "settings": { "fontStyle": "", "foreground": "#ffe862ff" @@ -393,6 +393,18 @@ "fontStyle": "", "foreground": "#fec758ff" } + }, + { + "name": "String interpolation", + "scope": [ + "punctuation.definition.template-expression.begin", + "punctuation.definition.template-expression.end", + "punctuation.section.embedded", + ".format.placeholder" + ], + "settings": { + "foreground": "#ec0d1e" + } } ] } \ No newline at end of file diff --git a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json index 88e94050dee..c7bf6f68cb1 100644 --- a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json +++ b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json @@ -8,7 +8,7 @@ } }, { - "scope": "meta.embedded", + "scope": ["meta.embedded", "source.groovy.embedded"], "settings": { "background": "#002B36", "foreground": "#93A1A1" diff --git a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json index a7a4be6acb7..6d2b7146583 100644 --- a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json +++ b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json @@ -8,7 +8,7 @@ } }, { - "scope": "meta.embedded", + "scope": ["meta.embedded", "source.groovy.embedded"], "settings": { "background": "#FDF6E3", "foreground": "#657B83" diff --git a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json index 23bf67fb9a6..7163c4d671a 100644 --- a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json +++ b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json @@ -61,7 +61,7 @@ } }, { - "scope": "meta.embedded", + "scope": ["meta.embedded", "source.groovy.embedded"], "settings": { "background": "#002451", "foreground": "#FFFFFF" diff --git a/extensions/typescript/syntaxes/TypeScript.tmLanguage.json b/extensions/typescript/syntaxes/TypeScript.tmLanguage.json index 96f7d0e6fed..185a638d78d 100644 --- a/extensions/typescript/syntaxes/TypeScript.tmLanguage.json +++ b/extensions/typescript/syntaxes/TypeScript.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/648a036db2bad78ee93463269ec49ed91ee5aa91", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/30d95ebb561fc57e784b43d08f0a6f46ad0c3e5d", "name": "TypeScript", "scopeName": "source.ts", "fileTypes": [ @@ -91,6 +91,29 @@ { "include": "#comment" }, + { + "begin": "(,)\\s*(?!\\S)", + "beginCaptures": { + "1": { + "name": "punctuation.separator.comma.ts" + } + }, + "end": "(?)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n )) |\n (:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n ))\n)", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n (=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n )) |\n (:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n ))\n)", "beginCaptures": { "1": { "name": "meta.definition.variable.ts entity.name.function.ts" @@ -721,7 +744,7 @@ }, "import-declaration": { "name": "meta.import.ts", - "begin": "(?)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n )) |\n (:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n ))\n)" + "match": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=(\\?\\s*)?\\s*\n (=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n )) |\n (:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n ))\n)" }, { "name": "meta.definition.property.ts variable.object.property.ts", @@ -1566,7 +1589,7 @@ } }, { - "match": "(?x)(?:\\s*\\b(public|private|protected|readonly)\\s+)?(\\.\\.\\.)?\\s*(?)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n )) |\n (:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n ))\n)", + "match": "(?x)(?:\\s*\\b(public|private|protected|readonly)\\s+)?(\\.\\.\\.)?\\s*(?)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n )) |\n (:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n ))\n)", "captures": { "1": { "name": "storage.modifier.ts" @@ -2404,7 +2427,7 @@ } }, { - "match": "(?x) (\\.) \\s* (?:\n (ATTRIBUTE_NODE|CDATA_SECTION_NODE|COMMENT_NODE|DOCUMENT_FRAGMENT_NODE|DOCUMENT_NODE|DOCUMENT_TYPE_NODE\n |DOMSTRING_SIZE_ERR|ELEMENT_NODE|ENTITY_NODE|ENTITY_REFERENCE_NODE|HIERARCHY_REQUEST_ERR|INDEX_SIZE_ERR\n |INUSE_ATTRIBUTE_ERR|INVALID_CHARACTER_ERR|NO_DATA_ALLOWED_ERR|NO_MODIFICATION_ALLOWED_ERR|NOT_FOUND_ERR\n |NOT_SUPPORTED_ERR|NOTATION_NODE|PROCESSING_INSTRUCTION_NODE|TEXT_NODE|WRONG_DOCUMENT_ERR)\n |\n (_content|[xyz]|abbr|above|accept|acceptCharset|accessKey|action|align|[av]Link(?:color)?|all|alt|anchors|appCodeName\n |appCore|applets|appMinorVersion|appName|appVersion|archive|areas|arguments|attributes|availHeight|availLeft|availTop\n |availWidth|axis|background|backgroundColor|backgroundImage|below|bgColor|body|border|borderBottomWidth|borderColor\n |borderLeftWidth|borderRightWidth|borderStyle|borderTopWidth|borderWidth|bottom|bufferDepth|callee|caller|caption\n |cellPadding|cells|cellSpacing|ch|characterSet|charset|checked|childNodes|chOff|cite|classes|className|clear\n |clientInformation|clip|clipBoardData|closed|code|codeBase|codeType|color|colorDepth|cols|colSpan|compact|complete\n |components|content|controllers|cookie|cookieEnabled|cords|cpuClass|crypto|current|data|dateTime|declare|defaultCharset\n |defaultChecked|defaultSelected|defaultStatus|defaultValue|defaultView|defer|description|dialogArguments|dialogHeight\n |dialogLeft|dialogTop|dialogWidth|dir|directories|disabled|display|docmain|doctype|documentElement|elements|embeds\n |enabledPlugin|encoding|enctype|entities|event|expando|external|face|fgColor|filename|firstChild|fontFamily|fontSize\n |fontWeight|form|formName|forms|frame|frameBorder|frameElement|frames|hasFocus|hash|headers|height|history|host\n |hostname|href|hreflang|hspace|htmlFor|httpEquiv|id|ids|ignoreCase|images|implementation|index|innerHeight|innerWidth\n |input|isMap|label|lang|language|lastChild|lastIndex|lastMatch|lastModified|lastParen|layer[sXY]|left|leftContext\n |lineHeight|link|linkColor|links|listStyleType|localName|location|locationbar|longDesc|lowsrc|lowSrc|marginBottom\n |marginHeight|marginLeft|marginRight|marginTop|marginWidth|maxLength|media|menubar|method|mimeTypes|multiline|multiple\n |name|nameProp|namespaces|namespaceURI|next|nextSibling|nodeName|nodeType|nodeValue|noHref|noResize|noShade|notationName\n |notations|noWrap|object|offscreenBuffering|onLine|onreadystatechange|opener|opsProfile|options|oscpu|outerHeight\n |outerWidth|ownerDocument|paddingBottom|paddingLeft|paddingRight|paddingTop|page[XY]|page[XY]Offset|parent|parentLayer\n |parentNode|parentWindow|pathname|personalbar|pixelDepth|pkcs11|platform|plugins|port|prefix|previous|previousDibling\n |product|productSub|profile|profileend|prompt|prompter|protocol|publicId|readOnly|readyState|referrer|rel|responseText\n |responseXML|rev|right|rightContext|rowIndex|rows|rowSpan|rules|scheme|scope|screen[XY]|screenLeft|screenTop|scripts\n |scrollbars|scrolling|sectionRowIndex|security|securityPolicy|selected|selectedIndex|selection|self|shape|siblingAbove\n |siblingBelow|size|source|specified|standby|start|status|statusbar|statusText|style|styleSheets|suffixes|summary\n |systemId|systemLanguage|tagName|tags|target|tBodies|text|textAlign|textDecoration|textIndent|textTransform|tFoot|tHead\n |title|toolbar|top|type|undefined|uniqueID|updateInterval|URL|URLUnencoded|useMap|userAgent|userLanguage|userProfile\n |vAlign|value|valueType|vendor|vendorSub|version|visibility|vspace|whiteSpace|width|X[MS]LDocument|zIndex))\\b(?!\\$|\\s*(<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "match": "(?x) (\\.) \\s* (?:\n (ATTRIBUTE_NODE|CDATA_SECTION_NODE|COMMENT_NODE|DOCUMENT_FRAGMENT_NODE|DOCUMENT_NODE|DOCUMENT_TYPE_NODE\n |DOMSTRING_SIZE_ERR|ELEMENT_NODE|ENTITY_NODE|ENTITY_REFERENCE_NODE|HIERARCHY_REQUEST_ERR|INDEX_SIZE_ERR\n |INUSE_ATTRIBUTE_ERR|INVALID_CHARACTER_ERR|NO_DATA_ALLOWED_ERR|NO_MODIFICATION_ALLOWED_ERR|NOT_FOUND_ERR\n |NOT_SUPPORTED_ERR|NOTATION_NODE|PROCESSING_INSTRUCTION_NODE|TEXT_NODE|WRONG_DOCUMENT_ERR)\n |\n (_content|[xyz]|abbr|above|accept|acceptCharset|accessKey|action|align|[av]Link(?:color)?|all|alt|anchors|appCodeName\n |appCore|applets|appMinorVersion|appName|appVersion|archive|areas|arguments|attributes|availHeight|availLeft|availTop\n |availWidth|axis|background|backgroundColor|backgroundImage|below|bgColor|body|border|borderBottomWidth|borderColor\n |borderLeftWidth|borderRightWidth|borderStyle|borderTopWidth|borderWidth|bottom|bufferDepth|callee|caller|caption\n |cellPadding|cells|cellSpacing|ch|characterSet|charset|checked|childNodes|chOff|cite|classes|className|clear\n |clientInformation|clip|clipBoardData|closed|code|codeBase|codeType|color|colorDepth|cols|colSpan|compact|complete\n |components|content|controllers|cookie|cookieEnabled|cords|cpuClass|crypto|current|data|dateTime|declare|defaultCharset\n |defaultChecked|defaultSelected|defaultStatus|defaultValue|defaultView|defer|description|dialogArguments|dialogHeight\n |dialogLeft|dialogTop|dialogWidth|dir|directories|disabled|display|docmain|doctype|documentElement|elements|embeds\n |enabledPlugin|encoding|enctype|entities|event|expando|external|face|fgColor|filename|firstChild|fontFamily|fontSize\n |fontWeight|form|formName|forms|frame|frameBorder|frameElement|frames|hasFocus|hash|headers|height|history|host\n |hostname|href|hreflang|hspace|htmlFor|httpEquiv|id|ids|ignoreCase|images|implementation|index|innerHeight|innerWidth\n |input|isMap|label|lang|language|lastChild|lastIndex|lastMatch|lastModified|lastParen|layer[sXY]|left|leftContext\n |lineHeight|link|linkColor|links|listStyleType|localName|location|locationbar|longDesc|lowsrc|lowSrc|marginBottom\n |marginHeight|marginLeft|marginRight|marginTop|marginWidth|maxLength|media|menubar|method|mimeTypes|multiline|multiple\n |name|nameProp|namespaces|namespaceURI|next|nextSibling|nodeName|nodeType|nodeValue|noHref|noResize|noShade|notationName\n |notations|noWrap|object|offscreenBuffering|onLine|onreadystatechange|opener|opsProfile|options|oscpu|outerHeight\n |outerWidth|ownerDocument|paddingBottom|paddingLeft|paddingRight|paddingTop|page[XY]|page[XY]Offset|parent|parentLayer\n |parentNode|parentWindow|pathname|personalbar|pixelDepth|pkcs11|platform|plugins|port|prefix|previous|previousDibling\n |product|productSub|profile|profileend|prompt|prompter|protocol|publicId|readOnly|readyState|referrer|rel|responseText\n |responseXML|rev|right|rightContext|rowIndex|rows|rowSpan|rules|scheme|scope|screen[XY]|screenLeft|screenTop|scripts\n |scrollbars|scrolling|sectionRowIndex|security|securityPolicy|selected|selectedIndex|selection|self|shape|siblingAbove\n |siblingBelow|size|source|specified|standby|start|status|statusbar|statusText|style|styleSheets|suffixes|summary\n |systemId|systemLanguage|tagName|tags|target|tBodies|text|textAlign|textDecoration|textIndent|textTransform|tFoot|tHead\n |title|toolbar|top|type|undefined|uniqueID|updateInterval|URL|URLUnencoded|useMap|userAgent|userLanguage|userProfile\n |vAlign|value|valueType|vendor|vendorSub|version|visibility|vspace|whiteSpace|width|X[MS]LDocument|zIndex))\\b(?!\\$|\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", "captures": { "1": { "name": "punctuation.accessor.ts" @@ -2479,13 +2502,13 @@ ] }, "function-call": { - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)?\\()", - "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", "patterns": [ { "name": "meta.function-call.ts", "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "end": "(?=\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", "patterns": [ { "include": "#literal" @@ -2546,7 +2569,7 @@ "include": "#object-identifiers" }, { - "match": "(?x)(?:(\\.)\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n))", + "match": "(?x)(?:(\\.)\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n))", "captures": { "1": { "name": "punctuation.accessor.ts" @@ -2625,7 +2648,7 @@ "patterns": [ { "name": "cast.expr.ts", - "begin": "(?:(?<=return|throw|yield|await|default|[=(,:>*?]))\\s*(<)(?!+*?]))\\s*(<)(?!)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n)))", + "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=:\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n)))", "captures": { "0": { "name": "meta.object-literal.key.ts" @@ -2745,8 +2768,7 @@ "0": { "name": "meta.object-literal.key.ts" } - }, - "end": "(?=,|\\})" + } }, { "name": "meta.object.member.ts", @@ -2772,6 +2794,16 @@ } } }, + { + "name": "meta.object.member.ts", + "begin": "(?=[_$[:alpha:]][_$[:alnum:]]*\\s*=)", + "end": "(?=,|\\}|$)", + "patterns": [ + { + "include": "#expression" + } + ] + }, { "include": "#object-member-body" }, @@ -2846,7 +2878,7 @@ "name": "keyword.control.as.ts" } }, - "end": "(?=$|^|[;,:})\\]])", + "end": "(?=$|^|[;,:})\\]]|((? is on new line\n (\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n ) |\n (\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends \n ) |\n # arrow function possible to detect only with => on same line\n (\n (<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\(([^()]|\\([^()]*\\))*\\) # parameteres\n (\\s*:\\s*(.)*)? # return type\n \\s*=> # arrow operator\n )\n )\n)", + "begin": "(?x) (?:\n (? is on new line\n (\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n ) |\n (\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends \n ) |\n # arrow function possible to detect only with => on same line\n (\n (<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\(([^()]|\\([^()]*\\))*\\) # parameteres\n (\\s*:\\s*(.)*)? # return type\n \\s*=> # arrow operator\n )\n )\n)", "beginCaptures": { "1": { "name": "storage.modifier.async.ts" @@ -3084,7 +3116,7 @@ "patterns": [ { "name": "string.regexp.ts", - "begin": "(?<=[=(:,\\[?+!]|return|case|=>|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/(?![\\/*])[gimy]*(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?<=[=(:,\\[?+!]|return|case|=>|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/(?![\\/*])[gimuy]*(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "1": { "name": "punctuation.definition.string.begin.ts" @@ -3107,7 +3139,7 @@ }, { "name": "string.regexp.ts", - "begin": "(?)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n )) |\n (:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n ))\n)", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n (=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n )) |\n (:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n ))\n)", "beginCaptures": { "1": { "name": "meta.definition.variable.tsx entity.name.function.tsx" @@ -724,7 +747,7 @@ }, "import-declaration": { "name": "meta.import.tsx", - "begin": "(?)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n )) |\n (:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n ))\n)" + "match": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=(\\?\\s*)?\\s*\n (=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n )) |\n (:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n ))\n)" }, { "name": "meta.definition.property.tsx variable.object.property.tsx", @@ -1569,7 +1592,7 @@ } }, { - "match": "(?x)(?:\\s*\\b(public|private|protected|readonly)\\s+)?(\\.\\.\\.)?\\s*(?)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n )) |\n (:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n ))\n)", + "match": "(?x)(?:\\s*\\b(public|private|protected|readonly)\\s+)?(\\.\\.\\.)?\\s*(?)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n )) |\n (:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n ))\n)", "captures": { "1": { "name": "storage.modifier.tsx" @@ -2407,7 +2430,7 @@ } }, { - "match": "(?x) (\\.) \\s* (?:\n (ATTRIBUTE_NODE|CDATA_SECTION_NODE|COMMENT_NODE|DOCUMENT_FRAGMENT_NODE|DOCUMENT_NODE|DOCUMENT_TYPE_NODE\n |DOMSTRING_SIZE_ERR|ELEMENT_NODE|ENTITY_NODE|ENTITY_REFERENCE_NODE|HIERARCHY_REQUEST_ERR|INDEX_SIZE_ERR\n |INUSE_ATTRIBUTE_ERR|INVALID_CHARACTER_ERR|NO_DATA_ALLOWED_ERR|NO_MODIFICATION_ALLOWED_ERR|NOT_FOUND_ERR\n |NOT_SUPPORTED_ERR|NOTATION_NODE|PROCESSING_INSTRUCTION_NODE|TEXT_NODE|WRONG_DOCUMENT_ERR)\n |\n (_content|[xyz]|abbr|above|accept|acceptCharset|accessKey|action|align|[av]Link(?:color)?|all|alt|anchors|appCodeName\n |appCore|applets|appMinorVersion|appName|appVersion|archive|areas|arguments|attributes|availHeight|availLeft|availTop\n |availWidth|axis|background|backgroundColor|backgroundImage|below|bgColor|body|border|borderBottomWidth|borderColor\n |borderLeftWidth|borderRightWidth|borderStyle|borderTopWidth|borderWidth|bottom|bufferDepth|callee|caller|caption\n |cellPadding|cells|cellSpacing|ch|characterSet|charset|checked|childNodes|chOff|cite|classes|className|clear\n |clientInformation|clip|clipBoardData|closed|code|codeBase|codeType|color|colorDepth|cols|colSpan|compact|complete\n |components|content|controllers|cookie|cookieEnabled|cords|cpuClass|crypto|current|data|dateTime|declare|defaultCharset\n |defaultChecked|defaultSelected|defaultStatus|defaultValue|defaultView|defer|description|dialogArguments|dialogHeight\n |dialogLeft|dialogTop|dialogWidth|dir|directories|disabled|display|docmain|doctype|documentElement|elements|embeds\n |enabledPlugin|encoding|enctype|entities|event|expando|external|face|fgColor|filename|firstChild|fontFamily|fontSize\n |fontWeight|form|formName|forms|frame|frameBorder|frameElement|frames|hasFocus|hash|headers|height|history|host\n |hostname|href|hreflang|hspace|htmlFor|httpEquiv|id|ids|ignoreCase|images|implementation|index|innerHeight|innerWidth\n |input|isMap|label|lang|language|lastChild|lastIndex|lastMatch|lastModified|lastParen|layer[sXY]|left|leftContext\n |lineHeight|link|linkColor|links|listStyleType|localName|location|locationbar|longDesc|lowsrc|lowSrc|marginBottom\n |marginHeight|marginLeft|marginRight|marginTop|marginWidth|maxLength|media|menubar|method|mimeTypes|multiline|multiple\n |name|nameProp|namespaces|namespaceURI|next|nextSibling|nodeName|nodeType|nodeValue|noHref|noResize|noShade|notationName\n |notations|noWrap|object|offscreenBuffering|onLine|onreadystatechange|opener|opsProfile|options|oscpu|outerHeight\n |outerWidth|ownerDocument|paddingBottom|paddingLeft|paddingRight|paddingTop|page[XY]|page[XY]Offset|parent|parentLayer\n |parentNode|parentWindow|pathname|personalbar|pixelDepth|pkcs11|platform|plugins|port|prefix|previous|previousDibling\n |product|productSub|profile|profileend|prompt|prompter|protocol|publicId|readOnly|readyState|referrer|rel|responseText\n |responseXML|rev|right|rightContext|rowIndex|rows|rowSpan|rules|scheme|scope|screen[XY]|screenLeft|screenTop|scripts\n |scrollbars|scrolling|sectionRowIndex|security|securityPolicy|selected|selectedIndex|selection|self|shape|siblingAbove\n |siblingBelow|size|source|specified|standby|start|status|statusbar|statusText|style|styleSheets|suffixes|summary\n |systemId|systemLanguage|tagName|tags|target|tBodies|text|textAlign|textDecoration|textIndent|textTransform|tFoot|tHead\n |title|toolbar|top|type|undefined|uniqueID|updateInterval|URL|URLUnencoded|useMap|userAgent|userLanguage|userProfile\n |vAlign|value|valueType|vendor|vendorSub|version|visibility|vspace|whiteSpace|width|X[MS]LDocument|zIndex))\\b(?!\\$|\\s*(<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "match": "(?x) (\\.) \\s* (?:\n (ATTRIBUTE_NODE|CDATA_SECTION_NODE|COMMENT_NODE|DOCUMENT_FRAGMENT_NODE|DOCUMENT_NODE|DOCUMENT_TYPE_NODE\n |DOMSTRING_SIZE_ERR|ELEMENT_NODE|ENTITY_NODE|ENTITY_REFERENCE_NODE|HIERARCHY_REQUEST_ERR|INDEX_SIZE_ERR\n |INUSE_ATTRIBUTE_ERR|INVALID_CHARACTER_ERR|NO_DATA_ALLOWED_ERR|NO_MODIFICATION_ALLOWED_ERR|NOT_FOUND_ERR\n |NOT_SUPPORTED_ERR|NOTATION_NODE|PROCESSING_INSTRUCTION_NODE|TEXT_NODE|WRONG_DOCUMENT_ERR)\n |\n (_content|[xyz]|abbr|above|accept|acceptCharset|accessKey|action|align|[av]Link(?:color)?|all|alt|anchors|appCodeName\n |appCore|applets|appMinorVersion|appName|appVersion|archive|areas|arguments|attributes|availHeight|availLeft|availTop\n |availWidth|axis|background|backgroundColor|backgroundImage|below|bgColor|body|border|borderBottomWidth|borderColor\n |borderLeftWidth|borderRightWidth|borderStyle|borderTopWidth|borderWidth|bottom|bufferDepth|callee|caller|caption\n |cellPadding|cells|cellSpacing|ch|characterSet|charset|checked|childNodes|chOff|cite|classes|className|clear\n |clientInformation|clip|clipBoardData|closed|code|codeBase|codeType|color|colorDepth|cols|colSpan|compact|complete\n |components|content|controllers|cookie|cookieEnabled|cords|cpuClass|crypto|current|data|dateTime|declare|defaultCharset\n |defaultChecked|defaultSelected|defaultStatus|defaultValue|defaultView|defer|description|dialogArguments|dialogHeight\n |dialogLeft|dialogTop|dialogWidth|dir|directories|disabled|display|docmain|doctype|documentElement|elements|embeds\n |enabledPlugin|encoding|enctype|entities|event|expando|external|face|fgColor|filename|firstChild|fontFamily|fontSize\n |fontWeight|form|formName|forms|frame|frameBorder|frameElement|frames|hasFocus|hash|headers|height|history|host\n |hostname|href|hreflang|hspace|htmlFor|httpEquiv|id|ids|ignoreCase|images|implementation|index|innerHeight|innerWidth\n |input|isMap|label|lang|language|lastChild|lastIndex|lastMatch|lastModified|lastParen|layer[sXY]|left|leftContext\n |lineHeight|link|linkColor|links|listStyleType|localName|location|locationbar|longDesc|lowsrc|lowSrc|marginBottom\n |marginHeight|marginLeft|marginRight|marginTop|marginWidth|maxLength|media|menubar|method|mimeTypes|multiline|multiple\n |name|nameProp|namespaces|namespaceURI|next|nextSibling|nodeName|nodeType|nodeValue|noHref|noResize|noShade|notationName\n |notations|noWrap|object|offscreenBuffering|onLine|onreadystatechange|opener|opsProfile|options|oscpu|outerHeight\n |outerWidth|ownerDocument|paddingBottom|paddingLeft|paddingRight|paddingTop|page[XY]|page[XY]Offset|parent|parentLayer\n |parentNode|parentWindow|pathname|personalbar|pixelDepth|pkcs11|platform|plugins|port|prefix|previous|previousDibling\n |product|productSub|profile|profileend|prompt|prompter|protocol|publicId|readOnly|readyState|referrer|rel|responseText\n |responseXML|rev|right|rightContext|rowIndex|rows|rowSpan|rules|scheme|scope|screen[XY]|screenLeft|screenTop|scripts\n |scrollbars|scrolling|sectionRowIndex|security|securityPolicy|selected|selectedIndex|selection|self|shape|siblingAbove\n |siblingBelow|size|source|specified|standby|start|status|statusbar|statusText|style|styleSheets|suffixes|summary\n |systemId|systemLanguage|tagName|tags|target|tBodies|text|textAlign|textDecoration|textIndent|textTransform|tFoot|tHead\n |title|toolbar|top|type|undefined|uniqueID|updateInterval|URL|URLUnencoded|useMap|userAgent|userLanguage|userProfile\n |vAlign|value|valueType|vendor|vendorSub|version|visibility|vspace|whiteSpace|width|X[MS]LDocument|zIndex))\\b(?!\\$|\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", "captures": { "1": { "name": "punctuation.accessor.tsx" @@ -2482,13 +2505,13 @@ ] }, "function-call": { - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)?\\()", - "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", "patterns": [ { "name": "meta.function-call.tsx", "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "end": "(?=\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", "patterns": [ { "include": "#literal" @@ -2549,7 +2572,7 @@ "include": "#object-identifiers" }, { - "match": "(?x)(?:(\\.)\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n))", + "match": "(?x)(?:(\\.)\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n))", "captures": { "1": { "name": "punctuation.accessor.tsx" @@ -2694,7 +2717,7 @@ }, { "name": "meta.object.member.tsx", - "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=:\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n)))", + "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=:\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([(]\\s*(([)]\\s*:)|([_$[:alpha:]][_$[:alnum:]]*\\s*:)|(\\.\\.\\.) )) |\n ([<]\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s*[^=>])|(\\s*[,]))) |\n ((<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\(([^()]|\\([^()]*\\))*\\)(\\s*:\\s*(.)*)?\\s*=>)\n ))\n)))", "captures": { "0": { "name": "meta.object-literal.key.tsx" @@ -2711,8 +2734,7 @@ "0": { "name": "meta.object-literal.key.tsx" } - }, - "end": "(?=,|\\})" + } }, { "name": "meta.object.member.tsx", @@ -2738,6 +2760,16 @@ } } }, + { + "name": "meta.object.member.tsx", + "begin": "(?=[_$[:alpha:]][_$[:alnum:]]*\\s*=)", + "end": "(?=,|\\}|$)", + "patterns": [ + { + "include": "#expression" + } + ] + }, { "include": "#object-member-body" }, @@ -2812,7 +2844,7 @@ "name": "keyword.control.as.tsx" } }, - "end": "(?=$|^|[;,:})\\]])", + "end": "(?=$|^|[;,:})\\]]|((? is on new line\n (\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n ) |\n (\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends \n ) |\n # arrow function possible to detect only with => on same line\n (\n (<([^<>=]|=[^<]|\\<([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\(([^()]|\\([^()]*\\))*\\) # parameteres\n (\\s*:\\s*(.)*)? # return type\n \\s*=> # arrow operator\n )\n )\n)", + "begin": "(?x) (?:\n (? is on new line\n (\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n ) |\n (\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends \n ) |\n # arrow function possible to detect only with => on same line\n (\n (<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\(([^()]|\\([^()]*\\))*\\) # parameteres\n (\\s*:\\s*(.)*)? # return type\n \\s*=> # arrow operator\n )\n )\n)", "beginCaptures": { "1": { "name": "storage.modifier.async.tsx" @@ -3050,7 +3082,7 @@ "patterns": [ { "name": "string.regexp.tsx", - "begin": "(?<=[=(:,\\[?+!]|return|case|=>|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/(?![\\/*])[gimy]*(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?<=[=(:,\\[?+!]|return|case|=>|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/(?![\\/*])[gimuy]*(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "1": { "name": "punctuation.definition.string.begin.tsx" @@ -3073,7 +3105,7 @@ }, { "name": "string.regexp.tsx", - "begin": "(? implements IDisposable { if (isSelectionRangeChangeEvent(e) && reference !== undefined) { const min = Math.min(reference, focus); const max = Math.max(reference, focus); - const rangeSelection = range(max + 1, min); + const rangeSelection = range(min, max + 1); const selection = this.list.getSelection(); const contiguousRange = getContiguousRangeContaining(disjunction(selection, [reference]), reference); diff --git a/src/vs/base/browser/ui/sash/sash.ts b/src/vs/base/browser/ui/sash/sash.ts index 338d07b4681..4fdafd509a4 100644 --- a/src/vs/base/browser/ui/sash/sash.ts +++ b/src/vs/base/browser/ui/sash/sash.ts @@ -268,6 +268,10 @@ export class Sash extends EventEmitter { this.isDisabled = true; } + get enabled(): boolean { + return !this.isDisabled; + } + public dispose(): void { if (this.$e) { this.$e.destroy(); diff --git a/src/vs/base/browser/ui/splitview/panelview.ts b/src/vs/base/browser/ui/splitview/panelview.ts new file mode 100644 index 00000000000..94ed8a2e53e --- /dev/null +++ b/src/vs/base/browser/ui/splitview/panelview.ts @@ -0,0 +1,361 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./splitview'; +import { IDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle'; +import Event, { Emitter, chain } from 'vs/base/common/event'; +import { domEvent } from 'vs/base/browser/event'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { $, append, addClass, removeClass, toggleClass } from 'vs/base/browser/dom'; +import { firstIndex } from 'vs/base/common/arrays'; +import { Color, RGBA } from 'vs/base/common/color'; +import { SplitView, IView } from './splitview2'; + +export interface IPanelOptions { + ariaHeaderLabel?: string; + minimumBodySize?: number; + maximumBodySize?: number; + expanded?: boolean; +} + +export interface IPanelStyles { + dropBackground?: Color; +} + +export abstract class Panel implements IView { + + private static HEADER_SIZE = 22; + + private _expanded: boolean; + private _headerVisible: boolean; + private _onDidChange = new Emitter(); + private _minimumBodySize: number; + private _maximumBodySize: number; + private ariaHeaderLabel: string; + + readonly header: HTMLElement; + protected disposables: IDisposable[] = []; + + get minimumBodySize(): number { + return this._minimumBodySize; + } + + set minimumBodySize(size: number) { + this._minimumBodySize = size; + this._onDidChange.fire(); + } + + get maximumBodySize(): number { + return this._maximumBodySize; + } + + set maximumBodySize(size: number) { + this._maximumBodySize = size; + this._onDidChange.fire(); + } + + get minimumSize(): number { + const headerSize = this.headerVisible ? Panel.HEADER_SIZE : 0; + const expanded = !this.headerVisible || this.expanded; + const minimumBodySize = expanded ? this._minimumBodySize : 0; + + return headerSize + minimumBodySize; + } + + get maximumSize(): number { + const headerSize = this.headerVisible ? Panel.HEADER_SIZE : 0; + const expanded = !this.headerVisible || this.expanded; + const maximumBodySize = expanded ? this._maximumBodySize : 0; + + return headerSize + maximumBodySize; + } + + readonly onDidChange: Event = this._onDidChange.event; + + constructor(options: IPanelOptions = {}) { + this._expanded = typeof options.expanded === 'undefined' ? true : !!options.expanded; + this.ariaHeaderLabel = options.ariaHeaderLabel || ''; + this._minimumBodySize = typeof options.minimumBodySize === 'number' ? options.minimumBodySize : 44; + this._maximumBodySize = typeof options.maximumBodySize === 'number' ? options.maximumBodySize : Number.POSITIVE_INFINITY; + } + + get expanded(): boolean { + return this._expanded; + } + + set expanded(expanded: boolean) { + if (this._expanded === !!expanded) { + return; + } + + this._expanded = !!expanded; + this.renderHeader(); + this._onDidChange.fire(); + } + + get headerVisible(): boolean { + return this._headerVisible; + } + + set headerVisible(visible: boolean) { + if (this._headerVisible === !!visible) { + return; + } + + this._headerVisible = !!visible; + this.renderHeader(); + this._onDidChange.fire(); + } + + render(container: HTMLElement): void { + const panel = append(container, $('.panel')); + const header = append(panel, $('.panel-header')); + + header.setAttribute('tabindex', '0'); + header.setAttribute('role', 'toolbar'); + header.setAttribute('aria-label', this.ariaHeaderLabel); + this.renderHeader(); + + const onHeaderKeyDown = chain(domEvent(header, 'keydown')) + .map(e => new StandardKeyboardEvent(e)); + + onHeaderKeyDown.filter(e => e.keyCode === KeyCode.Enter || e.keyCode === KeyCode.Space) + .event(() => this.expanded = !this.expanded, null, this.disposables); + + onHeaderKeyDown.filter(e => e.keyCode === KeyCode.LeftArrow) + .event(() => this.expanded = false, null, this.disposables); + + onHeaderKeyDown.filter(e => e.keyCode === KeyCode.RightArrow) + .event(() => this.expanded = true, null, this.disposables); + + domEvent(header, 'click') + (() => this.expanded = !this.expanded, null, this.disposables); + + // TODO@Joao move this down to panelview + // onHeaderKeyDown.filter(e => e.keyCode === KeyCode.UpArrow) + // .event(focusPrevious, this, this.disposables); + + // onHeaderKeyDown.filter(e => e.keyCode === KeyCode.DownArrow) + // .event(focusNext, this, this.disposables); + + const body = append(panel, $('.panel-body')); + this.renderBody(body); + } + + layout(size: number): void { + const headerSize = this.headerVisible ? Panel.HEADER_SIZE : 0; + this.layoutBody(size - headerSize); + } + + focus(): void { + // TODO@joao what to do + } + + private renderHeader(): void { + const expanded = !this.headerVisible || this.expanded; + + toggleClass(this.header, 'hidden', !this.headerVisible); + toggleClass(this.header, 'expanded', expanded); + this.header.setAttribute('aria-expanded', String(expanded)); + } + + protected abstract renderBody(container: HTMLElement): void; + protected abstract layoutBody(size: number): void; + + dispose(): void { + this.disposables = dispose(this.disposables); + } +} + +interface IDndContext { + dropBackground: Color | undefined; + draggable: PanelDraggable | null; +} + +class PanelDraggable implements IDisposable { + + private static DefaultDragOverBackgroundColor = new Color(new RGBA(128, 128, 128, 0.5)); + + // see https://github.com/Microsoft/vscode/issues/14470 + private dragOverCounter = 0; + private disposables: IDisposable[] = []; + + private _onDidDrop = new Emitter<{ from: Panel, to: Panel }>(); + readonly onDidDrop = this._onDidDrop.event; + + constructor(private panel: Panel, private context: IDndContext) { + domEvent(panel.header, 'dragstart')(this.onDragStart, this, this.disposables); + domEvent(panel.header, 'dragenter')(this.onDragEnter, this, this.disposables); + domEvent(panel.header, 'dragleave')(this.onDragLeave, this, this.disposables); + domEvent(panel.header, 'dragend')(this.onDragEnd, this, this.disposables); + domEvent(panel.header, 'drop')(this.onDrop, this, this.disposables); + } + + private onDragStart(e: DragEvent): void { + e.dataTransfer.effectAllowed = 'move'; + + const dragImage = append(document.body, $('.monaco-panel-drag-image', {}, this.panel.header.textContent)); + e.dataTransfer.setDragImage(dragImage, -10, -10); + setTimeout(() => document.body.removeChild(dragImage), 0); + + this.context.draggable = this; + } + + private onDragEnter(e: DragEvent): void { + if (!this.context.draggable || this.context.draggable === this) { + return; + } + + this.dragOverCounter++; + this.renderHeader(); + } + + private onDragLeave(e: DragEvent): void { + if (!this.context.draggable || this.context.draggable === this) { + return; + } + + this.dragOverCounter--; + + if (this.dragOverCounter === 0) { + this.renderHeader(); + } + } + + private onDragEnd(e: DragEvent): void { + if (!this.context.draggable) { + return; + } + + this.dragOverCounter = 0; + this.renderHeader(); + this.context.draggable = null; + } + + private onDrop(e: DragEvent): void { + if (!this.context.draggable) { + return; + } + + this.dragOverCounter = 0; + this.renderHeader(); + + if (this.context.draggable !== this) { + this._onDidDrop.fire({ from: this.context.draggable.panel, to: this.panel }); + } + + this.context.draggable = null; + } + + private renderHeader(): void { + let backgroundColor: string = null; + + if (this.dragOverCounter > 0) { + backgroundColor = (this.context.dropBackground || PanelDraggable.DefaultDragOverBackgroundColor).toString(); + } + + this.panel.header.style.backgroundColor = backgroundColor; + } + + dispose(): void { + this.disposables = dispose(this.disposables); + } +} + +export class IPanelViewOptions { + dnd?: boolean; +} + +interface IPanelItem { + panel: Panel; + disposable: IDisposable; +} + +export class PanelView implements IDisposable { + + private dnd: boolean; + private dndContext: IDndContext = { dropBackground: undefined, draggable: null }; + private el: HTMLElement; + private panelItems: IPanelItem[] = []; + private splitview: SplitView; + private animationTimer: number | null = null; + + private _onDidDrop = new Emitter<{ from: Panel, to: Panel }>(); + readonly onDidDrop: Event<{ from: Panel, to: Panel }> = this._onDidDrop.event; + + constructor(private container: HTMLElement, options?: IPanelViewOptions) { + this.dnd = !!options.dnd; + this.el = append(container, $('.monaco-panel-view')); + this.splitview = new SplitView(container); + } + + addPanel(panel: Panel, size: number, index = this.splitview.length): void { + const disposables: IDisposable[] = []; + panel.onDidChange(this.setupAnimation, this, disposables); + + if (this.dnd) { + const draggable = new PanelDraggable(panel, this.dndContext); + disposables.push(draggable); + draggable.onDidDrop(this._onDidDrop.fire, this._onDidDrop, disposables); + } + + const panelItem = { panel, disposable: combinedDisposable(disposables) }; + + this.panelItems.splice(index, 0, panelItem); + this.splitview.addView(panel, size, index); + } + + removePanel(panel: Panel): void { + const index = firstIndex(this.panelItems, item => item.panel === panel); + + if (index === -1) { + return; + } + + this.splitview.removeView(index); + const panelItem = this.panelItems.splice(index, 1)[0]; + panelItem.disposable.dispose(); + } + + movePanel(from: Panel, to: Panel): void { + const fromIndex = firstIndex(this.panelItems, item => item.panel === from); + const toIndex = firstIndex(this.panelItems, item => item.panel === to); + + if (fromIndex === -1 || toIndex === -1) { + return; + } + + this.splitview.moveView(fromIndex, toIndex); + } + + layout(size: number): void { + this.splitview.layout(size); + } + + style(styles: IPanelStyles): void { + this.dndContext.dropBackground = styles.dropBackground; + } + + private setupAnimation(): void { + if (typeof this.animationTimer === 'number') { + window.clearTimeout(this.animationTimer); + } + + addClass(this.el, 'animated'); + + this.animationTimer = window.setTimeout(() => { + this.animationTimer = null; + removeClass(this.el, 'animated'); + }, 200); + } + + dispose(): void { + this.panelItems.forEach(i => i.disposable.dispose()); + this.splitview.dispose(); + } +} diff --git a/src/vs/base/browser/ui/splitview/splitview2.ts b/src/vs/base/browser/ui/splitview/splitview2.ts new file mode 100644 index 00000000000..dceae97209f --- /dev/null +++ b/src/vs/base/browser/ui/splitview/splitview2.ts @@ -0,0 +1,293 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./splitview'; +import { IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import Event, { fromEventEmitter, mapEvent } from 'vs/base/common/event'; +import types = require('vs/base/common/types'); +import dom = require('vs/base/browser/dom'); +import { clamp } from 'vs/base/common/numbers'; +import { range, firstIndex } from 'vs/base/common/arrays'; +import { Sash, Orientation, ISashEvent as IBaseSashEvent } from 'vs/base/browser/ui/sash/sash'; +export { Orientation } from 'vs/base/browser/ui/sash/sash'; + +export interface ISplitViewOptions { + orientation?: Orientation; // default Orientation.VERTICAL +} + +export interface IView { + readonly minimumSize: number; + readonly maximumSize: number; + readonly onDidChange: Event; + render(container: HTMLElement, orientation: Orientation): void; + layout(size: number, orientation: Orientation): void; + focus(): void; +} + +interface ISashEvent { + sash: Sash; + start: number; + current: number; +} + +interface IViewItem { + view: IView; + size: number; + explicitSize: number; + container: HTMLElement; + disposable: IDisposable; +} + +interface ISashItem { + sash: Sash; + disposable: IDisposable; +} + +interface ISashDragState { + index: number; + start: number; + sizes: number[]; +} + +function layoutViewItem(item: IViewItem, orientation: Orientation): void { + if (orientation === Orientation.VERTICAL) { + item.container.style.height = `${item.size}px`; + } else { + item.container.style.width = `${item.size}px`; + } + + item.view.layout(item.size, orientation); +} + +export class SplitView implements IDisposable { + + private orientation: Orientation; + private el: HTMLElement; + private size = 0; + private viewItems: IViewItem[] = []; + private sashItems: ISashItem[] = []; + private sashDragState: ISashDragState; + + get length(): number { + return this.viewItems.length; + } + + constructor(private container: HTMLElement, options: ISplitViewOptions = {}) { + this.orientation = types.isUndefined(options.orientation) ? Orientation.VERTICAL : options.orientation; + + this.el = document.createElement('div'); + dom.addClass(this.el, 'monaco-split-view'); + dom.addClass(this.el, this.orientation === Orientation.VERTICAL ? 'vertical' : 'horizontal'); + container.appendChild(this.el); + } + + addView(view: IView, size: number, index = this.viewItems.length): void { + // Add view + const container = dom.$('.split-view-view'); + + if (this.viewItems.length === 1) { + this.el.appendChild(container); + } else { + this.el.insertBefore(container, this.el.children.item(index)); + } + + const onChangeDisposable = mapEvent(view.onDidChange, () => item)(this.onViewChange, this); + const containerDisposable = toDisposable(() => this.el.removeChild(container)); + const disposable = combinedDisposable([onChangeDisposable, containerDisposable]); + + const explicitSize = size; + const item: IViewItem = { view, container, explicitSize, size, disposable }; + this.viewItems.splice(index, 0, item); + + // Add sash + if (this.viewItems.length > 1) { + const orientation = this.orientation === Orientation.VERTICAL ? Orientation.HORIZONTAL : Orientation.VERTICAL; + const layoutProvider = this.orientation === Orientation.VERTICAL ? { getHorizontalSashTop: sash => this.getSashPosition(sash) } : { getVerticalSashLeft: sash => this.getSashPosition(sash) }; + const sash = new Sash(this.el, layoutProvider, { orientation }); + const sashEventMapper = this.orientation === Orientation.VERTICAL + ? (e: IBaseSashEvent) => ({ sash, start: e.startY, current: e.currentY }) + : (e: IBaseSashEvent) => ({ sash, start: e.startX, current: e.currentX }); + + const onStart = mapEvent(fromEventEmitter(sash, 'start'), sashEventMapper); + const onStartDisposable = onStart(this.onSashStart, this); + const onChange = mapEvent(fromEventEmitter(sash, 'change'), sashEventMapper); + const onSashChangeDisposable = onChange(this.onSashChange, this); + const disposable = combinedDisposable([onStartDisposable, onSashChangeDisposable, sash]); + const sashItem: ISashItem = { sash, disposable }; + + this.sashItems.splice(index - 1, 0, sashItem); + } + + view.render(container, this.orientation); + this.relayoutPreferredSizes(); + } + + removeView(index: number): void { + if (index < 0 || index >= this.viewItems.length) { + return; + } + + // Remove view + const viewItem = this.viewItems.splice(index, 1)[0]; + viewItem.disposable.dispose(); + + // Remove sash + if (this.viewItems.length >= 1) { + const sashIndex = Math.max(index - 1, 0); + const sashItem = this.sashItems.splice(sashIndex, 1)[0]; + sashItem.disposable.dispose(); + } + + this.relayoutPreferredSizes(); + } + + moveView(from: number, to: number): void { + if (from < 0 || from >= this.viewItems.length) { + return; + } + + if (to < 0 || to >= this.viewItems.length) { + return; + } + + if (from === to) { + return; + } + + const viewItem = this.viewItems.splice(from, 1)[0]; + this.viewItems.splice(to, 0, viewItem); + this.layoutViews(); + } + + private relayoutPreferredSizes(): void { + this.viewItems.forEach(i => i.size = clamp(i.explicitSize, i.view.minimumSize, i.view.maximumSize)); + this.relayout(); + } + + private relayout(): void { + const previousSize = this.size; + this.size = this.viewItems.reduce((r, i) => r + i.size, 0); + this.layout(previousSize); + } + + layout(size: number): void { + this.resize(this.viewItems.length - 1, size - this.size); + this.size = Math.max(size, this.viewItems.reduce((r, i) => r + i.size, 0)); + } + + private onSashStart({ sash, start }: ISashEvent): void { + const index = firstIndex(this.sashItems, item => item.sash === sash); + const sizes = this.viewItems.map(i => i.size); + + this.sashDragState = { start, index, sizes }; + } + + private onSashChange({ sash, current }: ISashEvent): void { + const { index, start, sizes } = this.sashDragState; + + this.resize(index, current - start, sizes); + this.viewItems.forEach(viewItem => viewItem.explicitSize = viewItem.size); + } + + private onViewChange(item: IViewItem): void { + item.size = clamp(item.size, item.view.minimumSize, item.view.maximumSize); + this.relayout(); + } + + resizeView(index: number, size: number): void { + if (index < 0 || index >= this.viewItems.length - 1) { + throw new Error('Cant resize view'); + } + + this.resize(index, size - this.viewItems[index].size); + } + + private resize(index: number, delta: number, sizes = this.viewItems.map(i => i.size)): void { + if (index < 0 || index >= this.viewItems.length) { + return; + } + + if (delta !== 0) { + const upIndexes = range(index, -1); + const up = upIndexes.map(i => this.viewItems[i]); + const upSizes = upIndexes.map(i => sizes[i]); + + const downIndexes = range(index + 1, this.viewItems.length); + const down = downIndexes.map(i => this.viewItems[i]); + const downSizes = downIndexes.map(i => sizes[i]); + + for (let i = 0, deltaUp = delta; deltaUp !== 0 && i < up.length; i++) { + const item = up[i]; + const size = clamp(upSizes[i] + deltaUp, item.view.minimumSize, item.view.maximumSize); + const viewDelta = size - upSizes[i]; + + deltaUp -= viewDelta; + item.size = size; + } + + for (let i = 0, deltaDown = delta; deltaDown !== 0 && i < down.length; i++) { + const item = down[i]; + const size = clamp(downSizes[i] - deltaDown, item.view.minimumSize, item.view.maximumSize); + const viewDelta = size - downSizes[i]; + + deltaDown += viewDelta; + item.size = size; + } + } + + this.layoutViews(); + } + + private layoutViews(): void { + this.viewItems.forEach(item => layoutViewItem(item, this.orientation)); + this.sashItems.forEach(item => item.sash.layout()); + + // Update sashes enablement + let previous = false; + const collapsesDown = this.viewItems.map(i => previous = (i.size - i.view.minimumSize > 0) || previous); + + previous = false; + const expandsDown = this.viewItems.map(i => previous = (i.view.maximumSize - i.size > 0) || previous); + + const reverseViews = [...this.viewItems].reverse(); + previous = false; + const collapsesUp = reverseViews.map(i => previous = (i.size - i.view.minimumSize > 0) || previous).reverse(); + + previous = false; + const expandsUp = reverseViews.map(i => previous = (i.view.maximumSize - i.size > 0) || previous).reverse(); + + this.sashItems.forEach((s, i) => { + if ((collapsesDown[i] && expandsUp[i + 1]) || (expandsDown[i] && collapsesUp[i + 1])) { + s.sash.enable(); + } else { + s.sash.disable(); + } + }); + } + + private getSashPosition(sash: Sash): number { + let position = 0; + + for (let i = 0; i < this.sashItems.length; i++) { + position += this.viewItems[i].size; + + if (this.sashItems[i].sash === sash) { + return position; + } + } + + throw new Error('Sash not found'); + } + + dispose(): void { + this.viewItems.forEach(i => i.disposable.dispose()); + this.viewItems = []; + + this.sashItems.forEach(i => i.disposable.dispose()); + this.sashItems = []; + } +} diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index 4be8d60f396..a64b84276eb 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -325,11 +325,43 @@ export function flatten(arr: T[][]): T[] { return arr.reduce((r, v) => r.concat(v), []); } -export function range(to: number, from = 0): number[] { +export function range(to: number): number[]; +export function range(from: number, to: number): number[]; +export function range(arg: number, to?: number): number[] { + let from = typeof to === 'number' ? arg : 0; + + if (typeof to === 'number') { + from = arg; + } else { + from = 0; + to = arg; + } + const result: number[] = []; - for (let i = from; i < to; i++) { - result.push(i); + if (from <= to) { + for (let i = from; i < to; i++) { + result.push(i); + } + } else { + for (let i = from; i > to; i--) { + result.push(i); + } + } + + return result; +} + +export function weave(a: T[], b: T[]): T[] { + const result: T[] = []; + let ai = 0, bi = 0; + + for (let i = 0, length = a.length + b.length; i < length; i++) { + if ((i % 2 === 0 && ai < a.length) || bi >= b.length) { + result.push(a[ai++]); + } else { + result.push(b[bi++]); + } } return result; diff --git a/src/vs/base/common/jsonSchema.ts b/src/vs/base/common/jsonSchema.ts index 1405eee19a5..44496dbcfb1 100644 --- a/src/vs/base/common/jsonSchema.ts +++ b/src/vs/base/common/jsonSchema.ts @@ -45,6 +45,7 @@ export interface IJSONSchema { patternErrorMessage?: string; // VSCode extension deprecationMessage?: string; // VSCode extension enumDescriptions?: string[]; // VSCode extension + doNotSuggest?: boolean; // VSCode extension } export interface IJSONSchemaMap { diff --git a/src/vs/base/common/labels.ts b/src/vs/base/common/labels.ts index d86e3f668e2..6192b7dcd01 100644 --- a/src/vs/base/common/labels.ts +++ b/src/vs/base/common/labels.ts @@ -17,10 +17,10 @@ export interface ILabelProvider { getLabel(element: any): string; } -export interface IRootProvider { - getRoot(resource: URI): URI; +export interface IWorkspaceFolderProvider { + getWorkspaceFolder(resource: URI): URI; getWorkspace(): { - roots: URI[]; + folders: URI[]; }; } @@ -28,7 +28,7 @@ export interface IUserHomeProvider { userHome: string; } -export function getPathLabel(resource: URI | string, rootProvider?: IRootProvider, userHomeProvider?: IUserHomeProvider): string { +export function getPathLabel(resource: URI | string, rootProvider?: IWorkspaceFolderProvider, userHomeProvider?: IUserHomeProvider): string { if (!resource) { return null; } @@ -38,9 +38,9 @@ export function getPathLabel(resource: URI | string, rootProvider?: IRootProvide } // return early if we can resolve a relative path label from the root - const baseResource = rootProvider ? rootProvider.getRoot(resource) : null; + const baseResource = rootProvider ? rootProvider.getWorkspaceFolder(resource) : null; if (baseResource) { - const hasMultipleRoots = rootProvider.getWorkspace().roots.length > 1; + const hasMultipleRoots = rootProvider.getWorkspace().folders.length > 1; let pathLabel: string; if (isEqual(baseResource.fsPath, resource.fsPath, !platform.isLinux /* ignorecase */)) { diff --git a/src/vs/base/common/numbers.ts b/src/vs/base/common/numbers.ts index 65bcbbd7922..9f804fe6dd9 100644 --- a/src/vs/base/common/numbers.ts +++ b/src/vs/base/common/numbers.ts @@ -46,3 +46,8 @@ export function countToArray(fromOrTo: number, to?: number): number[] { return result; } + + +export function clamp(value: number, min: number, max: number): number { + return Math.min(Math.max(value, min), max); +} \ No newline at end of file diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index 7af18aadcb6..2200f4e5721 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -238,9 +238,19 @@ export function regExpLeadsToEndlessLoop(regexp: RegExp): boolean { * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize} */ export const canNormalize = typeof (('').normalize) === 'function'; -const nonAsciiCharactersPattern = /[^\u0000-\u0080]/; -const normalizedCache = new BoundedMap(10000); // bounded to 10000 elements + +const nfcCache = new BoundedMap(10000); // bounded to 10000 elements export function normalizeNFC(str: string): string { + return normalize(str, 'NFC', nfcCache); +} + +const nfdCache = new BoundedMap(10000); // bounded to 10000 elements +export function normalizeNFD(str: string): string { + return normalize(str, 'NFD', nfdCache); +} + +const nonAsciiCharactersPattern = /[^\u0000-\u0080]/; +function normalize(str: string, form: string, normalizedCache: BoundedMap): string { if (!canNormalize || !str) { return str; } @@ -252,7 +262,7 @@ export function normalizeNFC(str: string): string { let res: string; if (nonAsciiCharactersPattern.test(str)) { - res = (str).normalize('NFC'); + res = (str).normalize(form); } else { res = str; } diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index 043e9be8be9..39ad9e80805 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -301,8 +301,20 @@ export default class URI { parts.push('//'); } if (authority) { + let idx = authority.indexOf('@'); + if (idx !== -1) { + const userinfo = authority.substr(0, idx); + authority = authority.substr(idx + 1); + idx = userinfo.indexOf(':'); + if (idx === -1) { + parts.push(encoder(userinfo)); + } else { + parts.push(encoder(userinfo.substr(0, idx)), ':', encoder(userinfo.substr(idx + 1))); + } + parts.push('@'); + } authority = authority.toLowerCase(); - let idx = authority.indexOf(':'); + idx = authority.indexOf(':'); if (idx === -1) { parts.push(encoder(authority)); } else { diff --git a/src/vs/base/node/config.ts b/src/vs/base/node/config.ts index 6d30e650d6e..4ec9fede36f 100644 --- a/src/vs/base/node/config.ts +++ b/src/vs/base/node/config.ts @@ -6,7 +6,7 @@ 'use strict'; import * as fs from 'fs'; -import * as path from 'path'; +import { dirname, basename } from 'path'; import * as objects from 'vs/base/common/objects'; import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import Event, { Emitter } from 'vs/base/common/event'; @@ -49,9 +49,11 @@ export class ConfigWatcher implements IConfigWatcher, IDisposable { private timeoutHandle: NodeJS.Timer; private disposables: IDisposable[]; private _onDidUpdateConfiguration: Emitter>; + private configName: string; constructor(private _path: string, private options: IConfigOptions = { changeBufferDelay: 0, defaultConfig: Object.create(null), onError: error => console.error(error) }) { this.disposables = []; + this.configName = basename(this._path); this._onDidUpdateConfiguration = new Emitter>(); this.disposables.push(this._onDidUpdateConfiguration); @@ -121,8 +123,8 @@ export class ConfigWatcher implements IConfigWatcher, IDisposable { private registerWatcher(): void { // Watch the parent of the path so that we detect ADD and DELETES - const parentFolder = path.dirname(this._path); - this.watch(parentFolder); + const parentFolder = dirname(this._path); + this.watch(parentFolder, true); // Check if the path is a symlink and watch its target if so fs.lstat(this._path, (err, stat) => { @@ -137,20 +139,20 @@ export class ConfigWatcher implements IConfigWatcher, IDisposable { return; // path is not a valid symlink } - this.watch(realPath); + this.watch(realPath, false); }); } }); } - private watch(path: string): void { + private watch(path: string, isParentFolder: boolean): void { if (this.disposed) { return; // avoid watchers that will never get disposed by checking for being disposed } try { const watcher = fs.watch(path); - watcher.on('change', () => this.onConfigFileChange()); + watcher.on('change', (type, file) => this.onConfigFileChange(type, file.toString(), isParentFolder)); watcher.on('error', (code, signal) => this.options.onError(`Error watching ${path} for configuration changes (${code}, ${signal})`)); this.disposables.push(toDisposable(() => { @@ -166,7 +168,11 @@ export class ConfigWatcher implements IConfigWatcher, IDisposable { } } - private onConfigFileChange(): void { + private onConfigFileChange(eventType: string, filename: string, isParentFolder: boolean): void { + if (isParentFolder && filename !== this.configName) { + return; // a change to a sibling file that is not our config file + } + if (this.timeoutHandle) { global.clearTimeout(this.timeoutHandle); this.timeoutHandle = null; diff --git a/src/vs/base/node/pfs.ts b/src/vs/base/node/pfs.ts index 16f8d7509be..ce1f52d799c 100644 --- a/src/vs/base/node/pfs.ts +++ b/src/vs/base/node/pfs.ts @@ -189,3 +189,18 @@ const tmpDir = os.tmpdir(); export function del(path: string, tmp = tmpDir): TPromise { return nfcall(extfs.del, path, tmp); } + +export function whenDeleted(path: string): TPromise { + + // Complete when wait marker file is deleted + return new TPromise(c => { + const interval = setInterval(() => { + fs.exists(path, exists => { + if (!exists) { + clearInterval(interval); + c(null); + } + }); + }, 1000); + }); +} \ No newline at end of file diff --git a/src/vs/base/test/browser/ui/splitview/splitview.test.ts b/src/vs/base/test/browser/ui/splitview/splitview.test.ts new file mode 100644 index 00000000000..be73ee96d46 --- /dev/null +++ b/src/vs/base/test/browser/ui/splitview/splitview.test.ts @@ -0,0 +1,338 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { Emitter } from 'vs/base/common/event'; +import { SplitView, IView, Orientation } from 'vs/base/browser/ui/splitview/splitview2'; +import { Sash } from 'vs/base/browser/ui/sash/sash'; + +class TestView implements IView { + + private _onDidChange = new Emitter(); + readonly onDidChange = this._onDidChange.event; + + get minimumSize(): number { return this._minimumSize; } + set minimumSize(size: number) { this._minimumSize = size; this._onDidChange.fire(); } + + get maximumSize(): number { return this._maximumSize; } + set maximumSize(size: number) { this._maximumSize = size; this._onDidChange.fire(); } + + private _onDidRender = new Emitter<{ container: HTMLElement; orientation: Orientation }>(); + readonly onDidRender = this._onDidRender.event; + + private _size = 0; + get size(): number { return this._size; } + private _onDidLayout = new Emitter<{ size: number; orientation: Orientation }>(); + readonly onDidLayout = this._onDidLayout.event; + + private _onDidFocus = new Emitter(); + readonly onDidFocus = this._onDidFocus.event; + + constructor( + private _minimumSize: number, + private _maximumSize: number + ) { + assert(_minimumSize <= _maximumSize, 'splitview view minimum size must be <= maximum size'); + } + + render(container: HTMLElement, orientation: Orientation): void { + this._onDidRender.fire({ container, orientation }); + } + + layout(size: number, orientation: Orientation): void { + this._size = size; + this._onDidLayout.fire({ size, orientation }); + } + + focus(): void { + this._onDidFocus.fire(); + } + + dispose(): void { + this._onDidChange.dispose(); + this._onDidRender.dispose(); + this._onDidLayout.dispose(); + this._onDidFocus.dispose(); + } +} + +function getSashes(splitview: SplitView): Sash[] { + return (splitview as any).sashItems.map(i => i.sash) as Sash[]; +} + +suite('Splitview', () => { + let container: HTMLElement; + + setup(() => { + container = document.createElement('div'); + container.style.position = 'absolute'; + container.style.width = `${200}px`; + container.style.height = `${200}px`; + }); + + teardown(() => { + container = null; + }); + + test('empty splitview has empty DOM', () => { + const splitview = new SplitView(container); + assert.equal(container.firstElementChild.childElementCount, 0, 'split view should be empty'); + splitview.dispose(); + }); + + test('has views as sashes as children', () => { + const view1 = new TestView(20, 20); + const view2 = new TestView(20, 20); + const view3 = new TestView(20, 20); + const splitview = new SplitView(container); + + splitview.addView(view1, 20); + splitview.addView(view2, 20); + splitview.addView(view3, 20); + + let viewQuery = container.querySelectorAll('.monaco-split-view > .split-view-view'); + assert.equal(viewQuery.length, 3, 'split view should have 3 views'); + + let sashQuery = container.querySelectorAll('.monaco-split-view > .monaco-sash'); + assert.equal(sashQuery.length, 2, 'split view should have 2 sashes'); + + splitview.removeView(2); + + viewQuery = container.querySelectorAll('.monaco-split-view > .split-view-view'); + assert.equal(viewQuery.length, 2, 'split view should have 2 views'); + + sashQuery = container.querySelectorAll('.monaco-split-view > .monaco-sash'); + assert.equal(sashQuery.length, 1, 'split view should have 1 sash'); + + splitview.removeView(0); + + viewQuery = container.querySelectorAll('.monaco-split-view > .split-view-view'); + assert.equal(viewQuery.length, 1, 'split view should have 1 view'); + + sashQuery = container.querySelectorAll('.monaco-split-view > .monaco-sash'); + assert.equal(sashQuery.length, 0, 'split view should have no sashes'); + + splitview.removeView(0); + + viewQuery = container.querySelectorAll('.monaco-split-view > .split-view-view'); + assert.equal(viewQuery.length, 0, 'split view should have no views'); + + sashQuery = container.querySelectorAll('.monaco-split-view > .monaco-sash'); + assert.equal(sashQuery.length, 0, 'split view should have no sashes'); + + splitview.dispose(); + view1.dispose(); + view2.dispose(); + view3.dispose(); + }); + + test('calls view methods on addView and removeView', () => { + const view = new TestView(20, 20); + const splitview = new SplitView(container); + + let didLayout = false; + const layoutDisposable = view.onDidLayout(() => didLayout = true); + + let didRender = false; + const renderDisposable = view.onDidRender(() => didRender = true); + + splitview.addView(view, 20); + + assert.equal(view.size, 20, 'view has right size'); + assert(didLayout, 'layout is called'); + assert(didLayout, 'render is called'); + + splitview.dispose(); + layoutDisposable.dispose(); + renderDisposable.dispose(); + view.dispose(); + }); + + test('stretches view to viewport', () => { + const view = new TestView(20, Number.POSITIVE_INFINITY); + const splitview = new SplitView(container); + splitview.layout(200); + + splitview.addView(view, 20); + assert.equal(view.size, 200, 'view is stretched'); + + splitview.layout(200); + assert.equal(view.size, 200, 'view stayed the same'); + + splitview.layout(100); + assert.equal(view.size, 100, 'view is collapsed'); + + splitview.layout(20); + assert.equal(view.size, 20, 'view is collapsed'); + + splitview.layout(10); + assert.equal(view.size, 20, 'view is clamped'); + + splitview.layout(200); + assert.equal(view.size, 200, 'view is stretched'); + + splitview.dispose(); + view.dispose(); + }); + + test('respects preferred sizes with structural changes', () => { + const view1 = new TestView(20, Number.POSITIVE_INFINITY); + const view2 = new TestView(20, Number.POSITIVE_INFINITY); + const view3 = new TestView(20, Number.POSITIVE_INFINITY); + const splitview = new SplitView(container); + splitview.layout(200); + + splitview.addView(view1, 20); + assert.equal(view1.size, 200, 'view1 is stretched'); + + splitview.addView(view2, 20); + assert.equal(view1.size, 20, 'view1 size is restored'); + assert.equal(view2.size, 200 - 20, 'view2 is stretched'); + + splitview.addView(view3, 20); + assert.equal(view1.size, 20, 'view1 size is restored'); + assert.equal(view2.size, 20, 'view2 size is restored'); + assert.equal(view3.size, 160, 'view3 is stretched'); + + splitview.dispose(); + view3.dispose(); + view2.dispose(); + view1.dispose(); + }); + + test('can resize views', () => { + const view1 = new TestView(20, Number.POSITIVE_INFINITY); + const view2 = new TestView(20, Number.POSITIVE_INFINITY); + const view3 = new TestView(20, Number.POSITIVE_INFINITY); + const splitview = new SplitView(container); + splitview.layout(200); + + splitview.addView(view1, 20); + splitview.addView(view2, 20); + splitview.addView(view3, 20); + + assert.equal(view1.size, 20, 'view1 size is the default'); + assert.equal(view2.size, 20, 'view2 size the the default'); + assert.equal(view3.size, 160, 'view3 is stretched'); + + splitview.resizeView(1, 40); + + assert.equal(view1.size, 20, 'view1 is untouched'); + assert.equal(view2.size, 40, 'view2 is stretched'); + assert.equal(view3.size, 140, 'view3 is collapsed'); + + splitview.resizeView(0, 70); + + assert.equal(view1.size, 70, 'view1 is stretched'); + assert.equal(view2.size, 20, 'view2 is collapsed'); + assert.equal(view3.size, 110, 'view3 is collapsed'); + + assert.throws(() => splitview.resizeView(2, 20)); + + assert.equal(view1.size, 70, 'view1 stays the same'); + assert.equal(view2.size, 20, 'view2 stays the same'); + assert.equal(view3.size, 110, 'view3 stays the same'); + + splitview.dispose(); + view3.dispose(); + view2.dispose(); + view1.dispose(); + }); + + test('reacts to view changes', () => { + const view1 = new TestView(20, Number.POSITIVE_INFINITY); + const view2 = new TestView(20, Number.POSITIVE_INFINITY); + const view3 = new TestView(20, Number.POSITIVE_INFINITY); + const splitview = new SplitView(container); + splitview.layout(200); + + splitview.addView(view1, 20); + splitview.addView(view2, 20); + splitview.addView(view3, 20); + + assert.equal(view1.size, 20, 'view1 size is restored'); + assert.equal(view2.size, 20, 'view2 size is restored'); + assert.equal(view3.size, 160, 'view3 is stretched'); + + view3.maximumSize = 20; + + assert.equal(view1.size, 20, 'view1 stays the same'); + assert.equal(view2.size, 160, 'view2 is stretched'); + assert.equal(view3.size, 20, 'view3 is collapsed'); + + view2.maximumSize = 40; + + assert.equal(view1.size, 140, 'view1 is stretched'); + assert.equal(view2.size, 40, 'view2 is collapsed'); + assert.equal(view3.size, 20, 'view3 is collapsed'); + + view3.maximumSize = 200; + + assert.equal(view1.size, 140, 'view1 stays the same'); + assert.equal(view2.size, 40, 'view2 stays the same'); + assert.equal(view3.size, 20, 'view3 stays the same'); + + view3.minimumSize = 100; + + assert.equal(view1.size, 80, 'view1 is collapsed'); + assert.equal(view2.size, 20, 'view2 stays the same'); + assert.equal(view3.size, 100, 'view3 is stretched'); + + splitview.dispose(); + view3.dispose(); + view2.dispose(); + view1.dispose(); + }); + + test('sashes are properly enabled/disabled', () => { + const view1 = new TestView(20, Number.POSITIVE_INFINITY); + const view2 = new TestView(20, Number.POSITIVE_INFINITY); + const view3 = new TestView(20, Number.POSITIVE_INFINITY); + const splitview = new SplitView(container); + splitview.layout(200); + + splitview.addView(view1, 20); + splitview.addView(view2, 20); + splitview.addView(view3, 20); + + let sashes = getSashes(splitview); + assert.equal(sashes.length, 2, 'there are two sashes'); + assert.equal(sashes[0].enabled, true, 'first sash is enabled'); + assert.equal(sashes[1].enabled, true, 'second sash is enabled'); + + splitview.layout(60); + assert.equal(sashes[0].enabled, false, 'first sash is disabled'); + assert.equal(sashes[1].enabled, false, 'second sash is disabled'); + + splitview.layout(20); + assert.equal(sashes[0].enabled, false, 'first sash is disabled'); + assert.equal(sashes[1].enabled, false, 'second sash is disabled'); + + splitview.layout(200); + assert.equal(sashes[0].enabled, true, 'first sash is enabled'); + assert.equal(sashes[1].enabled, true, 'second sash is enabled'); + + view1.maximumSize = 20; + assert.equal(sashes[0].enabled, false, 'first sash is disabled'); + assert.equal(sashes[1].enabled, true, 'second sash is enabled'); + + view2.maximumSize = 20; + assert.equal(sashes[0].enabled, false, 'first sash is disabled'); + assert.equal(sashes[1].enabled, false, 'second sash is disabled'); + + view1.maximumSize = 300; + assert.equal(sashes[0].enabled, true, 'first sash is enabled'); + assert.equal(sashes[1].enabled, true, 'second sash is enabled'); + + view2.maximumSize = 200; + assert.equal(sashes[0].enabled, true, 'first sash is enabled'); + assert.equal(sashes[1].enabled, true, 'second sash is enabled'); + + splitview.dispose(); + view3.dispose(); + view2.dispose(); + view1.dispose(); + }); +}); \ No newline at end of file diff --git a/src/vs/base/test/common/uri.test.ts b/src/vs/base/test/common/uri.test.ts index 1e8340f98c7..3341b0bcb6a 100644 --- a/src/vs/base/test/common/uri.test.ts +++ b/src/vs/base/test/common/uri.test.ts @@ -368,6 +368,23 @@ suite('URI', () => { assert.equal(value.toString(), 'http://l%C3%B6calhost:8080/far'); }); + test('URI#toString, user information in authority', () => { + var value = URI.parse('http://foo:bar@localhost/far'); + assert.equal(value.toString(), 'http://foo:bar@localhost/far'); + + value = URI.parse('http://foo@localhost/far'); + assert.equal(value.toString(), 'http://foo@localhost/far'); + + value = URI.parse('http://foo:bAr@localhost:8080/far'); + assert.equal(value.toString(), 'http://foo:bAr@localhost:8080/far'); + + value = URI.parse('http://foo@localhost:8080/far'); + assert.equal(value.toString(), 'http://foo@localhost:8080/far'); + + value = URI.from({ scheme: 'http', authority: 'fƶƶ:bƶr@lƶcalhost:8080', path: '/far', query: undefined, fragment: undefined }); + assert.equal(value.toString(), 'http://f%C3%B6%C3%B6:b%C3%B6r@l%C3%B6calhost:8080/far'); + }); + test('correctFileUriToFilePath2', () => { var test = (input: string, expected: string) => { diff --git a/src/vs/code/electron-main/keyboard.ts b/src/vs/code/electron-main/keyboard.ts index 73c3289adf6..3b19f2fea15 100644 --- a/src/vs/code/electron-main/keyboard.ts +++ b/src/vs/code/electron-main/keyboard.ts @@ -174,4 +174,9 @@ export class KeybindingsResolver { return this.keybindings[commandId]; } + + public dispose(): void { + this._onKeybindingsChanged.dispose(); + this.keybindingsWatcher.dispose(); + } } \ No newline at end of file diff --git a/src/vs/code/electron-main/launch.ts b/src/vs/code/electron-main/launch.ts index 1df7365dc64..189224ef9c6 100644 --- a/src/vs/code/electron-main/launch.ts +++ b/src/vs/code/electron-main/launch.ts @@ -14,6 +14,7 @@ import { ParsedArgs } from 'vs/platform/environment/common/environment'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { OpenContext } from 'vs/platform/windows/common/windows'; import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows'; +import { whenDeleted } from 'vs/base/node/pfs'; export const ID = 'launchService'; export const ILaunchService = createDecorator(ID); @@ -113,9 +114,13 @@ export class LaunchService implements ILaunchService { } // If the other instance is waiting to be killed, we hook up a window listener if one window - // is being used and only then resolve the startup promise which will kill this second instance + // is being used and only then resolve the startup promise which will kill this second instance. + // In addition, we poll for the wait marker file to be deleted to return. if (args.wait && usedWindows.length === 1 && usedWindows[0]) { - return this.windowsService.waitForWindowCloseOrLoad(usedWindows[0].id); + return TPromise.any([ + this.windowsService.waitForWindowCloseOrLoad(usedWindows[0].id), + whenDeleted(args.waitMarkerFilePath) + ]).then(() => void 0, () => void 0); } return TPromise.as(null); diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index e04278782bf..5c6464d7db1 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -5,7 +5,7 @@ 'use strict'; -import * as path from 'path'; +import { basename, normalize, join, dirname } from 'path'; import * as fs from 'original-fs'; import { localize } from 'vs/nls'; import * as arrays from 'vs/base/common/arrays'; @@ -114,6 +114,7 @@ export class WindowsManager implements IWindowsMainService { private lastClosedWindowState: IWindowState; private fileDialog: FileDialog; + private workspacesManager: WorkspacesManager; private _onWindowReady = new Emitter(); onWindowReady: CommonEvent = this._onWindowReady.event; @@ -146,7 +147,9 @@ export class WindowsManager implements IWindowsMainService { @IInstantiationService private instantiationService: IInstantiationService ) { this.windowsState = this.storageService.getItem(WindowsManager.windowsStateStorageKey) || { openedWindows: [] }; + this.fileDialog = new FileDialog(environmentService, telemetryService, storageService, this); + this.workspacesManager = new WorkspacesManager(workspacesService, lifecycleService, backupService, environmentService, this); this.migrateLegacyWindowState(); } @@ -403,7 +406,7 @@ export class WindowsManager implements IWindowsMainService { workspacesToRestore.push(...this.workspacesService.getUntitledWorkspacesSync()); // collect from previous window session emptyToRestore = this.backupService.getEmptyWindowBackupPaths(); - emptyToRestore.push(...pathsToOpen.filter(w => !w.workspace && !w.folderPath && w.backupPath).map(w => path.basename(w.backupPath))); // add empty windows with backupPath + emptyToRestore.push(...pathsToOpen.filter(w => !w.workspace && !w.folderPath && w.backupPath).map(w => basename(w.backupPath))); // add empty windows with backupPath emptyToRestore = arrays.distinct(emptyToRestore); // prevent duplicates } @@ -932,7 +935,7 @@ export class WindowsManager implements IWindowsMainService { anyPath = parsedPath.path; } - const candidate = path.normalize(anyPath); + const candidate = normalize(anyPath); try { const candidateStat = fs.statSync(candidate); if (candidateStat) { @@ -1051,7 +1054,7 @@ export class WindowsManager implements IWindowsMainService { // For all other cases we first call into registerEmptyWindowBackupSync() to set it before // loading the window. if (options.emptyWindowBackupFolder) { - configuration.backupPath = path.join(this.environmentService.backupHome, options.emptyWindowBackupFolder); + configuration.backupPath = join(this.environmentService.backupHome, options.emptyWindowBackupFolder); } let window: CodeWindow; @@ -1291,93 +1294,19 @@ export class WindowsManager implements IWindowsMainService { }); } - public saveAndOpenWorkspace(window: CodeWindow, path: string): TPromise { - if (!window || !window.win || window.readyState !== ReadyState.READY || !window.openedWorkspace || !path) { - return TPromise.as(null); // return early if the window is not ready or disposed or does not have a workspace - } - - return this.doSaveAndOpenWorkspace(window, window.openedWorkspace, path); + public saveAndOpenWorkspace(win: CodeWindow, path: string): TPromise { + return this.workspacesManager.saveAndOpenWorkspace(win, path); } - public createAndOpenWorkspace(window: CodeWindow, folders?: string[], path?: string): TPromise { - if (!window || !window.win || window.readyState !== ReadyState.READY) { - return TPromise.as(null); // return early if the window is not ready or disposed - } - - return this.workspacesService.createWorkspace(folders).then(workspace => { - return this.doSaveAndOpenWorkspace(window, workspace, path); - }); + public createAndOpenWorkspace(win: CodeWindow, folders?: string[], path?: string): TPromise { + return this.workspacesManager.createAndOpenWorkspace(win, folders, path); } - private doSaveAndOpenWorkspace(window: CodeWindow, workspace: IWorkspaceIdentifier, path?: string): TPromise { - let savePromise: TPromise; - if (path) { - savePromise = this.workspacesService.saveWorkspace(workspace, path); - } else { - savePromise = TPromise.as(workspace); - } - - return savePromise.then(workspace => { - window.focus(); - - // Only open workspace when the window has not vetoed this - return this.lifecycleService.unload(window, UnloadReason.RELOAD, workspace).done(veto => { - if (!veto) { - - // Register window for backups and migrate current backups over - let backupPath: string; - if (window.config && !window.config.extensionDevelopmentPath) { - backupPath = this.backupService.registerWorkspaceBackupSync(workspace, window.config.backupPath); - } - - // Craft a new window configuration to use for the transition - const configuration: IWindowConfiguration = mixin({}, window.config); - configuration.folderPath = void 0; - configuration.workspace = workspace; - configuration.backupPath = backupPath; - - // Reload - window.reload(configuration); - } - }); - }); + public openWorkspace(win?: CodeWindow): void { + this.workspacesManager.openWorkspace(win); } - public openWorkspace(window: CodeWindow = this.getLastActiveWindow()): void { - let defaultPath: string; - if (window && window.openedWorkspace && !this.workspacesService.isUntitledWorkspace(window.openedWorkspace)) { - defaultPath = path.dirname(window.openedWorkspace.configPath); - } else { - defaultPath = this.getWorkspaceDialogDefaultPath(window ? (window.openedWorkspace || window.openedFolderPath) : void 0); - } - this.pickFileAndOpen({ - windowId: window ? window.id : void 0, - dialogOptions: { - buttonLabel: mnemonicButtonLabel(localize({ key: 'openWorkspace', comment: ['&& denotes a mnemonic'] }, "&&Open")), - title: localize('openWorkspaceTitle', "Open Workspace"), - filters: WORKSPACE_FILTER, - properties: ['openFile'], - defaultPath - } - }); - } - - private getWorkspaceDialogDefaultPath(workspace?: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier): string { - let defaultPath: string; - if (workspace) { - if (isSingleFolderWorkspaceIdentifier(workspace)) { - defaultPath = path.dirname(workspace); - } else { - const resolvedWorkspace = this.workspacesService.resolveWorkspaceSync(workspace.configPath); - if (resolvedWorkspace && resolvedWorkspace.folders.length > 0) { - defaultPath = path.dirname(resolvedWorkspace.folders[0].path); - } - } - } - - return defaultPath; - } private onBeforeWindowUnload(e: IWindowUnloadEvent): void { const windowClosing = (e.reason === UnloadReason.CLOSE); @@ -1399,74 +1328,8 @@ export class WindowsManager implements IWindowsMainService { return; // Windows/Linux: quits when last window is closed, so do not ask then } - this.promptToSaveUntitledWorkspace(e, workspace); - } - - private promptToSaveUntitledWorkspace(e: IWindowUnloadEvent, workspace: IWorkspaceIdentifier): void { - enum ConfirmResult { - SAVE, - DONT_SAVE, - CANCEL - } - - const save = { label: mnemonicButtonLabel(localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save")), result: ConfirmResult.SAVE }; - const dontSave = { label: mnemonicButtonLabel(localize({ key: 'doNotSave', comment: ['&& denotes a mnemonic'] }, "Do&&n't Save")), result: ConfirmResult.DONT_SAVE }; - const cancel = { label: localize('cancel', "Cancel"), result: ConfirmResult.CANCEL }; - - const buttons: { label: string; result: ConfirmResult; }[] = []; - if (isWindows) { - buttons.push(save, dontSave, cancel); - } else if (isLinux) { - buttons.push(dontSave, cancel, save); - } else { - buttons.push(save, cancel, dontSave); - } - - const options: Electron.MessageBoxOptions = { - title: this.environmentService.appNameLong, - message: localize('saveWorkspaceMessage', "Do you want to save your workspace configuration as a file?"), - detail: localize('saveWorkspaceDetail', "Save your workspace if you plan to open it again."), - noLink: true, - type: 'warning', - buttons: buttons.map(button => button.label), - cancelId: buttons.indexOf(cancel) - }; - - if (isLinux) { - options.defaultId = 2; - } - - const res = dialog.showMessageBox(e.window.win, options); - - switch (buttons[res].result) { - - // Cancel: veto unload - case ConfirmResult.CANCEL: - e.veto(true); - break; - - // Don't Save: delete workspace - case ConfirmResult.DONT_SAVE: - this.workspacesService.deleteUntitledWorkspaceSync(workspace); - e.veto(false); - break; - - // Save: save workspace, but do not veto unload - case ConfirmResult.SAVE: { - const target = dialog.showSaveDialog(e.window.win, { - buttonLabel: mnemonicButtonLabel(localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save")), - title: localize('saveWorkspace', "Save Workspace"), - filters: WORKSPACE_FILTER, - defaultPath: this.getWorkspaceDialogDefaultPath(workspace) - }); - - if (target) { - e.veto(this.workspacesService.saveWorkspace(workspace, target).then(() => false, () => false)); - } else { - e.veto(true); // keep veto if no target was provided - } - } - } + // Handle untitled workspaces with prompt as needed + this.workspacesManager.promptToSaveUntitledWorkspace(e, workspace); } public focusLastActive(cli: ParsedArgs, context: OpenContext): CodeWindow { @@ -1754,7 +1617,7 @@ class FileDialog { if (paths && paths.length > 0) { // Remember path in storage for next time - this.storageService.setItem(FileDialog.workingDirPickerStorageKey, path.dirname(paths[0])); + this.storageService.setItem(FileDialog.workingDirPickerStorageKey, dirname(paths[0])); // Return return clb(paths); @@ -1763,4 +1626,204 @@ class FileDialog { return clb(void (0)); }); } +} + +class WorkspacesManager { + + constructor( + private workspacesService: IWorkspacesMainService, + private lifecycleService: ILifecycleService, + private backupService: IBackupMainService, + private environmentService: IEnvironmentService, + private windowsMainService: IWindowsMainService + ) { + } + + public saveAndOpenWorkspace(window: CodeWindow, path: string): TPromise { + if (!window || !window.win || window.readyState !== ReadyState.READY || !window.openedWorkspace || !path || !this.isValidTargetWorkspacePath(window, path)) { + return TPromise.as(null); // return early if the window is not ready or disposed or does not have a workspace + } + + return this.doSaveAndOpenWorkspace(window, window.openedWorkspace, path); + } + + public createAndOpenWorkspace(window: CodeWindow, folders?: string[], path?: string): TPromise { + if (!window || !window.win || window.readyState !== ReadyState.READY || !this.isValidTargetWorkspacePath(window, path)) { + return TPromise.as(null); // return early if the window is not ready or disposed + } + + return this.workspacesService.createWorkspace(folders).then(workspace => { + return this.doSaveAndOpenWorkspace(window, workspace, path); + }); + } + + private isValidTargetWorkspacePath(window: CodeWindow, path?: string): boolean { + if (!path) { + return true; + } + + if (window.openedWorkspace && window.openedWorkspace.configPath === path) { + return false; // window is already opened on a workspace with that path + } + + // Prevent overwriting a workspace that is currently opened in another window + if (findWindowOnWorkspace(this.windowsMainService.getWindows(), { id: this.workspacesService.getWorkspaceId(path), configPath: path })) { + const options: Electron.MessageBoxOptions = { + title: product.nameLong, + type: 'info', + buttons: [localize('ok', "OK")], + message: localize('workspaceOpenedMessage', "Unable to save workspace '{0}'", basename(path)), + detail: localize('workspaceOpenedDetail', "The workspace is already opened in another window. Please close that window first and then try again."), + noLink: true + }; + + const activeWindow = BrowserWindow.getFocusedWindow(); + if (activeWindow) { + dialog.showMessageBox(activeWindow, options); + } else { + dialog.showMessageBox(options); + } + + return false; + } + + return true; // OK + } + + private doSaveAndOpenWorkspace(window: CodeWindow, workspace: IWorkspaceIdentifier, path?: string): TPromise { + let savePromise: TPromise; + if (path) { + savePromise = this.workspacesService.saveWorkspace(workspace, path); + } else { + savePromise = TPromise.as(workspace); + } + + return savePromise.then(workspace => { + window.focus(); + + // Only open workspace when the window has not vetoed this + return this.lifecycleService.unload(window, UnloadReason.RELOAD, workspace).done(veto => { + if (!veto) { + + // Register window for backups and migrate current backups over + let backupPath: string; + if (window.config && !window.config.extensionDevelopmentPath) { + backupPath = this.backupService.registerWorkspaceBackupSync(workspace, window.config.backupPath); + } + + // Craft a new window configuration to use for the transition + const configuration: IWindowConfiguration = mixin({}, window.config); + configuration.folderPath = void 0; + configuration.workspace = workspace; + configuration.backupPath = backupPath; + + // Reload + window.reload(configuration); + } + }); + }); + } + + public openWorkspace(window = this.windowsMainService.getLastActiveWindow()): void { + let defaultPath: string; + if (window && window.openedWorkspace && !this.workspacesService.isUntitledWorkspace(window.openedWorkspace)) { + defaultPath = dirname(window.openedWorkspace.configPath); + } else { + defaultPath = this.getWorkspaceDialogDefaultPath(window ? (window.openedWorkspace || window.openedFolderPath) : void 0); + } + + this.windowsMainService.pickFileAndOpen({ + windowId: window ? window.id : void 0, + dialogOptions: { + buttonLabel: mnemonicButtonLabel(localize({ key: 'openWorkspace', comment: ['&& denotes a mnemonic'] }, "&&Open")), + title: localize('openWorkspaceTitle', "Open Workspace"), + filters: WORKSPACE_FILTER, + properties: ['openFile'], + defaultPath + } + }); + } + + public promptToSaveUntitledWorkspace(e: IWindowUnloadEvent, workspace: IWorkspaceIdentifier): void { + enum ConfirmResult { + SAVE, + DONT_SAVE, + CANCEL + } + + const save = { label: mnemonicButtonLabel(localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save")), result: ConfirmResult.SAVE }; + const dontSave = { label: mnemonicButtonLabel(localize({ key: 'doNotSave', comment: ['&& denotes a mnemonic'] }, "Do&&n't Save")), result: ConfirmResult.DONT_SAVE }; + const cancel = { label: localize('cancel', "Cancel"), result: ConfirmResult.CANCEL }; + + const buttons: { label: string; result: ConfirmResult; }[] = []; + if (isWindows) { + buttons.push(save, dontSave, cancel); + } else if (isLinux) { + buttons.push(dontSave, cancel, save); + } else { + buttons.push(save, cancel, dontSave); + } + + const options: Electron.MessageBoxOptions = { + title: this.environmentService.appNameLong, + message: localize('saveWorkspaceMessage', "Do you want to save your workspace configuration as a file?"), + detail: localize('saveWorkspaceDetail', "Save your workspace if you plan to open it again."), + noLink: true, + type: 'warning', + buttons: buttons.map(button => button.label), + cancelId: buttons.indexOf(cancel) + }; + + if (isLinux) { + options.defaultId = 2; + } + + const res = dialog.showMessageBox(e.window.win, options); + + switch (buttons[res].result) { + + // Cancel: veto unload + case ConfirmResult.CANCEL: + e.veto(true); + break; + + // Don't Save: delete workspace + case ConfirmResult.DONT_SAVE: + this.workspacesService.deleteUntitledWorkspaceSync(workspace); + e.veto(false); + break; + + // Save: save workspace, but do not veto unload + case ConfirmResult.SAVE: { + const target = dialog.showSaveDialog(e.window.win, { + buttonLabel: mnemonicButtonLabel(localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save")), + title: localize('saveWorkspace', "Save Workspace"), + filters: WORKSPACE_FILTER, + defaultPath: this.getWorkspaceDialogDefaultPath(workspace) + }); + + if (target) { + e.veto(this.workspacesService.saveWorkspace(workspace, target).then(() => false, () => false)); + } else { + e.veto(true); // keep veto if no target was provided + } + } + } + } + + private getWorkspaceDialogDefaultPath(workspace?: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier): string { + let defaultPath: string; + if (workspace) { + if (isSingleFolderWorkspaceIdentifier(workspace)) { + defaultPath = dirname(workspace); + } else { + const resolvedWorkspace = this.workspacesService.resolveWorkspaceSync(workspace.configPath); + if (resolvedWorkspace && resolvedWorkspace.folders.length > 0) { + defaultPath = dirname(resolvedWorkspace.folders[0].path); + } + } + } + + return defaultPath; + } } \ No newline at end of file diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index d0f0416e6ff..7476115fcee 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -14,6 +14,7 @@ import pkg from 'vs/platform/node/package'; import * as fs from 'fs'; import * as paths from 'path'; import * as os from 'os'; +import { whenDeleted } from 'vs/base/node/pfs'; function shouldSpawnCliProcess(argv: ParsedArgs): boolean { return argv['list-extensions'] || !!argv['install-extension'] || !!argv['uninstall-extension']; @@ -105,14 +106,7 @@ export function main(argv: string[]): TPromise { child.once('exit', () => c(null)); // Complete when wait marker file is deleted - const interval = setInterval(() => { - fs.exists(waitMarkerFilePath, exists => { - if (!exists) { - clearInterval(interval); - c(null); - } - }); - }, 1000); + whenDeleted(waitMarkerFilePath).done(c, c); }); } } diff --git a/src/vs/editor/browser/view/viewImpl.ts b/src/vs/editor/browser/view/viewImpl.ts index b8c5900a2d2..79cf5f2c105 100644 --- a/src/vs/editor/browser/view/viewImpl.ts +++ b/src/vs/editor/browser/view/viewImpl.ts @@ -308,7 +308,8 @@ export class View extends ViewEventHandler { } private getEditorClassName() { - return this._context.configuration.editor.editorClassName + ' ' + getThemeTypeSelector(this._context.theme.type); + let focused = this._textAreaHandler.isFocused() ? ' focused' : ''; + return this._context.configuration.editor.editorClassName + ' ' + getThemeTypeSelector(this._context.theme.type) + focused; } // --- begin event handlers @@ -323,7 +324,7 @@ export class View extends ViewEventHandler { return false; } public onFocusChanged(e: viewEvents.ViewFocusChangedEvent): boolean { - this.domNode.toggleClassName('focused', e.isFocused); + this.domNode.setClassName(this.getEditorClassName()); if (e.isFocused) { this.outgoingEvents.emitViewFocusGained(); } else { diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index fe646ba39e4..22efae7b65e 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -400,6 +400,7 @@ export abstract class CodeEditorWidget extends CommonCodeEditor implements edito this._view.render(false, true); this.hasView = true; + this._view.domNode.domNode.setAttribute('data-uri', model.uri.toString()); } } diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index 14d3ec12623..d00a0e2365b 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -598,6 +598,11 @@ const editorConfiguration: IConfigurationNode = { 'default': EDITOR_DEFAULTS.contribInfo.colorDecorators, 'description': nls.localize('colorDecorators', "Controls whether the editor should render the inline color decorators and color picker.") }, + 'editor.lightbulb.enabled': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.lightbulbEnabled, + 'description': nls.localize('codeActions', "Enables the code action lightbulb") + }, 'diffEditor.renderSideBySide': { 'type': 'boolean', 'default': true, diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index d3a94dcc623..c619e76e5e9 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -114,6 +114,17 @@ export interface IEditorMinimapOptions { maxColumn?: number; } +/** + * Configuration options for editor minimap + */ +export interface IEditorLightbulbOptions { + /** + * Enable the lightbulb code action. + * Defaults to true. + */ + enabled?: boolean; +} + /** * Configuration options for the editor. */ @@ -462,6 +473,10 @@ export interface IEditorOptions { * @internal */ referenceInfos?: boolean; + /** + * Control the behavior and rendering of the code action lightbulb. + */ + lightbulb?: IEditorLightbulbOptions; /** * Enable code folding * Defaults to true in vscode and to false in monaco-editor. @@ -795,6 +810,7 @@ export interface EditorContribOptions { readonly matchBrackets: boolean; readonly find: InternalEditorFindOptions; readonly colorDecorators: boolean; + readonly lightbulbEnabled: boolean; } /** @@ -1140,6 +1156,7 @@ export class InternalEditorOptions { && a.matchBrackets === b.matchBrackets && this._equalFindOptions(a.find, b.find) && a.colorDecorators === b.colorDecorators + && a.lightbulbEnabled === b.lightbulbEnabled ); } @@ -1669,6 +1686,7 @@ export class EditorOptionsValidator { matchBrackets: _boolean(opts.matchBrackets, defaults.matchBrackets), find: find, colorDecorators: _boolean(opts.colorDecorators, defaults.colorDecorators), + lightbulbEnabled: _boolean(opts.lightbulb ? opts.lightbulb.enabled : false, defaults.lightbulbEnabled) }; } } @@ -1766,7 +1784,8 @@ export class InternalEditorOptionsFactory { showFoldingControls: opts.contribInfo.showFoldingControls, matchBrackets: (accessibilityIsOn ? false : opts.contribInfo.matchBrackets), // DISABLED WHEN SCREEN READER IS ATTACHED find: opts.contribInfo.find, - colorDecorators: opts.contribInfo.colorDecorators + colorDecorators: opts.contribInfo.colorDecorators, + lightbulbEnabled: opts.contribInfo.lightbulbEnabled } }; } @@ -2205,6 +2224,7 @@ export const EDITOR_DEFAULTS: IValidatedEditorOptions = { seedSearchStringFromSelection: true, autoFindInSelection: false }, - colorDecorators: true + colorDecorators: true, + lightbulbEnabled: true }, }; diff --git a/src/vs/editor/contrib/parameterHints/browser/parameterHints.css b/src/vs/editor/contrib/parameterHints/browser/parameterHints.css index db3ff8001ae..812bff41e87 100644 --- a/src/vs/editor/contrib/parameterHints/browser/parameterHints.css +++ b/src/vs/editor/contrib/parameterHints/browser/parameterHints.css @@ -45,6 +45,11 @@ .monaco-editor .parameter-hints-widget .docs { padding: 0 10px 0 5px; + white-space: pre-wrap; +} + +.monaco-editor .parameter-hints-widget .docs.markdown-docs { + white-space: initial; } .monaco-editor .parameter-hints-widget .docs .code { diff --git a/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts index bacb7f633f8..547ca4d3757 100644 --- a/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts @@ -336,8 +336,10 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { if (activeParameter && activeParameter.documentation) { const documentation = $('span.documentation'); if (typeof activeParameter.documentation === 'string') { + dom.removeClass(this.docs, 'markdown-docs'); documentation.textContent = activeParameter.documentation; } else { + dom.addClass(this.docs, 'markdown-docs'); documentation.appendChild(this.markdownRenderer.render(activeParameter.documentation)); } dom.append(this.docs, $('p', null, documentation)); diff --git a/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.ts b/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.ts index 8bb2b4c2f02..2b76c9e2540 100644 --- a/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.ts +++ b/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.ts @@ -29,12 +29,12 @@ export class LightBulbWidget implements IDisposable, IContentWidget { private _futureFixes = new CancellationTokenSource(); constructor(editor: ICodeEditor) { - this._editor = editor; - this._editor.addContentWidget(this); - this._domNode = document.createElement('div'); this._domNode.className = 'lightbulb-glyph'; + this._editor = editor; + this._editor.addContentWidget(this); + this._disposables.push(dom.addStandardDisposableListener(this._domNode, 'click', e => { // a bit of extra work to make sure the menu // doesn't cover the line-text @@ -66,6 +66,12 @@ export class LightBulbWidget implements IDisposable, IContentWidget { dom.removeClass(this._domNode, 'hidden'); }); })); + this._disposables.push(this._editor.onDidChangeConfiguration(e => { + // hide when told to do so + if (e.contribInfo && !this._editor.getConfiguration().contribInfo.lightbulbEnabled) { + this.hide(); + } + })); } dispose(): void { @@ -124,11 +130,14 @@ export class LightBulbWidget implements IDisposable, IContentWidget { } private _show(): void { - const { fontInfo } = this._editor.getConfiguration(); + const config = this._editor.getConfiguration(); + if (!config.contribInfo.lightbulbEnabled) { + return; + } const { lineNumber } = this._model.position; const model = this._editor.getModel(); const indent = model.getIndentLevel(lineNumber); - const lineHasSpace = fontInfo.spaceWidth * indent > 22; + const lineHasSpace = config.fontInfo.spaceWidth * indent > 22; let effectiveLineNumber = lineNumber; if (!lineHasSpace) { diff --git a/src/vs/editor/contrib/referenceSearch/browser/peekViewWidget.ts b/src/vs/editor/contrib/referenceSearch/browser/peekViewWidget.ts index 1d912e8d055..a0a78bac128 100644 --- a/src/vs/editor/contrib/referenceSearch/browser/peekViewWidget.ts +++ b/src/vs/editor/contrib/referenceSearch/browser/peekViewWidget.ts @@ -174,7 +174,6 @@ export abstract class PeekViewWidget extends ZoneWidget { if (!this._isShowing && heightInPixel < 0) { // Looks like the view zone got folded away! this.dispose(); - this._onDidClose.fire(this); return; } diff --git a/src/vs/editor/contrib/suggest/browser/media/suggest.css b/src/vs/editor/contrib/suggest/browser/media/suggest.css index 3eee4201f43..2cd4a1b4896 100644 --- a/src/vs/editor/contrib/suggest/browser/media/suggest.css +++ b/src/vs/editor/contrib/suggest/browser/media/suggest.css @@ -228,6 +228,11 @@ .monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs { margin: 0; padding: 4px 5px; + white-space: pre-wrap; +} + +.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs.markdown-docs { + white-space: initial; } .monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs .code { diff --git a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts index 63a2a0e9e3a..472fce09a86 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts @@ -249,8 +249,10 @@ class SuggestionDetails { } removeClass(this.el, 'no-docs'); if (typeof item.suggestion.documentation === 'string') { + removeClass(this.docs, 'markdown-docs'); this.docs.textContent = item.suggestion.documentation; } else { + addClass(this.docs, 'markdown-docs'); this.docs.innerHTML = this.markdownRenderer.render(item.suggestion.documentation).innerHTML; } diff --git a/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts b/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts index 53c35db2f3b..f2ebd600999 100644 --- a/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts +++ b/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts @@ -7,7 +7,6 @@ import 'vs/css!./zoneWidget'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { Widget } from 'vs/base/browser/ui/widget'; import * as objects from 'vs/base/common/objects'; import * as dom from 'vs/base/browser/dom'; import { Sash, Orientation, IHorizontalSashLayoutProvider, ISashEvent } from 'vs/base/browser/ui/sash/sash'; @@ -157,7 +156,7 @@ class Arrow { } } -export abstract class ZoneWidget extends Widget implements IHorizontalSashLayoutProvider { +export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { private _arrow: Arrow; private _overlayWidget: OverlayWidgetDelegate; @@ -174,7 +173,6 @@ export abstract class ZoneWidget extends Widget implements IHorizontalSashLayout constructor(editor: ICodeEditor, options: IOptions = {}) { - super(); this.editor = editor; this.options = objects.clone(options); objects.mixin(this.options, defaultOptions, false); diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index 7863e391781..3dadc377cef 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -9,6 +9,7 @@ import Severity from 'vs/base/common/severity'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import { IConfigurationService, IConfigurationServiceEvent, IConfigurationValue, IConfigurationKeys, IConfigurationValues, Configuration, IConfigurationData, ConfigurationModel, IConfigurationOverrides } from 'vs/platform/configuration/common/configuration'; +import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IEditor, IEditorInput, IEditorOptions, IEditorService, IResourceInput, Position } from 'vs/platform/editor/common/editor'; import { ICommandService, ICommand, ICommandEvent, ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands'; import { AbstractKeybindingService } from 'vs/platform/keybinding/common/abstractKeybindingService'; @@ -17,7 +18,7 @@ import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingReso import { IKeybindingEvent, KeybindingSource, IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IConfirmation, IMessageService } from 'vs/platform/message/common/message'; -import { IWorkspaceContextService, ILegacyWorkspace, IWorkspace } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, IWorkspace, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { Selection } from 'vs/editor/common/core/selection'; @@ -525,38 +526,31 @@ export class SimpleWorkspaceContextService implements IWorkspaceContextService { public readonly onDidChangeWorkspaceName: Event = this._onDidChangeWorkspaceName.event; private readonly _onDidChangeWorkspaceRoots: Emitter = new Emitter(); - public readonly onDidChangeWorkspaceRoots: Event = this._onDidChangeWorkspaceRoots.event; + public readonly onDidChangeWorkspaceFolders: Event = this._onDidChangeWorkspaceRoots.event; - private readonly legacyWorkspace: ILegacyWorkspace; private readonly workspace: IWorkspace; constructor() { - this.legacyWorkspace = { resource: URI.from({ scheme: SimpleWorkspaceContextService.SCHEME, authority: 'model', path: '/' }) }; - this.workspace = { id: '4064f6ec-cb38-4ad0-af64-ee6467e63c82', roots: [this.legacyWorkspace.resource], name: this.legacyWorkspace.resource.fsPath }; - } - - public getLegacyWorkspace(): ILegacyWorkspace { - return this.legacyWorkspace; + const resource = URI.from({ scheme: SimpleWorkspaceContextService.SCHEME, authority: 'model', path: '/' }); + this.workspace = { id: '4064f6ec-cb38-4ad0-af64-ee6467e63c82', folders: [resource], name: resource.fsPath }; } public getWorkspace(): IWorkspace { return this.workspace; } - public getRoot(resource: URI): URI { - return resource && resource.scheme === SimpleWorkspaceContextService.SCHEME ? this.workspace.roots[0] : void 0; + public getWorkbenchState(): WorkbenchState { + if (this.workspace) { + if (this.workspace.configuration) { + return WorkbenchState.WORKSPACE; + } + return WorkbenchState.FOLDER; + } + return WorkbenchState.EMPTY; } - public hasWorkspace(): boolean { - return true; - } - - public hasFolderWorkspace(): boolean { - return true; - } - - public hasMultiFolderWorkspace(): boolean { - return false; + public getWorkspaceFolder(resource: URI): URI { + return resource && resource.scheme === SimpleWorkspaceContextService.SCHEME ? this.workspace.folders[0] : void 0; } public isInsideWorkspace(resource: URI): boolean { @@ -566,4 +560,8 @@ export class SimpleWorkspaceContextService implements IWorkspaceContextService { public toResource(workspaceRelativePath: string): URI { return URI.file(workspaceRelativePath); } + + public isCurrentWorkspace(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): boolean { + return true; + } } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 467ac20a374..194484f2618 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -2683,6 +2683,17 @@ declare module monaco.editor { maxColumn?: number; } + /** + * Configuration options for editor minimap + */ + export interface IEditorLightbulbOptions { + /** + * Enable the lightbulb code action. + * Defaults to true. + */ + enabled?: boolean; + } + /** * Configuration options for the editor. */ @@ -3018,6 +3029,10 @@ declare module monaco.editor { * Defaults to true. */ codeLens?: boolean; + /** + * Control the behavior and rendering of the code action lightbulb. + */ + lightbulb?: IEditorLightbulbOptions; /** * Enable code folding * Defaults to true in vscode and to false in monaco-editor. @@ -3296,6 +3311,7 @@ declare module monaco.editor { readonly matchBrackets: boolean; readonly find: InternalEditorFindOptions; readonly colorDecorators: boolean; + readonly lightbulbEnabled: boolean; } /** diff --git a/src/vs/platform/backup/electron-main/backupMainService.ts b/src/vs/platform/backup/electron-main/backupMainService.ts index 94210b0572f..9ea43c880e6 100644 --- a/src/vs/platform/backup/electron-main/backupMainService.ts +++ b/src/vs/platform/backup/electron-main/backupMainService.ts @@ -88,14 +88,19 @@ export class BackupMainService implements IBackupMainService { } private moveBackupFolderSync(backupPath: string, moveFromPath: string): void { - if (!fs.existsSync(moveFromPath)) { - return; + + // Target exists: make sure to convert existing backups to empty window backups + if (fs.existsSync(backupPath)) { + this.convertToEmptyWindowBackup(backupPath); } - try { - fs.renameSync(moveFromPath, backupPath); - } catch (ex) { - this.logService.error(`Backup: Could not move backup folder to new location: ${ex.toString()}`); + // When we have data to migrate from, move it over to the target location + if (fs.existsSync(moveFromPath)) { + try { + fs.renameSync(moveFromPath, backupPath); + } catch (ex) { + this.logService.error(`Backup: Could not move backup folder to new location: ${ex.toString()}`); + } } } @@ -231,16 +236,7 @@ export class BackupMainService implements IBackupMainService { staleBackupWorkspaces.push({ workspaceIdentifier: workspaceId, backupPath, target: workspaceOrFolder.target }); if (missingWorkspace) { - const identifier = this.getRandomEmptyWindowId(); - this.pushBackupPathsSync(identifier, this.backups.emptyWorkspaces); - const newEmptyWindowBackupPath = path.join(this.backupHome, identifier); - try { - fs.renameSync(backupPath, newEmptyWindowBackupPath); - } catch (ex) { - this.logService.error(`Backup: Could not rename backup folder for missing workspace: ${ex.toString()}`); - - this.removeBackupPathSync(identifier, this.backups.emptyWorkspaces); - } + this.convertToEmptyWindowBackup(backupPath); } } }); @@ -267,6 +263,27 @@ export class BackupMainService implements IBackupMainService { }); } + private convertToEmptyWindowBackup(backupPath: string): boolean { + + // New empty window backup + const identifier = this.getRandomEmptyWindowId(); + this.pushBackupPathsSync(identifier, this.backups.emptyWorkspaces); + + // Rename backupPath to new empty window backup path + const newEmptyWindowBackupPath = path.join(this.backupHome, identifier); + try { + fs.renameSync(backupPath, newEmptyWindowBackupPath); + } catch (ex) { + this.logService.error(`Backup: Could not rename backup folder: ${ex.toString()}`); + + this.removeBackupPathSync(identifier, this.backups.emptyWorkspaces); + + return false; + } + + return true; + } + private hasBackupsSync(backupPath: string): boolean { try { const backupSchemas = extfs.readdirSync(backupPath); diff --git a/src/vs/platform/backup/test/electron-main/backupMainService.test.ts b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts index 063ebe91c6a..7d5a37d4edb 100644 --- a/src/vs/platform/backup/test/electron-main/backupMainService.test.ts +++ b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts @@ -208,6 +208,33 @@ suite('BackupMainService', () => { assert.ok(fs.existsSync(path.join(workspaceBackupPath, 'backup.txt'))); assert.ok(!fs.existsSync(backupPathToMigrate)); + const emptyBackups = service.getEmptyWindowBackupPaths(); + assert.equal(0, emptyBackups.length); + + done(); + }); + + test('service backup migration makes sure to preserve existing backups', done => { + const backupPathToMigrate = service.toBackupPath(fooFile.fsPath); + fs.mkdirSync(backupPathToMigrate); + fs.writeFileSync(path.join(backupPathToMigrate, 'backup.txt'), 'Some Data'); + service.registerFolderBackupSync(backupPathToMigrate); + + const backupPathToPreserve = service.toBackupPath(barFile.fsPath); + fs.mkdirSync(backupPathToPreserve); + fs.writeFileSync(path.join(backupPathToPreserve, 'backup.txt'), 'Some Data'); + service.registerFolderBackupSync(backupPathToPreserve); + + const workspaceBackupPath = service.registerWorkspaceBackupSync(toWorkspace(barFile.fsPath), backupPathToMigrate); + + assert.ok(fs.existsSync(workspaceBackupPath)); + assert.ok(fs.existsSync(path.join(workspaceBackupPath, 'backup.txt'))); + assert.ok(!fs.existsSync(backupPathToMigrate)); + + const emptyBackups = service.getEmptyWindowBackupPaths(); + assert.equal(1, emptyBackups.length); + assert.equal(1, fs.readdirSync(path.join(backupHome, emptyBackups[0])).length); + done(); }); diff --git a/src/vs/platform/configuration/common/configuration.ts b/src/vs/platform/configuration/common/configuration.ts index 85e3e00ff4c..7b9ecd5e500 100644 --- a/src/vs/platform/configuration/common/configuration.ts +++ b/src/vs/platform/configuration/common/configuration.ts @@ -257,7 +257,7 @@ export class Configuration { lookupLegacy(key: string): IConfigurationValue { if (!this._legacyWorkspaceConsolidatedConfiguration) { - this._legacyWorkspaceConsolidatedConfiguration = this._workspace ? new ConfigurationModel().merge(this._workspaceConfiguration).merge(this.folders.get(this._workspace.roots[0])) : null; + this._legacyWorkspaceConsolidatedConfiguration = this._workspace ? new ConfigurationModel().merge(this._workspaceConfiguration).merge(this.folders.get(this._workspace.folders[0])) : null; } const consolidateConfigurationModel = this.getConsolidateConfigurationModel({}); return { @@ -325,7 +325,7 @@ export class Configuration { return this._workspaceConsolidatedConfiguration; } - const root = this._workspace.getRoot(resource); + const root = this._workspace.getFolder(resource); if (!root) { return this._workspaceConsolidatedConfiguration; } @@ -338,7 +338,7 @@ export class Configuration { return null; } - const root = this._workspace.getRoot(resource); + const root = this._workspace.getFolder(resource); return root ? this.folders.get(root) : null; } diff --git a/src/vs/platform/configuration/node/configurationService.ts b/src/vs/platform/configuration/node/configurationService.ts index 2eff5bb204f..8f6005c6430 100644 --- a/src/vs/platform/configuration/node/configurationService.ts +++ b/src/vs/platform/configuration/node/configurationService.ts @@ -8,7 +8,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { ConfigWatcher } from 'vs/base/node/config'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; -import { IDisposable, toDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { ConfigurationSource, IConfigurationService, IConfigurationServiceEvent, IConfigurationValue, IConfigurationKeys, ConfigurationModel, IConfigurationOverrides, Configuration, IConfigurationValues, IConfigurationData } from 'vs/platform/configuration/common/configuration'; import { CustomConfigurationModel, DefaultConfigurationModel } from 'vs/platform/configuration/common/model'; import Event, { Emitter } from 'vs/base/common/event'; @@ -37,7 +37,7 @@ export class ConfigurationService extends Disposable implements IConfiguratio return userConfigModel; } }); - this._register(toDisposable(() => this.userConfigModelWatcher.dispose())); + this._register(this.userConfigModelWatcher); // Listeners this._register(this.userConfigModelWatcher.onDidUpdateConfiguration(() => this.onConfigurationChange(ConfigurationSource.User))); diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index b4ee3dfcca3..540f812d271 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -40,6 +40,7 @@ export interface ParsedArgs { 'enable-proposed-api'?: string | string[]; 'open-url'?: string | string[]; 'skip-getting-started'?: boolean; + 'sticky-quickopen'?: boolean; } export const IEnvironmentService = createDecorator('environmentService'); diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index ad6e24e0eb2..6eb3036e94a 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -46,7 +46,8 @@ const options: minimist.Opts = { 'list-extensions', 'show-versions', 'nolazy', - 'skip-getting-started' + 'skip-getting-started', + 'sticky-quickopen' ], alias: { add: 'a', diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index 9ba9802831a..01222188e6a 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -10,7 +10,7 @@ import * as os from 'os'; import * as path from 'path'; import * as fs from 'fs'; import URI from 'vs/base/common/uri'; -import { generateUuid } from 'vs/base/common/uuid'; +import { generateUuid, isUUID } from 'vs/base/common/uuid'; import { memoize } from 'vs/base/common/decorators'; import pkg from 'vs/platform/node/package'; import product from 'vs/platform/node/product'; @@ -32,9 +32,6 @@ function getUniqueUserId(): string { } function getNixIPCHandle(userDataPath: string, type: string): string { - if (process.env['XDG_RUNTIME_DIR']) { - return path.join(process.env['XDG_RUNTIME_DIR'], `${pkg.name}-${pkg.version}-${type}.sock`); - } return path.join(userDataPath, `${pkg.version}-${type}.sock`); } @@ -151,11 +148,15 @@ export class EnvironmentService implements IEnvironmentService { try { this.machineUUID = fs.readFileSync(machineIdPath, 'utf8'); + + if (!isUUID(this.machineUUID)) { + throw new Error('Not a UUID'); + } } catch (err) { this.machineUUID = generateUuid(); try { - fs.writeFileSync(machineIdPath, this.machineUUID); + fs.writeFileSync(machineIdPath, this.machineUUID, 'utf8'); } catch (err) { console.warn('Could not store machine ID'); } diff --git a/src/vs/platform/extensionManagement/common/extensionEnablementService.ts b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts index 75606dd0a96..65348d5095c 100644 --- a/src/vs/platform/extensionManagement/common/extensionEnablementService.ts +++ b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts @@ -10,7 +10,7 @@ import Event, { Emitter } from 'vs/base/common/event'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IExtensionManagementService, DidUninstallExtensionEvent, IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { adoptToGalleryExtensionId, getIdAndVersionFromLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -35,7 +35,7 @@ export class ExtensionEnablementService implements IExtensionEnablementService { } private get hasWorkspace(): boolean { - return this.contextService.hasWorkspace(); + return this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY; } public getGloballyDisabledExtensions(): string[] { diff --git a/src/vs/platform/extensionManagement/test/common/extensionEnablementService.test.ts b/src/vs/platform/extensionManagement/test/common/extensionEnablementService.test.ts index e44ff52c8c9..e35a98d864c 100644 --- a/src/vs/platform/extensionManagement/test/common/extensionEnablementService.test.ts +++ b/src/vs/platform/extensionManagement/test/common/extensionEnablementService.test.ts @@ -12,7 +12,7 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/ import { Emitter } from 'vs/base/common/event'; import { StorageService, InMemoryLocalStorage } from 'vs/platform/storage/common/storageService'; import { IStorageService } from 'vs/platform/storage/common/storage'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; function storageService(instantiationService: TestInstantiationService): IStorageService { @@ -21,9 +21,7 @@ function storageService(instantiationService: TestInstantiationService): IStorag let workspaceContextService = instantiationService.get(IWorkspaceContextService); if (!workspaceContextService) { workspaceContextService = instantiationService.stub(IWorkspaceContextService, { - hasWorkspace: () => { - return true; - }, + getWorkbenchState: () => WorkbenchState.FOLDER, }); } service = instantiationService.stub(IStorageService, instantiationService.createInstance(StorageService, new InMemoryLocalStorage(), new InMemoryLocalStorage())); @@ -73,7 +71,7 @@ suite('ExtensionEnablementService Test', () => { test('test when no extensions are disabled for workspace when there is no workspace', (done) => { testObject.setEnablement('pub.a', false, true) .then(() => { - instantiationService.stub(IWorkspaceContextService, 'hasWorkspace', false); + instantiationService.stub(IWorkspaceContextService, 'getWorkbenchState', WorkbenchState.EMPTY); assert.deepEqual([], testObject.getWorkspaceDisabledExtensions()); }) .then(done, done); @@ -178,7 +176,7 @@ suite('ExtensionEnablementService Test', () => { }); test('test disable an extension for workspace when there is no workspace throws error', (done) => { - instantiationService.stub(IWorkspaceContextService, 'hasWorkspace', false); + instantiationService.stub(IWorkspaceContextService, 'getWorkbenchState', WorkbenchState.EMPTY); testObject.setEnablement('pub.a', false, true) .then(() => assert.fail('should throw an error'), error => assert.ok(error)) .then(done, done); diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index 455db1408ec..c89cc6399bf 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -93,7 +93,7 @@ export interface IFileService { * * The optional parameter content can be used as value to fill into the new file. */ - createFile(resource: URI, content?: string): TPromise; + createFile(resource: URI, content?: string, options?: ICreateFileOptions): TPromise; /** * Creates a new folder with the given path. The returned promise @@ -491,6 +491,15 @@ export interface IResolveFileOptions { resolveSingleChildDescendants?: boolean; } +export interface ICreateFileOptions { + + /** + * Overwrite the file to create if it already exists on disk. Otherwise + * an error will be thrown (FILE_MODIFIED_SINCE). + */ + overwrite?: boolean; +} + export interface IImportResult { stat: IFileStat; isNew: boolean; diff --git a/src/vs/platform/storage/test/common/storageService.test.ts b/src/vs/platform/storage/test/common/storageService.test.ts index 2bd09689b54..f51d6957118 100644 --- a/src/vs/platform/storage/test/common/storageService.test.ts +++ b/src/vs/platform/storage/test/common/storageService.test.ts @@ -8,7 +8,7 @@ import * as assert from 'assert'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { StorageScope } from 'vs/platform/storage/common/storage'; -import { IWorkspaceContextService, IWorkspace } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, IWorkspace, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { StorageService, InMemoryLocalStorage } from 'vs/platform/storage/common/storageService'; import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; @@ -19,9 +19,7 @@ suite('Workbench StorageSevice', () => { setup(() => { instantiationService = new TestInstantiationService(); contextService = instantiationService.stub(IWorkspaceContextService, { - hasWorkspace: () => { - return true; - }, + getWorkbenchState: () => WorkbenchState.FOLDER, getWorkspace: () => { return TestWorkspace; } diff --git a/src/vs/platform/telemetry/common/experiments.ts b/src/vs/platform/telemetry/common/experiments.ts index 103f1908f50..6911c881760 100644 --- a/src/vs/platform/telemetry/common/experiments.ts +++ b/src/vs/platform/telemetry/common/experiments.ts @@ -4,14 +4,12 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import * as platform from 'vs/base/common/platform'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export interface IExperiments { ripgrepQuickSearch: boolean; - deployToAzureQuickLink: boolean; } export const IExperimentService = createDecorator('experimentService'); @@ -44,10 +42,7 @@ export class ExperimentService implements IExperimentService { function loadExperiments(storageService: IStorageService, configurationService: IConfigurationService): IExperiments { const experiments = splitExperimentsRandomness(storageService); - if (platform.isWindows) { - // Ripgrep triggers MsMpEng.exe (https://github.com/BurntSushi/ripgrep/issues/600) - experiments.ripgrepQuickSearch = false; - } + experiments.ripgrepQuickSearch = true; return applyOverrides(experiments, configurationService); } @@ -64,12 +59,11 @@ function applyOverrides(experiments: IExperiments, configurationService: IConfig function splitExperimentsRandomness(storageService: IStorageService): IExperiments { const random1 = getExperimentsRandomness(storageService); const [random2, ripgrepQuickSearch] = splitRandom(random1); - const [/* random3 */, deployToAzureQuickLink] = splitRandom(random2); + const [/* random3 */, /* deployToAzureQuickLink */] = splitRandom(random2); // const [random4, /* mergeQuickLinks */] = splitRandom(random3); // const [random5, /* enableWelcomePage */] = splitRandom(random4); return { ripgrepQuickSearch, - deployToAzureQuickLink }; } diff --git a/src/vs/platform/theme/common/colorExtensionPoint.ts b/src/vs/platform/theme/common/colorExtensionPoint.ts index f42ffb926a3..51a47fdb4bb 100644 --- a/src/vs/platform/theme/common/colorExtensionPoint.ts +++ b/src/vs/platform/theme/common/colorExtensionPoint.ts @@ -93,7 +93,7 @@ export class ColorExtensionPoint { extensionValue.forEach(extension => { if (typeof extension.id !== 'string' || extension.id.length === 0) { - collector.error(nls.localize('invalid.id', "'configuration.colors.id' must be defined an can not be empty")); + collector.error(nls.localize('invalid.id', "'configuration.colors.id' must be defined and can not be empty")); return; } if (!extension.id.match(colorIdPattern)) { @@ -101,7 +101,7 @@ export class ColorExtensionPoint { return; } if (typeof extension.description !== 'string' || extension.id.length === 0) { - collector.error(nls.localize('invalid.description', "'configuration.colors.description' must be defined an can not be empty")); + collector.error(nls.localize('invalid.description', "'configuration.colors.description' must be defined and can not be empty")); return; } let defaults = extension.defaults; diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index 4eeb0b8fb83..38f755fc658 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -6,59 +6,58 @@ import URI from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import * as paths from 'vs/base/common/paths'; import { TrieMap } from 'vs/base/common/map'; import Event from 'vs/base/common/event'; import { isLinux } from 'vs/base/common/platform'; import { distinct } from 'vs/base/common/arrays'; +import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; export const IWorkspaceContextService = createDecorator('contextService'); +export enum WorkbenchState { + EMPTY = 1, + FOLDER, + WORKSPACE +} + export interface IWorkspaceContextService { _serviceBrand: any; - /** - * Returns if the application was opened with a workspace or not. - */ - hasWorkspace(): boolean; - - /** - * Returns if the application was opened with a folder. - */ - hasFolderWorkspace(): boolean; - - /** - * Returns if the application was opened with a workspace that can have one or more folders. - */ - hasMultiFolderWorkspace(): boolean; - - /** - * Provides access to the workspace object the platform is running with. This may be null if the workbench was opened - * without workspace (empty); - */ - getLegacyWorkspace(): ILegacyWorkspace; - /** * Provides access to the workspace object the platform is running with. This may be null if the workbench was opened * without workspace (empty); */ getWorkspace(): IWorkspace; + /** + * Return the state of the workbench. + * + * WorkbenchState.EMPTY - if the workbench was opened with empty window or file + * WorkbenchState.FOLDER - if the workbench was opened with a folder + * WorkbenchState.WORKSPACE - if the workbench was opened with a workspace + */ + getWorkbenchState(): WorkbenchState; + /** * An event which fires on workspace name changes. */ onDidChangeWorkspaceName: Event; /** - * An event which fires on workspace roots change. + * An event which fires on workspace folders change. */ - onDidChangeWorkspaceRoots: Event; + onDidChangeWorkspaceFolders: Event; /** - * Returns the root for the given resource from the workspace. + * Returns the folder for the given resource from the workspace. * Can be null if there is no workspace or the resource is not inside the workspace. */ - getRoot(resource: URI): URI; + getWorkspaceFolder(resource: URI): URI; + + /** + * Return `true` if the current workspace has the given identifier otherwise `false`. + */ + isCurrentWorkspace(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): boolean; /** * Returns if the provided resource is inside the workspace or not. @@ -71,20 +70,6 @@ export interface IWorkspaceContextService { toResource: (workspaceRelativePath: string) => URI; } -export interface ILegacyWorkspace { - - /** - * the full uri of the workspace. this is a file:// URL to the location - * of the workspace on disk. - */ - resource: URI; - - /** - * creation time of the workspace folder if known - */ - ctime?: number; -} - export interface IWorkspace { /** @@ -98,9 +83,9 @@ export interface IWorkspace { readonly name: string; /** - * Roots in the workspace. + * Folders in the workspace. */ - readonly roots: URI[]; + readonly folders: URI[]; /** * the location of the workspace configuration @@ -108,59 +93,32 @@ export interface IWorkspace { readonly configuration?: URI; } -export class LegacyWorkspace implements ILegacyWorkspace { - private _name: string; - - constructor(private _resource: URI, private _ctime?: number) { - this._name = paths.basename(this._resource.fsPath) || this._resource.fsPath; - } - - public get resource(): URI { - return this._resource; - } - - public get name(): string { - return this._name; - } - - public get ctime(): number { - return this._ctime; - } - - public toResource(workspaceRelativePath: string, root?: URI): URI { - if (typeof workspaceRelativePath === 'string') { - return URI.file(paths.join(root ? root.fsPath : this._resource.fsPath, workspaceRelativePath)); - } - - return null; - } -} - export class Workspace implements IWorkspace { - private _rootsMap: TrieMap = new TrieMap(); - private _roots: URI[]; + private _foldersMap: TrieMap = new TrieMap(); + private _folders: URI[]; constructor( public readonly id: string, private _name: string, - roots: URI[], - private _configuration: URI = null + folders: URI[], + private _configuration: URI = null, + public readonly ctime?: number ) { - this.roots = roots; + this.folders = folders; } - private ensureUnique(roots: URI[]): URI[] { - return distinct(roots, root => isLinux ? root.fsPath : root.fsPath.toLowerCase()); + private ensureUnique(folders: URI[]): URI[] { + return distinct(folders, folder => isLinux ? folder.fsPath : folder.fsPath.toLowerCase()); } - public get roots(): URI[] { - return this._roots; + public get folders(): URI[] { + return this._folders; } - public set roots(roots: URI[]) { - this._roots = this.ensureUnique(roots); - this.updateRootsMap(); + public set folders(folders: URI[]) { + this._folders = this.ensureUnique(folders); + this.updateFoldersMap(); } public get name(): string { @@ -179,22 +137,22 @@ export class Workspace implements IWorkspace { this._configuration = configuration; } - public getRoot(resource: URI): URI { + public getFolder(resource: URI): URI { if (!resource) { return null; } - return this._rootsMap.findSubstr(resource.fsPath); + return this._foldersMap.findSubstr(resource.fsPath); } - private updateRootsMap(): void { - this._rootsMap = new TrieMap(); - for (const root of this.roots) { - this._rootsMap.insert(root.fsPath, root); + private updateFoldersMap(): void { + this._foldersMap = new TrieMap(); + for (const folder of this.folders) { + this._foldersMap.insert(folder.fsPath, folder); } } public toJSON(): IWorkspace { - return { id: this.id, roots: this.roots, name: this.name }; + return { id: this.id, folders: this.folders, name: this.name }; } } diff --git a/src/vs/platform/workspace/test/common/workspace.test.ts b/src/vs/platform/workspace/test/common/workspace.test.ts index 1f296428317..dcfdff48e38 100644 --- a/src/vs/platform/workspace/test/common/workspace.test.ts +++ b/src/vs/platform/workspace/test/common/workspace.test.ts @@ -17,6 +17,6 @@ suite('Workspace', () => { let roots = [URI.file('/some/path'), URI.file('/some/path')]; let ws = new Workspace('id', 'name', roots, URI.file('/config')); - assert.equal(ws.roots.length, 1); + assert.equal(ws.folders.length, 1); }); }); \ No newline at end of file diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 5f4723974a6..c825bfef95b 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -773,7 +773,7 @@ declare module 'vscode' { export interface ThemableDecorationRenderOptions { /** * Background color of the decoration. Use rgba() and define transparent background colors to play well with other decorations. - * Alternativly a color from the color registry an be [referenced](#ColorIdentifier). + * Alternatively a color from the color registry can be [referenced](#ThemeColor). */ backgroundColor?: string | ThemeColor; @@ -3869,6 +3869,21 @@ declare module 'vscode' { options?: ShellExecutionOptions; } + /** + * The scope of a task. + */ + export enum TaskScope { + /** + * The task is a global task + */ + Global = 1, + + /** + * The task is a workspace task + */ + Workspace = 2 + } + /** * A task to execute */ @@ -3877,8 +3892,10 @@ declare module 'vscode' { /** * Creates a new task. * + * @deprecated: Use the new constructors that allow specifying a target for the task. + * * @param definition The task definition as defined in the taskDefinitions extension point. - * @param name The task's name. Is presented in the user interface. + * @param scope The task's name. Is presented in the user interface. * @param source The task's source (e.g. 'gulp', 'npm', ...). Is presented in the user interface. * @param execution The process or shell execution. * @param problemMatchers the names of problem matchers to use, like '$tsc' @@ -3887,11 +3904,31 @@ declare module 'vscode' { */ constructor(taskDefinition: TaskDefinition, name: string, source: string, execution?: ProcessExecution | ShellExecution, problemMatchers?: string | string[]); + /** + * Creates a new task. + * + * @param definition The task definition as defined in the taskDefinitions extension point. + * @param scope Specifies the task's scope. It is either a global or a workspace task or a task for a specific workspace folder. + * @param workspaceFolder The workspace folder this task is created for. + * @param name The task's name. Is presented in the user interface. + * @param source The task's source (e.g. 'gulp', 'npm', ...). Is presented in the user interface. + * @param execution The process or shell execution. + * @param problemMatchers the names of problem matchers to use, like '$tsc' + * or '$eslint'. Problem matchers can be contributed by an extension using + * the `problemMatchers` extension point. + */ + constructor(taskDefinition: TaskDefinition, target: TaskScope.Global | TaskScope.Workspace | WorkspaceFolder, name: string, source: string, execution?: ProcessExecution | ShellExecution, problemMatchers?: string | string[]); + /** * The task's definition. */ definition: TaskDefinition; + /** + * The task's scope. + */ + scope?: TaskScope.Global | TaskScope.Workspace | WorkspaceFolder; + /** * The task's name */ @@ -4947,7 +4984,7 @@ declare module 'vscode' { /** * An event that is emitted when a [text document](#TextDocument) is changed. This usually happens * when the [contents](#TextDocument.getText) changes but also when other things like the - * [dirty](TextDocument#isDirty)-state changes. + * [dirty](#TextDocument.isDirty)-state changes. */ export const onDidChangeTextDocument: Event; diff --git a/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts b/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts index 36b5e975ef9..71f54113cfd 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts @@ -9,7 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; import { MainThreadConfigurationShape, MainContext, ExtHostContext, IExtHostContext } from '../node/extHost.protocol'; @@ -51,7 +51,7 @@ export class MainThreadConfiguration implements MainThreadConfigurationShape { } private deriveConfigurationTarget(key: string, resource: URI): ConfigurationTarget { - if (resource && this._workspaceContextService.hasMultiFolderWorkspace()) { + if (resource && this._workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { const configurationProperties = Registry.as(ConfigurationExtensions.Configuration).getConfigurationProperties(); if (configurationProperties[key] && configurationProperties[key].scope === ConfigurationScope.RESOURCE) { return ConfigurationTarget.FOLDER; diff --git a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts index ad58576214d..fd9eef3cbef 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts @@ -7,7 +7,7 @@ import { isPromiseCanceledError } from 'vs/base/common/errors'; import URI from 'vs/base/common/uri'; import { ISearchService, QueryType, ISearchQuery, ISearchProgressItem, ISearchComplete } from 'vs/platform/search/common/search'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ICommonCodeEditor, isCommonCodeEditor } from 'vs/editor/common/editorCommon'; @@ -20,6 +20,7 @@ import { IDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecyc import { RemoteFileService } from 'vs/workbench/services/files/electron-browser/remoteFileService'; import { Emitter } from 'vs/base/common/event'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; +import { IExperimentService } from 'vs/platform/telemetry/common/experiments'; @extHostNamedCustomer(MainContext.MainThreadWorkspace) export class MainThreadWorkspace implements MainThreadWorkspaceShape { @@ -35,10 +36,11 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { @ITextFileService private readonly _textFileService: ITextFileService, @IWorkbenchEditorService private readonly _editorService: IWorkbenchEditorService, @ITextModelService private readonly _textModelResolverService: ITextModelService, + @IExperimentService private experimentService: IExperimentService, @IFileService private readonly _fileService: IFileService ) { this._proxy = extHostContext.get(ExtHostContext.ExtHostWorkspace); - this._contextService.onDidChangeWorkspaceRoots(this._onDidChangeWorkspace, this, this._toDispose); + this._contextService.onDidChangeWorkspaceFolders(this._onDidChangeWorkspace, this, this._toDispose); } dispose(): void { @@ -53,23 +55,23 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { // --- workspace --- private _onDidChangeWorkspace(): void { - this._proxy.$acceptWorkspaceData(this._contextService.getWorkspace()); + this._proxy.$acceptWorkspaceData(this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : this._contextService.getWorkspace()); } // --- search --- $startSearch(include: string, exclude: string, maxResults: number, requestId: number): Thenable { const workspace = this._contextService.getWorkspace(); - if (!workspace) { + if (!workspace.folders.length) { return undefined; } - const query: ISearchQuery = { - folderQueries: workspace.roots.map(root => ({ folder: root })), + folderQueries: workspace.folders.map(root => ({ folder: root })), type: QueryType.File, maxResults, includePattern: { [include]: true }, excludePattern: { [exclude]: true }, + useRipgrep: this.experimentService.getExperiments().ripgrepQuickSearch }; this._searchService.extendQuery(query); diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index efe5e932d5c..c678975f35e 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -97,7 +97,7 @@ export function createApiFactory( const extHostQuickOpen = threadService.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(threadService)); const extHostTerminalService = threadService.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(threadService)); const extHostSCM = threadService.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(threadService, extHostCommands)); - const extHostTask = threadService.set(ExtHostContext.ExtHostTask, new ExtHostTask(threadService)); + const extHostTask = threadService.set(ExtHostContext.ExtHostTask, new ExtHostTask(threadService, extHostWorkspace)); const extHostCredentials = threadService.set(ExtHostContext.ExtHostCredentials, new ExtHostCredentials(threadService)); const extHostWindow = threadService.set(ExtHostContext.ExtHostWindow, new ExtHostWindow(threadService)); threadService.set(ExtHostContext.ExtHostExtensionService, extensionService); @@ -599,6 +599,7 @@ export function createApiFactory( TaskGroup: extHostTypes.TaskGroup, ProcessExecution: extHostTypes.ProcessExecution, ShellExecution: extHostTypes.ShellExecution, + TaskScope: extHostTypes.TaskScope, Task: extHostTypes.Task, ConfigurationTarget: extHostTypes.ConfigurationTarget }; diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 7245c7d80bd..04275bdc50a 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -64,7 +64,7 @@ export interface IEnvironment { export interface IWorkspaceData { id: string; name: string; - roots: URI[]; + folders: URI[]; } export interface IInitData { diff --git a/src/vs/workbench/api/node/extHostFileSystemEventService.ts b/src/vs/workbench/api/node/extHostFileSystemEventService.ts index 822298414c3..6038446ffc4 100644 --- a/src/vs/workbench/api/node/extHostFileSystemEventService.ts +++ b/src/vs/workbench/api/node/extHostFileSystemEventService.ts @@ -6,7 +6,7 @@ import Event, { Emitter } from 'vs/base/common/event'; import { Disposable } from './extHostTypes'; -import { match } from 'vs/base/common/glob'; +import { parse } from 'vs/base/common/glob'; import { Uri, FileSystemWatcher as _FileSystemWatcher } from 'vscode'; import { FileSystemEvents, ExtHostFileSystemEventServiceShape } from './extHost.protocol'; @@ -43,24 +43,26 @@ class FileSystemWatcher implements _FileSystemWatcher { this._config += 0b100; } + const parsedPattern = parse(globPattern); + let subscription = dispatcher(events => { if (!ignoreCreateEvents) { for (let created of events.created) { - if (match(globPattern, created.fsPath)) { + if (parsedPattern(created.fsPath)) { this._onDidCreate.fire(created); } } } if (!ignoreChangeEvents) { for (let changed of events.changed) { - if (match(globPattern, changed.fsPath)) { + if (parsedPattern(changed.fsPath)) { this._onDidChange.fire(changed); } } } if (!ignoreDeleteEvents) { for (let deleted of events.deleted) { - if (match(globPattern, deleted.fsPath)) { + if (parsedPattern(deleted.fsPath)) { this._onDidDelete.fire(deleted); } } diff --git a/src/vs/workbench/api/node/extHostTask.ts b/src/vs/workbench/api/node/extHostTask.ts index a174c82f1b7..b644dfb2424 100644 --- a/src/vs/workbench/api/node/extHostTask.ts +++ b/src/vs/workbench/api/node/extHostTask.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; +import URI from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { TPromise } from 'vs/base/common/winjs.base'; import * as Objects from 'vs/base/common/objects'; @@ -15,6 +16,7 @@ import * as TaskSystem from 'vs/workbench/parts/tasks/common/tasks'; import { MainContext, MainThreadTaskShape, ExtHostTaskShape, IMainContext } from 'vs/workbench/api/node/extHost.protocol'; import * as types from 'vs/workbench/api/node/extHostTypes'; +import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; import * as vscode from 'vscode'; interface StringMap { @@ -295,13 +297,13 @@ namespace ShellConfiguration { namespace Tasks { - export function from(tasks: vscode.Task[], extension: IExtensionDescription): TaskSystem.Task[] { + export function from(tasks: vscode.Task[], rootFolder: vscode.WorkspaceFolder, extension: IExtensionDescription): TaskSystem.Task[] { if (tasks === void 0 || tasks === null) { return []; } let result: TaskSystem.Task[] = []; for (let task of tasks) { - let converted = fromSingle(task, extension); + let converted = fromSingle(task, rootFolder, extension); if (converted) { result.push(converted); } @@ -309,7 +311,7 @@ namespace Tasks { return result; } - function fromSingle(task: vscode.Task, extension: IExtensionDescription): TaskSystem.ContributedTask { + function fromSingle(task: vscode.Task, rootFolder: vscode.WorkspaceFolder, extension: IExtensionDescription): TaskSystem.ContributedTask { if (typeof task.name !== 'string') { return undefined; } @@ -326,10 +328,27 @@ namespace Tasks { return undefined; } command.presentation = PresentationOptions.from(task.presentationOptions); - let source = { + + let taskScope: types.TaskScope.Global | types.TaskScope.Workspace | vscode.WorkspaceFolder | undefined = task.scope; + let workspaceFolder: vscode.WorkspaceFolder | undefined; + let scope: TaskSystem.TaskScope; + // For backwards compatibility + if (taskScope === void 0) { + scope = TaskSystem.TaskScope.Folder; + workspaceFolder = rootFolder; + } else if (taskScope === types.TaskScope.Global) { + scope = TaskSystem.TaskScope.Global; + } else if (taskScope === types.TaskScope.Workspace) { + scope = TaskSystem.TaskScope.Workspace; + } else { + workspaceFolder = taskScope; + } + let source: TaskSystem.ExtensionTaskSource = { kind: TaskSystem.TaskSourceKind.Extension, label: typeof task.source === 'string' ? task.source : extension.name, - extension: extension.id + extension: extension.id, + scope: scope, + workspaceFolder: workspaceFolder ? { uri: workspaceFolder.uri as URI } : undefined }; let label = nls.localize('task.label', '{0}: {1}', source.label, task.name); let key = (task as types.Task).definitionKey; @@ -398,11 +417,13 @@ interface HandlerData { export class ExtHostTask implements ExtHostTaskShape { private _proxy: MainThreadTaskShape; + private _extHostWorkspace: ExtHostWorkspace; private _handleCounter: number; private _handlers: Map; - constructor(mainContext: IMainContext) { + constructor(mainContext: IMainContext, extHostWorkspace: ExtHostWorkspace) { this._proxy = mainContext.get(MainContext.MainThreadTask); + this._extHostWorkspace = extHostWorkspace; this._handleCounter = 0; this._handlers = new Map(); }; @@ -426,8 +447,9 @@ export class ExtHostTask implements ExtHostTaskShape { return TPromise.wrapError(new Error('no handler found')); } return asWinJsPromise(token => handler.provider.provideTasks(token)).then(value => { + let workspaceFolders = this._extHostWorkspace.getWorkspaceFolders(); return { - tasks: Tasks.from(value, handler.extension), + tasks: Tasks.from(value, workspaceFolders && workspaceFolders.length > 0 ? workspaceFolders[0] : undefined, handler.extension), extension: handler.extension }; }); diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index e1b60f1ba51..a9b213648b8 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -1214,10 +1214,16 @@ export class ShellExecution implements vscode.ShellExecution { } } +export enum TaskScope { + Global = 1, + Workspace = 2 +} + export class Task implements vscode.Task { private _definition: vscode.TaskDefinition; private _definitionKey: string; + private _scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder; private _name: string; private _execution: ProcessExecution | ShellExecution; private _problemMatchers: string[]; @@ -1227,11 +1233,29 @@ export class Task implements vscode.Task { private _group: TaskGroup; private _presentationOptions: vscode.TaskPresentationOptions; - constructor(definition: vscode.TaskDefinition, name: string, source: string, execution?: ProcessExecution | ShellExecution, problemMatchers?: string | string[]) { + constructor(definition: vscode.TaskDefinition, name: string, source: string, execution?: ProcessExecution | ShellExecution, problemMatchers?: string | string[]); + constructor(definition: vscode.TaskDefinition, scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder, name: string, source: string, execution?: ProcessExecution | ShellExecution, problemMatchers?: string | string[]); + constructor(definition: vscode.TaskDefinition, arg2: string | (vscode.TaskScope.Global | vscode.TaskScope.Workspace) | vscode.WorkspaceFolder, arg3: any, arg4?: any, arg5?: any, arg6?: any) { this.definition = definition; - this.name = name; - this.source = source; - this.execution = execution; + let problemMatchers: string | string[]; + if (typeof arg2 === 'string') { + this.name = arg2; + this.source = arg3; + this.execution = arg4; + problemMatchers = arg5; + } else if (arg2 === TaskScope.Global || arg2 === TaskScope.Workspace) { + this.target = arg2; + this.name = arg3; + this.source = arg4; + this.execution = arg5; + problemMatchers = arg6; + } else { + this.target = arg2; + this.name = arg3; + this.source = arg4; + this.execution = arg5; + problemMatchers = arg6; + } if (typeof problemMatchers === 'string') { this._problemMatchers = [problemMatchers]; this._hasDefinedMatchers = true; @@ -1266,6 +1290,14 @@ export class Task implements vscode.Task { return this._definitionKey; } + get scope(): vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder { + return this._scope; + } + + set target(value: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder) { + this._scope = value; + } + get name(): string { return this._name; } diff --git a/src/vs/workbench/api/node/extHostWorkspace.ts b/src/vs/workbench/api/node/extHostWorkspace.ts index ba50508d0ef..219bfac0312 100644 --- a/src/vs/workbench/api/node/extHostWorkspace.ts +++ b/src/vs/workbench/api/node/extHostWorkspace.ts @@ -32,10 +32,10 @@ class Workspace2 extends Workspace { private readonly _structure = new TrieMap(s => s.split('/')); private constructor(data: IWorkspaceData) { - super(data.id, data.name, data.roots); + super(data.id, data.name, data.folders); // setup the workspace folder data structure - this.roots.forEach((uri, index) => { + this.folders.forEach((uri, index) => { const folder = { name: basename(uri.fsPath), uri, @@ -46,7 +46,7 @@ class Workspace2 extends Workspace { }); } - get folders(): vscode.WorkspaceFolder[] { + get workspaceFolders(): vscode.WorkspaceFolder[] { return this._folder.slice(0); } @@ -92,7 +92,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape { if (!this._workspace) { return undefined; } else { - return this._workspace.folders.slice(0); + return this._workspace.workspaceFolders.slice(0); } } @@ -110,11 +110,11 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape { if (!this._workspace) { return undefined; } - const { roots } = this._workspace; - if (roots.length === 0) { + const { folders } = this._workspace; + if (folders.length === 0) { return undefined; } - return roots[0].fsPath; + return folders[0].fsPath; } getRelativePath(pathOrUri: string | vscode.Uri, includeWorkspace?: boolean): string { @@ -140,7 +140,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape { } if (typeof includeWorkspace === 'undefined') { - includeWorkspace = this.workspace.roots.length > 1; + includeWorkspace = this.workspace.folders.length > 1; } let result = relative(folder.uri.fsPath, path); @@ -155,10 +155,10 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape { // keep old workspace folder, build new workspace, and // capture new workspace folders. Compute delta between // them send that as event - const oldRoots = this._workspace ? this._workspace.folders.sort(ExtHostWorkspace._compareWorkspaceFolder) : []; + const oldRoots = this._workspace ? this._workspace.workspaceFolders.sort(ExtHostWorkspace._compareWorkspaceFolder) : []; this._workspace = Workspace2.fromData(data); - const newRoots = this._workspace ? this._workspace.folders.sort(ExtHostWorkspace._compareWorkspaceFolder) : []; + const newRoots = this._workspace ? this._workspace.workspaceFolders.sort(ExtHostWorkspace._compareWorkspaceFolder) : []; const { added, removed } = delta(oldRoots, newRoots, ExtHostWorkspace._compareWorkspaceFolder); this._onDidChangeWorkspace.fire(Object.freeze({ diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index d67da02d0a5..525931079e0 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -11,7 +11,7 @@ import nls = require('vs/nls'); import { distinct } from 'vs/base/common/arrays'; import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; import URI from 'vs/base/common/uri'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -74,10 +74,10 @@ export abstract class BaseWorkspacesAction extends Action { } protected pickFolders(buttonLabel: string, title: string): string[] { - const workspace = this.contextService.getWorkspace(); let defaultPath: string; - if (workspace && workspace.roots.length > 0) { - defaultPath = dirname(workspace.roots[0].fsPath); // pick the parent of the first root by default + const workspace = this.contextService.getWorkspace(); + if (workspace.folders.length > 0) { + defaultPath = dirname(workspace.folders[0].fsPath); // pick the parent of the first root by default } return this.windowService.showOpenDialog({ @@ -108,22 +108,16 @@ export class AddRootFolderAction extends BaseWorkspacesAction { } public run(): TPromise { - if (!this.contextService.hasWorkspace()) { - return this.instantiationService.createInstance(NewWorkspaceAction, NewWorkspaceAction.ID, NewWorkspaceAction.LABEL, []).run(); + if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { + const folders = super.pickFolders(mnemonicButtonLabel(nls.localize({ key: 'add', comment: ['&& denotes a mnemonic'] }, "&&Add")), nls.localize('addFolderToWorkspaceTitle', "Add Folder to Workspace")); + if (!folders || !folders.length) { + return TPromise.as(null); + } + return this.workspaceEditingService.addFolders(folders.map(folder => URI.file(folder))).then(() => { + return this.viewletService.openViewlet(this.viewletService.getDefaultViewletId(), true); + }); } - - if (this.contextService.hasFolderWorkspace()) { - return this.instantiationService.createInstance(NewWorkspaceAction, NewWorkspaceAction.ID, NewWorkspaceAction.LABEL, this.contextService.getWorkspace().roots).run(); - } - - const folders = super.pickFolders(mnemonicButtonLabel(nls.localize({ key: 'add', comment: ['&& denotes a mnemonic'] }, "&&Add")), nls.localize('addFolderToWorkspaceTitle', "Add Folder to Workspace")); - if (!folders || !folders.length) { - return TPromise.as(null); - } - - return this.workspaceEditingService.addRoots(folders.map(folder => URI.file(folder))).then(() => { - return this.viewletService.openViewlet(this.viewletService.getDefaultViewletId(), true); - }); + return this.instantiationService.createInstance(NewWorkspaceAction, NewWorkspaceAction.ID, NewWorkspaceAction.LABEL, this.contextService.getWorkspace().folders).run(); } } @@ -176,7 +170,7 @@ export class RemoveRootFolderAction extends Action { } public run(): TPromise { - return this.workspaceEditingService.removeRoots([this.rootUri]); + return this.workspaceEditingService.removeFolders([this.rootUri]); } } @@ -199,42 +193,35 @@ export class SaveWorkspaceAsAction extends BaseWorkspacesAction { } public run(): TPromise { - if (!this.contextService.hasWorkspace()) { + const workspaceState = this.contextService.getWorkbenchState(); + if (workspaceState === WorkbenchState.EMPTY) { this.messageService.show(Severity.Info, nls.localize('saveEmptyWorkspaceNotSupported', "Please open a workspace first to save.")); return TPromise.as(null); } const configPath = this.getNewWorkspaceConfigPath(); if (configPath) { - if (this.contextService.hasFolderWorkspace()) { - return this.saveFolderWorkspace(configPath); - } + switch (workspaceState) { - if (this.contextService.hasMultiFolderWorkspace()) { - return this.saveWorkspace(configPath); + case WorkbenchState.FOLDER: + const workspaceFolders = this.contextService.getWorkspace().folders.map(root => root.fsPath); + return this.windowService.createAndOpenWorkspace(workspaceFolders, configPath); + + case WorkbenchState.WORKSPACE: + return this.windowService.saveAndOpenWorkspace(configPath); } } return TPromise.as(null); } - private saveWorkspace(configPath: string): TPromise { - return this.windowService.saveAndOpenWorkspace(configPath); - } - - private saveFolderWorkspace(configPath: string): TPromise { - const workspaceFolders = this.contextService.getWorkspace().roots.map(root => root.fsPath); - - return this.windowService.createAndOpenWorkspace(workspaceFolders, configPath); - } - private getNewWorkspaceConfigPath(): string { const workspace = this.contextService.getWorkspace(); let defaultPath: string; - if (this.contextService.hasMultiFolderWorkspace() && !this.isUntitledWorkspace(workspace.configuration.fsPath)) { + if (workspace.configuration && !this.isUntitledWorkspace(workspace.configuration.fsPath)) { defaultPath = workspace.configuration.fsPath; - } else if (workspace && workspace.roots.length > 0) { - defaultPath = dirname(workspace.roots[0].fsPath); // pick the parent of the first root by default + } else if (workspace.folders.length > 0) { + defaultPath = dirname(workspace.folders[0].fsPath); // pick the parent of the first root by default } return this.windowService.showSaveDialog({ @@ -281,7 +268,7 @@ export class OpenWorkspaceConfigFileAction extends Action { ) { super(id, label); - this.enabled = this.workspaceContextService.hasMultiFolderWorkspace(); + this.enabled = !!this.workspaceContextService.getWorkspace().configuration; } public run(): TPromise { diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index 655bec21217..66922084d46 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -12,7 +12,7 @@ import { IExtensionService } from 'vs/platform/extensions/common/extensions'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IEditorInput } from 'vs/platform/editor/common/editor'; import { toResource } from 'vs/workbench/common/editor'; -import { getPathLabel, IRootProvider } from 'vs/base/common/labels'; +import { getPathLabel, IWorkspaceFolderProvider } from 'vs/base/common/labels'; import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -211,9 +211,9 @@ export class FileLabel extends ResourceLabel { public setFile(resource: uri, options?: IFileLabelOptions): void { const hidePath = (options && options.hidePath) || (resource.scheme === Schemas.untitled && !this.untitledEditorService.hasAssociatedFilePath(resource)); - const rootProvider: IRootProvider = (options && options.root) ? { - getRoot(): uri { return options.root; }, - getWorkspace(): { roots: uri[]; } { return { roots: [options.root] }; }, + const rootProvider: IWorkspaceFolderProvider = (options && options.root) ? { + getWorkspaceFolder(): uri { return options.root; }, + getWorkspace(): { folders: uri[]; } { return { folders: [options.root] }; }, } : this.contextService; this.setLabel({ diff --git a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts index ab33817c228..10d32871c4b 100644 --- a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts +++ b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts @@ -111,6 +111,7 @@ export class QuickOpenController extends Component implements IQuickOpenService @IInstantiationService private instantiationService: IInstantiationService, @IPartService private partService: IPartService, @IListService private listService: IListService, + @IEnvironmentService private environmentService: IEnvironmentService, @IThemeService themeService: IThemeService ) { super(QuickOpenController.ID, themeService); @@ -138,7 +139,11 @@ export class QuickOpenController extends Component implements IQuickOpenService } private updateConfiguration(settings: IWorkbenchQuickOpenConfiguration): void { - this.closeOnFocusLost = settings.workbench && settings.workbench.quickOpen && settings.workbench.quickOpen.closeOnFocusLost; + if (this.environmentService.args['sticky-quickopen']) { + this.closeOnFocusLost = false; + } else { + this.closeOnFocusLost = settings.workbench && settings.workbench.quickOpen && settings.workbench.quickOpen.closeOnFocusLost; + } } public get onShow(): Event { diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts index 8131a18ab78..a83c9847408 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts @@ -28,7 +28,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { Action } from 'vs/base/common/actions'; import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { STATUS_BAR_BACKGROUND, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_ITEM_HOVER_BACKGROUND, STATUS_BAR_ITEM_ACTIVE_BACKGROUND, STATUS_BAR_PROMINENT_ITEM_BACKGROUND, STATUS_BAR_PROMINENT_ITEM_HOVER_BACKGROUND, STATUS_BAR_BORDER, STATUS_BAR_NO_FOLDER_FOREGROUND, STATUS_BAR_NO_FOLDER_BORDER } from 'vs/workbench/common/theme'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { isThemeColor } from 'vs/editor/common/editorCommon'; import { Color } from 'vs/base/common/color'; @@ -55,7 +55,7 @@ export class StatusbarPart extends Part implements IStatusbarService { } private registerListeners(): void { - this.toUnbind.push(this.contextService.onDidChangeWorkspaceRoots(() => this.updateStyles())); + this.toUnbind.push(this.contextService.onDidChangeWorkspaceFolders(() => this.updateStyles())); } public addEntry(entry: IStatusbarEntry, alignment: StatusbarAlignment, priority: number = 0): IDisposable { @@ -141,10 +141,10 @@ export class StatusbarPart extends Part implements IStatusbarService { const container = this.getContainer(); - container.style('color', this.getColor(this.contextService.hasWorkspace() ? STATUS_BAR_FOREGROUND : STATUS_BAR_NO_FOLDER_FOREGROUND)); - container.style('background-color', this.getColor(this.contextService.hasWorkspace() ? STATUS_BAR_BACKGROUND : STATUS_BAR_NO_FOLDER_BACKGROUND)); + container.style('color', this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_FOREGROUND : STATUS_BAR_NO_FOLDER_FOREGROUND)); + container.style('background-color', this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_BACKGROUND : STATUS_BAR_NO_FOLDER_BACKGROUND)); - const borderColor = this.getColor(this.contextService.hasWorkspace() ? STATUS_BAR_BORDER : STATUS_BAR_NO_FOLDER_BORDER) || this.getColor(contrastBorder); + const borderColor = this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_BORDER : STATUS_BAR_NO_FOLDER_BORDER) || this.getColor(contrastBorder); container.style('border-top-width', borderColor ? '1px' : null); container.style('border-top-style', borderColor ? 'solid' : null); container.style('border-top-color', borderColor); diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 15f9670b6c8..683183e95a3 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -27,7 +27,7 @@ import nls = require('vs/nls'); import * as labels from 'vs/base/common/labels'; import { EditorInput, toResource } from 'vs/workbench/common/editor'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { Verbosity } from 'vs/platform/editor/common/editor'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TITLE_BAR_ACTIVE_BACKGROUND, TITLE_BAR_ACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_BACKGROUND, TITLE_BAR_BORDER } from 'vs/workbench/common/theme'; @@ -101,7 +101,7 @@ export class TitlebarPart extends Part implements ITitleService { this.toUnbind.push(DOM.addDisposableListener(window, DOM.EventType.FOCUS, () => this.onFocus())); this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(() => this.onConfigurationChanged(true))); this.toUnbind.push(this.editorGroupService.onEditorsChanged(() => this.onEditorsChanged())); - this.toUnbind.push(this.contextService.onDidChangeWorkspaceRoots(() => this.onDidChangeWorkspaceRoots())); + this.toUnbind.push(this.contextService.onDidChangeWorkspaceFolders(() => this.onDidChangeWorkspaceFolders())); this.toUnbind.push(this.contextService.onDidChangeWorkspaceName(() => this.onDidChangeWorkspaceName())); } @@ -115,7 +115,7 @@ export class TitlebarPart extends Part implements ITitleService { this.updateStyles(); } - private onDidChangeWorkspaceRoots(): void { + private onDidChangeWorkspaceFolders(): void { this.setTitle(this.getWindowTitle()); } @@ -192,34 +192,17 @@ export class TitlebarPart extends Part implements ITitleService { const input = this.editorService.getActiveEditorInput(); const workspace = this.contextService.getWorkspace(); - // Compute root resource - // Single Root Workspace: always the single root workspace in this case - // Multi Root Workspace: workspace configuration file - let root: URI; - if (this.contextService.hasMultiFolderWorkspace()) { - root = workspace.configuration; - } else if (this.contextService.hasFolderWorkspace()) { - root = workspace.roots[0]; - } - // Compute folder resource // Single Root Workspace: always the root single workspace in this case - // Multi Root Workspace: root folder of the currently active file if any - let folder: URI; - if (workspace) { - if (workspace.roots.length === 1) { - folder = workspace.roots[0]; - } else { - folder = this.contextService.getRoot(toResource(input, { supportSideBySide: true, filter: 'file' })); - } - } + // Otherwise: root folder of the currently active file if any + let folder: URI = this.contextService.getWorkbenchState() === WorkbenchState.FOLDER ? workspace.folders[0] : this.contextService.getWorkspaceFolder(toResource(input, { supportSideBySide: true, filter: 'file' })); // Variables const activeEditorShort = input ? input.getTitle(Verbosity.SHORT) : ''; const activeEditorMedium = input ? input.getTitle(Verbosity.MEDIUM) : activeEditorShort; const activeEditorLong = input ? input.getTitle(Verbosity.LONG) : activeEditorMedium; - const rootName = workspace ? workspace.name : ''; - const rootPath = workspace ? labels.getPathLabel(root, void 0, this.environmentService) : ''; + const rootName = workspace.name; + const rootPath = labels.getPathLabel(workspace.configuration || workspace.folders[0], void 0, this.environmentService) || ''; const folderName = folder ? (paths.basename(folder.fsPath) || folder.fsPath) : ''; const folderPath = folder ? labels.getPathLabel(folder, void 0, this.environmentService) : ''; const dirty = input && input.isDirty() ? TitlebarPart.TITLE_DIRTY : ''; diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index 3fe40af888f..1c7c48aa596 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -28,7 +28,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND, SIDE_BAR_SECTION_HEADER_FOREGROUND, SIDE_BAR_SECTION_HEADER_BACKGROUND } from 'vs/workbench/common/theme'; @@ -739,11 +739,11 @@ export class PersistentViewsViewlet extends ViewsViewlet { } }); - this.storageService.store(this.viewletStateStorageId, JSON.stringify(viewsStates), this.contextService.hasWorkspace() ? StorageScope.WORKSPACE : StorageScope.GLOBAL); + this.storageService.store(this.viewletStateStorageId, JSON.stringify(viewsStates), this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? StorageScope.WORKSPACE : StorageScope.GLOBAL); } private loadViewsStates(): void { - const viewsStates = JSON.parse(this.storageService.get(this.viewletStateStorageId, this.contextService.hasWorkspace() ? StorageScope.WORKSPACE : StorageScope.GLOBAL, '{}')); + const viewsStates = JSON.parse(this.storageService.get(this.viewletStateStorageId, this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? StorageScope.WORKSPACE : StorageScope.GLOBAL, '{}')); Object.keys(viewsStates).forEach(id => this.viewsStates.set(id, viewsStates[id])); } } \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/views/views2.ts b/src/vs/workbench/browser/parts/views/views2.ts new file mode 100644 index 00000000000..e6592e5a305 --- /dev/null +++ b/src/vs/workbench/browser/parts/views/views2.ts @@ -0,0 +1,238 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { TPromise } from 'vs/base/common/winjs.base'; +import Event, { Emitter } from 'vs/base/common/event'; +import { attachStyler } from 'vs/platform/theme/common/styler'; +import { Dimension, Builder } from 'vs/base/browser/builder'; +import { append, $ } from 'vs/base/browser/dom'; +import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; +import { firstIndex } from 'vs/base/common/arrays'; +import { IAction, IActionRunner } from 'vs/base/common/actions'; +import { IActionItem, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { prepareActions } from 'vs/workbench/browser/actions'; +import { Viewlet, ViewletRegistry, Extensions } from 'vs/workbench/browser/viewlet'; +import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/common/theme'; +import { PanelView, IPanelOptions, Panel } from 'vs/base/browser/ui/splitview/panelview'; + +export interface IViewletPanelOptions extends IPanelOptions { + actionRunner?: IActionRunner; +} + +export abstract class ViewletPanel extends Panel { + + private _onDidFocus = new Emitter(); + readonly onDidFocus: Event = this._onDidFocus.event; + + private actionRunner: IActionRunner; + private toolbar: ToolBar; + + constructor( + readonly name: string, + initialSize: number, + options: IViewletPanelOptions, + protected keybindingService: IKeybindingService, + protected contextMenuService: IContextMenuService + ) { + super(options); + + this.actionRunner = options.actionRunner; + } + + render(container: HTMLElement): void { + super.render(container); + + const title = append(this.header, $('div.title')); + title.textContent = this.name; + + const actions = append(this.header, $('div.actions')); + this.toolbar = new ToolBar(actions, this.contextMenuService, { + orientation: ActionsOrientation.HORIZONTAL, + actionItemProvider: action => this.getActionItem(action), + ariaLabel: nls.localize('viewToolbarAriaLabel', "{0} actions", this.name), + getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id), + actionRunner: this.actionRunner + }); + + this.disposables.push(this.toolbar); + this.updateActions(); + } + + focus(): void { + super.focus(); + this._onDidFocus.fire(); + } + + protected updateActions(): void { + this.toolbar.setActions(prepareActions(this.getActions()), prepareActions(this.getSecondaryActions()))(); + this.toolbar.context = this.getActionsContext(); + } + + getActions(): IAction[] { + return []; + } + + getSecondaryActions(): IAction[] { + return []; + } + + getActionItem(action: IAction): IActionItem { + return null; + } + + getActionsContext(): any { + return undefined; + } + + getOptimalWidth(): number { + return 0; + } +} + +export interface IViewsViewletOptions { + showHeaderInTitleWhenSingleView: boolean; +} + +const SplitViewThemeMapping = { + dropBackground: SIDE_BAR_DRAG_AND_DROP_BACKGROUND +}; + +interface IViewletPanelItem { + panel: ViewletPanel; + disposable: IDisposable; +} + +export class PanelViewlet extends Viewlet { + + protected lastFocusedPanel: ViewletPanel | undefined; + private panelItems: IViewletPanelItem[] = []; + private panelview: PanelView; + + protected get isSingleView(): boolean { + return this.options.showHeaderInTitleWhenSingleView && this.panelItems.length === 1; + } + + protected get length(): number { + return this.panelItems.length; + } + + constructor( + id: string, + private options: Partial, + @ITelemetryService telemetryService: ITelemetryService, + @IThemeService themeService: IThemeService + ) { + super(id, telemetryService, themeService); + } + + async create(parent: Builder): TPromise { + super.create(parent); + + const container = parent.getHTMLElement(); + this.panelview = this._register(new PanelView(container)); + this._register(attachStyler(this.themeService, SplitViewThemeMapping, this.panelview)); + // this._register(this.panelview.onFocus(view => this.lastFocusedView = view)); + } + + getTitle(): string { + let title = Registry.as(Extensions.Viewlets).getViewlet(this.getId()).name; + + if (this.isSingleView) { + title += ': ' + this.panelItems[0].panel.name; + } + + return title; + } + + getActions(): IAction[] { + if (this.isSingleView) { + return this.panelItems[0].panel.getActions(); + } + + return []; + } + + getSecondaryActions(): IAction[] { + if (this.isSingleView) { + return this.panelItems[0].panel.getSecondaryActions(); + } + + return []; + } + + focus(): void { + super.focus(); + + if (this.lastFocusedPanel) { + this.lastFocusedPanel.focus(); + } else if (this.panelItems.length > 0) { + this.panelItems[0].panel.focus(); + } + } + + layout(dimension: Dimension): void { + this.panelview.layout(dimension.height); + } + + getOptimalWidth(): number { + const sizes = this.panelItems + .map(panelItem => panelItem.panel.getOptimalWidth() || 0); + + return Math.max(...sizes); + } + + addView(panel: ViewletPanel, index = this.panelItems.length - 1): void { + const disposables: IDisposable[] = []; + const onDidFocus = panel.onDidFocus(() => this.lastFocusedPanel = panel, null, disposables); + const disposable = combinedDisposable([onDidFocus]); + const panelItem: IViewletPanelItem = { panel, disposable }; + + this.panelItems.splice(index, 0, panelItem); + this.panelview.addPanel(panel, 200, index); + + this.updateViewHeaders(); + this.updateTitleArea(); + } + + removeView(panel: ViewletPanel): void { + const index = firstIndex(this.panelItems, i => i.panel === panel); + + if (index === -1) { + return; + } + + if (this.lastFocusedPanel === panel) { + this.lastFocusedPanel = undefined; + } + + this.panelview.removePanel(panel); + const [panelItem] = this.panelItems.splice(index, 1); + panelItem.disposable.dispose(); + + this.updateViewHeaders(); + this.updateTitleArea(); + } + + private updateViewHeaders(): void { + if (this.isSingleView) { + this.panelItems[0].panel.headerVisible = false; + } else { + this.panelItems.forEach(i => i.panel.headerVisible = true); + } + } + + dispose(): void { + super.dispose(); + this.panelItems.forEach(i => i.disposable.dispose()); + this.panelview.dispose(); + } +} \ No newline at end of file diff --git a/src/vs/workbench/common/resources.ts b/src/vs/workbench/common/resources.ts index 830f77fd26f..d8ae21d82d3 100644 --- a/src/vs/workbench/common/resources.ts +++ b/src/vs/workbench/common/resources.ts @@ -91,14 +91,14 @@ export class ResourceGlobMatcher { private registerListeners(): void { this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(() => this.onConfigurationChanged())); - this.toUnbind.push(this.contextService.onDidChangeWorkspaceRoots(() => this.onDidChangeWorkspaceRoots())); + this.toUnbind.push(this.contextService.onDidChangeWorkspaceFolders(() => this.onDidChangeWorkspaceFolders())); } private onConfigurationChanged(): void { this.updateExcludes(true); } - private onDidChangeWorkspaceRoots(): void { + private onDidChangeWorkspaceFolders(): void { this.updateExcludes(true); } @@ -106,17 +106,15 @@ export class ResourceGlobMatcher { let changed = false; // Add excludes per workspaces that got added - if (this.contextService.hasWorkspace()) { - this.contextService.getWorkspace().roots.forEach(root => { - const rootExcludes = this.globFn(root); - if (!this.mapRootToExpressionConfig.has(root.toString()) || !objects.equals(this.mapRootToExpressionConfig.get(root.toString()), rootExcludes)) { - changed = true; + this.contextService.getWorkspace().folders.forEach(folder => { + const rootExcludes = this.globFn(folder); + if (!this.mapRootToExpressionConfig.has(folder.toString()) || !objects.equals(this.mapRootToExpressionConfig.get(folder.toString()), rootExcludes)) { + changed = true; - this.mapRootToParsedExpression.set(root.toString(), this.parseFn(rootExcludes)); - this.mapRootToExpressionConfig.set(root.toString(), objects.clone(rootExcludes)); - } - }); - } + this.mapRootToParsedExpression.set(folder.toString(), this.parseFn(rootExcludes)); + this.mapRootToExpressionConfig.set(folder.toString(), objects.clone(rootExcludes)); + } + }); // Remove excludes per workspace no longer present this.mapRootToExpressionConfig.forEach((value, root) => { @@ -124,7 +122,7 @@ export class ResourceGlobMatcher { return; // always keep this one } - if (!this.contextService.getRoot(URI.parse(root))) { + if (!this.contextService.getWorkspaceFolder(URI.parse(root))) { this.mapRootToParsedExpression.delete(root); this.mapRootToExpressionConfig.delete(root); @@ -147,7 +145,7 @@ export class ResourceGlobMatcher { } public matches(resource: URI): boolean { - const root = this.contextService.getRoot(resource); + const root = this.contextService.getWorkspaceFolder(resource); let expressionForRoot: ParsedExpression; if (root && this.mapRootToParsedExpression.has(root.toString())) { diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions.ts index 2f7d94696bc..67796b9bc2f 100644 --- a/src/vs/workbench/electron-browser/actions.ts +++ b/src/vs/workbench/electron-browser/actions.ts @@ -18,7 +18,7 @@ import product from 'vs/platform/node/product'; import pkg from 'vs/platform/node/package'; import errors = require('vs/base/common/errors'); import { IMessageService, Severity } from 'vs/platform/message/common/message'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -104,7 +104,7 @@ export class CloseWorkspaceAction extends Action { } run(): TPromise { - if (!this.contextService.hasWorkspace()) { + if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { this.messageService.show(Severity.Info, nls.localize('noWorkspaceOpened', "There is currently no workspace opened in this instance to close.")); return TPromise.as(null); @@ -759,23 +759,12 @@ export abstract class BaseOpenRecentAction extends Action { const workspacePicks: IFilePickOpenEntry[] = recentWorkspaces.map((workspace, index) => toPick(workspace, index === 0 ? { label: nls.localize('workspaces', "workspaces") } : void 0, isSingleFolderWorkspaceIdentifier(workspace) ? FileKind.FOLDER : FileKind.ROOT_FOLDER, this.environmentService, !this.isQuickNavigate() ? this.removeAction : void 0)); const filePicks: IFilePickOpenEntry[] = recentFiles.map((p, index) => toPick(p, index === 0 ? { label: nls.localize('files', "files"), border: true } : void 0, FileKind.FILE, this.environmentService, !this.isQuickNavigate() ? this.removeAction : void 0)); - let isCurrentWorkspaceInList: boolean; - if (!this.contextService.hasWorkspace()) { - isCurrentWorkspaceInList = false; // we never show empty workspaces - } else if (this.contextService.hasFolderWorkspace()) { - isCurrentWorkspaceInList = true; // we always show folder workspaces - } else { - const firstWorkspace = recentWorkspaces[0]; - if (firstWorkspace && !isSingleFolderWorkspaceIdentifier(firstWorkspace)) { - isCurrentWorkspaceInList = firstWorkspace.id === this.contextService.getWorkspace().id; - } else { - isCurrentWorkspaceInList = false; // this is an untitled workspace thereby - } - } + // focus second entry if the first recent workspace is the current workspace + let autoFocusSecondEntry: boolean = recentWorkspaces[0] && this.contextService.isCurrentWorkspace(recentWorkspaces[0]); this.quickOpenService.pick([...workspacePicks, ...filePicks], { contextKey: inRecentFilesPickerContextKey, - autoFocus: { autoFocusFirstEntry: !isCurrentWorkspaceInList, autoFocusSecondEntry: isCurrentWorkspaceInList }, + autoFocus: { autoFocusFirstEntry: !autoFocusSecondEntry, autoFocusSecondEntry: autoFocusSecondEntry }, placeHolder: isMacintosh ? nls.localize('openRecentPlaceHolderMac', "Select to open (hold Cmd-key to open in new window)") : nls.localize('openRecentPlaceHolder', "Select to open (hold Ctrl-key to open in new window)"), matchOnDescription: true, quickNavigateConfiguration: this.isQuickNavigate() ? { keybindings: this.keybindingService.lookupKeybindings(this.id) } : void 0 diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts index f1b566e63da..70cbf4e7b09 100644 --- a/src/vs/workbench/electron-browser/main.ts +++ b/src/vs/workbench/electron-browser/main.ts @@ -16,13 +16,12 @@ import platform = require('vs/base/common/platform'); import paths = require('vs/base/common/paths'); import uri from 'vs/base/common/uri'; import strings = require('vs/base/common/strings'); -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, Workspace, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { EmptyWorkspaceServiceImpl, WorkspaceServiceImpl, WorkspaceService } from 'vs/workbench/services/configuration/node/configuration'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { realpath } from 'vs/base/node/pfs'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; -import path = require('path'); import gracefulFs = require('graceful-fs'); import { IInitData } from 'vs/workbench/services/timer/common/timerService'; import { TimerService } from 'vs/workbench/services/timer/node/timerService'; @@ -79,8 +78,8 @@ function openWorkbench(configuration: IWindowConfiguration): TPromise { // Since the configuration service is one of the core services that is used in so many places, we initialize it // right before startup of the workbench shell to have its data ready for consumers return createAndInitializeWorkspaceService(configuration, environmentService, mainServices.get(IWorkspacesService)).then(workspaceService => { - const timerService = new TimerService((window).MonacoEnvironment.timers as IInitData, !workspaceService.hasWorkspace()); - const storageService = createStorageService(configuration, workspaceService, environmentService); + const timerService = new TimerService((window).MonacoEnvironment.timers as IInitData, workspaceService.getWorkbenchState() === WorkbenchState.EMPTY); + const storageService = createStorageService(workspaceService, environmentService); timerService.beforeDOMContentLoaded = Date.now(); @@ -116,10 +115,10 @@ function createAndInitializeWorkspaceService(configuration: IWindowConfiguration if (configuration.workspace || configuration.folderPath) { workspaceService = new WorkspaceServiceImpl(configuration.workspace || configuration.folderPath, environmentService, workspacesService); } else { - workspaceService = new EmptyWorkspaceServiceImpl(environmentService); + workspaceService = new EmptyWorkspaceServiceImpl(configuration, environmentService); } - return workspaceService.initialize().then(() => workspaceService, error => new EmptyWorkspaceServiceImpl(environmentService)); + return workspaceService.initialize().then(() => workspaceService, error => new EmptyWorkspaceServiceImpl(configuration, environmentService)); }); } @@ -147,33 +146,35 @@ function validateWorkspacePath(configuration: IWindowConfiguration): TPromiseworkspaceService.getWorkspace(); + workspaceId = workspace.folders[0].toString(); + secondaryWorkspaceId = workspace.ctime; + break; + + // finaly, if we do not have a workspace open, we need to find another identifier for the window to store + // workspace UI state. if we have a backup path in the configuration we can use that because this + // will be a unique identifier per window that is stable between restarts as long as there are + // dirty files in the workspace. + // We use basename() to produce a short identifier, we do not need the full path. We use a custom + // scheme so that we can later distinguish these identifiers from the workspace one. + case WorkbenchState.EMPTY: + workspaceId = workspaceService.getWorkspace().id; + break; } const disableStorage = !!environmentService.extensionTestsPath; // never keep any state when running extension tests! diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts index f6bec8be10f..e188d34337d 100644 --- a/src/vs/workbench/electron-browser/shell.ts +++ b/src/vs/workbench/electron-browser/shell.ts @@ -63,7 +63,7 @@ import { ChoiceChannel } from 'vs/platform/message/common/messageIpc'; import { ISearchService } from 'vs/platform/search/common/search'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { CommandService } from 'vs/platform/commands/common/commandService'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IExtensionService } from 'vs/platform/extensions/common/extensions'; import { WorkbenchModeServiceImpl } from 'vs/workbench/services/mode/common/workbenchModeService'; import { IModeService } from 'vs/editor/common/services/modeService'; @@ -202,7 +202,7 @@ export class WorkbenchShell { this.telemetryService.publicLog('workspaceLoad', { userAgent: navigator.userAgent, windowSize: { innerHeight: window.innerHeight, innerWidth: window.innerWidth, outerHeight: window.outerHeight, outerWidth: window.outerWidth }, - emptyWorkbench: !this.contextService.hasWorkspace(), + emptyWorkbench: this.contextService.getWorkbenchState() === WorkbenchState.EMPTY, 'workbench.filesToOpen': filesToOpen && filesToOpen.length || void 0, 'workbench.filesToCreate': filesToCreate && filesToCreate.length || void 0, 'workbench.filesToDiff': filesToDiff && filesToDiff.length || void 0, diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index 947b60ef708..e3307c35999 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -39,7 +39,7 @@ import { IExtensionService } from 'vs/platform/extensions/common/extensions'; import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/keybindingService'; import { Themable } from 'vs/workbench/common/theme'; import { ipcRenderer as ipc, webFrame } from 'electron'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; const TextInputActions: IAction[] = [ @@ -288,18 +288,13 @@ export class ElectronWindow extends Themable { const foldersToAdd = request.foldersToAdd.map(folderToAdd => URI.file(folderToAdd.filePath)); // Workspace: just add to workspace config - if (this.contextService.hasMultiFolderWorkspace()) { - this.workspaceEditingService.addRoots(foldersToAdd).done(null, errors.onUnexpectedError); + if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { + this.workspaceEditingService.addFolders(foldersToAdd).done(null, errors.onUnexpectedError); } // Single folder or no workspace: create workspace and open else { - const workspaceFolders: URI[] = []; - - // Folder of workspace is the first of multi root workspace, so add it - if (this.contextService.hasFolderWorkspace()) { - workspaceFolders.push(...this.contextService.getWorkspace().roots); - } + const workspaceFolders: URI[] = [...this.contextService.getWorkspace().folders]; // Fill in remaining ones from request workspaceFolders.push(...request.foldersToAdd.map(folderToAdd => URI.file(folderToAdd.filePath))); diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index 6fe5bb86fa8..c2a5865957f 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -41,7 +41,7 @@ import { QuickOpenController } from 'vs/workbench/browser/parts/quickopen/quickO import { getServices } from 'vs/platform/instantiation/common/extensions'; import { WorkbenchEditorService } from 'vs/workbench/services/editor/browser/editorService'; import { Position, Parts, IPartService, ILayoutOptions } from 'vs/workbench/services/part/common/partService'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ContextMenuService } from 'vs/workbench/services/contextview/electron-browser/contextmenuService'; import { WorkbenchKeybindingService } from 'vs/workbench/services/keybinding/electron-browser/keybindingService'; @@ -444,7 +444,7 @@ export class Workbench implements IPartService { } // Empty workbench - else if (!this.contextService.hasWorkspace() && this.openUntitledFile()) { + else if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY && this.openUntitledFile()) { if (this.editorPart.hasEditorsToRestore()) { return TPromise.as([]); // do not open any empty untitled file if we have editors to restore } @@ -635,7 +635,7 @@ export class Workbench implements IPartService { private initSettings(): void { // Sidebar visibility - this.sideBarHidden = this.storageService.getBoolean(Workbench.sidebarHiddenSettingKey, StorageScope.WORKSPACE, !this.contextService.hasWorkspace()); + this.sideBarHidden = this.storageService.getBoolean(Workbench.sidebarHiddenSettingKey, StorageScope.WORKSPACE, this.contextService.getWorkbenchState() === WorkbenchState.EMPTY); // Panel part visibility const panelRegistry = Registry.as(PanelExtensions.Panels); @@ -830,7 +830,7 @@ export class Workbench implements IPartService { return promise.then(() => { // Remember in settings - const defaultHidden = !this.contextService.hasWorkspace(); + const defaultHidden = this.contextService.getWorkbenchState() === WorkbenchState.EMPTY; if (hidden !== defaultHidden) { this.storageService.store(Workbench.sidebarHiddenSettingKey, hidden ? 'true' : 'false', StorageScope.WORKSPACE); } else { @@ -1054,7 +1054,7 @@ export class Workbench implements IPartService { // Close when empty: check if we should close the window based on the setting // Overruled by: window has a workspace opened or this window is for extension development // or setting is disabled. Also enabled when running with --wait from the command line. - if (visibleEditors === 0 && !this.contextService.hasWorkspace() && !this.environmentService.isExtensionDevelopment) { + if (visibleEditors === 0 && this.contextService.getWorkbenchState() === WorkbenchState.EMPTY && !this.environmentService.isExtensionDevelopment) { const closeWhenEmpty = this.configurationService.lookup(Workbench.closeWhenEmptyConfigurationKey).value; if (closeWhenEmpty || this.environmentService.args.wait) { this.closeEmptyWindowScheduler.schedule(); diff --git a/src/vs/workbench/node/extensionHostMain.ts b/src/vs/workbench/node/extensionHostMain.ts index b798a89d457..9bd3d58ff3d 100644 --- a/src/vs/workbench/node/extensionHostMain.ts +++ b/src/vs/workbench/node/extensionHostMain.ts @@ -145,7 +145,7 @@ export class ExtensionHostMain { } private handleWorkspaceContainsEagerExtensions(): TPromise { - if (!this._workspace || this._workspace.roots.length === 0) { + if (!this._workspace || this._workspace.folders.length === 0) { return TPromise.as(null); } @@ -177,7 +177,7 @@ export class ExtensionHostMain { } const query: ISearchQuery = { - folderQueries: this._workspace.roots.map(root => ({ folder: root })), + folderQueries: this._workspace.folders.map(root => ({ folder: root })), type: QueryType.File, maxResults: 1, includePattern: { [p]: true } @@ -187,7 +187,7 @@ export class ExtensionHostMain { } else { // find exact path return (async resolve => { - for (const { fsPath } of this._workspace.roots) { + for (const { fsPath } of this._workspace.folders) { if (await pfs.exists(join(fsPath, p))) { return p; } diff --git a/src/vs/workbench/parts/debug/browser/debugActions.ts b/src/vs/workbench/parts/debug/browser/debugActions.ts index b462d58e460..4b53aa781f5 100644 --- a/src/vs/workbench/parts/debug/browser/debugActions.ts +++ b/src/vs/workbench/parts/debug/browser/debugActions.ts @@ -11,7 +11,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IFileService } from 'vs/platform/files/common/files'; import { IMessageService } from 'vs/platform/message/common/message'; import { IDebugService, State, IProcess, IThread, IEnablement, IBreakpoint, IStackFrame, IFunctionBreakpoint, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, IExpression, REPL_ID, ProcessState } @@ -98,7 +98,7 @@ export class ConfigureAction extends AbstractDebugAction { } public run(event?: any): TPromise { - if (!this.contextService.hasWorkspace()) { + if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { this.messageService.show(severity.Info, nls.localize('noFolderDebugConfig', "Please first open a folder in order to do advanced debug configuration.")); return TPromise.as(null); } @@ -140,7 +140,7 @@ export class StartAction extends AbstractDebugAction { if (state === State.Initializing) { return false; } - if (this.contextService && !this.contextService.hasWorkspace() && processes.length > 0) { + if (this.contextService && this.contextService.getWorkbenchState() === WorkbenchState.EMPTY && processes.length > 0) { return false; } if (processes.some(p => p.getName(false) === selectedName && (!launch || p.session.root.toString() === launch.workspaceUri.toString()))) { diff --git a/src/vs/workbench/parts/debug/browser/debugContentProvider.ts b/src/vs/workbench/parts/debug/browser/debugContentProvider.ts index ad96768d0d5..ccf2795916f 100644 --- a/src/vs/workbench/parts/debug/browser/debugContentProvider.ts +++ b/src/vs/workbench/parts/debug/browser/debugContentProvider.ts @@ -32,13 +32,21 @@ export class DebugContentProvider implements IWorkbenchContribution, ITextModelC public provideTextContent(resource: uri): TPromise { let process: IProcess; + let sourceRef: number; + if (resource.query) { const keyvalues = resource.query.split('&'); for (let keyvalue of keyvalues) { const pair = keyvalue.split('='); - if (pair.length === 2 && pair[0] === 'session') { - process = this.debugService.findProcessByUUID(decodeURIComponent(pair[1])); - break; + if (pair.length === 2) { + switch (pair[0]) { + case 'session': + process = this.debugService.findProcessByUUID(decodeURIComponent(pair[1])); + break; + case 'sourceRef': + sourceRef = parseInt(pair[1]); + break; + } } } } @@ -55,18 +63,26 @@ export class DebugContentProvider implements IWorkbenchContribution, ITextModelC let rawSource: DebugProtocol.Source; if (source) { rawSource = source.raw; + if (!sourceRef) { + sourceRef = source.reference; + } } else { - // Remove debug: scheme - rawSource = { path: resource.with({ scheme: '', query: '' }).toString(true) }; + // create a Source + rawSource = { + path: resource.with({ scheme: '', query: '' }).toString(true), // Remove debug: scheme + sourceReference: sourceRef + }; } - return process.session.source({ sourceReference: source ? source.reference : undefined, source: rawSource }).then(response => { + return process.session.source({ sourceReference: sourceRef, source: rawSource }).then(response => { + const mime = response.body.mimeType || guessMimeTypes(resource.toString())[0]; const modePromise = this.modeService.getOrCreateMode(mime); const model = this.modelService.createModel(response.body.content, modePromise, resource); return model; }, (err: DebugProtocol.ErrorResponse) => { + this.debugService.sourceIsNotAvailable(resource); const modePromise = this.modeService.getOrCreateMode(MIME_TEXT); const model = this.modelService.createModel(err.message, modePromise, resource); diff --git a/src/vs/workbench/parts/debug/browser/debugViewlet.ts b/src/vs/workbench/parts/debug/browser/debugViewlet.ts index 98ac337283e..d0ff9fd24e1 100644 --- a/src/vs/workbench/parts/debug/browser/debugViewlet.ts +++ b/src/vs/workbench/parts/debug/browser/debugViewlet.ts @@ -16,7 +16,7 @@ import { StartDebugActionItem } from 'vs/workbench/parts/debug/browser/debugActi import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionService } from 'vs/platform/extensions/common/extensions'; import { IProgressService, IProgressRunner } from 'vs/platform/progress/common/progress'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -56,10 +56,6 @@ export class DebugViewlet extends PersistentViewsViewlet { public focus(): void { super.focus(); - if (!this.contextService.hasWorkspace()) { - this.views[0].focusBody(); - } - if (this.startDebugActionItem) { this.startDebugActionItem.focus(); } @@ -69,7 +65,7 @@ export class DebugViewlet extends PersistentViewsViewlet { if (!this.actions) { this.actions = []; this.actions.push(this.instantiationService.createInstance(StartAction, StartAction.ID, StartAction.LABEL)); - if (this.contextService.hasWorkspace()) { + if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY) { this.actions.push(this.instantiationService.createInstance(ConfigureAction, ConfigureAction.ID, ConfigureAction.LABEL)); } this.actions.push(this._register(this.instantiationService.createInstance(ToggleReplAction, ToggleReplAction.ID, ToggleReplAction.LABEL))); @@ -83,7 +79,7 @@ export class DebugViewlet extends PersistentViewsViewlet { } public getActionItem(action: IAction): IActionItem { - if (action.id === StartAction.ID && this.contextService.hasWorkspace()) { + if (action.id === StartAction.ID && this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY) { this.startDebugActionItem = this.instantiationService.createInstance(StartDebugActionItem, null, action); return this.startDebugActionItem; } diff --git a/src/vs/workbench/parts/debug/browser/exceptionWidget.ts b/src/vs/workbench/parts/debug/browser/exceptionWidget.ts index 3719a86393b..cbb4c4e8bd9 100644 --- a/src/vs/workbench/parts/debug/browser/exceptionWidget.ts +++ b/src/vs/workbench/parts/debug/browser/exceptionWidget.ts @@ -21,7 +21,7 @@ const $ = dom.$; // theming export const debugExceptionWidgetBorder = registerColor('debugExceptionWidget.border', { dark: '#a31515', light: '#a31515', hc: '#a31515' }, nls.localize('debugExceptionWidgetBorder', 'Exception widget border color.')); -export const debugExceptionWidgetBackground = registerColor('debugExceptionWidget.background', { dark: '#a3151540', light: '#a315150d', hc: '#a3151573' }, nls.localize('debugExceptionWidgetBackground', 'Exception widget background color.')); +export const debugExceptionWidgetBackground = registerColor('debugExceptionWidget.background', { dark: '#420b0d', light: '#f1dfde', hc: '#420b0d' }, nls.localize('debugExceptionWidgetBackground', 'Exception widget background color.')); export class ExceptionWidget extends ZoneWidget { diff --git a/src/vs/workbench/parts/debug/electron-browser/debugCommands.ts b/src/vs/workbench/parts/debug/electron-browser/debugCommands.ts index c1824de5b62..336c9d2fe87 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugCommands.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugCommands.ts @@ -15,7 +15,7 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IListService } from 'vs/platform/list/browser/listService'; import { IMessageService } from 'vs/platform/message/common/message'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IDebugService, IConfig, IEnablement, CONTEXT_NOT_IN_DEBUG_MODE, CONTEXT_IN_DEBUG_MODE, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution } from 'vs/workbench/parts/debug/common/debug'; import { Expression, Variable, Breakpoint, FunctionBreakpoint } from 'vs/workbench/parts/debug/common/debugModel'; import { IExtensionsViewlet, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/parts/extensions/common/extensions'; @@ -204,7 +204,7 @@ export function registerCommands(): void { primary: undefined, handler: (accessor, workspaceUri: string) => { const manager = accessor.get(IDebugService).getConfigurationManager(); - if (!accessor.get(IWorkspaceContextService).hasWorkspace()) { + if (accessor.get(IWorkspaceContextService).getWorkbenchState() === WorkbenchState.EMPTY) { accessor.get(IMessageService).show(severity.Info, nls.localize('noFolderDebugConfig', "Please first open a folder in order to do advanced debug configuration.")); return TPromise.as(null); } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts index 980714a1412..5aa8b1e8f0a 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts @@ -341,7 +341,7 @@ export class ConfigurationManager implements IConfigurationManager { }); }); - this.toDispose.push(this.contextService.onDidChangeWorkspaceRoots(() => { + this.toDispose.push(this.contextService.onDidChangeWorkspaceFolders(() => { this.initLaunches(); this.selectConfiguration(); })); @@ -353,8 +353,7 @@ export class ConfigurationManager implements IConfigurationManager { } private initLaunches(): void { - const workspace = this.contextService.getWorkspace(); - this.launches = workspace ? workspace.roots.map(root => this.instantiationService.createInstance(Launch, this, root)) : []; + this.launches = this.contextService.getWorkspace().folders.map(folder => this.instantiationService.createInstance(Launch, this, folder)); if (this.launches.indexOf(this._selectedLaunch) === -1) { this._selectedLaunch = undefined; } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts b/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts index 9a2d6bfe963..bcb12909cf6 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts @@ -365,7 +365,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { // First call stack frame that is available is the frame where exception has been thrown const exceptionSf = first(callStack, sf => sf.source && sf.source.available, undefined); - if (!exceptionSf) { + if (!exceptionSf || exceptionSf !== focusedSf) { this.closeExceptionWidget(); return; } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugHover.ts b/src/vs/workbench/parts/debug/electron-browser/debugHover.ts index 3af92756683..9dec498f49a 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugHover.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugHover.ts @@ -254,7 +254,8 @@ export class DebugHoverWidget implements IContentWidget { this.valueContainer.hidden = false; renderExpressionValue(expression, this.valueContainer, { showChanged: false, - preserveWhitespace: true + preserveWhitespace: true, + colorize: true }); this.valueContainer.title = ''; this.editor.layoutContentWidget(this); diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index 08ffc1b1b97..953b094abc8 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -46,7 +46,7 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ILogEntry, EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL, EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost'; import { IBroadcastService, IBroadcast } from 'vs/platform/broadcast/electron-browser/broadcastService'; @@ -365,7 +365,7 @@ export class DebugService implements debug.IDebugService { // 'Run without debugging' mode VSCode must terminate the extension host. More details: #3905 const process = this.viewModel.focusedProcess; if (process && session && process.getId() === session.getId() && strings.equalsIgnoreCase(process.configuration.type, 'extensionhost') && this.sessionStates.get(session.getId()) === debug.State.Running && - process && this.contextService.hasWorkspace() && process.configuration.noDebug) { + process && this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY && process.configuration.noDebug) { this.broadcastService.broadcast({ channel: EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, payload: [process.session.root.fsPath] @@ -759,7 +759,7 @@ export class DebugService implements debug.IDebugService { }); }); }, err => { - if (!this.contextService.hasWorkspace()) { + if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { this.messageService.show(severity.Error, nls.localize('noFolderWorkspaceDebugError', "The active file can not be debugged. Make sure it is saved on disk and that you have a debug extension installed for that file type.")); return undefined; } @@ -846,7 +846,7 @@ export class DebugService implements debug.IDebugService { this.panelService.openPanel(debug.REPL_ID, false).done(undefined, errors.onUnexpectedError); } - if (!this.viewModel.changedWorkbenchViewState && (this.partService.isVisible(Parts.SIDEBAR_PART) || !this.contextService.hasWorkspace())) { + if (!this.viewModel.changedWorkbenchViewState && (this.partService.isVisible(Parts.SIDEBAR_PART) || this.contextService.getWorkbenchState() === WorkbenchState.EMPTY)) { // We only want to change the workbench view state on the first debug session #5738 and if the side bar is not hidden this.viewModel.changedWorkbenchViewState = true; this.viewletService.openViewlet(debug.VIEWLET_ID); @@ -866,7 +866,7 @@ export class DebugService implements debug.IDebugService { watchExpressionsCount: this.model.getWatchExpressions().length, extensionName: `${adapter.extensionDescription.publisher}.${adapter.extensionDescription.name}`, isBuiltin: adapter.extensionDescription.isBuiltin, - launchJsonExists: this.contextService.hasWorkspace() && !!this.configurationService.getConfiguration('launch', { resource: root }) + launchJsonExists: this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY && !!this.configurationService.getConfiguration('launch', { resource: root }) }); }).then(() => process, (error: any) => { if (error instanceof Error && error.message === 'Canceled') { diff --git a/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts b/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts index 4737247e86b..5690c49421c 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts @@ -22,7 +22,7 @@ import { DefaultController, DefaultDragAndDrop, ClickBehavior } from 'vs/base/pa import { Constants } from 'vs/editor/common/core/uint'; import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IMenuService, IMenu, MenuId } from 'vs/platform/actions/common/actions'; import { fillInActions } from 'vs/platform/actions/browser/menuItemActionItem'; @@ -48,6 +48,7 @@ export interface IRenderValueOptions { showChanged?: boolean; maxValueLength?: number; showHover?: boolean; + colorize?: boolean; } function replaceWhitespace(value: string): string { @@ -66,12 +67,16 @@ export function renderExpressionValue(expressionOrValue: debug.IExpression | str if (value !== Expression.DEFAULT_VALUE) { dom.addClass(container, 'error'); } - } else if (!isNaN(+value)) { - dom.addClass(container, 'number'); - } else if (booleanRegex.test(value)) { - dom.addClass(container, 'boolean'); - } else if (stringRegex.test(value)) { - dom.addClass(container, 'string'); + } + + if (options.colorize) { + if (!isNaN(+value)) { + dom.addClass(container, 'number'); + } else if (booleanRegex.test(value)) { + dom.addClass(container, 'boolean'); + } else if (stringRegex.test(value)) { + dom.addClass(container, 'string'); + } } if (options.showChanged && (expressionOrValue).valueChanged && value !== Expression.DEFAULT_VALUE) { @@ -104,7 +109,8 @@ export function renderVariable(tree: ITree, variable: Variable, data: IVariableT showChanged, maxValueLength: MAX_VALUE_RENDER_LENGTH_IN_VIEWLET, preserveWhitespace: false, - showHover: true + showHover: true, + colorize: true }); } else { data.value.textContent = ''; @@ -209,7 +215,7 @@ export class BaseDebugController extends DefaultController { this.contributedContextMenu = menuService.createMenu(menuId, contextKeyService); } - public onContextMenu(tree: ITree, element: debug.IEnablement, event: ContextMenuEvent): boolean { + public onContextMenu(tree: ITree, element: debug.IEnablement, event: ContextMenuEvent, focusElement = true): boolean { if (event.target && event.target.tagName && event.target.tagName.toLowerCase() === 'input') { return false; } @@ -217,7 +223,9 @@ export class BaseDebugController extends DefaultController { event.preventDefault(); event.stopPropagation(); - tree.setFocus(element); + if (focusElement) { + tree.setFocus(element); + } if (this.actionProvider.hasSecondaryActions(tree, element)) { const anchor = { x: event.posx, y: event.posy }; @@ -524,7 +532,7 @@ export class CallStackRenderer implements IRenderer { private renderProcess(process: debug.IProcess, data: IProcessTemplateData): void { data.process.title = nls.localize({ key: 'process', comment: ['Process is a noun'] }, "Process"); - data.name.textContent = process.getName(this.contextService.hasMultiFolderWorkspace()); + data.name.textContent = process.getName(this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE); const stoppedThread = process.getAllThreads().filter(t => t.stopped).pop(); data.stateLabel.textContent = stoppedThread ? nls.localize('paused', "Paused") @@ -942,7 +950,8 @@ export class WatchExpressionsRenderer implements IRenderer { showChanged: true, maxValueLength: MAX_VALUE_RENDER_LENGTH_IN_VIEWLET, preserveWhitespace: false, - showHover: true + showHover: true, + colorize: true }); data.name.title = watchExpression.type ? watchExpression.type : watchExpression.value; } diff --git a/src/vs/workbench/parts/debug/electron-browser/replViewer.ts b/src/vs/workbench/parts/debug/electron-browser/replViewer.ts index 9ae5593472c..bfe1493d1d1 100644 --- a/src/vs/workbench/parts/debug/electron-browser/replViewer.ts +++ b/src/vs/workbench/parts/debug/electron-browser/replViewer.ts @@ -11,7 +11,7 @@ import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import * as dom from 'vs/base/browser/dom'; import severity from 'vs/base/common/severity'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; -import { ITree, IAccessibilityProvider, IDataSource, IRenderer, IActionProvider } from 'vs/base/parts/tree/browser/tree'; +import { ITree, IAccessibilityProvider, ContextMenuEvent, IDataSource, IRenderer, IActionProvider } from 'vs/base/parts/tree/browser/tree'; import { ICancelableEvent } from 'vs/base/parts/tree/browser/treeDefaults'; import { IExpressionContainer, IExpression } from 'vs/workbench/parts/debug/common/debug'; import { Model, OutputNameValueElement, Expression, OutputElement, Variable } from 'vs/workbench/parts/debug/common/debugModel'; @@ -431,4 +431,8 @@ export class ReplExpressionsController extends BaseDebugController { return true; } + + public onContextMenu(tree: ITree, element: any, event: ContextMenuEvent): boolean { + return super.onContextMenu(tree, element, event, false); + } } diff --git a/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.ts b/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.ts index f5c8e2040ce..b4ae967d067 100644 --- a/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.ts +++ b/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.ts @@ -9,7 +9,7 @@ import { registerColor, contrastBorder } from 'vs/platform/theme/common/colorReg import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; import { IDebugService, State } from 'vs/workbench/parts/debug/common/debug'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_NO_FOLDER_FOREGROUND, STATUS_BAR_BACKGROUND, Themable, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_BORDER, STATUS_BAR_BORDER } from 'vs/workbench/common/theme'; import { addClass, removeClass } from 'vs/base/browser/dom'; @@ -49,7 +49,7 @@ export class StatusBarColorProvider extends Themable implements IWorkbenchContri private registerListeners(): void { this.toUnbind.push(this.debugService.onDidChangeState(state => this.updateStyles())); - this.toUnbind.push(this.contextService.onDidChangeWorkspaceRoots(state => this.updateStyles())); + this.toUnbind.push(this.contextService.onDidChangeWorkspaceFolders(state => this.updateStyles())); } protected updateStyles(): void { @@ -75,7 +75,7 @@ export class StatusBarColorProvider extends Themable implements IWorkbenchContri // Not debugging if (!this.isDebugging()) { - if (this.contextService.hasWorkspace()) { + if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY) { return normalColor; } diff --git a/src/vs/workbench/parts/extensions/browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/browser/extensionsActions.ts index 7251ddc6149..51d6dc2eb26 100644 --- a/src/vs/workbench/parts/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/browser/extensionsActions.ts @@ -26,7 +26,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { Query } from 'vs/workbench/parts/extensions/common/extensionQuery'; import { IFileService } from 'vs/platform/files/common/files'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { IExtensionService, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import URI from 'vs/base/common/uri'; @@ -559,7 +559,7 @@ export class DisableForWorkspaceAction extends Action implements IExtensionActio private update(): void { this.enabled = false; - if (this.extension && this.workspaceContextService.hasWorkspace()) { + if (this.extension && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY) { this.enabled = this.extension.type !== LocalExtensionType.System && !this.extension.disabledGlobally && !this.extension.disabledForWorkspace; } } @@ -1093,7 +1093,7 @@ export class ShowWorkspaceRecommendedExtensionsAction extends Action { @IWorkspaceContextService contextService: IWorkspaceContextService, @IViewletService private viewletService: IViewletService ) { - super(id, label, null, contextService.hasWorkspace()); + super(id, label, null, contextService.getWorkbenchState() !== WorkbenchState.EMPTY); } run(): TPromise { @@ -1251,7 +1251,7 @@ export class ConfigureWorkspaceRecommendedExtensionsAction extends Action { @IWorkbenchEditorService private editorService: IWorkbenchEditorService, @IMessageService private messageService: IMessageService ) { - super(id, label, null, contextService.hasWorkspace()); + super(id, label, null, contextService.getWorkbenchState() !== WorkbenchState.EMPTY); } public run(event: any): TPromise { @@ -1259,7 +1259,7 @@ export class ConfigureWorkspaceRecommendedExtensionsAction extends Action { } private openExtensionsFile(): TPromise { - if (!this.contextService.hasWorkspace()) { + if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { this.messageService.show(severity.Info, localize('ConfigureWorkspaceRecommendations.noWorkspace', 'Recommendations are only available on a workspace folder.')); return TPromise.as(undefined); } @@ -1276,7 +1276,7 @@ export class ConfigureWorkspaceRecommendedExtensionsAction extends Action { } private getOrCreateExtensionsFile(): TPromise<{ created: boolean, extensionsFileResource: URI }> { - const extensionsFileResource = URI.file(paths.join(this.contextService.getLegacyWorkspace().resource.fsPath, '.vscode', 'extensions.json')); // TODO@Sandeep (https://github.com/Microsoft/vscode/issues/29242) + const extensionsFileResource = URI.file(paths.join(this.contextService.getWorkspace().folders[0].fsPath, '.vscode', 'extensions.json')); // TODO@Sandeep (https://github.com/Microsoft/vscode/issues/29242) return this.fileService.resolveContent(extensionsFileResource).then(content => { return { created: false, extensionsFileResource }; @@ -1363,7 +1363,7 @@ export class DisableAllWorkpsaceAction extends Action { } private update(): void { - this.enabled = this.workspaceContextService.hasWorkspace() && this.extensionsWorkbenchService.local.some(e => e.type === LocalExtensionType.User && !e.disabledForWorkspace && !e.disabledGlobally); + this.enabled = this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.extensionsWorkbenchService.local.some(e => e.type === LocalExtensionType.User && !e.disabledForWorkspace && !e.disabledGlobally); } run(): TPromise { @@ -1426,7 +1426,7 @@ export class EnableAllWorkpsaceAction extends Action { } private update(): void { - this.enabled = this.workspaceContextService.hasWorkspace() && this.extensionsWorkbenchService.local.some(e => this.extensionEnablementService.canEnable(e.id) && !e.disabledGlobally && e.disabledForWorkspace); + this.enabled = this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.extensionsWorkbenchService.local.some(e => this.extensionEnablementService.canEnable(e.id) && !e.disabledGlobally && e.disabledForWorkspace); } run(): TPromise { diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts index 75ef396df9d..6761e846ab6 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts @@ -19,14 +19,14 @@ import { IChoiceService, IMessageService } from 'vs/platform/message/common/mess import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ShowRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction } from 'vs/workbench/parts/extensions/browser/extensionsActions'; import Severity from 'vs/base/common/severity'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { Schemas } from 'vs/base/common/network'; import { IFileService } from 'vs/platform/files/common/files'; import { IExtensionsConfiguration, ConfigurationKey } from 'vs/workbench/parts/extensions/common/extensions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import * as cp from 'child_process'; +import * as fs from 'fs'; import { distinct } from 'vs/base/common/arrays'; interface IExtensionsContent { @@ -68,11 +68,10 @@ export class ExtensionTipsService implements IExtensionTipsService { this._suggestTips(); this._suggestWorkspaceRecommendations(); - this._suggestBasedOnExecutables(); } getWorkspaceRecommendations(): TPromise { - if (!this.contextService.hasWorkspace()) { + if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { return TPromise.as([]); } return this.fileService.resolveContent(this.contextService.toResource(paths.join('.vscode', 'extensions.json'))).then(content => { //TODO@Sandeep (https://github.com/Microsoft/vscode/issues/29242) @@ -92,7 +91,7 @@ export class ExtensionTipsService implements IExtensionTipsService { const fileBased = Object.keys(this._fileBasedRecommendations) .filter(recommendation => allRecomendations.indexOf(recommendation) !== -1); - const exeBased = distinct(this._exeBasedRecommendations); + const exeBased = distinct(this._suggestBasedOnExecutables()); this.telemetryService.publicLog('extensionRecommendations:unfiltered', { fileBased, exeBased }); @@ -319,15 +318,42 @@ export class ExtensionTipsService implements IExtensionTipsService { }); } - private _suggestBasedOnExecutables() { - const cmd = process.platform === 'win32' ? 'where' : 'which'; + private _suggestBasedOnExecutables(): string[] { + if (!process.env.PATH || this._exeBasedRecommendations.length > 0) { + return this._exeBasedRecommendations; + } + + let envpaths = process.env.PATH.split(process.platform === 'win32' ? ';' : ':'); + let foundExecutables: Set = new Set(); + + // Loop through recommended extensions forEach(product.exeBasedExtensionTips, entry => { - cp.exec(`${cmd} ${entry.value.replace(/,/g, ' ')}`, (err, stdout, stderr) => { - if (stdout) { - this._exeBasedRecommendations.push(entry.key); + let executables = entry.value.split(','); + + // Loop through executables that would result in recommending current extension + for (let i = 0; i < executables.length; i++) { + if (!foundExecutables.has(executables[i])) { + + // Loop through paths in PATH to find current executable + for (let pathEntry of envpaths) { + let fullPath = paths.join(pathEntry, executables[i]); + if (process.platform === 'win32') { + fullPath += '.exe'; + } + if (fs.existsSync(fullPath)) { + foundExecutables.add(executables[i]); + break; + } + } } - }); + if (foundExecutables.has(executables[i])) { + this._exeBasedRecommendations.push(entry.key); + break; + } + } }); + + return this._exeBasedRecommendations; } private setIgnoreRecommendationsConfig(configVal: boolean) { diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index 029bed45771..192350635c5 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -34,7 +34,7 @@ import { IExtension, IExtensionDependencies, ExtensionState, IExtensionsWorkbenc import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IURLService } from 'vs/platform/url/common/url'; import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import product from 'vs/platform/node/product'; interface IExtensionStateProvider { @@ -663,7 +663,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService { } const globalElablement = this.extensionEnablementService.setEnablement(extension.id, enable, false); - if (enable && this.workspaceContextService.hasWorkspace()) { + if (enable && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY) { const workspaceEnablement = this.extensionEnablementService.setEnablement(extension.id, enable, true); return TPromise.join([globalElablement, workspaceEnablement]).then(values => values[0] || values[1]); } diff --git a/src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts b/src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts index 7a93025af9d..873ab35c441 100644 --- a/src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts +++ b/src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts @@ -13,7 +13,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import product from 'vs/platform/node/product'; import { Themable, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_FOREGROUND } from 'vs/workbench/common/theme'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; class TwitterFeedbackService implements IFeedbackService { @@ -63,14 +63,14 @@ export class FeedbackStatusbarItem extends Themable implements IStatusbarItem { } private registerListeners(): void { - this.toUnbind.push(this.contextService.onDidChangeWorkspaceRoots(() => this.updateStyles())); + this.toUnbind.push(this.contextService.onDidChangeWorkspaceFolders(() => this.updateStyles())); } protected updateStyles(): void { super.updateStyles(); if (this.dropdown) { - this.dropdown.label.style('background-color', this.getColor(this.contextService.hasWorkspace() ? STATUS_BAR_FOREGROUND : STATUS_BAR_NO_FOLDER_FOREGROUND)); + this.dropdown.label.style('background-color', this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_FOREGROUND : STATUS_BAR_NO_FOLDER_FOREGROUND)); } } diff --git a/src/vs/workbench/parts/files/browser/explorerViewlet.ts b/src/vs/workbench/parts/files/browser/explorerViewlet.ts index 2374bdd2558..4db3893c1b1 100644 --- a/src/vs/workbench/parts/files/browser/explorerViewlet.ts +++ b/src/vs/workbench/parts/files/browser/explorerViewlet.ts @@ -22,7 +22,7 @@ import { OpenEditorsView } from 'vs/workbench/parts/files/browser/views/openEdit import { IStorageService } from 'vs/platform/storage/common/storage'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionService } from 'vs/platform/extensions/common/extensions'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { DelegatingWorkbenchEditorService } from 'vs/workbench/services/editor/browser/editorService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; @@ -78,7 +78,7 @@ export class ExplorerViewlet extends PersistentViewsViewlet { viewDescriptors.push(this.createOpenEditorsViewDescriptor()); - if (this.contextService.hasWorkspace()) { + if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY) { viewDescriptors.push(this.createExplorerViewDescriptor()); } else { viewDescriptors.push(this.createEmptyViewDescriptor()); @@ -122,7 +122,7 @@ export class ExplorerViewlet extends PersistentViewsViewlet { } private onConfigurationUpdated(): void { - this.openEditorsVisibleContextKey.set(!this.contextService.hasWorkspace() || (this.configurationService.getConfiguration()).explorer.openEditors.visible !== 0); + this.openEditorsVisibleContextKey.set(this.contextService.getWorkbenchState() === WorkbenchState.EMPTY || (this.configurationService.getConfiguration()).explorer.openEditors.visible !== 0); } protected createView(viewDescriptor: IViewDescriptor, initialSize: number, options: IViewletViewOptions): IViewletView { diff --git a/src/vs/workbench/parts/files/browser/fileActions.contribution.ts b/src/vs/workbench/parts/files/browser/fileActions.contribution.ts index 11385383db6..b72884929b5 100644 --- a/src/vs/workbench/parts/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/parts/files/browser/fileActions.contribution.ts @@ -95,7 +95,7 @@ class FilesViewerActionContributor extends ActionBarContributor { let action: Action = this.instantiationService.createInstance(AddRootFolderAction, AddRootFolderAction.ID, AddRootFolderAction.LABEL); action.order = 52; actions.push(action); - if (this.contextService.getWorkspace().roots.length > 1) { + if (this.contextService.getWorkspace().folders.length > 1) { action = this.instantiationService.createInstance(RemoveRootFolderAction, stat.resource, RemoveRootFolderAction.ID, RemoveRootFolderAction.LABEL); action.order = 53; actions.push(action); diff --git a/src/vs/workbench/parts/files/browser/views/explorerView.ts b/src/vs/workbench/parts/files/browser/views/explorerView.ts index e99e1d220a8..85fe8e72a8a 100644 --- a/src/vs/workbench/parts/files/browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/browser/views/explorerView.ts @@ -32,7 +32,7 @@ import { FileStat, Model } from 'vs/workbench/parts/files/common/explorerModel'; import { IListService } from 'vs/platform/list/browser/listService'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IPartService } from 'vs/workbench/services/part/common/partService'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -133,7 +133,7 @@ export class ExplorerView extends CollapsibleView { const titleSpan = $('span').appendTo(titleDiv); const setHeader = () => { const workspace = this.contextService.getWorkspace(); - const title = workspace.roots.map(root => labels.getPathLabel(root.fsPath, void 0, this.environmentService)).join(); + const title = workspace.folders.map(folder => labels.getPathLabel(folder.fsPath, void 0, this.environmentService)).join(); titleSpan.text(this.name).title(title); }; this.toDispose.push(this.contextService.onDidChangeWorkspaceName(() => setHeader())); @@ -166,7 +166,7 @@ export class ExplorerView extends CollapsibleView { }; this.toDispose.push(this.themeService.onDidFileIconThemeChange(onFileIconThemeChange)); - this.toDispose.push(this.contextService.onDidChangeWorkspaceRoots(() => this.refreshFromEvent())); + this.toDispose.push(this.contextService.onDidChangeWorkspaceFolders(() => this.refreshFromEvent())); onFileIconThemeChange(this.themeService.getFileIconTheme()); } @@ -727,25 +727,25 @@ export class ExplorerView extends CollapsibleView { let targetsToExpand: URI[] = []; if (this.settings[ExplorerView.MEMENTO_EXPANDED_FOLDER_RESOURCES]) { targetsToExpand = this.settings[ExplorerView.MEMENTO_EXPANDED_FOLDER_RESOURCES].map((e: string) => URI.parse(e)); - } else if (this.contextService.hasFolderWorkspace() || (this.contextService.hasMultiFolderWorkspace() && this.model.roots.length === 1)) { - targetsToExpand = this.model.roots.map(root => root.resource); // always expand single folder workspace and multi folder workspace with only 1 root + } else if (this.model.roots.length === 1) { + targetsToExpand = this.model.roots.map(root => root.resource); // always expand if there is just one root } // First time refresh: Receive target through active editor input or selection and also include settings from previous session if (!this.isCreated) { const activeFile = this.getActiveFile(); if (activeFile) { - const root = this.contextService.getRoot(activeFile); - if (root) { - const found = targetsToResolve.filter(t => t.root.resource.toString() === root.toString()).pop(); + const workspaceFolder = this.contextService.getWorkspaceFolder(activeFile); + if (workspaceFolder) { + const found = targetsToResolve.filter(t => t.root.resource.toString() === workspaceFolder.toString()).pop(); found.options.resolveTo.push(activeFile); } } targetsToExpand.forEach(toExpand => { - const root = this.contextService.getRoot(toExpand); - if (root) { - const found = targetsToResolve.filter(ttr => ttr.resource.toString() === root.toString()).pop(); + const workspaceFolder = this.contextService.getWorkspaceFolder(toExpand); + if (workspaceFolder) { + const found = targetsToResolve.filter(ttr => ttr.resource.toString() === workspaceFolder.toString()).pop(); found.options.resolveTo.push(toExpand); } }); @@ -778,7 +778,7 @@ export class ExplorerView extends CollapsibleView { // Subsequent refresh: Merge stat into our local model and refresh tree modelStats.forEach((modelStat, index) => FileStat.mergeLocalWithDisk(modelStat, this.model.roots[index])); - const input = this.contextService.hasFolderWorkspace() ? this.model.roots[0] : this.model; + const input = this.contextService.getWorkbenchState() === WorkbenchState.FOLDER ? this.model.roots[0] : this.model; if (input === this.explorerViewer.getInput()) { return this.explorerViewer.refresh(); } @@ -855,7 +855,7 @@ export class ExplorerView extends CollapsibleView { // Stat needs to be resolved first and then revealed const options: IResolveFileOptions = { resolveTo: [resource] }; - const rootUri = this.contextService.getRoot(resource) || this.model.roots[0].resource; + const rootUri = this.contextService.getWorkspaceFolder(resource) || this.model.roots[0].resource; return this.fileService.resolveFile(rootUri, options).then(stat => { // Convert to model diff --git a/src/vs/workbench/parts/files/browser/views/explorerViewer.ts b/src/vs/workbench/parts/files/browser/views/explorerViewer.ts index d67a2880f86..5061faf54cf 100644 --- a/src/vs/workbench/parts/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/browser/views/explorerViewer.ts @@ -35,7 +35,7 @@ import { FileStat, NewStatPlaceholder, Model } from 'vs/workbench/parts/files/co import { DragMouseEvent, IMouseEvent } from 'vs/base/browser/mouseEvent'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IPartService } from 'vs/workbench/services/part/common/partService'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -549,7 +549,8 @@ export class FileSorter implements ISorter { private sortOrder: SortOrder; constructor( - @IConfigurationService private configurationService: IConfigurationService + @IConfigurationService private configurationService: IConfigurationService, + @IWorkspaceContextService private contextService: IWorkspaceContextService ) { this.toDispose = []; @@ -570,6 +571,10 @@ export class FileSorter implements ISorter { // Do not sort roots if (statA.isRoot) { + if (statB.isRoot) { + const ws = this.contextService.getWorkspace(); + return ws.folders.indexOf(statA.resource) - ws.folders.indexOf(statB.resource); + } return -1; } if (statB.isRoot) { @@ -655,16 +660,16 @@ export class FileFilter implements IFilter { @IConfigurationService private configurationService: IConfigurationService ) { this.hiddenExpressionPerRoot = new Map(); - this.contextService.onDidChangeWorkspaceRoots(() => this.updateConfiguration()); + this.contextService.onDidChangeWorkspaceFolders(() => this.updateConfiguration()); } public updateConfiguration(): boolean { let needsRefresh = false; - this.contextService.getWorkspace().roots.forEach(root => { - const configuration = this.configurationService.getConfiguration(undefined, { resource: root }); + this.contextService.getWorkspace().folders.forEach(folder => { + const configuration = this.configurationService.getConfiguration(undefined, { resource: folder }); const excludesConfig = (configuration && configuration.files && configuration.files.exclude) || Object.create(null); - needsRefresh = needsRefresh || !objects.equals(this.hiddenExpressionPerRoot.get(root.toString()), excludesConfig); - this.hiddenExpressionPerRoot.set(root.toString(), objects.clone(excludesConfig)); // do not keep the config, as it gets mutated under our hoods + needsRefresh = needsRefresh || !objects.equals(this.hiddenExpressionPerRoot.get(folder.toString()), excludesConfig); + this.hiddenExpressionPerRoot.set(folder.toString(), objects.clone(excludesConfig)); // do not keep the config, as it gets mutated under our hoods }); return needsRefresh; @@ -829,7 +834,7 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { // All (target = model) if (target instanceof Model) { - return this.contextService.hasMultiFolderWorkspace() ? DRAG_OVER_ACCEPT_BUBBLE_DOWN_COPY(false) : DRAG_OVER_REJECT; // can only drop folders to workspace + return this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE ? DRAG_OVER_ACCEPT_BUBBLE_DOWN_COPY(false) : DRAG_OVER_REJECT; // can only drop folders to workspace } // All (target = file/folder) @@ -838,8 +843,7 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { return fromDesktop || isCopy ? DRAG_OVER_ACCEPT_BUBBLE_DOWN_COPY(true) : DRAG_OVER_ACCEPT_BUBBLE_DOWN(true); } - const workspace = this.contextService.getWorkspace(); - if (workspace && workspace.roots.every(r => r.toString() !== target.resource.toString())) { + if (this.contextService.getWorkspace().folders.every(r => r.toString() !== target.resource.toString())) { return fromDesktop || isCopy ? DRAG_OVER_ACCEPT_BUBBLE_UP_COPY : DRAG_OVER_ACCEPT_BUBBLE_UP; } } @@ -883,8 +887,8 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { return void 0; // TODO@Ben multi root } - if (this.contextService.hasMultiFolderWorkspace()) { - return this.workspaceEditingService.addRoots(folders); + if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { + return this.workspaceEditingService.addFolders(folders); } // If we are in single-folder context, ask for confirmation to create a workspace @@ -895,8 +899,8 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { }); if (result) { - const currentRoots = this.contextService.getWorkspace().roots; - const newRoots = [...currentRoots, ...folders]; + const currentFolders = this.contextService.getWorkspace().folders; + const newRoots = [...currentFolders, ...folders]; return this.windowService.createAndOpenWorkspace(distinct(newRoots.map(root => root.fsPath))); } diff --git a/src/vs/workbench/parts/files/common/explorerModel.ts b/src/vs/workbench/parts/files/common/explorerModel.ts index e3d97b1d97b..cbcbad4b33d 100644 --- a/src/vs/workbench/parts/files/common/explorerModel.ts +++ b/src/vs/workbench/parts/files/common/explorerModel.ts @@ -25,8 +25,8 @@ export class Model { private _roots: FileStat[]; constructor( @IWorkspaceContextService private contextService: IWorkspaceContextService) { - const setRoots = () => this._roots = this.contextService.getWorkspace().roots.map(uri => new FileStat(uri, undefined)); - this.contextService.onDidChangeWorkspaceRoots(() => setRoots()); + const setRoots = () => this._roots = this.contextService.getWorkspace().folders.map(uri => new FileStat(uri, undefined)); + this.contextService.onDidChangeWorkspaceFolders(() => setRoots()); setRoots(); } @@ -49,9 +49,9 @@ export class Model { * Will return null in case the FileStat does not exist. */ public findClosest(resource: URI): FileStat { - const rootUri = this.contextService.getRoot(resource); - if (rootUri) { - const root = this.roots.filter(r => r.resource.toString() === rootUri.toString()).pop(); + const folder = this.contextService.getWorkspaceFolder(resource); + if (folder) { + const root = this.roots.filter(r => r.resource.toString() === folder.toString()).pop(); if (root) { return root.find(resource); } diff --git a/src/vs/workbench/parts/output/common/outputLinkProvider.ts b/src/vs/workbench/parts/output/common/outputLinkProvider.ts index abdd048bdad..e457e1073c4 100644 --- a/src/vs/workbench/parts/output/common/outputLinkProvider.ts +++ b/src/vs/workbench/parts/output/common/outputLinkProvider.ts @@ -10,7 +10,7 @@ import URI from 'vs/base/common/uri'; import { RunOnceScheduler, wireCancellationToken } from 'vs/base/common/async'; import { IModelService } from 'vs/editor/common/services/modelService'; import { LinkProviderRegistry, ILink } from 'vs/editor/common/modes'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { OUTPUT_MODE_ID } from 'vs/workbench/parts/output/common/output'; import { MonacoWebWorker, createWebWorker } from 'vs/editor/common/services/webWorker'; import { ICreateData, OutputLinkComputer } from 'vs/workbench/parts/output/common/outputLinkComputer'; @@ -37,13 +37,13 @@ export class OutputLinkProvider { } private registerListeners(): void { - this.contextService.onDidChangeWorkspaceRoots(() => this.updateLinkProviderWorker()); + this.contextService.onDidChangeWorkspaceFolders(() => this.updateLinkProviderWorker()); } private updateLinkProviderWorker(): void { // We have a workspace - if (this.contextService.hasWorkspace()) { + if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY) { // Register link provider unless done already if (!this.linkProviderRegistration) { @@ -55,7 +55,7 @@ export class OutputLinkProvider { } // Update link provider worker if workspace roots changed - const newWorkspacesCount = this.contextService.getWorkspace().roots.length; + const newWorkspacesCount = this.contextService.getWorkspace().folders.length; if (this.workspacesCount !== newWorkspacesCount) { this.workspacesCount = newWorkspacesCount; @@ -80,7 +80,7 @@ export class OutputLinkProvider { if (!this.worker) { const createData: ICreateData = { - workspaceFolders: this.contextService.getWorkspace().roots.map(root => root.toString()) + workspaceFolders: this.contextService.getWorkspace().folders.map(folder => folder.toString()) }; this.worker = createWebWorker(this.modelService, { diff --git a/src/vs/workbench/parts/preferences/browser/preferencesActions.ts b/src/vs/workbench/parts/preferences/browser/preferencesActions.ts index 1cee1d5d6c2..aeee25d30bf 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesActions.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesActions.ts @@ -11,7 +11,7 @@ import { Action } from 'vs/base/common/actions'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IQuickOpenService, IPickOpenEntry, IFilePickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen'; import { IPreferencesService, getSettingsTargetName } from 'vs/workbench/parts/preferences/common/preferences'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; export class OpenGlobalSettingsAction extends Action { @@ -80,7 +80,7 @@ export class OpenWorkspaceSettingsAction extends Action { @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService ) { super(id, label); - this.enabled = this.workspaceContextService.hasWorkspace(); + this.enabled = this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY; } public run(event?: any): TPromise { @@ -101,13 +101,13 @@ export class OpenFolderSettingsAction extends Action { @IQuickOpenService private quickOpenService: IQuickOpenService ) { super(id, label); - this.enabled = this.workspaceContextService.hasMultiFolderWorkspace(); + this.enabled = this.workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE; } public run(): TPromise { - const picks: IPickOpenEntry[] = this.workspaceContextService.getWorkspace().roots.map((root, index) => { + const picks: IPickOpenEntry[] = this.workspaceContextService.getWorkspace().folders.map((folder, index) => { return { - label: getSettingsTargetName(ConfigurationTarget.FOLDER, root, this.workspaceContextService), + label: getSettingsTargetName(ConfigurationTarget.FOLDER, folder, this.workspaceContextService), id: `${index}` }; }); @@ -115,7 +115,7 @@ export class OpenFolderSettingsAction extends Action { return this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickFolder', "Select Folder") }) .then(pick => { if (pick) { - return this.preferencesService.openFolderSettings(this.workspaceContextService.getWorkspace().roots[parseInt(pick.id)]); + return this.preferencesService.openFolderSettings(this.workspaceContextService.getWorkspace().folders[parseInt(pick.id)]); } return undefined; }); diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index 7b92f7a36ff..624ab8f0ef9 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -152,7 +152,7 @@ export class PreferencesEditor extends BaseEditor { this._register(this.sideBySidePreferencesWidget.onFocus(() => this.lastFocusedWidget = this.sideBySidePreferencesWidget)); this.preferencesRenderers = this._register(new PreferencesRenderers()); - this._register(this.workspaceContextService.onDidChangeWorkspaceRoots(() => this.onWorkspaceRootsChanged())); + this._register(this.workspaceContextService.onDidChangeWorkspaceFolders(() => this.onWorkspaceFoldersChanged())); } public setInput(newInput: PreferencesEditorInput, options?: EditorOptions): TPromise { @@ -226,7 +226,7 @@ export class PreferencesEditor extends BaseEditor { if (this.preferencesService.workspaceSettingsResource.fsPath === resource.fsPath) { return ConfigurationTarget.WORKSPACE; } - if (this.workspaceContextService.getRoot(resource)) { + if (this.workspaceContextService.getWorkspaceFolder(resource)) { return ConfigurationTarget.FOLDER; } return null; @@ -240,10 +240,10 @@ export class PreferencesEditor extends BaseEditor { return resource; } - return this.workspaceContextService.getRoot(resource); + return this.workspaceContextService.getWorkspaceFolder(resource); } - private onWorkspaceRootsChanged(): void { + private onWorkspaceFoldersChanged(): void { if (this.input) { const settingsResource = toResource((this.input).master); const targetResource = this.getSettingsConfigurationTargetUri(settingsResource); @@ -886,13 +886,10 @@ class SettingsEditorContribution extends AbstractSettingsEditorContribution impl return true; } - const workspace = this.workspaceContextService.getWorkspace(); - if (workspace) { - for (const root of workspace.roots) { - const folderSettingsResource = this.preferencesService.getFolderSettingsResource(root); - if (folderSettingsResource && folderSettingsResource.fsPath === model.uri.fsPath) { - return true; - } + for (const folder of this.workspaceContextService.getWorkspace().folders) { + const folderSettingsResource = this.preferencesService.getFolderSettingsResource(folder); + if (folderSettingsResource && folderSettingsResource.fsPath === model.uri.fsPath) { + return true; } } diff --git a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts index ad7e9950cfe..142d611efc6 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts @@ -31,7 +31,7 @@ import { IMessageService, Severity } from 'vs/platform/message/common/message'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { MarkdownString } from 'vs/base/common/htmlContent'; export interface IPreferencesRenderer extends IDisposable { @@ -1064,7 +1064,7 @@ class WorkspaceConfigurationRenderer extends Disposable { } public render(): void { - if (this.workspaceContextService.hasMultiFolderWorkspace()) { + if (this.workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { this.editor.changeDecorations(changeAccessor => this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, [])); const ranges: IRange[] = []; diff --git a/src/vs/workbench/parts/preferences/browser/preferencesService.ts b/src/vs/workbench/parts/preferences/browser/preferencesService.ts index d37e517d962..e820df5ea27 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesService.ts @@ -16,7 +16,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { Emitter } from 'vs/base/common/event'; import { EditorInput } from 'vs/workbench/common/editor'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { Position as EditorPosition, IEditor } from 'vs/platform/editor/common/editor'; import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; @@ -179,7 +179,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.createEditableSettingsEditorModel(ConfigurationTarget.WORKSPACE, workspaceSettingsUri); } - if (this.contextService.hasMultiFolderWorkspace()) { + if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { return this.createEditableSettingsEditorModel(ConfigurationTarget.FOLDER, uri); } @@ -191,7 +191,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic } openWorkspaceSettings(): TPromise { - if (!this.contextService.hasWorkspace()) { + if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { this.messageService.show(Severity.Info, nls.localize('openFolderFirst', "Open a folder first to create workspace settings")); return TPromise.as(null); } @@ -299,7 +299,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic } private resolveSettingsContentFromWorkspaceConfiguration(): TPromise { - if (this.contextService.hasMultiFolderWorkspace()) { + if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { return this.textModelResolverService.createModelReference(this.contextService.getWorkspace().configuration) .then(reference => { const model = reference.object.textEditorModel; @@ -316,17 +316,14 @@ export class PreferencesService extends Disposable implements IPreferencesServic case ConfigurationTarget.USER: return URI.file(this.environmentService.appSettingsPath); case ConfigurationTarget.WORKSPACE: + if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { + return null; + } const workspace = this.contextService.getWorkspace(); - if (this.contextService.hasFolderWorkspace()) { - return this.toResource(paths.join('.vscode', 'settings.json'), workspace.roots[0]); - } - if (this.contextService.hasMultiFolderWorkspace()) { - return workspace.configuration; - } - return null; + return workspace.configuration || this.toResource(paths.join('.vscode', 'settings.json'), workspace.folders[0]); case ConfigurationTarget.FOLDER: - const root = this.contextService.getRoot(resource); - return root ? this.toResource(paths.join('.vscode', 'settings.json'), root) : null; + const folder = this.contextService.getWorkspaceFolder(resource); + return folder ? this.toResource(paths.join('.vscode', 'settings.json'), folder) : null; } return null; } @@ -336,7 +333,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic } private createSettingsIfNotExists(target: ConfigurationTarget, resource: URI): TPromise { - if (this.contextService.hasMultiFolderWorkspace() && target === ConfigurationTarget.WORKSPACE) { + if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE && target === ConfigurationTarget.WORKSPACE) { if (!this.configurationService.keys().workspace.length) { return this.jsonEditingService.write(resource, { key: 'settings', value: {} }, true).then(null, () => { }); } diff --git a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts index c2212fcb657..cb4392d3c8c 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts @@ -20,7 +20,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { ISettingsGroup, IPreferencesService, getSettingsTargetName } from 'vs/workbench/parts/preferences/common/preferences'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IAction, IActionRunner } from 'vs/base/common/actions'; import { attachInputBoxStyler, attachStylerCallback, attachSelectBoxStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -293,7 +293,7 @@ export class SettingsTargetsWidget extends Widget { private create(parent: HTMLElement): void { this.settingsTargetsContainer = DOM.append(parent, DOM.$('.settings-targets-widget')); - this.settingsTargetsContainer.style.width = this.workspaceContextService.hasMultiFolderWorkspace() ? '200px' : '150px'; + this.settingsTargetsContainer.style.width = this.workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE ? '200px' : '150px'; const targetElement = DOM.append(this.settingsTargetsContainer, DOM.$('.settings-target')); this.targetLabel = DOM.append(targetElement, DOM.$('.settings-target-label')); @@ -337,7 +337,7 @@ export class SettingsTargetsWidget extends Widget { run: () => this.onTargetClicked(userSettingsResource) }); - if (this.workspaceContextService.hasWorkspace()) { + if (this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY) { const workspaceSettingsResource = this.preferencesService.workspaceSettingsResource; actions.push({ id: 'workspaceSettingsTarget', @@ -348,15 +348,15 @@ export class SettingsTargetsWidget extends Widget { }); } - if (this.workspaceContextService.hasMultiFolderWorkspace()) { + if (this.workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { actions.push(new Separator()); - actions.push(...this.workspaceContextService.getWorkspace().roots.map((root, index) => { + actions.push(...this.workspaceContextService.getWorkspace().folders.map((folder, index) => { return { id: 'folderSettingsTarget' + index, - label: getSettingsTargetName(ConfigurationTarget.FOLDER, root, this.workspaceContextService), - checked: this.uri.fsPath === root.fsPath, + label: getSettingsTargetName(ConfigurationTarget.FOLDER, folder, this.workspaceContextService), + checked: this.uri.fsPath === folder.fsPath, enabled: true, - run: () => this.onTargetClicked(root) + run: () => this.onTargetClicked(folder) }; })); } diff --git a/src/vs/workbench/parts/preferences/common/preferences.ts b/src/vs/workbench/parts/preferences/common/preferences.ts index 7037e86c7e7..ec69c7a8cb8 100644 --- a/src/vs/workbench/parts/preferences/common/preferences.ts +++ b/src/vs/workbench/parts/preferences/common/preferences.ts @@ -107,8 +107,8 @@ export function getSettingsTargetName(target: ConfigurationTarget, resource: URI case ConfigurationTarget.WORKSPACE: return localize('workspaceSettingsTarget', "Workspace Settings"); case ConfigurationTarget.FOLDER: - const root = workspaceContextService.getRoot(resource); - return root ? paths.basename(root.fsPath) : ''; + const folder = workspaceContextService.getWorkspaceFolder(resource); + return folder ? paths.basename(folder.fsPath) : ''; } } diff --git a/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts b/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts index f83349ff4f7..f5dc4573e53 100644 --- a/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts +++ b/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts @@ -30,8 +30,8 @@ export class SettingsChangeRelauncher implements IWorkbenchContribution { private nativeTabs: boolean; private updateChannel: string; private enableCrashReporter: boolean; - private rootCount: number; - private firstRootPath: string; + private foldersCount: number; + private firstFolderPath: string; constructor( @IWindowsService private windowsService: IWindowsService, @@ -44,12 +44,8 @@ export class SettingsChangeRelauncher implements IWorkbenchContribution { @IExtensionService private extensionService: IExtensionService ) { const workspace = this.contextService.getWorkspace(); - if (workspace) { - this.rootCount = workspace.roots.length; - this.firstRootPath = workspace.roots.length > 0 ? workspace.roots[0].fsPath : void 0; - } else { - this.rootCount = 0; - } + this.foldersCount = workspace.folders.length; + this.firstFolderPath = workspace.folders.length > 0 ? workspace.folders[0].fsPath : void 0; this.onConfigurationChange(configurationService.getConfiguration(), false); @@ -58,7 +54,7 @@ export class SettingsChangeRelauncher implements IWorkbenchContribution { private registerListeners(): void { this.toDispose.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationChange(this.configurationService.getConfiguration(), true))); - this.toDispose.push(this.contextService.onDidChangeWorkspaceRoots(() => this.onDidChangeWorkspaceRoots())); + this.toDispose.push(this.contextService.onDidChangeWorkspaceFolders(() => this.onDidChangeWorkspaceFolders())); } private onConfigurationChange(config: IConfiguration, notify: boolean): void { @@ -99,27 +95,27 @@ export class SettingsChangeRelauncher implements IWorkbenchContribution { } } - private onDidChangeWorkspaceRoots(): void { + private onDidChangeWorkspaceFolders(): void { const workspace = this.contextService.getWorkspace(); - const newRootCount = workspace ? workspace.roots.length : 0; - const newFirstRootPath = workspace && workspace.roots.length > 0 ? workspace.roots[0].fsPath : void 0; + const newFoldersCount = workspace.folders.length; + const newFirstFolderPath = workspace.folders.length > 0 ? workspace.folders[0].fsPath : void 0; let reloadWindow = false; let reloadExtensionHost = false; - if (this.rootCount === 0 && newRootCount > 0) { + if (this.foldersCount === 0 && newFoldersCount > 0) { reloadWindow = true; // transition: from 0 folders to 1+ - } else if (this.rootCount > 0 && newRootCount === 0) { + } else if (this.foldersCount > 0 && newFoldersCount === 0) { reloadWindow = true; // transition: from 1+ folders to 0 } - if (this.firstRootPath !== newFirstRootPath) { + if (this.firstFolderPath !== newFirstFolderPath) { reloadExtensionHost = true; // first root folder changed (impact on deprecated workspace.rootPath API) } - this.rootCount = newRootCount; - this.firstRootPath = newFirstRootPath; + this.foldersCount = newFoldersCount; + this.firstFolderPath = newFirstFolderPath; // Reload window if this is needed if (reloadWindow) { diff --git a/src/vs/workbench/parts/search/browser/openFileHandler.ts b/src/vs/workbench/parts/search/browser/openFileHandler.ts index bb19b6d2ce7..7fbbcfdbe2c 100644 --- a/src/vs/workbench/parts/search/browser/openFileHandler.ts +++ b/src/vs/workbench/parts/search/browser/openFileHandler.ts @@ -164,7 +164,7 @@ export class OpenFileHandler extends QuickOpenHandler { iconClass = 'file'; // only use a generic file icon if we are forced to use an icon and have no icon theme set otherwise } - const folderResources = this.contextService.hasWorkspace() ? this.contextService.getWorkspace().roots : []; + const folderResources = this.contextService.getWorkspace().folders; return this.searchService.search(this.queryBuilder.file(folderResources, query)).then((complete) => { const results: QuickOpenEntry[] = []; for (let i = 0; i < complete.results.length; i++) { @@ -199,7 +199,7 @@ export class OpenFileHandler extends QuickOpenHandler { useRipgrep: this.experimentService.getExperiments().ripgrepQuickSearch }; - const folderResources = this.contextService.hasWorkspace() ? this.contextService.getWorkspace().roots : []; + const folderResources = this.contextService.getWorkspace().folders; const query = this.queryBuilder.file(folderResources, options); return query; diff --git a/src/vs/workbench/parts/search/browser/searchViewlet.ts b/src/vs/workbench/parts/search/browser/searchViewlet.ts index 6f6dce24526..e550acb92fb 100644 --- a/src/vs/workbench/parts/search/browser/searchViewlet.ts +++ b/src/vs/workbench/parts/search/browser/searchViewlet.ts @@ -41,7 +41,7 @@ import { IContextViewService } from 'vs/platform/contextview/browser/contextView import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IMessageService, IConfirmation } from 'vs/platform/message/common/message'; import { IProgressService } from 'vs/platform/progress/common/progress'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -239,7 +239,7 @@ export class SearchViewlet extends Viewlet { }).getHTMLElement(); this.messages = builder.div({ 'class': 'messages' }).hide().clone(); - if (!this.contextService.hasWorkspace()) { + if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { this.searchWithoutFolderMessage(this.clearMessage()); } @@ -468,7 +468,7 @@ export class SearchViewlet extends Viewlet { this.results = div; this.results.addClass('show-file-icons'); - let dataSource = new SearchDataSource(this.contextService.hasMultiFolderWorkspace()); + let dataSource = new SearchDataSource(this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE); let renderer = this.instantiationService.createInstance(SearchRenderer, this.getActionRunner(), this); let dnd = new SimpleFileResourceDragAndDrop(obj => obj instanceof FileMatch ? obj.resource() : void 0); @@ -753,7 +753,7 @@ export class SearchViewlet extends Viewlet { public clearSearchResults(): void { this.viewModel.searchResult.clear(); this.showEmptyStage(); - if (!this.contextService.hasWorkspace()) { + if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { this.searchWithoutFolderMessage(this.clearMessage()); } this.searchWidget.clear(); @@ -871,22 +871,22 @@ export class SearchViewlet extends Viewlet { public searchInFolder(resource: URI): void { let folderPath = null; const workspace = this.contextService.getWorkspace(); - if (workspace && resource) { - if (this.contextService.hasFolderWorkspace()) { + if (resource) { + if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) { // Show relative path from the root for single-root mode - folderPath = paths.relative(workspace.roots[0].fsPath, resource.fsPath); + folderPath = paths.relative(workspace.folders[0].fsPath, resource.fsPath); if (folderPath && folderPath !== '.') { folderPath = './' + folderPath; } } else { - const owningRoot = this.contextService.getRoot(resource); - if (owningRoot) { - const owningRootBasename = paths.basename(owningRoot.fsPath); + const owningFolder = this.contextService.getWorkspaceFolder(resource); + if (owningFolder) { + const owningRootBasename = paths.basename(owningFolder.fsPath); // If this root is the only one with its basename, use a relative ./ path. If there is another, use an absolute path - const isUniqueRoot = workspace.roots.filter(root => paths.basename(root.fsPath) === owningRootBasename).length === 1; - if (isUniqueRoot) { - folderPath = `./${owningRootBasename}/${paths.relative(owningRoot.fsPath, resource.fsPath)}`; + const isUniqueFolder = workspace.folders.filter(root => paths.basename(root.fsPath) === owningRootBasename).length === 1; + if (isUniqueFolder) { + folderPath = `./${owningRootBasename}/${paths.relative(owningFolder.fsPath, resource.fsPath)}`; } else { folderPath = resource.fsPath; } @@ -960,7 +960,7 @@ export class SearchViewlet extends Viewlet { excludePattern, includePattern }; - const folderResources = this.contextService.hasWorkspace() ? this.contextService.getWorkspace().roots : []; + const folderResources = this.contextService.getWorkspace().folders; const onQueryValidationError = (err: Error) => { this.searchWidget.searchInput.showMessage({ content: err.message, type: MessageType.ERROR }); @@ -1123,7 +1123,7 @@ export class SearchViewlet extends Viewlet { }).on(dom.EventType.CLICK, (e: MouseEvent) => { dom.EventHelper.stop(e, false); - let editorPromise = this.contextService.hasWorkspace() ? this.preferencesService.openWorkspaceSettings() : this.preferencesService.openGlobalSettings(); + let editorPromise = this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? this.preferencesService.openWorkspaceSettings() : this.preferencesService.openGlobalSettings(); editorPromise.done(editor => { if (editor instanceof PreferencesEditor) { editor.focusSearch('.exclude'); @@ -1148,7 +1148,7 @@ export class SearchViewlet extends Viewlet { }); } - if (!this.contextService.hasWorkspace()) { + if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { this.searchWithoutFolderMessage(div); } } else { diff --git a/src/vs/workbench/parts/search/common/queryBuilder.ts b/src/vs/workbench/parts/search/common/queryBuilder.ts index 51e17c9a4fb..49fa52d03d5 100644 --- a/src/vs/workbench/parts/search/common/queryBuilder.ts +++ b/src/vs/workbench/parts/search/common/queryBuilder.ts @@ -12,7 +12,7 @@ import * as glob from 'vs/base/common/glob'; import * as paths from 'vs/base/common/paths'; import * as strings from 'vs/base/common/strings'; import uri from 'vs/base/common/uri'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IPatternInfo, IQueryOptions, IFolderQuery, ISearchQuery, QueryType, ISearchConfiguration, getExcludes, pathIncludedInQuery } from 'vs/platform/search/common/search'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -184,7 +184,7 @@ export class QueryBuilder { * Split search paths (./ or absolute paths in the includePatterns) into absolute paths and globs applied to those paths */ private expandSearchPathPatterns(searchPaths: string[]): ISearchPathPattern[] { - if (!this.workspaceContextService.hasWorkspace() || !searchPaths || !searchPaths.length) { + if (this.workspaceContextService.getWorkbenchState() === WorkbenchState.EMPTY || !searchPaths || !searchPaths.length) { // No workspace => ignore search paths return []; } @@ -212,17 +212,16 @@ export class QueryBuilder { return [paths.normalize(searchPath)]; } - const workspace = this.workspaceContextService.getWorkspace(); - if (this.workspaceContextService.hasFolderWorkspace()) { + if (this.workspaceContextService.getWorkbenchState() === WorkbenchState.FOLDER) { // TODO: @Sandy Try checking workspace folders length instead. return [paths.normalize( - paths.join(workspace.roots[0].fsPath, searchPath))]; + paths.join(this.workspaceContextService.getWorkspace().folders[0].fsPath, searchPath))]; } else if (searchPath === './') { return []; // ./ or ./**/foo makes sense for single-folder but not multi-folder workspaces } else { const relativeSearchPathMatch = searchPath.match(/\.[\/\\]([^\/\\]+)([\/\\].+)?/); if (relativeSearchPathMatch) { const searchPathRoot = relativeSearchPathMatch[1]; - const matchingRoots = workspace.roots.filter(root => paths.basename(root.fsPath) === searchPathRoot); + const matchingRoots = this.workspaceContextService.getWorkspace().folders.filter(folder => paths.basename(folder.fsPath) === searchPathRoot); if (matchingRoots.length) { return matchingRoots.map(root => { return relativeSearchPathMatch[2] ? diff --git a/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts index 9a492fa3f65..8c79981d63f 100644 --- a/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts +++ b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts @@ -154,7 +154,7 @@ suite('QueryBuilder', () => { const ROOT_2_URI = getUri(ROOT_2); const ROOT_3 = fixPath('/project/root3'); const ROOT_3_URI = getUri(ROOT_3); - mockWorkspace.roots = [ROOT_1_URI, ROOT_2_URI, ROOT_3_URI]; + mockWorkspace.folders = [ROOT_1_URI, ROOT_2_URI, ROOT_3_URI]; mockWorkspace.configuration = uri.file(fixPath('/config')); mockConfigService.setUserConfiguration('search', { @@ -473,7 +473,7 @@ suite('QueryBuilder', () => { test('relative includes w/two root folders', () => { const ROOT_2 = '/project/root2'; - mockWorkspace.roots = [ROOT_1_URI, getUri(ROOT_2)]; + mockWorkspace.folders = [ROOT_1_URI, getUri(ROOT_2)]; mockWorkspace.configuration = uri.file(fixPath('config')); [ @@ -513,7 +513,7 @@ suite('QueryBuilder', () => { test('relative includes w/multiple ambiguous root folders', () => { const ROOT_2 = '/project/rootB'; const ROOT_3 = '/otherproject/rootB'; - mockWorkspace.roots = [ROOT_1_URI, getUri(ROOT_2), getUri(ROOT_3)]; + mockWorkspace.folders = [ROOT_1_URI, getUri(ROOT_2), getUri(ROOT_3)]; mockWorkspace.configuration = uri.file(fixPath('/config')); [ diff --git a/src/vs/workbench/parts/tasks/browser/quickOpen.ts b/src/vs/workbench/parts/tasks/browser/quickOpen.ts index 2f64e812655..32a4d495b83 100644 --- a/src/vs/workbench/parts/tasks/browser/quickOpen.ts +++ b/src/vs/workbench/parts/tasks/browser/quickOpen.ts @@ -21,7 +21,7 @@ import { ActionBarContributor, ContributableActionProvider } from 'vs/workbench/ export class TaskEntry extends Model.QuickOpenEntry { - constructor(protected taskService: ITaskService, protected quickOpenService: IQuickOpenService, protected _task: CustomTask | ContributedTask, highlights: Model.IHighlight[] = []) { + constructor(protected quickOpenService: IQuickOpenService, protected taskService: ITaskService, protected _task: CustomTask | ContributedTask, highlights: Model.IHighlight[] = []) { super(highlights); } @@ -29,6 +29,17 @@ export class TaskEntry extends Model.QuickOpenEntry { return this.task._label; } + public getDescription(): string { + if (!this.taskService.hasMultipleFolders()) { + return null; + } + let workspaceFolder = Task.getWorkspaceFolder(this.task); + if (!workspaceFolder) { + return null; + } + return workspaceFolder.uri.fsPath; + } + public getAriaLabel(): string { return nls.localize('entryAriaLabel', "{0}, tasks", this.getLabel()); } diff --git a/src/vs/workbench/parts/tasks/browser/taskQuickOpen.ts b/src/vs/workbench/parts/tasks/browser/taskQuickOpen.ts index 46d3c023200..b7325389b2b 100644 --- a/src/vs/workbench/parts/tasks/browser/taskQuickOpen.ts +++ b/src/vs/workbench/parts/tasks/browser/taskQuickOpen.ts @@ -14,12 +14,11 @@ import { CustomTask, ContributedTask } from 'vs/workbench/parts/tasks/common/tas import { ITaskService } from 'vs/workbench/parts/tasks/common/taskService'; import { IExtensionService } from 'vs/platform/extensions/common/extensions'; - import * as base from './quickOpen'; class TaskEntry extends base.TaskEntry { - constructor(taskService: ITaskService, quickOpenService: IQuickOpenService, task: CustomTask | ContributedTask, highlights: Model.IHighlight[] = []) { - super(taskService, quickOpenService, task, highlights); + constructor(quickOpenService: IQuickOpenService, taskService: ITaskService, task: CustomTask | ContributedTask, highlights: Model.IHighlight[] = []) { + super(quickOpenService, taskService, task, highlights); } public run(mode: QuickOpen.Mode, context: Model.IContext): boolean { @@ -36,8 +35,8 @@ export class QuickOpenHandler extends base.QuickOpenHandler { constructor( @IQuickOpenService quickOpenService: IQuickOpenService, - @ITaskService taskService: ITaskService, - @IExtensionService extensionService: IExtensionService + @IExtensionService extensionService: IExtensionService, + @ITaskService taskService: ITaskService ) { super(quickOpenService, taskService); this.activationPromise = extensionService.activateByEvent('onCommand:workbench.action.tasks.runTask'); @@ -54,7 +53,7 @@ export class QuickOpenHandler extends base.QuickOpenHandler { } protected createEntry(task: CustomTask | ContributedTask, highlights: Model.IHighlight[]): base.TaskEntry { - return new TaskEntry(this.taskService, this.quickOpenService, task, highlights); + return new TaskEntry(this.quickOpenService, this.taskService, task, highlights); } public getEmptyLabel(searchString: string): string { diff --git a/src/vs/workbench/parts/tasks/common/taskService.ts b/src/vs/workbench/parts/tasks/common/taskService.ts index 7598b170398..d56ad59ce58 100644 --- a/src/vs/workbench/parts/tasks/common/taskService.ts +++ b/src/vs/workbench/parts/tasks/common/taskService.ts @@ -59,7 +59,8 @@ export interface ITaskService extends IEventEmitter { getTasksForGroup(group: string): TPromise; getRecentlyUsedTasks(): LinkedMap; - canCustomize(): boolean; + hasMultipleFolders(); + canCustomize(task: ContributedTask | CustomTask): boolean; customize(task: ContributedTask | CustomTask, properties?: {}, openConfig?: boolean): TPromise; openConfig(task: CustomTask): TPromise; diff --git a/src/vs/workbench/parts/tasks/common/tasks.ts b/src/vs/workbench/parts/tasks/common/tasks.ts index fd9b28ce2d5..9c15a51436b 100644 --- a/src/vs/workbench/parts/tasks/common/tasks.ts +++ b/src/vs/workbench/parts/tasks/common/tasks.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; +import URI from 'vs/base/common/uri'; import * as Types from 'vs/base/common/types'; import { IJSONSchemaMap } from 'vs/base/common/jsonSchema'; @@ -215,34 +216,47 @@ export namespace TaskGroup { export type TaskGroup = 'clean' | 'build' | 'rebuild' | 'test'; +export enum TaskScope { + Global = 1, + Workspace = 2, + Folder = 3 +} + export namespace TaskSourceKind { export const Workspace: 'workspace' = 'workspace'; export const Extension: 'extension' = 'extension'; export const Composite: 'composite' = 'composite'; } +export interface WorkspaceFolder { + uri: URI; +} + export interface TaskSourceConfigElement { + workspaceFolder: WorkspaceFolder; file: string; index: number; element: any; } export interface WorkspaceTaskSource { - kind: 'workspace'; - label: string; - config: TaskSourceConfigElement; - customizes?: TaskIdentifier; + readonly kind: 'workspace'; + readonly label: string; + readonly config: TaskSourceConfigElement; + readonly customizes?: TaskIdentifier; } export interface ExtensionTaskSource { - kind: 'extension'; - label: string; - extension: string; + readonly kind: 'extension'; + readonly label: string; + readonly extension: string; + readonly scope: TaskScope; + readonly workspaceFolder: WorkspaceFolder | undefined; } export interface CompositeTaskSource { - kind: 'composite'; - label: string; + readonly kind: 'composite'; + readonly label: string; } export type TaskSource = WorkspaceTaskSource | ExtensionTaskSource | CompositeTaskSource; @@ -411,6 +425,16 @@ export namespace Task { } } + export function getWorkspaceFolder(task: Task): WorkspaceFolder | undefined { + if (CustomTask.is(task)) { + return task._source.config.workspaceFolder; + } else if (ContributedTask.is(task)) { + return task._source.workspaceFolder; + } else { + return undefined; + } + } + export function getTelemetryKind(task: Task): string { if (ContributedTask.is(task)) { return 'extension'; diff --git a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts index b96413b5374..c02e59693d7 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts @@ -67,7 +67,7 @@ import { IPartService } from 'vs/workbench/services/part/common/partService'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { IConfigurationEditingService, ConfigurationTarget, IConfigurationValue } from 'vs/workbench/services/configuration/common/configurationEditing'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IOutputService, IOutputChannelRegistry, Extensions as OutputExt, IOutputChannel } from 'vs/workbench/parts/output/common/output'; @@ -76,7 +76,7 @@ import { Scope, IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs import { ITerminalService } from 'vs/workbench/parts/terminal/common/terminal'; import { ITaskSystem, ITaskResolver, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, TaskSystemEvents, TaskTerminateResponse } from 'vs/workbench/parts/tasks/common/taskSystem'; -import { Task, CustomTask, ConfiguringTask, ContributedTask, CompositeTask, TaskSet, TaskGroup, ExecutionEngine, JsonSchemaVersion, TaskSourceKind, TaskIdentifier } from 'vs/workbench/parts/tasks/common/tasks'; +import { Task, CustomTask, ConfiguringTask, ContributedTask, CompositeTask, TaskSet, TaskGroup, ExecutionEngine, JsonSchemaVersion, TaskSourceKind, TaskIdentifier, WorkspaceFolder } from 'vs/workbench/parts/tasks/common/tasks'; import { ITaskService, TaskServiceEvents, ITaskProvider, TaskEvent, RunOptions, CustomizationProperties } from 'vs/workbench/parts/tasks/common/taskService'; import { templates as taskTemplates } from 'vs/workbench/parts/tasks/common/taskTemplates'; @@ -113,7 +113,7 @@ abstract class OpenTaskConfigurationAction extends Action { } public run(event?: any): TPromise { - if (!this.contextService.hasWorkspace()) { + if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { this.messageService.show(Severity.Info, nls.localize('ConfigureTaskRunnerAction.noWorkspace', 'Tasks are only available on a workspace folder.')); return TPromise.as(undefined); } @@ -301,14 +301,14 @@ class BuildStatusBarItem extends Themable implements IStatusbarItem { } private registerListeners(): void { - this.toUnbind.push(this.contextService.onDidChangeWorkspaceRoots(() => this.updateStyles())); + this.toUnbind.push(this.contextService.onDidChangeWorkspaceFolders(() => this.updateStyles())); } protected updateStyles(): void { super.updateStyles(); this.icons.forEach(icon => { - icon.style.backgroundColor = this.getColor(this.contextService.hasWorkspace() ? STATUS_BAR_FOREGROUND : STATUS_BAR_NO_FOLDER_FOREGROUND); + icon.style.backgroundColor = this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_FOREGROUND : STATUS_BAR_NO_FOLDER_FOREGROUND); }); } @@ -618,7 +618,12 @@ interface WorkspaceTaskResult { hasErrors: boolean; } -interface WorkspaceConfigurationResult { +interface WorkspaceFolderTaskResult extends WorkspaceTaskResult { + workspaceFolder: WorkspaceFolder; +} + +interface WorkspaceFolderConfigurationResult { + workspaceFolder: WorkspaceFolder; config: TaskConfig.ExternalTaskRunnerConfiguration; hasErrors: boolean; } @@ -657,9 +662,12 @@ class TaskService extends EventEmitter implements ITaskService { private quickOpenService: IQuickOpenService; private _configHasErrors: boolean; + private _schemaVersion: JsonSchemaVersion; + private _executionEngine: ExecutionEngine; + private _workspaceFolders: WorkspaceFolder[]; private _providers: Map; - private _workspaceTasksPromise: TPromise; + private _workspaceTasksPromise: TPromise>; private _taskSystem: ITaskSystem; private _taskSystemListeners: IDisposable[]; @@ -711,22 +719,14 @@ class TaskService extends EventEmitter implements ITaskService { if (!this._taskSystem && !this._workspaceTasksPromise) { return; } - this.updateWorkspaceTasks(); - if (!this._taskSystem) { - return; - } - let currentExecutionEngine = this._taskSystem instanceof TerminalTaskSystem - ? ExecutionEngine.Terminal - : this._taskSystem instanceof ProcessTaskSystem - ? ExecutionEngine.Process - : ExecutionEngine._default; - if (currentExecutionEngine !== this.getExecutionEngine()) { + let folderSetup = this.computeWorkspaceFolders(); + if (this._executionEngine !== folderSetup[1] && this._taskSystem && this._taskSystem.getActiveTasks().length > 0) { this.messageService.show( Severity.Info, { message: nls.localize( 'TaskSystem.noHotSwap', - 'Changing the task execution engine requires to reload the Window' + 'Changing the task execution engine with an active task running requires to reload the Window' ), actions: [ new ReloadWindowAction(ReloadWindowAction.ID, ReloadWindowAction.LABEL, this._windowServive), @@ -734,8 +734,17 @@ class TaskService extends EventEmitter implements ITaskService { ] } ); + return; } + this._workspaceFolders = folderSetup[0]; + this._executionEngine = folderSetup[1]; + this._schemaVersion = folderSetup[2]; + this.updateWorkspaceTasks(); }); + let folderSetup = this.computeWorkspaceFolders(); + this._workspaceFolders = folderSetup[0]; + this._executionEngine = folderSetup[1]; + this._schemaVersion = folderSetup[2]; lifecycleService.onWillShutdown(event => event.veto(this.beforeShutdown())); this.registerCommands(); } @@ -814,20 +823,14 @@ class TaskService extends EventEmitter implements ITaskService { } public getTask(identifier: string): TPromise { - return this.getTaskSets().then((sets) => { - let resolver = this.createResolver(sets); + return this.getAllTasks().then((tasks) => { + let resolver = this.createResolver(tasks); return resolver.resolve(identifier); }); } public tasks(): TPromise { - return this.getTaskSets().then((sets) => { - let result: Task[] = []; - for (let set of sets) { - result.push(...set.tasks); - } - return result; - }); + return this.getAllTasks(); }; public isActive(): TPromise { @@ -881,10 +884,10 @@ class TaskService extends EventEmitter implements ITaskService { } public build(): TPromise { - return this.getTaskSets().then((values) => { - let runnable = this.createRunnableTask(values, TaskGroup.Build); + return this.getAllTasks().then((tasks) => { + let runnable = this.createRunnableTask(tasks, TaskGroup.Build); if (!runnable || !runnable.task) { - if (this.getJsonSchemaVersion() === JsonSchemaVersion.V0_1_0) { + if (this._schemaVersion === JsonSchemaVersion.V0_1_0) { throw new TaskError(Severity.Info, nls.localize('TaskService.noBuildTask1', 'No build task defined. Mark a task with \'isBuildCommand\' in the tasks.json file.'), TaskErrors.NoBuildTask); } else { throw new TaskError(Severity.Info, nls.localize('TaskService.noBuildTask2', 'No build task defined. Mark a task with as a \'build\' group in the tasks.json file.'), TaskErrors.NoBuildTask); @@ -906,10 +909,10 @@ class TaskService extends EventEmitter implements ITaskService { } public runTest(): TPromise { - return this.getTaskSets().then((values) => { - let runnable = this.createRunnableTask(values, TaskGroup.Test); + return this.getAllTasks().then((tasks) => { + let runnable = this.createRunnableTask(tasks, TaskGroup.Test); if (!runnable || !runnable.task) { - if (this.getJsonSchemaVersion() === JsonSchemaVersion.V0_1_0) { + if (this._schemaVersion === JsonSchemaVersion.V0_1_0) { throw new TaskError(Severity.Info, nls.localize('TaskService.noTestTask1', 'No test task defined. Mark a task with \'isTestCommand\' in the tasks.json file.'), TaskErrors.NoTestTask); } else { throw new TaskError(Severity.Info, nls.localize('TaskService.noTestTask2', 'No test task defined. Mark a task with as a \'test\' group in the tasks.json file.'), TaskErrors.NoTestTask); @@ -923,8 +926,8 @@ class TaskService extends EventEmitter implements ITaskService { } public run(task: string | Task, options?: RunOptions): TPromise { - return this.getTaskSets().then((values) => { - let resolver = this.createResolver(values); + return this.getAllTasks().then((tasks) => { + let resolver = this.createResolver(tasks); let requested: string; let toExecute: Task; if (Types.isString(task)) { @@ -955,7 +958,7 @@ class TaskService extends EventEmitter implements ITaskService { } private shouldAttachProblemMatcher(task: Task): boolean { - if (!this.canCustomize()) { + if (!this.canCustomize(task)) { return false; } if (task.group !== void 0 && task.group !== TaskGroup.Build) { @@ -1033,25 +1036,40 @@ class TaskService extends EventEmitter implements ITaskService { } public getTasksForGroup(group: string): TPromise { - return this.getTaskSets().then((values) => { + return this.getAllTasks().then((tasks) => { let result: Task[] = []; - for (let value of values) { - for (let task of value.tasks) { - if (task.group === group) { - result.push(task); - } + for (let task of tasks) { + if (task.group === group) { + result.push(task); } } return result; }); } - public canCustomize(): boolean { - return this.getJsonSchemaVersion() === JsonSchemaVersion.V2_0_0; + public hasMultipleFolders(): boolean { + return this._workspaceFolders && this._workspaceFolders.length > 1; + } + + public canCustomize(task: Task): boolean { + if (this._schemaVersion !== JsonSchemaVersion.V2_0_0) { + return false; + } + if (CustomTask.is(task)) { + return true; + } + if (ContributedTask.is(task)) { + return !!Task.getWorkspaceFolder(task); + } + return false; } public customize(task: ContributedTask | CustomTask, properties?: CustomizationProperties, openConfig?: boolean): TPromise { - let configuration = this.getConfiguration(); + let workspaceFolder = Task.getWorkspaceFolder(task); + if (!workspaceFolder) { + return TPromise.as(undefined); + } + let configuration = this.getConfiguration(workspaceFolder); if (configuration.hasParseErrors) { this.messageService.show(Severity.Warning, nls.localize('customizeParseErrors', 'The current task configuration has errors. Please fix the errors first before customizing a task.')); return TPromise.as(undefined); @@ -1114,12 +1132,12 @@ class TaskService extends EventEmitter implements ITaskService { fileConfig.problemMatcher = properties.problemMatcher; value.key = 'tasks.problemMatchers'; value.value = fileConfig.problemMatcher; - promise = this.configurationEditingService.writeConfiguration(ConfigurationTarget.WORKSPACE, value); + promise = this.writeConfiguration(workspaceFolder, value); } else if (properties.group !== void 0) { fileConfig.group = properties.group; value.key = 'tasks.group'; value.value = fileConfig.group; - promise = this.configurationEditingService.writeConfiguration(ConfigurationTarget.WORKSPACE, value); + promise = this.writeConfiguration(workspaceFolder, value); } } else { if (!Array.isArray(fileConfig.tasks)) { @@ -1132,7 +1150,7 @@ class TaskService extends EventEmitter implements ITaskService { } else { fileConfig.tasks[index] = toCustomize; } - promise = this.configurationEditingService.writeConfiguration(ConfigurationTarget.WORKSPACE, value); + promise = this.writeConfiguration(workspaceFolder, value); } }; if (!promise) { @@ -1156,7 +1174,18 @@ class TaskService extends EventEmitter implements ITaskService { }); } + private writeConfiguration(workspaceFolder: WorkspaceFolder, value: IConfigurationValue): TPromise { + if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) { + return this.configurationEditingService.writeConfiguration(ConfigurationTarget.WORKSPACE, value); + } else if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { + return this.configurationEditingService.writeConfiguration(ConfigurationTarget.FOLDER, value, { scopes: { resource: workspaceFolder.uri } }); + } else { + return undefined; + } + } + public openConfig(task: CustomTask): TPromise { + // @ToDo need to adopt since this is not working anymore let resource = this.contextService.toResource(task._source.config.file); return this.editorService.openEditor({ resource: resource, @@ -1167,26 +1196,24 @@ class TaskService extends EventEmitter implements ITaskService { }, false).then(() => undefined); } - private createRunnableTask(sets: TaskSet[], group: TaskGroup): { task: Task; resolver: ITaskResolver } { + private createRunnableTask(tasks: Task[], group: TaskGroup): { task: Task; resolver: ITaskResolver } { let idMap: IStringDictionary = Object.create(null); let labelMap: IStringDictionary = Object.create(null); let identifierMap: IStringDictionary = Object.create(null); let workspaceTasks: Task[] = []; let extensionTasks: Task[] = []; - sets.forEach((set) => { - set.tasks.forEach((task) => { - idMap[task._id] = task; - labelMap[task._label] = task; - identifierMap[task.identifier] = task; - if (group && task.group === group) { - if (task._source.kind === TaskSourceKind.Workspace) { - workspaceTasks.push(task); - } else { - extensionTasks.push(task); - } + tasks.forEach((task) => { + idMap[task._id] = task; + labelMap[task._label] = task; + identifierMap[task.identifier] = task; + if (group && task.group === group) { + if (task._source.kind === TaskSourceKind.Workspace) { + workspaceTasks.push(task); + } else { + extensionTasks.push(task); } - }); + } }); let resolver: ITaskResolver = { resolve: (id: string) => { @@ -1222,15 +1249,13 @@ class TaskService extends EventEmitter implements ITaskService { } } - private createResolver(sets: TaskSet[]): ITaskResolver { + private createResolver(tasks: Task[]): ITaskResolver { let labelMap: IStringDictionary = Object.create(null); let identifierMap: IStringDictionary = Object.create(null); - sets.forEach((set) => { - set.tasks.forEach((task) => { - labelMap[task._label] = task; - identifierMap[task.identifier] = task; - }); + tasks.forEach((task) => { + labelMap[task._label] = task; + identifierMap[task.identifier] = task; }); return { resolve: (id: string) => { @@ -1299,8 +1324,7 @@ class TaskService extends EventEmitter implements ITaskService { if (this._taskSystem) { return this._taskSystem; } - let engine = this.getExecutionEngine(); - if (engine === ExecutionEngine.Terminal) { + if (this._executionEngine === ExecutionEngine.Terminal) { this._taskSystem = new TerminalTaskSystem( this.terminalService, this.outputService, this.markerService, this.modelService, this.configurationResolverService, this.telemetryService, @@ -1322,7 +1346,7 @@ class TaskService extends EventEmitter implements ITaskService { return this._taskSystem; } - private getTaskSets(): TPromise { + private getAllTasks(): TPromise { return this.extensionService.activateByEvent('onCommand:workbench.action.tasks.runTask').then(() => { return new TPromise((resolve, reject) => { let result: TaskSet[] = []; @@ -1340,7 +1364,7 @@ class TaskService extends EventEmitter implements ITaskService { resolve(result); } }; - if (this.getJsonSchemaVersion() === JsonSchemaVersion.V2_0_0 && this._providers.size > 0) { + if (this._schemaVersion === JsonSchemaVersion.V2_0_0 && this._providers.size > 0) { this._providers.forEach((provider) => { counter++; provider.provideTasks().done(done, error); @@ -1349,57 +1373,95 @@ class TaskService extends EventEmitter implements ITaskService { resolve(result); } }); - }).then((result) => { - return this.getWorkspaceTasks().then((workspaceTaskResult) => { - let workspaceTasksToDelete: Task[] = []; - let configurations = workspaceTaskResult.configurations; - let legacyTaskConfigurations = workspaceTaskResult.set ? this.getLegacyTaskConfigurations(workspaceTaskResult.set) : undefined; - if (configurations || legacyTaskConfigurations) { - for (let set of result) { - for (let i = 0; i < set.tasks.length; i++) { - let task = set.tasks[i]; - if (!ContributedTask.is(task)) { - continue; - } - if (configurations) { - let configuringTask = configurations.byIdentifier[task.defines._key]; - if (configuringTask) { - set.tasks[i] = TaskConfig.createCustomTask(task, configuringTask); + }).then((contributedTaskSets) => { + let result: Task[] = []; + let contributedTasks: Map = new Map(); + for (let set of contributedTaskSets) { + for (let task of set.tasks) { + if (!ContributedTask.is(task)) { + continue; + } + let workspaceFolder = task._source.workspaceFolder; + if (workspaceFolder) { + let values = contributedTasks.get(workspaceFolder.uri.toString()); + if (!values) { + values = [task]; + contributedTasks.set(workspaceFolder.uri.toString(), values); + } else { + values.push(task); + } + } else { + result.push(task); + } + } + } + return this.getWorkspaceTasks().then((customTasks) => { + customTasks.forEach((folderTasks, key) => { + let contributed = contributedTasks.get(key); + if (!folderTasks.set) { + if (contributed) { + result.push(...contributed); + } + return; + } + + if (!contributed) { + result.push(...folderTasks.set.tasks); + } else { + let configurations = folderTasks.configurations; + let legacyTaskConfigurations = folderTasks.set ? this.getLegacyTaskConfigurations(folderTasks.set) : undefined; + let customTasksToDelete: Task[] = []; + if (configurations || legacyTaskConfigurations) { + for (let task of contributed) { + if (!ContributedTask.is(task)) { continue; } - } - if (legacyTaskConfigurations) { - let configuringTask = legacyTaskConfigurations[task.defines._key]; - if (configuringTask) { - set.tasks[i] = TaskConfig.createCustomTask(task, configuringTask); - workspaceTasksToDelete.push(configuringTask); - set.tasks[i] = configuringTask; - continue; + if (configurations) { + let configuringTask = configurations.byIdentifier[task.defines._key]; + if (configuringTask) { + result.push(TaskConfig.createCustomTask(task, configuringTask)); + } else { + result.push(task); + } + } else if (legacyTaskConfigurations) { + let configuringTask = legacyTaskConfigurations[task.defines._key]; + if (configuringTask) { + result.push(TaskConfig.createCustomTask(task, configuringTask)); + customTasksToDelete.push(configuringTask); + } else { + result.push(task); + } + } else { + result.push(task); } } + if (customTasksToDelete.length > 0) { + let toDelete = customTasksToDelete.reduce>((map, task) => { + map[task._id] = true; + return map; + }, Object.create(null)); + for (let task of folderTasks.set.tasks) { + if (toDelete[task._id]) { + continue; + } + result.push(task); + } + } else { + result.push(...folderTasks.set.tasks); + } + } else { + result.push(...folderTasks.set.tasks); + result.push(...contributed); } } - } - if (workspaceTaskResult.set) { - if (workspaceTasksToDelete.length > 0) { - let tasks = workspaceTaskResult.set.tasks; - let newSet: TaskSet = { - extension: workspaceTaskResult.set.extension, - tasks: [] - }; - let toDelete = workspaceTasksToDelete.reduce>((map, task) => { - map[task._id] = true; - return map; - }, Object.create(null)); - newSet.tasks = tasks.filter(task => !toDelete[task._id]); - result.push(newSet); - } else { - result.push(workspaceTaskResult.set); - } - } + }); return result; }, () => { // If we can't read the tasks.json file provide at least the contributed tasks + let result: Task[] = []; + for (let set of contributedTaskSets) { + result.push(...set.tasks); + } return result; }); }); @@ -1431,7 +1493,7 @@ class TaskService extends EventEmitter implements ITaskService { return result; } - private getWorkspaceTasks(): TPromise { + private getWorkspaceTasks(): TPromise> { if (this._workspaceTasksPromise) { return this._workspaceTasksPromise; } @@ -1441,113 +1503,162 @@ class TaskService extends EventEmitter implements ITaskService { private updateWorkspaceTasks(): void { this._workspaceTasksPromise = this.computeWorkspaceTasks().then(value => { - this._configHasErrors = value.hasErrors; - if (this._taskSystem instanceof ProcessTaskSystem) { - this._taskSystem.hasErrors(this._configHasErrors); + if (this._executionEngine === ExecutionEngine.Process && this._taskSystem instanceof ProcessTaskSystem) { + // We can only have a process engine if we have one folder. + value.forEach((value) => { + this._configHasErrors = value.hasErrors; + (this._taskSystem as ProcessTaskSystem).hasErrors(this._configHasErrors); + }); } return value; }); } - private computeWorkspaceTasks(): TPromise { - let configPromise: TPromise; - { - let { config, hasParseErrors } = this.getConfiguration(); - if (hasParseErrors) { - return TPromise.as({ set: undefined, hasErrors: true, configurations: undefined }); + private computeWorkspaceTasks(): TPromise> { + if (this._workspaceFolders.length === 0) { + return TPromise.as(new Map()); + } else { + let promises: TPromise[] = []; + for (let folder of this._workspaceFolders) { + promises.push(this.computeWorkspaceFolderTasks(folder).then((value) => value, () => undefined)); } - let engine = ExecutionEngine._default; - if (config) { - engine = TaskConfig.ExecutionEngine.from(config); - if (engine === ExecutionEngine.Process) { - if (this.hasDetectorSupport(config)) { - configPromise = new ProcessRunnerDetector(this.fileService, this.contextService, this.configurationResolverService, config).detect(true).then((value): WorkspaceConfigurationResult => { - let hasErrors = this.printStderr(value.stderr); - let detectedConfig = value.config; - if (!detectedConfig) { - return { config, hasErrors }; - } - let result: TaskConfig.ExternalTaskRunnerConfiguration = Objects.clone(config); - let configuredTasks: IStringDictionary = Object.create(null); - if (!result.tasks) { - if (detectedConfig.tasks) { - result.tasks = detectedConfig.tasks; - } - } else { - result.tasks.forEach(task => configuredTasks[task.taskName] = task); - detectedConfig.tasks.forEach((task) => { - if (!configuredTasks[task.taskName]) { - result.tasks.push(task); - } - }); - } - return { config: result, hasErrors }; - }); - } else { - configPromise = TPromise.as({ config, hasErrors: false }); + return TPromise.join(promises).then((values) => { + let result = new Map(); + for (let value of values) { + if (value) { + result.set(value.workspaceFolder.uri.toString(), value); } - } else { - configPromise = TPromise.as({ config, hasErrors: false }); } + return result; + }); + } + } + + private computeWorkspaceFolderTasks(workspaceFolder: WorkspaceFolder): TPromise { + return (this._executionEngine === ExecutionEngine.Process + ? this.computeLegacyConfiguration(workspaceFolder) + : this.computeConfiguration(workspaceFolder)). + then((workspaceFolderConfiguration) => { + if (!workspaceFolderConfiguration || !workspaceFolderConfiguration.config || workspaceFolderConfiguration.hasErrors) { + return TPromise.as({ workspaceFolder, set: undefined, configurations: undefined, hasErrors: workspaceFolderConfiguration ? workspaceFolderConfiguration.hasErrors : false }); + } + return ProblemMatcherRegistry.onReady().then((): WorkspaceFolderTaskResult => { + let problemReporter = new ProblemReporter(this._outputChannel); + let parseResult = TaskConfig.parse(workspaceFolder, workspaceFolderConfiguration.config, problemReporter); + let hasErrors = false; + if (!parseResult.validationStatus.isOK()) { + hasErrors = true; + this.showOutput(); + } + if (problemReporter.status.isFatal()) { + problemReporter.fatal(nls.localize('TaskSystem.configurationErrors', 'Error: the provided task configuration has validation errors and can\'t not be used. Please correct the errors first.')); + return { workspaceFolder, set: undefined, configurations: undefined, hasErrors }; + } + let customizedTasks: { byIdentifier: IStringDictionary; }; + if (parseResult.configured && parseResult.configured.length > 0) { + customizedTasks = { + byIdentifier: Object.create(null) + }; + for (let task of parseResult.configured) { + customizedTasks.byIdentifier[task.configures._key] = task; + } + } + return { workspaceFolder, set: { tasks: parseResult.custom }, configurations: customizedTasks, hasErrors }; + }); + }); + } + + private computeConfiguration(workspaceFolder: WorkspaceFolder): TPromise { + let { config, hasParseErrors } = this.getConfiguration(workspaceFolder); + return TPromise.as({ workspaceFolder, config, hasErrors: hasParseErrors }); + } + + private computeLegacyConfiguration(workspaceFolder: WorkspaceFolder): TPromise { + let { config, hasParseErrors } = this.getConfiguration(workspaceFolder); + if (hasParseErrors) { + return TPromise.as({ workspaceFolder: workspaceFolder, hasErrors: true, config: undefined }); + } + if (config) { + if (this.hasDetectorSupport(config)) { + return new ProcessRunnerDetector(this.fileService, this.contextService, this.configurationResolverService, config).detect(true).then((value): WorkspaceFolderConfigurationResult => { + let hasErrors = this.printStderr(value.stderr); + let detectedConfig = value.config; + if (!detectedConfig) { + return { workspaceFolder, config, hasErrors }; + } + let result: TaskConfig.ExternalTaskRunnerConfiguration = Objects.clone(config); + let configuredTasks: IStringDictionary = Object.create(null); + if (!result.tasks) { + if (detectedConfig.tasks) { + result.tasks = detectedConfig.tasks; + } + } else { + result.tasks.forEach(task => configuredTasks[task.taskName] = task); + detectedConfig.tasks.forEach((task) => { + if (!configuredTasks[task.taskName]) { + result.tasks.push(task); + } + }); + } + return { workspaceFolder, config: result, hasErrors }; + }); } else { - if (engine === ExecutionEngine.Terminal) { - configPromise = TPromise.as({ config, hasErrors: false }); + return TPromise.as({ workspaceFolder, config, hasErrors: false }); + } + } else { + return new ProcessRunnerDetector(this.fileService, this.contextService, this.configurationResolverService).detect(true).then((value) => { + let hasErrors = this.printStderr(value.stderr); + return { workspaceFolder, config: value.config, hasErrors }; + }); + } + } + + private computeWorkspaceFolders(): [WorkspaceFolder[], ExecutionEngine, JsonSchemaVersion] { + let workspaceFolders: WorkspaceFolder[] = []; + let executionEngine = ExecutionEngine.Terminal; + let schemaVersion = JsonSchemaVersion.V2_0_0; + + if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) { + let workspaceFolder: WorkspaceFolder = { uri: this.contextService.getWorkspace().folders[0] }; + workspaceFolders.push(workspaceFolder); + executionEngine = this.computeExecutionEngine(workspaceFolder); + schemaVersion = this.computeJsonSchemaVersion(workspaceFolder); + } else if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { + for (let folder of this.contextService.getWorkspace().folders) { + let workspaceFolder = { uri: folder }; + if (schemaVersion === this.computeJsonSchemaVersion(workspaceFolder)) { + workspaceFolders.push(workspaceFolder); } else { - configPromise = new ProcessRunnerDetector(this.fileService, this.contextService, this.configurationResolverService).detect(true).then((value) => { - let hasErrors = this.printStderr(value.stderr); - return { config: value.config, hasErrors }; - }); + this._outputChannel.append(nls.localize( + 'taskService.ignoreingFolder', + 'Ignoring task configurations for workspace folder {0}. Multi root folder support requires that all folders use task version 2.0.', + folder.fsPath)); } } } - return configPromise.then((resolved) => { - return ProblemMatcherRegistry.onReady().then((): WorkspaceTaskResult => { - if (!resolved || !resolved.config) { - return { set: undefined, configurations: undefined, hasErrors: resolved !== void 0 ? resolved.hasErrors : false }; - } - let problemReporter = new ProblemReporter(this._outputChannel); - let parseResult = TaskConfig.parse(resolved.config, problemReporter); - let hasErrors = false; - if (!parseResult.validationStatus.isOK()) { - hasErrors = true; - this.showOutput(); - } - if (problemReporter.status.isFatal()) { - problemReporter.fatal(nls.localize('TaskSystem.configurationErrors', 'Error: the provided task configuration has validation errors and can\'t not be used. Please correct the errors first.')); - return { set: undefined, configurations: undefined, hasErrors }; - } - let customizedTasks: { byIdentifier: IStringDictionary; }; - if (parseResult.configured && parseResult.configured.length > 0) { - customizedTasks = { - byIdentifier: Object.create(null) - }; - for (let task of parseResult.configured) { - customizedTasks.byIdentifier[task.configures._key] = task; - } - } - return { set: { tasks: parseResult.custom }, configurations: customizedTasks, hasErrors }; - }); - }); + return [workspaceFolders, executionEngine, schemaVersion]; } - private getExecutionEngine(): ExecutionEngine { - let { config } = this.getConfiguration(); + private computeExecutionEngine(workspaceFolder: WorkspaceFolder): ExecutionEngine { + let { config } = this.getConfiguration(workspaceFolder); if (!config) { return ExecutionEngine._default; } return TaskConfig.ExecutionEngine.from(config); } - private getJsonSchemaVersion(): JsonSchemaVersion { - let { config } = this.getConfiguration(); + private computeJsonSchemaVersion(workspaceFolder: WorkspaceFolder): JsonSchemaVersion { + let { config } = this.getConfiguration(workspaceFolder); if (!config) { return JsonSchemaVersion.V2_0_0; } return TaskConfig.JsonSchemaVersion.from(config); } - private getConfiguration(): { config: TaskConfig.ExternalTaskRunnerConfiguration; hasParseErrors: boolean } { - let result = this.contextService.hasWorkspace() ? this.configurationService.getConfiguration('tasks', { resource: this.contextService.getLegacyWorkspace().resource }) : undefined; + private getConfiguration(workspaceFolder: WorkspaceFolder): { config: TaskConfig.ExternalTaskRunnerConfiguration; hasParseErrors: boolean } { + let result = this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY + ? this.configurationService.getConfiguration('tasks', { resource: workspaceFolder.uri }) + : undefined; if (!result) { return { config: undefined, hasParseErrors: false }; } @@ -1585,11 +1696,11 @@ class TaskService extends EventEmitter implements ITaskService { if (this._taskSystem) { return this._taskSystem instanceof TerminalTaskSystem; } - return this.getExecutionEngine() === ExecutionEngine.Terminal; + return this._executionEngine === ExecutionEngine.Terminal; } private hasDetectorSupport(config: TaskConfig.ExternalTaskRunnerConfiguration): boolean { - if (!config.command || !this.contextService.hasWorkspace()) { + if (!config.command || this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { return false; } return ProcessRunnerDetector.supports(config.command); @@ -1700,7 +1811,7 @@ class TaskService extends EventEmitter implements ITaskService { } private canRunCommand(): boolean { - if (!this.contextService.hasWorkspace()) { + if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { this.messageService.show(Severity.Info, nls.localize('TaskService.noWorkspace', 'Tasks are only available on a workspace folder.')); return false; } @@ -1797,7 +1908,7 @@ class TaskService extends EventEmitter implements ITaskService { if (!this.canRunCommand()) { return; } - if (this.getJsonSchemaVersion() === JsonSchemaVersion.V0_1_0) { + if (this._schemaVersion === JsonSchemaVersion.V0_1_0) { this.build(); return; } @@ -1840,7 +1951,7 @@ class TaskService extends EventEmitter implements ITaskService { if (!this.canRunCommand()) { return; } - if (this.getJsonSchemaVersion() === JsonSchemaVersion.V0_1_0) { + if (this._schemaVersion === JsonSchemaVersion.V0_1_0) { this.runTest(); return; } @@ -1946,7 +2057,7 @@ class TaskService extends EventEmitter implements ITaskService { if (!this.canRunCommand()) { return; } - if (this.getJsonSchemaVersion() === JsonSchemaVersion.V2_0_0) { + if (this._schemaVersion === JsonSchemaVersion.V2_0_0) { this.tasks().then((tasks => { if (tasks.length === 0) { this.configureBuildTask().run(); @@ -1981,7 +2092,7 @@ class TaskService extends EventEmitter implements ITaskService { if (!this.canRunCommand()) { return; } - if (this.getJsonSchemaVersion() === JsonSchemaVersion.V2_0_0) { + if (this._schemaVersion === JsonSchemaVersion.V2_0_0) { this.tasks().then((tasks => { if (tasks.length === 0) { this.configureAction().run(); diff --git a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts index 39bd49ba690..cf38b3bcb18 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts @@ -647,7 +647,7 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem { private resolveVariable(value: string): string { // TODO@Dirk adopt new configuration resolver service https://github.com/Microsoft/vscode/issues/31365 - return this.configurationResolverService.resolve(this.contextService.getLegacyWorkspace().resource, value); + return this.configurationResolverService.resolve(this.contextService.getWorkspace().folders[0], value); } private resolveOptions(options: CommandOptions): CommandOptions { diff --git a/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts b/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts index bbc05d0d7d4..e0e81b063ff 100644 --- a/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts +++ b/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts @@ -18,7 +18,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import * as Tasks from '../common/tasks'; import * as TaskConfig from './taskConfiguration'; @@ -156,7 +156,7 @@ export class ProcessRunnerDetector { this.taskConfiguration = config; this._stderr = []; this._stdout = []; - this._cwd = this.contextService.hasWorkspace() ? Paths.normalize(this.contextService.getLegacyWorkspace().resource.fsPath, true) : ''; + this._cwd = this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? Paths.normalize(this.contextService.getWorkspace().folders[0].fsPath, true) : ''; } public get stderr(): string[] { @@ -175,7 +175,7 @@ export class ProcessRunnerDetector { let isShellCommand = !!this.taskConfiguration.isShellCommand; // TODO@Dirk adopt new configuration resolver service https://github.com/Microsoft/vscode/issues/31365 return this.runDetection( - new LineProcess(this.taskConfiguration.command, this.configurationResolverService.resolve(this.contextService.getLegacyWorkspace().resource, args), isShellCommand, options), + new LineProcess(this.taskConfiguration.command, this.configurationResolverService.resolve(this.contextService.getWorkspace().folders[0], args), isShellCommand, options), this.taskConfiguration.command, isShellCommand, config.matcher, ProcessRunnerDetector.DefaultProblemMatchers, list); } else { if (detectSpecific) { @@ -219,10 +219,10 @@ export class ProcessRunnerDetector { // TODO@Dirk adopt new configuration resolver service https://github.com/Microsoft/vscode/issues/31365 let result = Objects.clone(options); if (result.cwd) { - result.cwd = this.configurationResolverService.resolve(this.contextService.getLegacyWorkspace().resource, result.cwd); + result.cwd = this.configurationResolverService.resolve(this.contextService.getWorkspace().folders[0], result.cwd); } if (result.env) { - result.env = this.configurationResolverService.resolve(this.contextService.getLegacyWorkspace().resource, result.env); + result.env = this.configurationResolverService.resolve(this.contextService.getWorkspace().folders[0], result.env); } return result; } diff --git a/src/vs/workbench/parts/tasks/node/processTaskSystem.ts b/src/vs/workbench/parts/tasks/node/processTaskSystem.ts index b15509838dc..e0006516a31 100644 --- a/src/vs/workbench/parts/tasks/node/processTaskSystem.ts +++ b/src/vs/workbench/parts/tasks/node/processTaskSystem.ts @@ -382,7 +382,7 @@ export class ProcessTaskSystem extends EventEmitter implements ITaskSystem { private resolveVariable(value: string): string { // TODO@Dirk adopt new configuration resolver service https://github.com/Microsoft/vscode/issues/31365 - return this.configurationResolverService.resolve(this.contextService.getLegacyWorkspace().resource, value); + return this.configurationResolverService.resolve(this.contextService.getWorkspace().folders[0], value); } public log(value: string): void { diff --git a/src/vs/workbench/parts/tasks/node/taskConfiguration.ts b/src/vs/workbench/parts/tasks/node/taskConfiguration.ts index 513e890116a..9bd43c3080f 100644 --- a/src/vs/workbench/parts/tasks/node/taskConfiguration.ts +++ b/src/vs/workbench/parts/tasks/node/taskConfiguration.ts @@ -561,6 +561,7 @@ function _freeze(this: void, target: T, properties: MetaData[]): Read } interface ParseContext { + workspaceFolder: Tasks.WorkspaceFolder; problemReporter: IProblemReporter; namedProblemMatchers: IStringDictionary; uuidMap: UUIDMap; @@ -1160,6 +1161,7 @@ namespace ConfiguringTask { } let taskIdentifier = TaskIdentifier.from(identifier); let configElement: Tasks.TaskSourceConfigElement = { + workspaceFolder: context.workspaceFolder, file: '.vscode\\tasks.json', index, element: external @@ -1220,7 +1222,7 @@ namespace CustomTask { let result: Tasks.CustomTask = { type: 'custom', _id: context.uuidMap.getUUID(taskName), - _source: Objects.assign({}, source, { config: { index, element: external, file: '.vscode\\tasks.json' } }), + _source: Objects.assign({}, source, { config: { index, element: external, file: '.vscode\\tasks.json', workspaceFolder: context.workspaceFolder } }), _label: taskName, name: taskName, identifier: taskName, @@ -1678,10 +1680,12 @@ class UUIDMap { class ConfigurationParser { + private workspaceFolder: Tasks.WorkspaceFolder; private problemReporter: IProblemReporter; private uuidMap: UUIDMap; - constructor(problemReporter: IProblemReporter, uuidMap: UUIDMap) { + constructor(workspaceFolder: Tasks.WorkspaceFolder, problemReporter: IProblemReporter, uuidMap: UUIDMap) { + this.workspaceFolder = workspaceFolder; this.problemReporter = problemReporter; this.uuidMap = uuidMap; } @@ -1693,6 +1697,7 @@ class ConfigurationParser { this.problemReporter.clearOutput(); } let context: ParseContext = { + workspaceFolder: this.workspaceFolder, problemReporter: this.problemReporter, uuidMap: this.uuidMap, namedProblemMatchers: undefined, @@ -1751,7 +1756,7 @@ class ConfigurationParser { let isBackground = fileConfig.isBackground ? !!fileConfig.isBackground : fileConfig.isWatching ? !!fileConfig.isWatching : undefined; let task: Tasks.CustomTask = { _id: context.uuidMap.getUUID(globals.command.name), - _source: Objects.assign({}, source, { config: { index: -1, element: fileConfig } }), + _source: Objects.assign({}, source, { config: { index: -1, element: fileConfig, workspaceFolder: context.workspaceFolder } }), _label: globals.command.name, type: 'custom', name: globals.command.name, @@ -1783,11 +1788,16 @@ class ConfigurationParser { } } -let uuidMap: UUIDMap = new UUIDMap(); -export function parse(configuration: ExternalTaskRunnerConfiguration, logger: IProblemReporter): ParseResult { +let uuidMaps: Map = new Map(); +export function parse(workspaceFolder: Tasks.WorkspaceFolder, configuration: ExternalTaskRunnerConfiguration, logger: IProblemReporter): ParseResult { + let uuidMap = uuidMaps.get(workspaceFolder.uri.toString()); + if (!uuidMap) { + uuidMap = new UUIDMap(); + uuidMaps.set(workspaceFolder.uri.toString(), uuidMap); + } try { uuidMap.start(); - return (new ConfigurationParser(logger, uuidMap)).run(configuration); + return (new ConfigurationParser(workspaceFolder, logger, uuidMap)).run(configuration); } finally { uuidMap.finish(); } @@ -1801,35 +1811,6 @@ export function getTaskIdentifier(value: TaskIdentifier): Tasks.TaskIdentifier { return TaskIdentifier.from(value); } -export function findTaskIndex(fileConfig: ExternalTaskRunnerConfiguration, task: Tasks.Task): number { - if (!fileConfig || !fileConfig.tasks) { - return undefined; - } - if (fileConfig.tasks.length === 0) { - return -1; - } - let localMap = new UUIDMap(uuidMap); - let context: ParseContext = { - problemReporter: this.problemReporter, - uuidMap: localMap, - namedProblemMatchers: undefined, - engine: ExecutionEngine.from(fileConfig), - schemaVersion: JsonSchemaVersion.from(fileConfig) - }; - try { - localMap.start(); - let tasks = TaskParser.quickParse(fileConfig.tasks, context); - for (let i = 0; i < tasks.length; i++) { - if (task._id === tasks[i]._id) { - return i; - } - } - return -1; - } finally { - localMap.finish(); - } -} - /* class VersionConverter { constructor(private problemReporter: IProblemReporter) { diff --git a/src/vs/workbench/parts/tasks/test/electron-browser/configuration.test.ts b/src/vs/workbench/parts/tasks/test/electron-browser/configuration.test.ts index c89319b81dd..c9601239fb1 100644 --- a/src/vs/workbench/parts/tasks/test/electron-browser/configuration.test.ts +++ b/src/vs/workbench/parts/tasks/test/electron-browser/configuration.test.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; +import URI from 'vs/base/common/uri'; import * as assert from 'assert'; import Severity from 'vs/base/common/severity'; import * as UUID from 'vs/base/common/uuid'; @@ -177,7 +178,7 @@ class CustomTaskBuilder { this.commandBuilder = new CommandConfigurationBuilder(this, command); this.result = { _id: name, - _source: { kind: Tasks.TaskSourceKind.Workspace, label: 'workspace', config: { element: undefined, index: -1, file: '.vscode/tasks.json' } }, + _source: { kind: Tasks.TaskSourceKind.Workspace, label: 'workspace', config: { workspaceFolder: { uri: undefined }, element: undefined, index: -1, file: '.vscode/tasks.json' } }, _label: name, type: 'custom', identifier: name, @@ -347,7 +348,7 @@ class PatternBuilder { function testDefaultProblemMatcher(external: ExternalTaskRunnerConfiguration, resolved: number) { let reporter = new ProblemReporter(); - let result = parse(external, reporter); + let result = parse({ uri: URI.file('/Workspace/folderOne') }, external, reporter); assert.ok(!reporter.receivedMessage); assert.strictEqual(result.custom.length, 1); let task = result.custom[0]; @@ -358,7 +359,7 @@ function testDefaultProblemMatcher(external: ExternalTaskRunnerConfiguration, re function testConfiguration(external: ExternalTaskRunnerConfiguration, builder: ConfiguationBuilder): void { builder.done(); let reporter = new ProblemReporter(); - let result = parse(external, reporter); + let result = parse({ uri: URI.file('/Workspace/folderOne') }, external, reporter); if (reporter.receivedMessage) { assert.ok(false, reporter.lastMessage); } diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts index d949eaec21d..90ae98f2274 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts @@ -74,7 +74,7 @@ export class TerminalConfigHelper implements ITerminalConfigHelper { fontFamily, fontSize, lineHeight, - charWidth: Math.ceil(rect.width), + charWidth: rect.width, charHeight: Math.ceil(rect.height) }; return this._lastFontMeasurement; diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 5ec52de5602..dd9d79a8c17 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -206,7 +206,16 @@ export class TerminalInstance implements ITerminalInstance { } const font = this._configHelper.getFont(); this._cols = Math.max(Math.floor(dimension.width / font.charWidth), 1); - this._rows = Math.max(Math.floor(dimension.height / Math.floor(font.charHeight * font.lineHeight)), 1); + + // xterm.js does the horizontal space calculation using values scaled + // with window.devicePixelRatio. In these calculations, ceil and floor + // are used which require us to check it on this side as well or we + // would lose that precision. + const scaledSpaceAvailable = dimension.height * window.devicePixelRatio; + const scaledCharHeight = Math.ceil(font.charHeight * window.devicePixelRatio); + const scaledLineHeight = Math.floor(scaledCharHeight * font.lineHeight); + this._rows = Math.floor(scaledSpaceAvailable / scaledLineHeight); + return dimension.width; } diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts index d16a97535cc..811bf77c390 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts @@ -41,7 +41,7 @@ const lineAndColumnClause = [ // Changing any regex may effect this value, hence changes this as well if required. const winLineAndColumnMatchIndex = 12; -const unixLineAndColumnMatchIndex = 15; +const unixLineAndColumnMatchIndex = 23; // Each line and column clause have 6 groups (ie no. of expressions in round brackets) const lineAndColumnClauseGroupCount = 6; diff --git a/src/vs/workbench/parts/terminal/electron-browser/windowsShellHelper.ts b/src/vs/workbench/parts/terminal/electron-browser/windowsShellHelper.ts index 1fbb6bb2cd8..b4a2d55c42a 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/windowsShellHelper.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/windowsShellHelper.ts @@ -18,6 +18,7 @@ export class WindowsShellHelper { private _onCheckShell: Emitter>; private _isDisposed: boolean; private _currentRequest: TPromise; + private _newLineFeed: boolean; public constructor( private _rootProcessId: number, @@ -44,7 +45,19 @@ export class WindowsShellHelper { }, 50); }); - this._xterm.on('lineFeed', () => this._onCheckShell.fire()); + // We want to fire a new check for the shell on a lineFeed, but only + // when parsing has finished which is indicated by the cursormove event. + // If this is done on every lineFeed, parsing ends up taking + // significantly longer due to resetting timers. Note that this is + // private API. + this._xterm.on('lineFeed', () => this._newLineFeed = true); + this._xterm.on('cursormove', () => { + if (this._newLineFeed) { + this._onCheckShell.fire(); + } + }); + + // Fire a new check for the shell when any key is pressed. this._xterm.on('keypress', () => this._onCheckShell.fire()); } diff --git a/src/vs/workbench/parts/terminal/test/electron-browser/terminalLinkHandler.test.ts b/src/vs/workbench/parts/terminal/test/electron-browser/terminalLinkHandler.test.ts index d351ba0dc6d..77d9fed7145 100644 --- a/src/vs/workbench/parts/terminal/test/electron-browser/terminalLinkHandler.test.ts +++ b/src/vs/workbench/parts/terminal/test/electron-browser/terminalLinkHandler.test.ts @@ -138,21 +138,21 @@ suite('Workbench - TerminalLinkHandler', () => { // { urlFormat: '{0} on line {1}, column {2}', line: '5', column: '3' }, // { urlFormat: '{0}:line {1}', line: '5' }, // { urlFormat: '{0}:line {1}, column {2}', line: '5', column: '3' }, - // { urlFormat: '{0}({1})', line: '5' }, - // { urlFormat: '{0} ({1})', line: '5' }, - // { urlFormat: '{0}({1},{2})', line: '5', column: '3' }, - // { urlFormat: '{0} ({1},{2})', line: '5', column: '3' }, - // { urlFormat: '{0}:{1}', line: '5' }, - // { urlFormat: '{0}:{1}:{2}', line: '5', column: '3' }, - // { urlFormat: '{0}[{1}]', line: '5' }, - // { urlFormat: '{0} [{1}]', line: '5' }, - // { urlFormat: '{0}[{1},{2}]', line: '5', column: '3' }, - // { urlFormat: '{0} [{1},{2}]', line: '5', column: '3' } + { urlFormat: '{0}({1})', line: '5' }, + { urlFormat: '{0} ({1})', line: '5' }, + { urlFormat: '{0}({1},{2})', line: '5', column: '3' }, + { urlFormat: '{0} ({1},{2})', line: '5', column: '3' }, + { urlFormat: '{0}:{1}', line: '5' }, + { urlFormat: '{0}:{1}:{2}', line: '5', column: '3' }, + { urlFormat: '{0}[{1}]', line: '5' }, + { urlFormat: '{0} [{1}]', line: '5' }, + { urlFormat: '{0}[{1},{2}]', line: '5', column: '3' }, + { urlFormat: '{0} [{1},{2}]', line: '5', column: '3' } ]; linkUrls.forEach(linkUrl => { supportedLinkFormats.forEach(linkFormatInfo => { - console.log('linkFormatInfo: ', linkFormatInfo); + // console.log('linkFormatInfo: ', linkFormatInfo); testLink( strings.format(linkFormatInfo.urlFormat, linkUrl, linkFormatInfo.line, linkFormatInfo.column), linkUrl, diff --git a/src/vs/workbench/parts/watermark/electron-browser/watermark.ts b/src/vs/workbench/parts/watermark/electron-browser/watermark.ts index 8d108a933d7..96585270fa8 100644 --- a/src/vs/workbench/parts/watermark/electron-browser/watermark.ts +++ b/src/vs/workbench/parts/watermark/electron-browser/watermark.ts @@ -14,7 +14,7 @@ import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -147,7 +147,7 @@ export class WatermarkContribution implements IWorkbenchContribution { .div({ 'class': 'watermark' }); const box = $(this.watermark) .div({ 'class': 'watermark-box' }); - const folder = this.contextService.hasWorkspace(); + const folder = this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY; const selected = folder ? folderEntries : noFolderEntries .filter(entry => !('mac' in entry) || entry.mac === isMacintosh); const update = () => { diff --git a/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts b/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts index 1f09a886909..7672c53a798 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts @@ -70,7 +70,6 @@ export default () => `

diff --git a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts index 452f9863d31..d98c51565d7 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts @@ -35,7 +35,6 @@ import { used } from 'vs/workbench/parts/welcome/page/electron-browser/vs_code_w import { ILifecycleService, StartupKind } from 'vs/platform/lifecycle/common/lifecycle'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { tildify } from 'vs/base/common/labels'; -import { isLinux } from 'vs/base/common/platform'; import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { registerColor, focusBorder, textLinkForeground, textLinkActiveForeground, foreground, descriptionForeground, contrastBorder, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { getExtraColor } from 'vs/workbench/parts/welcome/walkThrough/node/walkThroughUtils'; @@ -232,18 +231,8 @@ class WelcomePage { }); recentlyOpened.then(({ workspaces }) => { - const context = this.contextService.getWorkspace(); - workspaces = workspaces.filter(workspace => { - if (this.contextService.hasMultiFolderWorkspace() && typeof workspace !== 'string' && context.id === workspace.id) { - return false; // do not show current workspace - } - - if (this.contextService.hasFolderWorkspace() && isSingleFolderWorkspaceIdentifier(workspace) && this.pathEquals(context.roots[0].fsPath, workspace)) { - return false; // do not show current workspace (single folder case) - } - - return true; - }); + // Filter out the current workspace + workspaces = workspaces.filter(workspace => !this.contextService.isCurrentWorkspace(workspace)); if (!workspaces.length) { const recent = container.querySelector('.welcomePage') as HTMLElement; recent.classList.add('emptyRecent'); @@ -317,12 +306,6 @@ class WelcomePage { }; })); - if (this.experimentService.getExperiments().deployToAzureQuickLink) { - container.querySelector('.showInterfaceOverview').remove(); - } else { - container.querySelector('.deployToAzure').remove(); - } - if (product.quality !== 'stable') { container.querySelector('.stable-only').remove(); } else { @@ -366,15 +349,6 @@ class WelcomePage { } } - private pathEquals(path1: string, path2: string): boolean { - if (!isLinux) { - path1 = path1.toLowerCase(); - path2 = path2.toLowerCase(); - } - - return path1 === path2; - } - private installExtension(extensionSuggestion: ExtensionSuggestion, strings: Strings): void { this.telemetryService.publicLog(strings.installEvent, { from: telemetryFrom, diff --git a/src/vs/workbench/services/configuration/node/configuration.ts b/src/vs/workbench/services/configuration/node/configuration.ts index 0f45a64cd69..102b1824712 100644 --- a/src/vs/workbench/services/configuration/node/configuration.ts +++ b/src/vs/workbench/services/configuration/node/configuration.ts @@ -13,12 +13,12 @@ import { equals, coalesce } from 'vs/base/common/arrays'; import * as objects from 'vs/base/common/objects'; import * as errors from 'vs/base/common/errors'; import * as collections from 'vs/base/common/collections'; -import { Disposable, toDisposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { RunOnceScheduler } from 'vs/base/common/async'; import { readFile, stat } from 'vs/base/node/pfs'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import * as extfs from 'vs/base/node/extfs'; -import { IWorkspaceContextService, IWorkspace, Workspace, ILegacyWorkspace, LegacyWorkspace } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, IWorkspace, Workspace, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { FileChangeType, FileChangesEvent } from 'vs/platform/files/common/files'; import { isLinux } from 'vs/base/common/platform'; import { ConfigWatcher } from 'vs/base/node/config'; @@ -33,7 +33,9 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { ExtensionsRegistry, ExtensionMessageCollector } from 'vs/platform/extensions/common/extensionsRegistry'; import { IConfigurationNode, IConfigurationRegistry, Extensions, editorConfigurationSchemaId, IDefaultConfigurationExtension, validateProperty, ConfigurationScope, schemaId } from 'vs/platform/configuration/common/configurationRegistry'; import { createHash } from 'crypto'; -import { getWorkspaceLabel, IWorkspacesService, IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, IStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; +import { getWorkspaceLabel, IWorkspacesService, IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, IStoredWorkspaceFolder, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; interface IStat { resource: URI; @@ -55,9 +57,7 @@ type IWorkspaceFoldersConfiguration = { [rootFolder: string]: { folders: string[ const configurationRegistry = Registry.as(Extensions.Configuration); -// BEGIN VSCode extension point `configuration` -const configurationExtPoint = ExtensionsRegistry.registerExtensionPoint('configuration', [], { - description: nls.localize('vscode.extension.contributes.configuration', 'Contributes configuration settings.'), +const configurationEntrySchema: IJSONSchema = { type: 'object', defaultSnippets: [{ body: { title: '', properties: {} } }], properties: { @@ -93,20 +93,25 @@ const configurationExtPoint = ExtensionsRegistry.registerExtensionPoint('configuration', [], { + description: nls.localize('vscode.extension.contributes.configuration', 'Contributes configuration settings.'), + oneOf: [ + configurationEntrySchema, + { + type: 'array', + items: configurationEntrySchema + } + ] }); configurationExtPoint.setHandler(extensions => { const configurations: IConfigurationNode[] = []; - - for (let i = 0; i < extensions.length; i++) { - const configuration = objects.clone(extensions[i].value); - const collector = extensions[i].collector; - - if (configuration.type && configuration.type !== 'object') { - collector.warn(nls.localize('invalid.type', "if set, 'configuration.type' must be set to 'object")); - } else { - configuration.type = 'object'; - } + function handleConfiguration(node: IConfigurationNode, id: string, collector: ExtensionMessageCollector) { + let configuration = objects.clone(node); if (configuration.title && (typeof configuration.title !== 'string')) { collector.error(nls.localize('invalid.title', "'configuration.title' must be a string")); @@ -114,10 +119,20 @@ configurationExtPoint.setHandler(extensions => { validateProperties(configuration, collector); - configuration.id = extensions[i].description.id; + configuration.id = id; configurations.push(configuration); - } + }; + for (let extension of extensions) { + const collector = extension.collector; + const value = extension.value; + const id = extension.description.id; + if (!Array.isArray(value)) { + handleConfiguration(value, id, collector); + } else { + value.forEach(v => handleConfiguration(v, id, collector)); + } + } configurationRegistry.registerConfigurations(configurations, false); }); // END VSCode extension point `configuration` @@ -167,6 +182,7 @@ function validateProperties(configuration: IConfigurationNode, collector: Extens } let subNodes = configuration.allOf; if (subNodes) { + collector.error(nls.localize('invalid.allOf', "'configuration.allOf' is deprecated and should no longer be used. Instead, pass multiple configuration sections as an array to the 'configuration' contribution point.")); for (let node of subNodes) { validateProperties(node, collector); } @@ -178,14 +194,13 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat public _serviceBrand: any; protected workspace: Workspace = null; - protected legacyWorkspace: LegacyWorkspace = null; protected _configuration: Configuration; protected readonly _onDidUpdateConfiguration: Emitter = this._register(new Emitter()); public readonly onDidUpdateConfiguration: Event = this._onDidUpdateConfiguration.event; - protected readonly _onDidChangeWorkspaceRoots: Emitter = this._register(new Emitter()); - public readonly onDidChangeWorkspaceRoots: Event = this._onDidChangeWorkspaceRoots.event; + protected readonly _onDidChangeWorkspaceFolders: Emitter = this._register(new Emitter()); + public readonly onDidChangeWorkspaceFolders: Event = this._onDidChangeWorkspaceFolders.event; protected readonly _onDidChangeWorkspaceName: Emitter = this._register(new Emitter()); public readonly onDidChangeWorkspaceName: Event = this._onDidChangeWorkspaceName.event; @@ -195,40 +210,48 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat this._configuration = new Configuration(new BaseConfiguration(new ConfigurationModel(), new ConfigurationModel()), new ConfigurationModel(), new StrictResourceMap>(), this.workspace); } - public getLegacyWorkspace(): ILegacyWorkspace { - return this.legacyWorkspace; - } - public getWorkspace(): IWorkspace { return this.workspace; } - public hasWorkspace(): boolean { - return !!this.workspace; + public getWorkbenchState(): WorkbenchState { + // Workspace has configuration file + if (this.workspace.configuration) { + return WorkbenchState.WORKSPACE; + } + + // Folder has single root + if (this.workspace.folders.length === 1) { + return WorkbenchState.FOLDER; + } + + // Empty + return WorkbenchState.EMPTY; } - public hasFolderWorkspace(): boolean { - return this.workspace && !this.workspace.configuration; - } - - public hasMultiFolderWorkspace(): boolean { - return this.workspace && !!this.workspace.configuration; - } - - public getRoot(resource: URI): URI { - return this.workspace ? this.workspace.getRoot(resource) : null; - } - - private get workspaceUri(): URI { - return this.workspace ? this.workspace.roots[0] : null; + public getWorkspaceFolder(resource: URI): URI { + return this.workspace.getFolder(resource); } public isInsideWorkspace(resource: URI): boolean { - return !!this.getRoot(resource); + return !!this.getWorkspaceFolder(resource); + } + + public isCurrentWorkspace(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): boolean { + switch (this.getWorkbenchState()) { + case WorkbenchState.FOLDER: + return isSingleFolderWorkspaceIdentifier(workspaceIdentifier) && this.pathEquals(this.workspace.folders[0].fsPath, workspaceIdentifier); + case WorkbenchState.WORKSPACE: + return isWorkspaceIdentifier(workspaceIdentifier) && this.workspace.id === workspaceIdentifier.id; + } + return false; } public toResource(workspaceRelativePath: string): URI { - return this.workspace ? this.legacyWorkspace.toResource(workspaceRelativePath) : null; + if (this.workspace.folders.length) { + return URI.file(paths.join(this.workspace.folders[0].fsPath, workspaceRelativePath)); + } + return null; } public initialize(trigger: boolean = true): TPromise { @@ -289,14 +312,25 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat // implemented by sub classes return TPromise.as(false); } + + private pathEquals(path1: string, path2: string): boolean { + if (!isLinux) { + path1 = path1.toLowerCase(); + path2 = path2.toLowerCase(); + } + + return path1 === path2; + } } export class EmptyWorkspaceServiceImpl extends WorkspaceService { private baseConfigurationService: GlobalConfigurationService; - constructor(environmentService: IEnvironmentService) { + constructor(configuration: IWindowConfiguration, environmentService: IEnvironmentService) { super(); + let id = configuration.backupPath ? URI.from({ path: paths.basename(configuration.backupPath), scheme: 'empty' }).toString() : ''; + this.workspace = new Workspace(id, '', []); this.baseConfigurationService = this._register(new GlobalConfigurationService(environmentService)); this._register(this.baseConfigurationService.onDidUpdateConfiguration(e => this.onBaseConfigurationChanged(e))); this.resetCaches(); @@ -354,7 +388,7 @@ export class WorkspaceServiceImpl extends WorkspaceService { } public getUnsupportedWorkspaceKeys(): string[] { - return this.hasFolderWorkspace() ? this._configuration.getFolderConfigurationModel(this.workspace.roots[0]).workspaceSettingsConfig.unsupportedKeys : []; + return this.getWorkbenchState() === WorkbenchState.FOLDER ? this._configuration.getFolderConfigurationModel(this.workspace.folders[0]).workspaceSettingsConfig.unsupportedKeys : []; } public initialize(trigger: boolean = true): TPromise { @@ -363,7 +397,7 @@ export class WorkspaceServiceImpl extends WorkspaceService { .then(() => super.initialize(trigger)); } - if (this.hasMultiFolderWorkspace()) { + if (this.workspaceConfigPath) { return this.workspaceConfiguration.load(this.workspaceConfigPath) .then(() => super.initialize(trigger)); } @@ -385,9 +419,9 @@ export class WorkspaceServiceImpl extends WorkspaceService { } public handleWorkspaceFileEvents(event: FileChangesEvent): void { - TPromise.join(this.workspace.roots.map(folder => this.cachedFolderConfigs.get(folder).handleWorkspaceFileEvents(event))) // handle file event for each folder + TPromise.join(this.workspace.folders.map(folder => this.cachedFolderConfigs.get(folder).handleWorkspaceFileEvents(event))) // handle file event for each folder .then(folderConfigurations => - folderConfigurations.map((configuration, index) => ({ configuration, folder: this.workspace.roots[index] })) + folderConfigurations.map((configuration, index) => ({ configuration, folder: this.workspace.folders[index] })) .filter(folderConfiguration => !!folderConfiguration.configuration) // Filter folders which are not impacted by events .map(folderConfiguration => this.updateFolderConfiguration(folderConfiguration.folder, folderConfiguration.configuration, true)) // Update the configuration of impacted folders .reduce((result, value) => result || value, false)) // Check if the effective configuration of folder is changed @@ -397,7 +431,7 @@ export class WorkspaceServiceImpl extends WorkspaceService { protected resetCaches(): void { this.cachedFolderConfigs = new StrictResourceMap>(); this._configuration = new Configuration(this.baseConfigurationService.configuration(), new ConfigurationModel(), new StrictResourceMap>(), this.workspace); - this.initCachesForFolders(this.workspace.roots); + this.initCachesForFolders(this.workspace.folders); } private initializeWorkspace(): TPromise { @@ -443,7 +477,6 @@ export class WorkspaceServiceImpl extends WorkspaceService { const workspaceId = (this.workspaceIdentifier as IWorkspaceIdentifier).id; const workspaceName = getWorkspaceLabel({ id: workspaceId, configPath: this.workspaceConfigPath.fsPath }, this.environmentService); this.workspace = new Workspace(workspaceId, workspaceName, workspaceFolders, this.workspaceConfigPath); - this.legacyWorkspace = new LegacyWorkspace(this.workspace.roots[0]); this._register(this.workspaceConfiguration.onDidUpdateConfiguration(() => this.onWorkspaceConfigurationChanged())); return null; }); @@ -511,20 +544,19 @@ export class WorkspaceServiceImpl extends WorkspaceService { const ctime = isLinux ? workspaceStat.ino : workspaceStat.birthtime.getTime(); // On Linux, birthtime is ctime, so we cannot use it! We use the ino instead! const id = createHash('md5').update(this.folderPath.fsPath).update(ctime ? String(ctime) : '').digest('hex'); const folder = URI.file(this.folderPath.fsPath); - this.workspace = new Workspace(id, paths.basename(this.folderPath.fsPath), [folder], null); - this.legacyWorkspace = new LegacyWorkspace(folder, ctime); + this.workspace = new Workspace(id, paths.basename(this.folderPath.fsPath), [folder], null, ctime); return TPromise.as(null); }); } private initCachesForFolders(folders: URI[]): void { for (const folder of folders) { - this.cachedFolderConfigs.set(folder, this._register(new FolderConfiguration(folder, this.workspaceSettingsRootFolder, this.hasMultiFolderWorkspace() ? ConfigurationScope.RESOURCE : ConfigurationScope.WINDOW))); + this.cachedFolderConfigs.set(folder, this._register(new FolderConfiguration(folder, this.workspaceSettingsRootFolder, this.getWorkbenchState() === WorkbenchState.WORKSPACE ? ConfigurationScope.RESOURCE : ConfigurationScope.WINDOW))); this.updateFolderConfiguration(folder, new FolderConfigurationModel(new FolderSettingsModel(null), [], ConfigurationScope.RESOURCE), false); } } - protected updateConfiguration(folders: URI[] = this.workspace.roots): TPromise { + protected updateConfiguration(folders: URI[] = this.workspace.folders): TPromise { return TPromise.join([...folders.map(folder => this.cachedFolderConfigs.get(folder).loadConfiguration() .then(configuration => this.updateFolderConfiguration(folder, configuration, true)))]) .then(changed => changed.reduce((result, value) => result || value, false)) @@ -533,7 +565,7 @@ export class WorkspaceServiceImpl extends WorkspaceService { private onBaseConfigurationChanged({ source, sourceConfig }: IConfigurationServiceEvent): void { if (source === ConfigurationSource.Default) { - this.workspace.roots.forEach(folder => this._configuration.getFolderConfigurationModel(folder).update()); + this.workspace.folders.forEach(folder => this._configuration.getFolderConfigurationModel(folder).update()); } if (this._configuration.updateBaseConfiguration(this.baseConfigurationService.configuration())) { this._onDidUpdateConfiguration.fire({ source, sourceConfig }); @@ -542,12 +574,12 @@ export class WorkspaceServiceImpl extends WorkspaceService { private onWorkspaceConfigurationChanged(): void { let configuredFolders = this.parseWorkspaceFolders(this.workspaceConfiguration.workspaceConfigurationModel.folders); - const foldersChanged = !equals(this.workspace.roots, configuredFolders, (r1, r2) => r1.fsPath === r2.fsPath); + const foldersChanged = !equals(this.workspace.folders, configuredFolders, (r1, r2) => r1.fsPath === r2.fsPath); if (foldersChanged) { // TODO@Sandeep be smarter here about detecting changes - this.workspace.roots = configuredFolders; + this.workspace.folders = configuredFolders; this.onFoldersChanged() .then(configurationChanged => { - this._onDidChangeWorkspaceRoots.fire(); + this._onDidChangeWorkspaceFolders.fire(); if (configurationChanged) { this.triggerConfigurationChange(); } @@ -565,7 +597,7 @@ export class WorkspaceServiceImpl extends WorkspaceService { // Remove the configurations of deleted folders for (const key of this.cachedFolderConfigs.keys()) { - if (!this.workspace.roots.filter(folder => folder.toString() === key.toString())[0]) { + if (!this.workspace.folders.filter(folder => folder.toString() === key.toString())[0]) { this.cachedFolderConfigs.delete(key); if (this._configuration.deleteFolderConfiguration(key)) { configurationChangedOnRemoval = true; @@ -574,7 +606,7 @@ export class WorkspaceServiceImpl extends WorkspaceService { } // Initialize the newly added folders - const toInitialize = this.workspace.roots.filter(folder => !this.cachedFolderConfigs.has(folder)); + const toInitialize = this.workspace.folders.filter(folder => !this.cachedFolderConfigs.has(folder)); if (toInitialize.length) { this.initCachesForFolders(toInitialize); return this.updateConfiguration(toInitialize) @@ -588,7 +620,7 @@ export class WorkspaceServiceImpl extends WorkspaceService { private updateFolderConfiguration(folder: URI, folderConfiguration: FolderConfigurationModel, compare: boolean): boolean { let configurationChanged = this._configuration.updateFolderConfiguration(folder, folderConfiguration, compare); - if (this.hasFolderWorkspace()) { + if (this.getWorkbenchState() === WorkbenchState.FOLDER) { // Workspace configuration changed configurationChanged = this.updateWorkspaceConfiguration(compare) || configurationChanged; } @@ -596,12 +628,12 @@ export class WorkspaceServiceImpl extends WorkspaceService { } private updateWorkspaceConfiguration(compare: boolean): boolean { - const workspaceConfiguration = this.hasMultiFolderWorkspace() ? this.workspaceConfiguration.workspaceConfigurationModel.workspaceConfiguration : this._configuration.getFolderConfigurationModel(this.workspace.roots[0]); + const workspaceConfiguration = this.getWorkbenchState() === WorkbenchState.WORKSPACE ? this.workspaceConfiguration.workspaceConfigurationModel.workspaceConfiguration : this._configuration.getFolderConfigurationModel(this.workspace.folders[0]); return this._configuration.updateWorkspaceConfiguration(workspaceConfiguration, compare); } protected triggerConfigurationChange(): void { - this._onDidUpdateConfiguration.fire({ source: ConfigurationSource.Workspace, sourceConfig: this._configuration.getFolderConfigurationModel(this.workspace.roots[0]).contents }); + this._onDidUpdateConfiguration.fire({ source: ConfigurationSource.Workspace, sourceConfig: this._configuration.getFolderConfigurationModel(this.workspace.folders[0]).contents }); } } @@ -631,7 +663,7 @@ class WorkspaceConfiguration extends Disposable { return workspaceConfigurationModel; }, initCallback: () => c(null) }); - this._workspaceConfigurationWatcherDisposables.push(toDisposable(() => this._workspaceConfigurationWatcher.dispose())); + this._workspaceConfigurationWatcherDisposables.push(this._workspaceConfigurationWatcher); this._workspaceConfigurationWatcher.onDidUpdateConfiguration(() => this._onDidUpdateConfiguration.fire(), this, this._workspaceConfigurationWatcherDisposables); }); } @@ -877,7 +909,7 @@ export class Configuration extends BaseConfiguration { } deleteFolderConfiguration(folder: URI): boolean { - if (this._workspace && this._workspace.roots.length > 0 && this._workspace.roots[0].fsPath === folder.fsPath) { + if (this._workspace && this._workspace.folders.length > 0 && this._workspace.folders[0].fsPath === folder.fsPath) { // Do not remove workspace configuration return false; } diff --git a/src/vs/workbench/services/configuration/node/configurationEditingService.ts b/src/vs/workbench/services/configuration/node/configurationEditingService.ts index e4568ab9394..113aeeacda5 100644 --- a/src/vs/workbench/services/configuration/node/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/node/configurationEditingService.ts @@ -21,7 +21,7 @@ import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Registry } from 'vs/platform/registry/common/platform'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IConfigurationService, IConfigurationOverrides } from 'vs/platform/configuration/common/configuration'; @@ -252,7 +252,7 @@ export class ConfigurationEditingService implements IConfigurationEditingService } // Target cannot be workspace or folder if no workspace opened - if ((target === ConfigurationTarget.WORKSPACE || target === ConfigurationTarget.FOLDER) && !this.contextService.hasWorkspace()) { + if ((target === ConfigurationTarget.WORKSPACE || target === ConfigurationTarget.FOLDER) && this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { return this.wrapError(ConfigurationEditingErrorCode.ERROR_NO_WORKSPACE_OPENED, target, operation); } @@ -296,14 +296,14 @@ export class ConfigurationEditingService implements IConfigurationEditingService // Check for prefix if (config.key === key) { - const jsonPath = workspace && workspace.configuration && resource && workspace.configuration.fsPath === resource.fsPath ? [key] : []; + const jsonPath = workspace.configuration && resource && workspace.configuration.fsPath === resource.fsPath ? [key] : []; return { key: jsonPath[jsonPath.length - 1], jsonPath, value: config.value, resource, isWorkspaceStandalone: true }; } // Check for prefix. const keyPrefix = `${key}.`; if (config.key.indexOf(keyPrefix) === 0) { - const jsonPath = workspace && workspace.configuration && resource && workspace.configuration.fsPath === resource.fsPath ? [key, config.key.substr(keyPrefix.length)] : [config.key.substr(keyPrefix.length)]; + const jsonPath = workspace.configuration && resource && workspace.configuration.fsPath === resource.fsPath ? [key, config.key.substr(keyPrefix.length)] : [config.key.substr(keyPrefix.length)]; return { key: jsonPath[jsonPath.length - 1], jsonPath, value: config.value, resource, isWorkspaceStandalone: true }; } } @@ -316,7 +316,7 @@ export class ConfigurationEditingService implements IConfigurationEditingService } const resource = this.getConfigurationFileResource(target, WORKSPACE_CONFIG_DEFAULT_PATH, overrides.resource); - if (workspace && workspace.configuration && resource && workspace.configuration.fsPath === resource.fsPath) { + if (workspace.configuration && resource && workspace.configuration.fsPath === resource.fsPath) { jsonPath = ['settings', ...jsonPath]; } return { key, jsonPath, value: config.value, resource }; @@ -327,19 +327,20 @@ export class ConfigurationEditingService implements IConfigurationEditingService return URI.file(this.environmentService.appSettingsPath); } - const workspace = this.contextService.getWorkspace(); - if (workspace) { + if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY) { + + const workspace = this.contextService.getWorkspace(); if (target === ConfigurationTarget.WORKSPACE) { - return this.contextService.hasMultiFolderWorkspace() ? workspace.configuration : this.toResource(relativePath, workspace.roots[0]); + return workspace.configuration || this.toResource(relativePath, workspace.folders[0]); } - if (target === ConfigurationTarget.FOLDER && this.contextService.hasMultiFolderWorkspace()) { + if (target === ConfigurationTarget.FOLDER && this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { if (resource) { - const root = this.contextService.getRoot(resource); - if (root) { - return this.toResource(relativePath, root); + const folder = this.contextService.getWorkspaceFolder(resource); + if (folder) { + return this.toResource(relativePath, folder); } } } @@ -347,7 +348,7 @@ export class ConfigurationEditingService implements IConfigurationEditingService return null; } - private toResource(relativePath: string, root: URI): URI { - return URI.file(paths.join(root.fsPath, relativePath)); + private toResource(relativePath: string, folder: URI): URI { + return URI.file(paths.join(folder.fsPath, relativePath)); } } diff --git a/src/vs/workbench/services/configuration/test/node/configurationEditingService.test.ts b/src/vs/workbench/services/configuration/test/node/configurationEditingService.test.ts index d6bf47c961c..358c6d64603 100644 --- a/src/vs/workbench/services/configuration/test/node/configurationEditingService.test.ts +++ b/src/vs/workbench/services/configuration/test/node/configurationEditingService.test.ts @@ -45,6 +45,7 @@ import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { IChoiceService, IMessageService } from 'vs/platform/message/common/message'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; +import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; class SettingsTestEnvironmentService extends EnvironmentService { @@ -117,30 +118,32 @@ suite('ConfigurationEditingService', () => { const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, globalSettingsFile); instantiationService.stub(IEnvironmentService, environmentService); const workspacesService = instantiationService.stub(IWorkspacesService, {}); - const workspaceService = noWorkspace ? new EmptyWorkspaceServiceImpl(environmentService) : new WorkspaceServiceImpl(workspaceDir, environmentService, workspacesService); + const workspaceService = noWorkspace ? new EmptyWorkspaceServiceImpl({}, environmentService) : new WorkspaceServiceImpl(workspaceDir, environmentService, workspacesService); instantiationService.stub(IWorkspaceContextService, workspaceService); - instantiationService.stub(IConfigurationService, workspaceService); - instantiationService.stub(ILifecycleService, new TestLifecycleService()); - instantiationService.stub(IEditorGroupService, new TestEditorGroupService()); - instantiationService.stub(ITelemetryService, NullTelemetryService); - instantiationService.stub(IModeService, ModeServiceImpl); - instantiationService.stub(IModelService, instantiationService.createInstance(ModelServiceImpl)); - instantiationService.stub(IFileService, new FileService(workspaceService, new TestTextResourceConfigurationService(), new TestConfigurationService(), { disableWatcher: true })); - instantiationService.stub(IUntitledEditorService, instantiationService.createInstance(UntitledEditorService)); - instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService)); - instantiationService.stub(ITextModelService, instantiationService.createInstance(TextModelResolverService)); - instantiationService.stub(IBackupFileService, new TestBackupFileService()); - choiceService = instantiationService.stub(IChoiceService, { - choose: (severity, message, options, cancelId): TPromise => { - return TPromise.as(cancelId); - } - }); - instantiationService.stub(IMessageService, { - show: (severity, message, options, cancelId): void => { } - }); + return workspaceService.initialize().then(() => { + instantiationService.stub(IConfigurationService, workspaceService); + instantiationService.stub(ILifecycleService, new TestLifecycleService()); + instantiationService.stub(IEditorGroupService, new TestEditorGroupService()); + instantiationService.stub(ITelemetryService, NullTelemetryService); + instantiationService.stub(IModeService, ModeServiceImpl); + instantiationService.stub(IModelService, instantiationService.createInstance(ModelServiceImpl)); + instantiationService.stub(IFileService, new FileService(workspaceService, new TestTextResourceConfigurationService(), new TestConfigurationService(), { disableWatcher: true })); + instantiationService.stub(IUntitledEditorService, instantiationService.createInstance(UntitledEditorService)); + instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService)); + instantiationService.stub(ITextModelService, instantiationService.createInstance(TextModelResolverService)); + instantiationService.stub(IBackupFileService, new TestBackupFileService()); + choiceService = instantiationService.stub(IChoiceService, { + choose: (severity, message, options, cancelId): TPromise => { + return TPromise.as(cancelId); + } + }); + instantiationService.stub(IMessageService, { + show: (severity, message, options, cancelId): void => { } + }); - testObject = instantiationService.createInstance(ConfigurationEditingService); - return workspaceService.initialize(); + testObject = instantiationService.createInstance(ConfigurationEditingService); + return workspaceService.initialize(); + }); } teardown(() => { diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index 13137b48b4c..cee692ce0e6 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -16,7 +16,7 @@ import { findFreePort } from 'vs/base/node/ports'; import { IMessageService, Severity } from 'vs/platform/message/common/message'; import { ILifecycleService, ShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ChildProcess, fork } from 'child_process'; import { ipcRenderer as ipc } from 'electron'; @@ -356,7 +356,7 @@ export class ExtensionHostProcessWorker { enableProposedApiForAll: !this._environmentService.isBuilt || (!!this._environmentService.extensionDevelopmentPath && product.nameLong.indexOf('Insiders') >= 0), enableProposedApiFor: this._environmentService.args['enable-proposed-api'] || [] }, - workspace: this._contextService.getWorkspace(), + workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : this._contextService.getWorkspace(), extensions: extensionDescriptions, configuration: this._configurationService.getConfigurationData(), telemetryInfo diff --git a/src/vs/workbench/services/files/electron-browser/fileService.ts b/src/vs/workbench/services/files/electron-browser/fileService.ts index 402c628d38c..5c63068eddf 100644 --- a/src/vs/workbench/services/files/electron-browser/fileService.ts +++ b/src/vs/workbench/services/files/electron-browser/fileService.ts @@ -12,7 +12,7 @@ import encoding = require('vs/base/node/encoding'); import errors = require('vs/base/common/errors'); import uri from 'vs/base/common/uri'; import { toResource } from 'vs/workbench/common/editor'; -import { FileOperation, FileOperationEvent, IFileService, IFilesConfiguration, IResolveFileOptions, IFileStat, IResolveFileResult, IContent, IStreamContent, IImportResult, IResolveContentOptions, IUpdateContentOptions, FileChangesEvent } from 'vs/platform/files/common/files'; +import { FileOperation, FileOperationEvent, IFileService, IFilesConfiguration, IResolveFileOptions, IFileStat, IResolveFileResult, IContent, IStreamContent, IImportResult, IResolveContentOptions, IUpdateContentOptions, FileChangesEvent, ICreateFileOptions } from 'vs/platform/files/common/files'; import { FileService as NodeFileService, IFileServiceOptions, IEncodingOverride } from 'vs/workbench/services/files/node/fileService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -133,24 +133,22 @@ export class FileService implements IFileService { this.toUnbind.push(this.editorGroupService.onEditorsChanged(() => this.onEditorsChanged())); // Root changes - this.toUnbind.push(this.contextService.onDidChangeWorkspaceRoots(() => this.onDidChangeWorkspaceRoots())); + this.toUnbind.push(this.contextService.onDidChangeWorkspaceFolders(() => this.onDidChangeWorkspaceFolders())); // Lifecycle this.lifecycleService.onShutdown(this.dispose, this); } - private onDidChangeWorkspaceRoots(): void { + private onDidChangeWorkspaceFolders(): void { this.updateOptions({ encodingOverride: this.getEncodingOverrides() }); } private getEncodingOverrides(): IEncodingOverride[] { const encodingOverride: IEncodingOverride[] = []; encodingOverride.push({ resource: uri.file(this.environmentService.appSettingsHome), encoding: encoding.UTF8 }); - if (this.contextService.hasWorkspace()) { - this.contextService.getWorkspace().roots.forEach(root => { - encodingOverride.push({ resource: uri.file(paths.join(root.fsPath, '.vscode')), encoding: encoding.UTF8 }); - }); - } + this.contextService.getWorkspace().folders.forEach(folder => { + encodingOverride.push({ resource: uri.file(paths.join(folder.fsPath, '.vscode')), encoding: encoding.UTF8 }); + }); return encodingOverride; } @@ -226,8 +224,8 @@ export class FileService implements IFileService { return this.raw.copyFile(source, target, overwrite); } - public createFile(resource: uri, content?: string): TPromise { - return this.raw.createFile(resource, content); + public createFile(resource: uri, content?: string, options?: ICreateFileOptions): TPromise { + return this.raw.createFile(resource, content, options); } public createFolder(resource: uri): TPromise { diff --git a/src/vs/workbench/services/files/node/fileService.ts b/src/vs/workbench/services/files/node/fileService.ts index 942a4d1207a..5d08ba12177 100644 --- a/src/vs/workbench/services/files/node/fileService.ts +++ b/src/vs/workbench/services/files/node/fileService.ts @@ -11,7 +11,7 @@ import os = require('os'); import crypto = require('crypto'); import assert = require('assert'); -import { isParent, FileOperation, FileOperationEvent, IContent, IFileService, IResolveFileOptions, IResolveFileResult, IResolveContentOptions, IFileStat, IStreamContent, FileOperationError, FileOperationResult, IUpdateContentOptions, FileChangeType, IImportResult, MAX_FILE_SIZE, FileChangesEvent, IFilesConfiguration } from 'vs/platform/files/common/files'; +import { isParent, FileOperation, FileOperationEvent, IContent, IFileService, IResolveFileOptions, IResolveFileResult, IResolveContentOptions, IFileStat, IStreamContent, FileOperationError, FileOperationResult, IUpdateContentOptions, FileChangeType, IImportResult, MAX_FILE_SIZE, FileChangesEvent, IFilesConfiguration, ICreateFileOptions } from 'vs/platform/files/common/files'; import { isEqualOrParent } from 'vs/base/common/paths'; import { ResourceMap } from 'vs/base/common/map'; import arrays = require('vs/base/common/arrays'); @@ -90,7 +90,7 @@ export class FileService implements IFileService { private undeliveredRawFileChangesEvents: IRawFileChange[]; private activeWorkspaceChangeWatcher: IDisposable; - private currentWorkspaceRootsCount: number; + private currentWorkspaceFoldersCount: number; constructor( private contextService: IWorkspaceContextService, @@ -101,7 +101,7 @@ export class FileService implements IFileService { this.toDispose = []; this.options = options || Object.create(null); this.tmpPath = this.options.tmpDir || os.tmpdir(); - this.currentWorkspaceRootsCount = contextService.hasWorkspace() ? contextService.getWorkspace().roots.length : 0; + this.currentWorkspaceFoldersCount = contextService.getWorkspace().folders.length; this._onFileChanges = new Emitter(); this.toDispose.push(this._onFileChanges); @@ -113,7 +113,7 @@ export class FileService implements IFileService { this.options.errorLogger = console.error; } - if (this.currentWorkspaceRootsCount > 0 && !this.options.disableWatcher) { + if (this.currentWorkspaceFoldersCount > 0 && !this.options.disableWatcher) { this.setupWorkspaceWatching(); } @@ -125,16 +125,16 @@ export class FileService implements IFileService { } private registerListeners(): void { - this.toDispose.push(this.contextService.onDidChangeWorkspaceRoots(() => this.onDidChangeWorkspaceRoots())); + this.toDispose.push(this.contextService.onDidChangeWorkspaceFolders(() => this.onDidChangeWorkspaceFolders())); } - private onDidChangeWorkspaceRoots(): void { - const newRootCount = this.contextService.hasWorkspace() ? this.contextService.getWorkspace().roots.length : 0; + private onDidChangeWorkspaceFolders(): void { + const newFoldersCount = this.contextService.getWorkspace().folders.length; let restartWorkspaceWatcher = false; - if (this.currentWorkspaceRootsCount <= 1 && newRootCount > 1) { + if (this.currentWorkspaceFoldersCount <= 1 && newFoldersCount > 1) { restartWorkspaceWatcher = true; // transition: from 1 or 0 folders to 2+ - } else if (this.currentWorkspaceRootsCount > 1 && newRootCount <= 1) { + } else if (this.currentWorkspaceFoldersCount > 1 && newFoldersCount <= 1) { restartWorkspaceWatcher = true; // transition: from 2+ folders to 1 or 0 } @@ -142,7 +142,7 @@ export class FileService implements IFileService { this.setupWorkspaceWatching(); } - this.currentWorkspaceRootsCount = newRootCount; + this.currentWorkspaceFoldersCount = newFoldersCount; } public get onFileChanges(): Event { @@ -167,7 +167,7 @@ export class FileService implements IFileService { } // new watcher: use it if setting tells us so or we run in multi-root environment - if (this.options.useExperimentalFileWatcher || this.contextService.getWorkspace().roots.length > 1) { + if (this.options.useExperimentalFileWatcher || this.contextService.getWorkspace().folders.length > 1) { this.activeWorkspaceChangeWatcher = toDisposable(this.setupNsfwWorkspaceWatching().startWatching()); } @@ -220,7 +220,7 @@ export class FileService implements IFileService { // Guard early against attempts to resolve an invalid file path if (resource.scheme !== 'file' || !resource.fsPath) { return TPromise.wrapError(new FileOperationError( - nls.localize('fileInvalidPath', "Invalid file resource ({0})", resource.toString()), + nls.localize('fileInvalidPath', "Invalid file resource ({0})", resource.toString(true)), FileOperationResult.FILE_INVALID_PATH )); } @@ -292,7 +292,7 @@ export class FileService implements IFileService { // Return if file not found if (!exists) { return TPromise.wrapError(new FileOperationError( - nls.localize('fileNotFoundError', "File not found ({0})", resource.toString()), + nls.localize('fileNotFoundError', "File not found ({0})", resource.toString(true)), FileOperationResult.FILE_NOT_FOUND )); } @@ -381,15 +381,33 @@ export class FileService implements IFileService { }); } - public createFile(resource: uri, content: string = ''): TPromise { + public createFile(resource: uri, content: string = '', options: ICreateFileOptions = Object.create(null)): TPromise { + const absolutePath = this.toAbsolutePath(resource); - // Create file - return this.updateContent(resource, content).then(result => { + let checkFilePromise: TPromise; + if (options.overwrite) { + checkFilePromise = TPromise.as(false); + } else { + checkFilePromise = pfs.exists(absolutePath); + } - // Events - this._onAfterOperation.fire(new FileOperationEvent(resource, FileOperation.CREATE, result)); + // Check file exists + return checkFilePromise.then(exists => { + if (exists && !options.overwrite) { + return TPromise.wrapError(new FileOperationError( + nls.localize('fileExists', "File to create already exits ({0})", resource.toString(true)), + FileOperationResult.FILE_MODIFIED_SINCE + )); + } - return result; + // Create file + return this.updateContent(resource, content).then(result => { + + // Events + this._onAfterOperation.fire(new FileOperationEvent(resource, FileOperation.CREATE, result)); + + return result; + }); }); } diff --git a/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts b/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts index b3f8c608892..6a52fff3712 100644 --- a/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts @@ -81,30 +81,30 @@ export class FileWatcher { }); // Start watching - this.updateRoots(); - this.toDispose.push(this.contextService.onDidChangeWorkspaceRoots(() => this.updateRoots())); - this.toDispose.push(this.configurationService.onDidUpdateConfiguration(() => this.updateRoots())); + this.updateFolders(); + this.toDispose.push(this.contextService.onDidChangeWorkspaceFolders(() => this.updateFolders())); + this.toDispose.push(this.configurationService.onDidUpdateConfiguration(() => this.updateFolders())); return () => this.dispose(); } - private updateRoots() { + private updateFolders() { if (this.isDisposed) { return; } - const roots = this.contextService.getWorkspace().roots; - this.service.setRoots(roots.map(root => { + const folders = this.contextService.getWorkspace().folders; + this.service.setRoots(folders.map(folder => { // Fetch the root's watcherExclude setting and return it const configuration = this.configurationService.getConfiguration(undefined, { - resource: root + resource: folder }); let ignored: string[] = []; if (configuration.files && configuration.files.watcherExclude) { ignored = Object.keys(configuration.files.watcherExclude).filter(k => !!configuration.files.watcherExclude[k]); } return { - basePath: root.fsPath, + basePath: folder.fsPath, ignored }; })); diff --git a/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts b/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts index 0f96d2c3b9c..a330c432f4f 100644 --- a/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts @@ -52,7 +52,7 @@ export class FileWatcher { const service = new WatcherChannelClient(channel); // Start watching - const basePath: string = normalize(this.contextService.getWorkspace().roots[0].fsPath); + const basePath: string = normalize(this.contextService.getWorkspace().folders[0].fsPath); service.watch({ basePath: basePath, ignored: this.ignored, verboseLogging: this.verboseLogging }).then(null, err => { if (!this.isDisposed && !(err instanceof Error && err.name === 'Canceled' && err.message === 'Canceled')) { return TPromise.wrapError(err); // the service lib uses the promise cancel error to indicate the process died, we do not want to bubble this up diff --git a/src/vs/workbench/services/files/node/watcher/win32/watcherService.ts b/src/vs/workbench/services/files/node/watcher/win32/watcherService.ts index 54ba8d297fd..e0b3b87fbee 100644 --- a/src/vs/workbench/services/files/node/watcher/win32/watcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/win32/watcherService.ts @@ -26,7 +26,7 @@ export class FileWatcher { } public startWatching(): () => void { - let basePath: string = normalize(this.contextService.getWorkspace().roots[0].fsPath); + let basePath: string = normalize(this.contextService.getWorkspace().folders[0].fsPath); if (basePath && basePath.indexOf('\\\\') === 0 && endsWith(basePath, sep)) { // for some weird reason, node adds a trailing slash to UNC paths diff --git a/src/vs/workbench/services/files/test/node/fileService.test.ts b/src/vs/workbench/services/files/test/node/fileService.test.ts index e345b6a0d2d..c9ebdeabde9 100644 --- a/src/vs/workbench/services/files/test/node/fileService.test.ts +++ b/src/vs/workbench/services/files/test/node/fileService.test.ts @@ -71,6 +71,45 @@ suite('FileService', () => { }, error => onError(error, done)); }); + test('createFile (does not overwrite by default)', function (done: () => void) { + const contents = 'Hello World'; + const resource = uri.file(path.join(testDir, 'test.txt')); + + fs.writeFileSync(resource.fsPath, ''); // create file + + service.createFile(resource, contents).done(null, error => { + assert.ok(error); + + done(); + }); + }); + + test('createFile (allows to overwrite existing)', function (done: () => void) { + let event: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + event = e; + }); + + const contents = 'Hello World'; + const resource = uri.file(path.join(testDir, 'test.txt')); + + fs.writeFileSync(resource.fsPath, ''); // create file + + service.createFile(resource, contents, { overwrite: true }).done(s => { + assert.equal(s.name, 'test.txt'); + assert.equal(fs.existsSync(s.resource.fsPath), true); + assert.equal(fs.readFileSync(s.resource.fsPath), contents); + + assert.ok(event); + assert.equal(event.resource.fsPath, resource.fsPath); + assert.equal(event.operation, FileOperation.CREATE); + assert.equal(event.target.resource.fsPath, resource.fsPath); + toDispose.dispose(); + + done(); + }, error => onError(error, done)); + }); + test('createFolder', function (done: () => void) { let event: FileOperationEvent; const toDispose = service.onAfterOperation(e => { diff --git a/src/vs/workbench/services/history/browser/history.ts b/src/vs/workbench/services/history/browser/history.ts index 3b55591f9ff..f767cc2f5a7 100644 --- a/src/vs/workbench/services/history/browser/history.ts +++ b/src/vs/workbench/services/history/browser/history.ts @@ -15,7 +15,7 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { FileChangesEvent, IFileService, FileChangeType } from 'vs/platform/files/common/files'; import { Selection } from 'vs/editor/common/core/selection'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; @@ -174,7 +174,6 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic private static MAX_HISTORY_ITEMS = 200; private static MAX_STACK_ITEMS = 20; private static MAX_RECENTLY_CLOSED_EDITORS = 20; - private static MERGE_EVENT_CHANGES_THRESHOLD = 300; private stack: IStackEntry[]; private index: number; @@ -545,19 +544,12 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic // on the stack. // We can also be instructed to force replace the last entry. let replace = false; - if (this.stack[this.index]) { + const currentEntry = this.stack[this.index]; + if (currentEntry) { if (forceReplace) { - replace = true; - } else { - const currentEntry = this.stack[this.index]; - if (this.matches(input, currentEntry.input) && // and: entry of same input - ( - this.sameSelection(currentEntry.selection, selection) || // and: entry has same selection - (Date.now() - currentEntry.timestamp < HistoryService.MERGE_EVENT_CHANGES_THRESHOLD) // or: entry occured very fast and is likely not human - ) - ) { - replace = true; - } + replace = true; // replace if we are forced to + } else if (this.matches(input, currentEntry.input) && this.sameSelection(currentEntry.selection, selection)) { + replace = true; // replace if the input is the same as the current one and the selection as well } } @@ -769,7 +761,7 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic } public getLastActiveWorkspaceRoot(): URI { - if (!this.contextService.hasWorkspace()) { + if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { return void 0; } @@ -781,13 +773,13 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic } const resourceInput = input as IResourceInput; - const resourceWorkspace = this.contextService.getRoot(resourceInput.resource); + const resourceWorkspace = this.contextService.getWorkspaceFolder(resourceInput.resource); if (resourceWorkspace) { return resourceWorkspace; } } // fallback to first workspace - return this.contextService.getWorkspace().roots[0]; + return this.contextService.getWorkspace().folders[0]; } } diff --git a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts index e374bd0a2a3..4d7dfa3e616 100644 --- a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts @@ -8,7 +8,6 @@ import * as nls from 'vs/nls'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { ResolvedKeybinding, Keybinding } from 'vs/base/common/keyCodes'; import { OS, OperatingSystem } from 'vs/base/common/platform'; -import { toDisposable } from 'vs/base/common/lifecycle'; import { ExtensionMessageCollector, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry'; import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { AbstractKeybindingService } from 'vs/platform/keybinding/common/abstractKeybindingService'; @@ -296,7 +295,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { this._firstTimeComputingResolver = true; this.userKeybindings = new ConfigWatcher(environmentService.appKeybindingsPath, { defaultConfig: [], onError: error => onUnexpectedError(error) }); - this.toDispose.push(toDisposable(() => this.userKeybindings.dispose())); + this.toDispose.push(this.userKeybindings); keybindingsExtPoint.setHandler((extensions) => { let commandAdded = false; diff --git a/src/vs/workbench/services/search/node/fileSearch.ts b/src/vs/workbench/services/search/node/fileSearch.ts index 057c74b4dc7..c621b8b2c48 100644 --- a/src/vs/workbench/services/search/node/fileSearch.ts +++ b/src/vs/workbench/services/search/node/fileSearch.ts @@ -49,6 +49,7 @@ interface IDirectoryTree { export class FileWalker { private config: IRawSearch; + private useRipgrep: boolean; private filePattern: string; private normalizedFilePatternLowercase: string; private includePattern: glob.ParsedExpression; @@ -73,6 +74,7 @@ export class FileWalker { constructor(config: IRawSearch) { this.config = config; + this.useRipgrep = config.useRipgrep !== false; this.filePattern = config.filePattern; this.includePattern = config.includePattern && glob.parse(config.includePattern); this.maxResults = config.maxResults || null; @@ -153,7 +155,7 @@ export class FileWalker { let traverse = this.nodeJSTraversal; if (!this.maxFilesize) { - if (this.config.useRipgrep) { + if (this.useRipgrep) { this.traversal = Traversal.Ripgrep; traverse = this.cmdTraversal; } else if (platform.isMacintosh) { @@ -216,11 +218,12 @@ export class FileWalker { let first = true; const tree = this.initDirectoryTree(); - const useRipgrep = this.config.useRipgrep; + const useRipgrep = this.useRipgrep; let cmd: childProcess.ChildProcess; let noSiblingsClauses: boolean; + let filePatternSeen = false; if (useRipgrep) { - const ripgrep = spawnRipgrepCmd(folderQuery, this.config.includePattern, this.folderExcludePatterns.get(folderQuery.folder)); + const ripgrep = spawnRipgrepCmd(folderQuery, this.config.includePattern, this.folderExcludePatterns.get(folderQuery.folder).expression); cmd = ripgrep.cmd; noSiblingsClauses = !Object.keys(ripgrep.siblingClauses).length; } else { @@ -236,7 +239,7 @@ export class FileWalker { // Mac: uses NFD unicode form on disk, but we want NFC const normalized = leftover + (isMac ? strings.normalizeNFC(stdout) : stdout); const relativeFiles = normalized.split(useRipgrep ? '\n' : '\n./'); - if (first && normalized.length >= 2) { + if (!useRipgrep && first && normalized.length >= 2) { first = false; relativeFiles[0] = relativeFiles[0].trim().substr(2); } @@ -260,11 +263,28 @@ export class FileWalker { if (useRipgrep && noSiblingsClauses) { for (const relativePath of relativeFiles) { + if (relativePath === this.filePattern) { + filePatternSeen = true; + } const basename = path.basename(relativePath); this.matchFile(onResult, { base: rootFolder, relativePath, basename }); } if (last) { - done(); + if (!filePatternSeen) { + this.checkFilePatternRelativeMatch(folderQuery.folder, (match, size) => { + if (match) { + this.resultCount++; + onResult({ + base: folderQuery.folder, + relativePath: this.filePattern, + basename: path.basename(this.filePattern), + }); + } + done(); + }); + } else { + done(); + } } return; } @@ -367,7 +387,7 @@ export class FileWalker { cmd.on('close', (code: number) => { if (code !== 0) { - done(new Error(`find failed with error code ${code}: ${this.decodeData(stderr, encoding)}`)); + done(new Error(`command failed with error code ${code}: ${this.decodeData(stderr, encoding)}`)); } else { done(null, '', true); } @@ -710,8 +730,8 @@ class AbsoluteAndRelativeParsedExpression { private absoluteParsedExpr: glob.ParsedExpression; private relativeParsedExpr: glob.ParsedExpression; - constructor(expr: glob.IExpression, private root: string) { - this.init(expr); + constructor(public expression: glob.IExpression, private root: string) { + this.init(expression); } /** diff --git a/src/vs/workbench/services/search/node/rawSearchService.ts b/src/vs/workbench/services/search/node/rawSearchService.ts index ebe2799e716..19d197cc750 100644 --- a/src/vs/workbench/services/search/node/rawSearchService.ts +++ b/src/vs/workbench/services/search/node/rawSearchService.ts @@ -83,6 +83,7 @@ export class SearchService implements IRawSearchService { includePattern: config.includePattern, excludePattern: config.excludePattern, filePattern: config.filePattern, + useRipgrep: false, maxFilesize: MAX_FILE_SIZE }), this.textSearchWorkerProvider); diff --git a/src/vs/workbench/services/search/node/ripgrepFileSearch.ts b/src/vs/workbench/services/search/node/ripgrepFileSearch.ts index 325a7ac554d..3dfe7f85dac 100644 --- a/src/vs/workbench/services/search/node/ripgrepFileSearch.ts +++ b/src/vs/workbench/services/search/node/ripgrepFileSearch.ts @@ -6,7 +6,9 @@ import * as cp from 'child_process'; import { rgPath } from 'vscode-ripgrep'; +import { isMacintosh as isMac } from 'vs/base/common/platform'; import * as glob from 'vs/base/common/glob'; +import { normalizeNFD } from 'vs/base/common/strings'; import { IFolderSearch } from './search'; import { foldersToIncludeGlobs, foldersToRgExcludeGlobs } from './ripgrepTextSearch'; @@ -24,14 +26,14 @@ function getRgArgs(folderQuery: IFolderSearch, includePattern: glob.IExpression, // includePattern can't have siblingClauses foldersToIncludeGlobs([folderQuery], includePattern, false).forEach(globArg => { - args.push('-g', globArg); + args.push('-g', isMac ? normalizeNFD(globArg) : globArg); }); let siblingClauses: glob.IExpression; const rgGlobs = foldersToRgExcludeGlobs([folderQuery], excludePattern, undefined, false); rgGlobs.globArgs - .forEach(rgGlob => args.push('-g', `!${rgGlob}`)); + .forEach(rgGlob => args.push('-g', `!${isMac ? normalizeNFD(rgGlob) : rgGlob}`)); siblingClauses = rgGlobs.siblingClauses; // Don't use .gitignore or .ignore diff --git a/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts b/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts index de5000fdcfe..cdc6ada67da 100644 --- a/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts +++ b/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts @@ -37,7 +37,7 @@ const textSearchWorkerProvider = new TextSearchWorkerProvider(); function doLegacySearchTest(config: IRawSearch, expectedResultCount: number | Function): TPromise { return new TPromise((resolve, reject) => { - let engine = new TextSearchEngine(config, new FileWalker(config), textSearchWorkerProvider); + let engine = new TextSearchEngine(config, new FileWalker({ ...config, useRipgrep: false }), textSearchWorkerProvider); let c = 0; engine.search((result) => { diff --git a/src/vs/workbench/services/telemetry/common/workspaceStats.ts b/src/vs/workbench/services/telemetry/common/workspaceStats.ts index dd8965ca6bb..9975ddf8c80 100644 --- a/src/vs/workbench/services/telemetry/common/workspaceStats.ts +++ b/src/vs/workbench/services/telemetry/common/workspaceStats.ts @@ -11,7 +11,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import URI from 'vs/base/common/uri'; import { IFileService, IFileStat } from 'vs/platform/files/common/files'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; @@ -152,11 +152,12 @@ export class WorkspaceStats { tags['workbench.filesToCreate'] = filesToCreate && filesToCreate.length || undefined; tags['workbench.filesToDiff'] = filesToDiff && filesToDiff.length || undefined; + const isEmpty = this.contextService.getWorkbenchState() === WorkbenchState.EMPTY; const workspace = this.contextService.getWorkspace(); - tags['workspace.roots'] = workspace ? workspace.roots.length : 0; - tags['workspace.empty'] = !workspace; + tags['workspace.roots'] = isEmpty ? 0 : workspace.folders.length; + tags['workspace.empty'] = isEmpty; - const folders = workspace ? workspace.roots : this.environmentService.appQuality !== 'stable' && this.findFolders(configuration); + const folders = !isEmpty ? workspace.folders : this.environmentService.appQuality !== 'stable' && this.findFolders(configuration); if (folders && folders.length && this.fileService) { return this.fileService.resolveFiles(folders.map(resource => ({ resource }))).then(results => { const names = ([]).concat(...results.map(result => result.success ? (result.stat.children || []) : [])).map(c => c.name); @@ -323,8 +324,7 @@ export class WorkspaceStats { } public reportCloudStats(): void { - const workspace = this.contextService.getWorkspace(); - const uris = workspace && workspace.roots; + const uris = this.contextService.getWorkspace().folders; if (uris && uris.length && this.fileService) { this.reportRemoteDomains(uris); this.reportRemotes(uris); diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index bdfaaa0b9ba..d112fa773c7 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -745,13 +745,10 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } // Check for workspace settings file - if (this.contextService.hasWorkspace()) { - return this.contextService.getWorkspace().roots.some(root => { - return paths.isEqualOrParent(this.resource.fsPath, path.join(root.fsPath, '.vscode')); - }); - } + return this.contextService.getWorkspace().folders.some(folder => { + return paths.isEqualOrParent(this.resource.fsPath, path.join(folder.fsPath, '.vscode')); + }); - return false; } private doTouch(): TPromise { diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index 70e7a582514..33f5ee1e9d1 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -17,7 +17,7 @@ import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { IRevertOptions, IResult, ITextFileOperationResult, ITextFileService, IRawTextContent, IAutoSaveConfiguration, AutoSaveMode, SaveReason, ITextFileEditorModelManager, ITextFileEditorModel, ModelState, ISaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; import { ConfirmResult } from 'vs/workbench/common/editor'; import { ILifecycleService, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IFileService, IResolveContentOptions, IFilesConfiguration, FileOperationError, FileOperationResult, AutoSaveConfiguration, HotExitConfiguration } from 'vs/platform/files/common/files'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -180,7 +180,7 @@ export abstract class TextFileService implements ITextFileService { let doBackup: boolean; switch (reason) { case ShutdownReason.CLOSE: - if (this.contextService.hasWorkspace() && this.configuredHotExit === HotExitConfiguration.ON_EXIT_AND_WINDOW_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 @@ -198,7 +198,7 @@ export abstract class TextFileService implements ITextFileService { break; case ShutdownReason.LOAD: - if (this.contextService.hasWorkspace() && this.configuredHotExit === HotExitConfiguration.ON_EXIT_AND_WINDOW_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 { doBackup = false; // do not backup because we are switching contexts diff --git a/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts b/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts index 8233a577d9e..263f48a9b35 100644 --- a/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts +++ b/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts @@ -167,6 +167,7 @@ const schema: IJSONSchema = { }, fontColor: { type: 'string', + format: 'color', description: nls.localize('schema.fontColor', 'When using a glyph font: The color to use.') }, fontSize: { diff --git a/src/vs/workbench/services/workspace/common/workspaceEditing.ts b/src/vs/workbench/services/workspace/common/workspaceEditing.ts index f3ed40ee523..6be9313390f 100644 --- a/src/vs/workbench/services/workspace/common/workspaceEditing.ts +++ b/src/vs/workbench/services/workspace/common/workspaceEditing.ts @@ -16,14 +16,14 @@ export interface IWorkspaceEditingService { _serviceBrand: ServiceIdentifier; /** - * add roots to the existing workspace + * add folders to the existing workspace */ - addRoots(roots: URI[]): TPromise; + addFolders(folders: URI[]): TPromise; /** - * remove roots from the existing workspace + * remove folders from the existing workspace */ - removeRoots(roots: URI[]): TPromise; + removeFolders(folders: URI[]): TPromise; } export const IWorkspaceMigrationService = createDecorator('workspaceMigrationService'); diff --git a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts index 33cbf289a08..ffd1ff7c3b4 100644 --- a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts @@ -31,70 +31,69 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { ) { } - public addRoots(rootsToAdd: URI[]): TPromise { + public addFolders(foldersToAdd: URI[]): TPromise { if (!this.isSupported()) { return TPromise.as(void 0); // we need a workspace to begin with } - const roots = this.contextService.getWorkspace().roots; + const folders = this.contextService.getWorkspace().folders; - return this.doSetRoots([...roots, ...rootsToAdd]); + return this.doSetFolders([...folders, ...foldersToAdd]); } - public removeRoots(rootsToRemove: URI[]): TPromise { + public removeFolders(foldersToRemove: URI[]): TPromise { if (!this.isSupported()) { return TPromise.as(void 0); // we need a workspace to begin with } - const roots = this.contextService.getWorkspace().roots; - const rootsToRemoveRaw = rootsToRemove.map(root => root.toString()); + const folders = this.contextService.getWorkspace().folders; + const foldersToRemoveRaw = foldersToRemove.map(folder => folder.toString()); - return this.doSetRoots(roots.filter(root => rootsToRemoveRaw.indexOf(root.toString()) === -1)); + return this.doSetFolders(folders.filter(folder => foldersToRemoveRaw.indexOf(folder.toString()) === -1)); } private isSupported(): boolean { - // TODO@Ben multi root return ( this.environmentService.appQuality !== 'stable' // not yet enabled in stable - && this.contextService.hasMultiFolderWorkspace() // we need a multi folder workspace to begin with + && !!this.contextService.getWorkspace().configuration // we need a workspace configuration file to begin with ); } - private doSetRoots(newRoots: URI[]): TPromise { + private doSetFolders(newFolders: URI[]): TPromise { const workspace = this.contextService.getWorkspace(); - const currentWorkspaceRoots = this.contextService.getWorkspace().roots.map(root => root.fsPath); - const newWorkspaceRoots = this.validateRoots(newRoots); + const currentWorkspaceFolders = this.contextService.getWorkspace().folders.map(folder => folder.fsPath); + const newWorkspaceFolders = this.validateFolders(newFolders); // See if there are any changes - if (equals(currentWorkspaceRoots, newWorkspaceRoots)) { + if (equals(currentWorkspaceFolders, newWorkspaceFolders)) { return TPromise.as(void 0); } // Apply to config - if (newWorkspaceRoots.length) { + if (newWorkspaceFolders.length) { const workspaceConfigFolder = dirname(workspace.configuration.fsPath); - const value: IStoredWorkspaceFolder[] = newWorkspaceRoots.map(newWorkspaceRoot => { - if (isEqualOrParent(newWorkspaceRoot, workspaceConfigFolder, !isLinux)) { - newWorkspaceRoot = relative(workspaceConfigFolder, newWorkspaceRoot) || '.'; // absolute paths get converted to relative ones to workspace location if possible + const value: IStoredWorkspaceFolder[] = newWorkspaceFolders.map(newWorkspaceFolder => { + if (isEqualOrParent(newWorkspaceFolder, workspaceConfigFolder, !isLinux)) { + newWorkspaceFolder = relative(workspaceConfigFolder, newWorkspaceFolder) || '.'; // absolute paths get converted to relative ones to workspace location if possible } - return { path: newWorkspaceRoot }; + return { path: newWorkspaceFolder }; }); return this.jsonEditingService.write(workspace.configuration, { key: 'folders', value }, true); } else { - // TODO: Sandeep - Removing all roots? + // TODO: Sandeep - Removing all folders? } return TPromise.as(null); } - private validateRoots(roots: URI[]): string[] { - if (!roots) { + private validateFolders(folders: URI[]): string[] { + if (!folders) { return []; } // Prevent duplicates - return distinct(roots.map(root => root.fsPath), root => isLinux ? root : root.toLowerCase()); + return distinct(folders.map(folder => folder.fsPath), folder => isLinux ? folder : folder.toLowerCase()); } } diff --git a/src/vs/workbench/services/workspace/node/workspaceMigrationService.ts b/src/vs/workbench/services/workspace/node/workspaceMigrationService.ts index 218ccc286d3..5fc35fecbd8 100644 --- a/src/vs/workbench/services/workspace/node/workspaceMigrationService.ts +++ b/src/vs/workbench/services/workspace/node/workspaceMigrationService.ts @@ -9,7 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import URI from 'vs/base/common/uri'; import { once } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -62,7 +62,7 @@ export class WorkspaceMigrationService implements IWorkspaceMigrationService { } private migrateConfiguration(toWorkspaceId: IWorkspaceIdentifier): TPromise { - if (!this.contextService.hasFolderWorkspace()) { + if (this.contextService.getWorkbenchState() !== WorkbenchState.FOLDER) { return TPromise.as(void 0); // return early if not a folder workspace is opened } diff --git a/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts b/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts index 66589da8664..7a889e4f0df 100644 --- a/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts @@ -14,10 +14,10 @@ import * as Platform from 'vs/platform/registry/common/platform'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; -import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; import { workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; +import URI from 'vs/base/common/uri'; const NullThemeService = new TestThemeService(); @@ -165,10 +165,10 @@ suite('Workbench BaseEditor', () => { let inst = new TestInstantiationService(); - inst.createInstance(EditorRegistry.getEditor(inst.createInstance(MyResourceInput, 'fake', '', '', PLAINTEXT_MODE_ID, false)), 'id').then(editor => { + inst.createInstance(EditorRegistry.getEditor(inst.createInstance(MyResourceInput, 'fake', '', URI.file('/fake'))), 'id').then(editor => { assert.strictEqual(editor.getId(), 'myEditor'); - return inst.createInstance(EditorRegistry.getEditor(inst.createInstance(ResourceEditorInput, 'fake', '', '', PLAINTEXT_MODE_ID, false)), 'id').then(editor => { + return inst.createInstance(EditorRegistry.getEditor(inst.createInstance(ResourceEditorInput, 'fake', '', URI.file('/fake'))), 'id').then(editor => { assert.strictEqual(editor.getId(), 'myOtherEditor'); (EditorRegistry).setEditors(oldEditors); @@ -186,7 +186,7 @@ suite('Workbench BaseEditor', () => { let inst = new TestInstantiationService(); - inst.createInstance(EditorRegistry.getEditor(inst.createInstance(MyResourceInput, 'fake', '', '', PLAINTEXT_MODE_ID, false)), 'id').then(editor => { + inst.createInstance(EditorRegistry.getEditor(inst.createInstance(MyResourceInput, 'fake', '', URI.file('/fake'))), 'id').then(editor => { assert.strictEqual('myOtherEditor', editor.getId()); (EditorRegistry).setEditors(oldEditors); diff --git a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts index 8f7c6e4163f..d854c4ec6f1 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts @@ -131,7 +131,7 @@ suite('ExtHostConfiguration', function () { new class extends mock() { }, new ExtHostWorkspace(new TestThreadService(), { 'id': 'foo', - 'roots': [URI.file('foo')], + 'folders': [URI.file('foo')], 'name': 'foo' }), { @@ -203,7 +203,7 @@ suite('ExtHostConfiguration', function () { new class extends mock() { }, new ExtHostWorkspace(new TestThreadService(), { 'id': 'foo', - 'roots': [firstRoot, secondRoot], + 'folders': [firstRoot, secondRoot], 'name': 'foo' }), { diff --git a/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts b/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts index ee65bea102b..dcde31bf2a6 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts @@ -24,7 +24,7 @@ suite('ExtHostWorkspace', function () { test('asRelativePath', function () { - const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', roots: [URI.file('/Coding/Applications/NewsWoWBot')], name: 'Test' }); + const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [URI.file('/Coding/Applications/NewsWoWBot')], name: 'Test' }); assertAsRelativePath(ws, '/Coding/Applications/NewsWoWBot/bernd/das/brot', 'bernd/das/brot'); assertAsRelativePath(ws, '/Apps/DartPubCache/hosted/pub.dartlang.org/convert-2.0.1/lib/src/hex.dart', @@ -38,7 +38,7 @@ suite('ExtHostWorkspace', function () { test('asRelativePath, same paths, #11402', function () { const root = '/home/aeschli/workspaces/samples/docker'; const input = '/home/aeschli/workspaces/samples/docker'; - const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', roots: [URI.file(root)], name: 'Test' }); + const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [URI.file(root)], name: 'Test' }); assertAsRelativePath(ws, (input), input); @@ -53,14 +53,14 @@ suite('ExtHostWorkspace', function () { }); test('asRelativePath, multiple folders', function () { - const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', roots: [URI.file('/Coding/One'), URI.file('/Coding/Two')], name: 'Test' }); + const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [URI.file('/Coding/One'), URI.file('/Coding/Two')], name: 'Test' }); assertAsRelativePath(ws, '/Coding/One/file.txt', 'One/file.txt'); assertAsRelativePath(ws, '/Coding/Two/files/out.txt', 'Two/files/out.txt'); assertAsRelativePath(ws, '/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt'); }); test('slightly inconsistent behaviour of asRelativePath and getWorkspaceFolder, #31553', function () { - const mrws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', roots: [URI.file('/Coding/One'), URI.file('/Coding/Two')], name: 'Test' }); + const mrws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [URI.file('/Coding/One'), URI.file('/Coding/Two')], name: 'Test' }); assertAsRelativePath(mrws, '/Coding/One/file.txt', 'One/file.txt'); assertAsRelativePath(mrws, '/Coding/One/file.txt', 'One/file.txt', true); @@ -72,7 +72,7 @@ suite('ExtHostWorkspace', function () { assertAsRelativePath(mrws, '/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt', true); assertAsRelativePath(mrws, '/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt', false); - const srws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', roots: [URI.file('/Coding/One')], name: 'Test' }); + const srws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [URI.file('/Coding/One')], name: 'Test' }); assertAsRelativePath(srws, '/Coding/One/file.txt', 'file.txt'); assertAsRelativePath(srws, '/Coding/One/file.txt', 'file.txt', false); assertAsRelativePath(srws, '/Coding/One/file.txt', 'One/file.txt', true); @@ -82,7 +82,7 @@ suite('ExtHostWorkspace', function () { }); test('getPath, legacy', function () { - let ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', roots: [] }); + let ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', folders: [] }); assert.equal(ws.getPath(), undefined); ws = new ExtHostWorkspace(new TestThreadService(), null); @@ -91,15 +91,15 @@ suite('ExtHostWorkspace', function () { ws = new ExtHostWorkspace(new TestThreadService(), undefined); assert.equal(ws.getPath(), undefined); - ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', roots: [URI.file('Folder'), URI.file('Another/Folder')] }); + ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', folders: [URI.file('Folder'), URI.file('Another/Folder')] }); assert.equal(ws.getPath().replace(/\\/g, '/'), '/Folder'); - ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', roots: [URI.file('/Folder')] }); + ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', folders: [URI.file('/Folder')] }); assert.equal(ws.getPath().replace(/\\/g, '/'), '/Folder'); }); test('WorkspaceFolder has name and index', function () { - const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', roots: [URI.file('/Coding/One'), URI.file('/Coding/Two')], name: 'Test' }); + const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [URI.file('/Coding/One'), URI.file('/Coding/Two')], name: 'Test' }); const [one, two] = ws.getWorkspaceFolders(); @@ -110,7 +110,7 @@ suite('ExtHostWorkspace', function () { }); test('getContainingWorkspaceFolder', function () { - const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', roots: [URI.file('/Coding/One'), URI.file('/Coding/Two'), URI.file('/Coding/Two/Nested')] }); + const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', folders: [URI.file('/Coding/One'), URI.file('/Coding/Two'), URI.file('/Coding/Two/Nested')] }); let folder = ws.getWorkspaceFolder(URI.file('/foo/bar')); assert.equal(folder, undefined); @@ -141,13 +141,13 @@ suite('ExtHostWorkspace', function () { }); test('Multiroot change event should have a delta, #29641', function () { - let ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', roots: [] }); + let ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', folders: [] }); let sub = ws.onDidChangeWorkspace(e => { assert.deepEqual(e.added, []); assert.deepEqual(e.removed, []); }); - ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', roots: [] }); + ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [] }); sub.dispose(); sub = ws.onDidChangeWorkspace(e => { @@ -155,7 +155,7 @@ suite('ExtHostWorkspace', function () { assert.equal(e.added.length, 1); assert.equal(e.added[0].uri.toString(), 'foo:bar'); }); - ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', roots: [URI.parse('foo:bar')] }); + ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [URI.parse('foo:bar')] }); sub.dispose(); sub = ws.onDidChangeWorkspace(e => { @@ -163,7 +163,7 @@ suite('ExtHostWorkspace', function () { assert.equal(e.added.length, 1); assert.equal(e.added[0].uri.toString(), 'foo:bar2'); }); - ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', roots: [URI.parse('foo:bar'), URI.parse('foo:bar2')] }); + ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [URI.parse('foo:bar'), URI.parse('foo:bar2')] }); sub.dispose(); sub = ws.onDidChangeWorkspace(e => { @@ -174,13 +174,13 @@ suite('ExtHostWorkspace', function () { assert.equal(e.added.length, 1); assert.equal(e.added[0].uri.toString(), 'foo:bar3'); }); - ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', roots: [URI.parse('foo:bar3')] }); + ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [URI.parse('foo:bar3')] }); sub.dispose(); }); test('Multiroot change event is immutable', function () { - let ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', roots: [] }); + let ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', folders: [] }); let sub = ws.onDidChangeWorkspace(e => { assert.throws(() => { (e).added = []; @@ -189,7 +189,7 @@ suite('ExtHostWorkspace', function () { (e.added)[0] = null; }); }); - ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', roots: [] }); + ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [] }); sub.dispose(); }); }); diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadConfiguration.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadConfiguration.test.ts index effdeecbea4..425dd180872 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadConfiguration.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadConfiguration.test.ts @@ -10,7 +10,7 @@ import * as sinon from 'sinon'; import URI from 'vs/base/common/uri'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions, IConfigurationRegistry, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { MainThreadConfiguration } from 'vs/workbench/api/electron-browser/mainThreadConfiguration'; import { ConfigurationTarget, IConfigurationEditingService } from 'vs/workbench/services/configuration/common/configurationEditing'; @@ -56,7 +56,7 @@ suite('ExtHostConfiguration', function () { }); test('update resource configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () { - instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => true }); + instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.WORKSPACE }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', null); @@ -65,7 +65,7 @@ suite('ExtHostConfiguration', function () { }); test('update resource configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () { - instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => false }); + instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.FOLDER }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', URI.file('abc')); @@ -74,7 +74,7 @@ suite('ExtHostConfiguration', function () { }); test('update resource configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () { - instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => false }); + instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.FOLDER }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', null); @@ -83,7 +83,7 @@ suite('ExtHostConfiguration', function () { }); test('update window configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () { - instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => true }); + instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.WORKSPACE }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', null); @@ -92,7 +92,7 @@ suite('ExtHostConfiguration', function () { }); test('update window configuration without configuration target defaults to workspace in multi root workspace when resource is provided', function () { - instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => true }); + instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.WORKSPACE }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', URI.file('abc')); @@ -101,7 +101,7 @@ suite('ExtHostConfiguration', function () { }); test('update window configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () { - instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => false }); + instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.FOLDER }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', URI.file('abc')); @@ -110,7 +110,7 @@ suite('ExtHostConfiguration', function () { }); test('update window configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () { - instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => false }); + instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.FOLDER }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', null); @@ -119,7 +119,7 @@ suite('ExtHostConfiguration', function () { }); test('update resource configuration without configuration target defaults to folder', function () { - instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => true }); + instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.WORKSPACE }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', URI.file('abc')); @@ -128,7 +128,7 @@ suite('ExtHostConfiguration', function () { }); test('update configuration with user configuration target', function () { - instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => false }); + instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.FOLDER }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); testObject.$updateConfigurationOption(ConfigurationTarget.USER, 'extHostConfiguration.window', 'value', URI.file('abc')); @@ -137,7 +137,7 @@ suite('ExtHostConfiguration', function () { }); test('update configuration with workspace configuration target', function () { - instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => false }); + instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.FOLDER }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); testObject.$updateConfigurationOption(ConfigurationTarget.WORKSPACE, 'extHostConfiguration.window', 'value', URI.file('abc')); @@ -146,7 +146,7 @@ suite('ExtHostConfiguration', function () { }); test('update configuration with folder configuration target', function () { - instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => false }); + instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.FOLDER }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); testObject.$updateConfigurationOption(ConfigurationTarget.FOLDER, 'extHostConfiguration.window', 'value', URI.file('abc')); @@ -155,7 +155,7 @@ suite('ExtHostConfiguration', function () { }); test('remove resource configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () { - instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => true }); + instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.WORKSPACE }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', null); @@ -164,7 +164,7 @@ suite('ExtHostConfiguration', function () { }); test('remove resource configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () { - instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => false }); + instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.FOLDER }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', URI.file('abc')); @@ -173,7 +173,7 @@ suite('ExtHostConfiguration', function () { }); test('remove resource configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () { - instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => false }); + instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.FOLDER }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', null); @@ -182,7 +182,7 @@ suite('ExtHostConfiguration', function () { }); test('remove window configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () { - instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => true }); + instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.WORKSPACE }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', null); @@ -191,7 +191,7 @@ suite('ExtHostConfiguration', function () { }); test('remove window configuration without configuration target defaults to workspace in multi root workspace when resource is provided', function () { - instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => true }); + instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.WORKSPACE }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', URI.file('abc')); @@ -200,7 +200,7 @@ suite('ExtHostConfiguration', function () { }); test('remove window configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () { - instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => false }); + instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.FOLDER }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', URI.file('abc')); @@ -209,7 +209,7 @@ suite('ExtHostConfiguration', function () { }); test('remove window configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () { - instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => false }); + instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.FOLDER }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', null); @@ -218,7 +218,7 @@ suite('ExtHostConfiguration', function () { }); test('remove configuration without configuration target defaults to folder', function () { - instantiationService.stub(IWorkspaceContextService, { hasMultiFolderWorkspace: () => true }); + instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.WORKSPACE }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', URI.file('abc')); diff --git a/src/vs/workbench/test/electron-browser/quickopen.perf.integrationTest.ts b/src/vs/workbench/test/electron-browser/quickopen.perf.integrationTest.ts index d967748494d..1dbfedb35a1 100644 --- a/src/vs/workbench/test/electron-browser/quickopen.perf.integrationTest.ts +++ b/src/vs/workbench/test/electron-browser/quickopen.perf.integrationTest.ts @@ -7,7 +7,7 @@ import 'vs/workbench/parts/search/browser/search.contribution'; // load contributions import * as assert from 'assert'; -import { IWorkspaceContextService, LegacyWorkspace } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { createSyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; import { ISearchService } from 'vs/platform/search/common/search'; @@ -30,6 +30,7 @@ import { SimpleConfigurationService } from 'vs/editor/standalone/browser/simpleS import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { IModelService } from 'vs/editor/common/services/modelService'; +import { testWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; namespace Timer { export interface ITimerEvent { @@ -74,7 +75,7 @@ suite('QuickOpen performance (integration)', () => { [IExperimentService, experimentService], [IConfigurationService, configurationService], [IModelService, new ModelServiceImpl(null, configurationService)], - [IWorkspaceContextService, new TestContextService(new LegacyWorkspace(URI.file(testWorkspacePath)))], + [IWorkspaceContextService, new TestContextService(testWorkspace(URI.file(testWorkspacePath)))], [IWorkbenchEditorService, new TestEditorService()], [IEditorGroupService, new TestEditorGroupService()], [IEnvironmentService, TestEnvironmentService], @@ -188,7 +189,6 @@ class TestExperimentService implements IExperimentService { getExperiments(): IExperiments { return { ripgrepQuickSearch: true, - deployToAzureQuickLink: false }; } } diff --git a/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts b/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts index b5b53cf077e..0cc049bd232 100644 --- a/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts +++ b/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts @@ -8,7 +8,7 @@ import 'vs/workbench/parts/search/browser/search.contribution'; // load contributions import * as assert from 'assert'; import * as fs from 'fs'; -import { IWorkspaceContextService, LegacyWorkspace } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { createSyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; import { ISearchService, IQueryOptions } from 'vs/platform/search/common/search'; @@ -33,6 +33,7 @@ import { SearchModel } from 'vs/workbench/parts/search/common/searchModel'; import { QueryBuilder } from 'vs/workbench/parts/search/common/queryBuilder'; import Event, * as event from 'vs/base/common/event'; +import { testWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; declare var __dirname: string; @@ -61,7 +62,7 @@ suite('TextSearch performance (integration)', () => { [ITelemetryService, telemetryService], [IConfigurationService, configurationService], [IModelService, new ModelServiceImpl(null, configurationService)], - [IWorkspaceContextService, new TestContextService(new LegacyWorkspace(URI.file(testWorkspacePath)))], + [IWorkspaceContextService, new TestContextService(testWorkspace(URI.file(testWorkspacePath)))], [IWorkbenchEditorService, new TestEditorService()], [IEditorGroupService, new TestEditorGroupService()], [IEnvironmentService, TestEnvironmentService], diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 44617592da0..80a54b01068 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -27,14 +27,14 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { IEditorInput, IEditorOptions, Position, Direction, IEditor, IResourceInput, ITextEditorSelection } from 'vs/platform/editor/common/editor'; import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { IMessageService, IConfirmation } from 'vs/platform/message/common/message'; -import { ILegacyWorkspace, IWorkspaceContextService, IWorkspace as IWorkbenchWorkspace } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, IWorkspace as IWorkbenchWorkspace, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { ILifecycleService, ShutdownEvent, ShutdownReason, StartupKind, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { EditorStacksModel } from 'vs/workbench/common/editor/editorStacksModel'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { IEditorGroupService, GroupArrangement, GroupOrientation, IEditorTabOptions, IMoveOptions } from 'vs/workbench/services/group/common/groupService'; import { TextFileService } from 'vs/workbench/services/textfile/common/textFileService'; -import { FileOperationEvent, IFileService, IResolveContentOptions, FileOperationError, IFileStat, IResolveFileResult, IImportResult, FileChangesEvent, IResolveFileOptions, IContent, IUpdateContentOptions, IStreamContent } from 'vs/platform/files/common/files'; +import { FileOperationEvent, IFileService, IResolveContentOptions, FileOperationError, IFileStat, IResolveFileResult, IImportResult, FileChangesEvent, IResolveFileOptions, IContent, IUpdateContentOptions, IStreamContent, ICreateFileOptions } from 'vs/platform/files/common/files'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; @@ -54,7 +54,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { isLinux } from 'vs/base/common/platform'; import { generateUuid } from 'vs/base/common/uuid'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; -import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IRecentlyOpened } from 'vs/platform/history/common/history'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; import { IPosition } from 'vs/editor/common/core/position'; @@ -73,49 +73,43 @@ export class TestContextService implements IWorkspaceContextService { private options: any; private _onDidChangeWorkspaceName: Emitter; - private _onDidChangeWorkspaceRoots: Emitter; + private _onDidChangeWorkspaceFolders: Emitter; constructor(workspace: any = TestWorkspace, options: any = null) { this.workspace = workspace; this.id = generateUuid(); this.options = options || Object.create(null); - this._onDidChangeWorkspaceRoots = new Emitter(); + this._onDidChangeWorkspaceFolders = new Emitter(); } public get onDidChangeWorkspaceName(): Event { return this._onDidChangeWorkspaceName.event; } - public get onDidChangeWorkspaceRoots(): Event { - return this._onDidChangeWorkspaceRoots.event; + public get onDidChangeWorkspaceFolders(): Event { + return this._onDidChangeWorkspaceFolders.event; } public getFolders(): URI[] { - return this.workspace ? this.workspace.roots : []; + return this.workspace ? this.workspace.folders : []; } - public hasWorkspace(): boolean { - return !!this.workspace; - } - - public hasFolderWorkspace(): boolean { - return this.workspace && !this.workspace.configuration; - } - - public hasMultiFolderWorkspace(): boolean { - return this.workspace && !!this.workspace.configuration; - } - - public getLegacyWorkspace(): ILegacyWorkspace { - return this.workspace ? { resource: this.workspace.roots[0] } : void 0; + public getWorkbenchState(): WorkbenchState { + if (this.workspace) { + if (this.workspace.configuration) { + return WorkbenchState.WORKSPACE; + } + return WorkbenchState.FOLDER; + } + return WorkbenchState.EMPTY; } public getWorkspace(): IWorkbenchWorkspace { return this.workspace; } - public getRoot(resource: URI): URI { - return this.isInsideWorkspace(resource) ? this.workspace.roots[0] : null; + public getWorkspaceFolder(resource: URI): URI { + return this.isInsideWorkspace(resource) ? this.workspace.folders[0] : null; } public setWorkspace(workspace: any): void { @@ -132,7 +126,7 @@ export class TestContextService implements IWorkspaceContextService { public isInsideWorkspace(resource: URI): boolean { if (resource && this.workspace) { - return paths.isEqualOrParent(resource.fsPath, this.workspace.roots[0].fsPath, !isLinux /* ignorecase */); + return paths.isEqualOrParent(resource.fsPath, this.workspace.folders[0].fsPath, !isLinux /* ignorecase */); } return false; @@ -141,6 +135,19 @@ export class TestContextService implements IWorkspaceContextService { public toResource(workspaceRelativePath: string): URI { return URI.file(paths.join('C:\\', workspaceRelativePath)); } + + public isCurrentWorkspace(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): boolean { + return isSingleFolderWorkspaceIdentifier(workspaceIdentifier) && this.pathEquals(this.workspace.folders[0].fsPath, workspaceIdentifier); + } + + private pathEquals(path1: string, path2: string): boolean { + if (!isLinux) { + path1 = path1.toLowerCase(); + path2 = path2.toLowerCase(); + } + + return path1 === path2; + } } export class TestTextFileService extends TextFileService { @@ -742,7 +749,7 @@ export class TestFileService implements IFileService { return TPromise.as(null); } - createFile(resource: URI, content?: string): TPromise { + createFile(resource: URI, content?: string, options?: ICreateFileOptions): TPromise { return TPromise.as(null); } diff --git a/test/smoke/package.json b/test/smoke/package.json index 6237dc93093..99c529813c7 100644 --- a/test/smoke/package.json +++ b/test/smoke/package.json @@ -19,10 +19,12 @@ "htmlparser2": "^3.9.2", "mocha": "^3.2.0", "ncp": "^2.0.0", + "portastic": "^1.0.1", "rimraf": "^2.6.1", "spectron": "~3.6.4", "strip-json-comments": "^2.0.1", "tmp": "0.0.33", - "typescript": "^2.2.2" + "typescript": "^2.2.2", + "vscode-uri": "^1.0.1" } } diff --git a/test/smoke/src/areas/debug/debug.test.ts b/test/smoke/src/areas/debug/debug.test.ts index 47c46599cad..a6e35ef4ea7 100644 --- a/test/smoke/src/areas/debug/debug.test.ts +++ b/test/smoke/src/areas/debug/debug.test.ts @@ -9,10 +9,11 @@ import * as os from 'os'; import * as path from 'path'; import * as fs from 'fs'; import * as stripJsonComments from 'strip-json-comments'; -import { SpectronApplication, VSCODE_BUILD, EXTENSIONS_DIR } from '../../spectron/application'; +import { SpectronApplication, VSCODE_BUILD, EXTENSIONS_DIR, findFreePort } from '../../spectron/application'; describe('Debug', () => { let app: SpectronApplication = new SpectronApplication(); + let port: number; if (app.build === VSCODE_BUILD.DEV) { const extensionsPath = path.join(os.homedir(), '.vscode-oss-dev', 'extensions'); @@ -37,7 +38,9 @@ describe('Debug', () => { fs.symlinkSync(debug2Path, path.join(EXTENSIONS_DIR, 'vscode-node-debug2')); } - before(() => app.start('Debug')); + // We must get a different port for our smoketest express app + // otherwise concurrent test runs will clash on those ports + before(async () => await app.start('Debug', [], { PORT: String(await findFreePort()), ...process.env })); after(() => app.stop()); beforeEach(function () { app.screenCapturer.testName = this.currentTest.title; }); @@ -45,11 +48,8 @@ describe('Debug', () => { await app.workbench.debug.openDebugViewlet(); await app.workbench.openFile('app.js'); await app.workbench.debug.configure(); + await app.screenCapturer.capture('launch.json file'); const content = await app.workbench.editor.getEditorVisibleText(); - - // TODO@isidor: sometimes on the linux build agent, - // you get the contents of app.js here, so everything - // blows up const json = JSON.parse(stripJsonComments(content)); assert.equal(json.configurations[0].request, 'launch'); @@ -64,62 +64,69 @@ describe('Debug', () => { it('breakpoints', async function () { await app.workbench.openFile('index.js'); await app.workbench.debug.setBreakpointOnLine(6); + await app.screenCapturer.capture('breakpoints are set'); }); it('start debugging', async function () { - await app.workbench.debug.startDebugging(); + port = await app.workbench.debug.startDebugging(); + await app.screenCapturer.capture('debugging has started'); - await new Promise(c => { - setTimeout(() => { - http.get(`http://localhost:3000`) - .on('error', e => void 0); - c(); - }, 400); + await new Promise((c, e) => { + const request = http.get(`http://localhost:${port}`); + request.on('error', e); + app.workbench.debug.waitForStackFrame(sf => sf.name === 'index.js' && sf.lineNumber === 6).then(c, e); }); - await app.workbench.debug.waitForStackFrame(sf => sf.name === 'index.js' && sf.lineNumber === 6); + await app.screenCapturer.capture('debugging is paused'); }); it('focus stack frames and variables', async function () { - assert.equal(await app.workbench.debug.getLocalVariableCount(), 4); + await app.client.waitFor(() => app.workbench.debug.getLocalVariableCount(), c => c === 4, 'there should be 4 local variables'); + await app.workbench.debug.focusStackFrame('layer.js'); - assert.equal(await app.workbench.debug.getLocalVariableCount(), 5); + await app.client.waitFor(() => app.workbench.debug.getLocalVariableCount(), c => c === 5, 'there should be 5 local variables'); + await app.workbench.debug.focusStackFrame('route.js'); - assert.equal(await app.workbench.debug.getLocalVariableCount(), 3); + await app.client.waitFor(() => app.workbench.debug.getLocalVariableCount(), c => c === 3, 'there should be 3 local variables'); + await app.workbench.debug.focusStackFrame('index.js'); - assert.equal(await app.workbench.debug.getLocalVariableCount(), 4); + await app.client.waitFor(() => app.workbench.debug.getLocalVariableCount(), c => c === 4, 'there should be 4 local variables'); }); it('stepOver, stepIn, stepOut', async function () { await app.workbench.debug.stepIn(); + await app.screenCapturer.capture('debugging has stepped in'); + const first = await app.workbench.debug.waitForStackFrame(sf => sf.name === 'response.js'); await app.workbench.debug.stepOver(); + await app.screenCapturer.capture('debugging has stepped over'); + await app.workbench.debug.waitForStackFrame(sf => sf.name === 'response.js' && sf.lineNumber === first.lineNumber + 1); await app.workbench.debug.stepOut(); + await app.screenCapturer.capture('debugging has stepped out'); + await app.workbench.debug.waitForStackFrame(sf => sf.name === 'index.js' && sf.lineNumber === 7); }); - it('continue', async function () { await app.workbench.debug.continue(); + await app.screenCapturer.capture('debugging has continued'); - await new Promise(c => { - setTimeout(() => { - http.get(`http://localhost:3000`) - .on('error', e => void 0); - c(); - }, 400); + await new Promise((c, e) => { + const request = http.get(`http://localhost:${port}`); + request.on('error', e); + app.workbench.debug.waitForStackFrame(sf => sf.name === 'index.js' && sf.lineNumber === 6).then(c, e); }); - await app.workbench.debug.waitForStackFrame(sf => sf.name === 'index.js' && sf.lineNumber === 6); + await app.screenCapturer.capture('debugging is paused'); }); it('debug console', async function () { - const result = await app.workbench.debug.console('2 + 2 \n', 'number'); - assert.equal(result, '4'); + await app.workbench.debug.waitForReplCommand('2 + 2 \n', r => r === '4'); }); it('stop debugging', async function () { await app.workbench.debug.stopDebugging(); + await app.screenCapturer.capture('debugging has stopped'); }); }); diff --git a/test/smoke/src/areas/debug/debug.ts b/test/smoke/src/areas/debug/debug.ts index 6a515e67346..e16e3a51bc5 100644 --- a/test/smoke/src/areas/debug/debug.ts +++ b/test/smoke/src/areas/debug/debug.ts @@ -24,8 +24,7 @@ const TOOLBAR_HIDDEN = `.debug-actions-widget.builder-hidden`; const STACK_FRAME = `${VIEWLET} .monaco-tree-row .stack-frame`; const VARIABLE = `${VIEWLET} .debug-variables .monaco-tree-row .expression`; const CONSOLE_OUTPUT = `.repl .output.expression`; -const CONSOLE_INPUT_OUTPUT = `.repl .input-output-pair .output.expression`; -const SCOPE = `${VIEWLET} .debug-variables .scope`; +const CONSOLE_INPUT_OUTPUT = `.repl .input-output-pair .output.expression .value`; const REPL_FOCUSED = '.repl-input-wrapper .monaco-editor.focused'; @@ -57,10 +56,19 @@ export class Debug extends Viewlet { await this.spectron.client.waitForElement(BREAKPOINT_GLYPH); } - async startDebugging(): Promise { + async startDebugging(): Promise { await this.spectron.client.waitAndClick(START); await this.spectron.client.waitForElement(PAUSE); await this.spectron.client.waitForElement(DEBUG_STATUS_BAR); + const portPrefix = 'Port: '; + await this.spectron.client.waitFor(async () => { + const output = await this.getConsoleOutput(); + return output.join(''); + }, text => !!text && text.indexOf(portPrefix) >= 0); + const output = await this.getConsoleOutput(); + const lastOutput = output.pop(); + + return lastOutput ? parseInt(lastOutput.substr(portPrefix.length)) : 3000; } async stepOver(): Promise { @@ -100,21 +108,21 @@ export class Debug extends Viewlet { async focusStackFrame(name: string): Promise { const stackFrame = await this.waitForStackFrame(sf => sf.name === name); await this.spectron.client.spectron.client.elementIdClick(stackFrame.id); - await this.spectron.workbench.waitForOpen(name); + await this.spectron.workbench.waitForTab(name); } - async console(text: string, type: string): Promise { + async waitForReplCommand(text: string, accept: (result: string) => boolean): Promise { await this.spectron.workbench.quickopen.runCommand('Debug: Focus Debug Console'); await this.spectron.client.waitForElement(REPL_FOCUSED); await this.spectron.client.type(text); - await this.spectron.client.waitForElement(CONSOLE_INPUT_OUTPUT + ` .${type}`); - - const result = await this.getConsoleOutput(); - return result[result.length - 1] || ''; + await this.spectron.client.waitForElement(CONSOLE_INPUT_OUTPUT); + await this.spectron.client.waitFor(async () => { + const result = await this.getConsoleOutput(); + return result[result.length - 1] || ''; + }, accept); } async getLocalVariableCount(): Promise { - await this.spectron.client.waitForElement(SCOPE); return await this.spectron.webclient.selectorExecute(VARIABLE, div => (Array.isArray(div) ? div : [div]).length); } diff --git a/test/smoke/src/areas/editor/editor.test.ts b/test/smoke/src/areas/editor/editor.test.ts index bbabe64e456..caee9d9b152 100644 --- a/test/smoke/src/areas/editor/editor.test.ts +++ b/test/smoke/src/areas/editor/editor.test.ts @@ -64,7 +64,7 @@ describe('Editor', () => { await app.workbench.editor.gotoDefinition('express', 11); - await app.workbench.waitForActiveOpen('index.d.ts'); + await app.workbench.waitForActiveTab('index.d.ts'); }); it(`verifies that 'Peek Definition' works`, async function () { diff --git a/test/smoke/src/areas/editor/editor.ts b/test/smoke/src/areas/editor/editor.ts index 36b326b2f56..e37d916cedf 100644 --- a/test/smoke/src/areas/editor/editor.ts +++ b/test/smoke/src/areas/editor/editor.ts @@ -102,6 +102,13 @@ export class Editor { await this.spectron.client.waitAndClick(selector); } + public async getFocusedEditorUri(): Promise { + return this.spectron.webclient.selectorExecute(`.editor-container .monaco-editor.focused`, (elements: HTMLElement[]) => { + elements = Array.isArray(elements) ? elements : [elements]; + return elements[0].getAttribute('data-uri'); + }); + } + private async getClassSelectors(term: string, viewline: number): Promise { const result: { text: string, className: string }[] = await this.spectron.webclient.selectorExecute(`${Editor.VIEW_LINES}>:nth-child(${viewline}) span span`, elements => (Array.isArray(elements) ? elements : [elements]) diff --git a/test/smoke/src/areas/explorer/explorer.ts b/test/smoke/src/areas/explorer/explorer.ts index d59f02244d2..904afdbc170 100644 --- a/test/smoke/src/areas/explorer/explorer.ts +++ b/test/smoke/src/areas/explorer/explorer.ts @@ -25,14 +25,8 @@ export class Explorer extends Viewlet { } public async openFile(fileName: string): Promise { - let selector = `div[class="monaco-icon-label file-icon ${fileName}-name-file-icon ${this.getExtensionSelector(fileName)} explorer-item"]`; - try { - await this.spectron.client.doubleClickAndWait(selector); - await this.spectron.client.waitForElement(`.tabs-container div[aria-label="${fileName}, tab"]`); - await this.spectron.client.waitForElement(`.monaco-editor.focused`); - } catch (e) { - return Promise.reject(`Cannot fine ${fileName} in a viewlet.`); - } + await this.spectron.client.doubleClickAndWait(`div[class="monaco-icon-label file-icon ${fileName}-name-file-icon ${this.getExtensionSelector(fileName)} explorer-item"]`); + await this.spectron.workbench.waitForEditorFocus(fileName); } public getExtensionSelector(fileName: string): string { diff --git a/test/smoke/src/areas/extensions/extensions.ts b/test/smoke/src/areas/extensions/extensions.ts index 85e31128658..af4fdbb47a0 100644 --- a/test/smoke/src/areas/extensions/extensions.ts +++ b/test/smoke/src/areas/extensions/extensions.ts @@ -33,7 +33,11 @@ export class Extensions extends Viewlet { public async installExtension(name: string): Promise { await this.searchForExtension(name); - await this.spectron.client.waitAndClick(`div.extensions-viewlet[id="workbench.view.extensions"] .monaco-list-row[aria-label="${name}"] .extension li[class='action-item'] .extension-action.install`); + + // we might want to wait for a while longer since the Marketplace can be slow + // a minute should do + await this.spectron.client.waitFor(() => this.spectron.client.click(`div.extensions-viewlet[id="workbench.view.extensions"] .monaco-list-row[aria-label="${name}"] .extension li[class='action-item'] .extension-action.install`), void 0, 'waiting for install button', 600); + await this.spectron.client.waitForElement(`div.extensions-viewlet[id="workbench.view.extensions"] .monaco-list-row[aria-label="${name}"] .extension li[class='action-item'] .extension-action.reload`); return true; } diff --git a/test/smoke/src/areas/problems/problems.ts b/test/smoke/src/areas/problems/problems.ts index ec05cd4a025..43548fc490a 100644 --- a/test/smoke/src/areas/problems/problems.ts +++ b/test/smoke/src/areas/problems/problems.ts @@ -21,7 +21,7 @@ export class Problems { public async showProblemsView(): Promise { if (!await this.isVisible()) { await this.spectron.command('workbench.actions.view.problems'); - await this.spectron.client.waitForElement(Problems.PROBLEMS_VIEW_SELECTOR); + await this.waitForProblemsView(); } } @@ -37,6 +37,10 @@ export class Problems { return !!element; } + public async waitForProblemsView(): Promise { + await this.spectron.client.waitForElement(Problems.PROBLEMS_VIEW_SELECTOR); + } + public static getSelectorInProblemsView(problemType: ProblemSeverity): string { let selector = problemType === ProblemSeverity.WARNING ? 'warning' : 'error'; return `div[aria-label="Problems grouped by files"] .icon.${selector}`; diff --git a/test/smoke/src/areas/quickopen/quickopen.ts b/test/smoke/src/areas/quickopen/quickopen.ts index 23053718da6..9f91b96f1ae 100644 --- a/test/smoke/src/areas/quickopen/quickopen.ts +++ b/test/smoke/src/areas/quickopen/quickopen.ts @@ -52,8 +52,7 @@ export class QuickOpen { await this.getQuickOpenElements(); await this.spectron.client.keys(['Enter', 'NULL']); - await this.spectron.client.waitForElement(`.tabs-container div[aria-selected="true"][aria-label="${fileName}, tab"]`); - await this.spectron.client.waitForElement(`div.editor-container[aria-label="${fileName}. Text file editor., Group 1."]`); + await this.spectron.workbench.waitForActiveTab(fileName); await this.spectron.workbench.waitForEditorFocus(fileName); } @@ -70,19 +69,14 @@ export class QuickOpen { await this.spectron.client.waitAndClick(QuickOpen.QUICK_OPEN_FOCUSED_ELEMENT); } - protected waitForQuickOpenOpened(): Promise { + public waitForQuickOpenOpened(): Promise { return this.spectron.client.waitForElement(QuickOpen.QUICK_OPEN_FOCUSSED_INPUT); } - protected waitForQuickOpenClosed(): Promise { + public waitForQuickOpenClosed(): Promise { return this.spectron.client.waitForElement(QuickOpen.QUICK_OPEN_HIDDEN); } - public async isQuickOpenVisible(): Promise { - await this.waitForQuickOpenOpened(); - return true; - } - public async submit(text: string): Promise { await this.spectron.client.type(text); await this.spectron.client.keys(['Enter', 'NULL']); diff --git a/test/smoke/src/areas/statusbar/statusbar.test.ts b/test/smoke/src/areas/statusbar/statusbar.test.ts index 37221970fa1..b5f38b67194 100644 --- a/test/smoke/src/areas/statusbar/statusbar.test.ts +++ b/test/smoke/src/areas/statusbar/statusbar.test.ts @@ -33,27 +33,27 @@ describe('Statusbar', () => { it(`verifies that 'quick open' opens when clicking on status bar elements`, async function () { await app.workbench.statusbar.clickOn(StatusBarElement.BRANCH_STATUS); - assert.ok(await app.workbench.quickopen.isQuickOpenVisible(), 'Quick open is not opened for branch indicator.'); + await app.workbench.quickopen.waitForQuickOpenOpened(); await app.workbench.quickopen.closeQuickOpen(); await app.workbench.quickopen.openFile('app.js'); await app.workbench.statusbar.clickOn(StatusBarElement.INDENTATION_STATUS); - assert.ok(await app.workbench.quickopen.isQuickOpenVisible(), 'Quick open is not opened for indentation indicator.'); + await app.workbench.quickopen.waitForQuickOpenOpened(); await app.workbench.quickopen.closeQuickOpen(); await app.workbench.statusbar.clickOn(StatusBarElement.ENCODING_STATUS); - assert.ok(await app.workbench.quickopen.isQuickOpenVisible(), 'Quick open is not opened for encoding indicator.'); + await app.workbench.quickopen.waitForQuickOpenOpened(); await app.workbench.quickopen.closeQuickOpen(); await app.workbench.statusbar.clickOn(StatusBarElement.EOL_STATUS); - assert.ok(await app.workbench.quickopen.isQuickOpenVisible(), 'Quick open is not opened for EOL indicator.'); + await app.workbench.quickopen.waitForQuickOpenOpened(); await app.workbench.quickopen.closeQuickOpen(); await app.workbench.statusbar.clickOn(StatusBarElement.LANGUAGE_STATUS); - assert.ok(await app.workbench.quickopen.isQuickOpenVisible(), 'Quick open is not opened for language indicator.'); + await app.workbench.quickopen.waitForQuickOpenOpened(); await app.workbench.quickopen.closeQuickOpen(); }); it(`verifies that 'Problems View' appears when clicking on 'Problems' status element`, async function () { await app.workbench.statusbar.clickOn(StatusBarElement.PROBLEMS_STATUS); - assert.ok(await app.workbench.problems.isVisible()); + await app.workbench.problems.waitForProblemsView(); }); if (app.build !== VSCODE_BUILD.DEV) { @@ -67,7 +67,7 @@ describe('Statusbar', () => { await app.workbench.quickopen.openFile('app.js'); await app.workbench.statusbar.clickOn(StatusBarElement.SELECTION_STATUS); - assert.ok(await app.workbench.quickopen.isQuickOpenVisible(), 'Quick open is not opened line number selection.'); + await app.workbench.quickopen.waitForQuickOpenOpened(); await app.workbench.quickopen.submit('15'); await app.workbench.editor.waitForHighlightingLine(15); @@ -77,9 +77,9 @@ describe('Statusbar', () => { await app.workbench.quickopen.openFile('app.js'); await app.workbench.statusbar.clickOn(StatusBarElement.EOL_STATUS); - assert.ok(await app.workbench.quickopen.isQuickOpenVisible(), 'Quick open is not opened line number selection.'); + await app.workbench.quickopen.waitForQuickOpenOpened(); - app.workbench.quickopen.selectQuickOpenElement(1); + await app.workbench.quickopen.selectQuickOpenElement(1); await app.workbench.statusbar.waitForEOL('CRLF'); }); }); \ No newline at end of file diff --git a/test/smoke/src/areas/workbench/data-loss.test.ts b/test/smoke/src/areas/workbench/data-loss.test.ts index 01e79a8acc7..62c4fde7667 100644 --- a/test/smoke/src/areas/workbench/data-loss.test.ts +++ b/test/smoke/src/areas/workbench/data-loss.test.ts @@ -25,12 +25,12 @@ describe('Dataloss', () => { await app.reload(); await app.screenCapturer.capture('After reload'); - await app.workbench.waitForActiveOpen(fileName, true); + await app.workbench.waitForActiveTab(fileName, true); await app.screenCapturer.capture(`${fileName} after reload`); let actual = await app.workbench.editor.getEditorFirstLineText(); assert.ok(actual.startsWith(textToType), `${actual} did not start with ${textToType}`); - await app.workbench.waitForOpen(untitled, true); + await app.workbench.waitForTab(untitled, true); await app.workbench.selectTab('Untitled-1', true); await app.screenCapturer.capture('Untitled file after reload'); actual = await app.workbench.editor.getEditorFirstLineText(); diff --git a/test/smoke/src/areas/workbench/data-migration.test.ts b/test/smoke/src/areas/workbench/data-migration.test.ts index d4a785d74e6..4156ad0501a 100644 --- a/test/smoke/src/areas/workbench/data-migration.test.ts +++ b/test/smoke/src/areas/workbench/data-migration.test.ts @@ -36,7 +36,7 @@ describe('Data Migration', () => { await app.start('Data Migration'); app.screenCapturer.testName = 'Untitled is restorted'; - assert.ok(await app.workbench.waitForActiveOpen('Untitled-1', true), `Untitled-1 tab is not present after migration.`); + assert.ok(await app.workbench.waitForActiveTab('Untitled-1', true), `Untitled-1 tab is not present after migration.`); const actual = await app.workbench.editor.getEditorFirstLineText(); await app.screenCapturer.capture('Untitled file text'); assert.ok(actual.startsWith(textToType), `${actual} did not start with ${textToType}`); @@ -52,7 +52,7 @@ describe('Data Migration', () => { await app.start('Data Migration'); app.screenCapturer.testName = 'Newly created dirty file is restorted'; - await app.workbench.waitForActiveOpen(fileName); + await app.workbench.waitForActiveTab(fileName); await app.client.type(firstTextPart); await app.workbench.saveOpenedFile(); await app.client.type(secondTextPart); @@ -65,7 +65,7 @@ describe('Data Migration', () => { await app.start('Data Migration'); app.screenCapturer.testName = 'Newly created dirty file is restorted'; - assert.ok(await app.workbench.waitForActiveOpen(fileName.split('/')[1]), `Untitled-1 tab is not present after migration.`); + assert.ok(await app.workbench.waitForActiveTab(fileName.split('/')[1]), `Untitled-1 tab is not present after migration.`); const actual = await app.workbench.editor.getEditorFirstLineText(); await app.screenCapturer.capture(fileName + ' text'); assert.ok(actual.startsWith(firstTextPart.concat(secondTextPart)), `${actual} did not start with ${firstTextPart.concat(secondTextPart)}`); @@ -88,8 +88,8 @@ describe('Data Migration', () => { await app.start('Data Migration'); app.screenCapturer.testName = 'Opened tabs are restored'; - assert.ok(await app.workbench.waitForOpen(fileName1), `${fileName1} tab was not restored after migration.`); - assert.ok(await app.workbench.waitForOpen(fileName2), `${fileName2} tab was not restored after migration.`); - assert.ok(await app.workbench.waitForOpen(fileName3), `${fileName3} tab was not restored after migration.`); + assert.ok(await app.workbench.waitForTab(fileName1), `${fileName1} tab was not restored after migration.`); + assert.ok(await app.workbench.waitForTab(fileName2), `${fileName2} tab was not restored after migration.`); + assert.ok(await app.workbench.waitForTab(fileName3), `${fileName3} tab was not restored after migration.`); }); }); \ No newline at end of file diff --git a/test/smoke/src/areas/workbench/localization.test.ts b/test/smoke/src/areas/workbench/localization.test.ts index 64170e8bf11..ebed884c5ed 100644 --- a/test/smoke/src/areas/workbench/localization.test.ts +++ b/test/smoke/src/areas/workbench/localization.test.ts @@ -21,26 +21,26 @@ describe('Localization', () => { let text = await app.workbench.explorer.getOpenEditorsViewTitle(); await app.screenCapturer.capture('Open editors title'); - assert.equal(text.toLowerCase(), 'geƶffnete editoren'); + assert(/geƶffnete editoren/i.test(text)); await app.workbench.search.openSearchViewlet(); text = await app.workbench.search.getTitle(); await app.screenCapturer.capture('Search title'); - assert.equal(text.toLowerCase(), 'suchen'); + assert(/suchen/i.test(text)); await app.workbench.scm.openSCMViewlet(); text = await app.workbench.scm.getTitle(); await app.screenCapturer.capture('Scm title'); - assert.equal(text.toLowerCase(), 'quellcodeverwaltung: vscode-smoketest-express (git)'); + assert(/quellcodeverwaltung/i.test(text)); await app.workbench.debug.openDebugViewlet(); text = await app.workbench.debug.getTitle(); await app.screenCapturer.capture('Debug title'); - assert.equal(text.toLowerCase(), 'debuggen'); + assert(/debuggen/i.test(text)); await app.workbench.extensions.openExtensionsViewlet(); text = await app.workbench.extensions.getTitle(); await app.screenCapturer.capture('Extensions title'); - assert.equal(text.toLowerCase(), 'erweiterungen'); + assert(/erweiterungen/i.test(text)); }); }); \ No newline at end of file diff --git a/test/smoke/src/areas/workbench/workbench.ts b/test/smoke/src/areas/workbench/workbench.ts index 8700a83ec97..1d04da84db5 100644 --- a/test/smoke/src/areas/workbench/workbench.ts +++ b/test/smoke/src/areas/workbench/workbench.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as path from 'path'; +import URI from 'vscode-uri'; import { SpectronApplication } from '../../spectron/application'; import { Explorer } from '../explorer/explorer'; import { ActivityBar } from '../activitybar/activityBar'; @@ -63,30 +65,31 @@ export class Workbench { public async selectTab(tabName: string, untitled: boolean = false): Promise { await this.spectron.client.waitAndClick(`.tabs-container div.tab[aria-label="${tabName}, tab"]`); - await this.waitForActiveOpen(tabName); await this.waitForEditorFocus(tabName, untitled); } public async waitForEditorFocus(fileName: string, untitled: boolean = false): Promise { - await this.spectron.client.waitForElement(`.editor-container[aria-label="${fileName}. ${untitled ? 'Untitled file text editor.' : 'Text file editor.'}, Group 1."] .monaco-editor.focused`); + await this.waitForActiveTab(fileName); + await this.spectron.client.waitFor(async () => { + const uri = await this.editor.getFocusedEditorUri(); + return uri && path.basename(URI.parse(uri).path) === fileName; + }, void 0, `Wait for editor with ${fileName} is focussed`); } - public async waitForActiveOpen(fileName: string, isDirty: boolean = false): Promise { + public async waitForActiveTab(fileName: string, isDirty: boolean = false): Promise { return this.spectron.client.waitForElement(`.tabs-container div.tab.active${isDirty ? '.dirty' : ''}[aria-selected="true"][aria-label="${fileName}, tab"]`).then(() => true); } - public async waitForOpen(fileName: string, isDirty: boolean = false): Promise { + public async waitForTab(fileName: string, isDirty: boolean = false): Promise { return this.spectron.client.waitForElement(`.tabs-container div.tab${isDirty ? '.dirty' : ''}[aria-label="${fileName}, tab"]`).then(() => true); } public async newUntitledFile(): Promise { await this.spectron.command('workbench.action.files.newUntitledFile'); - await this.waitForActiveOpen('Untitled-1'); await this.waitForEditorFocus('Untitled-1', true); } async openFile(fileName: string): Promise { await this.quickopen.openFile(fileName); - await this.spectron.client.waitForElement(`.monaco-editor.focused`); } } diff --git a/test/smoke/src/helpers/screenshot.ts b/test/smoke/src/helpers/screenshot.ts index e991a4fb9fe..289e600b728 100644 --- a/test/smoke/src/helpers/screenshot.ts +++ b/test/smoke/src/helpers/screenshot.ts @@ -25,7 +25,6 @@ export class ScreenCapturer { return; } - const image = await this.application.browserWindow.capturePage(); const screenshotPath = path.join( SCREENSHOTS_DIR, sanitize(this.suiteName), @@ -33,6 +32,7 @@ export class ScreenCapturer { `${ScreenCapturer.counter++}-${sanitize(name)}.png` ); + const image = await this.application.browserWindow.capturePage(); await new Promise((c, e) => mkdirp(path.dirname(screenshotPath), err => err ? e(err) : c())); await new Promise((c, e) => fs.writeFile(screenshotPath, image, err => err ? e(err) : c())); } diff --git a/test/smoke/src/spectron/application.ts b/test/smoke/src/spectron/application.ts index 72bce7b5e2f..abdeb24ff01 100644 --- a/test/smoke/src/spectron/application.ts +++ b/test/smoke/src/spectron/application.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Application, SpectronClient as WebClient } from 'spectron'; +import { test as testPort } from 'portastic'; import { SpectronClient } from './client'; import { ScreenCapturer } from '../helpers/screenshot'; import { Workbench } from '../areas/workbench/workbench'; @@ -26,6 +27,19 @@ export enum VSCODE_BUILD { STABLE } +// Just hope random helps us here, cross your fingers! +export async function findFreePort(): Promise { + for (let i = 0; i < 10; i++) { + const port = 10000 + Math.round(Math.random() * 10000); + + if (await testPort(port)) { + return port; + } + } + + throw new Error('Could not find free port!'); +} + /** * Wraps Spectron's Application instance with its used methods. */ @@ -70,12 +84,13 @@ export class SpectronApplication { return this._workbench; } - public async start(testSuiteName: string, codeArgs: string[] = []): Promise { + public async start(testSuiteName: string, codeArgs: string[] = [], env = process.env): Promise { await this.retrieveKeybindings(); cp.execSync('git checkout .', { cwd: WORKSPACE_PATH }); - await this.startApplication(testSuiteName, codeArgs); + await this.startApplication(testSuiteName, codeArgs, env); await this.checkWindowReady(); await this.waitForWelcome(); + await this.screenCapturer.capture('Application started'); } public async reload(): Promise { @@ -87,6 +102,7 @@ export class SpectronApplication { public async stop(): Promise { if (this.spectron && this.spectron.isRunning()) { + await this.screenCapturer.capture('Stopping application'); return await this.spectron.stop(); } } @@ -95,7 +111,7 @@ export class SpectronApplication { return new Promise(resolve => setTimeout(resolve, seconds * 1000)); } - private async startApplication(testSuiteName: string, codeArgs: string[] = []): Promise { + private async startApplication(testSuiteName: string, codeArgs: string[] = [], env = process.env): Promise { let args: string[] = []; let chromeDriverArgs: string[] = []; @@ -105,8 +121,13 @@ export class SpectronApplication { } args.push(this._workspace); + // Prevent 'Getting Started' web page from opening on clean user-data-dir args.push('--skip-getting-started'); + + // Prevent Quick Open from closing when focus is stolen, this allows concurrent smoketest suite running + args.push('--sticky-quickopen'); + // Ensure that running over custom extensions directory, rather than picking up the one that was used by a tester previously args.push(`--extensions-dir=${EXTENSIONS_DIR}`); @@ -114,9 +135,17 @@ export class SpectronApplication { chromeDriverArgs.push(`--user-data-dir=${path.join(this._userDir, new Date().getTime().toString())}`); + // Spectron always uses the same port number for the chrome driver + // and it handles gracefully when two instances use the same port number + // This works, but when one of the instances quits, it takes down + // chrome driver with it, leaving the other instance in DISPAIR!!! :( + const port = await findFreePort(); + this.spectron = new Application({ path: this._electronPath, + port, args, + env, chromeDriverArgs, startTimeout: 10000, requireName: 'nodeRequire' @@ -133,7 +162,7 @@ export class SpectronApplication { // Spectron opens multiple terminals in Windows platform // Workaround to focus the right window - https://github.com/electron/spectron/issues/60 await this.client.windowByIndex(1); - await this.app.browserWindow.focus(); + // await this.app.browserWindow.focus(); await this.client.waitForHTML('[id="workbench.main.container"]'); } diff --git a/test/smoke/src/spectron/client.ts b/test/smoke/src/spectron/client.ts index ddc67fb7a1e..72697d5d903 100644 --- a/test/smoke/src/spectron/client.ts +++ b/test/smoke/src/spectron/client.ts @@ -145,29 +145,41 @@ export class SpectronClient { return this.spectron.client.getTitle(); } - public async waitFor(func: () => T | Promise, accept?: (result: T) => boolean | Promise, timeoutMessage?: string): Promise; - public async waitFor(func: () => T | Promise, accept: (result: T) => boolean | Promise = result => !!result, timeoutMessage?: string): Promise { - let trial = 1; + private running = false; + public async waitFor(func: () => T | Promise, accept?: (result: T) => boolean | Promise, timeoutMessage?: string, retryCount?: number): Promise; + public async waitFor(func: () => T | Promise, accept: (result: T) => boolean | Promise = result => !!result, timeoutMessage?: string, retryCount?: number): Promise { + if (this.running) { + throw new Error('Not allowed to run nested waitFor calls!'); + } - while (true) { - if (trial > this.retryCount) { - this.application.screenCapturer.capture('timeout'); - throw new Error(`${timeoutMessage}: Timed out after ${(this.retryCount * this.retryDuration) / 1000} seconds.`); + this.running = true; + + try { + let trial = 1; + retryCount = typeof retryCount === 'number' ? retryCount : this.retryCount; + + while (true) { + if (trial > retryCount) { + await this.application.screenCapturer.capture('timeout'); + throw new Error(`${timeoutMessage}: Timed out after ${(retryCount * this.retryDuration) / 1000} seconds.`); + } + + let result; + try { + result = await func(); + } catch (e) { + // console.log(e); + } + + if (accept(result)) { + return result; + } + + await new Promise(resolve => setTimeout(resolve, this.retryDuration)); + trial++; } - - let result; - try { - result = await func(); - } catch (e) { - // console.log(e); - } - - if (accept(result)) { - return result; - } - - await new Promise(resolve => setTimeout(resolve, this.retryDuration)); - trial++; + } finally { + this.running = false; } } diff --git a/test/smoke/test/mocha.opts b/test/smoke/test/mocha.opts index f7385dc1642..102d5b65ade 100644 --- a/test/smoke/test/mocha.opts +++ b/test/smoke/test/mocha.opts @@ -1,3 +1,3 @@ --timeout 60000 ---slow 10000 +--slow 20000 out/main.js \ No newline at end of file