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            cx,
 77        );
 78
 79        project.clone().update(cx, move |_, cx| {
 80            cx.spawn(async move |_, cx| {
 81                println!("Loading worktrees");
 82                let worktrees = project.update(cx, |this, cx| {
 83                    args.worktrees
 84                        .into_iter()
 85                        .map(|worktree| this.find_or_create_worktree(worktree, true, cx))
 86                        .collect::<Vec<_>>()
 87                })?;
 88
 89                let worktrees = futures::future::join_all(worktrees)
 90                    .await
 91                    .into_iter()
 92                    .collect::<Result<Vec<_>, anyhow::Error>>()?;
 93
 94                for (worktree, _) in &worktrees {
 95                    worktree
 96                        .update(cx, |this, _| this.as_local().unwrap().scan_complete())?
 97                        .await;
 98                }
 99                println!("Worktrees loaded");
100
101                println!("Starting a project search");
102                let timer = std::time::Instant::now();
103                let mut first_match = None;
104                let matches = project
105                    .update(cx, |this, cx| this.search(query, cx))
106                    .unwrap();
107                let mut matched_files = 0;
108                let mut matched_chunks = 0;
109                while let Ok(match_result) = matches.recv().await {
110                    if first_match.is_none() {
111                        let time = timer.elapsed();
112                        first_match = Some(time);
113                        println!("First match found after {time:?}");
114                    }
115                    if let SearchResult::Buffer { ranges, .. } = match_result {
116                        matched_files += 1;
117                        matched_chunks += ranges.len();
118                    } else {
119                        break;
120                    }
121                }
122                let elapsed = timer.elapsed();
123                println!(
124                    "Finished project search after {elapsed:?}. Matched {matched_files} files and {matched_chunks} excerpts"
125                );
126                drop(project);
127                cx.update(|cx| cx.quit())?;
128
129                anyhow::Ok(())
130            })
131            .detach();
132        });
133    });
134    Ok(())
135}