22//
33// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
44
5+ use std:: collections:: HashMap ;
6+ use std:: path:: { Path , PathBuf } ;
57use std:: sync:: LazyLock ;
68
9+ use rayon:: prelude:: * ;
710use regex:: Regex ;
8- use std:: path:: Path ;
911use tree_sitter:: { Language , Parser } ;
1012
1113use crate :: domain:: { CodeSymbol , FileChange , SymbolKind } ;
@@ -81,59 +83,49 @@ impl AnalyzerService {
8183 Ok ( Self )
8284 }
8385
84- /// Extract symbols from file changes using full file content + hunk mapping
86+ /// Extract symbols from file changes using full file content + hunk mapping.
87+ /// Uses rayon to parse files in parallel across CPU cores.
8588 pub fn extract_symbols (
86- & mut self ,
89+ & self ,
8790 changes : & [ FileChange ] ,
88- staged_content : & dyn Fn ( & Path ) -> Option < String > ,
89- head_content : & dyn Fn ( & Path ) -> Option < String > ,
91+ staged_content : & HashMap < PathBuf , String > ,
92+ head_content : & HashMap < PathBuf , String > ,
9093 ) -> Vec < CodeSymbol > {
91- let mut symbols = Vec :: new ( ) ;
92-
93- for change in changes {
94- if change. is_binary {
95- continue ;
96- }
97-
98- let ext = change
99- . path
100- . extension ( )
101- . and_then ( |e| e. to_str ( ) )
102- . unwrap_or ( "" ) ;
103-
104- let hunks = DiffHunk :: parse_from_diff ( & change. diff ) ;
105-
106- // Get the appropriate language for parsing
107- let language: Option < Language > = match ext {
108- "rs" => Some ( tree_sitter_rust:: LANGUAGE . into ( ) ) ,
109- "ts" | "tsx" => Some ( tree_sitter_typescript:: LANGUAGE_TYPESCRIPT . into ( ) ) ,
110- "py" => Some ( tree_sitter_python:: LANGUAGE . into ( ) ) ,
111- "go" => Some ( tree_sitter_go:: LANGUAGE . into ( ) ) ,
112- "js" | "jsx" => Some ( tree_sitter_javascript:: LANGUAGE . into ( ) ) ,
113- _ => None ,
114- } ;
115-
116- if let Some ( lang) = language {
117- let file_symbols = Self :: extract_for_file_static (
118- lang,
119- change,
120- & hunks,
121- staged_content,
122- head_content,
123- ) ;
124- symbols. extend ( file_symbols) ;
125- }
126- }
127-
128- symbols
94+ changes
95+ . par_iter ( )
96+ . filter ( |change| !change. is_binary )
97+ . flat_map ( |change| {
98+ let ext = change
99+ . path
100+ . extension ( )
101+ . and_then ( |e| e. to_str ( ) )
102+ . unwrap_or ( "" ) ;
103+
104+ let language: Option < Language > = match ext {
105+ "rs" => Some ( tree_sitter_rust:: LANGUAGE . into ( ) ) ,
106+ "ts" | "tsx" => Some ( tree_sitter_typescript:: LANGUAGE_TYPESCRIPT . into ( ) ) ,
107+ "py" => Some ( tree_sitter_python:: LANGUAGE . into ( ) ) ,
108+ "go" => Some ( tree_sitter_go:: LANGUAGE . into ( ) ) ,
109+ "js" | "jsx" => Some ( tree_sitter_javascript:: LANGUAGE . into ( ) ) ,
110+ _ => None ,
111+ } ;
112+
113+ language
114+ . map ( |lang| {
115+ let hunks = DiffHunk :: parse_from_diff ( & change. diff ) ;
116+ Self :: extract_for_file ( lang, change, & hunks, staged_content, head_content)
117+ } )
118+ . unwrap_or_default ( )
119+ } )
120+ . collect ( )
129121 }
130122
131- fn extract_for_file_static (
123+ fn extract_for_file (
132124 language : Language ,
133125 change : & FileChange ,
134126 hunks : & [ DiffHunk ] ,
135- staged_content : & dyn Fn ( & Path ) -> Option < String > ,
136- head_content : & dyn Fn ( & Path ) -> Option < String > ,
127+ staged_content : & HashMap < PathBuf , String > ,
128+ head_content : & HashMap < PathBuf , String > ,
137129 ) -> Vec < CodeSymbol > {
138130 let mut parser = Parser :: new ( ) ;
139131 if parser. set_language ( & language) . is_err ( ) {
@@ -143,23 +135,23 @@ impl AnalyzerService {
143135 let mut symbols = Vec :: new ( ) ;
144136
145137 // Parse staged (new) file content
146- if let Some ( content) = staged_content ( & change. path ) {
138+ if let Some ( content) = staged_content. get ( & change. path ) {
147139 let changed = Self :: extract_changed_symbols_static (
148140 & mut parser,
149141 & change. path ,
150- & content,
142+ content,
151143 hunks,
152144 true ,
153145 ) ;
154146 symbols. extend ( changed) ;
155147 }
156148
157149 // Parse HEAD (old) file content
158- if let Some ( content) = head_content ( & change. path ) {
150+ if let Some ( content) = head_content. get ( & change. path ) {
159151 let changed = Self :: extract_changed_symbols_static (
160152 & mut parser,
161153 & change. path ,
162- & content,
154+ content,
163155 hunks,
164156 false ,
165157 ) ;
0 commit comments