abbreviationActions.ts 3.4 KB
Newer Older
1 2 3 4 5 6 7
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import * as vscode from 'vscode';
import { expand } from '@emmetio/expand-abbreviation';
8 9 10
import parseStylesheet from '@emmetio/css-parser';
import parse from '@emmetio/html-matcher';
import Node from '@emmetio/node';
11
import { getSyntax, getNode, getInnerRange } from './util';
12
import { getExpandOptions, extractAbbreviation, isStyleSheet } from 'vscode-emmet-helper';
13
import { DocumentStreamReader } from './bufferStream';
14 15 16 17 18 19 20

export function wrapWithAbbreviation() {
	let editor = vscode.window.activeTextEditor;
	if (!editor) {
		vscode.window.showInformationMessage('No editor is active');
		return;
	}
21
	let syntax = getSyntax(editor.document);
22 23 24

	vscode.window.showInputBox({ prompt: 'Enter Abbreviation' }).then(abbr => {
		if (!abbr || !abbr.trim()) { return; }
25 26 27 28 29 30 31 32 33
		editor.selections.forEach(selection => {
			let rangeToReplace: vscode.Range = selection;
			if (rangeToReplace.isEmpty) {
				rangeToReplace = new vscode.Range(rangeToReplace.start.line, 0, rangeToReplace.start.line, editor.document.lineAt(rangeToReplace.start.line).text.length);
			}
			let textToReplace = editor.document.getText(rangeToReplace);
			let expandedText = expand(abbr, getExpandOptions(syntax, textToReplace));
			editor.insertSnippet(new vscode.SnippetString(expandedText), rangeToReplace);
		});
34 35 36
	});
}

37 38
export function expandAbbreviation(args) {

39 40 41 42 43
	let editor = vscode.window.activeTextEditor;
	if (!editor) {
		vscode.window.showInformationMessage('No editor is active');
		return;
	}
44
	if (typeof args !== 'object' || !args['syntax']) {
45 46
		return;
	}
47
	let syntax = args['syntax'];
48
	let parseContent = isStyleSheet(syntax) ? parseStylesheet : parse;
49
	let rootNode: Node = parseContent(new DocumentStreamReader(editor.document));
50

51 52 53 54 55 56 57
	editor.selections.forEach(selection => {
		let abbreviationRange: vscode.Range = selection;
		let position = selection.isReversed ? selection.anchor : selection.active;
		let abbreviation = editor.document.getText(abbreviationRange);
		if (abbreviationRange.isEmpty) {
			[abbreviationRange, abbreviation] = extractAbbreviation(editor.document, position);
		}
58

59 60 61 62 63 64 65 66 67 68
		let currentNode = getNode(rootNode, position);
		if (!isValidLocationForEmmetAbbreviation(currentNode, syntax, position)) {
			return;
		}

		let expandedText = expand(abbreviation, getExpandOptions(syntax));
		if (expandedText) {
			editor.insertSnippet(new vscode.SnippetString(expandedText), abbreviationRange);
		}
	});
69 70 71 72
}


/**
73 74
 * Checks if given position is a valid location to expand emmet abbreviation.
 * Works only on html and css/less/scss syntax
75 76 77 78
 * @param currentNode parsed node at given position
 * @param syntax syntax of the abbreviation
 * @param position position to validate
 */
79
export function isValidLocationForEmmetAbbreviation(currentNode: Node, syntax: string, position: vscode.Position): boolean {
80 81 82 83 84 85 86 87 88 89 90 91 92 93
	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;
94
}