| undefined {
const completionResult = this.provideCompletionItemsInternal(document, position, context);
if (!completionResult) {
@@ -76,20 +79,55 @@ export class DefaultCompletionItemProvider implements vscode.CompletionItemProvi
}
if (validateLocation) {
- rootNode = parseDocument(document, false);
- currentNode = getNode(rootNode, position, true);
- if (isStyleAttribute(currentNode, position)) {
+ const lsDoc = TextDocument.create(document.uri.toString(), 'html', 0, document.getText());
+ const parsedLsDoc = this.htmlLS.parseHTMLDocument(lsDoc);
+ const positionOffset = document.offsetAt(position);
+ const node = parsedLsDoc.findNodeAt(positionOffset);
+
+ if (node.tag === 'script') {
+ if (node.attributes && 'type' in node.attributes) {
+ const rawTypeAttrValue = node.attributes['type'];
+ if (rawTypeAttrValue) {
+ const typeAttrValue = trimQuotes(rawTypeAttrValue);
+ if (typeAttrValue === 'application/javascript' || typeAttrValue === 'text/javascript') {
+ if (!getSyntaxFromArgs({ language: 'javascript' })) {
+ return;
+ } else {
+ validateLocation = false;
+ }
+ }
+
+ else if (allowedMimeTypesInScriptTag.indexOf(trimQuotes(rawTypeAttrValue)) > -1) {
+ validateLocation = false;
+ }
+ }
+ } else {
+ return;
+ }
+ }
+ else if (node.tag === 'style') {
syntax = 'css';
validateLocation = false;
} else {
- const embeddedCssNode = getEmbeddedCssNodeIfAny(document, currentNode, position);
- if (embeddedCssNode) {
- currentNode = getNode(embeddedCssNode, position, true);
- syntax = 'css';
+ if (node.attributes && node.attributes['style']) {
+ const scanner = this.htmlLS.createScanner(document.getText(), node.start);
+ let tokenType = scanner.scan();
+ let prevAttr = undefined;
+ while (tokenType !== TokenType.EOS && (scanner.getTokenEnd() <= positionOffset)) {
+ tokenType = scanner.scan();
+ if (tokenType === TokenType.AttributeName) {
+ prevAttr = scanner.getTokenText();
+ }
+ }
+ if (prevAttr === 'style') {
+ syntax = 'css';
+ validateLocation = false;
+ }
}
}
}
+
}
const extractAbbreviationResults = helper.extractAbbreviation(document, position, !isStyleSheet(syntax));
@@ -158,4 +196,4 @@ export class DefaultCompletionItemProvider implements vscode.CompletionItemProvi
return new vscode.CompletionList(newItems, true);
});
}
-}
\ No newline at end of file
+}
diff --git a/extensions/emmet/src/test/abbreviationAction.test.ts b/extensions/emmet/src/test/abbreviationAction.test.ts
index 0a99bae18..31f468919 100644
--- a/extensions/emmet/src/test/abbreviationAction.test.ts
+++ b/extensions/emmet/src/test/abbreviationAction.test.ts
@@ -264,16 +264,6 @@ suite('Tests for Expand Abbreviations (HTML)', () => {
});
});
- test('No expanding text in completion list inside style tag if position is not for property name (HTML)', () => {
- return withRandomFileEditor(htmlContents, 'html', (editor, _doc) => {
- editor.selection = new Selection(13, 14, 13, 14);
- const cancelSrc = new CancellationTokenSource();
- const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke });
- assert.equal(!completionPromise, true, `Got unexpected comapletion promise instead of undefined`);
- return Promise.resolve();
- });
- });
-
test('Expand css when inside style attribute (HTML)', () => {
const styleAttributeContent = '
';
return withRandomFileEditor(styleAttributeContent, 'html', async (editor, _doc) => {
diff --git a/extensions/emmet/src/util.ts b/extensions/emmet/src/util.ts
index c90ff7f5c..b62812728 100644
--- a/extensions/emmet/src/util.ts
+++ b/extensions/emmet/src/util.ts
@@ -95,7 +95,7 @@ export function getMappingForIncludedLanguages(): any {
/**
* Get the corresponding emmet mode for given vscode language mode
-* Eg: jsx for typescriptreact/javascriptreact or pug for jade
+* E.g.: jsx for typescriptreact/javascriptreact or pug for jade
* If the language is not supported by emmet or has been excluded via `excludeLanguages` setting,
* then nothing is returned
*
@@ -608,3 +608,18 @@ export function isStyleAttribute(currentNode: Node | null, position: vscode.Posi
}
+export function trimQuotes(s: string) {
+ if (s.length <= 1) {
+ return s.replace(/['"]/, '');
+ }
+
+ if (s[0] === `'` || s[0] === `"`) {
+ s = s.slice(1);
+ }
+
+ if (s[s.length - 1] === `'` || s[s.length - 1] === `"`) {
+ s = s.slice(0, -1);
+ }
+
+ return s;
+}
\ No newline at end of file
diff --git a/extensions/emmet/yarn.lock b/extensions/emmet/yarn.lock
index 1fb4a9aff..7b22c5334 100644
--- a/extensions/emmet/yarn.lock
+++ b/extensions/emmet/yarn.lock
@@ -40,10 +40,10 @@
resolved "https://registry.yarnpkg.com/@emmetio/stream-reader/-/stream-reader-2.2.0.tgz#46cffea119a0a003312a21c2d9b5628cb5fcd442"
integrity sha1-Rs/+oRmgoAMxKiHC2bVijLX81EI=
-"@types/node@8.0.33":
- version "8.0.33"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.33.tgz#1126e94374014e54478092830704f6ea89df04cd"
- integrity sha512-vmCdO8Bm1ExT+FWfC9sd9r4jwqM7o97gGy2WBshkkXbf/2nLAJQUrZfIhw27yVOtLUev6kSZc4cav/46KbDd8A==
+"@types/node@^10.14.8":
+ version "10.14.8"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9"
+ integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw==
ajv@^5.1.0:
version "5.3.0"
@@ -2478,11 +2478,35 @@ vscode-emmet-helper@^1.2.15:
jsonc-parser "^1.0.0"
vscode-languageserver-types "^3.6.0-next.1"
+vscode-html-languageservice@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.0.3.tgz#0aeae18a59000e317447ea34965f72680a2140ef"
+ integrity sha512-U+upM3gHp3HaF3wXAnUduA6IDKcz6frWS/dTAju3cZVIyZwOLBBFElQVlLH0ycHyMzqUFrjvdv+kEyPAEWfQ/g==
+ dependencies:
+ vscode-languageserver-types "^3.15.0-next.2"
+ vscode-nls "^4.1.1"
+ vscode-uri "^2.0.3"
+
+vscode-languageserver-types@^3.15.0-next.2:
+ version "3.15.0-next.2"
+ resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.2.tgz#a0601332cdaafac21931f497bb080cfb8d73f254"
+ integrity sha512-2JkrMWWUi2rlVLSo9OFR2PIGUzdiowEM8NgNYiwLKnXTjpwpjjIrJbNNxDik7Rv4oo9KtikcFQZKXbrKilL/MQ==
+
vscode-languageserver-types@^3.6.0-next.1:
version "3.6.0-next.1"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.6.0-next.1.tgz#98e488d3f87b666b4ee1a3d89f0023e246d358f3"
integrity sha512-n4G+hCgZwAhtcJSCkwJP153TLdcEBWwrIrb3Su/SpOkhmU7KjDgxaQBLA45hf+QbhB8uKQb+TVStPvbuYFHSMA==
+vscode-nls@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c"
+ integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A==
+
+vscode-uri@^2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.0.3.tgz#25e5f37f552fbee3cec7e5f80cef8469cefc6543"
+ integrity sha512-4D3DI3F4uRy09WNtDGD93H9q034OHImxiIcSq664Hq1Y1AScehlP3qqZyTkX/RWxeu0MRMHGkrxYqm2qlDF/aw==
+
vscode@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/vscode/-/vscode-1.0.1.tgz#3d161200615fe2af1d92ddc650751159411a513b"
diff --git a/extensions/extension-editing/package.json b/extensions/extension-editing/package.json
index 52159b965..9c07a2568 100644
--- a/extensions/extension-editing/package.json
+++ b/extensions/extension-editing/package.json
@@ -4,6 +4,7 @@
"description": "%description%",
"version": "1.0.0",
"publisher": "vscode",
+ "license": "MIT",
"engines": {
"vscode": "^1.4.0"
},
@@ -53,6 +54,6 @@
},
"devDependencies": {
"@types/markdown-it": "0.0.2",
- "@types/node": "^10.12.21"
+ "@types/node": "^10.14.8"
}
}
diff --git a/extensions/extension-editing/yarn.lock b/extensions/extension-editing/yarn.lock
index 720c84f06..971bf580b 100644
--- a/extensions/extension-editing/yarn.lock
+++ b/extensions/extension-editing/yarn.lock
@@ -7,10 +7,10 @@
resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-0.0.2.tgz#5d9ad19e6e6508cdd2f2596df86fd0aade598660"
integrity sha1-XZrRnm5lCM3S8llt+G/Qqt5ZhmA=
-"@types/node@^10.12.21":
- version "10.12.21"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e"
- integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ==
+"@types/node@^10.14.8":
+ version "10.14.8"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9"
+ integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw==
"@types/node@^6.0.46":
version "6.0.78"
diff --git a/extensions/fsharp/cgmanifest.json b/extensions/fsharp/cgmanifest.json
index 851e97ca6..b093d9085 100644
--- a/extensions/fsharp/cgmanifest.json
+++ b/extensions/fsharp/cgmanifest.json
@@ -6,7 +6,7 @@
"git": {
"name": "ionide/ionide-fsgrammar",
"repositoryUrl": "https://github.com/ionide/ionide-fsgrammar",
- "commitHash": "be0bdfd1e272b6633f5edf1429052fe9fa4df7c1"
+ "commitHash": "b57388e5a0971412c081cf0cea8b50b9c24ea4e8"
}
},
"license": "MIT",
diff --git a/extensions/fsharp/package.json b/extensions/fsharp/package.json
index 8aee9182f..b7050e22b 100644
--- a/extensions/fsharp/package.json
+++ b/extensions/fsharp/package.json
@@ -4,6 +4,7 @@
"description": "%description%",
"version": "1.0.0",
"publisher": "vscode",
+ "license": "MIT",
"engines": { "vscode": "*" },
"scripts": {
"update-grammar": "node ../../build/npm/update-grammar.js ionide/ionide-fsgrammar grammar/fsharp.json ./syntaxes/fsharp.tmLanguage.json"
diff --git a/extensions/fsharp/syntaxes/fsharp.tmLanguage.json b/extensions/fsharp/syntaxes/fsharp.tmLanguage.json
index 9a9b1b1f2..82bd02d54 100644
--- a/extensions/fsharp/syntaxes/fsharp.tmLanguage.json
+++ b/extensions/fsharp/syntaxes/fsharp.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/ionide/ionide-fsgrammar/commit/be0bdfd1e272b6633f5edf1429052fe9fa4df7c1",
+ "version": "https://github.com/ionide/ionide-fsgrammar/commit/b57388e5a0971412c081cf0cea8b50b9c24ea4e8",
"name": "fsharp",
"scopeName": "source.fsharp",
"patterns": [
@@ -285,6 +285,33 @@
}
]
},
+ {
+ "begin": "(\\()",
+ "end": "(\\))",
+ "beginCaptures": {
+ "1": {
+ "name": "keyword.symbol.fsharp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name": "keyword.symbol.fsharp"
+ }
+ },
+ "patterns": [
+ {
+ "match": "(([?[:alpha:]0-9'`^._ ]+))+",
+ "captures": {
+ "1": {
+ "name": "entity.name.type.fsharp"
+ }
+ }
+ },
+ {
+ "include": "#tuple_signature"
+ }
+ ]
+ },
{
"match": "(?!when|and|or\\b)\\b([\\w0-9'`^._]+)",
"comments": "Here we need the \\w modifier in order to check that the words isn't blacklisted",
@@ -308,6 +335,44 @@
}
]
},
+ "anonymous_record_declaration": {
+ "begin": "(\\{\\|)",
+ "end": "(\\|\\})",
+ "beginCaptures": {
+ "1": {
+ "name": "keyword.symbol.fsharp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name": "keyword.symbol.fsharp"
+ }
+ },
+ "patterns": [
+ {
+ "match": "[[:alpha:]0-9'`^_ ]+(:)",
+ "captures": {
+ "1": {
+ "name": "keyword.symbol.fsharp"
+ }
+ }
+ },
+ {
+ "match": "([[:alpha:]0-9'`^_ ]+)",
+ "captures": {
+ "1": {
+ "name": "entity.name.type.fsharp"
+ }
+ }
+ },
+ {
+ "include": "#anonymous_record_declaration"
+ },
+ {
+ "include": "#keywords"
+ }
+ ]
+ },
"record_signature": {
"patterns": [
{
@@ -571,7 +636,7 @@
"include": "#common_declaration"
},
{
- "match": "(\\?{0,1})([[:alpha:]0-9'`^._ ]+)\\s*(:)(\\s*([[:alpha:]0-9'`^._ ]+)){0,1}",
+ "match": "(\\?{0,1})([[:alpha:]0-9'`^._ ]+)\\s*(:)((?!with\\b)\\b([\\w0-9'`^._ ]+)){0,1}",
"captures": {
"1": {
"name": "keyword.symbol.fsharp"
@@ -772,9 +837,9 @@
}
},
{
- "begin": "(<(?![[:space:]]*\\)))",
+ "begin": "(<+(?![[:space:]]*\\)))",
"beginComment": "The group (?![[:space:]]*\\) is for protection against overload operator. static member (<)",
- "end": "((?)",
+ "end": "((?|\\))",
"endComment": "The group (? when using SRTP synthax",
"beginCaptures": {
"1": {
@@ -792,6 +857,9 @@
}
]
},
+ {
+ "include": "#anonymous_record_declaration"
+ },
{
"begin": "({)",
"end": "(})",
@@ -814,6 +882,14 @@
{
"include": "#definition"
},
+ {
+ "match": "(?<=>)\\s*(``([[:alpha:]0-9'^._ ]+)``|[[:alpha:]0-9'`^._]+)",
+ "captures": {
+ "1": {
+ "name": "entity.name.type.fsharp"
+ }
+ }
+ },
{
"include": "#variables"
},
@@ -826,7 +902,7 @@
"patterns": [
{
"name": "binding.fsharp",
- "begin": "\\b(let mutable|static let mutable|let inline|let|member val|static member inline|static member|default|member|override|let!)(\\s+rec|mutable)?(\\s+\\[\\<.*\\>\\])?\\s*(private|internal|public)?\\s+(\\[[^-=]*\\]|[_[:alpha:]]([_[:alpha:]0-9,\\._]+)*|``[_[:alpha:]]([_[:alpha:]0-9,\\._`\\s]+|(?<=,)\\s)*)?",
+ "begin": "\\b(let mutable|static let mutable|let inline|let|member val|static member inline|static member|default|member|override|let!)(\\s+rec|mutable)?(\\s+\\[\\<.*\\>\\])?\\s*(private|internal|public)?\\s+(\\[[^-=]*\\]|[_[:alpha:]]([_[:alpha:]0-9\\._]+)*|``[_[:alpha:]]([_[:alpha:]0-9\\._`\\s]+|(?<=,)\\s)*)?",
"end": "\\s*(with\\b|=|\\n+=|(?<=\\=))",
"beginCaptures": {
"1": {
@@ -856,6 +932,26 @@
}
]
},
+ {
+ "name": "binding.fsharp",
+ "begin": "\\b((get|set)\\s*(?=\\())(\\[[^-=]*\\]|[_[:alpha:]]([_[:alpha:]0-9\\._]+)*|``[_[:alpha:]]([_[:alpha:]0-9\\._`\\s]+|(?<=,)\\s)*)?",
+ "end": "\\s*(=|\\n+=|(?<=\\=))",
+ "beginCaptures": {
+ "3": {
+ "name": "variable.fsharp"
+ }
+ },
+ "endCaptures": {
+ "1": {
+ "name": "keyword.fsharp"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#common_binding_definition"
+ }
+ ]
+ },
{
"name": "binding.fsharp",
"begin": "\\b(static val mutable|val mutable|val)(\\s+rec|mutable)?(\\s+\\[\\<.*\\>\\])?\\s*(private|internal|public)?\\s+(\\[[^-=]*\\]|[_[:alpha:]]([_[:alpha:]0-9,\\._]+)*|``[_[:alpha:]]([_[:alpha:]0-9,\\._`\\s]+|(?<=,)\\s)*)?",
@@ -920,13 +1016,16 @@
}
},
{
- "match": "([[:alpha:]0-9'`^._]+)|``([[:alpha:]0-9'^._ ]+)``",
+ "match": "(``([[:alpha:]0-9'^._ ]+)``|[[:alpha:]0-9'`^._]+)",
"captures": {
"1": {
"name": "entity.name.type.fsharp"
}
}
},
+ {
+ "include": "#anonymous_record_declaration"
+ },
{
"include": "#keywords"
}
@@ -1007,6 +1106,9 @@
"name": "entity.name.section.fsharp"
}
}
+ },
+ {
+ "include": "#comments"
}
]
},
@@ -1142,7 +1244,7 @@
"match": "\\(\\)"
},
{
- "match": "(\\?{0,1})(``[[:alpha:]0-9'`^:,._ ]+``|[[:alpha:]0-9'`<>^._ ]\\w*)",
+ "match": "(\\?{0,1})(``[[:alpha:]0-9'`^:,._ ]+``|(?!private\\b)\\b[\\w[:alpha:]0-9'`<>^._ ]+)",
"captures": {
"1": {
"name": "keyword.symbol.fsharp"
@@ -1200,6 +1302,9 @@
}
}
},
+ {
+ "include": "#anonymous_record_declaration"
+ },
{
"begin": "(\\?{0,1})([[:alpha:]0-9'`^._ ]+)\\s*(:)(\\s*([?[:alpha:]0-9'`^._ ]+)(<))",
"end": "(>)",
diff --git a/extensions/fsharp/test/colorize-results/test_fs.json b/extensions/fsharp/test/colorize-results/test_fs.json
index 37c0b61c1..e5736fc10 100644
--- a/extensions/fsharp/test/colorize-results/test_fs.json
+++ b/extensions/fsharp/test/colorize-results/test_fs.json
@@ -605,7 +605,7 @@
}
},
{
- "c": " get",
+ "c": " ",
"t": "source.fsharp",
"r": {
"dark_plus": "default: #D4D4D4",
@@ -615,9 +615,20 @@
"hc_black": "default: #FFFFFF"
}
},
+ {
+ "c": "get",
+ "t": "source.fsharp binding.fsharp",
+ "r": {
+ "dark_plus": "default: #D4D4D4",
+ "light_plus": "default: #000000",
+ "dark_vs": "default: #D4D4D4",
+ "light_vs": "default: #000000",
+ "hc_black": "default: #FFFFFF"
+ }
+ },
{
"c": "()",
- "t": "source.fsharp constant.language.unit.fsharp",
+ "t": "source.fsharp binding.fsharp constant.language.unit.fsharp",
"r": {
"dark_plus": "constant.language: #569CD6",
"light_plus": "constant.language: #0000FF",
@@ -628,7 +639,7 @@
},
{
"c": " ",
- "t": "source.fsharp",
+ "t": "source.fsharp binding.fsharp",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@@ -639,7 +650,7 @@
},
{
"c": "=",
- "t": "source.fsharp keyword.symbol.fsharp",
+ "t": "source.fsharp binding.fsharp keyword.fsharp",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
@@ -682,7 +693,7 @@
}
},
{
- "c": " set",
+ "c": " ",
"t": "source.fsharp",
"r": {
"dark_plus": "default: #D4D4D4",
@@ -692,9 +703,20 @@
"hc_black": "default: #FFFFFF"
}
},
+ {
+ "c": "set",
+ "t": "source.fsharp binding.fsharp",
+ "r": {
+ "dark_plus": "default: #D4D4D4",
+ "light_plus": "default: #000000",
+ "dark_vs": "default: #D4D4D4",
+ "light_vs": "default: #000000",
+ "hc_black": "default: #FFFFFF"
+ }
+ },
{
"c": "(",
- "t": "source.fsharp keyword.symbol.fsharp",
+ "t": "source.fsharp binding.fsharp keyword.symbol.fsharp",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
@@ -705,18 +727,18 @@
},
{
"c": "value",
- "t": "source.fsharp",
+ "t": "source.fsharp binding.fsharp variable.parameter.fsharp",
"r": {
- "dark_plus": "default: #D4D4D4",
- "light_plus": "default: #000000",
+ "dark_plus": "variable: #9CDCFE",
+ "light_plus": "variable: #001080",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
- "hc_black": "default: #FFFFFF"
+ "hc_black": "variable: #9CDCFE"
}
},
{
"c": ")",
- "t": "source.fsharp keyword.symbol.fsharp",
+ "t": "source.fsharp binding.fsharp keyword.symbol.fsharp",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
@@ -727,7 +749,7 @@
},
{
"c": " ",
- "t": "source.fsharp",
+ "t": "source.fsharp binding.fsharp",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@@ -738,7 +760,7 @@
},
{
"c": "=",
- "t": "source.fsharp keyword.symbol.fsharp",
+ "t": "source.fsharp binding.fsharp keyword.fsharp",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
@@ -979,7 +1001,7 @@
}
},
{
- "c": " targetAge",
+ "c": " targetAge ",
"t": "source.fsharp binding.fsharp variable.parameter.fsharp",
"r": {
"dark_plus": "variable: #9CDCFE",
@@ -989,17 +1011,6 @@
"hc_black": "variable: #9CDCFE"
}
},
- {
- "c": " ",
- "t": "source.fsharp binding.fsharp",
- "r": {
- "dark_plus": "default: #D4D4D4",
- "light_plus": "default: #000000",
- "dark_vs": "default: #D4D4D4",
- "light_vs": "default: #000000",
- "hc_black": "default: #FFFFFF"
- }
- },
{
"c": "=",
"t": "source.fsharp binding.fsharp keyword.fsharp",
diff --git a/extensions/git-ui/.vscodeignore b/extensions/git-ui/.vscodeignore
new file mode 100644
index 000000000..7462f7448
--- /dev/null
+++ b/extensions/git-ui/.vscodeignore
@@ -0,0 +1,8 @@
+src/**
+test/**
+out/**
+tsconfig.json
+build/**
+extension.webpack.config.js
+cgmanifest.json
+yarn.lock
diff --git a/extensions/git-ui/README.md b/extensions/git-ui/README.md
new file mode 100644
index 000000000..d418425ac
--- /dev/null
+++ b/extensions/git-ui/README.md
@@ -0,0 +1,7 @@
+# Git UI integration for Visual Studio Code
+
+**Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled.
+
+## Features
+
+See [Git support in VS Code](https://code.visualstudio.com/docs/editor/versioncontrol#_git-support) to learn about the features of this extension.
diff --git a/extensions/git-ui/cgmanifest.json b/extensions/git-ui/cgmanifest.json
new file mode 100644
index 000000000..f3071eb69
--- /dev/null
+++ b/extensions/git-ui/cgmanifest.json
@@ -0,0 +1,4 @@
+{
+ "registrations": [],
+ "version": 1
+}
\ No newline at end of file
diff --git a/extensions/git-ui/extension.webpack.config.js b/extensions/git-ui/extension.webpack.config.js
new file mode 100644
index 000000000..19c0ea304
--- /dev/null
+++ b/extensions/git-ui/extension.webpack.config.js
@@ -0,0 +1,17 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+//@ts-check
+
+'use strict';
+
+const withDefaults = require('../shared.webpack.config');
+
+module.exports = withDefaults({
+ context: __dirname,
+ entry: {
+ main: './src/main.ts'
+ }
+});
diff --git a/extensions/git-ui/package.json b/extensions/git-ui/package.json
new file mode 100644
index 000000000..2f1ab43f8
--- /dev/null
+++ b/extensions/git-ui/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "git-ui",
+ "displayName": "%displayName%",
+ "description": "%description%",
+ "publisher": "vscode",
+ "version": "1.0.0",
+ "engines": {
+ "vscode": "^1.5.0"
+ },
+ "extensionKind": "ui",
+ "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217",
+ "enableProposedApi": true,
+ "categories": [
+ "Other"
+ ],
+ "activationEvents": [
+ "onCommand:git.credential"
+ ],
+ "main": "./out/main",
+ "icon": "resources/icons/git.png",
+ "scripts": {
+ "compile": "gulp compile-extension:git-ui",
+ "watch": "gulp watch-extension:git-ui"
+ },
+ "devDependencies": {
+ "@types/node": "^10.14.8"
+ }
+}
\ No newline at end of file
diff --git a/extensions/git-ui/package.nls.json b/extensions/git-ui/package.nls.json
new file mode 100644
index 000000000..5303e91f4
--- /dev/null
+++ b/extensions/git-ui/package.nls.json
@@ -0,0 +1,4 @@
+{
+ "displayName": "Git UI",
+ "description": "Git SCM UI Integration"
+}
\ No newline at end of file
diff --git a/extensions/git-ui/resources/icons/git.png b/extensions/git-ui/resources/icons/git.png
new file mode 100644
index 000000000..51f4ae540
Binary files /dev/null and b/extensions/git-ui/resources/icons/git.png differ
diff --git a/extensions/git-ui/src/main.ts b/extensions/git-ui/src/main.ts
new file mode 100644
index 000000000..d233b753b
--- /dev/null
+++ b/extensions/git-ui/src/main.ts
@@ -0,0 +1,57 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { ExtensionContext, commands } from 'vscode';
+
+import * as cp from 'child_process';
+
+export async function deactivate(): Promise
{
+}
+
+export async function activate(context: ExtensionContext): Promise {
+ context.subscriptions.push(commands.registerCommand('git.credential', async (data: any) => {
+ try {
+ const { stdout, stderr } = await exec(`git credential ${data.command}`, {
+ stdin: data.stdin,
+ env: Object.assign(process.env, { GIT_TERMINAL_PROMPT: '0' })
+ });
+ return { stdout, stderr, code: 0 };
+ } catch ({ stdout, stderr, error }) {
+ const code = error.code || 0;
+ if (stderr.indexOf('terminal prompts disabled') !== -1) {
+ stderr = '';
+ }
+ return { stdout, stderr, code };
+ }
+ }));
+}
+
+export interface ExecResult {
+ error: Error | null;
+ stdout: string;
+ stderr: string;
+}
+
+
+export function exec(command: string, options: cp.ExecOptions & { stdin?: string } = {}) {
+ return new Promise((resolve, reject) => {
+ const child = cp.exec(command, options, (error, stdout, stderr) => {
+ (error ? reject : resolve)({ error, stdout, stderr });
+ });
+ if (options.stdin) {
+ child.stdin.write(options.stdin, (err: any) => {
+ if (err) {
+ reject(err);
+ return;
+ }
+ child.stdin.end((err: any) => {
+ if (err) {
+ reject(err);
+ }
+ });
+ });
+ }
+ });
+}
diff --git a/extensions/git-ui/src/typings/refs.d.ts b/extensions/git-ui/src/typings/refs.d.ts
new file mode 100644
index 000000000..a93343122
--- /dev/null
+++ b/extensions/git-ui/src/typings/refs.d.ts
@@ -0,0 +1,8 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+///
+///
+///
\ No newline at end of file
diff --git a/extensions/git-ui/tsconfig.json b/extensions/git-ui/tsconfig.json
new file mode 100644
index 000000000..27e9268b3
--- /dev/null
+++ b/extensions/git-ui/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "extends": "../shared.tsconfig.json",
+ "compilerOptions": {
+ "outDir": "./out",
+ "experimentalDecorators": true,
+ "typeRoots": [
+ "./node_modules/@types"
+ ]
+ },
+ "include": [
+ "src/**/*"
+ ]
+}
\ No newline at end of file
diff --git a/extensions/git-ui/yarn.lock b/extensions/git-ui/yarn.lock
new file mode 100644
index 000000000..b23b0ac03
--- /dev/null
+++ b/extensions/git-ui/yarn.lock
@@ -0,0 +1,8 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@types/node@^10.14.8":
+ version "10.14.8"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9"
+ integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw==
diff --git a/extensions/git/cgmanifest.json b/extensions/git/cgmanifest.json
index d0bdb9ac4..e8081d647 100644
--- a/extensions/git/cgmanifest.json
+++ b/extensions/git/cgmanifest.json
@@ -6,7 +6,7 @@
"git": {
"name": "textmate/git.tmbundle",
"repositoryUrl": "https://github.com/textmate/git.tmbundle",
- "commitHash": "3f6ad2138200db14b57a090ecb2d2e733275ca3e"
+ "commitHash": "5870cf3f8abad3a6637bdf69250b5d2ded427dc4"
}
},
"licenseDetail": [
diff --git a/extensions/git/package.json b/extensions/git/package.json
index 4395d306b..4a789aaa6 100644
--- a/extensions/git/package.json
+++ b/extensions/git/package.json
@@ -3,6 +3,7 @@
"displayName": "%displayName%",
"description": "%description%",
"publisher": "vscode",
+ "license": "MIT",
"version": "1.0.0",
"engines": {
"vscode": "^1.5.0"
@@ -20,7 +21,8 @@
"scripts": {
"compile": "gulp compile-extension:git",
"watch": "gulp watch-extension:git",
- "update-grammar": "node ./build/update-grammars.js"
+ "update-grammar": "node ./build/update-grammars.js",
+ "test": "mocha"
},
"contributes": {
"commands": [
@@ -80,8 +82,8 @@
"title": "%command.openFile%",
"category": "Git",
"icon": {
- "light": "resources/icons/light/open-file-mono.svg",
- "dark": "resources/icons/dark/open-file-mono.svg"
+ "light": "resources/icons/light/open-file.svg",
+ "dark": "resources/icons/dark/open-file.svg"
}
},
{
@@ -317,12 +319,12 @@
},
{
"command": "git.pushWithTags",
- "title": "%command.pushWithTags%",
+ "title": "%command.pushFollowTags%",
"category": "Git"
},
{
"command": "git.pushWithTagsForce",
- "title": "%command.pushWithTagsForce%",
+ "title": "%command.pushFollowTagsForce%",
"category": "Git"
},
{
@@ -1244,12 +1246,18 @@
},
"git.ignoredRepositories": {
"type": "array",
+ "items": {
+ "type": "string"
+ },
"default": [],
"scope": "window",
"description": "%config.ignoredRepositories%"
},
"git.scanRepositories": {
"type": "array",
+ "items": {
+ "type": "string"
+ },
"default": [],
"scope": "resource",
"description": "%config.scanRepositories%"
@@ -1272,6 +1280,12 @@
"default": false,
"description": "%config.fetchOnPull%"
},
+ "git.pullTags": {
+ "type": "boolean",
+ "scope": "resource",
+ "default": true,
+ "description": "%config.pullTags%"
+ },
"git.autoStash": {
"type": "boolean",
"scope": "resource",
@@ -1452,13 +1466,14 @@
"jschardet": "^1.6.0",
"vscode-extension-telemetry": "0.1.1",
"vscode-nls": "^4.0.0",
+ "vscode-uri": "^2.0.0",
"which": "^1.3.0"
},
"devDependencies": {
"@types/byline": "4.2.31",
"@types/file-type": "^5.2.1",
"@types/mocha": "2.2.43",
- "@types/node": "^10.12.21",
+ "@types/node": "^10.14.8",
"@types/which": "^1.0.28",
"mocha": "^3.2.0"
}
diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json
index 892e3519c..adff0134e 100644
--- a/extensions/git/package.nls.json
+++ b/extensions/git/package.nls.json
@@ -47,8 +47,8 @@
"command.pushForce": "Push (Force)",
"command.pushTo": "Push to...",
"command.pushToForce": "Push to... (Force)",
- "command.pushWithTags": "Push With Tags",
- "command.pushWithTagsForce": "Push With Tags (Force)",
+ "command.pushFollowTags": "Push (Follow Tags)",
+ "command.pushFollowTagsForce": "Push (Follow Tags, Force)",
"command.addRemote": "Add Remote",
"command.removeRemote": "Remove Remote",
"command.sync": "Sync",
@@ -112,6 +112,7 @@
"config.rebaseWhenSync": "Force git to use rebase when running the sync command.",
"config.confirmEmptyCommits": "Always confirm the creation of empty commits.",
"config.fetchOnPull": "Fetch all branches when pulling or just the current one.",
+ "config.pullTags": "Fetch all tags when pulling.",
"config.autoStash": "Stash any changes before pulling and restore them after successful pull.",
"config.allowForcePush": "Controls whether force push (with or without lease) is enabled.",
"config.useForcePushWithLease": "Controls whether force pushing uses the safer force-with-lease variant.",
diff --git a/extensions/git/resources/icons/dark/check.svg b/extensions/git/resources/icons/dark/check.svg
index c225b2f59..865cc83c3 100644
--- a/extensions/git/resources/icons/dark/check.svg
+++ b/extensions/git/resources/icons/dark/check.svg
@@ -1 +1,3 @@
-
\ No newline at end of file
+
diff --git a/extensions/git/resources/icons/dark/clean.svg b/extensions/git/resources/icons/dark/clean.svg
index 3770d63d5..de85d6ba6 100644
--- a/extensions/git/resources/icons/dark/clean.svg
+++ b/extensions/git/resources/icons/dark/clean.svg
@@ -1 +1,3 @@
-
\ No newline at end of file
+
diff --git a/extensions/git/resources/icons/dark/git.svg b/extensions/git/resources/icons/dark/git.svg
index c08b1c2e4..4d9389336 100644
--- a/extensions/git/resources/icons/dark/git.svg
+++ b/extensions/git/resources/icons/dark/git.svg
@@ -1 +1,3 @@
-
\ No newline at end of file
+
diff --git a/extensions/git/resources/icons/dark/open-change.svg b/extensions/git/resources/icons/dark/open-change.svg
index 6f785c26a..41ebc85a8 100644
--- a/extensions/git/resources/icons/dark/open-change.svg
+++ b/extensions/git/resources/icons/dark/open-change.svg
@@ -1 +1,3 @@
-
\ No newline at end of file
+
diff --git a/extensions/git/resources/icons/dark/open-file-mono.svg b/extensions/git/resources/icons/dark/open-file-mono.svg
deleted file mode 100644
index 830727e70..000000000
--- a/extensions/git/resources/icons/dark/open-file-mono.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/extensions/git/resources/icons/dark/open-file.svg b/extensions/git/resources/icons/dark/open-file.svg
index f6302185a..ed302ae13 100644
--- a/extensions/git/resources/icons/dark/open-file.svg
+++ b/extensions/git/resources/icons/dark/open-file.svg
@@ -1 +1,3 @@
-
\ No newline at end of file
+
diff --git a/extensions/git/resources/icons/dark/refresh.svg b/extensions/git/resources/icons/dark/refresh.svg
index d79fdaa4e..e1f05aade 100644
--- a/extensions/git/resources/icons/dark/refresh.svg
+++ b/extensions/git/resources/icons/dark/refresh.svg
@@ -1 +1,4 @@
-
\ No newline at end of file
+
diff --git a/extensions/git/resources/icons/dark/stage.svg b/extensions/git/resources/icons/dark/stage.svg
index 3475c1e19..4d9389336 100644
--- a/extensions/git/resources/icons/dark/stage.svg
+++ b/extensions/git/resources/icons/dark/stage.svg
@@ -1 +1,3 @@
-
\ No newline at end of file
+
diff --git a/extensions/git/resources/icons/dark/unstage.svg b/extensions/git/resources/icons/dark/unstage.svg
index 2de46fcf5..4c5a9c1e3 100644
--- a/extensions/git/resources/icons/dark/unstage.svg
+++ b/extensions/git/resources/icons/dark/unstage.svg
@@ -1 +1,3 @@
-
\ No newline at end of file
+
diff --git a/extensions/git/resources/icons/light/check.svg b/extensions/git/resources/icons/light/check.svg
index 3f365c480..e1a546660 100644
--- a/extensions/git/resources/icons/light/check.svg
+++ b/extensions/git/resources/icons/light/check.svg
@@ -1 +1,3 @@
-
\ No newline at end of file
+
diff --git a/extensions/git/resources/icons/light/clean.svg b/extensions/git/resources/icons/light/clean.svg
index f86ec7d62..b70626957 100644
--- a/extensions/git/resources/icons/light/clean.svg
+++ b/extensions/git/resources/icons/light/clean.svg
@@ -1 +1,3 @@
-
\ No newline at end of file
+
diff --git a/extensions/git/resources/icons/light/git.svg b/extensions/git/resources/icons/light/git.svg
index d1049a44d..01a9de7d5 100644
--- a/extensions/git/resources/icons/light/git.svg
+++ b/extensions/git/resources/icons/light/git.svg
@@ -1 +1,3 @@
-
\ No newline at end of file
+
diff --git a/extensions/git/resources/icons/light/open-change.svg b/extensions/git/resources/icons/light/open-change.svg
index 873b93d81..772c3c198 100644
--- a/extensions/git/resources/icons/light/open-change.svg
+++ b/extensions/git/resources/icons/light/open-change.svg
@@ -1 +1,3 @@
-
\ No newline at end of file
+
diff --git a/extensions/git/resources/icons/light/open-file-mono.svg b/extensions/git/resources/icons/light/open-file-mono.svg
deleted file mode 100644
index fa3f245b7..000000000
--- a/extensions/git/resources/icons/light/open-file-mono.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/extensions/git/resources/icons/light/open-file.svg b/extensions/git/resources/icons/light/open-file.svg
index d23a23c6b..392a840c5 100644
--- a/extensions/git/resources/icons/light/open-file.svg
+++ b/extensions/git/resources/icons/light/open-file.svg
@@ -1 +1,3 @@
-
\ No newline at end of file
+
diff --git a/extensions/git/resources/icons/light/refresh.svg b/extensions/git/resources/icons/light/refresh.svg
index e03457481..9b1d91084 100644
--- a/extensions/git/resources/icons/light/refresh.svg
+++ b/extensions/git/resources/icons/light/refresh.svg
@@ -1 +1,4 @@
-
\ No newline at end of file
+
diff --git a/extensions/git/resources/icons/light/stage.svg b/extensions/git/resources/icons/light/stage.svg
index bdecdb0e4..01a9de7d5 100644
--- a/extensions/git/resources/icons/light/stage.svg
+++ b/extensions/git/resources/icons/light/stage.svg
@@ -1 +1,3 @@
-
\ No newline at end of file
+
diff --git a/extensions/git/resources/icons/light/unstage.svg b/extensions/git/resources/icons/light/unstage.svg
index f5d128b2d..d12a8ee31 100644
--- a/extensions/git/resources/icons/light/unstage.svg
+++ b/extensions/git/resources/icons/light/unstage.svg
@@ -1 +1,3 @@
-
\ No newline at end of file
+
diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts
index 25742babc..a4fd677db 100644
--- a/extensions/git/src/api/api1.ts
+++ b/extensions/git/src/api/api1.ts
@@ -5,7 +5,7 @@
import { Model } from '../model';
import { Repository as BaseRepository, Resource } from '../repository';
-import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions } from './git';
+import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState } from './git';
import { Event, SourceControlInputBox, Uri, SourceControl } from 'vscode';
import { mapEvent } from '../util';
@@ -214,6 +214,14 @@ export class ApiImpl implements API {
readonly git = new ApiGit(this._model);
+ get state(): APIState {
+ return this._model.state;
+ }
+
+ get onDidChangeState(): Event {
+ return this._model.onDidChangeState;
+ }
+
get onDidOpenRepository(): Event {
return mapEvent(this._model.onDidOpenRepository, r => new ApiRepository(r));
}
diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts
index ae8eb5315..21195974f 100644
--- a/extensions/git/src/api/git.d.ts
+++ b/extensions/git/src/api/git.d.ts
@@ -176,7 +176,11 @@ export interface Repository {
log(options?: LogOptions): Promise;
}
+export type APIState = 'uninitialized' | 'initialized';
+
export interface API {
+ readonly state: APIState;
+ readonly onDidChangeState: Event;
readonly git: Git;
readonly repositories: Repository[];
readonly onDidOpenRepository: Event;
@@ -235,4 +239,4 @@ export const enum GitErrorCodes {
CantRebaseMultipleBranches = 'CantRebaseMultipleBranches',
PatchDoesNotApply = 'PatchDoesNotApply',
NoPathFound = 'NoPathFound'
-}
\ No newline at end of file
+}
diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts
index f6e00e9ab..d75e6f9bc 100755
--- a/extensions/git/src/commands.ts
+++ b/extensions/git/src/commands.ts
@@ -56,7 +56,13 @@ class CheckoutRemoteHeadItem extends CheckoutItem {
return;
}
- await repository.checkoutTracking(this.ref.name);
+ const branches = await repository.findTrackingBranches(this.ref.name);
+
+ if (branches.length > 0) {
+ await repository.checkout(branches[0].name!);
+ } else {
+ await repository.checkoutTracking(this.ref.name);
+ }
}
}
@@ -196,7 +202,7 @@ function createCheckoutItems(repository: Repository): CheckoutItem[] {
enum PushType {
Push,
PushTo,
- PushTags,
+ PushFollowTags,
}
interface PushOptions {
@@ -481,10 +487,10 @@ export class CommandCenter {
(_, token) => this.git.clone(url!, parentPath, token)
);
- const choices = [];
let message = localize('proposeopen', "Would you like to open the cloned repository?");
- const open = localize('openrepo', "Open Repository");
- choices.push(open);
+ const open = localize('openrepo', "Open");
+ const openNewWindow = localize('openreponew', "Open in New Window");
+ const choices = [open, openNewWindow];
const addToWorkspace = localize('add', "Add to Workspace");
if (workspace.workspaceFolders) {
@@ -509,6 +515,8 @@ export class CommandCenter {
commands.executeCommand('vscode.openFolder', uri);
} else if (result === addToWorkspace) {
workspace.updateWorkspaceFolders(workspace.workspaceFolders!.length, 0, { uri });
+ } else if (result === openNewWindow) {
+ commands.executeCommand('vscode.openFolder', uri, true);
}
} catch (err) {
if (/already exists and is not an empty directory/.test(err && err.stderr || '')) {
@@ -593,10 +601,10 @@ export class CommandCenter {
await this.git.init(repositoryPath);
- const choices = [];
let message = localize('proposeopen init', "Would you like to open the initialized repository?");
- const open = localize('openrepo', "Open Repository");
- choices.push(open);
+ const open = localize('openrepo', "Open");
+ const openNewWindow = localize('openreponew', "Open in New Window");
+ const choices = [open, openNewWindow];
if (!askToOpen) {
return;
@@ -615,6 +623,8 @@ export class CommandCenter {
commands.executeCommand('vscode.openFolder', uri);
} else if (result === addToWorkspace) {
workspace.updateWorkspaceFolders(workspace.workspaceFolders!.length, 0, { uri });
+ } else if (result === openNewWindow) {
+ commands.executeCommand('vscode.openFolder', uri, true);
} else {
await this.model.openRepository(repositoryPath);
}
@@ -663,7 +673,6 @@ export class CommandCenter {
if (!(resource instanceof Resource)) {
// can happen when called from a keybinding
- console.log('WHAT');
resource = this.getSCMResource();
}
@@ -728,6 +737,8 @@ export class CommandCenter {
}
const HEAD = await this.getLeftResource(resource);
+ const basename = path.basename(resource.resourceUri.fsPath);
+ const title = `${basename} (HEAD)`;
if (!HEAD) {
window.showWarningMessage(localize('HEAD not available', "HEAD version of '{0}' is not available.", path.basename(resource.resourceUri.fsPath)));
@@ -738,7 +749,7 @@ export class CommandCenter {
preview
};
- return await commands.executeCommand('vscode.open', HEAD, opts);
+ return await commands.executeCommand('vscode.open', HEAD, opts, title);
}
@command('git.openChange')
@@ -1104,7 +1115,7 @@ export class CommandCenter {
if (scmResources.length === 1) {
if (untrackedCount > 0) {
- message = localize('confirm delete', "Are you sure you want to DELETE {0}?", path.basename(scmResources[0].resourceUri.fsPath));
+ message = localize('confirm delete', "Are you sure you want to DELETE {0}?\nThis is IRREVERSIBLE!\nThis file will be FOREVER LOST.", path.basename(scmResources[0].resourceUri.fsPath));
yes = localize('delete file', "Delete file");
} else {
if (scmResources[0].type === Status.DELETED) {
@@ -1123,7 +1134,7 @@ export class CommandCenter {
}
if (untrackedCount > 0) {
- message = `${message}\n\n${localize('warn untracked', "This will DELETE {0} untracked files!", untrackedCount)}`;
+ message = `${message}\n\n${localize('warn untracked', "This will DELETE {0} untracked files!\nThis is IRREVERSIBLE!\nThese files will be FOREVER LOST.", untrackedCount)}`;
}
}
@@ -1164,7 +1175,7 @@ export class CommandCenter {
await repository.clean(resources.map(r => r.resourceUri));
return;
} else if (resources.length === 1) {
- const message = localize('confirm delete', "Are you sure you want to DELETE {0}?", path.basename(resources[0].resourceUri.fsPath));
+ const message = localize('confirm delete', "Are you sure you want to DELETE {0}?\nThis is IRREVERSIBLE!\nThis file will be FOREVER LOST.", path.basename(resources[0].resourceUri.fsPath));
const yes = localize('delete file', "Delete file");
const pick = await window.showWarningMessage(message, { modal: true }, yes);
@@ -1560,8 +1571,11 @@ export class CommandCenter {
@command('git.renameBranch', { repository: true })
async renameBranch(repository: Repository): Promise {
- const placeHolder = localize('provide branch name', "Please provide a branch name");
- const name = await window.showInputBox({ placeHolder });
+ const name = await window.showInputBox({
+ placeHolder: localize('branch name', "Branch name"),
+ prompt: localize('provide branch name', "Please provide a branch name"),
+ value: repository.HEAD && repository.HEAD.name
+ });
if (!name || name.trim().length === 0) {
return;
@@ -1753,10 +1767,8 @@ export class CommandCenter {
}
}
- if (pushOptions.pushType === PushType.PushTags) {
- await repository.pushTags(undefined, forcePushMode);
-
- window.showInformationMessage(localize('push with tags success', "Successfully pushed with tags."));
+ if (pushOptions.pushType === PushType.PushFollowTags) {
+ await repository.pushFollowTags(undefined, forcePushMode);
return;
}
@@ -1813,13 +1825,13 @@ export class CommandCenter {
}
@command('git.pushWithTags', { repository: true })
- async pushWithTags(repository: Repository): Promise {
- await this._push(repository, { pushType: PushType.PushTags });
+ async pushFollowTags(repository: Repository): Promise {
+ await this._push(repository, { pushType: PushType.PushFollowTags });
}
@command('git.pushWithTagsForce', { repository: true })
- async pushWithTagsForce(repository: Repository): Promise {
- await this._push(repository, { pushType: PushType.PushTags, forcePush: true });
+ async pushFollowTagsForce(repository: Repository): Promise {
+ await this._push(repository, { pushType: PushType.PushFollowTags, forcePush: true });
}
@command('git.pushTo', { repository: true })
diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts
index 3132b8cdf..30348608d 100644
--- a/extensions/git/src/git.ts
+++ b/extensions/git/src/git.ts
@@ -12,7 +12,8 @@ import { EventEmitter } from 'events';
import iconv = require('iconv-lite');
import * as filetype from 'file-type';
import { assign, groupBy, denodeify, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent } from './util';
-import { CancellationToken, Uri } from 'vscode';
+import { CancellationToken } from 'vscode';
+import { URI } from 'vscode-uri';
import { detectEncoding } from './encoding';
import { Ref, RefType, Branch, Remote, GitErrorCodes, LogOptions, Change, Status } from './api/git';
@@ -306,6 +307,8 @@ function getGitErrorCode(stderr: string): string | undefined {
return GitErrorCodes.BranchAlreadyExists;
} else if (/'.+' is not a valid branch name/.test(stderr)) {
return GitErrorCodes.InvalidBranchName;
+ } else if (/Please,? commit your changes or stash them/.test(stderr)) {
+ return GitErrorCodes.DirtyWorkTree;
}
return undefined;
@@ -326,8 +329,8 @@ export class Git {
this.env = options.env || {};
}
- open(repository: string): Repository {
- return new Repository(this, repository);
+ open(repository: string, dotGit: string): Repository {
+ return new Repository(this, repository, dotGit);
}
async init(repository: string): Promise {
@@ -336,7 +339,7 @@ export class Git {
}
async clone(url: string, parentPath: string, cancellationToken?: CancellationToken): Promise {
- let baseFolderName = decodeURI(url).replace(/^.*\//, '').replace(/\.git$/, '') || 'repository';
+ let baseFolderName = decodeURI(url).replace(/[\/]+$/, '').replace(/^.*\//, '').replace(/\.git$/, '') || 'repository';
let folderName = baseFolderName;
let folderPath = path.join(parentPath, folderName);
let count = 1;
@@ -367,6 +370,17 @@ export class Git {
return path.normalize(result.stdout.trim());
}
+ async getRepositoryDotGit(repositoryPath: string): Promise {
+ const result = await this.exec(repositoryPath, ['rev-parse', '--git-dir']);
+ let dotGitPath = result.stdout.trim();
+
+ if (!path.isAbsolute(dotGitPath)) {
+ dotGitPath = path.join(repositoryPath, dotGitPath);
+ }
+
+ return path.normalize(dotGitPath);
+ }
+
async exec(cwd: string, args: string[], options: SpawnOptions = {}): Promise> {
options = assign({ cwd }, options || {});
return await this._exec(args, options);
@@ -555,7 +569,7 @@ export function parseGitmodules(raw: string): Submodule[] {
return;
}
- const propertyMatch = /^\s*(\w+) = (.*)$/.exec(line);
+ const propertyMatch = /^\s*(\w+)\s+=\s+(.*)$/.exec(line);
if (!propertyMatch) {
return;
@@ -634,6 +648,7 @@ export interface CommitOptions {
export interface PullOptions {
unshallow?: boolean;
+ tags?: boolean;
}
export enum ForcePushMode {
@@ -645,7 +660,8 @@ export class Repository {
constructor(
private _git: Git,
- private repositoryRoot: string
+ private repositoryRoot: string,
+ readonly dotGit: string
) { }
get git(): Git {
@@ -993,7 +1009,7 @@ export class Repository {
break;
}
- const originalUri = Uri.file(path.isAbsolute(resourcePath) ? resourcePath : path.join(this.repositoryRoot, resourcePath));
+ const originalUri = URI.file(path.isAbsolute(resourcePath) ? resourcePath : path.join(this.repositoryRoot, resourcePath));
let status: Status = Status.UNTRACKED;
// Copy or Rename status comes with a number, e.g. 'R100'. We don't need the number, so we use only first character of the status.
@@ -1021,7 +1037,7 @@ export class Repository {
break;
}
- const uri = Uri.file(path.isAbsolute(newPath) ? newPath : path.join(this.repositoryRoot, newPath));
+ const uri = URI.file(path.isAbsolute(newPath) ? newPath : path.join(this.repositoryRoot, newPath));
result.push({
uri,
renameUri: uri,
@@ -1360,7 +1376,11 @@ export class Repository {
}
async pull(rebase?: boolean, remote?: string, branch?: string, options: PullOptions = {}): Promise {
- const args = ['pull', '--tags'];
+ const args = ['pull'];
+
+ if (options.tags) {
+ args.push('--tags');
+ }
if (options.unshallow) {
args.push('--unshallow');
@@ -1411,7 +1431,7 @@ export class Repository {
}
if (tags) {
- args.push('--tags');
+ args.push('--follow-tags');
}
if (remote) {
@@ -1571,6 +1591,14 @@ export class Repository {
}
}
+ async findTrackingBranches(upstreamBranch: string): Promise {
+ const result = await this.run(['for-each-ref', '--format', '%(refname:short)%00%(upstream:short)', 'refs/heads']);
+ return result.stdout.trim().split('\n')
+ .map(line => line.trim().split('\0'))
+ .filter(([_, upstream]) => upstream === upstreamBranch)
+ .map(([ref]) => ({ name: ref, type: RefType.Head } as Branch));
+ }
+
async getRefs(): Promise[ {
const result = await this.run(['for-each-ref', '--format', '%(refname) %(objectname)', '--sort', '-committerdate']);
diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts
index 22c690a5d..da6a94042 100644
--- a/extensions/git/src/model.ts
+++ b/extensions/git/src/model.ts
@@ -12,7 +12,7 @@ import * as path from 'path';
import * as fs from 'fs';
import * as nls from 'vscode-nls';
import { fromGitUri } from './uri';
-import { GitErrorCodes } from './api/git';
+import { GitErrorCodes, APIState as State } from './api/git';
const localize = nls.loadMessageBundle();
@@ -63,26 +63,41 @@ export class Model {
private possibleGitRepositoryPaths = new Set();
+ private _onDidChangeState = new EventEmitter();
+ readonly onDidChangeState = this._onDidChangeState.event;
+
+ private _state: State = 'uninitialized';
+ get state(): State { return this._state; }
+
+ setState(state: State): void {
+ this._state = state;
+ this._onDidChangeState.fire(state);
+ }
+
private disposables: Disposable[] = [];
constructor(readonly git: Git, private globalState: Memento, private outputChannel: OutputChannel) {
workspace.onDidChangeWorkspaceFolders(this.onDidChangeWorkspaceFolders, this, this.disposables);
- this.onDidChangeWorkspaceFolders({ added: workspace.workspaceFolders || [], removed: [] });
-
window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, this.disposables);
- this.onDidChangeVisibleTextEditors(window.visibleTextEditors);
-
workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, this.disposables);
const fsWatcher = workspace.createFileSystemWatcher('**');
this.disposables.push(fsWatcher);
const onWorkspaceChange = anyEvent(fsWatcher.onDidChange, fsWatcher.onDidCreate, fsWatcher.onDidDelete);
- const onGitRepositoryChange = filterEvent(onWorkspaceChange, uri => /\/\.git\//.test(uri.path));
+ const onGitRepositoryChange = filterEvent(onWorkspaceChange, uri => /\/\.git/.test(uri.path));
const onPossibleGitRepositoryChange = filterEvent(onGitRepositoryChange, uri => !this.getRepository(uri));
onPossibleGitRepositoryChange(this.onPossibleGitRepositoryChange, this, this.disposables);
- this.scanWorkspaceFolders();
+ this.doInitialScan().finally(() => this.setState('initialized'));
+ }
+
+ private async doInitialScan(): Promise {
+ await Promise.all([
+ this.onDidChangeWorkspaceFolders({ added: workspace.workspaceFolders || [], removed: [] }),
+ this.onDidChangeVisibleTextEditors(window.visibleTextEditors),
+ this.scanWorkspaceFolders()
+ ]);
}
/**
@@ -157,8 +172,8 @@ export class Model {
.filter(r => !activeRepositories.has(r!.repository))
.filter(r => !(workspace.workspaceFolders || []).some(f => isDescendant(f.uri.fsPath, r!.repository.root))) as OpenRepository[];
- possibleRepositoryFolders.forEach(p => this.openRepository(p.uri.fsPath));
openRepositoriesToDispose.forEach(r => r.dispose());
+ await Promise.all(possibleRepositoryFolders.map(p => this.openRepository(p.uri.fsPath)));
}
private onDidChangeConfiguration(): void {
@@ -175,7 +190,7 @@ export class Model {
openRepositoriesToDispose.forEach(r => r.dispose());
}
- private onDidChangeVisibleTextEditors(editors: TextEditor[]): void {
+ private async onDidChangeVisibleTextEditors(editors: TextEditor[]): Promise {
const config = workspace.getConfiguration('git');
const autoRepositoryDetection = config.get('autoRepositoryDetection');
@@ -183,7 +198,7 @@ export class Model {
return;
}
- editors.forEach(editor => {
+ await Promise.all(editors.map(async editor => {
const uri = editor.document.uri;
if (uri.scheme !== 'file') {
@@ -196,8 +211,8 @@ export class Model {
return;
}
- this.openRepository(path.dirname(uri.fsPath));
- });
+ await this.openRepository(path.dirname(uri.fsPath));
+ }));
}
@sequentialize
@@ -232,9 +247,11 @@ export class Model {
return;
}
- const repository = new Repository(this.git.open(repositoryRoot), this.globalState);
+ const dotGit = await this.git.getRepositoryDotGit(repositoryRoot);
+ const repository = new Repository(this.git.open(repositoryRoot, dotGit), this.globalState, this.outputChannel);
this.open(repository);
+ await repository.status();
} catch (err) {
if (err.gitErrorCode === GitErrorCodes.NotAGitRepository) {
return;
@@ -420,4 +437,4 @@ export class Model {
this.possibleGitRepositoryPaths.clear();
this.disposables = dispose(this.disposables);
}
-}
\ No newline at end of file
+}
diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts
index 2fac9e2a3..196a1bdda 100644
--- a/extensions/git/src/repository.ts
+++ b/extensions/git/src/repository.ts
@@ -3,9 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { commands, Uri, Command, EventEmitter, Event, scm, SourceControl, SourceControlInputBox, SourceControlResourceGroup, SourceControlResourceState, SourceControlResourceDecorations, SourceControlInputBoxValidation, Disposable, ProgressLocation, window, workspace, WorkspaceEdit, ThemeColor, DecorationData, Memento, SourceControlInputBoxValidationType } from 'vscode';
+import { commands, Uri, Command, EventEmitter, Event, scm, SourceControl, SourceControlInputBox, SourceControlResourceGroup, SourceControlResourceState, SourceControlResourceDecorations, SourceControlInputBoxValidation, Disposable, ProgressLocation, window, workspace, WorkspaceEdit, ThemeColor, DecorationData, Memento, SourceControlInputBoxValidationType, OutputChannel, LogLevel, env } from 'vscode';
import { Repository as BaseRepository, Commit, Stash, GitError, Submodule, CommitOptions, ForcePushMode } from './git';
-import { anyEvent, filterEvent, eventToPromise, dispose, find, isDescendant, IDisposable, onceEvent, EmptyDisposable, debounceEvent } from './util';
+import { anyEvent, filterEvent, eventToPromise, dispose, find, isDescendant, IDisposable, onceEvent, EmptyDisposable, debounceEvent, combinedDisposable, watch, IFileWatcher } from './util';
import { memoize, throttle, debounce } from './decorators';
import { toGitUri } from './uri';
import { AutoFetcher } from './autofetch';
@@ -299,6 +299,7 @@ export const enum Operation {
GetObjectDetails = 'GetObjectDetails',
SubmoduleUpdate = 'SubmoduleUpdate',
RebaseContinue = 'RebaseContinue',
+ FindTrackingBranches = 'GetTracking',
Apply = 'Apply',
Blame = 'Blame',
Log = 'Log',
@@ -446,6 +447,91 @@ class ProgressManager {
}
}
+class FileEventLogger {
+
+ private eventDisposable: IDisposable = EmptyDisposable;
+ private logLevelDisposable: IDisposable = EmptyDisposable;
+
+ constructor(
+ private onWorkspaceWorkingTreeFileChange: Event,
+ private onDotGitFileChange: Event,
+ private outputChannel: OutputChannel
+ ) {
+ this.logLevelDisposable = env.onDidChangeLogLevel(this.onDidChangeLogLevel, this);
+ this.onDidChangeLogLevel(env.logLevel);
+ }
+
+ private onDidChangeLogLevel(level: LogLevel): void {
+ this.eventDisposable.dispose();
+
+ if (level > LogLevel.Debug) {
+ return;
+ }
+
+ this.eventDisposable = combinedDisposable([
+ this.onWorkspaceWorkingTreeFileChange(uri => this.outputChannel.appendLine(`[debug] [wt] Change: ${uri.fsPath}`)),
+ this.onDotGitFileChange(uri => this.outputChannel.appendLine(`[debug] [.git] Change: ${uri.fsPath}`))
+ ]);
+ }
+
+ dispose(): void {
+ this.eventDisposable.dispose();
+ this.logLevelDisposable.dispose();
+ }
+}
+
+class DotGitWatcher implements IFileWatcher {
+
+ readonly event: Event;
+
+ private emitter = new EventEmitter();
+ private transientDisposables: IDisposable[] = [];
+ private disposables: IDisposable[] = [];
+
+ constructor(
+ private repository: Repository,
+ private outputChannel: OutputChannel
+ ) {
+ const rootWatcher = watch(repository.dotGit);
+ this.disposables.push(rootWatcher);
+
+ const filteredRootWatcher = filterEvent(rootWatcher.event, uri => !/\/\.git(\/index\.lock)?$/.test(uri.path));
+ this.event = anyEvent(filteredRootWatcher, this.emitter.event);
+
+ repository.onDidRunGitStatus(this.updateTransientWatchers, this, this.disposables);
+ this.updateTransientWatchers();
+ }
+
+ private updateTransientWatchers() {
+ this.transientDisposables = dispose(this.transientDisposables);
+
+ if (!this.repository.HEAD || !this.repository.HEAD.upstream) {
+ return;
+ }
+
+ this.transientDisposables = dispose(this.transientDisposables);
+
+ const { name, remote } = this.repository.HEAD.upstream;
+ const upstreamPath = path.join(this.repository.dotGit, 'refs', 'remotes', remote, name);
+
+ try {
+ const upstreamWatcher = watch(upstreamPath);
+ this.transientDisposables.push(upstreamWatcher);
+ upstreamWatcher.event(this.emitter.fire, this.emitter, this.transientDisposables);
+ } catch (err) {
+ if (env.logLevel <= LogLevel.Error) {
+ this.outputChannel.appendLine(`Failed to watch ref '${upstreamPath}', is most likely packed.\n${err.stack || err}`);
+ }
+ }
+ }
+
+ dispose() {
+ this.emitter.dispose();
+ this.transientDisposables = dispose(this.transientDisposables);
+ this.disposables = dispose(this.disposables);
+ }
+}
+
export class Repository implements Disposable {
private _onDidChangeRepository = new EventEmitter();
@@ -543,36 +629,52 @@ export class Repository implements Disposable {
return this.repository.root;
}
+ get dotGit(): string {
+ return this.repository.dotGit;
+ }
+
private isRepositoryHuge = false;
private didWarnAboutLimit = false;
private isFreshRepository: boolean | undefined = undefined;
+
private disposables: Disposable[] = [];
constructor(
private readonly repository: BaseRepository,
- globalState: Memento
+ globalState: Memento,
+ outputChannel: OutputChannel
) {
- const fsWatcher = workspace.createFileSystemWatcher('**');
- this.disposables.push(fsWatcher);
+ const workspaceWatcher = workspace.createFileSystemWatcher('**');
+ this.disposables.push(workspaceWatcher);
+
+ const onWorkspaceFileChange = anyEvent(workspaceWatcher.onDidChange, workspaceWatcher.onDidCreate, workspaceWatcher.onDidDelete);
+ const onWorkspaceRepositoryFileChange = filterEvent(onWorkspaceFileChange, uri => isDescendant(repository.root, uri.fsPath));
+ const onWorkspaceWorkingTreeFileChange = filterEvent(onWorkspaceRepositoryFileChange, uri => !/\/\.git($|\/)/.test(uri.path));
+
+ let onDotGitFileChange: Event;
+
+ try {
+ const dotGitFileWatcher = new DotGitWatcher(this, outputChannel);
+ onDotGitFileChange = dotGitFileWatcher.event;
+ this.disposables.push(dotGitFileWatcher);
+ } catch (err) {
+ if (env.logLevel <= LogLevel.Error) {
+ outputChannel.appendLine(`Failed to watch '${this.dotGit}', reverting to legacy API file watched. Some events might be lost.\n${err.stack || err}`);
+ }
- const workspaceFilter = (uri: Uri) => isDescendant(repository.root, uri.fsPath);
- const onWorkspaceDelete = filterEvent(fsWatcher.onDidDelete, workspaceFilter);
- const onWorkspaceChange = filterEvent(anyEvent(fsWatcher.onDidChange, fsWatcher.onDidCreate), workspaceFilter);
- const onRepositoryDotGitDelete = filterEvent(onWorkspaceDelete, uri => /\/\.git$/.test(uri.path));
- const onRepositoryChange = anyEvent(onWorkspaceDelete, onWorkspaceChange);
+ onDotGitFileChange = filterEvent(onWorkspaceRepositoryFileChange, uri => /\/\.git($|\/)/.test(uri.path));
+ }
- // relevant repository changes are:
- // - DELETE .git folder
- // - ANY CHANGE within .git folder except .git itself and .git/index.lock
- const onRelevantRepositoryChange = anyEvent(
- onRepositoryDotGitDelete,
- filterEvent(onRepositoryChange, uri => !/\/\.git(\/index\.lock)?$/.test(uri.path))
- );
+ // FS changes should trigger `git status`:
+ // - any change inside the repository working tree
+ // - any change whithin the first level of the `.git` folder, except the folder itself and `index.lock`
+ const onFileChange = anyEvent(onWorkspaceWorkingTreeFileChange, onDotGitFileChange);
+ onFileChange(this.onFileChange, this, this.disposables);
- onRelevantRepositoryChange(this.onFSChange, this, this.disposables);
+ // Relevate repository changes should trigger virtual document change events
+ onDotGitFileChange(this._onDidChangeRepository.fire, this._onDidChangeRepository, this.disposables);
- const onRelevantGitChange = filterEvent(onRelevantRepositoryChange, uri => /\/\.git\//.test(uri.path));
- onRelevantGitChange(this._onDidChangeRepository.fire, this._onDidChangeRepository, this.disposables);
+ this.disposables.push(new FileEventLogger(onWorkspaceWorkingTreeFileChange, onDotGitFileChange, outputChannel));
const root = Uri.file(repository.root);
this._sourceControl = scm.createSourceControl('git', 'Git', root);
@@ -582,9 +684,9 @@ export class Repository implements Disposable {
this._sourceControl.inputBox.validateInput = this.validateInput.bind(this);
this.disposables.push(this._sourceControl);
- this._mergeGroup = this._sourceControl.createResourceGroup('merge', localize('merge changes', "Merge Changes"));
- this._indexGroup = this._sourceControl.createResourceGroup('index', localize('staged changes', "Staged Changes"));
- this._workingTreeGroup = this._sourceControl.createResourceGroup('workingTree', localize('changes', "Changes"));
+ this._mergeGroup = this._sourceControl.createResourceGroup('merge', localize('merge changes', "MERGE CHANGES"));
+ this._indexGroup = this._sourceControl.createResourceGroup('index', localize('staged changes', "STAGED CHANGES"));
+ this._workingTreeGroup = this._sourceControl.createResourceGroup('workingTree', localize('changes', "CHANGES"));
const updateIndexGroupVisibility = () => {
const config = workspace.getConfiguration('git', root);
@@ -622,7 +724,6 @@ export class Repository implements Disposable {
this.disposables.push(progressManager);
this.updateCommitTemplate();
- this.status();
}
validateInput(text: string, position: number): SourceControlInputBoxValidation | undefined {
@@ -909,6 +1010,10 @@ export class Repository implements Disposable {
await this.run(Operation.CheckoutTracking, () => this.repository.checkout(treeish, [], { track: true }));
}
+ async findTrackingBranches(upstreamRef: string): Promise {
+ return await this.run(Operation.FindTrackingBranches, () => this.repository.findTrackingBranches(upstreamRef));
+ }
+
async getCommit(ref: string): Promise {
return await this.repository.getCommit(ref);
}
@@ -979,11 +1084,12 @@ export class Repository implements Disposable {
await this.maybeAutoStash(async () => {
const config = workspace.getConfiguration('git', Uri.file(this.root));
const fetchOnPull = config.get('fetchOnPull');
+ const tags = config.get('pullTags');
if (fetchOnPull) {
- await this.repository.pull(rebase, undefined, undefined, { unshallow });
+ await this.repository.pull(rebase, undefined, undefined, { unshallow, tags });
} else {
- await this.repository.pull(rebase, remote, branch, { unshallow });
+ await this.repository.pull(rebase, remote, branch, { unshallow, tags });
}
});
});
@@ -1006,7 +1112,7 @@ export class Repository implements Disposable {
await this.run(Operation.Push, () => this.repository.push(remote, name, setUpstream, undefined, forcePushMode));
}
- async pushTags(remote?: string, forcePushMode?: ForcePushMode): Promise {
+ async pushFollowTags(remote?: string, forcePushMode?: ForcePushMode): Promise {
await this.run(Operation.Push, () => this.repository.push(remote, undefined, false, true, forcePushMode));
}
@@ -1039,11 +1145,12 @@ export class Repository implements Disposable {
await this.maybeAutoStash(async () => {
const config = workspace.getConfiguration('git', Uri.file(this.root));
const fetchOnPull = config.get('fetchOnPull');
+ const tags = config.get('pullTags');
if (fetchOnPull) {
- await this.repository.pull(rebase);
+ await this.repository.pull(rebase, undefined, undefined, { tags });
} else {
- await this.repository.pull(rebase, remoteName, pullBranch);
+ await this.repository.pull(rebase, remoteName, pullBranch, { tags });
}
const remote = this.remotes.find(r => r.name === remoteName);
@@ -1156,7 +1263,7 @@ export class Repository implements Disposable {
}
// https://git-scm.com/docs/git-check-ignore#git-check-ignore--z
- const child = this.repository.stream(['check-ignore', '-z', '--stdin'], { stdio: [null, null, null] });
+ const child = this.repository.stream(['check-ignore', '-v', '-z', '--stdin'], { stdio: [null, null, null] });
child.stdin.end(filePaths.join('\0'), 'utf8');
const onExit = (exitCode: number) => {
@@ -1164,8 +1271,7 @@ export class Repository implements Disposable {
// nothing ignored
resolve(new Set());
} else if (exitCode === 0) {
- // paths are separated by the null-character
- resolve(new Set(data.split('\0')));
+ resolve(new Set(this.parseIgnoreCheck(data)));
} else {
if (/ is in submodule /.test(stderr)) {
reject(new GitError({ stdout: data, stderr, exitCode, gitErrorCode: GitErrorCodes.IsInSubmodule }));
@@ -1193,6 +1299,23 @@ export class Repository implements Disposable {
});
}
+ // Parses output of `git check-ignore -v -z` and returns only those paths
+ // that are actually ignored by git.
+ // Matches to a negative pattern (starting with '!') are filtered out.
+ // See also https://git-scm.com/docs/git-check-ignore#_output.
+ private parseIgnoreCheck(raw: string): string[] {
+ const ignored = [];
+ const elements = raw.split('\0');
+ for (let i = 0; i < elements.length; i += 4) {
+ const pattern = elements[i + 2];
+ const path = elements[i + 3];
+ if (pattern && !pattern.startsWith('!')) {
+ ignored.push(path);
+ }
+ }
+ return ignored;
+ }
+
private async run(operation: Operation, runOperation: () => Promise = () => Promise.resolve(null)): Promise {
if (this.state !== RepositoryState.Idle) {
throw new Error('Repository not initialized');
@@ -1430,7 +1553,7 @@ export class Repository implements Disposable {
return result;
}
- private onFSChange(_uri: Uri): void {
+ private onFileChange(_uri: Uri): void {
const config = workspace.getConfiguration('git');
const autorefresh = config.get('autorefresh');
diff --git a/extensions/git/src/test/git.test.ts b/extensions/git/src/test/git.test.ts
index e28cf10d1..f0444ce57 100644
--- a/extensions/git/src/test/git.test.ts
+++ b/extensions/git/src/test/git.test.ts
@@ -172,6 +172,17 @@ suite('git', () => {
{ name: 'deps/spdlog4', path: 'deps/spdlog4', url: 'https://github.com/gabime/spdlog4.git' }
]);
});
+
+ test('whitespace #74844', () => {
+ const sample = `[submodule "deps/spdlog"]
+ path = deps/spdlog
+ url = https://github.com/gabime/spdlog.git
+`;
+
+ assert.deepEqual(parseGitmodules(sample), [
+ { name: 'deps/spdlog', path: 'deps/spdlog', url: 'https://github.com/gabime/spdlog.git' }
+ ]);
+ });
});
suite('parseGitCommit', () => {
diff --git a/extensions/git/src/util.ts b/extensions/git/src/util.ts
index 7bf81adcc..c4e938506 100644
--- a/extensions/git/src/util.ts
+++ b/extensions/git/src/util.ts
@@ -3,8 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { Event } from 'vscode';
-import { dirname, sep } from 'path';
+import { Event, EventEmitter, Uri } from 'vscode';
+import { dirname, sep, join } from 'path';
import { Readable } from 'stream';
import * as fs from 'fs';
import * as byline from 'byline';
@@ -343,4 +343,20 @@ export function pathEquals(a: string, b: string): boolean {
}
return a === b;
-}
\ No newline at end of file
+}
+
+export interface IFileWatcher extends IDisposable {
+ readonly event: Event;
+}
+
+export function watch(location: string): IFileWatcher {
+ const dotGitWatcher = fs.watch(location);
+ const onDotGitFileChangeEmitter = new EventEmitter();
+ dotGitWatcher.on('change', (_, e) => onDotGitFileChangeEmitter.fire(Uri.file(join(location, e as string))));
+ dotGitWatcher.on('error', err => console.error(err));
+
+ return new class implements IFileWatcher {
+ event = onDotGitFileChangeEmitter.event;
+ dispose() { dotGitWatcher.close(); }
+ };
+}
diff --git a/extensions/git/syntaxes/git-rebase.tmLanguage.json b/extensions/git/syntaxes/git-rebase.tmLanguage.json
index a2c116bd0..0238d8a6f 100644
--- a/extensions/git/syntaxes/git-rebase.tmLanguage.json
+++ b/extensions/git/syntaxes/git-rebase.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/textmate/git.tmbundle/commit/3f6ad2138200db14b57a090ecb2d2e733275ca3e",
+ "version": "https://github.com/textmate/git.tmbundle/commit/5870cf3f8abad3a6637bdf69250b5d2ded427dc4",
"name": "Git Rebase Message",
"scopeName": "text.git-rebase",
"patterns": [
@@ -47,6 +47,15 @@
},
"match": "^\\s*(exec|x)\\s+(.*)$",
"name": "meta.commit-command.git-rebase"
+ },
+ {
+ "captures": {
+ "1": {
+ "name": "support.function.git-rebase"
+ }
+ },
+ "match": "^\\s*(break|b)\\s*$",
+ "name": "meta.commit-command.git-rebase"
}
]
}
\ No newline at end of file
diff --git a/extensions/git/yarn.lock b/extensions/git/yarn.lock
index f13ef93c3..1f2ea8aed 100644
--- a/extensions/git/yarn.lock
+++ b/extensions/git/yarn.lock
@@ -26,10 +26,10 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.51.tgz#b31d716fb8d58eeb95c068a039b9b6292817d5fb"
integrity sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==
-"@types/node@^10.12.21":
- version "10.12.21"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e"
- integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ==
+"@types/node@^10.14.8":
+ version "10.14.8"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9"
+ integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw==
"@types/which@^1.0.28":
version "1.0.28"
@@ -325,6 +325,11 @@ vscode-nls@^4.0.0:
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002"
integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw==
+vscode-uri@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.0.0.tgz#2df704222f72b8a71ff266ba0830ed6c51ac1542"
+ integrity sha512-lWXWofDSYD8r/TIyu64MdwB4FaSirQ608PP/TzUyslyOeHGwQ0eTHUZeJrK1ILOmwUHaJtV693m2JoUYroUDpw==
+
which@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a"
diff --git a/extensions/go/language-configuration.json b/extensions/go/language-configuration.json
index cf20e02f3..a5e06a56b 100644
--- a/extensions/go/language-configuration.json
+++ b/extensions/go/language-configuration.json
@@ -27,5 +27,11 @@
"indentationRules": {
"increaseIndentPattern": "^.*(\\bcase\\b.*:|\\bdefault\\b:|(\\b(func|if|else|switch|select|for|struct)\\b.*)?{[^}\"'`]*|\\([^)\"'`]*)$",
"decreaseIndentPattern": "^\\s*(\\bcase\\b.*:|\\bdefault\\b:|}[)}]*[),]?|\\)[,]?)$"
+ },
+ "folding": {
+ "markers": {
+ "start": "^\\s*//\\s*#?region\\b",
+ "end": "^\\s*//\\s*#?endregion\\b"
+ }
}
}
\ No newline at end of file
diff --git a/extensions/go/package.json b/extensions/go/package.json
index 97aced567..a91d729ad 100644
--- a/extensions/go/package.json
+++ b/extensions/go/package.json
@@ -4,6 +4,7 @@
"description": "%description%",
"version": "1.0.0",
"publisher": "vscode",
+ "license": "MIT",
"engines": { "vscode": "*" },
"scripts": {
"update-grammar": "node ../../build/npm/update-grammar.js atom/language-go grammars/go.cson ./syntaxes/go.tmLanguage.json"
diff --git a/extensions/groovy/package.json b/extensions/groovy/package.json
index 9cb93f299..9bed57804 100644
--- a/extensions/groovy/package.json
+++ b/extensions/groovy/package.json
@@ -4,6 +4,7 @@
"description": "%description%",
"version": "1.0.0",
"publisher": "vscode",
+ "license": "MIT",
"engines": { "vscode": "*" },
"scripts": {
"update-grammar": "node ../../build/npm/update-grammar.js textmate/groovy.tmbundle Syntaxes/Groovy.tmLanguage ./syntaxes/groovy.tmLanguage.json"
diff --git a/extensions/grunt/package.json b/extensions/grunt/package.json
index 74fa45202..f65cc6e62 100644
--- a/extensions/grunt/package.json
+++ b/extensions/grunt/package.json
@@ -5,6 +5,7 @@
"displayName": "Grunt support for VS Code",
"version": "1.0.0",
"icon": "images/grunt.png",
+ "license": "MIT",
"engines": {
"vscode": "*"
},
@@ -19,7 +20,7 @@
"vscode-nls": "^4.0.0"
},
"devDependencies": {
- "@types/node": "^10.12.21"
+ "@types/node": "^10.14.8"
},
"main": "./out/main",
"activationEvents": [
diff --git a/extensions/grunt/src/main.ts b/extensions/grunt/src/main.ts
index 09a812ac0..deec1587c 100644
--- a/extensions/grunt/src/main.ts
+++ b/extensions/grunt/src/main.ts
@@ -54,14 +54,14 @@ function isTestTask(name: string): boolean {
let _channel: vscode.OutputChannel;
function getOutputChannel(): vscode.OutputChannel {
if (!_channel) {
- _channel = vscode.window.createOutputChannel('Gulp Auto Detection');
+ _channel = vscode.window.createOutputChannel('Grunt Auto Detection');
}
return _channel;
}
function showError() {
- vscode.window.showWarningMessage(localize('gulpTaskDetectError', 'Problem finding jake tasks. See the output for more information.'),
- localize('jakeShowOutput', 'Go to output')).then(() => {
+ vscode.window.showWarningMessage(localize('gruntTaskDetectError', 'Problem finding grunt tasks. See the output for more information.'),
+ localize('gruntShowOutput', 'Go to output')).then(() => {
getOutputChannel().show(true);
});
}
@@ -230,7 +230,7 @@ class TaskDetector {
this.detectors.clear();
}
- private updateWorkspaceFolders(added: vscode.WorkspaceFolder[], removed: vscode.WorkspaceFolder[]): void {
+ private updateWorkspaceFolders(added: readonly vscode.WorkspaceFolder[], removed: readonly vscode.WorkspaceFolder[]): void {
for (let remove of removed) {
let detector = this.detectors.get(remove.uri.toString());
if (detector) {
@@ -272,7 +272,7 @@ class TaskDetector {
private updateProvider(): void {
if (!this.taskProvider && this.detectors.size > 0) {
- this.taskProvider = vscode.workspace.registerTaskProvider('gulp', {
+ this.taskProvider = vscode.workspace.registerTaskProvider('grunt', {
provideTasks: () => {
return this.getTasks();
},
diff --git a/extensions/grunt/yarn.lock b/extensions/grunt/yarn.lock
index 1bcd757b8..e6247e292 100644
--- a/extensions/grunt/yarn.lock
+++ b/extensions/grunt/yarn.lock
@@ -2,10 +2,10 @@
# yarn lockfile v1
-"@types/node@^10.12.21":
- version "10.12.21"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e"
- integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ==
+"@types/node@^10.14.8":
+ version "10.14.8"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9"
+ integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw==
vscode-nls@^4.0.0:
version "4.0.0"
diff --git a/extensions/gulp/package.json b/extensions/gulp/package.json
index a9139780f..b602bf30c 100644
--- a/extensions/gulp/package.json
+++ b/extensions/gulp/package.json
@@ -5,6 +5,7 @@
"displayName": "%displayName%",
"version": "1.0.0",
"icon": "images/gulp.png",
+ "license": "MIT",
"engines": {
"vscode": "*"
},
@@ -19,7 +20,7 @@
"vscode-nls": "^4.0.0"
},
"devDependencies": {
- "@types/node": "^10.12.21"
+ "@types/node": "^10.14.8"
},
"main": "./out/main",
"activationEvents": [
diff --git a/extensions/gulp/src/main.ts b/extensions/gulp/src/main.ts
index 653e34bd2..ddc07a2f5 100644
--- a/extensions/gulp/src/main.ts
+++ b/extensions/gulp/src/main.ts
@@ -67,6 +67,24 @@ function showError() {
});
}
+async function findGulpCommand(rootPath: string): Promise {
+ let gulpCommand: string;
+ let platform = process.platform;
+ if (platform === 'win32' && await exists(path.join(rootPath, 'node_modules', '.bin', 'gulp.cmd'))) {
+ const globalGulp = path.join(process.env.APPDATA ? process.env.APPDATA : '', 'npm', 'gulp.cmd');
+ if (await exists(globalGulp)) {
+ gulpCommand = '"' + globalGulp + '"';
+ } else {
+ 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';
+ }
+ return gulpCommand;
+}
+
interface GulpTaskDefinition extends vscode.TaskDefinition {
task: string;
file?: string;
@@ -77,7 +95,9 @@ class FolderDetector {
private fileWatcher: vscode.FileSystemWatcher | undefined;
private promise: Thenable | undefined;
- constructor(private _workspaceFolder: vscode.WorkspaceFolder) {
+ constructor(
+ private _workspaceFolder: vscode.WorkspaceFolder,
+ private _gulpCommand: Promise) {
}
public get workspaceFolder(): vscode.WorkspaceFolder {
@@ -97,10 +117,28 @@ class FolderDetector {
}
public async getTasks(): Promise {
- if (!this.promise) {
- this.promise = this.computeTasks();
+ if (this.isEnabled()) {
+ if (!this.promise) {
+ this.promise = this.computeTasks();
+ }
+ return this.promise;
+ } else {
+ return [];
+ }
+ }
+
+ public async getTask(_task: vscode.Task): Promise {
+ const gulpTask = (_task.definition).task;
+ if (gulpTask) {
+ let kind: GulpTaskDefinition = {
+ type: 'gulp',
+ task: gulpTask
+ };
+ let options: vscode.ShellExecutionOptions = { cwd: this.workspaceFolder.uri.fsPath };
+ let task = new vscode.Task(kind, this.workspaceFolder, gulpTask, 'gulp', new vscode.ShellExecution(await this._gulpCommand, [gulpTask], options));
+ return task;
}
- return this.promise;
+ return undefined;
}
private async computeTasks(): Promise {
@@ -117,22 +155,7 @@ class FolderDetector {
}
}
- let gulpCommand: string;
- let platform = process.platform;
- if (platform === 'win32' && await exists(path.join(rootPath!, 'node_modules', '.bin', 'gulp.cmd'))) {
- const globalGulp = path.join(process.env.APPDATA ? process.env.APPDATA : '', 'npm', 'gulp.cmd');
- if (await exists(globalGulp)) {
- gulpCommand = '"' + globalGulp + '"';
- } else {
- 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`;
+ let commandLine = `${await this._gulpCommand} --tasks-simple --no-color`;
try {
let { stdout, stderr } = await exec(commandLine, { cwd: rootPath });
if (stderr && stderr.length > 0) {
@@ -151,7 +174,7 @@ class FolderDetector {
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));
+ let task = new vscode.Task(kind, this.workspaceFolder, line, 'gulp', new vscode.ShellExecution(await this._gulpCommand, [line], options));
result.push(task);
let lowerCaseLine = line.toLowerCase();
if (isBuildTask(lowerCaseLine)) {
@@ -209,7 +232,7 @@ class TaskDetector {
this.detectors.clear();
}
- private updateWorkspaceFolders(added: vscode.WorkspaceFolder[], removed: vscode.WorkspaceFolder[]): void {
+ private updateWorkspaceFolders(added: readonly vscode.WorkspaceFolder[], removed: readonly vscode.WorkspaceFolder[]): void {
for (let remove of removed) {
let detector = this.detectors.get(remove.uri.toString());
if (detector) {
@@ -218,9 +241,9 @@ class TaskDetector {
}
}
for (let add of added) {
- let detector = new FolderDetector(add);
+ let detector = new FolderDetector(add, findGulpCommand(add.uri.fsPath));
+ this.detectors.set(add.uri.toString(), detector);
if (detector.isEnabled()) {
- this.detectors.set(add.uri.toString(), detector);
detector.start();
}
}
@@ -229,18 +252,16 @@ class TaskDetector {
private updateConfiguration(): void {
for (let detector of this.detectors.values()) {
- if (!detector.isEnabled()) {
- detector.dispose();
- this.detectors.delete(detector.workspaceFolder.uri.toString());
- }
+ 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);
+ let detector = new FolderDetector(folder, findGulpCommand(folder.uri.fsPath));
+ this.detectors.set(folder.uri.toString(), detector);
if (detector.isEnabled()) {
- this.detectors.set(folder.uri.toString(), detector);
detector.start();
}
}
@@ -251,12 +272,13 @@ class TaskDetector {
private updateProvider(): void {
if (!this.taskProvider && this.detectors.size > 0) {
+ const thisCapture = this;
this.taskProvider = vscode.workspace.registerTaskProvider('gulp', {
- provideTasks: () => {
- return this.getTasks();
+ provideTasks(): Promise {
+ return thisCapture.getTasks();
},
- resolveTask(_task: vscode.Task): vscode.Task | undefined {
- return undefined;
+ resolveTask(_task: vscode.Task): Promise {
+ return thisCapture.getTask(_task);
}
});
}
@@ -291,6 +313,25 @@ class TaskDetector {
});
}
}
+
+ public async getTask(task: vscode.Task): Promise {
+ if (this.detectors.size === 0) {
+ return undefined;
+ } else if (this.detectors.size === 1) {
+ return this.detectors.values().next().value.getTask(task);
+ } else {
+ if ((task.scope === vscode.TaskScope.Workspace) || (task.scope === vscode.TaskScope.Global)) {
+ // Not supported, we don't have enough info to create the task.
+ return undefined;
+ } else if (task.scope) {
+ const detector = this.detectors.get(task.scope.uri.toString());
+ if (detector) {
+ return detector.getTask(task);
+ }
+ }
+ return undefined;
+ }
+ }
}
let detector: TaskDetector;
diff --git a/extensions/gulp/yarn.lock b/extensions/gulp/yarn.lock
index 1bcd757b8..e6247e292 100644
--- a/extensions/gulp/yarn.lock
+++ b/extensions/gulp/yarn.lock
@@ -2,10 +2,10 @@
# yarn lockfile v1
-"@types/node@^10.12.21":
- version "10.12.21"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e"
- integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ==
+"@types/node@^10.14.8":
+ version "10.14.8"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9"
+ integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw==
vscode-nls@^4.0.0:
version "4.0.0"
diff --git a/extensions/handlebars/cgmanifest.json b/extensions/handlebars/cgmanifest.json
index 4d30387e9..39f8efc67 100644
--- a/extensions/handlebars/cgmanifest.json
+++ b/extensions/handlebars/cgmanifest.json
@@ -6,7 +6,7 @@
"git": {
"name": "daaain/Handlebars",
"repositoryUrl": "https://github.com/daaain/Handlebars",
- "commitHash": "790f2b0222098a3a236bd9e91bb9a039eeca4d8e"
+ "commitHash": "85a153a6f759df4e8da7533e1b3651f007867c51"
}
},
"licenseDetail": [
@@ -29,7 +29,7 @@
"THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
],
"license": "MIT",
- "version": "1.7.1"
+ "version": "1.8.0"
}
],
"version": 1
diff --git a/extensions/handlebars/package.json b/extensions/handlebars/package.json
index fdb8d4fc7..37b63ee54 100644
--- a/extensions/handlebars/package.json
+++ b/extensions/handlebars/package.json
@@ -4,6 +4,7 @@
"description": "%description%",
"version": "1.0.0",
"publisher": "vscode",
+ "license": "MIT",
"engines": {
"vscode": "0.10.x"
},
diff --git a/extensions/handlebars/syntaxes/Handlebars.tmLanguage.json b/extensions/handlebars/syntaxes/Handlebars.tmLanguage.json
index 957f16ae0..be8b06fa0 100644
--- a/extensions/handlebars/syntaxes/Handlebars.tmLanguage.json
+++ b/extensions/handlebars/syntaxes/Handlebars.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/daaain/Handlebars/commit/790f2b0222098a3a236bd9e91bb9a039eeca4d8e",
+ "version": "https://github.com/daaain/Handlebars/commit/85a153a6f759df4e8da7533e1b3651f007867c51",
"name": "Handlebars",
"scopeName": "text.html.handlebars",
"patterns": [
@@ -653,7 +653,7 @@
]
},
"else_token": {
- "begin": "(\\{\\{)(~?else)(@?\\s(if)\\s([-a-zA-Z0-9_\\./]+))?",
+ "begin": "(\\{\\{)(~?else)(@?\\s(if)\\s([-a-zA-Z0-9_\\.\\(\\s\\)/]+))?",
"end": "(~?\\}\\}\\}*)",
"name": "meta.function.inline.else.handlebars",
"beginCaptures": {
diff --git a/extensions/hlsl/package.json b/extensions/hlsl/package.json
index f68ce50ee..b2f635b4a 100644
--- a/extensions/hlsl/package.json
+++ b/extensions/hlsl/package.json
@@ -4,6 +4,7 @@
"description": "%description%",
"version": "1.0.0",
"publisher": "vscode",
+ "license": "MIT",
"engines": { "vscode": "*" },
"scripts": {
"update-grammar": "node ../../build/npm/update-grammar.js tgjones/shaders-tmLanguage grammars/hlsl.json ./syntaxes/hlsl.tmLanguage.json"
diff --git a/extensions/html-language-features/client/src/htmlMain.ts b/extensions/html-language-features/client/src/htmlMain.ts
index 6fe901907..5f98f732b 100644
--- a/extensions/html-language-features/client/src/htmlMain.ts
+++ b/extensions/html-language-features/client/src/htmlMain.ts
@@ -8,7 +8,7 @@ import * as fs from 'fs';
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
-import { languages, ExtensionContext, IndentAction, Position, TextDocument, Range, CompletionItem, CompletionItemKind, SnippetString, workspace, SelectionRange } from 'vscode';
+import { languages, ExtensionContext, IndentAction, Position, TextDocument, Range, CompletionItem, CompletionItemKind, SnippetString, workspace } from 'vscode';
import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, RequestType, TextDocumentPositionParams } from 'vscode-languageclient';
import { EMPTY_ELEMENTS } from './htmlEmptyTagsShared';
import { activateTagClosing } from './tagClosing';
@@ -87,26 +87,6 @@ export function activate(context: ExtensionContext) {
}
});
toDispose.push(disposable);
-
- documentSelector.forEach(selector => {
- context.subscriptions.push(languages.registerSelectionRangeProvider(selector, {
- async provideSelectionRanges(document: TextDocument, positions: Position[]): Promise {
- const textDocument = client.code2ProtocolConverter.asTextDocumentIdentifier(document);
- const rawResult = await client.sendRequest('$/textDocument/selectionRanges', { textDocument, positions: positions.map(client.code2ProtocolConverter.asPosition) });
- if (Array.isArray(rawResult)) {
- return rawResult.map(rawSelectionRanges => {
- return rawSelectionRanges.reduceRight((parent: SelectionRange | undefined, selectionRange: SelectionRange) => {
- return {
- range: client.protocol2CodeConverter.asRange(selectionRange.range),
- parent
- };
- }, undefined)!;
- });
- }
- return [];
- }
- }));
- });
});
languages.setLanguageConfiguration('html', {
diff --git a/extensions/html-language-features/client/src/tagClosing.ts b/extensions/html-language-features/client/src/tagClosing.ts
index 35511e63f..298edcdaa 100644
--- a/extensions/html-language-features/client/src/tagClosing.ts
+++ b/extensions/html-language-features/client/src/tagClosing.ts
@@ -32,7 +32,7 @@ export function activateTagClosing(tagProvider: (document: TextDocument, positio
isEnabled = true;
}
- function onDidChangeTextDocument(document: TextDocument, changes: TextDocumentContentChangeEvent[]) {
+ function onDidChangeTextDocument(document: TextDocument, changes: readonly TextDocumentContentChangeEvent[]) {
if (!isEnabled) {
return;
}
diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json
index e44dc9f71..3985c07b9 100644
--- a/extensions/html-language-features/package.json
+++ b/extensions/html-language-features/package.json
@@ -5,6 +5,7 @@
"description": "%description%",
"version": "1.0.0",
"publisher": "vscode",
+ "license": "MIT",
"aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217",
"engines": {
"vscode": "0.10.x"
@@ -172,14 +173,20 @@
"description": "%html.trace.server.desc%"
}
}
- }
+ },
+ "jsonValidation": [
+ {
+ "fileMatch": "*.html-data.json",
+ "url": "https://raw.githubusercontent.com/Microsoft/vscode-html-languageservice/master/docs/customData.schema.json"
+ }
+ ]
},
"dependencies": {
"vscode-extension-telemetry": "0.1.1",
- "vscode-languageclient": "^5.2.1",
- "vscode-nls": "^4.0.0"
+ "vscode-languageclient": "^5.3.0-next.6",
+ "vscode-nls": "^4.1.1"
},
"devDependencies": {
- "@types/node": "^10.12.21"
+ "@types/node": "^10.14.8"
}
}
diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json
index 74aa5e694..95a97f111 100644
--- a/extensions/html-language-features/server/package.json
+++ b/extensions/html-language-features/server/package.json
@@ -9,16 +9,16 @@
},
"main": "./out/htmlServerMain",
"dependencies": {
- "vscode-css-languageservice": "^4.0.2-next.3",
- "vscode-html-languageservice": "^3.0.0-next.6",
- "vscode-languageserver": "^5.3.0-next.2",
- "vscode-languageserver-types": "^3.14.0",
- "vscode-nls": "^4.0.0",
- "vscode-uri": "^1.0.6"
+ "vscode-css-languageservice": "^4.0.3-next.1",
+ "vscode-html-languageservice": "^3.0.4-next.0",
+ "vscode-languageserver": "^5.3.0-next.8",
+ "vscode-languageserver-types": "3.15.0-next.2",
+ "vscode-nls": "^4.1.1",
+ "vscode-uri": "^2.0.3"
},
"devDependencies": {
"@types/mocha": "2.2.33",
- "@types/node": "^10.12.21",
+ "@types/node": "^10.14.8",
"glob": "^7.1.2",
"mocha": "^5.2.0",
"mocha-junit-reporter": "^1.17.0",
diff --git a/extensions/html-language-features/server/src/htmlServerMain.ts b/extensions/html-language-features/server/src/htmlServerMain.ts
index 7974284bd..01b43d9a9 100644
--- a/extensions/html-language-features/server/src/htmlServerMain.ts
+++ b/extensions/html-language-features/server/src/htmlServerMain.ts
@@ -15,7 +15,7 @@ import { getLanguageModes, LanguageModes, Settings } from './modes/languageModes
import { format } from './modes/formatting';
import { pushAll } from './utils/arrays';
import { getDocumentContext } from './utils/documentContext';
-import uri from 'vscode-uri';
+import { URI } from 'vscode-uri';
import { formatError, runSafe, runSafeAsync } from './utils/runner';
import { getFoldingRanges } from './modes/htmlFolding';
@@ -38,8 +38,7 @@ process.on('uncaughtException', (e: any) => {
console.error(formatError(`Unhandled exception`, e));
});
-// Create a simple text document manager. The text document manager
-// supports full document sync only
+// Create a text document manager.
const documents: TextDocuments = new TextDocuments();
// Make the text document manager listen on the connection
// for open, change and close text document events
@@ -85,7 +84,7 @@ connection.onInitialize((params: InitializeParams): InitializeResult => {
if (!Array.isArray(workspaceFolders)) {
workspaceFolders = [];
if (params.rootPath) {
- workspaceFolders.push({ name: '', uri: uri.file(params.rootPath).toString() });
+ workspaceFolders.push({ name: '', uri: URI.file(params.rootPath).toString() });
}
}
@@ -97,7 +96,7 @@ connection.onInitialize((params: InitializeParams): InitializeResult => {
get folders() { return workspaceFolders; }
};
- languageModes = getLanguageModes(initializationOptions ? initializationOptions.embeddedLanguages : { css: true, javascript: true }, workspace, providers);
+ languageModes = getLanguageModes(initializationOptions ? initializationOptions.embeddedLanguages : { css: true, javascript: true }, workspace, params.capabilities, providers);
documents.onDidClose(e => {
languageModes.onDocumentRemoved(e.document);
@@ -136,7 +135,8 @@ connection.onInitialize((params: InitializeParams): InitializeResult => {
signatureHelpProvider: { triggerCharacters: ['('] },
referencesProvider: true,
colorProvider: {},
- foldingRangeProvider: true
+ foldingRangeProvider: true,
+ selectionRangeProvider: true
};
return { capabilities };
});
@@ -455,7 +455,7 @@ connection.onFoldingRanges((params, token) => {
}, null, `Error while computing folding regions for ${params.textDocument.uri}`, token);
});
-connection.onRequest('$/textDocument/selectionRanges', async (params, token) => {
+connection.onSelectionRanges((params, token) => {
return runSafe(() => {
const document = documents.get(params.textDocument.uri);
const positions: Position[] = params.positions;
@@ -466,8 +466,8 @@ connection.onRequest('$/textDocument/selectionRanges', async (params, token) =>
return htmlMode.getSelectionRanges(document, positions);
}
}
- return Promise.resolve(null);
- }, null, `Error while computing selection ranges for ${params.textDocument.uri}`, token);
+ return [];
+ }, [], `Error while computing selection ranges for ${params.textDocument.uri}`, token);
});
diff --git a/extensions/html-language-features/server/src/modes/cssMode.ts b/extensions/html-language-features/server/src/modes/cssMode.ts
index 168c7ceff..6e60c32d8 100644
--- a/extensions/html-language-features/server/src/modes/cssMode.ts
+++ b/extensions/html-language-features/server/src/modes/cssMode.ts
@@ -5,13 +5,12 @@
import { LanguageModelCache, getLanguageModelCache } from '../languageModelCache';
import { TextDocument, Position, Range, CompletionList } from 'vscode-languageserver-types';
-import { getCSSLanguageService, Stylesheet, FoldingRange } from 'vscode-css-languageservice';
+import { Stylesheet, FoldingRange, LanguageService as CSSLanguageService } from 'vscode-css-languageservice';
import { LanguageMode, Workspace } from './languageModes';
import { HTMLDocumentRegions, CSS_STYLE_RULE } from './embeddedSupport';
import { Color } from 'vscode-languageserver';
-export function getCSSMode(documentRegions: LanguageModelCache, workspace: Workspace): LanguageMode {
- let cssLanguageService = getCSSLanguageService();
+export function getCSSMode(cssLanguageService: CSSLanguageService, documentRegions: LanguageModelCache, workspace: Workspace): LanguageMode {
let embeddedCSSDocuments = getLanguageModelCache(10, 60, document => documentRegions.get(document).getEmbeddedDocument('css'));
let cssStylesheets = getLanguageModelCache(10, 60, document => cssLanguageService.parseStylesheet(document));
diff --git a/extensions/html-language-features/server/src/modes/htmlMode.ts b/extensions/html-language-features/server/src/modes/htmlMode.ts
index 09efb996f..57249a65b 100644
--- a/extensions/html-language-features/server/src/modes/htmlMode.ts
+++ b/extensions/html-language-features/server/src/modes/htmlMode.ts
@@ -15,7 +15,7 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace:
getId() {
return 'html';
},
- getSelectionRanges(document: TextDocument, positions: Position[]): SelectionRange[][] {
+ getSelectionRanges(document: TextDocument, positions: Position[]): SelectionRange[] {
return htmlLanguageService.getSelectionRanges(document, positions);
},
doComplete(document: TextDocument, position: Position, settings = workspace.settings) {
diff --git a/extensions/html-language-features/server/src/modes/languageModes.ts b/extensions/html-language-features/server/src/modes/languageModes.ts
index 94c0b04a2..d07e0bd80 100644
--- a/extensions/html-language-features/server/src/modes/languageModes.ts
+++ b/extensions/html-language-features/server/src/modes/languageModes.ts
@@ -3,18 +3,15 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { getLanguageService as getHTMLLanguageService, DocumentContext, IHTMLDataProvider, SelectionRange } from 'vscode-html-languageservice';
-import {
- CompletionItem, Location, SignatureHelp, Definition, TextEdit, TextDocument, Diagnostic, DocumentLink, Range,
- Hover, DocumentHighlight, CompletionList, Position, FormattingOptions, SymbolInformation, FoldingRange
-} from 'vscode-languageserver-types';
-import { ColorInformation, ColorPresentation, Color, WorkspaceFolder } from 'vscode-languageserver';
-
+import { getCSSLanguageService } from 'vscode-css-languageservice';
+import { ClientCapabilities, DocumentContext, getLanguageService as getHTMLLanguageService, IHTMLDataProvider, SelectionRange } from 'vscode-html-languageservice';
+import { Color, ColorInformation, ColorPresentation, WorkspaceFolder } from 'vscode-languageserver';
+import { CompletionItem, CompletionList, Definition, Diagnostic, DocumentHighlight, DocumentLink, FoldingRange, FormattingOptions, Hover, Location, Position, Range, SignatureHelp, SymbolInformation, TextDocument, TextEdit } from 'vscode-languageserver-types';
import { getLanguageModelCache, LanguageModelCache } from '../languageModelCache';
-import { getDocumentRegions, HTMLDocumentRegions } from './embeddedSupport';
import { getCSSMode } from './cssMode';
-import { getJavaScriptMode } from './javascriptMode';
+import { getDocumentRegions, HTMLDocumentRegions } from './embeddedSupport';
import { getHTMLMode } from './htmlMode';
+import { getJavaScriptMode } from './javascriptMode';
export { ColorInformation, ColorPresentation, Color };
@@ -31,7 +28,7 @@ export interface Workspace {
export interface LanguageMode {
getId(): string;
- getSelectionRanges?: (document: TextDocument, positions: Position[]) => SelectionRange[][];
+ getSelectionRanges?: (document: TextDocument, positions: Position[]) => SelectionRange[];
doValidation?: (document: TextDocument, settings?: Settings) => Diagnostic[];
doComplete?: (document: TextDocument, position: Position, settings?: Settings) => CompletionList;
doResolve?: (document: TextDocument, item: CompletionItem) => CompletionItem;
@@ -66,8 +63,9 @@ export interface LanguageModeRange extends Range {
attributeValue?: boolean;
}
-export function getLanguageModes(supportedLanguages: { [languageId: string]: boolean; }, workspace: Workspace, customDataProviders?: IHTMLDataProvider[]): LanguageModes {
- const htmlLanguageService = getHTMLLanguageService({ customDataProviders });
+export function getLanguageModes(supportedLanguages: { [languageId: string]: boolean; }, workspace: Workspace, clientCapabilities: ClientCapabilities, customDataProviders?: IHTMLDataProvider[]): LanguageModes {
+ const htmlLanguageService = getHTMLLanguageService({ customDataProviders, clientCapabilities });
+ const cssLanguageService = getCSSLanguageService({ clientCapabilities });
let documentRegions = getLanguageModelCache(10, 60, document => getDocumentRegions(htmlLanguageService, document));
@@ -77,7 +75,7 @@ export function getLanguageModes(supportedLanguages: { [languageId: string]: boo
let modes = Object.create(null);
modes['html'] = getHTMLMode(htmlLanguageService, workspace);
if (supportedLanguages['css']) {
- modes['css'] = getCSSMode(documentRegions, workspace);
+ modes['css'] = getCSSMode(cssLanguageService, documentRegions, workspace);
}
if (supportedLanguages['javascript']) {
modes['javascript'] = getJavaScriptMode(documentRegions);
diff --git a/extensions/html-language-features/server/src/modes/pathCompletion.ts b/extensions/html-language-features/server/src/modes/pathCompletion.ts
index cb3a20f8e..b7d7c1e72 100644
--- a/extensions/html-language-features/server/src/modes/pathCompletion.ts
+++ b/extensions/html-language-features/server/src/modes/pathCompletion.ts
@@ -7,7 +7,7 @@ import { TextDocument, CompletionItemKind, CompletionItem, TextEdit, Range, Posi
import { WorkspaceFolder } from 'vscode-languageserver';
import * as path from 'path';
import * as fs from 'fs';
-import URI from 'vscode-uri';
+import { URI } from 'vscode-uri';
import { ICompletionParticipant } from 'vscode-html-languageservice';
import { startsWith } from '../utils/strings';
import { contains } from '../utils/arrays';
@@ -108,11 +108,12 @@ function pathToSuggestion(p: string, valueBeforeCursor: string, fullValue: strin
// Find the last slash before cursor, and calculate the start of replace range from there
const valueAfterLastSlash = fullValue.slice(lastIndexOfSlash + 1);
const startPos = shiftPosition(range.end, -1 - valueAfterLastSlash.length);
- // If whitespace exists, replace until it
- const whiteSpaceIndex = valueAfterLastSlash.indexOf(' ');
+
+ // If whitespace exists, replace until there is no more
+ const whitespaceIndex = valueAfterLastSlash.indexOf(' ');
let endPos;
- if (whiteSpaceIndex !== -1) {
- endPos = shiftPosition(startPos, whiteSpaceIndex);
+ if (whitespaceIndex !== -1) {
+ endPos = shiftPosition(startPos, whitespaceIndex);
} else {
endPos = shiftPosition(range.end, -1);
}
@@ -160,6 +161,7 @@ function shiftRange(range: Range, startOffset: number, endOffset: number): Range
const PATH_TAG_AND_ATTR: { [tag: string]: string | string[] } = {
// HTML 4
a: 'href',
+ area: 'href',
body: 'background',
del: 'cite',
form: 'action',
@@ -176,7 +178,7 @@ const PATH_TAG_AND_ATTR: { [tag: string]: string | string[] } = {
command: 'icon',
embed: 'src',
html: 'manifest',
- input: 'formaction',
+ input: ['src', 'formaction'],
source: 'src',
track: 'src',
video: ['src', 'poster']
diff --git a/extensions/html-language-features/server/src/test/completions.test.ts b/extensions/html-language-features/server/src/test/completions.test.ts
index c28daf9f0..aaba72add 100644
--- a/extensions/html-language-features/server/src/test/completions.test.ts
+++ b/extensions/html-language-features/server/src/test/completions.test.ts
@@ -5,10 +5,11 @@
import 'mocha';
import * as assert from 'assert';
import * as path from 'path';
-import Uri from 'vscode-uri';
+import { URI } from 'vscode-uri';
import { TextDocument, CompletionList, CompletionItemKind } from 'vscode-languageserver-types';
import { getLanguageModes } from '../modes/languageModes';
import { WorkspaceFolder } from 'vscode-languageserver';
+import { ClientCapabilities } from 'vscode-html-languageservice';
export interface ItemDescription {
label: string;
@@ -58,8 +59,8 @@ export function testCompletionFor(value: string, expected: { count?: number, ite
let document = TextDocument.create(uri, 'html', 0, value);
let position = document.positionAt(offset);
- var languageModes = getLanguageModes({ css: true, javascript: true }, workspace);
- var mode = languageModes.getModeAtPosition(document, position)!;
+ const languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST);
+ const mode = languageModes.getModeAtPosition(document, position)!;
let list = mode.doComplete!(document, position);
@@ -95,9 +96,9 @@ suite('HTML Path Completion', () => {
};
const fixtureRoot = path.resolve(__dirname, '../../src/test/pathCompletionFixtures');
- const fixtureWorkspace = { name: 'fixture', uri: Uri.file(fixtureRoot).toString() };
- const indexHtmlUri = Uri.file(path.resolve(fixtureRoot, 'index.html')).toString();
- const aboutHtmlUri = Uri.file(path.resolve(fixtureRoot, 'about/about.html')).toString();
+ const fixtureWorkspace = { name: 'fixture', uri: URI.file(fixtureRoot).toString() };
+ const indexHtmlUri = URI.file(path.resolve(fixtureRoot, 'index.html')).toString();
+ const aboutHtmlUri = URI.file(path.resolve(fixtureRoot, 'about/about.html')).toString();
test('Basics - Correct label/kind/result/command', () => {
testCompletionFor('
- ${this.getStyles(sourceUri, nonce, config, state)}
-
+ data-settings="${escapeAttribute(JSON.stringify(initialData))}"
+ data-strings="${escapeAttribute(JSON.stringify(previewStrings))}"
+ data-state="${escapeAttribute(JSON.stringify(state || {}))}">
+
+ ${this.getStyles(resourceProvider, sourceUri, config, state)}
+
${body}
- ${this.getScripts(nonce)}
+ ${this.getScripts(resourceProvider, nonce)}
]