Enforce node boundaries in places where it matters (#53192)
* Enfoce node boundries in places where it matters * Flip defualt emmet includeBoundries to true Explicitly set getNode's include boundries where needed * Remove defualt papameter * Add update image boundry tests * Add tests for bondries on some of the tag actions * Rest of tag tests
This commit is contained in:
parent
d8836913b4
commit
4f870afa58
11 changed files with 137 additions and 15 deletions
|
@ -61,7 +61,7 @@ function balance(out: boolean) {
|
|||
}
|
||||
|
||||
function getRangeToBalanceOut(document: vscode.TextDocument, selection: vscode.Selection, rootNode: HtmlNode): vscode.Selection {
|
||||
let nodeToBalance = getHtmlNode(document, rootNode, selection.start);
|
||||
let nodeToBalance = getHtmlNode(document, rootNode, selection.start, false);
|
||||
if (!nodeToBalance) {
|
||||
return selection;
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ function getRangesToReplace(document: vscode.TextDocument, selection: vscode.Sel
|
|||
let endNodeToUpdate: Node | null;
|
||||
|
||||
if (selection.isEmpty) {
|
||||
startNodeToUpdate = endNodeToUpdate = getNode(rootNode, selection.start);
|
||||
startNodeToUpdate = endNodeToUpdate = getNode(rootNode, selection.start, true);
|
||||
} else {
|
||||
startNodeToUpdate = getNode(rootNode, selection.start, true);
|
||||
endNodeToUpdate = getNode(rootNode, selection.end, true);
|
||||
|
|
|
@ -38,7 +38,7 @@ export function removeTag() {
|
|||
|
||||
function getRangeToRemove(editor: vscode.TextEditor, rootNode: HtmlNode, selection: vscode.Selection, indentInSpaces: string): vscode.Range[] {
|
||||
|
||||
let nodeToUpdate = getHtmlNode(editor.document, rootNode, selection.start);
|
||||
let nodeToUpdate = getHtmlNode(editor.document, rootNode, selection.start, true);
|
||||
if (!nodeToUpdate) {
|
||||
return [];
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import { getDeepestNode, findNextWord, findPrevWord, getHtmlNode } from './util'
|
|||
import { HtmlNode } from 'EmmetNode';
|
||||
|
||||
export function nextItemHTML(selectionStart: vscode.Position, selectionEnd: vscode.Position, editor: vscode.TextEditor, rootNode: HtmlNode): vscode.Selection | undefined {
|
||||
let currentNode = getHtmlNode(editor.document, rootNode, selectionEnd);
|
||||
let currentNode = getHtmlNode(editor.document, rootNode, selectionEnd, false);
|
||||
let nextNode: HtmlNode | undefined = undefined;
|
||||
|
||||
if (!currentNode) {
|
||||
|
@ -54,7 +54,7 @@ export function nextItemHTML(selectionStart: vscode.Position, selectionEnd: vsco
|
|||
}
|
||||
|
||||
export function prevItemHTML(selectionStart: vscode.Position, selectionEnd: vscode.Position, editor: vscode.TextEditor, rootNode: HtmlNode): vscode.Selection | undefined {
|
||||
let currentNode = getHtmlNode(editor.document, rootNode, selectionStart);
|
||||
let currentNode = getHtmlNode(editor.document, rootNode, selectionStart, false);
|
||||
let prevNode: HtmlNode | undefined = undefined;
|
||||
|
||||
if (!currentNode) {
|
||||
|
|
|
@ -51,7 +51,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 | undefined {
|
||||
let currentNode = <CssNode>getNode(rootNode, startOffset);
|
||||
let currentNode = <CssNode>getNode(rootNode, startOffset, false);
|
||||
if (!currentNode) {
|
||||
currentNode = rootNode;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ export function splitJoinTag() {
|
|||
|
||||
return editor.edit(editBuilder => {
|
||||
editor.selections.reverse().forEach(selection => {
|
||||
let nodeToUpdate = getHtmlNode(editor.document, rootNode, selection.start);
|
||||
let nodeToUpdate = getHtmlNode(editor.document, rootNode, selection.start, true);
|
||||
if (nodeToUpdate) {
|
||||
let textEdit = getRangesToReplace(editor.document, nodeToUpdate);
|
||||
editBuilder.replace(textEdit.range, textEdit.newText);
|
||||
|
|
|
@ -56,6 +56,16 @@ suite('Tests for Emmet: Reflect CSS Value command', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('Reflect Css Value in css file, selecting entire property', function (): any {
|
||||
return withRandomFileEditor(cssContents, '.css', (editor, doc) => {
|
||||
editor.selections = [new Selection(5, 2, 5, 32)];
|
||||
return reflectCssValue().then(() => {
|
||||
assert.equal(doc.getText(), cssContents.replace(/\(50deg\)/g, '(20deg)'));
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Reflect Css Value in html file', function (): any {
|
||||
return withRandomFileEditor(htmlContents, '.html', (editor, doc) => {
|
||||
editor.selections = [new Selection(7, 20, 7, 20)];
|
||||
|
@ -66,4 +76,14 @@ suite('Tests for Emmet: Reflect CSS Value command', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('Reflect Css Value in html file, selecting entire property', function (): any {
|
||||
return withRandomFileEditor(htmlContents, '.html', (editor, doc) => {
|
||||
editor.selections = [new Selection(7, 4, 7, 34)];
|
||||
return reflectCssValue().then(() => {
|
||||
assert.equal(doc.getText(), htmlContents.replace(/\(50deg\)/g, '(20deg)'));
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
|
@ -66,6 +66,31 @@ suite('Tests for Emmet actions on html tags', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// #region update tag
|
||||
test('update tag with entire node selected', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
<ul>
|
||||
<li><section>Hello</section></li>
|
||||
<li><span>There</span></li>
|
||||
<section><li><span>Bye</span></li></section>
|
||||
</ul>
|
||||
<span/>
|
||||
</div>
|
||||
`;
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(3, 7, 3, 25),
|
||||
new Selection(5, 3, 5, 39),
|
||||
];
|
||||
|
||||
return updateTag('section')!.then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('update tag with template', () => {
|
||||
const expectedContents = `
|
||||
<script type="text/template">
|
||||
|
@ -89,8 +114,9 @@ suite('Tests for Emmet actions on html tags', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
// #endregion
|
||||
|
||||
|
||||
// #region remove tag
|
||||
test('remove tag with mutliple cursors', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
|
@ -116,6 +142,31 @@ suite('Tests for Emmet actions on html tags', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('remove tag with boundary conditions', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
<ul>
|
||||
<li>Hello</li>
|
||||
<li><span>There</span></li>
|
||||
<li><span>Bye</span></li>
|
||||
</ul>
|
||||
<span/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(3, 7, 3, 25),
|
||||
new Selection(5, 3, 5, 39),
|
||||
];
|
||||
|
||||
return removeTag()!.then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('remove tag with template', () => {
|
||||
const expectedContents = `
|
||||
|
@ -139,7 +190,9 @@ suite('Tests for Emmet actions on html tags', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
// #endregion
|
||||
|
||||
// #region split/join tag
|
||||
test('split/join tag with mutliple cursors', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
|
@ -164,6 +217,30 @@ suite('Tests for Emmet actions on html tags', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('split/join tag with boundary selection', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
<ul>
|
||||
<li><span/></li>
|
||||
<li><span>There</span></li>
|
||||
<div><li><span>Bye</span></li></div>
|
||||
</ul>
|
||||
<span></span>
|
||||
</div>
|
||||
`;
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(3, 7, 3, 25), // join tag
|
||||
new Selection(7, 2, 7, 9), // split tag
|
||||
];
|
||||
|
||||
return splitJoinTag()!.then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('split/join tag with templates', () => {
|
||||
const expectedContents = `
|
||||
<script type="text/template">
|
||||
|
@ -214,7 +291,9 @@ suite('Tests for Emmet actions on html tags', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
// #endregion
|
||||
|
||||
// #region match tag
|
||||
test('match tag with mutliple cursors', () => {
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
|
@ -265,6 +344,9 @@ suite('Tests for Emmet actions on html tags', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region merge lines
|
||||
test('merge lines of tag with children when empty selection', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
|
@ -284,6 +366,25 @@ suite('Tests for Emmet actions on html tags', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('merge lines of tag with children when full node selection', () => {
|
||||
const expectedContents = `
|
||||
<div class="hello">
|
||||
<ul><li><span>Hello</span></li><li><span>There</span></li><div><li><span>Bye</span></li></div></ul>
|
||||
<span/>
|
||||
</div>
|
||||
`;
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
new Selection(2, 3, 6, 7)
|
||||
];
|
||||
|
||||
return mergeLines()!.then(() => {
|
||||
assert.equal(doc.getText(), expectedContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('merge lines is no-op when start and end nodes are on the same line', () => {
|
||||
return withRandomFileEditor(contents, 'html', (editor, doc) => {
|
||||
editor.selections = [
|
||||
|
@ -298,5 +399,6 @@ suite('Tests for Emmet actions on html tags', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
// #endregion
|
||||
});
|
||||
|
||||
|
|
|
@ -73,13 +73,13 @@ function updateImageSizeHTML(editor: TextEditor, position: Position): Promise<Te
|
|||
function updateImageSizeStyleTag(editor: TextEditor, position: Position): Promise<TextEdit[] | undefined> {
|
||||
const getPropertyInsiderStyleTag = (editor: TextEditor): Property | null => {
|
||||
const rootNode = parseDocument(editor.document);
|
||||
const currentNode = <HtmlNode>getNode(rootNode, position);
|
||||
const currentNode = <HtmlNode>getNode(rootNode, position, true);
|
||||
if (currentNode && currentNode.name === 'style'
|
||||
&& currentNode.open.end.isBefore(position)
|
||||
&& currentNode.close.start.isAfter(position)) {
|
||||
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);
|
||||
const node = getNode(rootNode, position, true);
|
||||
return (node && node.type === 'property') ? <Property>node : null;
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -30,7 +30,7 @@ export function updateTag(tagName: string): Thenable<boolean> | undefined {
|
|||
}
|
||||
|
||||
function getRangesToUpdate(editor: vscode.TextEditor, selection: vscode.Selection, rootNode: HtmlNode): vscode.Range[] {
|
||||
let nodeToUpdate = getHtmlNode(editor.document, rootNode, selection.start);
|
||||
let nodeToUpdate = getHtmlNode(editor.document, rootNode, selection.start, true);
|
||||
if (!nodeToUpdate) {
|
||||
return [];
|
||||
}
|
||||
|
|
|
@ -285,7 +285,7 @@ function findClosingCommentAfterPosition(document: vscode.TextDocument, position
|
|||
/**
|
||||
* Returns node corresponding to given position in the given root node
|
||||
*/
|
||||
export function getNode(root: Node | undefined, position: vscode.Position, includeNodeBoundary: boolean = false) {
|
||||
export function getNode(root: Node | undefined, position: vscode.Position, includeNodeBoundary: boolean) {
|
||||
if (!root) {
|
||||
return null;
|
||||
}
|
||||
|
@ -310,7 +310,7 @@ export function getNode(root: Node | undefined, position: vscode.Position, inclu
|
|||
return foundNode;
|
||||
}
|
||||
|
||||
export function getHtmlNode(document: vscode.TextDocument, root: Node | undefined, position: vscode.Position, includeNodeBoundary: boolean = false): HtmlNode | undefined {
|
||||
export function getHtmlNode(document: vscode.TextDocument, root: Node | undefined, position: vscode.Position, includeNodeBoundary: boolean): HtmlNode | undefined {
|
||||
let currentNode = <HtmlNode>getNode(root, position, includeNodeBoundary);
|
||||
if (!currentNode) { return; }
|
||||
|
||||
|
@ -532,7 +532,7 @@ export function getCssPropertyFromRule(rule: Rule, name: string): Property | und
|
|||
*/
|
||||
export function getCssPropertyFromDocument(editor: vscode.TextEditor, position: vscode.Position): Property | null | undefined {
|
||||
const rootNode = parseDocument(editor.document);
|
||||
const node = getNode(rootNode, position);
|
||||
const node = getNode(rootNode, position, true);
|
||||
|
||||
if (isStyleSheet(editor.document.languageId)) {
|
||||
return node && node.type === 'property' ? <Property>node : null;
|
||||
|
@ -545,7 +545,7 @@ export function getCssPropertyFromDocument(editor: vscode.TextEditor, position:
|
|||
&& htmlNode.close.start.isAfter(position)) {
|
||||
let buffer = new DocumentStreamReader(editor.document, htmlNode.start, new vscode.Range(htmlNode.start, htmlNode.end));
|
||||
let rootNode = parseStylesheet(buffer);
|
||||
const node = getNode(rootNode, position);
|
||||
const node = getNode(rootNode, position, true);
|
||||
return (node && node.type === 'property') ? <Property>node : null;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue