@@ -22,6 +22,37 @@ const Inclusivity = enum {
2222 exclusive_ignore_space ,
2323};
2424
25+ /// Check if a node is an @import() call or an alias/field access based on an import.
26+ /// This drills down field accesses to find the base, assuming identifiers are aliases.
27+ fn isImportOrAlias (tree : * const Ast , init_node : Ast.Node.Index ) bool {
28+ var node = init_node ;
29+ while (true ) {
30+ switch (tree .nodeTag (node )) {
31+ .builtin_call_two , .builtin_call_two_comma = > {
32+ // Check if this is @import("...")
33+ const token = tree .nodeMainToken (node );
34+ const builtin_name = offsets .tokenToSlice (tree , token );
35+ if (! std .mem .eql (u8 , builtin_name , "@import" )) return false ;
36+
37+ const first_param , const second_param = tree .nodeData (node ).opt_node_and_opt_node ;
38+ const param_node = first_param .unwrap () orelse return false ;
39+ if (second_param != .none ) return false ;
40+ return tree .nodeTag (param_node ) == .string_literal ;
41+ },
42+ .field_access = > {
43+ // Field access like @import("foo").bar or std.ascii
44+ // Continue drilling down to check the left side
45+ node = tree .nodeData (node ).node_and_token [0 ];
46+ },
47+ .identifier = > {
48+ // Assume identifiers are aliases like `const ascii = std.ascii`
49+ return true ;
50+ },
51+ else = > return false ,
52+ }
53+ }
54+ }
55+
2556const Builder = struct {
2657 allocator : std.mem.Allocator ,
2758 locations : std .ArrayList (FoldingRange ),
@@ -150,7 +181,39 @@ pub fn generateFoldingRanges(allocator: std.mem.Allocator, tree: *const Ast, enc
150181
151182 // TODO add folding range normal comments
152183
153- // TODO add folding range for top level `@Import()`
184+ // Folding range for top level imports
185+ {
186+ var start_import : ? Ast.Node.Index = null ;
187+ var end_import : ? Ast.Node.Index = null ;
188+
189+ const root_decls = tree .rootDecls ();
190+ for (root_decls ) | node | {
191+ const is_import = blk : {
192+ if (tree .nodeTag (node ) != .simple_var_decl ) break :blk false ;
193+ const var_decl = tree .simpleVarDecl (node );
194+ const init_node = var_decl .ast .init_node .unwrap () orelse break :blk false ;
195+
196+ break :blk isImportOrAlias (tree , init_node );
197+ };
198+
199+ if (is_import ) {
200+ if (start_import == null ) {
201+ start_import = node ;
202+ }
203+ end_import = node ;
204+ } else if (start_import != null and end_import != null ) {
205+ // We found a non-import after a sequence of imports, create folding range
206+ try builder .add (null , tree .firstToken (start_import .? ), ast .lastToken (tree , end_import .? ), .inclusive , .inclusive );
207+ start_import = null ;
208+ end_import = null ;
209+ }
210+ }
211+
212+ // Handle the case where imports continue to the end of the file
213+ if (start_import != null and end_import != null and start_import .? != end_import .? ) {
214+ try builder .add (null , tree .firstToken (start_import .? ), ast .lastToken (tree , end_import .? ), .inclusive , .inclusive );
215+ }
216+ }
154217
155218 for (0.. tree .nodes .len ) | i | {
156219 const node : Ast.Node.Index = @enumFromInt (i );
0 commit comments