main.rs

  1use std::sync::Arc;
  2
  3use clap::Parser;
  4use client::{Client, UserStore};
  5use gpui::{AppContext as _, Application};
  6use http_client::FakeHttpClient;
  7use language::LanguageRegistry;
  8use node_runtime::NodeRuntime;
  9use project::{
 10    Project, RealFs,
 11    search::{SearchQuery, SearchResult},
 12};
 13
 14#[derive(Parser)]
 15struct Args {
 16    /// List of worktrees to run the search against.
 17    worktrees: Vec<String>,
 18    #[clap(short)]
 19    query: String,
 20    /// Treat query as a regex.
 21    #[clap(short, long)]
 22    regex: bool,
 23    /// Matches have to be standalone words.
 24    #[clap(long)]
 25    whole_word: bool,
 26    /// Make matching case-sensitive.
 27    #[clap(long, default_value_t = false)]
 28    case_sensitive: bool,
 29    /// Include gitignored files in the search.
 30    #[clap(long)]
 31    include_ignored: bool,
 32}
 33
 34fn main() -> Result<(), anyhow::Error> {
 35    let args = Args::parse();
 36    let query = if args.regex {
 37        SearchQuery::regex(
 38            args.query,
 39            args.whole_word,
 40            args.case_sensitive,
 41            args.include_ignored,
 42            false,
 43            Default::default(),
 44            Default::default(),
 45            false,
 46            None,
 47        )
 48    } else {
 49        SearchQuery::text(
 50            args.query,
 51            args.whole_word,
 52            args.case_sensitive,
 53            args.include_ignored,
 54            Default::default(),
 55            Default::default(),
 56            false,
 57            None,
 58        )
 59    }?;
 60    Application::headless().run(|cx| {
 61        settings::init(cx);
 62        let client = Client::production(cx);
 63        let http_client = FakeHttpClient::with_200_response();
 64        let (_, rx) = watch::channel(None);
 65        let node = NodeRuntime::new(http_client, None, rx);
 66        let user_store = cx.new(|cx| UserStore::new(client.clone(), cx));
 67        let registry = Arc::new(LanguageRegistry::new(cx.background_executor().clone()));
 68        let fs = Arc::new(RealFs::new(None, cx.background_executor().clone()));
 69        let project = Project::local(
 70            client,
 71            node,
 72            user_store,
 73            registry,
 74            fs,
 75            Some(Default::default()),
 76            false,
 77            cx,
 78        );
 79
 80        project.clone().update(cx, move |_, cx| {
 81            cx.spawn(async move |_, cx| {
 82                println!("Loading worktrees");
 83                let worktrees = project.update(cx, |this, cx| {
 84                    args.worktrees
 85                        .into_iter()
 86                        .map(|worktree| this.find_or_create_worktree(worktree, true, cx))
 87                        .collect::<Vec<_>>()
 88                })?;
 89
 90                let worktrees = futures::future::join_all(worktrees)
 91                    .await
 92                    .into_iter()
 93                    .collect::<Result<Vec<_>, anyhow::Error>>()?;
 94
 95                for (worktree, _) in &worktrees {
 96                    worktree
 97                        .update(cx, |this, _| this.as_local().unwrap().scan_complete())?
 98                        .await;
 99                }
100                println!("Worktrees loaded");
101
102                println!("Starting a project search");
103                let timer = std::time::Instant::now();
104                let mut first_match = None;
105                let matches = project
106                    .update(cx, |this, cx| this.search(query, cx))
107                    .unwrap();
108                let mut matched_files = 0;
109                let mut matched_chunks = 0;
110                while let Ok(match_result) = matches.recv().await {
111                    if first_match.is_none() {
112                        let time = timer.elapsed();
113                        first_match = Some(time);
114                        println!("First match found after {time:?}");
115                    }
116                    if let SearchResult::Buffer { ranges, .. } = match_result {
117                        matched_files += 1;
118                        matched_chunks += ranges.len();
119                    } else {
120                        break;
121                    }
122                }
123                let elapsed = timer.elapsed();
124                println!(
125                    "Finished project search after {elapsed:?}. Matched {matched_files} files and {matched_chunks} excerpts"
126                );
127                drop(project);
128                cx.update(|cx| cx.quit())?;
129
130                anyhow::Ok(())
131            })
132            .detach();
133        });
134    });
135    Ok(())
136}