DevelopmentNodeEnvironment_MicrosoftVSCodeDependency_22NodeVersion_Bundle_Clean_Debug_ElectronProfile_EsbuildCompiler_Mountain/RPC/CocoonService/FileSystem/
FindTextInFiles.rs1use tonic::{Response, Status};
6
7use crate::{
8 RPC::CocoonService::CocoonServiceImpl,
9 Vine::Generated::{FindTextInFilesRequest, FindTextInFilesResponse, Position, Range, TextMatch, Uri},
10 dev_log,
11};
12
13pub async fn Fn(
14 Service:&CocoonServiceImpl,
15
16 Request:FindTextInFilesRequest,
17) -> Result<Response<FindTextInFilesResponse>, Status> {
18 if Request.pattern.is_empty() {
19 return Ok(Response::new(FindTextInFilesResponse::default()));
20 }
21
22 dev_log!("cocoon", "[CocoonService] find_text_in_files: pattern='{}'", Request.pattern);
23
24 let Roots:Vec<std::path::PathBuf> = {
25 match Service.environment.ApplicationState.Workspace.WorkspaceFolders.lock() {
26 Ok(Guard) => Guard.iter().map(|F| std::path::PathBuf::from(F.URI.path())).collect(),
27
28 Err(_) => Vec::new(),
29 }
30 };
31
32 let SearchRoots = if Roots.is_empty() {
33 vec![std::env::current_dir().unwrap_or_default()]
34 } else {
35 Roots
36 };
37
38 let Pattern = Request.pattern.clone();
39
40 let Matches = tokio::task::spawn_blocking(move || {
41 let mut Results:Vec<TextMatch> = Vec::new();
42 const MAX_MATCHES:usize = 1000;
43
44 fn WalkAndSearch(Directory:&std::path::Path, Pattern:&str, Results:&mut Vec<TextMatch>) {
45 if Results.len() >= MAX_MATCHES {
46 return;
47 }
48 if let Ok(Entries) = std::fs::read_dir(Directory) {
49 for Entry in Entries.flatten() {
50 if Results.len() >= MAX_MATCHES {
51 break;
52 }
53 let Path = Entry.path();
54 if Path.is_dir() {
55 let Name = Path.file_name().and_then(|N| N.to_str()).unwrap_or("");
56 if Name.starts_with('.') || Name == "node_modules" || Name == "target" {
57 continue;
58 }
59 WalkAndSearch(&Path, Pattern, Results);
60 } else if Path.is_file() {
61 if let Ok(Content) = std::fs::read_to_string(&Path) {
62 for (LineIndex, Line) in Content.lines().enumerate() {
63 if Results.len() >= MAX_MATCHES {
64 break;
65 }
66 if let Some(ColumnIndex) = Line.find(Pattern) {
67 Results.push(TextMatch {
68 uri:Some(Uri { value:format!("file://{}", Path.display()) }),
69 range:Some(Range {
70 start:Some(Position {
71 line:LineIndex as u32,
72 character:ColumnIndex as u32,
73 }),
74 end:Some(Position {
75 line:LineIndex as u32,
76 character:(ColumnIndex + Pattern.len()) as u32,
77 }),
78 }),
79 preview:Line.to_string(),
80 });
81 }
82 }
83 }
84 }
85 }
86 }
87 }
88
89 for Root in &SearchRoots {
90 WalkAndSearch(Root, &Pattern, &mut Results);
91 if Results.len() >= MAX_MATCHES {
92 break;
93 }
94 }
95 Results
96 })
97 .await
98 .unwrap_or_default();
99
100 dev_log!(
101 "cocoon",
102 "[CocoonService] find_text_in_files: {} matches for '{}'",
103 Matches.len(),
104 Request.pattern
105 );
106
107 Ok(Response::new(FindTextInFilesResponse { matches:Matches }))
108}