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