提交 8902a30b 编写于 作者: R Ramya Achutha Rao

Consolidate emmet expand action and emmet completion

上级 7194723e
......@@ -5,7 +5,13 @@
import * as vscode from 'vscode';
import { expand } from '@emmetio/expand-abbreviation';
import { getSyntax, getProfile, extractAbbreviation } from './util';
import * as extract from '@emmetio/extract-abbreviation';
import parseStylesheet from '@emmetio/css-parser';
import parse from '@emmetio/html-matcher';
import Node from '@emmetio/node';
import { getSyntax, getProfile, isStyleSheet, getNode, getInnerRange } from './util';
import { DocumentStreamReader } from './bufferStream';
const field = (index, placeholder) => `\${${index}${placeholder ? ':' + placeholder : ''}}`;
......@@ -42,19 +48,97 @@ export function expandAbbreviation() {
vscode.window.showInformationMessage('No editor is active');
return;
}
let rangeToReplace: vscode.Range = editor.selection;
let abbr = editor.document.getText(rangeToReplace);
let syntax = getSyntax(editor.document);
let output = expandAbbreviationHelper(syntax, editor.document, editor.selection);
if (output) {
editor.insertSnippet(new vscode.SnippetString(output.expandedText), output.rangeToReplace);
}
}
export interface ExpandAbbreviationHelperOutput {
expandedText: string;
rangeToReplace: vscode.Range;
abbreviation: string;
syntax: string;
}
/**
* Expands abbreviation at given range in the given document
* @param syntax
* @param document
* @param rangeToReplace
*/
export function expandAbbreviationHelper(syntax: string, document: vscode.TextDocument, rangeToReplace: vscode.Range): ExpandAbbreviationHelperOutput {
let parseContent = isStyleSheet(syntax) ? parseStylesheet : parse;
let rootNode: Node = parseContent(new DocumentStreamReader(document));
let currentNode = getNode(rootNode, rangeToReplace.end);
if (forceCssSyntax(syntax, currentNode, rangeToReplace.end)) {
syntax = 'css';
} else if (!isValidLocationForEmmetAbbreviation(currentNode, syntax, rangeToReplace.end)) {
return;
}
let abbreviation = document.getText(rangeToReplace);
if (rangeToReplace.isEmpty) {
[rangeToReplace, abbr] = extractAbbreviation(rangeToReplace.start);
[rangeToReplace, abbreviation] = extractAbbreviation(document, rangeToReplace.start);
}
let syntax = getSyntax(editor.document);
let options = {
field: field,
syntax: syntax,
profile: getProfile(getSyntax(editor.document)),
profile: getProfile(syntax),
addons: syntax === 'jsx' ? { 'jsx': true } : null
};
let expandedText = expand(abbr, options);
editor.insertSnippet(new vscode.SnippetString(expandedText), rangeToReplace);
let expandedText = expand(abbreviation, options);
return { expandedText, rangeToReplace, abbreviation, syntax };
}
/**
* Extracts abbreviation from the given position in the given document
*/
function extractAbbreviation(document: vscode.TextDocument, position: vscode.Position): [vscode.Range, string] {
let currentLine = document.lineAt(position.line).text;
let result = extract(currentLine, position.character, true);
if (!result) {
return [null, ''];
}
let rangeToReplace = new vscode.Range(position.line, result.location, position.line, result.location + result.abbreviation.length);
return [rangeToReplace, result.abbreviation];
}
/**
* Inside <style> tag, force use of css abbreviations
*/
function forceCssSyntax(syntax: string, currentNode: Node, position: vscode.Position): boolean {
return !isStyleSheet(syntax)
&& currentNode
&& currentNode.close
&& currentNode.name === 'style'
&& getInnerRange(currentNode).contains(position);
}
/**
* Checks if given position is a valid location to expand emmet abbreviation
* @param currentNode parsed node at given position
* @param syntax syntax of the abbreviation
* @param position position to validate
*/
function isValidLocationForEmmetAbbreviation(currentNode: Node, syntax: string, position: vscode.Position): boolean {
if (!currentNode) {
return true;
}
if (isStyleSheet(syntax)) {
return currentNode.type !== 'rule'
|| (currentNode.selectorToken && position.isAfter(currentNode.selectorToken.end));
}
if (currentNode.close) {
return getInnerRange(currentNode).contains(position);
}
return false;
}
\ No newline at end of file
......@@ -6,11 +6,8 @@
import * as vscode from 'vscode';
import { expand, createSnippetsRegistry } from '@emmetio/expand-abbreviation';
import { getSyntax, getProfile, extractAbbreviation, isStyleSheet, getNode } from './util';
import parseStylesheet from '@emmetio/css-parser';
import parse from '@emmetio/html-matcher';
import Node from '@emmetio/node';
import { DocumentStreamReader } from './bufferStream';
import { getSyntax, isStyleSheet } from './util';
import { expandAbbreviationHelper, ExpandAbbreviationHelperOutput } from './abbreviationActions';
const field = (index, placeholder) => `\${${index}${placeholder ? ':' + placeholder : ''}}`;
const snippetCompletionsCache = new Map<string, vscode.CompletionItem[]>();
......@@ -23,65 +20,35 @@ export class EmmetCompletionItemProvider implements vscode.CompletionItemProvide
return Promise.resolve(null);
}
let completionItems: vscode.CompletionItem[] = [];
let syntax = getSyntax(document);
let currentWord = getCurrentWord(document, position);
let parseContent = isStyleSheet(syntax) ? parseStylesheet : parse;
let rootNode: Node = parseContent(new DocumentStreamReader(document));
let currentNode = getNode(rootNode, position);
// Inside <style> tag, trigger css abbreviations
if (!isStyleSheet(syntax) && currentNode && currentNode.name === 'style') {
syntax = 'css';
let expandedAbbr: vscode.CompletionItem;
if (vscode.workspace.getConfiguration('emmet')['showExpandedAbbreviation']) {
let output: ExpandAbbreviationHelperOutput = expandAbbreviationHelper(syntax, document, new vscode.Range(position, position));
if (output) {
expandedAbbr = new vscode.CompletionItem(output.abbreviation);
expandedAbbr.insertText = new vscode.SnippetString(output.expandedText);
expandedAbbr.documentation = removeTabStops(output.expandedText);
expandedAbbr.range = output.rangeToReplace;
expandedAbbr.detail = 'Expand Emmet Abbreviation';
syntax = output.syntax;
}
}
let expandedAbbr = this.getExpandedAbbreviation(document, position, syntax, currentNode);
let completionItems: vscode.CompletionItem[] = expandedAbbr ? [expandedAbbr] : [];
if (!isStyleSheet(syntax)) {
if (expandedAbbr) {
// In non stylesheet like syntax, this extension returns expanded abbr plus posssible abbr completions
// To differentiate between the 2, the former is given CompletionItemKind.Value so that it gets a different icon
expandedAbbr.kind = vscode.CompletionItemKind.Value;
}
let currentWord = getCurrentWord(document, position);
let abbreviationSuggestions = this.getAbbreviationSuggestions(syntax, currentWord, (expandedAbbr && currentWord === expandedAbbr.label));
completionItems = expandedAbbr ? [expandedAbbr, ...abbreviationSuggestions] : abbreviationSuggestions;
} else {
completionItems = expandedAbbr ? [expandedAbbr] : [];
completionItems = completionItems.concat(abbreviationSuggestions);
}
return Promise.resolve(new vscode.CompletionList(completionItems, true));
}
getExpandedAbbreviation(document: vscode.TextDocument, position: vscode.Position, syntax: string, currentNode: Node): vscode.CompletionItem {
if (!vscode.workspace.getConfiguration('emmet')['showExpandedAbbreviation']) {
return;
}
let [rangeToReplace, wordToExpand] = extractAbbreviation(position);
if (!rangeToReplace || !wordToExpand) {
return;
}
if (!isValidLocationForEmmetAbbreviation(currentNode, syntax, position)) {
return;
}
let expandedWord = expand(wordToExpand, {
field: field,
syntax: syntax,
profile: getProfile(syntax),
addons: syntax === 'jsx' ? { 'jsx': true } : null
});
let completionitem = new vscode.CompletionItem(wordToExpand);
completionitem.insertText = new vscode.SnippetString(expandedWord);
completionitem.documentation = removeTabStops(expandedWord);
completionitem.range = rangeToReplace;
completionitem.detail = 'Expand Emmet Abbreviation';
return completionitem;
}
getAbbreviationSuggestions(syntax: string, prefix: string, skipExactMatch: boolean) {
if (!vscode.workspace.getConfiguration('emmet')['showAbbreviationSuggestions'] || !prefix) {
return [];
......@@ -131,17 +98,6 @@ function removeTabStops(expandedWord: string): string {
return expandedWord.replace(/\$\{\d+\}/g, '').replace(/\$\{\d+:([^\}]+)\}/g, '$1');
}
function isValidLocationForEmmetAbbreviation(currentNode: Node, syntax: string, position: vscode.Position): boolean {
if (!currentNode) {
return true;
}
if (isStyleSheet(syntax)) {
return currentNode.type !== 'rule';
}
return position.isAfter(currentNode.open.end);
}
......@@ -6,7 +6,6 @@
import * as vscode from 'vscode';
import parse from '@emmetio/html-matcher';
import Node from '@emmetio/node';
import * as extract from '@emmetio/extract-abbreviation';
import { DocumentStreamReader } from './bufferStream';
export function validate(allowStylesheet: boolean = true): boolean {
......@@ -112,16 +111,11 @@ export function getNode(root: Node, position: vscode.Position, includeNodeBounda
return foundNode;
}
export function extractAbbreviation(position: vscode.Position): [vscode.Range, string] {
let editor = vscode.window.activeTextEditor;
let currentLine = editor.document.lineAt(position.line).text;
let result = extract(currentLine, position.character, true);
if (!result) {
return [null, ''];
export function getInnerRange(currentNode: Node): vscode.Range {
if (!currentNode.close) {
return;
}
let rangeToReplace = new vscode.Range(position.line, result.location, position.line, result.location + result.abbreviation.length);
return [rangeToReplace, result.abbreviation];
return new vscode.Range(currentNode.open.end, currentNode.close.start);
}
export function getDeepestNode(node: Node): Node {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册