1- import ts from 'typescript' ;
21import {
32 highlightCode ,
43 renderTokenizedCode ,
@@ -19,34 +18,159 @@ type TokenSpan = {
1918 fontStyle ?: number ;
2019} ;
2120
22- function propertyNameText ( name : ts . PropertyName ) : string | null {
23- if ( ts . isIdentifier ( name ) || ts . isStringLiteral ( name ) || ts . isNumericLiteral ( name ) ) {
24- return name . text ;
21+ function isIdentifierStart ( char : string ) : boolean {
22+ return / [ A - Z a - z _ $ ] / . test ( char ) ;
23+ }
24+
25+ function isIdentifierChar ( char : string ) : boolean {
26+ return / [ A - Z a - z 0 - 9 _ $ ] / . test ( char ) ;
27+ }
28+
29+ function skipString ( code : string , start : number , quote : "'" | '"' ) : number {
30+ let index = start + 1 ;
31+ while ( index < code . length ) {
32+ if ( code [ index ] === '\\' ) {
33+ index += 2 ;
34+ continue ;
35+ }
36+ if ( code [ index ] === quote ) return index + 1 ;
37+ index += 1 ;
38+ }
39+ return index ;
40+ }
41+
42+ function skipLineComment ( code : string , start : number ) : number {
43+ let index = start + 2 ;
44+ while ( index < code . length && code [ index ] !== '\n' ) {
45+ index += 1 ;
46+ }
47+ return index ;
48+ }
49+
50+ function skipBlockComment ( code : string , start : number ) : number {
51+ let index = start + 2 ;
52+ while ( index < code . length - 1 ) {
53+ if ( code [ index ] === '*' && code [ index + 1 ] === '/' ) return index + 2 ;
54+ index += 1 ;
55+ }
56+ return code . length ;
57+ }
58+
59+ function skipTrivia ( code : string , start : number ) : number {
60+ let index = start ;
61+ while ( index < code . length ) {
62+ if ( / \s / . test ( code [ index ] ) ) {
63+ index += 1 ;
64+ continue ;
65+ }
66+ if ( code [ index ] === '/' && code [ index + 1 ] === '/' ) {
67+ index = skipLineComment ( code , index ) ;
68+ continue ;
69+ }
70+ if ( code [ index ] === '/' && code [ index + 1 ] === '*' ) {
71+ index = skipBlockComment ( code , index ) ;
72+ continue ;
73+ }
74+ return index ;
75+ }
76+ return index ;
77+ }
78+
79+ function skipTemplateExpression ( code : string , start : number ) : number {
80+ let depth = 1 ;
81+ let index = start ;
82+ while ( index < code . length ) {
83+ const char = code [ index ] ;
84+ if ( char === "'" || char === '"' ) {
85+ index = skipString ( code , index , char ) ;
86+ continue ;
87+ }
88+ if ( char === '`' ) {
89+ index = skipTemplateLiteral ( code , index ) ;
90+ continue ;
91+ }
92+ if ( char === '/' && code [ index + 1 ] === '/' ) {
93+ index = skipLineComment ( code , index ) ;
94+ continue ;
95+ }
96+ if ( char === '/' && code [ index + 1 ] === '*' ) {
97+ index = skipBlockComment ( code , index ) ;
98+ continue ;
99+ }
100+ if ( char === '{' ) depth += 1 ;
101+ if ( char === '}' ) {
102+ depth -= 1 ;
103+ if ( depth === 0 ) return index + 1 ;
104+ }
105+ index += 1 ;
25106 }
26- return null ;
107+ return index ;
108+ }
109+
110+ function skipTemplateLiteral ( code : string , start : number ) : number {
111+ let index = start + 1 ;
112+ while ( index < code . length ) {
113+ if ( code [ index ] === '\\' ) {
114+ index += 2 ;
115+ continue ;
116+ }
117+ if ( code [ index ] === '`' ) return index + 1 ;
118+ if ( code [ index ] === '$' && code [ index + 1 ] === '{' ) {
119+ index = skipTemplateExpression ( code , index + 2 ) ;
120+ continue ;
121+ }
122+ index += 1 ;
123+ }
124+ return index ;
27125}
28126
29127function findBodyRanges ( code : string ) : BodyRange [ ] {
30- const sourceFile = ts . createSourceFile ( 'snippet.ts' , code , ts . ScriptTarget . Latest , true , ts . ScriptKind . TS ) ;
31128 const ranges : BodyRange [ ] = [ ] ;
129+ let index = 0 ;
32130
33- function visit ( node : ts . Node ) {
34- if (
35- ts . isPropertyAssignment ( node ) &&
36- propertyNameText ( node . name ) === 'body' &&
37- ( ts . isNoSubstitutionTemplateLiteral ( node . initializer ) || ts . isTemplateExpression ( node . initializer ) )
38- ) {
39- const start = node . initializer . getStart ( sourceFile ) + 1 ;
40- const end = node . initializer . getEnd ( ) - 1 ;
41- if ( end > start ) {
42- ranges . push ( { start, end } ) ;
43- }
131+ while ( index < code . length ) {
132+ index = skipTrivia ( code , index ) ;
133+ if ( index >= code . length ) break ;
134+
135+ const char = code [ index ] ;
136+ if ( char === "'" || char === '"' ) {
137+ index = skipString ( code , index , char ) ;
138+ continue ;
139+ }
140+ if ( char === '`' ) {
141+ index = skipTemplateLiteral ( code , index ) ;
142+ continue ;
143+ }
144+ if ( ! isIdentifierStart ( char ) ) {
145+ index += 1 ;
146+ continue ;
147+ }
148+
149+ const propertyStart = index ;
150+ index += 1 ;
151+ while ( index < code . length && isIdentifierChar ( code [ index ] ) ) {
152+ index += 1 ;
153+ }
154+
155+ if ( code . slice ( propertyStart , index ) !== 'body' ) continue ;
156+
157+ let valueStart = skipTrivia ( code , index ) ;
158+ if ( code [ valueStart ] !== ':' ) continue ;
159+
160+ valueStart = skipTrivia ( code , valueStart + 1 ) ;
161+ if ( code [ valueStart ] !== '`' ) {
162+ index = valueStart ;
163+ continue ;
164+ }
165+
166+ const templateEnd = skipTemplateLiteral ( code , valueStart ) ;
167+ if ( templateEnd > valueStart + 1 ) {
168+ ranges . push ( { start : valueStart + 1 , end : templateEnd - 1 } ) ;
44169 }
45- ts . forEachChild ( node , visit ) ;
170+ index = templateEnd ;
46171 }
47172
48- visit ( sourceFile ) ;
49- return ranges . sort ( ( a , b ) => a . start - b . start ) ;
173+ return ranges ;
50174}
51175
52176function flattenTokens ( lines : HighlightedToken [ ] [ ] , offsetShift = 0 ) : TokenSpan [ ] {
0 commit comments