11import * as vscode from 'vscode' ;
22import * as path from 'path' ;
33import * as fs from 'fs' ;
4+ import * as os from 'os' ;
45import * as toml from 'toml' ;
56import { getAllRazDirectories } from './utils/workspace' ;
67
@@ -42,9 +43,9 @@ export class RazOverrideTreeProvider implements vscode.TreeDataProvider<Override
4243 if ( ! element ) {
4344 // Root level - show all projects with .raz directories
4445 return Promise . resolve ( this . getRazProjects ( ) ) ;
45- } else if ( element . contextValue === 'razProject' ) {
46+ } else if ( element . contextValue === 'razProject' || element . contextValue === 'razGlobalProject' ) {
4647 // For projects, show overrides
47- return Promise . resolve ( this . getProjectChildren ( element . filePath ! ) ) ;
48+ return Promise . resolve ( this . getProjectChildren ( element . filePath ! , element . contextValue === 'razGlobalProject' ) ) ;
4849 } else if ( element . contextValue === 'razOverride' ) {
4950 // Show override details as children
5051 return Promise . resolve ( this . getOverrideDetails ( element ) ) ;
@@ -59,6 +60,55 @@ export class RazOverrideTreeProvider implements vscode.TreeDataProvider<Override
5960 const items : OverrideItem [ ] = [ ] ;
6061 const workspaceRoots = new Set < string > ( ) ;
6162
63+ // Add global overrides section first
64+ const globalRazPath = path . join ( os . homedir ( ) , '.raz' ) ;
65+ const globalOverridePath = path . join ( globalRazPath , 'overrides.toml' ) ;
66+
67+ if ( fs . existsSync ( globalOverridePath ) ) {
68+ // For global overrides, the overrides.toml is directly in .raz folder
69+ let globalOverrideCount = 0 ;
70+ try {
71+ const content = fs . readFileSync ( globalOverridePath , 'utf8' ) ;
72+ const data = toml . parse ( content ) ;
73+
74+ if ( data . overrides ) {
75+ // Filter global overrides to only those relevant to current workspace
76+ const workspaceFolders = vscode . workspace . workspaceFolders ;
77+ const workspacePaths = workspaceFolders ? workspaceFolders . map ( folder => folder . uri . fsPath ) : [ ] ;
78+
79+ let relevantCount = 0 ;
80+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
81+ for ( const [ , override ] of Object . entries ( data . overrides as Record < string , any > ) ) {
82+ if ( override && override . metadata && override . metadata . file_path ) {
83+ const filePath = override . metadata . file_path ;
84+ // Check if this file is within any workspace folder
85+ const isRelevant = workspacePaths . some ( workspacePath =>
86+ filePath . startsWith ( workspacePath )
87+ ) ;
88+ if ( isRelevant ) {
89+ relevantCount ++ ;
90+ }
91+ }
92+ }
93+ globalOverrideCount = relevantCount ;
94+ }
95+ } catch ( error ) {
96+ console . error ( 'Error counting global overrides:' , error ) ;
97+ }
98+
99+ if ( globalOverrideCount > 0 ) {
100+ const globalItem = new OverrideItem (
101+ `Standalone (${ globalOverrideCount } override${ globalOverrideCount !== 1 ? 's' : '' } )` ,
102+ vscode . TreeItemCollapsibleState . Collapsed ,
103+ 'razGlobalProject' ,
104+ globalRazPath
105+ ) ;
106+ globalItem . iconPath = new vscode . ThemeIcon ( 'globe' ) ;
107+ globalItem . tooltip = `${ globalRazPath } \n${ globalOverrideCount } standalone override${ globalOverrideCount !== 1 ? 's' : '' } ` ;
108+ items . push ( globalItem ) ;
109+ }
110+ }
111+
62112 // First pass: identify all workspace roots
63113 for ( const dir of razDirs ) {
64114 const cargoTomlPath = path . join ( dir , 'Cargo.toml' ) ;
@@ -80,6 +130,11 @@ export class RazOverrideTreeProvider implements vscode.TreeDataProvider<Override
80130 continue ;
81131 }
82132
133+ // Skip the global .raz directory (home directory) since we handle it separately as "Standalone"
134+ if ( dir === os . homedir ( ) ) {
135+ continue ;
136+ }
137+
83138 // Skip if this directory is inside a workspace (will be handled by workspace)
84139 let isInsideWorkspace = false ;
85140 for ( const wsRoot of workspaceRoots ) {
@@ -166,13 +221,17 @@ export class RazOverrideTreeProvider implements vscode.TreeDataProvider<Override
166221 return items ;
167222 }
168223
169- private getProjectChildren ( projectPath : string ) : OverrideItem [ ] {
224+ private getProjectChildren ( projectPath : string , isGlobal = false ) : OverrideItem [ ] {
170225 // Simply get the overrides for this project
171- return this . getProjectOverrides ( projectPath ) ;
226+ return this . getProjectOverrides ( projectPath , isGlobal ) ;
172227 }
173228
174- private getProjectOverrides ( projectPath : string ) : OverrideItem [ ] {
175- const overridePath = path . join ( projectPath , '.raz' , 'overrides.toml' ) ;
229+ private getProjectOverrides ( projectPath : string , isGlobal = false ) : OverrideItem [ ] {
230+ // For global overrides, the path is different
231+ const overridePath = isGlobal
232+ ? path . join ( projectPath , 'overrides.toml' )
233+ : path . join ( projectPath , '.raz' , 'overrides.toml' ) ;
234+
176235 if ( ! fs . existsSync ( overridePath ) ) {
177236 return [ ] ;
178237 }
@@ -183,9 +242,68 @@ export class RazOverrideTreeProvider implements vscode.TreeDataProvider<Override
183242 const items : OverrideItem [ ] = [ ] ;
184243
185244 if ( data . overrides ) {
186- for ( const [ , override ] of Object . entries ( data . overrides as Record < string , OverrideEntry > ) ) {
245+ // Get workspace paths for filtering
246+ const workspaceFolders = vscode . workspace . workspaceFolders ;
247+ const workspacePaths = workspaceFolders ? workspaceFolders . map ( folder => folder . uri . fsPath ) : [ ] ;
248+
249+ // For global overrides, each key is the override ID and contains the full override object
250+ for ( const [ overrideKey , override ] of Object . entries ( data . overrides as Record < string , OverrideEntry > ) ) {
251+ if ( ! override || typeof override !== 'object' ) {
252+ continue ;
253+ }
254+
255+ // For global overrides, only show those relevant to current workspace
256+ if ( isGlobal && override . metadata && override . metadata . file_path ) {
257+ const filePath = override . metadata . file_path ;
258+ const isRelevant = workspacePaths . some ( workspacePath =>
259+ filePath . startsWith ( workspacePath )
260+ ) ;
261+ if ( ! isRelevant ) {
262+ continue ;
263+ }
264+ }
265+
187266 const metadata = override . metadata ;
188- const functionName = metadata . function_name || 'unknown' ;
267+
268+ // Try to extract a better function name
269+ let functionName = metadata . function_name ;
270+
271+ if ( ! functionName || functionName === 'unknown' ) {
272+ // Try to extract from the override key
273+ const keyParts = overrideKey . split ( ':' ) ;
274+ if ( keyParts . length >= 2 ) {
275+ const lastPart = keyParts [ keyParts . length - 1 ] ;
276+ // Check if it looks like a function name (not just a line number)
277+ if ( lastPart && ! lastPart . startsWith ( 'L' ) && ! / ^ \d + $ / . test ( lastPart ) ) {
278+ functionName = lastPart ;
279+ } else if ( keyParts . length >= 3 ) {
280+ // Maybe it's file:line:function format
281+ const potentialFunction = keyParts [ keyParts . length - 2 ] ;
282+ if ( potentialFunction && ! potentialFunction . startsWith ( 'L' ) && ! / ^ \d + $ / . test ( potentialFunction ) ) {
283+ functionName = potentialFunction ;
284+ }
285+ }
286+ }
287+
288+ // If still no good name, check if it's a doctest or test
289+ if ( ! functionName || functionName === 'unknown' ) {
290+ const overrideConfigKey = override . override_config ?. key || '' ;
291+ const isTestRelated = overrideKey . includes ( 'doctest' ) ||
292+ overrideConfigKey === 'test' ||
293+ overrideConfigKey . includes ( 'test' ) ||
294+ overrideKey . includes ( ':L' ) ; // Line-based overrides are often doctests
295+
296+ if ( isTestRelated ) {
297+ // Try to extract from file path for doctests
298+ const filePath = metadata . file_path || '' ;
299+ const fileName = filePath . split ( '/' ) . pop ( ) ?. replace ( '.rs' , '' ) || 'doctest' ;
300+ functionName = `${ fileName } _doctest` ;
301+ } else {
302+ functionName = 'unknown' ;
303+ }
304+ }
305+ }
306+
189307 const line = metadata . original_line || 0 ;
190308
191309 // Create main override item with just function name
@@ -251,6 +369,7 @@ export class RazOverrideTreeProvider implements vscode.TreeDataProvider<Override
251369 }
252370 }
253371
372+ console . log ( `Returning ${ items . length } items for ${ isGlobal ? 'global' : 'project' } overrides` ) ;
254373 return items . sort ( ( a , b ) => a . label . localeCompare ( b . label ) ) ;
255374 } catch ( error ) {
256375 console . error ( 'Failed to parse overrides:' , error ) ;
0 commit comments