From a1b377e423018836b7e8a1d1733b8044136ed59c Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Wed, 6 Dec 2017 15:04:29 -0800 Subject: [PATCH] Skip emmet when open tag is not closed even when no parent node #35128 --- extensions/emmet/src/abbreviationActions.ts | 44 ++++---- .../emmet/src/test/abbreviationAction.test.ts | 102 +++++++++++++++--- 2 files changed, 113 insertions(+), 33 deletions(-) diff --git a/extensions/emmet/src/abbreviationActions.ts b/extensions/emmet/src/abbreviationActions.ts index 50951f4333c..480699eafe1 100644 --- a/extensions/emmet/src/abbreviationActions.ts +++ b/extensions/emmet/src/abbreviationActions.ts @@ -217,12 +217,12 @@ function fallbackTab(): Thenable { * @param abbreviationRange The range of the abbreviation for which given position is being validated */ export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocument, currentNode: Node | null, syntax: string, position: vscode.Position, abbreviationRange: vscode.Range): boolean { - // Continue validation only if the file was parse-able and the currentNode has been found - if (!currentNode) { - return true; - } - if (isStyleSheet(syntax)) { + // Continue validation only if the file was parse-able and the currentNode has been found + if (!currentNode) { + return true; + } + // If current node is a rule or at-rule, then perform additional checks to ensure // emmet suggestions are not provided in the rule selector if (currentNode.type !== 'rule' && currentNode.type !== 'at-rule') { @@ -253,24 +253,28 @@ export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocumen const endAngle = '>'; const escape = '\\'; const currentHtmlNode = currentNode; - const innerRange = getInnerRange(currentHtmlNode); + let start = new vscode.Position(0, 0); - // Fix for https://github.com/Microsoft/vscode/issues/28829 - if (!innerRange || !innerRange.contains(position)) { - return false; - } + if (currentHtmlNode) { + const innerRange = getInnerRange(currentHtmlNode); - // Fix for https://github.com/Microsoft/vscode/issues/35128 - // Find the position up till where we will backtrack looking for unescaped < or > - // to decide if current position is valid for emmet expansion - let start = innerRange.start; - let lastChildBeforePosition = currentHtmlNode.firstChild; - while (lastChildBeforePosition) { - if (lastChildBeforePosition.end.isAfter(position)) { - break; + // Fix for https://github.com/Microsoft/vscode/issues/28829 + if (!innerRange || !innerRange.contains(position)) { + return false; + } + + // Fix for https://github.com/Microsoft/vscode/issues/35128 + // Find the position up till where we will backtrack looking for unescaped < or > + // to decide if current position is valid for emmet expansion + start = innerRange.start; + let lastChildBeforePosition = currentHtmlNode.firstChild; + while (lastChildBeforePosition) { + if (lastChildBeforePosition.end.isAfter(position)) { + break; + } + start = lastChildBeforePosition.end; + lastChildBeforePosition = lastChildBeforePosition.nextSibling; } - start = lastChildBeforePosition.end; - lastChildBeforePosition = lastChildBeforePosition.nextSibling; } let textToBackTrack = document.getText(new vscode.Range(start.line, start.character, abbreviationRange.start.line, abbreviationRange.start.character)); diff --git a/extensions/emmet/src/test/abbreviationAction.test.ts b/extensions/emmet/src/test/abbreviationAction.test.ts index dee4ae6b878..101da90685c 100644 --- a/extensions/emmet/src/test/abbreviationAction.test.ts +++ b/extensions/emmet/src/test/abbreviationAction.test.ts @@ -42,7 +42,7 @@ const scssContents = ` p40 } } -` +`; const htmlContents = ` @@ -128,6 +128,36 @@ suite('Tests for Expand Abbreviations (HTML)', () => { return testHtmlCompletionProvider(new Selection(3, 23, 3, 23), 'img', '\"\"'); }); + test('Expand snippets when no parent node (HTML)', () => { + return withRandomFileEditor('img', 'html', (editor, doc) => { + editor.selection = new Selection(0, 3, 0, 3); + return expandEmmetAbbreviation(null).then(() => { + assert.equal(editor.document.getText(), '\"\"'); + return Promise.resolve(); + }); + }); + }); + + test('Expand snippets when no parent node in completion list (HTML)', () => { + return withRandomFileEditor('img', 'html', (editor, doc) => { + editor.selection = new Selection(0, 3, 0, 3); + const cancelSrc = new CancellationTokenSource(); + const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token); + if (!completionPromise) { + assert.equal(!completionPromise, false, `Got unexpected undefined instead of a completion promise`); + return Promise.resolve(); + } + return completionPromise.then(completionList => { + assert.equal(completionList && completionList.items && completionList.items.length > 0, true); + if (completionList) { + assert.equal(completionList.items[0].label, 'img'); + assert.equal((completionList.items[0].documentation || '').replace(/\|/g, ''), '\"\"'); + } + return Promise.resolve(); + }); + }); + }); + test('Expand abbreviation (HTML)', () => { return testExpandAbbreviation('html', new Selection(5, 25, 5, 25), 'ul>li', '
    \n\t\t\t
  • \n\t\t
'); }); @@ -157,7 +187,7 @@ suite('Tests for Expand Abbreviations (HTML)', () => { }); test('Expand abbreviation with numbered repeaters in completion list (HTML)', () => { - return testHtmlCompletionProvider( new Selection(7, 33, 7, 33), 'ul>li.item$*2', '
    \n\t
  • \n\t
  • \n
'); + return testHtmlCompletionProvider(new Selection(7, 33, 7, 33), 'ul>li.item$*2', '
    \n\t
  • \n\t
  • \n
'); }); test('Expand abbreviation with numbered repeaters with offset (HTML)', () => { @@ -165,7 +195,7 @@ suite('Tests for Expand Abbreviations (HTML)', () => { }); test('Expand abbreviation with numbered repeaters with offset in completion list (HTML)', () => { - return testHtmlCompletionProvider( new Selection(8, 36, 8, 36), 'ul>li.item$@44*2', '
    \n\t
  • \n\t
  • \n
'); + return testHtmlCompletionProvider(new Selection(8, 36, 8, 36), 'ul>li.item$@44*2', '
    \n\t
  • \n\t
  • \n
'); }); test('Expand abbreviation with numbered repeaters in groups (HTML)', () => { @@ -201,19 +231,65 @@ suite('Tests for Expand Abbreviations (HTML)', () => { }); test('No expanding text inside open tag (HTML)', () => { - return testExpandAbbreviation('html', new Selection(2, 4, 2, 4), '', '', true); + return withRandomFileEditor(htmlContents, 'html', (editor, doc) => { + editor.selection = new Selection(2, 4, 2, 4); + return expandEmmetAbbreviation(null).then(() => { + assert.equal(editor.document.getText(), htmlContents); + return Promise.resolve(); + }); + }); }); test('No expanding text inside open tag in completion list (HTML)', () => { - return testHtmlCompletionProvider(new Selection(2, 4, 2, 4), '', '', true); + return withRandomFileEditor(htmlContents, 'html', (editor, doc) => { + editor.selection = new Selection(2, 4, 2, 4); + const cancelSrc = new CancellationTokenSource(); + const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token); + assert.equal(!completionPromise, true, `Got unexpected comapletion promise instead of undefined`); + return Promise.resolve(); + }); }); test('No expanding text inside open tag when there is no closing tag (HTML)', () => { - return testExpandAbbreviation('html', new Selection(9, 8, 9, 8), '', '', true); + return withRandomFileEditor(htmlContents, 'html', (editor, doc) => { + editor.selection = new Selection(9, 8, 9, 8); + return expandEmmetAbbreviation(null).then(() => { + assert.equal(editor.document.getText(), htmlContents); + return Promise.resolve(); + }); + }); }); test('No expanding text inside open tag when there is no closing tag in completion list (HTML)', () => { - return testHtmlCompletionProvider(new Selection(9, 8, 9, 8), '', '', true); + return withRandomFileEditor(htmlContents, 'html', (editor, doc) => { + editor.selection = new Selection(9, 8, 9, 8); + const cancelSrc = new CancellationTokenSource(); + const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token); + assert.equal(!completionPromise, true, `Got unexpected comapletion promise instead of undefined`); + return Promise.resolve(); + }); + }); + + test('No expanding text inside open tag when there is no closing tag when there is no parent node (HTML)', () => { + const fileContents = ' { + editor.selection = new Selection(0, 6, 0, 6); + return expandEmmetAbbreviation(null).then(() => { + assert.equal(editor.document.getText(), fileContents); + return Promise.resolve(); + }); + }); + }); + + test('No expanding text in completion list inside open tag when there is no closing tag when there is no parent node (HTML)', () => { + const fileContents = ' { + editor.selection = new Selection(0, 6, 0, 6); + const cancelSrc = new CancellationTokenSource(); + const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token); + assert.equal(!completionPromise, true, `Got unexpected comapletion promise instead of undefined`); + return Promise.resolve(); + }); }); test('Expand css when inside style tag (HTML)', () => { @@ -251,7 +327,7 @@ suite('Tests for Expand Abbreviations (HTML)', () => { const emmetCompletionItem = completionList.items[0]; assert.equal(emmetCompletionItem.label, expandedText, `Label of completion item doesnt match.`); assert.equal((emmetCompletionItem.documentation || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`); - assert.equal(emmetCompletionItem.filterText, abbreviation, `FilterText of completion item doesnt match.`) + assert.equal(emmetCompletionItem.filterText, abbreviation, `FilterText of completion item doesnt match.`); return Promise.resolve(); }); }); @@ -304,8 +380,8 @@ suite('Tests for Expand Abbreviations (CSS)', () => { return withRandomFileEditor(cssContents, 'css', (editor, doc) => { editor.selection = new Selection(3, 1, 3, 4); const cancelSrc = new CancellationTokenSource(); - const completionPromise1 = completionProvider.provideCompletionItems(editor.document, new Position(3,4), cancelSrc.token); - const completionPromise2 = completionProvider.provideCompletionItems(editor.document, new Position(5,4), cancelSrc.token); + const completionPromise1 = completionProvider.provideCompletionItems(editor.document, new Position(3, 4), cancelSrc.token); + const completionPromise2 = completionProvider.provideCompletionItems(editor.document, new Position(5, 4), cancelSrc.token); if (!completionPromise1 || !completionPromise2) { assert.equal(1, 2, `Problem with expanding m10`); return Promise.resolve(); @@ -390,7 +466,7 @@ m10 background: } } - ` + `; return withRandomFileEditor(scssContentsNoExpand, 'scss', (editor, doc) => { editor.selections = [ @@ -413,7 +489,7 @@ m10 background: } } - ` + `; return withRandomFileEditor(scssContentsNoExpand, 'scss', (editor, doc) => { editor.selection = new Selection(1, 3, 1, 3); // outside rule @@ -431,7 +507,7 @@ m10 assert.equal(1, 2, `m10 gets expanded in invalid location (n the value part of property value)`); } return Promise.resolve(); - }) + }); } return Promise.resolve(); }); -- GitLab