@@ -83,17 +83,43 @@ class TextDocumentContentProvider implements vscode.TextDocumentContentProvider
8383// FIXME: consider implementing this via the Tree View API?
8484// https://code.visualstudio.com/api/extension-guides/tree-view
8585class AstInspector implements vscode . HoverProvider , Disposable {
86- private static readonly astDecorationType = vscode . window . createTextEditorDecorationType ( {
86+ private readonly astDecorationType = vscode . window . createTextEditorDecorationType ( {
8787 borderColor : new vscode . ThemeColor ( 'rust_analyzer.syntaxTreeBorder' ) ,
8888 borderStyle : "solid" ,
8989 borderWidth : "2px" ,
9090
9191 } ) ;
9292 private rustEditor : undefined | RustEditor ;
9393
94+ // Lazy rust token range -> syntax tree file range.
95+ private readonly rust2Ast = new Lazy ( ( ) => {
96+ const astEditor = this . findAstTextEditor ( ) ;
97+ if ( ! this . rustEditor || ! astEditor ) return undefined ;
98+
99+ console . time ( "Build goto def index" ) ;
100+ let buf : [ vscode . Range , vscode . Range ] [ ] = [ ] ;
101+ for ( let i = 0 ; i < astEditor . document . lineCount ; ++ i ) {
102+ const astLine = astEditor . document . lineAt ( i ) ;
103+
104+ // Heuristically look for nodes with quoted text (which are token nodes)
105+ const isTokenNode = astLine . text . lastIndexOf ( '"' ) >= 0 ;
106+ if ( ! isTokenNode ) continue ;
107+
108+ const rustRange = this . parseRustTextRange ( this . rustEditor . document , astLine . text ) ;
109+ if ( ! rustRange ) continue ;
110+
111+ buf . push ( [ rustRange , this . findAstRange ( astLine ) ] ) ;
112+ }
113+
114+ console . timeEnd ( "Build goto def index" ) ;
115+ return buf ;
116+ } ) ;
117+
94118 constructor ( ctx : Ctx ) {
95119 ctx . pushCleanup ( vscode . languages . registerHoverProvider ( { scheme : AST_FILE_SCHEME } , this ) ) ;
120+ ctx . pushCleanup ( vscode . languages . registerDefinitionProvider ( { language : "rust" } , this ) ) ;
96121 vscode . workspace . onDidCloseTextDocument ( this . onDidCloseTextDocument , this , ctx . subscriptions ) ;
122+ vscode . workspace . onDidChangeTextDocument ( this . onDidChangeTextDocument , this , ctx . subscriptions ) ;
97123 vscode . window . onDidChangeVisibleTextEditors ( this . onDidChangeVisibleTextEditors , this , ctx . subscriptions ) ;
98124
99125 ctx . pushCleanup ( this ) ;
@@ -102,27 +128,64 @@ class AstInspector implements vscode.HoverProvider, Disposable {
102128 this . setRustEditor ( undefined ) ;
103129 }
104130
131+ private onDidChangeTextDocument ( event : vscode . TextDocumentChangeEvent ) {
132+ if ( this . rustEditor && event . document . uri . toString ( ) === this . rustEditor . document . uri . toString ( ) ) {
133+ this . rust2Ast . reset ( ) ;
134+ }
135+ }
136+
105137 private onDidCloseTextDocument ( doc : vscode . TextDocument ) {
106138 if ( this . rustEditor && doc . uri . toString ( ) === this . rustEditor . document . uri . toString ( ) ) {
107139 this . setRustEditor ( undefined ) ;
108140 }
109141 }
110142
111143 private onDidChangeVisibleTextEditors ( editors : vscode . TextEditor [ ] ) {
112- if ( editors . every ( suspect => suspect . document . uri . scheme !== AST_FILE_SCHEME ) ) {
144+ if ( ! this . findAstTextEditor ( ) ) {
113145 this . setRustEditor ( undefined ) ;
114146 return ;
115147 }
116148 this . setRustEditor ( editors . find ( isRustEditor ) ) ;
117149 }
118150
151+ private findAstTextEditor ( ) : undefined | vscode . TextEditor {
152+ return vscode . window . visibleTextEditors . find ( it => it . document . uri . scheme === AST_FILE_SCHEME ) ;
153+ }
154+
119155 private setRustEditor ( newRustEditor : undefined | RustEditor ) {
120- if ( newRustEditor !== this . rustEditor ) {
121- this . rustEditor ?. setDecorations ( AstInspector . astDecorationType , [ ] ) ;
156+ if ( this . rustEditor && this . rustEditor !== newRustEditor ) {
157+ this . rustEditor . setDecorations ( this . astDecorationType , [ ] ) ;
158+ this . rust2Ast . reset ( ) ;
122159 }
123160 this . rustEditor = newRustEditor ;
124161 }
125162
163+ // additional positional params are omitted
164+ provideDefinition ( doc : vscode . TextDocument , pos : vscode . Position ) : vscode . ProviderResult < vscode . DefinitionLink [ ] > {
165+ if ( ! this . rustEditor || doc . uri . toString ( ) !== this . rustEditor . document . uri . toString ( ) ) return ;
166+
167+ const astEditor = this . findAstTextEditor ( ) ;
168+ if ( ! astEditor ) return ;
169+
170+ console . time ( "Goto def" ) ;
171+ const rust2AstRanges = this . rust2Ast . get ( ) ?. find ( ( [ rustRange , _ ] ) => rustRange . contains ( pos ) ) ;
172+ console . timeEnd ( "Goto def" ) ;
173+ if ( ! rust2AstRanges ) return ;
174+
175+ const [ rustFileRange , astFileRange ] = rust2AstRanges ;
176+
177+ astEditor . revealRange ( astFileRange ) ;
178+ astEditor . selection = new vscode . Selection ( astFileRange . start , astFileRange . end ) ;
179+
180+ return [ {
181+ targetRange : astFileRange ,
182+ targetUri : astEditor . document . uri ,
183+ originSelectionRange : rustFileRange ,
184+ targetSelectionRange : astFileRange ,
185+ } ] ;
186+ }
187+
188+ // additional positional params are omitted
126189 provideHover ( doc : vscode . TextDocument , hoverPosition : vscode . Position ) : vscode . ProviderResult < vscode . Hover > {
127190 if ( ! this . rustEditor ) return ;
128191
@@ -131,7 +194,7 @@ class AstInspector implements vscode.HoverProvider, Disposable {
131194 const rustTextRange = this . parseRustTextRange ( this . rustEditor . document , astTextLine . text ) ;
132195 if ( ! rustTextRange ) return ;
133196
134- this . rustEditor . setDecorations ( AstInspector . astDecorationType , [ rustTextRange ] ) ;
197+ this . rustEditor . setDecorations ( this . astDecorationType , [ rustTextRange ] ) ;
135198 this . rustEditor . revealRange ( rustTextRange ) ;
136199
137200 const rustSourceCode = this . rustEditor . document . getText ( rustTextRange ) ;
@@ -156,3 +219,18 @@ class AstInspector implements vscode.HoverProvider, Disposable {
156219 return new vscode . Range ( begin , end ) ;
157220 }
158221}
222+
223+ class Lazy < T > {
224+ val : undefined | T ;
225+
226+ constructor ( private readonly compute : ( ) => undefined | T ) { }
227+
228+ get ( ) {
229+ return this . val ?? ( this . val = this . compute ( ) ) ;
230+ }
231+
232+ reset ( ) {
233+ this . val = undefined ;
234+ }
235+
236+ }
0 commit comments