diff --git a/extensions/emmet/src/toggleComment.ts b/extensions/emmet/src/toggleComment.ts index 06f11b2d15f..1afb8fe2239 100644 --- a/extensions/emmet/src/toggleComment.ts +++ b/extensions/emmet/src/toggleComment.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { getNodesInBetween, getNode, getHtmlNode, parseDocument, sameNodes, isStyleSheet, validate } from './util'; -import { Node, Stylesheet, Rule } from 'EmmetNode'; +import { getNodesInBetween, getFlatNode, getHtmlFlatNode, sameNodes, isStyleSheet, validate, offsetRangeToVsRange, offsetRangeToSelection } from './util'; +import { Node, Stylesheet, Rule } from 'EmmetFlatNode'; import parseStylesheet from '@emmetio/css-parser'; -import { DocumentStreamReader } from './bufferStream'; +import { getRootNode } from './parseDocument'; let startCommentStylesheet: string; let endCommentStylesheet: string; @@ -21,7 +21,7 @@ export function toggleComment(): Thenable | undefined { setupCommentSpacing(); const editor = vscode.window.activeTextEditor; - let rootNode = parseDocument(editor.document); + const rootNode = getRootNode(editor.document, true); if (!rootNode) { return; } @@ -29,7 +29,7 @@ export function toggleComment(): Thenable | undefined { return editor.edit(editBuilder => { let allEdits: vscode.TextEdit[][] = []; editor.selections.reverse().forEach(selection => { - let edits = isStyleSheet(editor.document.languageId) ? toggleCommentStylesheet(selection, rootNode) : toggleCommentHTML(editor.document, selection, rootNode!); + const edits = isStyleSheet(editor.document.languageId) ? toggleCommentStylesheet(editor.document, selection, rootNode) : toggleCommentHTML(editor.document, selection, rootNode!); if (edits.length > 0) { allEdits.push(edits); } @@ -55,21 +55,25 @@ export function toggleComment(): Thenable | undefined { function toggleCommentHTML(document: vscode.TextDocument, selection: vscode.Selection, rootNode: Node): vscode.TextEdit[] { const selectionStart = selection.isReversed ? selection.active : selection.anchor; const selectionEnd = selection.isReversed ? selection.anchor : selection.active; + const selectionStartOffset = document.offsetAt(selectionStart); + const selectionEndOffset = document.offsetAt(selectionEnd); + const documentText = document.getText(); - let startNode = getHtmlNode(document, rootNode, selectionStart, true); - let endNode = getHtmlNode(document, rootNode, selectionEnd, true); + const startNode = getHtmlFlatNode(documentText, rootNode, selectionStartOffset, true); + const endNode = getHtmlFlatNode(documentText, rootNode, selectionEndOffset, true); if (!startNode || !endNode) { return []; } if (sameNodes(startNode, endNode) && startNode.name === 'style' - && startNode.open.end.isBefore(selectionStart) - && startNode.close.start.isAfter(selectionEnd)) { - let buffer = new DocumentStreamReader(document, startNode.open.end, new vscode.Range(startNode.open.end, startNode.close.start)); - let cssRootNode = parseStylesheet(buffer); - - return toggleCommentStylesheet(selection, cssRootNode); + && startNode.open && startNode.close + && startNode.open.end < selectionStartOffset + && startNode.close.start > selectionEndOffset) { + const buffer = ' '.repeat(startNode.open.end) + + documentText.substring(startNode.open.end, startNode.close.start); + const cssRootNode = parseStylesheet(buffer); + return toggleCommentStylesheet(document, selection, cssRootNode); } let allNodes: Node[] = getNodesInBetween(startNode, endNode); @@ -84,8 +88,8 @@ function toggleCommentHTML(document: vscode.TextDocument, selection: vscode.Sele } - edits.push(new vscode.TextEdit(new vscode.Range(allNodes[0].start, allNodes[0].start), startCommentHTML)); - edits.push(new vscode.TextEdit(new vscode.Range(allNodes[allNodes.length - 1].end, allNodes[allNodes.length - 1].end), endCommentHTML)); + edits.push(new vscode.TextEdit(offsetRangeToVsRange(document, allNodes[0].start, allNodes[0].start), startCommentHTML)); + edits.push(new vscode.TextEdit(offsetRangeToVsRange(document, allNodes[allNodes.length - 1].end, allNodes[allNodes.length - 1].end), endCommentHTML)); return edits; } @@ -95,10 +99,8 @@ function getRangesToUnCommentHTML(node: Node, document: vscode.TextDocument): vs // If current node is commented, then uncomment and return if (node.type === 'comment') { - - unCommentTextEdits.push(new vscode.TextEdit(new vscode.Range(node.start, node.start.translate(0, startCommentHTML.length)), '')); - unCommentTextEdits.push(new vscode.TextEdit(new vscode.Range(node.end.translate(0, -endCommentHTML.length), node.end), '')); - + unCommentTextEdits.push(new vscode.TextEdit(offsetRangeToVsRange(document, node.start, node.start + startCommentHTML.length), '')); + unCommentTextEdits.push(new vscode.TextEdit(offsetRangeToVsRange(document, node.end - endCommentHTML.length, node.end), '')); return unCommentTextEdits; } @@ -110,32 +112,34 @@ function getRangesToUnCommentHTML(node: Node, document: vscode.TextDocument): vs return unCommentTextEdits; } -function toggleCommentStylesheet(selection: vscode.Selection, rootNode: Stylesheet): vscode.TextEdit[] { - let selectionStart = selection.isReversed ? selection.active : selection.anchor; - let selectionEnd = selection.isReversed ? selection.anchor : selection.active; +function toggleCommentStylesheet(document: vscode.TextDocument, selection: vscode.Selection, rootNode: Stylesheet): vscode.TextEdit[] { + const selectionStart = selection.isReversed ? selection.active : selection.anchor; + const selectionEnd = selection.isReversed ? selection.anchor : selection.active; + let selectionStartOffset = document.offsetAt(selectionStart); + let selectionEndOffset = document.offsetAt(selectionEnd); - let startNode = getNode(rootNode, selectionStart, true); - let endNode = getNode(rootNode, selectionEnd, true); + const startNode = getFlatNode(rootNode, selectionStartOffset, true); + const endNode = getFlatNode(rootNode, selectionEndOffset, true); if (!selection.isEmpty) { - selectionStart = adjustStartNodeCss(startNode, selectionStart, rootNode); - selectionEnd = adjustEndNodeCss(endNode, selectionEnd, rootNode); - selection = new vscode.Selection(selectionStart, selectionEnd); + selectionStartOffset = adjustStartNodeCss(startNode, selectionStartOffset, rootNode); + selectionEndOffset = adjustEndNodeCss(endNode, selectionEndOffset, rootNode); + selection = offsetRangeToSelection(document, selectionStartOffset, selectionEndOffset); } else if (startNode) { - selectionStart = startNode.start; - selectionEnd = startNode.end; - selection = new vscode.Selection(selectionStart, selectionEnd); + selectionStartOffset = startNode.start; + selectionEndOffset = startNode.end; + selection = offsetRangeToSelection(document, selectionStartOffset, selectionEndOffset); } // Uncomment the comments that intersect with the selection. let rangesToUnComment: vscode.Range[] = []; let edits: vscode.TextEdit[] = []; rootNode.comments.forEach(comment => { - let commentRange = new vscode.Range(comment.start, comment.end); + const commentRange = offsetRangeToVsRange(document, comment.start, comment.end); if (selection.intersection(commentRange)) { rangesToUnComment.push(commentRange); - edits.push(new vscode.TextEdit(new vscode.Range(comment.start, comment.start.translate(0, startCommentStylesheet.length)), '')); - edits.push(new vscode.TextEdit(new vscode.Range(comment.end.translate(0, -endCommentStylesheet.length), comment.end), '')); + edits.push(new vscode.TextEdit(offsetRangeToVsRange(document, comment.start, comment.start + startCommentStylesheet.length), '')); + edits.push(new vscode.TextEdit(offsetRangeToVsRange(document, comment.end - endCommentStylesheet.length, comment.end), '')); } }); @@ -164,16 +168,15 @@ function setupCommentSpacing() { } } -function adjustStartNodeCss(node: Node | null, pos: vscode.Position, rootNode: Stylesheet): vscode.Position { +function adjustStartNodeCss(node: Node | undefined, offset: number, rootNode: Stylesheet): number { for (const comment of rootNode.comments) { - let commentRange = new vscode.Range(comment.start, comment.end); - if (commentRange.contains(pos)) { - return pos; + if (comment.start <= offset && offset <= comment.end) { + return offset; } } if (!node) { - return pos; + return offset; } if (node.type === 'property') { @@ -181,32 +184,31 @@ function adjustStartNodeCss(node: Node | null, pos: vscode.Position, rootNode: S } const rule = node; - if (pos.isBefore(rule.contentStartToken.end) || !rule.firstChild) { + if (offset < rule.contentStartToken.end || !rule.firstChild) { return rule.start; } - if (pos.isBefore(rule.firstChild.start)) { - return pos; + if (offset < rule.firstChild.start) { + return offset; } let newStartNode = rule.firstChild; - while (newStartNode.nextSibling && pos.isAfter(newStartNode.end)) { + while (newStartNode.nextSibling && offset > newStartNode.end) { newStartNode = newStartNode.nextSibling; } return newStartNode.start; } -function adjustEndNodeCss(node: Node | null, pos: vscode.Position, rootNode: Stylesheet): vscode.Position { +function adjustEndNodeCss(node: Node | undefined, offset: number, rootNode: Stylesheet): number { for (const comment of rootNode.comments) { - let commentRange = new vscode.Range(comment.start, comment.end); - if (commentRange.contains(pos)) { - return pos; + if (comment.start <= offset && offset <= comment.end) { + return offset; } } if (!node) { - return pos; + return offset; } if (node.type === 'property') { @@ -214,16 +216,16 @@ function adjustEndNodeCss(node: Node | null, pos: vscode.Position, rootNode: Sty } const rule = node; - if (pos.isEqual(rule.contentEndToken.end) || !rule.firstChild) { + if (offset === rule.contentEndToken.end || !rule.firstChild) { return rule.end; } - if (pos.isAfter(rule.children[rule.children.length - 1].end)) { - return pos; + if (offset > rule.children[rule.children.length - 1].end) { + return offset; } let newEndNode = rule.children[rule.children.length - 1]; - while (newEndNode.previousSibling && pos.isBefore(newEndNode.start)) { + while (newEndNode.previousSibling && offset < newEndNode.start) { newEndNode = newEndNode.previousSibling; } diff --git a/extensions/emmet/src/util.ts b/extensions/emmet/src/util.ts index e83aad0820e..e5653659e01 100644 --- a/extensions/emmet/src/util.ts +++ b/extensions/emmet/src/util.ts @@ -565,7 +565,7 @@ export function findPrevWord(propertyValue: string, pos: number): [number | unde return [newSelectionStart, newSelectionEnd]; } -export function getNodesInBetween(node1: Node, node2: Node): Node[] { +export function getNodesInBetween(node1: FlatNode, node2: FlatNode): FlatNode[] { // Same node if (sameNodes(node1, node2)) { return [node1]; @@ -574,50 +574,46 @@ export function getNodesInBetween(node1: Node, node2: Node): Node[] { // Not siblings if (!sameNodes(node1.parent, node2.parent)) { // node2 is ancestor of node1 - if (node2.start.isBefore(node1.start)) { + if (node2.start < node1.start) { return [node2]; } // node1 is ancestor of node2 - if (node2.start.isBefore(node1.end)) { + if (node2.start < node1.end) { return [node1]; } // Get the highest ancestor of node1 that should be commented - while (node1.parent && node1.parent.end.isBefore(node2.start)) { + while (node1.parent && node1.parent.end < node2.start) { node1 = node1.parent; } // Get the highest ancestor of node2 that should be commented - while (node2.parent && node2.parent.start.isAfter(node1.start)) { + while (node2.parent && node2.parent.start > node1.start) { node2 = node2.parent; } } - const siblings: Node[] = []; - let currentNode = node1; + const siblings: FlatNode[] = []; + let currentNode: FlatNode | undefined = node1; const position = node2.end; - while (currentNode && position.isAfter(currentNode.start)) { + while (currentNode && position > currentNode.start) { siblings.push(currentNode); currentNode = currentNode.nextSibling; } return siblings; } -function samePositions(pos1: vscode.Position | undefined, pos2: vscode.Position | undefined): boolean { - if (!pos1 && !pos2) { - return true; - } else if (pos1 && pos2 && pos1.isEqual(pos2)) { +export function sameNodes(node1: FlatNode | undefined, node2: FlatNode | undefined): boolean { + // return true if they're both undefined + if (!node1 && !node2) { return true; } - return false; -} - -export function sameNodes(node1: Node, node2: Node): boolean { + // return false if only one of them is undefined if (!node1 || !node2) { return false; } - return samePositions(node1.start, node2.start) && samePositions(node1.end, node2.end); + return node1.start === node2.start && node1.end === node2.end; } export function getEmmetConfiguration(syntax: string) {