vscode-html-languageservice
Advanced tools
Comparing version 2.0.5 to 2.0.6
@@ -0,1 +1,7 @@ | ||
2.0.6 / 2017-08-25 | ||
================== | ||
* Added new API `htmlLanguageService.doTagComplete`. Called behind a `>` or `\`, `doTagComplete` will compute a closing tag. The result is a snippet string that can be inserted behind the position, or null, if no tag completion should be performed. | ||
* New settings `CompletionConfiguration.hideAutoCompleteProposals`. If set, `doComplete` will not propose a closing tag proposals on `>`. | ||
* These APIs are experimental and might be improved. | ||
2.0.3 / 2017-03-21 | ||
@@ -2,0 +8,0 @@ ================== |
@@ -19,2 +19,3 @@ import { TextDocument, Position, CompletionItem, CompletionList, Hover, Range, SymbolInformation, Diagnostic, TextEdit, DocumentHighlight, FormattingOptions, MarkedString, DocumentLink } from 'vscode-languageserver-types'; | ||
[provider: string]: boolean; | ||
hideAutoCompleteProposals?: boolean; | ||
} | ||
@@ -96,3 +97,4 @@ export interface Node { | ||
findDocumentSymbols(document: TextDocument, htmlDocument: HTMLDocument): SymbolInformation[]; | ||
doTagComplete(document: TextDocument, position: Position, htmlDocument: HTMLDocument): string; | ||
} | ||
export declare function getLanguageService(): LanguageService; |
@@ -84,3 +84,4 @@ (function (factory) { | ||
findDocumentLinks: htmlLinks_1.findDocumentLinks, | ||
findDocumentSymbols: htmlSymbolsProvider_1.findDocumentSymbols | ||
findDocumentSymbols: htmlSymbolsProvider_1.findDocumentSymbols, | ||
doTagComplete: htmlCompletion_1.doTagComplete | ||
}; | ||
@@ -87,0 +88,0 @@ } |
@@ -7,3 +7,3 @@ (function (factory) { | ||
else if (typeof define === "function" && define.amd) { | ||
define(["require", "exports", "vscode-languageserver-types", "../parser/htmlScanner", "./tagProviders"], factory); | ||
define(["require", "exports", "vscode-languageserver-types", "../parser/htmlScanner", "../parser/htmlTags", "./tagProviders"], factory); | ||
} | ||
@@ -18,2 +18,3 @@ })(function (require, exports) { | ||
var htmlScanner_1 = require("../parser/htmlScanner"); | ||
var htmlTags_1 = require("../parser/htmlTags"); | ||
var tagProviders_1 = require("./tagProviders"); | ||
@@ -93,4 +94,4 @@ function doComplete(document, position, htmlDocument, settings) { | ||
var insertText = startIndent + '</' + tag + closeTag; | ||
item.textEdit = vscode_languageserver_types_1.TextEdit.replace(getReplaceRange(afterOpenBracket - 1 - endIndent.length), insertText), | ||
item.filterText = endIndent + '</' + tag + closeTag; | ||
item.textEdit = vscode_languageserver_types_1.TextEdit.replace(getReplaceRange(afterOpenBracket - 1 - endIndent.length), insertText); | ||
item.filterText = endIndent + '</' + tag + closeTag; | ||
} | ||
@@ -119,2 +120,18 @@ result.items.push(item); | ||
} | ||
function collectAutoCloseTagSuggestion(tagCloseEnd, tag) { | ||
if (settings && settings.hideAutoCompleteProposals) { | ||
return result; | ||
} | ||
if (!htmlTags_1.isEmptyElement(tag)) { | ||
var pos = document.positionAt(tagCloseEnd); | ||
result.items.push({ | ||
label: '</' + tag + '>', | ||
kind: vscode_languageserver_types_1.CompletionItemKind.Property, | ||
filterText: '</' + tag + '>', | ||
textEdit: vscode_languageserver_types_1.TextEdit.insert(pos, '$0</' + tag + '>'), | ||
insertTextFormat: vscode_languageserver_types_1.InsertTextFormat.Snippet | ||
}); | ||
} | ||
return result; | ||
} | ||
function collectTagSuggestions(tagStart, tagEnd) { | ||
@@ -262,2 +279,9 @@ collectOpenTagSuggestions(tagStart, tagEnd); | ||
break; | ||
case htmlScanner_1.TokenType.StartTagClose: | ||
if (offset <= scanner.getTokenEnd()) { | ||
if (currentTag) { | ||
return collectAutoCloseTagSuggestion(scanner.getTokenEnd(), currentTag); | ||
} | ||
} | ||
break; | ||
default: | ||
@@ -274,2 +298,40 @@ if (offset <= scanner.getTokenEnd()) { | ||
exports.doComplete = doComplete; | ||
function doTagComplete(document, position, htmlDocument) { | ||
var offset = document.offsetAt(position); | ||
if (offset <= 0) { | ||
return; | ||
} | ||
var char = document.getText().charAt(offset - 1); | ||
if (char === '>') { | ||
var node = htmlDocument.findNodeBefore(offset); | ||
if (node && node.tag && !htmlTags_1.isEmptyElement(node.tag) && node.start < offset && (!node.endTagStart || node.endTagStart > offset)) { | ||
var scanner = htmlScanner_1.createScanner(document.getText(), node.start); | ||
var token = scanner.scan(); | ||
while (token !== htmlScanner_1.TokenType.EOS && scanner.getTokenEnd() <= offset) { | ||
if (token === htmlScanner_1.TokenType.StartTagClose && scanner.getTokenEnd() === offset) { | ||
return "$0</" + node.tag + ">"; | ||
} | ||
token = scanner.scan(); | ||
} | ||
} | ||
} | ||
else if (char === '/') { | ||
var node = htmlDocument.findNodeBefore(offset); | ||
while (node && node.closed) { | ||
node = node.parent; | ||
} | ||
if (node && node.tag) { | ||
var scanner = htmlScanner_1.createScanner(document.getText(), node.start); | ||
var token = scanner.scan(); | ||
while (token !== htmlScanner_1.TokenType.EOS && scanner.getTokenEnd() <= offset) { | ||
if (token === htmlScanner_1.TokenType.EndTagOpen && scanner.getTokenEnd() === offset) { | ||
return node.tag + ">"; | ||
} | ||
token = scanner.scan(); | ||
} | ||
} | ||
} | ||
return null; | ||
} | ||
exports.doTagComplete = doTagComplete; | ||
function isWhiteSpace(s) { | ||
@@ -276,0 +338,0 @@ return /^\s*$/.test(s); |
@@ -62,2 +62,12 @@ (function (factory) { | ||
}; | ||
var testTagCompletion = function (value, expected) { | ||
var offset = value.indexOf('|'); | ||
value = value.substr(0, offset) + value.substr(offset + 1); | ||
var ls = htmlLanguageService.getLanguageService(); | ||
var document = vscode_languageserver_types_1.TextDocument.create('test://test/test.html', 'html', 0, value); | ||
var position = document.positionAt(offset); | ||
var htmlDoc = ls.parseHTMLDocument(document); | ||
var actual = ls.doTagComplete(document, position, htmlDoc); | ||
assert.equal(actual, expected); | ||
}; | ||
function run(tests, testDone) { | ||
@@ -321,3 +331,18 @@ Promise.all(tests).then(function () { | ||
] | ||
}) | ||
}), | ||
testCompletionFor('<div><h1><br><span></span><img></| </h1></div>', { | ||
items: [ | ||
{ label: '/h1', resultText: '<div><h1><br><span></span><img></h1> </h1></div>' }, | ||
] | ||
}), | ||
testCompletionFor('<div>|', { | ||
items: [ | ||
{ label: '</div>', resultText: '<div>$0</div>' } | ||
] | ||
}), | ||
testCompletionFor('<div>|', { | ||
items: [ | ||
{ notAvailable: true, label: '</div>' } | ||
] | ||
}, { hideAutoCompleteProposals: true }) | ||
], testDone); | ||
@@ -347,3 +372,8 @@ }); | ||
] | ||
}) | ||
}), | ||
testCompletionFor('<dIv>|', { | ||
items: [ | ||
{ label: '</dIv>', resultText: '<dIv>$0</dIv>' } | ||
] | ||
}), | ||
], testDone); | ||
@@ -472,4 +502,13 @@ }); | ||
}); | ||
test('doTagComplete', function () { | ||
testTagCompletion('<div>|', '$0</div>'); | ||
testTagCompletion('<div>|</div>', null); | ||
testTagCompletion('<div class="">|', "$0</div>"); | ||
testTagCompletion('<img>|', null); | ||
testTagCompletion('<div><br></|', "div>"); | ||
testTagCompletion('<div><br><span></span></|', "div>"); | ||
testTagCompletion('<div><h1><br><span></span><img></| </h1></div>', "h1>"); | ||
}); | ||
}); | ||
}); | ||
//# sourceMappingURL=completion.test.js.map |
{ | ||
"name": "vscode-html-languageservice", | ||
"version": "2.0.5", | ||
"version": "2.0.6", | ||
"description": "Language service for HTML", | ||
@@ -5,0 +5,0 @@ "main": "./lib/htmlLanguageService.js", |
335835
5797