未验证 提交 37c3cd11 编写于 作者: M Matt Bierner 提交者: GitHub

Start moving emmet extension to strict mode (#37740)

* Start moving emmet to strict mode

First part of moving the emmet extension to strict mode TypeScript. This change focuses on adding annotations when things can be undefined and removing jsdoc type comments

* Fix a few more errors

* Fix compile errors

* Tiny updates
上级 1d3f3bc2
......@@ -17,8 +17,8 @@ interface ExpandAbbreviationInput {
filter?: string;
}
export function wrapWithAbbreviation(args) {
if (!validate(false)) {
export function wrapWithAbbreviation(args: any) {
if (!validate(false) || !vscode.window.activeTextEditor) {
return;
}
......@@ -50,8 +50,8 @@ export function wrapWithAbbreviation(args) {
});
}
export function wrapIndividualLinesWithAbbreviation(args) {
if (!validate(false)) {
export function wrapIndividualLinesWithAbbreviation(args: any) {
if (!validate(false) || !vscode.window.activeTextEditor) {
return;
}
......@@ -88,7 +88,7 @@ export function wrapIndividualLinesWithAbbreviation(args) {
}
export function expandEmmetAbbreviation(args): Thenable<boolean> {
export function expandEmmetAbbreviation(args: any): Thenable<boolean> {
const syntax = getSyntaxFromArgs(args);
if (!syntax || !validate()) {
return fallbackTab();
......@@ -179,7 +179,7 @@ export function expandEmmetAbbreviation(args): Thenable<boolean> {
});
}
function fallbackTab(): Thenable<boolean> {
function fallbackTab(): Thenable<boolean | undefined> | undefined {
if (vscode.workspace.getConfiguration('emmet')['triggerExpansionOnTab'] === true) {
return vscode.commands.executeCommand('tab');
}
......@@ -226,7 +226,8 @@ export function isValidLocationForEmmetAbbreviation(currentNode: Node, syntax: s
const currentHtmlNode = <HtmlNode>currentNode;
if (currentHtmlNode.close) {
return getInnerRange(currentHtmlNode).contains(position);
const innerRange = getInnerRange(currentHtmlNode);
return !!innerRange && innerRange.contains(position);
}
return false;
......@@ -247,7 +248,7 @@ function expandAbbreviationInRange(editor: vscode.TextEditor, expandAbbrList: Ex
// Snippet to replace at multiple cursors are not the same
// `editor.insertSnippet` will have to be called for each instance separately
// We will not be able to maintain multiple cursors after snippet insertion
let insertPromises = [];
let insertPromises: Thenable<boolean>[] = [];
if (!insertSameSnippet) {
expandAbbrList.forEach((expandAbbrInput: ExpandAbbreviationInput) => {
let expandedText = expandAbbr(expandAbbrInput);
......@@ -278,7 +279,7 @@ function expandAbbreviationInRange(editor: vscode.TextEditor, expandAbbrList: Ex
/**
* Expands abbreviation as detailed in given input.
*/
function expandAbbr(input: ExpandAbbreviationInput): string {
function expandAbbr(input: ExpandAbbreviationInput): string | undefined {
const helper = getEmmetHelper();
const expandOptions = helper.getExpandOptions(input.syntax, getEmmetConfiguration(input.syntax), input.filter);
......@@ -322,7 +323,7 @@ function expandAbbr(input: ExpandAbbreviationInput): string {
}
function getSyntaxFromArgs(args: any): string {
function getSyntaxFromArgs(args: any): string | undefined {
let editor = vscode.window.activeTextEditor;
if (!editor) {
vscode.window.showInformationMessage('No editor is active.');
......
......@@ -16,11 +16,10 @@ export function balanceIn() {
}
function balance(out: boolean) {
let editor = vscode.window.activeTextEditor;
if (!validate(false)) {
if (!validate(false) || !vscode.window.activeTextEditor) {
return;
}
const editor = vscode.window.activeTextEditor;
let rootNode = <HtmlNode>parseDocument(editor.document);
if (!rootNode) {
return;
......@@ -30,7 +29,7 @@ function balance(out: boolean) {
let newSelections: vscode.Selection[] = [];
editor.selections.forEach(selection => {
let range = getRangeFunction(editor.document, selection, rootNode);
newSelections.push(range ? range : selection);
newSelections.push(range);
});
editor.selection = newSelections[0];
......@@ -40,7 +39,7 @@ function balance(out: boolean) {
function getRangeToBalanceOut(document: vscode.TextDocument, selection: vscode.Selection, rootNode: HtmlNode): vscode.Selection {
let nodeToBalance = <HtmlNode>getNode(rootNode, selection.start);
if (!nodeToBalance) {
return;
return selection;
}
if (!nodeToBalance.close) {
return new vscode.Selection(nodeToBalance.start, nodeToBalance.end);
......@@ -55,13 +54,13 @@ function getRangeToBalanceOut(document: vscode.TextDocument, selection: vscode.S
if (outerSelection.contains(selection) && !outerSelection.isEqual(selection)) {
return outerSelection;
}
return;
return selection;
}
function getRangeToBalanceIn(document: vscode.TextDocument, selection: vscode.Selection, rootNode: HtmlNode): vscode.Selection {
let nodeToBalance = <HtmlNode>getNode(rootNode, selection.start, true);
if (!nodeToBalance) {
return;
return selection;
}
if (selection.start.isEqual(nodeToBalance.start)
......@@ -71,7 +70,7 @@ function getRangeToBalanceIn(document: vscode.TextDocument, selection: vscode.Se
}
if (!nodeToBalance.firstChild) {
return;
return selection;
}
if (selection.start.isEqual(nodeToBalance.firstChild.start)
......
......@@ -43,20 +43,16 @@ export class DocumentStreamReader {
/**
* Creates a new stream instance which is limited to given range for given document
* @param {Position} start
* @param {Position} end
* @return {DocumentStreamReader}
*/
limit(start, end) {
limit(start: Position, end: Position): DocumentStreamReader {
return new DocumentStreamReader(this.document, start, new Range(start, end));
}
/**
* Returns the next character code in the stream without advancing it.
* Will return NaN at the end of the file.
* @returns {Number}
*/
peek() {
peek(): number {
if (this.eof()) {
return NaN;
}
......@@ -67,9 +63,8 @@ export class DocumentStreamReader {
/**
* Returns the next character in the stream and advances it.
* Also returns NaN when no more characters are available.
* @returns {Number}
*/
next() {
next(): number {
if (this.eof()) {
return NaN;
}
......@@ -95,9 +90,8 @@ export class DocumentStreamReader {
/**
* Backs up the stream n characters. Backing it up further than the
* start of the current token will cause things to break, so be careful.
* @param {Number} n
*/
backUp(n) {
backUp(n: number) {
let row = this.pos.line;
let column = this.pos.character;
column -= (n || 1);
......@@ -117,28 +111,22 @@ export class DocumentStreamReader {
/**
* Get the string between the start of the current token and the
* current stream position.
* @returns {String}
*/
current() {
current(): string {
return this.substring(this.start, this.pos);
}
/**
* Returns contents for given range
* @param {Position} from
* @param {Position} to
* @return {String}
*/
substring(from, to) {
substring(from: Position, to: Position): string {
return this.document.getText(new Range(from, to));
}
/**
* Creates error object with current stream state
* @param {String} message
* @return {Error}
*/
error(message) {
error(message: string): Error {
const err = new Error(`${message} at row ${this.pos.line}, column ${this.pos.character}`);
return err;
......@@ -146,10 +134,8 @@ export class DocumentStreamReader {
/**
* Returns line length of given row, including line ending
* @param {Number} row
* @return {Number}
*/
_lineLength(row) {
_lineLength(row: number): number {
if (row === this.document.lineCount - 1) {
return this.document.lineAt(row).text.length;
}
......@@ -161,10 +147,8 @@ export class DocumentStreamReader {
* and returns a boolean. If the next character in the stream 'matches'
* the given argument, it is consumed and returned.
* Otherwise, `false` is returned.
* @param {Number|Function} match
* @returns {Boolean}
*/
eat(match) {
eat(match: number | Function): boolean {
const ch = this.peek();
const ok = typeof match === 'function' ? match(ch) : ch === match;
......@@ -178,10 +162,8 @@ export class DocumentStreamReader {
/**
* Repeatedly calls <code>eat</code> with the given argument, until it
* fails. Returns <code>true</code> if any characters were eaten.
* @param {Object} match
* @returns {Boolean}
*/
eatWhile(match) {
eatWhile(match: number | Function): boolean {
const start = this.pos;
while (!this.eof() && this.eat(match)) { }
return !this.pos.isEqual(start);
......
......@@ -12,7 +12,7 @@ const allowedMimeTypesInScriptTag = ['text/html', 'text/plain', 'text/x-template
export class DefaultCompletionItemProvider implements vscode.CompletionItemProvider {
public provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Thenable<vscode.CompletionList> {
public provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Thenable<vscode.CompletionList | undefined> | undefined {
const mappedLanguages = getMappingForIncludedLanguages();
const emmetConfig = vscode.workspace.getConfiguration('emmet');
......@@ -45,8 +45,8 @@ export class DefaultCompletionItemProvider implements vscode.CompletionItemProvi
if (abbreviation.startsWith('this.')) {
noiseCheckPromise = Promise.resolve(true);
} else {
noiseCheckPromise = vscode.commands.executeCommand('vscode.executeDocumentSymbolProvider', document.uri).then((symbols: vscode.SymbolInformation[]) => {
return symbols.find(x => abbreviation === x.name || (abbreviation.startsWith(x.name + '.') && !/>|\*|\+/.test(abbreviation)));
noiseCheckPromise = vscode.commands.executeCommand<vscode.SymbolInformation[]>('vscode.executeDocumentSymbolProvider', document.uri).then((symbols: vscode.SymbolInformation[] | undefined) => {
return symbols && symbols.find(x => abbreviation === x.name || (abbreviation.startsWith(x.name + '.') && !/>|\*|\+/.test(abbreviation)));
});
}
}
......@@ -88,7 +88,7 @@ export class DefaultCompletionItemProvider implements vscode.CompletionItemProvi
* @param document vscode.Textdocument
* @param position vscode.Position position of the abbreviation that needs to be expanded
*/
private syntaxHelper(syntax: string, document: vscode.TextDocument, position: vscode.Position): string {
private syntaxHelper(syntax: string | undefined, document: vscode.TextDocument, position: vscode.Position): string | undefined {
if (!syntax) {
return syntax;
}
......
......@@ -7,40 +7,42 @@ import * as vscode from 'vscode';
import { validate } from './util';
export function fetchEditPoint(direction: string): void {
let editor = vscode.window.activeTextEditor;
if (!validate()) {
if (!validate() || !vscode.window.activeTextEditor) {
return;
}
const editor = vscode.window.activeTextEditor;
let newSelections: vscode.Selection[] = [];
editor.selections.forEach(selection => {
let updatedSelection = direction === 'next' ? nextEditPoint(selection.anchor, editor) : prevEditPoint(selection.anchor, editor);
newSelections.push(updatedSelection ? updatedSelection : selection);
let updatedSelection = direction === 'next' ? nextEditPoint(selection, editor) : prevEditPoint(selection, editor);
newSelections.push(updatedSelection);
});
editor.selections = newSelections;
editor.revealRange(editor.selections[editor.selections.length - 1]);
}
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');
function nextEditPoint(selection: vscode.Selection, editor: vscode.TextEditor): vscode.Selection {
for (let lineNum = selection.anchor.line; lineNum < editor.document.lineCount; lineNum++) {
let updatedSelection = findEditPoint(lineNum, editor, selection.anchor, 'next');
if (updatedSelection) {
return updatedSelection;
}
}
return selection;
}
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');
function prevEditPoint(selection: vscode.Selection, editor: vscode.TextEditor): vscode.Selection {
for (let lineNum = selection.anchor.line; lineNum >= 0; lineNum--) {
let updatedSelection = findEditPoint(lineNum, editor, selection.anchor, 'prev');
if (updatedSelection) {
return updatedSelection;
}
}
return selection;
}
function findEditPoint(lineNum: number, editor: vscode.TextEditor, position: vscode.Position, direction: string): vscode.Selection {
function findEditPoint(lineNum: number, editor: vscode.TextEditor, position: vscode.Position, direction: string): vscode.Selection | undefined {
let line = editor.document.lineAt(lineNum);
let lineContent = line.text;
......
......@@ -10,11 +10,11 @@ import evaluate from '@emmetio/math-expression';
import { DocumentStreamReader } from './bufferStream';
export function evaluateMathExpression() {
let editor = vscode.window.activeTextEditor;
if (!editor) {
if (!vscode.window.activeTextEditor) {
vscode.window.showInformationMessage('No editor is active');
return;
}
const editor = vscode.window.activeTextEditor;
const stream = new DocumentStreamReader(editor.document);
editor.edit(editBuilder => {
editor.selections.forEach(selection => {
......
......@@ -19,20 +19,16 @@ const reUrl = /^https?:/;
/**
* Get size of given image file. Supports files from local filesystem,
* as well as URLs
* @param {String} file Path to local file or URL
* @return {Promise}
*/
export function getImageSize(file) {
export function getImageSize(file: string) {
file = file.replace(/^file:\/\//, '');
return reUrl.test(file) ? getImageSizeFromURL(file) : getImageSizeFromFile(file);
}
/**
* Get image size from file on local file system
* @param {String} file
* @return {Promise}
*/
function getImageSizeFromFile(file) {
function getImageSizeFromFile(file: string) {
return new Promise((resolve, reject) => {
const isDataUrl = file.match(/^data:.+?;base64,/);
......@@ -46,7 +42,7 @@ function getImageSizeFromFile(file) {
}
}
sizeOf(file, (err, size) => {
sizeOf(file, (err: any, size: any) => {
if (err) {
reject(err);
} else {
......@@ -58,15 +54,13 @@ function getImageSizeFromFile(file) {
/**
* Get image size from given remove URL
* @param {String} url
* @return {Promise}
*/
function getImageSizeFromURL(url) {
function getImageSizeFromURL(urlStr: string) {
return new Promise((resolve, reject) => {
url = parseUrl(url);
const url = parseUrl(urlStr);
const getTransport = url.protocol === 'https:' ? https.get : http.get;
getTransport(url, resp => {
getTransport(url as any, resp => {
const chunks = [];
let bufSize = 0;
......@@ -102,11 +96,8 @@ function getImageSizeFromURL(url) {
/**
* Returns size object for given file name. If file name contains `@Nx` token,
* the final dimentions will be downscaled by N
* @param {String} fileName
* @param {Object} size
* @return {Object}
*/
function sizeForFileName(fileName, size) {
function sizeForFileName(fileName: string, size: any) {
const m = fileName.match(/@(\d+)x\./);
const scale = m ? +m[1] : 1;
......
......@@ -7,9 +7,9 @@ import * as vscode from 'vscode';
import { getDeepestNode, findNextWord, findPrevWord, getNode } from './util';
import { HtmlNode } from 'EmmetNode';
export function nextItemHTML(selectionStart: vscode.Position, selectionEnd: vscode.Position, editor: vscode.TextEditor, rootNode: HtmlNode): vscode.Selection {
export function nextItemHTML(selectionStart: vscode.Position, selectionEnd: vscode.Position, editor: vscode.TextEditor, rootNode: HtmlNode): vscode.Selection | undefined {
let currentNode = <HtmlNode>getNode(rootNode, selectionEnd);
let nextNode: HtmlNode;
let nextNode: HtmlNode | undefined = undefined;
if (!currentNode) {
return;
......@@ -50,12 +50,12 @@ export function nextItemHTML(selectionStart: vscode.Position, selectionEnd: vsco
}
}
return getSelectionFromNode(nextNode, editor.document);
return nextNode && getSelectionFromNode(nextNode, editor.document);
}
export function prevItemHTML(selectionStart: vscode.Position, selectionEnd: vscode.Position, editor: vscode.TextEditor, rootNode: HtmlNode): vscode.Selection {
export function prevItemHTML(selectionStart: vscode.Position, selectionEnd: vscode.Position, editor: vscode.TextEditor, rootNode: HtmlNode): vscode.Selection | undefined {
let currentNode = <HtmlNode>getNode(rootNode, selectionStart);
let prevNode: HtmlNode;
let prevNode: HtmlNode | undefined = undefined;
if (!currentNode) {
return;
......@@ -68,7 +68,7 @@ export function prevItemHTML(selectionStart: vscode.Position, selectionEnd: vsco
} else {
// Select the child that appears just before the cursor and is not a comment
prevNode = currentNode.firstChild;
let oldOption: HtmlNode;
let oldOption: HtmlNode | undefined = undefined;
while (prevNode.nextSibling && selectionStart.isAfterOrEqual(prevNode.nextSibling.end)) {
if (prevNode && prevNode.type !== 'comment') {
oldOption = prevNode;
......@@ -94,20 +94,25 @@ export function prevItemHTML(selectionStart: vscode.Position, selectionEnd: vsco
}
if (!prevNode) {
return undefined;
}
let attrSelection = getPrevAttribute(selectionStart, selectionEnd, editor.document, prevNode);
return attrSelection ? attrSelection : getSelectionFromNode(prevNode, editor.document);
}
function getSelectionFromNode(node: HtmlNode, document: vscode.TextDocument): vscode.Selection {
function getSelectionFromNode(node: HtmlNode, document: vscode.TextDocument): vscode.Selection | undefined {
if (node && node.open) {
let selectionStart = (<vscode.Position>node.open.start).translate(0, 1);
let selectionEnd = selectionStart.translate(0, node.name.length);
return new vscode.Selection(selectionStart, selectionEnd);
}
return undefined;
}
function getNextAttribute(selectionStart: vscode.Position, selectionEnd: vscode.Position, document: vscode.TextDocument, node: HtmlNode): vscode.Selection {
function getNextAttribute(selectionStart: vscode.Position, selectionEnd: vscode.Position, document: vscode.TextDocument, node: HtmlNode): vscode.Selection | undefined {
if (!node.attributes || node.attributes.length === 0 || node.type === 'comment') {
return;
......@@ -158,7 +163,7 @@ function getNextAttribute(selectionStart: vscode.Position, selectionEnd: vscode.
}
}
function getPrevAttribute(selectionStart: vscode.Position, selectionEnd: vscode.Position, document: vscode.TextDocument, node: HtmlNode): vscode.Selection {
function getPrevAttribute(selectionStart: vscode.Position, selectionEnd: vscode.Position, document: vscode.TextDocument, node: HtmlNode): vscode.Selection | undefined {
if (!node.attributes || node.attributes.length === 0 || node.type === 'comment') {
return;
......
......@@ -7,7 +7,7 @@ import * as vscode from 'vscode';
import { getDeepestNode, findNextWord, findPrevWord, getNode } from './util';
import { Node, CssNode, Rule, Property } from 'EmmetNode';
export function nextItemStylesheet(startOffset: vscode.Position, endOffset: vscode.Position, editor: vscode.TextEditor, rootNode: Node): vscode.Selection {
export function nextItemStylesheet(startOffset: vscode.Position, endOffset: vscode.Position, editor: vscode.TextEditor, rootNode: Node): vscode.Selection | undefined {
let currentNode = <CssNode>getNode(rootNode, endOffset, true);
if (!currentNode) {
currentNode = <CssNode>rootNode;
......@@ -50,7 +50,7 @@ export function nextItemStylesheet(startOffset: vscode.Position, endOffset: vsco
}
export function prevItemStylesheet(startOffset: vscode.Position, endOffset: vscode.Position, editor: vscode.TextEditor, rootNode: CssNode): vscode.Selection {
export function prevItemStylesheet(startOffset: vscode.Position, endOffset: vscode.Position, editor: vscode.TextEditor, rootNode: CssNode): vscode.Selection | undefined {
let currentNode = <CssNode>getNode(rootNode, startOffset);
if (!currentNode) {
currentNode = rootNode;
......@@ -88,7 +88,7 @@ export function prevItemStylesheet(startOffset: vscode.Position, endOffset: vsco
}
function getSelectionFromNode(node: Node, document: vscode.TextDocument): vscode.Selection {
function getSelectionFromNode(node: Node, document: vscode.TextDocument): vscode.Selection | undefined {
if (!node) {
return;
}
......@@ -98,7 +98,7 @@ function getSelectionFromNode(node: Node, document: vscode.TextDocument): vscode
}
function getSelectionFromProperty(node: Node, document: vscode.TextDocument, selectionStart: vscode.Position, selectionEnd: vscode.Position, selectFullValue: boolean, direction: string): vscode.Selection {
function getSelectionFromProperty(node: Node, document: vscode.TextDocument, selectionStart: vscode.Position, selectionEnd: vscode.Position, selectFullValue: boolean, direction: string): vscode.Selection | undefined {
if (!node || node.type !== 'property') {
return;
}
......
......@@ -8,11 +8,11 @@ import { HtmlNode } from 'EmmetNode';
import { getNode, parseDocument, validate } from './util';
export function splitJoinTag() {
let editor = vscode.window.activeTextEditor;
if (!validate(false)) {
if (!validate(false) || !vscode.window.activeTextEditor) {
return;
}
const editor = vscode.window.activeTextEditor;
let rootNode = <HtmlNode>parseDocument(editor.document);
if (!rootNode) {
return;
......@@ -20,23 +20,19 @@ export function splitJoinTag() {
return editor.edit(editBuilder => {
editor.selections.reverse().forEach(selection => {
let textEdit = getRangesToReplace(editor.document, selection, rootNode);
if (textEdit) {
let nodeToUpdate = <HtmlNode>getNode(rootNode, selection.start);
if (nodeToUpdate) {
let textEdit = getRangesToReplace(editor.document, nodeToUpdate);
editBuilder.replace(textEdit.range, textEdit.newText);
}
});
});
}
function getRangesToReplace(document: vscode.TextDocument, selection: vscode.Selection, rootNode: HtmlNode): vscode.TextEdit {
let nodeToUpdate = <HtmlNode>getNode(rootNode, selection.start);
function getRangesToReplace(document: vscode.TextDocument, nodeToUpdate: HtmlNode): vscode.TextEdit {
let rangeToReplace: vscode.Range;
let textToReplaceWith: string;
if (!nodeToUpdate) {
return;
}
if (!nodeToUpdate.close) {
// Split Tag
let nodeText = document.getText(new vscode.Range(nodeToUpdate.start, nodeToUpdate.end));
......
......@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { getNodesInBetween, getNode, parseDocument, sameNodes, isStyleSheet } from './util';
import { getNodesInBetween, getNode, parseDocument, sameNodes, isStyleSheet, validate } from './util';
import { Node, Stylesheet, Rule, HtmlNode } from 'EmmetNode';
import parseStylesheet from '@emmetio/css-parser';
import { DocumentStreamReader } from './bufferStream';
......@@ -14,14 +14,12 @@ const endCommentStylesheet = '*/';
const startCommentHTML = '<!--';
const endCommentHTML = '-->';
export function toggleComment(): Thenable<boolean> {
let editor = vscode.window.activeTextEditor;
if (!editor) {
vscode.window.showInformationMessage('No editor is active');
export function toggleComment(): Thenable<boolean> | undefined {
if (!validate() || !vscode.window.activeTextEditor) {
return;
}
let toggleCommentInternal;
const editor = vscode.window.activeTextEditor;
let toggleCommentInternal: (document: vscode.TextDocument, selection: vscode.Selection, rootNode: Node) => vscode.TextEdit[];
if (isStyleSheet(editor.document.languageId)) {
toggleCommentInternal = toggleCommentStylesheet;
......
......@@ -10,7 +10,7 @@
import { TextEditor, Range, Position, window, TextEdit } from 'vscode';
import * as path from 'path';
import { getImageSize } from './imageSizeHelper';
import { parseDocument, getNode, iterateCSSToken, getCssPropertyFromRule, isStyleSheet } from './util';
import { parseDocument, getNode, iterateCSSToken, getCssPropertyFromRule, isStyleSheet, validate } from './util';
import { HtmlNode, CssToken, HtmlToken, Attribute, Property } from 'EmmetNode';
import { locateFile } from './locateFile';
import parseStylesheet from '@emmetio/css-parser';
......@@ -20,11 +20,10 @@ import { DocumentStreamReader } from './bufferStream';
* Updates size of context image in given editor
*/
export function updateImageSize() {
let editor = window.activeTextEditor;
if (!editor) {
window.showInformationMessage('No editor is active.');
if (!validate() || !window.activeTextEditor) {
return;
}
const editor = window.activeTextEditor;
let allUpdatesPromise = editor.selections.reverse().map(selection => {
let position = selection.isReversed ? selection.active : selection.anchor;
......@@ -49,7 +48,7 @@ export function updateImageSize() {
/**
* Updates image size of context tag of HTML model
*/
function updateImageSizeHTML(editor: TextEditor, position: Position): Promise<TextEdit[]> {
function updateImageSizeHTML(editor: TextEditor, position: Position): Promise<TextEdit[] | undefined> {
const src = getImageSrcHTML(getImageHTMLNode(editor, position));
if (!src) {
......@@ -70,7 +69,7 @@ function updateImageSizeHTML(editor: TextEditor, position: Position): Promise<Te
}
function updateImageSizeStyleTag(editor: TextEditor, position: Position): Promise<TextEdit[]> {
let getPropertyInsiderStyleTag = (editor) => {
const getPropertyInsiderStyleTag = (editor: TextEditor): Property | undefined => {
const rootNode = parseDocument(editor.document);
const currentNode = <HtmlNode>getNode(rootNode, position);
if (currentNode && currentNode.name === 'style'
......@@ -79,7 +78,7 @@ function updateImageSizeStyleTag(editor: TextEditor, position: Position): Promis
let buffer = new DocumentStreamReader(editor.document, currentNode.open.end, new Range(currentNode.open.end, currentNode.close.start));
let rootNode = parseStylesheet(buffer);
const node = getNode(rootNode, position);
return (node && node.type === 'property') ? <Property>node : null;
return (node && node.type === 'property') ? <Property>node : undefined;
}
};
......@@ -93,7 +92,7 @@ function updateImageSizeCSSFile(editor: TextEditor, position: Position): Promise
/**
* Updates image size of context rule of stylesheet model
*/
function updateImageSizeCSS(editor: TextEditor, position: Position, fetchNode: (editor, position) => Property): Promise<TextEdit[]> {
function updateImageSizeCSS(editor: TextEditor, position: Position, fetchNode: (editor: TextEditor, position: Position) => Property | undefined): Promise<TextEdit[]> {
const src = getImageSrcCSS(fetchNode(editor, position), position);
......@@ -141,10 +140,8 @@ function getImageCSSNode(editor: TextEditor, position: Position): Property {
/**
* Returns image source from given <img> node
* @param {HtmlNode} node
* @return {string}
*/
function getImageSrcHTML(node: HtmlNode): string {
function getImageSrcHTML(node: HtmlNode): string | undefined {
const srcAttr = getAttribute(node, 'src');
if (!srcAttr) {
return;
......@@ -155,11 +152,8 @@ function getImageSrcHTML(node: HtmlNode): string {
/**
* Returns image source from given `url()` token
* @param {Property} node
* @param {Position}
* @return {string}
*/
function getImageSrcCSS(node: Property, position: Position): string {
function getImageSrcCSS(node: Property | undefined, position: Position): string | undefined {
if (!node) {
return;
}
......@@ -213,10 +207,6 @@ function updateHTMLTag(editor: TextEditor, node: HtmlNode, width: number, height
/**
* Updates size of given CSS rule
* @param {TextEditor} editor
* @param {Property} srcProp
* @param {number} width
* @param {number} height
*/
function updateCSSNode(editor: TextEditor, srcProp: Property, width: number, height: number): TextEdit[] {
const rule = srcProp.parent;
......@@ -252,36 +242,28 @@ function updateCSSNode(editor: TextEditor, srcProp: Property, width: number, hei
/**
* Returns attribute object with `attrName` name from given HTML node
* @param {Node} node
* @param {String} attrName
* @return {Object}
*/
function getAttribute(node, attrName): Attribute {
function getAttribute(node: HtmlNode, attrName: string): Attribute {
attrName = attrName.toLowerCase();
return node && node.open.attributes.find(attr => attr.name.value.toLowerCase() === attrName);
return node && (node.open as any).attributes.find(attr => attr.name.value.toLowerCase() === attrName);
}
/**
* Returns quote character, used for value of given attribute. May return empty
* string if attribute wasn’t quoted
* @param {TextEditor} editor
* @param {Object} attr
* @return {String}
*/
function getAttributeQuote(editor, attr) {
function getAttributeQuote(editor: TextEditor, attr: any): string {
const range = new Range(attr.value ? attr.value.end : attr.end, attr.end);
return range.isEmpty ? '' : editor.document.getText(range);
}
/**
* Finds 'url' token for given `pos` point in given CSS property `node`
* @param {Node} node
* @param {Position} pos
* @return {Token}
*/
function findUrlToken(node, pos: Position) {
for (let i = 0, il = node.parsedValue.length, url; i < il; i++) {
iterateCSSToken(node.parsedValue[i], (token: CssToken) => {
function findUrlToken(node: Property, pos: Position): CssToken | undefined {
for (let i = 0, il = (node as any).parsedValue.length, url; i < il; i++) {
iterateCSSToken((node as any).parsedValue[i], (token: CssToken) => {
if (token.type === 'url' && token.start.isBeforeOrEqual(pos) && token.end.isAfterOrEqual(pos)) {
url = token;
return false;
......@@ -296,11 +278,8 @@ function findUrlToken(node, pos: Position) {
/**
* Returns a string that is used to delimit properties in current node’s rule
* @param {TextEditor} editor
* @param {Property} node
* @return {String}
*/
function getPropertyDelimitor(editor: TextEditor, node: Property) {
function getPropertyDelimitor(editor: TextEditor, node: Property): string {
let anchor;
if (anchor = (node.previousSibling || node.parent.contentStartToken)) {
return editor.document.getText(new Range(anchor.end, node.start));
......
......@@ -7,17 +7,17 @@ import * as vscode from 'vscode';
import { HtmlNode } from 'EmmetNode';
import { getNode, parseDocument, validate } from './util';
export function updateTag(tagName: string): Thenable<boolean> {
let editor = vscode.window.activeTextEditor;
if (!validate(false)) {
export function updateTag(tagName: string): Thenable<boolean> | undefined {
if (!validate(false) || !vscode.window.activeTextEditor) {
return;
}
let editor = vscode.window.activeTextEditor;
let rootNode = <HtmlNode>parseDocument(editor.document);
if (!rootNode) {
return;
}
let rangesToUpdate = [];
let rangesToUpdate: vscode.Range[] = [];
editor.selections.reverse().forEach(selection => {
rangesToUpdate = rangesToUpdate.concat(getRangesToUpdate(editor, selection, rootNode));
});
......
......@@ -6,12 +6,12 @@
import * as vscode from 'vscode';
import parse from '@emmetio/html-matcher';
import parseStylesheet from '@emmetio/css-parser';
import { Node, HtmlNode, CssToken, Property } from 'EmmetNode';
import { Node, HtmlNode, CssToken, Property, Rule } from 'EmmetNode';
import { DocumentStreamReader } from './bufferStream';
import * as path from 'path';
let _emmetHelper;
let _currentExtensionsPath = undefined;
let _emmetHelper: any;
let _currentExtensionsPath: string | undefined = undefined;
export function getEmmetHelper() {
if (!_emmetHelper) {
......@@ -27,15 +27,15 @@ export function resolveUpdateExtensionsPath() {
}
let extensionsPath = vscode.workspace.getConfiguration('emmet')['extensionsPath'];
if (extensionsPath && !path.isAbsolute(extensionsPath)) {
extensionsPath = path.join(vscode.workspace.rootPath, extensionsPath);
extensionsPath = path.join(vscode.workspace.rootPath || '', extensionsPath);
}
if (_currentExtensionsPath !== extensionsPath) {
_currentExtensionsPath = extensionsPath;
_emmetHelper.updateExtensionsPath(_currentExtensionsPath).then(null, err => vscode.window.showErrorMessage(err));
_emmetHelper.updateExtensionsPath(_currentExtensionsPath).then(null, (err: string) => vscode.window.showErrorMessage(err));
}
}
export const LANGUAGE_MODES: Object = {
export const LANGUAGE_MODES: any = {
'html': ['!', '.', '}', ':', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
'jade': ['!', '.', '}', ':', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
'slim': ['!', '.', '}', ':', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
......@@ -62,7 +62,7 @@ export const MAPPED_MODES: Object = {
'php': 'html'
};
export function isStyleSheet(syntax): boolean {
export function isStyleSheet(syntax: string): boolean {
let stylesheetSyntaxes = ['css', 'scss', 'sass', 'less', 'stylus'];
return (stylesheetSyntaxes.indexOf(syntax) > -1);
}
......@@ -80,7 +80,7 @@ export function validate(allowStylesheet: boolean = true): boolean {
}
export function getMappingForIncludedLanguages(): any {
let finalMappedModes = {};
const finalMappedModes = Object.create(null);
let includeLanguagesConfig = vscode.workspace.getConfiguration('emmet')['includeLanguages'];
let includeLanguages = Object.assign({}, MAPPED_MODES, includeLanguagesConfig ? includeLanguagesConfig : {});
Object.keys(includeLanguages).forEach(syntax => {
......@@ -94,13 +94,13 @@ export function getMappingForIncludedLanguages(): any {
/**
* Get the corresponding emmet mode for given vscode language mode
* Eg: jsx for typescriptreact/javascriptreact or pug for jade
* If the language is not supported by emmet or has been exlcuded via `exlcudeLanguages` setting,
* If the language is not supported by emmet or has been exlcuded via `exlcudeLanguages` setting,
* then nothing is returned
*
* @param language
*
* @param language
* @param exlcudedLanguages Array of language ids that user has chosen to exlcude for emmet
*/
export function getEmmetMode(language: string, excludedLanguages: string[]): string {
export function getEmmetMode(language: string, excludedLanguages: string[]): string | undefined {
if (!language || excludedLanguages.indexOf(language) > -1) {
return;
}
......@@ -120,34 +120,29 @@ export function getEmmetMode(language: string, excludedLanguages: string[]): str
/**
* Parses the given document using emmet parsing modules
* @param document
*/
export function parseDocument(document: vscode.TextDocument, showError: boolean = true): Node {
export function parseDocument(document: vscode.TextDocument, showError: boolean = true): Node | undefined {
let parseContent = isStyleSheet(document.languageId) ? parseStylesheet : parse;
let rootNode: Node;
try {
rootNode = parseContent(new DocumentStreamReader(document));
return parseContent(new DocumentStreamReader(document));
} catch (e) {
if (showError) {
vscode.window.showErrorMessage('Emmet: Failed to parse the file');
}
}
return rootNode;
return undefined;
}
/**
* Returns node corresponding to given position in the given root node
* @param root
* @param position
* @param includeNodeBoundary
*/
export function getNode(root: Node, position: vscode.Position, includeNodeBoundary: boolean = false) {
export function getNode(root: Node | undefined, position: vscode.Position, includeNodeBoundary: boolean = false) {
if (!root) {
return null;
}
let currentNode = root.firstChild;
let foundNode: Node = null;
let foundNode: Node | null = null;
while (currentNode) {
const nodeStart: vscode.Position = currentNode.start;
......@@ -170,14 +165,14 @@ export function getNode(root: Node, position: vscode.Position, includeNodeBounda
* Returns inner range of an html node.
* @param currentNode
*/
export function getInnerRange(currentNode: HtmlNode): vscode.Range {
export function getInnerRange(currentNode: HtmlNode): vscode.Range | undefined {
if (!currentNode.close) {
return;
return undefined;
}
return new vscode.Range(currentNode.open.end, currentNode.close.start);
}
export function getDeepestNode(node: Node): Node {
export function getDeepestNode(node: Node | undefined): Node | undefined {
if (!node || !node.children || node.children.length === 0 || !node.children.find(x => x.type !== 'comment')) {
return node;
}
......@@ -186,6 +181,7 @@ export function getDeepestNode(node: Node): Node {
return getDeepestNode(node.children[i]);
}
}
return undefined;
}
export function findNextWord(propertyValue: string, pos: number): [number, number] {
......@@ -342,10 +338,8 @@ export function getEmmetConfiguration(syntax: string) {
/**
* Itereates by each child, as well as nested child’ children, in their order
* and invokes `fn` for each. If `fn` function returns `false`, iteration stops
* @param {Token} token
* @param {Function} fn
*/
export function iterateCSSToken(token: CssToken, fn) {
export function iterateCSSToken(token: CssToken, fn: (x: any) => any) {
for (let i = 0, il = token.size; i < il; i++) {
if (fn(token.item(i)) === false || iterateCSSToken(token.item(i), fn) === false) {
return false;
......@@ -355,21 +349,16 @@ export function iterateCSSToken(token: CssToken, fn) {
/**
* Returns `name` CSS property from given `rule`
* @param {Node} rule
* @param {String} name
* @return {Property}
*/
export function getCssPropertyFromRule(rule, name): Property {
return rule.children.find(node => node.type === 'property' && node.name === name);
export function getCssPropertyFromRule(rule: Rule, name: string): Property | undefined {
return rule.children.find(node => node.type === 'property' && node.name === name) as Property;
}
/**
* Returns css property under caret in given editor or `null` if such node cannot
* be found
* @param {TextEditor} editor
* @return {Property}
*/
export function getCssPropertyFromDocument(editor: vscode.TextEditor, position: vscode.Position): Property {
export function getCssPropertyFromDocument(editor: vscode.TextEditor, position: vscode.Position): Property | undefined {
const rootNode = parseDocument(editor.document);
const node = getNode(rootNode, position);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册