-
-
Notifications
You must be signed in to change notification settings - Fork 206
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
(fix) ignore html end-tag-like inside moustache (#671)
- Loading branch information
1 parent
34903a4
commit ff5d65e
Showing
5 changed files
with
172 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import { | ||
getLanguageService, | ||
HTMLDocument, | ||
TokenType, | ||
ScannerState, | ||
Scanner | ||
} from 'vscode-html-languageservice'; | ||
import { isInsideMoustacheTag } from './utils'; | ||
|
||
const parser = getLanguageService(); | ||
|
||
/** | ||
* Parses text as HTML | ||
*/ | ||
export function parseHtml(text: string): HTMLDocument { | ||
const preprocessed = preprocess(text); | ||
|
||
// We can safely only set getText because only this is used for parsing | ||
const parsedDoc = parser.parseHTMLDocument(<any>{ getText: () => preprocessed }); | ||
|
||
return parsedDoc; | ||
} | ||
|
||
const createScanner = parser.createScanner as ( | ||
input: string, | ||
initialOffset?: number, | ||
initialState?: ScannerState | ||
) => Scanner; | ||
|
||
/** | ||
* scan the text and remove any `>` or `<` that cause the tag to end short, | ||
*/ | ||
function preprocess(text: string) { | ||
let scanner = createScanner(text); | ||
let token = scanner.scan(); | ||
let currentStartTagStart: number | null = null; | ||
|
||
while (token !== TokenType.EOS) { | ||
const offset = scanner.getTokenOffset(); | ||
|
||
if (token === TokenType.StartTagOpen) { | ||
currentStartTagStart = offset; | ||
} | ||
|
||
if (token === TokenType.StartTagClose) { | ||
if (shouldBlankStartOrEndTagLike(offset)) { | ||
blankStartOrEndTagLike(offset); | ||
} else { | ||
currentStartTagStart = null; | ||
} | ||
} | ||
|
||
if (token === TokenType.StartTagSelfClose) { | ||
currentStartTagStart = null; | ||
} | ||
|
||
// <Foo checked={a < 1}> | ||
// https://github.com/microsoft/vscode-html-languageservice/blob/71806ef57be07e1068ee40900ef8b0899c80e68a/src/parser/htmlScanner.ts#L327 | ||
if ( | ||
token === TokenType.Unknown && | ||
scanner.getScannerState() === ScannerState.WithinTag && | ||
scanner.getTokenText() === '<' && | ||
shouldBlankStartOrEndTagLike(offset) | ||
) { | ||
blankStartOrEndTagLike(offset); | ||
} | ||
|
||
token = scanner.scan(); | ||
} | ||
|
||
return text; | ||
|
||
function shouldBlankStartOrEndTagLike(offset: number) { | ||
// not null rather than falsy, otherwise it won't work on first tag(0) | ||
return ( | ||
currentStartTagStart !== null && | ||
isInsideMoustacheTag(text, currentStartTagStart, offset) | ||
); | ||
} | ||
|
||
function blankStartOrEndTagLike(offset: number) { | ||
text = text.substring(0, offset) + ' ' + text.substring(offset + 1); | ||
scanner = createScanner(text, offset, ScannerState.WithinTag); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
76 changes: 76 additions & 0 deletions
76
packages/language-server/test/lib/documents/parseHtml.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import assert from 'assert'; | ||
import { HTMLDocument } from 'vscode-html-languageservice'; | ||
import { parseHtml } from '../../../src/lib/documents/parseHtml'; | ||
|
||
describe('parseHtml', () => { | ||
const testRootElements = (document: HTMLDocument) => { | ||
assert.deepStrictEqual( | ||
document.roots.map((r) => r.tag), | ||
['Foo', 'style'] | ||
); | ||
}; | ||
|
||
it('ignore arrow inside moustache', () => { | ||
testRootElements( | ||
parseHtml( | ||
`<Foo on:click={() => console.log('ya!!!')} /> | ||
<style></style>` | ||
) | ||
); | ||
}); | ||
|
||
it('ignore greater than operator inside moustache', () => { | ||
testRootElements( | ||
parseHtml( | ||
`<Foo checked={a > 1} /> | ||
<style></style>` | ||
) | ||
); | ||
}); | ||
|
||
it('ignore less than operator inside moustache', () => { | ||
testRootElements( | ||
parseHtml( | ||
`<Foo checked={a < 1} /> | ||
<style></style>` | ||
) | ||
); | ||
}); | ||
|
||
it('ignore less than operator inside moustache with tag not self closed', () => { | ||
testRootElements( | ||
parseHtml( | ||
`<Foo checked={a < 1}> | ||
</Foo> | ||
<style></style>` | ||
) | ||
); | ||
}); | ||
|
||
it('parse baseline html', () => { | ||
testRootElements( | ||
parseHtml( | ||
`<Foo checked /> | ||
<style></style>` | ||
) | ||
); | ||
}); | ||
|
||
it('parse baseline html with moustache', () => { | ||
testRootElements( | ||
parseHtml( | ||
`<Foo checked={a} /> | ||
<style></style>` | ||
) | ||
); | ||
}); | ||
|
||
it('parse baseline html with possibly un-closed start tag', () => { | ||
testRootElements( | ||
parseHtml( | ||
`<Foo checked={a} | ||
<style></style>` | ||
) | ||
); | ||
}); | ||
}); |