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