diff --git a/extensions/html/server/src/service/htmlLanguageService.ts b/extensions/html/server/src/service/htmlLanguageService.ts index b3ed7f3a60f..fb7efc32619 100644 --- a/extensions/html/server/src/service/htmlLanguageService.ts +++ b/extensions/html/server/src/service/htmlLanguageService.ts @@ -3,12 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { parse} from './parser/htmlParser'; -import { doComplete } from './services/htmlCompletion'; -import { format } from './services/htmlFormatter'; -import { TextDocument, Position, CompletionItem, CompletionList, Hover, Range, SymbolInformation, Diagnostic, TextEdit, FormattingOptions, MarkedString } from 'vscode-languageserver-types'; +import {parse} from './parser/htmlParser'; +import {doComplete} from './services/htmlCompletion'; +import {format} from './services/htmlFormatter'; +import {findDocumentHighlights} from './services/htmlHighlighting'; +import {TextDocument, Position, CompletionItem, CompletionList, Hover, Range, SymbolInformation, Diagnostic, TextEdit, DocumentHighlight, FormattingOptions, MarkedString } from 'vscode-languageserver-types'; -export { TextDocument, Position, CompletionItem, CompletionList, Hover, Range, SymbolInformation, Diagnostic, TextEdit, FormattingOptions, MarkedString }; +export {TextDocument, Position, CompletionItem, CompletionList, Hover, Range, SymbolInformation, Diagnostic, TextEdit, DocumentHighlight, FormattingOptions, MarkedString }; export interface HTMLFormatConfiguration { @@ -35,10 +36,8 @@ export interface LanguageService { configure(settings: LanguageSettings): void; parseHTMLDocument(document: TextDocument): HTMLDocument; doValidation(document: TextDocument, htmlDocument: HTMLDocument): Diagnostic[]; - -// doResolve(item: CompletionItem): CompletionItem; - doComplete(document: TextDocument, position: Position, doc: HTMLDocument): CompletionList; -// findDocumentSymbols(document: TextDocument, doc: HTMLDocument): SymbolInformation[]; + findDocumentHighlights(document: TextDocument, position: Position, htmlDocument: HTMLDocument): DocumentHighlight[]; + doComplete(document: TextDocument, position: Position, htmlDocument: HTMLDocument): CompletionList; // doHover(document: TextDocument, position: Position, doc: HTMLDocument): Hover; format(document: TextDocument, range: Range, options: HTMLFormatConfiguration): TextEdit[]; } @@ -46,10 +45,10 @@ export interface LanguageService { export function getLanguageService() : LanguageService { return { doValidation: (document, htmlDocument) => { return []; }, - configure: (settings) => {}, parseHTMLDocument: (document) => parse(document.getText()), doComplete, - format + format, + findDocumentHighlights }; } \ No newline at end of file diff --git a/extensions/html/server/src/service/services/htmlHighlighting.ts b/extensions/html/server/src/service/services/htmlHighlighting.ts new file mode 100644 index 00000000000..0bb0f148382 --- /dev/null +++ b/extensions/html/server/src/service/services/htmlHighlighting.ts @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import {HTMLDocument} from '../parser/htmlParser'; +import {TokenType, createScanner} from '../parser/htmlScanner'; +import {TextDocument, Range, Position, DocumentHighlightKind, DocumentHighlight} from 'vscode-languageserver-types'; + +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') { + return []; + } + 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 }]; + } + return []; +} + +function isBeforeOrEqual(pos1: Position, pos2: Position) { + return pos1.line < pos2.line || (pos1.line === pos2.line && pos1.character <= pos2.character); +} + +function covers(range: Range, position: Position) { + return isBeforeOrEqual(range.start, position) && isBeforeOrEqual(position, range.end); +} + +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) { + token = scanner.scan(); + } + if (token !== TokenType.EOS) { + return { start: document.positionAt(scanner.getTokenOffset()), end: document.positionAt(scanner.getTokenEnd()) }; + } + return null; +} diff --git a/extensions/html/server/src/service/test/completion.test.ts b/extensions/html/server/src/service/test/completion.test.ts index 06c85214d6e..04237fe03f3 100644 --- a/extensions/html/server/src/service/test/completion.test.ts +++ b/extensions/html/server/src/service/test/completion.test.ts @@ -60,8 +60,8 @@ let testCompletionFor = function (value: string, expected: { count?: number, ite let document = TextDocument.create('test://test/test.html', 'html', 0, value); let position = document.positionAt(offset); - let jsonDoc = ls.parseHTMLDocument(document); - return asPromise(ls.doComplete(document, position, jsonDoc)).then(list => { + let htmlDoc = ls.parseHTMLDocument(document); + return asPromise(ls.doComplete(document, position, htmlDoc)).then(list => { try { if (expected.count) { assert.equal(list.items, expected.count); diff --git a/extensions/html/server/src/service/test/highlighting.test.ts b/extensions/html/server/src/service/test/highlighting.test.ts new file mode 100644 index 00000000000..959abd92685 --- /dev/null +++ b/extensions/html/server/src/service/test/highlighting.test.ts @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * 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 { + let offset = value.indexOf('|'); + value = value.substr(0, offset) + value.substr(offset + 1); + + let document = TextDocument.create('test://test/test.html', 'html', 0, value); + let htmlDocument = htmlLanguageService.getLanguageService().parseHTMLDocument(document); + + let position = document.positionAt(offset); + let ls = htmlLanguageService.getLanguageService(); + let htmlDoc = ls.parseHTMLDocument(document); + + let highlights = ls.findDocumentHighlights(document, position, htmlDoc); + assert.equal(highlights.length, expectedMatches.length); + for (let i = 0; i < highlights.length; i++) { + let actualStartOffset = document.offsetAt(highlights[i].range.start); + assert.equal(actualStartOffset, expectedMatches[i]); + let actualEndOffset = document.offsetAt(highlights[i].range.end); + assert.equal(actualEndOffset, expectedMatches[i] + elementName.length); + + assert.equal(document.getText().substring(actualStartOffset, actualEndOffset), elementName); + } +} + +suite('HTML Highlighting', () => { + + + + test('Highlighting', function (testDone): any { + testHighlighting + + + +} \ No newline at end of file