11import type { CodeInformation } from '@volar/language-server' ;
22import { SourceMap , Stack } from '@volar/source-map' ;
3- import { ExportsInfoForLabs , TextDocument } from '@volar/vscode' ;
3+ import { BaseLanguageClient , LabsInfo , TextDocument } from '@volar/vscode' ;
44import * as vscode from 'vscode' ;
55
66const mappingDecorationType = vscode . window . createTextEditorDecorationType ( {
@@ -35,155 +35,25 @@ export const sourceUriToVirtualUris = new Map<string, Set<string>>();
3535
3636export const virtualUriToSourceUri = new Map < string , string > ( ) ;
3737
38- export async function activate ( info : ExportsInfoForLabs ) {
38+ export async function activate ( info : LabsInfo ) {
3939
4040 const subscriptions : vscode . Disposable [ ] = [ ] ;
4141 const docChangeEvent = new vscode . EventEmitter < vscode . Uri > ( ) ;
42- const client = info . volarLabs . languageClient ;
43-
44- subscriptions . push ( vscode . languages . registerHoverProvider ( { scheme : client . name . replace ( / / g, '_' ) . toLowerCase ( ) } , {
45- async provideHover ( document : vscode . TextDocument , position : vscode . Position , _token : vscode . CancellationToken ) {
46-
47- const maps = virtualUriToSourceMap . get ( document . uri . toString ( ) ) ;
48- if ( ! maps ) return ;
49-
50- const data : {
51- uri : string ,
52- mapping : any ,
53- } [ ] = [ ] ;
54-
55- for ( const [ sourceUri , _ , map ] of maps ) {
56- const source = map . getSourceOffset ( document . offsetAt ( position ) ) ;
57- if ( source ) {
58- data . push ( {
59- uri : sourceUri ,
60- mapping : source ,
61- } ) ;
62- }
63- }
64-
65- if ( data . length === 0 ) return ;
66-
67- return new vscode . Hover ( data . map ( ( data ) => [
68- data . uri ,
69- '' ,
70- '' ,
71- '```json' ,
72- JSON . stringify ( data . mapping , null , 2 ) ,
73- '```' ,
74- ] . join ( '\n' ) ) ) ;
75- }
76- } ) ) ;
77-
78- subscriptions . push ( vscode . languages . registerDefinitionProvider ( { scheme : client . name . replace ( / / g, '_' ) . toLowerCase ( ) } , {
79- async provideDefinition ( document : vscode . TextDocument , position : vscode . Position , _token : vscode . CancellationToken ) {
80-
81- const stacks = virtualUriToStacks . get ( document . uri . toString ( ) ) ;
82- if ( ! stacks ) return ;
83-
84- const offset = document . offsetAt ( position ) ;
85- const stack = stacks . find ( stack => stack . range [ 0 ] <= offset && offset <= stack . range [ 1 ] ) ;
86- if ( ! stack ) return ;
87-
88- const line = Number ( stack . source . split ( ':' ) . at ( - 2 ) ) ;
89- const character = Number ( stack . source . split ( ':' ) . at ( - 1 ) ) ;
90- const fileName = stack . source . split ( ':' ) . slice ( 0 , - 2 ) . join ( ':' ) ;
91- const link : vscode . DefinitionLink = {
92- originSelectionRange : new vscode . Range ( document . positionAt ( stack . range [ 0 ] ) , document . positionAt ( stack . range [ 1 ] ) ) ,
93- targetUri : vscode . Uri . file ( fileName ) ,
94- targetRange : new vscode . Range ( line - 1 , character - 1 , line - 1 , character - 1 ) ,
95- } ;
96- return [ link ] ;
97- }
98- } ) ) ;
99-
100- subscriptions . push ( vscode . languages . registerInlayHintsProvider ( { scheme : client . name . replace ( / / g, '_' ) . toLowerCase ( ) } , {
101- provideInlayHints ( document , range ) {
102- const stacks = virtualUriToStacks . get ( document . uri . toString ( ) ) ;
103- const result : vscode . InlayHint [ ] = [ ] ;
104- const range2 = [ document . offsetAt ( range . start ) , document . offsetAt ( range . end ) ] ;
105- const text = document . getText ( ) ;
106- for ( const stack of stacks ?? [ ] ) {
107- let [ start , end ] = stack . range ;
108- let startText = '[' ;
109- let endText = ']' ;
110- while ( end > start && text [ end - 1 ] === '\n' ) {
111- end -- ;
112- endText = '\n' + endText ;
113- }
114- while ( start < end && text [ start ] === '\n' ) {
115- start ++ ;
116- startText = '\n' + startText ;
117- }
118- if ( start >= range2 [ 0 ] && start <= range2 [ 1 ] ) {
119- result . push ( new vscode . InlayHint ( document . positionAt ( start ) , startText ) ) ;
120- result [ result . length - 1 ] . paddingLeft = true ;
121- }
122- if ( end >= range2 [ 0 ] && end <= range2 [ 1 ] ) {
123- result . push ( new vscode . InlayHint ( document . positionAt ( end ) , endText ) ) ;
124- result [ result . length - 1 ] . paddingRight = true ;
125- }
126- }
127- return result ;
128- } ,
129- } ) ) ;
130-
131- subscriptions . push ( vscode . workspace . registerTextDocumentContentProvider (
132- client . name . replace ( / / g, '_' ) . toLowerCase ( ) ,
133- {
134- onDidChange : docChangeEvent . event ,
135- async provideTextDocumentContent ( uri : vscode . Uri ) : Promise < string | undefined > {
136-
137- const requestUri = virtualUriToSourceUri . get ( uri . toString ( ) ) ;
138- if ( requestUri ) {
139-
140- const fileName = uri . with ( { scheme : 'file' } ) . fsPath ;
141- const virtualFile = await client . sendRequest ( info . volarLabs . languageServerProtocol . GetVirtualFileRequest . type , { sourceFileUri : requestUri , virtualFileName : fileName } ) ;
142- virtualUriToSourceMap . set ( uri . toString ( ) , [ ] ) ;
143-
144- Object . entries ( virtualFile . mappings ) . forEach ( ( [ sourceUri , mappings ] ) => {
145- const sourceEditor = vscode . window . visibleTextEditors . find ( editor => editor . document . uri . toString ( ) === sourceUri ) ;
146- if ( sourceEditor ) {
147- virtualUriToSourceMap . get ( uri . toString ( ) ) ?. push ( [
148- sourceEditor . document . uri . toString ( ) ,
149- sourceEditor . document . version ,
150- new SourceMap ( mappings ) ,
151- ] ) ;
152- if ( ! sourceUriToVirtualUris . has ( sourceUri ) ) {
153- sourceUriToVirtualUris . set ( sourceUri , new Set ( ) ) ;
154- }
155- sourceUriToVirtualUris . get ( sourceUri ) ?. add ( uri . toString ( ) ) ;
156- }
157- } ) ;
158- virtualDocuments . set ( uri . toString ( ) , TextDocument . create ( '' , '' , 0 , virtualFile . content ) ) ;
159- virtualUriToStacks . set ( uri . toString ( ) , virtualFile . codegenStacks ) ;
160-
161- clearTimeout ( updateDecorationsTimeout ) ;
162- updateDecorationsTimeout = setTimeout ( updateDecorations , 100 ) ;
163-
164- return virtualFile . content ;
165- }
166- }
167- } ,
168- ) ) ;
169-
17042 const virtualUriToSourceMap = new Map < string , [ string , number , SourceMap < CodeInformation > ] [ ] > ( ) ;
17143 const virtualUriToStacks = new Map < string , Stack [ ] > ( ) ;
17244 const virtualDocuments = new Map < string , TextDocument > ( ) ;
17345
46+ for ( const extension of info . volarLabs . languageClients ) {
47+ registerProviders ( extension ) ;
48+ }
49+ info . volarLabs . onDidAddLanguageClient ( registerProviders ) ;
50+
17451 let updateVirtualDocument : NodeJS . Timeout | undefined ;
17552 let updateDecorationsTimeout : NodeJS . Timeout | undefined ;
17653
17754 subscriptions . push ( vscode . window . onDidChangeActiveTextEditor ( updateDecorations ) ) ;
17855 subscriptions . push ( vscode . window . onDidChangeTextEditorSelection ( updateDecorations ) ) ;
17956 subscriptions . push ( vscode . window . onDidChangeVisibleTextEditors ( updateDecorations ) ) ;
180- subscriptions . push ( client . onDidChangeState ( ( ) => {
181- for ( const virtualUris of sourceUriToVirtualUris . values ( ) ) {
182- virtualUris . forEach ( uri => {
183- docChangeEvent . fire ( vscode . Uri . parse ( uri ) ) ;
184- } ) ;
185- }
186- } ) ) ;
18757 subscriptions . push ( vscode . workspace . onDidChangeTextDocument ( e => {
18858 if ( sourceUriToVirtualUris . has ( e . document . uri . toString ( ) ) ) {
18959 const virtualUris = sourceUriToVirtualUris . get ( e . document . uri . toString ( ) ) ;
@@ -198,6 +68,143 @@ export async function activate(info: ExportsInfoForLabs) {
19868
19969 return vscode . Disposable . from ( ...subscriptions ) ;
20070
71+ function registerProviders ( client : BaseLanguageClient ) {
72+
73+ subscriptions . push ( client . onDidChangeState ( ( ) => {
74+ for ( const virtualUris of sourceUriToVirtualUris . values ( ) ) {
75+ virtualUris . forEach ( uri => {
76+ docChangeEvent . fire ( vscode . Uri . parse ( uri ) ) ;
77+ } ) ;
78+ }
79+ } ) ) ;
80+
81+ subscriptions . push ( vscode . languages . registerHoverProvider ( { scheme : client . name . replace ( / / g, '_' ) . toLowerCase ( ) } , {
82+ async provideHover ( document : vscode . TextDocument , position : vscode . Position , _token : vscode . CancellationToken ) {
83+
84+ const maps = virtualUriToSourceMap . get ( document . uri . toString ( ) ) ;
85+ if ( ! maps ) return ;
86+
87+ const data : {
88+ uri : string ,
89+ mapping : any ,
90+ } [ ] = [ ] ;
91+
92+ for ( const [ sourceUri , _ , map ] of maps ) {
93+ const source = map . getSourceOffset ( document . offsetAt ( position ) ) ;
94+ if ( source ) {
95+ data . push ( {
96+ uri : sourceUri ,
97+ mapping : source ,
98+ } ) ;
99+ }
100+ }
101+
102+ if ( data . length === 0 ) return ;
103+
104+ return new vscode . Hover ( data . map ( ( data ) => [
105+ data . uri ,
106+ '' ,
107+ '' ,
108+ '```json' ,
109+ JSON . stringify ( data . mapping , null , 2 ) ,
110+ '```' ,
111+ ] . join ( '\n' ) ) ) ;
112+ }
113+ } ) ) ;
114+
115+ subscriptions . push ( vscode . languages . registerDefinitionProvider ( { scheme : client . name . replace ( / / g, '_' ) . toLowerCase ( ) } , {
116+ async provideDefinition ( document : vscode . TextDocument , position : vscode . Position , _token : vscode . CancellationToken ) {
117+
118+ const stacks = virtualUriToStacks . get ( document . uri . toString ( ) ) ;
119+ if ( ! stacks ) return ;
120+
121+ const offset = document . offsetAt ( position ) ;
122+ const stack = stacks . find ( stack => stack . range [ 0 ] <= offset && offset <= stack . range [ 1 ] ) ;
123+ if ( ! stack ) return ;
124+
125+ const line = Number ( stack . source . split ( ':' ) . at ( - 2 ) ) ;
126+ const character = Number ( stack . source . split ( ':' ) . at ( - 1 ) ) ;
127+ const fileName = stack . source . split ( ':' ) . slice ( 0 , - 2 ) . join ( ':' ) ;
128+ const link : vscode . DefinitionLink = {
129+ originSelectionRange : new vscode . Range ( document . positionAt ( stack . range [ 0 ] ) , document . positionAt ( stack . range [ 1 ] ) ) ,
130+ targetUri : vscode . Uri . file ( fileName ) ,
131+ targetRange : new vscode . Range ( line - 1 , character - 1 , line - 1 , character - 1 ) ,
132+ } ;
133+ return [ link ] ;
134+ }
135+ } ) ) ;
136+
137+ subscriptions . push ( vscode . languages . registerInlayHintsProvider ( { scheme : client . name . replace ( / / g, '_' ) . toLowerCase ( ) } , {
138+ provideInlayHints ( document , range ) {
139+ const stacks = virtualUriToStacks . get ( document . uri . toString ( ) ) ;
140+ const result : vscode . InlayHint [ ] = [ ] ;
141+ const range2 = [ document . offsetAt ( range . start ) , document . offsetAt ( range . end ) ] ;
142+ const text = document . getText ( ) ;
143+ for ( const stack of stacks ?? [ ] ) {
144+ let [ start , end ] = stack . range ;
145+ let startText = '[' ;
146+ let endText = ']' ;
147+ while ( end > start && text [ end - 1 ] === '\n' ) {
148+ end -- ;
149+ endText = '\n' + endText ;
150+ }
151+ while ( start < end && text [ start ] === '\n' ) {
152+ start ++ ;
153+ startText = '\n' + startText ;
154+ }
155+ if ( start >= range2 [ 0 ] && start <= range2 [ 1 ] ) {
156+ result . push ( new vscode . InlayHint ( document . positionAt ( start ) , startText ) ) ;
157+ result [ result . length - 1 ] . paddingLeft = true ;
158+ }
159+ if ( end >= range2 [ 0 ] && end <= range2 [ 1 ] ) {
160+ result . push ( new vscode . InlayHint ( document . positionAt ( end ) , endText ) ) ;
161+ result [ result . length - 1 ] . paddingRight = true ;
162+ }
163+ }
164+ return result ;
165+ } ,
166+ } ) ) ;
167+
168+ subscriptions . push ( vscode . workspace . registerTextDocumentContentProvider (
169+ client . name . replace ( / / g, '_' ) . toLowerCase ( ) ,
170+ {
171+ onDidChange : docChangeEvent . event ,
172+ async provideTextDocumentContent ( uri : vscode . Uri ) : Promise < string | undefined > {
173+
174+ const requestUri = virtualUriToSourceUri . get ( uri . toString ( ) ) ;
175+ if ( requestUri ) {
176+
177+ const fileName = uri . with ( { scheme : 'file' } ) . fsPath ;
178+ const virtualFile = await client . sendRequest ( info . volarLabs . languageServerProtocol . GetVirtualFileRequest . type , { sourceFileUri : requestUri , virtualFileName : fileName } ) ;
179+ virtualUriToSourceMap . set ( uri . toString ( ) , [ ] ) ;
180+
181+ Object . entries ( virtualFile . mappings ) . forEach ( ( [ sourceUri , mappings ] ) => {
182+ const sourceEditor = vscode . window . visibleTextEditors . find ( editor => editor . document . uri . toString ( ) === sourceUri ) ;
183+ if ( sourceEditor ) {
184+ virtualUriToSourceMap . get ( uri . toString ( ) ) ?. push ( [
185+ sourceEditor . document . uri . toString ( ) ,
186+ sourceEditor . document . version ,
187+ new SourceMap ( mappings ) ,
188+ ] ) ;
189+ if ( ! sourceUriToVirtualUris . has ( sourceUri ) ) {
190+ sourceUriToVirtualUris . set ( sourceUri , new Set ( ) ) ;
191+ }
192+ sourceUriToVirtualUris . get ( sourceUri ) ?. add ( uri . toString ( ) ) ;
193+ }
194+ } ) ;
195+ virtualDocuments . set ( uri . toString ( ) , TextDocument . create ( '' , '' , 0 , virtualFile . content ) ) ;
196+ virtualUriToStacks . set ( uri . toString ( ) , virtualFile . codegenStacks ) ;
197+
198+ clearTimeout ( updateDecorationsTimeout ) ;
199+ updateDecorationsTimeout = setTimeout ( updateDecorations , 100 ) ;
200+
201+ return virtualFile . content ;
202+ }
203+ }
204+ } ,
205+ ) ) ;
206+ }
207+
201208 function updateDecorations ( ) {
202209 for ( const [ _ , sources ] of virtualUriToSourceMap ) {
203210 for ( const [ sourceUri ] of sources ) {
0 commit comments