@@ -8,6 +8,15 @@ import { Cm6_Util } from 'src/codemirror/Cm6_Util';
88import { type ThemedToken } from 'shiki' ;
99import { editorLivePreviewField } from 'obsidian' ;
1010
11+ interface DecoQueueNode {
12+ from : number ;
13+ to : number ;
14+ lang : string ;
15+ content : string ;
16+ hideLang ?: boolean ;
17+ hideTo ?: number ;
18+ }
19+
1120// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
1221export function createCm6Plugin ( plugin : ShikiPlugin ) {
1322return ViewPlugin . fromClass (
@@ -58,9 +67,10 @@ export function createCm6Plugin(plugin: ShikiPlugin) {
5867 * @param view
5968 * @param docChanged
6069 */
61- updateWidgets ( view : EditorView , docChanged : boolean = true ) : void {
70+ async updateWidgets ( view : EditorView , docChanged : boolean = true ) : Promise < void > {
6271let lang = '' ;
6372let state : SyntaxNode [ ] = [ ] ;
73+ let decoQueue : DecoQueueNode [ ] = [ ] ;
6474
6575// const t1 = performance.now();
6676
@@ -82,26 +92,14 @@ export function createCm6Plugin(plugin: ShikiPlugin) {
8292if ( match ) {
8393const hasSelectionOverlap = Cm6_Util . checkSelectionAndRangeOverlap ( view . state . selection , node . from - 1 , node . to + 1 ) ;
8494
85- // if there is selection overlap, the user has the inline code block selected, so we don't want to hide the language tag.
86- // For this we just remove the decorations and rebuild them with the language tag visible.
87- if ( hasSelectionOverlap ) {
88- this . removeDecoration ( node . from , node . to ) ;
89- }
90- const hideTo = node . from + match [ 1 ] . length + 3 ; // hide `{lang} `
91-
92- try {
93- const decorations = this . buildDecorations ( hideTo , node . to , match [ 1 ] , match [ 2 ] ) ;
94-
95- this . removeDecoration ( node . from , node . to ) ;
96- // add the decoration that hides the language tag
97- if ( this . isLivePreview ( view . state ) && ! hasSelectionOverlap ) {
98- decorations . unshift ( Decoration . replace ( { } ) . range ( node . from , hideTo ) ) ;
99- }
100- // add the highlight decorations
101- this . addDecoration ( node . from , node . to , decorations ) ;
102- } catch ( e ) {
103- console . error ( e ) ;
104- }
95+ decoQueue . push ( {
96+ from : node . from ,
97+ to : node . to ,
98+ lang : match [ 1 ] ,
99+ content : match [ 2 ] ,
100+ hideLang : this . isLivePreview ( view . state ) && ! hasSelectionOverlap ,
101+ hideTo : node . from + match [ 1 ] . length + 3 // hide `{lang} `
102+ } ) ;
105103}
106104} else {
107105// we don't want to highlight normal inline code blocks, thus we remove any of our decorations
@@ -133,17 +131,12 @@ export function createCm6Plugin(plugin: ShikiPlugin) {
133131const start = state [ 0 ] . from ;
134132const end = state [ state . length - 1 ] . to ;
135133
136- const content = Cm6_Util . getContent ( view . state , start , end ) ;
137-
138- try {
139- const decorations = this . buildDecorations ( start , end , lang , content ) ;
140-
141- // when we have the decorations, we first remove all existing decorations in the range and then add the new ones
142- this . removeDecoration ( start , end ) ;
143- this . addDecoration ( start , end , decorations ) ;
144- } catch ( e ) {
145- console . error ( e ) ;
146- }
134+ decoQueue . push ( {
135+ from : start ,
136+ to : end ,
137+ lang,
138+ content : Cm6_Util . getContent ( view . state , start , end )
139+ } ) ;
147140}
148141
149142lang = '' ;
@@ -152,6 +145,29 @@ export function createCm6Plugin(plugin: ShikiPlugin) {
152145} ,
153146} ) ;
154147
148+ for ( const node of decoQueue ) {
149+ try {
150+ if ( node . hideTo ) { // inline decorations
151+ const decorations = await this . buildDecorations ( node . hideTo , node . to , node . lang , node . content ) ;
152+
153+ this . removeDecoration ( node . from , node . to ) ;
154+ // add the decoration that hides the language tag
155+ if ( node . hideLang ) {
156+ decorations . unshift ( Decoration . replace ( { } ) . range ( node . from , node . hideTo ) ) ;
157+ }
158+ // add the highlight decorations
159+ this . addDecoration ( node . from , node . to , decorations ) ;
160+ } else {
161+ const decorations = await this . buildDecorations ( node . from , node . to , node . lang , node . content ) ;
162+
163+ // when we have the decorations, we first remove all existing decorations in the range and then add the new ones
164+ this . removeDecoration ( node . from , node . to ) ;
165+ this . addDecoration ( node . from , node . to , decorations ) ;
166+ }
167+ } catch ( e ) {
168+ console . error ( e ) ;
169+ }
170+ }
155171// console.log('Traversed syntax tree in', performance.now() - t1, 'ms');
156172}
157173
@@ -201,12 +217,12 @@ export function createCm6Plugin(plugin: ShikiPlugin) {
201217 * @param language
202218 * @param content
203219 */
204- buildDecorations ( from : number , to : number , language : string , content : string ) : Range < Decoration > [ ] {
220+ async buildDecorations ( from : number , to : number , language : string , content : string ) : Promise < Range < Decoration > [ ] > {
205221if ( language === '' ) {
206222return [ ] ;
207223}
208224
209- const highlight = plugin . highlighter . getHighlightTokens ( content , language ) ;
225+ const highlight = await plugin . highlighter . getHighlightTokens ( content , language ) ;
210226
211227if ( ! highlight ) {
212228return [ ] ;
0 commit comments