Emmet refactor toggle comment (#113557)
This commit is contained in:
parent
f1ea605a69
commit
b5cd082cd4
|
@ -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<boolean> | 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<boolean> | undefined {
|
|||
return editor.edit(editBuilder => {
|
||||
let allEdits: vscode.TextEdit[][] = [];
|
||||
editor.selections.reverse().forEach(selection => {
|
||||
let edits = isStyleSheet(editor.document.languageId) ? toggleCommentStylesheet(selection, <Stylesheet>rootNode) : toggleCommentHTML(editor.document, selection, rootNode!);
|
||||
const edits = isStyleSheet(editor.document.languageId) ? toggleCommentStylesheet(editor.document, selection, <Stylesheet>rootNode) : toggleCommentHTML(editor.document, selection, rootNode!);
|
||||
if (edits.length > 0) {
|
||||
allEdits.push(edits);
|
||||
}
|
||||
|
@ -55,21 +55,25 @@ export function toggleComment(): Thenable<boolean> | 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 = <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 = <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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in a new issue