[html] update services, add folding for embedded css (for #47808)

This commit is contained in:
Martin Aeschlimann
2018-04-16 22:19:54 +02:00
parent e4c975592b
commit 21ef28c36e
7 changed files with 79 additions and 227 deletions

View File

@@ -6,7 +6,7 @@
import { LanguageModelCache, getLanguageModelCache } from '../languageModelCache';
import { TextDocument, Position, Range, CompletionList } from 'vscode-languageserver-types';
import { getCSSLanguageService, Stylesheet, ICompletionParticipant } from 'vscode-css-languageservice';
import { getCSSLanguageService, Stylesheet, ICompletionParticipant, FoldingRange } from 'vscode-css-languageservice';
import { LanguageMode, Workspace } from './languageModes';
import { HTMLDocumentRegions, CSS_STYLE_RULE } from './embeddedSupport';
import { Color } from 'vscode-languageserver';
@@ -37,7 +37,7 @@ export function getCSSMode(documentRegions: LanguageModelCache<HTMLDocumentRegio
if (typeof (<any>registeredCompletionParticipants[i]).getId === 'function' && (<any>registeredCompletionParticipants[i]).getId() === 'emmet') {
const extractedResults = extractAbbreviation(document, position, { lookAhead: false, syntax: 'css' });
if (extractedResults && extractedResults.abbreviation) {
registeredCompletionParticipants[i].onCssProperty({ propertyName: extractedResults.abbreviation, range: extractedResults.abbreviationRange });
registeredCompletionParticipants[i].onCssProperty!({ propertyName: extractedResults.abbreviation, range: extractedResults.abbreviationRange });
}
} else {
nonEmmetCompletionParticipants.push(registeredCompletionParticipants[i]);
@@ -75,6 +75,11 @@ export function getCSSMode(documentRegions: LanguageModelCache<HTMLDocumentRegio
let embedded = embeddedCSSDocuments.get(document);
return cssLanguageService.getColorPresentations(embedded, cssStylesheets.get(embedded), color, range);
},
getFoldingRanges(document: TextDocument, range: Range): FoldingRange[] {
let embedded = embeddedCSSDocuments.get(document);
let ranges = cssLanguageService.getFoldingRanges(embedded, {}).ranges;
return ranges.filter(r => r.startLine >= range.start.line && r.endLine < range.end.line);
},
onDocumentRemoved(document: TextDocument) {
embeddedCSSDocuments.onDocumentRemoved(document);
cssStylesheets.onDocumentRemoved(document);

View File

@@ -3,14 +3,11 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TextDocument, CancellationToken, Position } from 'vscode-languageserver';
import { LanguageService as HTMLLanguageService, TokenType, Range } from 'vscode-html-languageservice';
import { FoldingRangeType, FoldingRange, FoldingRangeList } from 'vscode-languageserver-protocol-foldingprovider';
import { TextDocument, CancellationToken, Position, Range } from 'vscode-languageserver';
import { FoldingRange, FoldingRangeList } from 'vscode-languageserver-protocol-foldingprovider';
import { LanguageModes } from './languageModes';
import { binarySearch } from '../utils/arrays';
export function getFoldingRegions(languageModes: LanguageModes, document: TextDocument, maxRanges: number | undefined, cancellationToken: CancellationToken | null): FoldingRangeList {
export function getFoldingRanges(languageModes: LanguageModes, document: TextDocument, maxRanges: number | undefined, cancellationToken: CancellationToken | null): FoldingRangeList {
let htmlMode = languageModes.getMode('html');
let range = Range.create(Position.create(0, 0), Position.create(document.lineCount, 0));
let ranges: FoldingRange[] = [];
@@ -92,94 +89,3 @@ function limitRanges(ranges: FoldingRange[], maxRanges: number) {
}
return ranges.filter((r, index) => (typeof nestingLevels[index] === 'number') && nestingLevels[index] < maxLevel);
}
export const EMPTY_ELEMENTS: string[] = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'];
export function isEmptyElement(e: string): boolean {
return !!e && binarySearch(EMPTY_ELEMENTS, e.toLowerCase(), (s1: string, s2: string) => s1.localeCompare(s2)) >= 0;
}
export function getHTMLFoldingRegions(htmlLanguageService: HTMLLanguageService, document: TextDocument, range: Range): FoldingRange[] {
const scanner = htmlLanguageService.createScanner(document.getText());
let token = scanner.scan();
let ranges: FoldingRange[] = [];
let stack: { startLine: number, tagName: string }[] = [];
let lastTagName = null;
let prevStart = -1;
function addRange(range: FoldingRange) {
ranges.push(range);
prevStart = range.startLine;
}
while (token !== TokenType.EOS) {
switch (token) {
case TokenType.StartTag: {
let tagName = scanner.getTokenText();
let startLine = document.positionAt(scanner.getTokenOffset()).line;
stack.push({ startLine, tagName });
lastTagName = tagName;
break;
}
case TokenType.EndTag: {
lastTagName = scanner.getTokenText();
break;
}
case TokenType.StartTagClose:
if (!lastTagName || !isEmptyElement(lastTagName)) {
break;
}
// fallthrough
case TokenType.EndTagClose:
case TokenType.StartTagSelfClose: {
let i = stack.length - 1;
while (i >= 0 && stack[i].tagName !== lastTagName) {
i--;
}
if (i >= 0) {
let stackElement = stack[i];
stack.length = i;
let line = document.positionAt(scanner.getTokenOffset()).line;
let startLine = stackElement.startLine;
let endLine = line - 1;
if (endLine > startLine && prevStart !== startLine) {
addRange({ startLine, endLine });
}
}
break;
}
case TokenType.Comment: {
let startLine = document.positionAt(scanner.getTokenOffset()).line;
let text = scanner.getTokenText();
let m = text.match(/^\s*#(region\b)|(endregion\b)/);
if (m) {
if (m[1]) { // start pattern match
stack.push({ startLine, tagName: '' }); // empty tagName marks region
} else {
let i = stack.length - 1;
while (i >= 0 && stack[i].tagName.length) {
i--;
}
if (i >= 0) {
let stackElement = stack[i];
stack.length = i;
let endLine = startLine;
startLine = stackElement.startLine;
if (endLine > startLine && prevStart !== startLine) {
addRange({ startLine, endLine, type: FoldingRangeType.Region });
}
}
}
} else {
let endLine = document.positionAt(scanner.getTokenOffset() + scanner.getTokenLength()).line;
if (startLine < endLine) {
addRange({ startLine, endLine, type: FoldingRangeType.Comment });
}
}
break;
}
}
token = scanner.scan();
}
return ranges;
}

View File

@@ -10,7 +10,6 @@ import { TextDocument, Position, Range, CompletionItem } from 'vscode-languagese
import { LanguageMode, Workspace } from './languageModes';
import { FoldingRange } from 'vscode-languageserver-protocol-foldingprovider';
import { getHTMLFoldingRegions } from './htmlFolding';
import { getPathCompletionParticipant } from './pathCompletion';
export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace: Workspace): LanguageMode {
@@ -65,9 +64,9 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace:
return htmlLanguageService.format(document, range, formatSettings);
},
getFoldingRanges(document: TextDocument, range: Range): FoldingRange[] {
return getHTMLFoldingRegions(htmlLanguageService, document, range);
let ranges = htmlLanguageService.getFoldingRanges(document).ranges;
return ranges.filter(r => r.startLine >= range.start.line && r.endLine < range.end.line);
},
doAutoClose(document: TextDocument, position: Position) {
let offset = document.offsetAt(position);
let text = document.getText();