@@ -63,16 +63,30 @@ const Highlighter = (() => {
6363 if ( com ) return span ( 'tk-com' , com ) ;
6464 if ( str ) {
6565 if ( / ^ [ f F r R b B u U ] * [ f F ] / . test ( str ) ) {
66- return span ( 'tk-str' , str . replace ( / \{ \{ | \} \} | \{ ( [ ^ { } ] * ) \} / g, ( m2 , expr ) =>
67- expr != null ? `{${ expr . replace ( new RegExp ( TOKEN_RE . source , TOKEN_RE . flags ) , tokenize ) } }` : m2
68- ) ) ;
66+ let result = '' ;
67+ let last = 0 ;
68+ const fRe = / \{ \{ | \} \} | \{ ( [ ^ { } ] * ) \} / g;
69+ let fm ;
70+ while ( ( fm = fRe . exec ( str ) ) !== null ) {
71+ if ( last < fm . index ) result += span ( 'tk-str' , str . slice ( last , fm . index ) ) ;
72+ if ( fm [ 1 ] != null ) {
73+ const inner = fm [ 1 ] . replace ( new RegExp ( TOKEN_RE . source , TOKEN_RE . flags ) , tokenize ) ;
74+ result += span ( 'tk-fexpr' , `{${ inner } }` ) ;
75+ } else {
76+ result += span ( 'tk-str' , fm [ 0 ] ) ;
77+ }
78+ last = fRe . lastIndex ;
79+ }
80+ if ( last < str . length ) result += span ( 'tk-str' , str . slice ( last ) ) ;
81+ return result ;
6982 }
7083 return span ( 'tk-str' , str ) ;
7184 }
7285 if ( num ) return span ( 'tk-num' , num ) ;
7386 if ( word ) {
7487 if ( full [ offset - 1 ] === '&' && full [ offset + word . length ] === ';' ) return word ;
7588 for ( const [ set , cls ] of CLASSES ) if ( set . has ( word ) ) return span ( cls , word ) ;
89+ if ( / ^ [ A - Z ] / . test ( word ) ) return span ( 'tk-class' , word ) ;
7690 return span ( / ^ \s * \( / . test ( full . slice ( offset + word . length ) ) ? 'tk-func' : 'tk-var' , word ) ;
7791 }
7892 return m ;
@@ -172,6 +186,13 @@ const Editor = (() => {
172186 const { inStr, isF } = stringCtx ( text , caret ) ;
173187 // Inside a normal string we don't auto-close anything; inside an f-string we still want `{` -> `{}` for expressions.
174188 if ( inStr && ! ( isF && key === '{' ) ) return null ;
189+ // Triple-quote opening: "" + " → """""" (or '' + ' → '''''')
190+ if ( ( key === '"' || key === "'" ) && caret >= 2 && text [ caret - 2 ] === key && text [ caret - 1 ] === key ) {
191+ return {
192+ text : text . slice ( 0 , caret - 2 ) + key . repeat ( 6 ) + text . slice ( caret ) ,
193+ caret : caret - 2 + 3 ,
194+ } ;
195+ }
175196 return {
176197 text : text . slice ( 0 , caret ) + key + PAIRS [ key ] + text . slice ( caret ) ,
177198 caret : caret + 1 ,
0 commit comments