diff --git a/extensions/emmet/src/abbreviationActions.ts b/extensions/emmet/src/abbreviationActions.ts index 0d474f1c3f1..0224594a33e 100644 --- a/extensions/emmet/src/abbreviationActions.ts +++ b/extensions/emmet/src/abbreviationActions.ts @@ -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 { +export function expandEmmetAbbreviation(args: any): Thenable { const syntax = getSyntaxFromArgs(args); if (!syntax || !validate()) { return fallbackTab(); @@ -179,7 +179,7 @@ export function expandEmmetAbbreviation(args): Thenable { }); } -function fallbackTab(): Thenable { +function fallbackTab(): Thenable | 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 = 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[] = []; 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.'); diff --git a/extensions/emmet/src/balance.ts b/extensions/emmet/src/balance.ts index 22474be22dd..2d4917994ae 100644 --- a/extensions/emmet/src/balance.ts +++ b/extensions/emmet/src/balance.ts @@ -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 = 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 = 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 = 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) diff --git a/extensions/emmet/src/bufferStream.ts b/extensions/emmet/src/bufferStream.ts index a7a6cd5a0d2..3ab9d9c2eab 100644 --- a/extensions/emmet/src/bufferStream.ts +++ b/extensions/emmet/src/bufferStream.ts @@ -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 eat with the given argument, until it * fails. Returns true 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); diff --git a/extensions/emmet/src/defaultCompletionProvider.ts b/extensions/emmet/src/defaultCompletionProvider.ts index 1c825fc21c3..ad6f548bd9f 100644 --- a/extensions/emmet/src/defaultCompletionProvider.ts +++ b/extensions/emmet/src/defaultCompletionProvider.ts @@ -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 { + public provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Thenable | 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.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; } diff --git a/extensions/emmet/src/editPoint.ts b/extensions/emmet/src/editPoint.ts index 2028d3295b3..afd73a4ddcb 100644 --- a/extensions/emmet/src/editPoint.ts +++ b/extensions/emmet/src/editPoint.ts @@ -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; diff --git a/extensions/emmet/src/evaluateMathExpression.ts b/extensions/emmet/src/evaluateMathExpression.ts index cf7b84d41cc..8a7de6ca423 100644 --- a/extensions/emmet/src/evaluateMathExpression.ts +++ b/extensions/emmet/src/evaluateMathExpression.ts @@ -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 => { diff --git a/extensions/emmet/src/imageSizeHelper.ts b/extensions/emmet/src/imageSizeHelper.ts index 2c8f1de5945..1a88f35c110 100644 --- a/extensions/emmet/src/imageSizeHelper.ts +++ b/extensions/emmet/src/imageSizeHelper.ts @@ -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; diff --git a/extensions/emmet/src/selectItemHTML.ts b/extensions/emmet/src/selectItemHTML.ts index 1b234e2f349..23647722bcc 100644 --- a/extensions/emmet/src/selectItemHTML.ts +++ b/extensions/emmet/src/selectItemHTML.ts @@ -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 = 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 = 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 = (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; diff --git a/extensions/emmet/src/selectItemStylesheet.ts b/extensions/emmet/src/selectItemStylesheet.ts index 09f0d6103a9..367e0320837 100644 --- a/extensions/emmet/src/selectItemStylesheet.ts +++ b/extensions/emmet/src/selectItemStylesheet.ts @@ -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 = getNode(rootNode, endOffset, true); if (!currentNode) { currentNode = 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 = 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; } diff --git a/extensions/emmet/src/splitJoinTag.ts b/extensions/emmet/src/splitJoinTag.ts index 382bd1841b0..67a403bce29 100644 --- a/extensions/emmet/src/splitJoinTag.ts +++ b/extensions/emmet/src/splitJoinTag.ts @@ -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 = 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 = 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 = 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)); diff --git a/extensions/emmet/src/toggleComment.ts b/extensions/emmet/src/toggleComment.ts index 1140473881d..e15dbd0c984 100644 --- a/extensions/emmet/src/toggleComment.ts +++ b/extensions/emmet/src/toggleComment.ts @@ -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 = ''; -export function toggleComment(): Thenable { - let editor = vscode.window.activeTextEditor; - if (!editor) { - vscode.window.showInformationMessage('No editor is active'); +export function toggleComment(): Thenable | 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; diff --git a/extensions/emmet/src/updateImageSize.ts b/extensions/emmet/src/updateImageSize.ts index ea57e099e8a..06fbd45a15b 100644 --- a/extensions/emmet/src/updateImageSize.ts +++ b/extensions/emmet/src/updateImageSize.ts @@ -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 { +function updateImageSizeHTML(editor: TextEditor, position: Position): Promise { const src = getImageSrcHTML(getImageHTMLNode(editor, position)); if (!src) { @@ -70,7 +69,7 @@ function updateImageSizeHTML(editor: TextEditor, position: Position): Promise { - let getPropertyInsiderStyleTag = (editor) => { + const getPropertyInsiderStyleTag = (editor: TextEditor): Property | undefined => { const rootNode = parseDocument(editor.document); const currentNode = 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') ? node : null; + return (node && node.type === '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 { +function updateImageSizeCSS(editor: TextEditor, position: Position, fetchNode: (editor: TextEditor, position: Position) => Property | undefined): Promise { const src = getImageSrcCSS(fetchNode(editor, position), position); @@ -141,10 +140,8 @@ function getImageCSSNode(editor: TextEditor, position: Position): Property { /** * Returns image source from given 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)); diff --git a/extensions/emmet/src/updateTag.ts b/extensions/emmet/src/updateTag.ts index 66a23e074b6..b62f167d70a 100644 --- a/extensions/emmet/src/updateTag.ts +++ b/extensions/emmet/src/updateTag.ts @@ -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 { - let editor = vscode.window.activeTextEditor; - if (!validate(false)) { +export function updateTag(tagName: string): Thenable | undefined { + if (!validate(false) || !vscode.window.activeTextEditor) { return; } + let editor = vscode.window.activeTextEditor; let rootNode = parseDocument(editor.document); if (!rootNode) { return; } - let rangesToUpdate = []; + let rangesToUpdate: vscode.Range[] = []; editor.selections.reverse().forEach(selection => { rangesToUpdate = rangesToUpdate.concat(getRangesToUpdate(editor, selection, rootNode)); }); diff --git a/extensions/emmet/src/util.ts b/extensions/emmet/src/util.ts index 3903c73190d..450aa136143 100644 --- a/extensions/emmet/src/util.ts +++ b/extensions/emmet/src/util.ts @@ -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);