diff --git a/extensions/html/package.json b/extensions/html/package.json index 76aac317588..258d884d495 100644 --- a/extensions/html/package.json +++ b/extensions/html/package.json @@ -24,6 +24,7 @@ "mimetypes": ["text/html", "text/x-jshtm", "text/template", "text/ng-template", "application/xhtml+xml"] }], "grammars": [{ + "language": "html", "scopeName": "text.html.basic", "path": "./syntaxes/html.json" }], diff --git a/extensions/html/server/src/htmlServerMain.ts b/extensions/html/server/src/htmlServerMain.ts index a5e54f959f7..7335c5fa820 100644 --- a/extensions/html/server/src/htmlServerMain.ts +++ b/extensions/html/server/src/htmlServerMain.ts @@ -35,9 +35,8 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { capabilities: { // Tell the client that the server works in FULL text document sync mode textDocumentSync: documents.syncKind, - completionProvider: { resolveProvider: false, triggerCharacters: ['"', ':'] }, - // hoverProvider: true, - // documentSymbolProvider: true, + completionProvider: { resolveProvider: false, triggerCharacters: ['.', ':', '<', '"', '=', '/'] }, + documentHighlightProvider: true, documentRangeFormattingProvider: true, documentFormattingProvider: true } @@ -122,21 +121,11 @@ connection.onCompletion(textDocumentPosition => { return languageService.doComplete(document, textDocumentPosition.position, htmlDocument); }); -// connection.onCompletionResolve(completionItem => { -// return languageService.doResolve(completionItem); -// }); - -// connection.onHover(textDocumentPositionParams => { -// let document = documents.get(textDocumentPositionParams.textDocument.uri); -// let htmlDocument = getHTMLDocument(document); -// return languageService.doHover(document, textDocumentPositionParams.position, htmlDocument); -// }); - -// connection.onDocumentSymbol(documentSymbolParams => { -// let document = documents.get(documentSymbolParams.textDocument.uri); -// let htmlDocument = getHTMLDocument(document); -// return languageService.findDocumentSymbols(document, htmlDocument); -// }); +connection.onDocumentHighlight(documentHighlightParams => { + let document = documents.get(documentHighlightParams.textDocument.uri); + let htmlDocument = getHTMLDocument(document); + return languageService.findDocumentHighlights(document, documentHighlightParams.position, htmlDocument); +}); function merge(src: any, dst: any) : any { for (var key in src) { diff --git a/extensions/html/server/src/service/parser/htmlParser.ts b/extensions/html/server/src/service/parser/htmlParser.ts index 8004343e750..fc3b004d980 100644 --- a/extensions/html/server/src/service/parser/htmlParser.ts +++ b/extensions/html/server/src/service/parser/htmlParser.ts @@ -41,7 +41,7 @@ export class Node { if (idx >= 0) { let child = this.children[idx]; if (offset > child.start && offset <= child.end) { - return child.findNodeBefore(offset); + return child.findNodeAt(offset); } } return this; diff --git a/extensions/html/server/src/service/services/htmlHighlighting.ts b/extensions/html/server/src/service/services/htmlHighlighting.ts index 0bb0f148382..ddb517853e9 100644 --- a/extensions/html/server/src/service/services/htmlHighlighting.ts +++ b/extensions/html/server/src/service/services/htmlHighlighting.ts @@ -11,15 +11,22 @@ import {TextDocument, Range, Position, DocumentHighlightKind, DocumentHighlight} export function findDocumentHighlights(document: TextDocument, position: Position, htmlDocument: HTMLDocument): DocumentHighlight[] { let offset = document.offsetAt(position); let node = htmlDocument.findNodeAt(offset); - if (!node.tag || typeof node.endTagStart !== 'number') { + if (!node.tag) { return []; } + let result = []; let startTagRange = getTagNameRange(TokenType.StartTag, document, node.start); - let endTagRange = getTagNameRange(TokenType.EndTag, document, node.endTagStart); - if (startTagRange && endTagRange && (covers(startTagRange, position) || covers(endTagRange, position))) { - return [ { kind: DocumentHighlightKind.Read, range: startTagRange }, { kind: DocumentHighlightKind.Read, range: endTagRange }]; + let endTagRange = typeof node.endTagStart === 'number' && getTagNameRange(TokenType.EndTag, document, node.endTagStart); + if (startTagRange && covers(startTagRange, position) || endTagRange && covers(endTagRange, position)) { + if (startTagRange) { + result.push({ kind: DocumentHighlightKind.Read, range: startTagRange }); + } + if (endTagRange) { + result.push({ kind: DocumentHighlightKind.Read, range: endTagRange }); + } } - return []; + console.log('foo' + result.length); + return result; } function isBeforeOrEqual(pos1: Position, pos2: Position) { @@ -30,10 +37,10 @@ function covers(range: Range, position: Position) { return isBeforeOrEqual(range.start, position) && isBeforeOrEqual(position, range.end); } -function getTagNameRange(tokenType: TokenType, document: TextDocument, startOffset: number ) : Range { +function getTagNameRange(tokenType: TokenType, document: TextDocument, startOffset: number) : Range { let scanner = createScanner(document.getText(), startOffset); let token = scanner.scan(); - while (token !== TokenType.EOS && token !== TokenType.StartTag) { + while (token !== TokenType.EOS && token !== tokenType) { token = scanner.scan(); } if (token !== TokenType.EOS) { diff --git a/extensions/html/server/src/service/test/highlighting.test.ts b/extensions/html/server/src/service/test/highlighting.test.ts index 959abd92685..0ba7d1b398f 100644 --- a/extensions/html/server/src/service/test/highlighting.test.ts +++ b/extensions/html/server/src/service/test/highlighting.test.ts @@ -8,7 +8,7 @@ import * as assert from 'assert'; import * as htmlLanguageService from '../htmlLanguageService'; import {CompletionList, TextDocument, TextEdit, Position, CompletionItemKind} from 'vscode-languageserver-types'; -export function assertHighlights(value: string, expectedMatches: number[], elementName: string): Thenable { +export function assertHighlights(value: string, expectedMatches: number[], elementName: string): void { let offset = value.indexOf('|'); value = value.substr(0, offset) + value.substr(offset + 1); @@ -33,11 +33,41 @@ export function assertHighlights(value: string, expectedMatches: number[], eleme suite('HTML Highlighting', () => { + test('Single', function (): any { + assertHighlights('|', [], null); + assertHighlights('<|html>', [1, 8], 'html'); + assertHighlights('', [1, 8], 'html'); + assertHighlights('', [1, 8], 'html'); + assertHighlights('', [1, 8], 'html'); + assertHighlights('|', [], null); + assertHighlights('<|/html>', [], null); + assertHighlights('', [1, 8], 'html'); + assertHighlights('', [1, 8], 'html'); + assertHighlights('', [1, 8], 'html'); + assertHighlights('', [1, 8], 'html'); + assertHighlights('', [1, 8], 'html'); + assertHighlights('|', [], null); + + }); + + test('Nested', function (): any { + assertHighlights('|
', [], null); + assertHighlights('<|div>', [7, 13], 'div'); + assertHighlights('
|
', [], null); + assertHighlights('
', [7, 13], 'div'); + assertHighlights('
', [7, 24], 'div'); + assertHighlights('
', [12, 18], 'div'); + assertHighlights('
', [12, 18], 'div'); + assertHighlights('
', [1, 30], 'html'); + assertHighlights('
', [7, 13], 'div'); + assertHighlights('
', [18, 24], 'div'); + }); + + test('Selfclosed', function (): any { + assertHighlights('<|div/>', [ 7 ], 'div'); + assertHighlights('<|br>', [ 7 ], 'br'); + assertHighlights('
', [ 12 ], 'div'); + }); - test('Highlighting', function (testDone): any { - testHighlighting - - - -} \ No newline at end of file +}); \ No newline at end of file