-
Notifications
You must be signed in to change notification settings - Fork 30k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial commit for builtin emmet extension #21943
- Loading branch information
1 parent
921107c
commit ea55445
Showing
20 changed files
with
1,302 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
{ | ||
"name": "emmet", | ||
"displayName": "emmet", | ||
"description": "Emmet support for VS Code", | ||
"version": "0.0.1", | ||
"publisher": "vscode", | ||
"engines": { | ||
"vscode": "^1.10.0" | ||
}, | ||
"categories": [ | ||
"Other" | ||
], | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/Microsoft/vscode-emmet" | ||
}, | ||
"activationEvents": [ | ||
"onLanguage:html", | ||
"onLanguage:jade", | ||
"onLanguage:slim", | ||
"onLanguage:haml", | ||
"onLanguage:xml", | ||
"onLanguage:xsl", | ||
"onLanguage:css", | ||
"onLanguage:scss", | ||
"onLanguage:sass", | ||
"onLanguage:less", | ||
"onLanguage:stylus", | ||
"onLanguage:javascriptreact", | ||
"onLanguage:typescriptreact" | ||
], | ||
"main": "./out/extension", | ||
"contributes": { | ||
"configuration": { | ||
"type": "object", | ||
"title": "Emmet configuration", | ||
"properties": { | ||
"emmet.suggestExpandedAbbreviation": { | ||
"type": "boolean", | ||
"default": true, | ||
"description": "Shows expanded emmet abbreviations as suggestions" | ||
}, | ||
"emmet.suggestAbbreviations": { | ||
"type": "boolean", | ||
"default": true, | ||
"description": "Shows possible emmet abbreviations as suggestions" | ||
} | ||
} | ||
} | ||
}, | ||
"scripts": { | ||
"vscode:prepublish": "tsc -p ./", | ||
"compile": "tsc -watch -p ./", | ||
"postinstall": "node ./node_modules/vscode/bin/install", | ||
"test": "node ./node_modules/vscode/bin/test" | ||
}, | ||
"devDependencies": { | ||
"typescript": "^2.0.3", | ||
"vscode": "^1.0.0", | ||
"mocha": "^2.3.3", | ||
"@types/node": "^6.0.40", | ||
"@types/mocha": "^2.2.32" | ||
}, | ||
"dependencies": { | ||
"@emmetio/expand-abbreviation": "^0.5.4", | ||
"@emmetio/extract-abbreviation": "^0.1.1", | ||
"@emmetio/html-matcher": "^0.3.1", | ||
"@emmetio/css-parser": "^0.3.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* 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'; | ||
import { getSyntax, getProfile, extractAbbreviation } from './util'; | ||
|
||
const field = (index, placeholder) => `\${${index}${placeholder ? ':' + placeholder : ''}}`; | ||
|
||
export function wrapWithAbbreviation() { | ||
let editor = vscode.window.activeTextEditor; | ||
if (!editor) { | ||
vscode.window.showInformationMessage('No editor is active'); | ||
return; | ||
} | ||
let rangeToReplace: vscode.Range = editor.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 options = { | ||
field: field, | ||
syntax: getSyntax(editor.document), | ||
profile: getProfile(getSyntax(editor.document)), | ||
text: textToReplace | ||
}; | ||
|
||
vscode.window.showInputBox({ prompt: 'Enter Abbreviation' }).then(abbr => { | ||
if (!abbr || !abbr.trim()) { return; } | ||
let expandedText = expand(abbr, options); | ||
editor.insertSnippet(new vscode.SnippetString(expandedText), rangeToReplace); | ||
}); | ||
} | ||
|
||
export function expandAbbreviation() { | ||
let editor = vscode.window.activeTextEditor; | ||
if (!editor) { | ||
vscode.window.showInformationMessage('No editor is active'); | ||
return; | ||
} | ||
let rangeToReplace: vscode.Range = editor.selection; | ||
let abbr = editor.document.getText(rangeToReplace); | ||
if (rangeToReplace.isEmpty) { | ||
[rangeToReplace, abbr] = extractAbbreviation(rangeToReplace.start); | ||
} | ||
|
||
let options = { | ||
field: field, | ||
syntax: getSyntax(editor.document), | ||
profile: getProfile(getSyntax(editor.document)) | ||
}; | ||
|
||
let expandedText = expand(abbr, options); | ||
editor.insertSnippet(new vscode.SnippetString(expandedText), rangeToReplace); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* 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 { getNode, getNodeOuterSelection, getNodeInnerSelection, isStyleSheet } from './util'; | ||
import parse from '@emmetio/html-matcher'; | ||
import Node from '@emmetio/node'; | ||
|
||
export function balanceOut() { | ||
balance(true); | ||
} | ||
|
||
export function balanceIn() { | ||
balance(false); | ||
} | ||
|
||
function balance(out: boolean) { | ||
let editor = vscode.window.activeTextEditor; | ||
if (!editor) { | ||
vscode.window.showInformationMessage('No editor is active'); | ||
return; | ||
} | ||
if (isStyleSheet(editor.document.languageId)) { | ||
return; | ||
} | ||
let getRangeFunction = out ? getRangeToBalanceOut : getRangeToBalanceIn; | ||
|
||
let rootNode: Node = parse(editor.document.getText()); | ||
|
||
let newSelections: vscode.Selection[] = []; | ||
editor.selections.forEach(selection => { | ||
let range = getRangeFunction(editor.document, selection, rootNode); | ||
if (range) { | ||
newSelections.push(range); | ||
} | ||
}); | ||
|
||
editor.selection = newSelections[0]; | ||
editor.selections = newSelections; | ||
} | ||
|
||
function getRangeToBalanceOut(document: vscode.TextDocument, selection: vscode.Selection, rootNode: Node): vscode.Selection { | ||
let offset = document.offsetAt(selection.start); | ||
let nodeToBalance = getNode(rootNode, offset); | ||
|
||
let innerSelection = getNodeInnerSelection(document, nodeToBalance); | ||
let outerSelection = getNodeOuterSelection(document, nodeToBalance); | ||
|
||
if (innerSelection.contains(selection) && !innerSelection.isEqual(selection)) { | ||
return innerSelection; | ||
} | ||
if (outerSelection.contains(selection) && !outerSelection.isEqual(selection)) { | ||
return outerSelection; | ||
} | ||
return; | ||
} | ||
|
||
function getRangeToBalanceIn(document: vscode.TextDocument, selection: vscode.Selection, rootNode: Node): vscode.Selection { | ||
let offset = document.offsetAt(selection.start); | ||
let nodeToBalance: Node = getNode(rootNode, offset); | ||
|
||
if (!nodeToBalance.firstChild) { | ||
return selection; | ||
} | ||
|
||
if (nodeToBalance.firstChild.start === offset && nodeToBalance.firstChild.end === document.offsetAt(selection.end)) { | ||
return getNodeInnerSelection(document, nodeToBalance.firstChild); | ||
} | ||
|
||
return new vscode.Selection(document.positionAt(nodeToBalance.firstChild.start), document.positionAt(nodeToBalance.firstChild.end)); | ||
|
||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* 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 { validate } from './util'; | ||
|
||
export function fetchEditPoint(direction: string): void { | ||
let editor = vscode.window.activeTextEditor; | ||
if (!validate()) { | ||
return; | ||
} | ||
|
||
let newSelections: vscode.Selection[] = []; | ||
editor.selections.forEach(selection => { | ||
let updatedSelection = direction === 'next' ? nextEditPoint(selection.anchor, editor) : prevEditPoint(selection.anchor, editor); | ||
newSelections.push(updatedSelection); | ||
}); | ||
editor.selections = newSelections; | ||
} | ||
|
||
function nextEditPoint(position: vscode.Position, editor: vscode.TextEditor): vscode.Selection { | ||
for (let lineNum = position.line; lineNum < editor.document.lineCount; lineNum++) { | ||
let updatedSelection = findEditPoint(lineNum, editor, position, 'next'); | ||
if (updatedSelection) { | ||
return updatedSelection; | ||
} | ||
} | ||
} | ||
|
||
function prevEditPoint(position: vscode.Position, editor: vscode.TextEditor): vscode.Selection { | ||
for (let lineNum = position.line; lineNum >= 0; lineNum--) { | ||
let updatedSelection = findEditPoint(lineNum, editor, position, 'prev'); | ||
if (updatedSelection) { | ||
return updatedSelection; | ||
} | ||
} | ||
} | ||
|
||
|
||
function findEditPoint(lineNum: number, editor: vscode.TextEditor, position: vscode.Position, direction: string): vscode.Selection { | ||
let line = editor.document.lineAt(lineNum); | ||
|
||
if (lineNum !== position.line && line.isEmptyOrWhitespace) { | ||
editor.selection = new vscode.Selection(lineNum, 0, lineNum, 0); | ||
return; | ||
} | ||
|
||
let lineContent = line.text; | ||
if (lineNum === position.line && direction === 'prev') { | ||
lineContent = lineContent.substr(0, position.character); | ||
} | ||
let emptyAttrIndex = direction === 'next' ? lineContent.indexOf('""', lineNum === position.line ? position.character : 0) : lineContent.lastIndexOf('""'); | ||
let emptyTagIndex = direction === 'next' ? lineContent.indexOf('><', lineNum === position.line ? position.character : 0) : lineContent.lastIndexOf('><'); | ||
|
||
let winner = -1; | ||
|
||
if (emptyAttrIndex > -1 && emptyTagIndex > -1) { | ||
winner = direction === 'next' ? Math.min(emptyAttrIndex, emptyTagIndex) : Math.max(emptyAttrIndex, emptyTagIndex); | ||
} else if (emptyAttrIndex > -1) { | ||
winner = emptyAttrIndex; | ||
} else { | ||
winner = emptyTagIndex; | ||
} | ||
|
||
if (winner > -1) { | ||
return new vscode.Selection(lineNum, winner + 1, lineNum, winner + 1); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* 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, createSnippetsRegistry } from '@emmetio/expand-abbreviation'; | ||
import { getSyntax, isStyleSheet, getProfile, extractAbbreviation } from './util'; | ||
|
||
const field = (index, placeholder) => `\${${index}${placeholder ? ':' + placeholder : ''}}`; | ||
const snippetCompletionsCache = new Map<string, vscode.CompletionItem[]>(); | ||
|
||
export class EmmetCompletionItemProvider implements vscode.CompletionItemProvider { | ||
|
||
public provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Thenable<vscode.CompletionList> { | ||
|
||
if (!vscode.workspace.getConfiguration('emmet')['useModules']) { | ||
return Promise.resolve(null); | ||
} | ||
|
||
let currentWord = getCurrentWord(document, position); | ||
let expandedAbbr = getExpandedAbbreviation(document, position); | ||
let abbreviationSuggestions = getAbbreviationSuggestions(getSyntax(document), currentWord, (expandedAbbr && currentWord === expandedAbbr.label)); | ||
let completionItems = expandedAbbr ? [expandedAbbr, ...abbreviationSuggestions] : abbreviationSuggestions; | ||
|
||
return Promise.resolve(new vscode.CompletionList(completionItems, true)); | ||
} | ||
} | ||
|
||
function getExpandedAbbreviation(document: vscode.TextDocument, position: vscode.Position): vscode.CompletionItem { | ||
if (!vscode.workspace.getConfiguration('emmet')['suggestExpandedAbbreviation']) { | ||
return; | ||
} | ||
let [rangeToReplace, wordToExpand] = extractAbbreviation(position); | ||
if (!rangeToReplace || !wordToExpand) { | ||
return; | ||
} | ||
let syntax = getSyntax(document); | ||
let expandedWord = expand(wordToExpand, { | ||
field: field, | ||
syntax: syntax, | ||
profile: getProfile(syntax) | ||
}); | ||
|
||
let completionitem = new vscode.CompletionItem(wordToExpand); | ||
completionitem.insertText = new vscode.SnippetString(expandedWord); | ||
completionitem.documentation = removeTabStops(expandedWord); | ||
completionitem.range = rangeToReplace; | ||
completionitem.detail = 'Expand Emmet Abbreviation'; | ||
|
||
// 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 | ||
if (!isStyleSheet(syntax)) { | ||
completionitem.kind = vscode.CompletionItemKind.Value; | ||
} | ||
return completionitem; | ||
} | ||
|
||
function getCurrentWord(document: vscode.TextDocument, position: vscode.Position): string { | ||
let wordAtPosition = document.getWordRangeAtPosition(position); | ||
let currentWord = ''; | ||
if (wordAtPosition && wordAtPosition.start.character < position.character) { | ||
let word = document.getText(wordAtPosition); | ||
currentWord = word.substr(0, position.character - wordAtPosition.start.character); | ||
} | ||
|
||
return currentWord; | ||
} | ||
|
||
function removeTabStops(expandedWord: string): string { | ||
return expandedWord.replace(/\$\{\d+\}/g, '').replace(/\$\{\d+:([^\}]+)\}/g, '$1'); | ||
} | ||
function getAbbreviationSuggestions(syntax: string, prefix: string, skipExactMatch: boolean) { | ||
if (!vscode.workspace.getConfiguration('emmet')['suggestAbbreviations'] || !prefix || isStyleSheet(syntax)) { | ||
return []; | ||
} | ||
|
||
if (!snippetCompletionsCache.has(syntax)) { | ||
let registry = createSnippetsRegistry(syntax); | ||
let completions: vscode.CompletionItem[] = registry.all({ type: 'string' }).map(snippet => { | ||
let expandedWord = expand(snippet.value, { | ||
field: field, | ||
syntax: syntax | ||
}); | ||
|
||
let item = new vscode.CompletionItem(snippet.key); | ||
item.documentation = removeTabStops(expandedWord); | ||
item.detail = 'Complete Emmet Abbreviation'; | ||
item.insertText = snippet.key; | ||
return item; | ||
}); | ||
snippetCompletionsCache.set(syntax, completions); | ||
} | ||
|
||
let snippetCompletions = snippetCompletionsCache.get(syntax); | ||
|
||
snippetCompletions = snippetCompletions.filter(x => x.label.startsWith(prefix) && (!skipExactMatch || x.label !== prefix)); | ||
|
||
return snippetCompletions; | ||
|
||
} | ||
|
||
|
||
|
Oops, something went wrong.