├── .gitignore ├── README.md ├── examples ├── lsp_client.rs ├── treesitter.rs └── walker.rs ├── Cargo.toml ├── src ├── main.rs ├── lsp │ └── rust.rs ├── project │ ├── workspace.rs │ ├── dependency_graph.rs │ └── document.rs ├── lsp.rs ├── lib.rs └── project.rs └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lopper 2 | 3 | Lopper is a code pruning tool that returns only the code related to a query. Lopper builds a dependency graph of projects via language servers and is able to safely prune irrelevant code segments and files. 4 | 5 | https://github.com/user-attachments/assets/aaabd677-c394-403a-9d3a-9f175b287483 6 | -------------------------------------------------------------------------------- /examples/lsp_client.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | use anyhow::Result; 4 | use lopper::lsp::{LspAdapter, rust::RustAnalyzerAdapter}; 5 | 6 | #[tokio::main(flavor = "current_thread")] 7 | async fn main() -> Result<()> { 8 | let cwd = env::current_dir()?; 9 | let rust_analyzer = RustAnalyzerAdapter {}; 10 | let process = rust_analyzer.start(&cwd).await?; 11 | process.shutdown().await?; 12 | Ok(()) 13 | } 14 | -------------------------------------------------------------------------------- /examples/treesitter.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | 3 | use anyhow::{Context, Result}; 4 | use tree_sitter::Parser; 5 | 6 | const CURRENT_PATH: &str = "examples/treesitter.rs"; 7 | 8 | fn main() -> Result<()> { 9 | let mut parser = Parser::new(); 10 | parser 11 | .set_language(&tree_sitter_rust::LANGUAGE.into()) 12 | .context("failed to set language")?; 13 | 14 | let text = fs::read_to_string(CURRENT_PATH).context("failed to read file")?; 15 | let tree = parser.parse(&text, None).context("failed to parse file")?; 16 | 17 | println!("{}", tree.root_node().to_sexp()); 18 | 19 | Ok(()) 20 | } 21 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lopper" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | anyhow = "1.0.97" 8 | async-lsp = "0.2.2" 9 | async-process = "2.3.0" 10 | async-trait = "0.1.88" 11 | baz-tree-sitter-traversal = "0.1.4" 12 | clap = { version = "4.5.32", features = ["derive"] } 13 | ignore = "0.4.23" 14 | indexmap = "2.8.0" 15 | itertools = "0.14.0" 16 | # See: https://github.com/gluon-lang/lsp-types/issues/284 17 | lsp-types = "0.95" 18 | serde_json = "1.0.140" 19 | thiserror = "2.0.12" 20 | tokio = { version = "1.44.1", features = ["macros", "rt", "sync"] } 21 | tower = "0.5.2" 22 | tree-sitter = "0.25.3" 23 | tree-sitter-rust = "0.23.2" 24 | url = "2.5.4" 25 | -------------------------------------------------------------------------------- /examples/walker.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | env, 3 | io::{BufWriter, Write, stdout}, 4 | sync::mpsc, 5 | }; 6 | 7 | use anyhow::{Context, Result, anyhow}; 8 | use ignore::{WalkBuilder, WalkState}; 9 | 10 | fn main() -> Result<()> { 11 | let cwd = env::current_dir().context("current directory not found")?; 12 | 13 | // Print directory entry paths in a separate thread. 14 | let (tx, rx) = mpsc::channel::(); 15 | let stdout_thread = std::thread::spawn(move || { 16 | let mut stdout = BufWriter::new(stdout()); 17 | for dirent in rx { 18 | let path = dirent.path().to_string_lossy(); 19 | writeln!(&mut stdout, "{}", path).unwrap(); 20 | } 21 | }); 22 | 23 | // Walk the current directory in parallel. 24 | let walker = WalkBuilder::new(&cwd).threads(4).build_parallel(); 25 | walker.run(|| { 26 | Box::new(|result| { 27 | let entry = result.unwrap(); 28 | tx.send(entry).unwrap(); 29 | WalkState::Continue 30 | }) 31 | }); 32 | 33 | drop(tx); 34 | stdout_thread 35 | .join() 36 | .map_err(|_| anyhow!("failed to join stdout thread"))?; 37 | 38 | Ok(()) 39 | } 40 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{env, path::PathBuf}; 2 | 3 | use anyhow::{Context, Result}; 4 | use clap::Parser; 5 | use lopper::{Position, project::Project}; 6 | 7 | #[derive(clap::Parser, Debug)] 8 | struct Config { 9 | #[clap(short, long)] 10 | directory: Option, 11 | #[clap(short, long)] 12 | file: PathBuf, 13 | #[clap(short, long)] 14 | line: usize, 15 | #[clap(short, long)] 16 | character: usize, 17 | } 18 | 19 | #[tokio::main(flavor = "current_thread")] 20 | async fn main() -> Result<()> { 21 | let config = Config::parse(); 22 | 23 | let directory = match config.directory { 24 | Some(directory) => directory.canonicalize().context("directory not found")?, 25 | None => env::current_dir().context("current directory not found")?, 26 | }; 27 | 28 | let project = Project::new(&directory)?; 29 | let graph = project.dependency_graph().await?; 30 | 31 | let file_path = config.file.canonicalize().context("file not found")?; 32 | let node = project 33 | .find_node(&file_path, Position::new(config.line, config.character)) 34 | .context("node not found")?; 35 | 36 | let dependencies = graph.get_dependencies(node); 37 | let output = project.render(&dependencies)?; 38 | println!("{}", output); 39 | 40 | Ok(()) 41 | } 42 | -------------------------------------------------------------------------------- /src/lsp/rust.rs: -------------------------------------------------------------------------------- 1 | use std::ops::ControlFlow; 2 | 3 | use async_lsp::{LanguageClient, ResponseError}; 4 | use lsp_types::{ 5 | NumberOrString, ProgressParams, ProgressParamsValue, PublishDiagnosticsParams, 6 | ShowMessageParams, WorkDoneProgress, 7 | }; 8 | use tokio::sync::oneshot; 9 | 10 | use super::LspAdapter; 11 | 12 | #[derive(Default)] 13 | pub struct RustAnalyzerAdapter {} 14 | 15 | impl LspAdapter for RustAnalyzerAdapter { 16 | type State = RustAnalyzerState; 17 | 18 | fn create_state(&self, indexed_tx: oneshot::Sender<()>) -> Self::State { 19 | RustAnalyzerState::new(indexed_tx) 20 | } 21 | 22 | fn get_server_command(&self) -> async_process::Command { 23 | async_process::Command::new("rust-analyzer") 24 | } 25 | } 26 | 27 | pub struct RustAnalyzerState { 28 | indexed_tx: Option>, 29 | } 30 | 31 | impl RustAnalyzerState { 32 | fn new(indexed_tx: oneshot::Sender<()>) -> Self { 33 | Self { 34 | indexed_tx: Some(indexed_tx), 35 | } 36 | } 37 | } 38 | 39 | impl LanguageClient for RustAnalyzerState { 40 | type Error = ResponseError; 41 | type NotifyResult = ControlFlow>; 42 | 43 | fn progress(&mut self, params: ProgressParams) -> Self::NotifyResult { 44 | match params { 45 | ProgressParams { 46 | token: NumberOrString::String(s), 47 | value: ProgressParamsValue::WorkDone(WorkDoneProgress::End(_)), 48 | } if s == "rustAnalyzer/Indexing" => { 49 | if let Some(tx) = self.indexed_tx.take() { 50 | let _ = tx.send(()); 51 | } 52 | } 53 | _ => {} 54 | } 55 | ControlFlow::Continue(()) 56 | } 57 | 58 | fn show_message(&mut self, _: ShowMessageParams) -> Self::NotifyResult { 59 | ControlFlow::Continue(()) 60 | } 61 | 62 | fn publish_diagnostics(&mut self, _: PublishDiagnosticsParams) -> Self::NotifyResult { 63 | ControlFlow::Continue(()) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/project/workspace.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | path::{Path, PathBuf}, 3 | process::Command, 4 | }; 5 | 6 | use indexmap::IndexMap; 7 | 8 | use super::document::{Document, DocumentRef}; 9 | 10 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 11 | pub struct WorkspaceRef(pub(crate) usize, pub(crate) usize); 12 | 13 | #[derive(Debug)] 14 | pub struct Workspace { 15 | pub id: Option, 16 | pub kind: WorkspaceKind, 17 | documents: IndexMap, 18 | } 19 | 20 | impl Workspace { 21 | /// Creates a new `Workspace`. 22 | pub fn new(kind: WorkspaceKind) -> Self { 23 | Self { 24 | kind, 25 | id: None, 26 | documents: IndexMap::new(), 27 | } 28 | } 29 | 30 | /// Returns whether the workspace may contain files of the file type of the given path. 31 | pub fn matches(&self, path: &Path) -> bool { 32 | match self.kind { 33 | WorkspaceKind::Rust => path.extension().is_some_and(|ext| ext == "rs"), 34 | } 35 | } 36 | 37 | /// Adds a document to the workspace. 38 | pub fn add_document(&mut self, path: &Path, mut document: Document) { 39 | let index = self.documents.len(); 40 | let dr = DocumentRef(index); 41 | document.id = Some(dr); 42 | self.documents.insert(path.to_path_buf(), document); 43 | } 44 | 45 | /// Returns the document at the given path. 46 | pub fn get_document(&self, path: &Path) -> Option<&Document> { 47 | self.documents.get(path) 48 | } 49 | 50 | /// Returns the document at the given document reference. 51 | pub fn get_document_index(&self, dr: DocumentRef) -> (&PathBuf, &Document) { 52 | self.documents.get_index(dr.0).unwrap() 53 | } 54 | 55 | /// Returns an iterator over the documents in the workspace. 56 | pub fn iter(&self) -> impl Iterator { 57 | self.documents 58 | .iter() 59 | .map(|(path, document)| (path.as_path(), document)) 60 | } 61 | } 62 | 63 | #[derive(Debug)] 64 | pub enum WorkspaceKind { 65 | Rust, 66 | } 67 | 68 | trait WorkspaceProvider { 69 | /// Searches for a workspace at the given path. 70 | fn search(&self, path: &Path) -> Option; 71 | } 72 | 73 | struct RustWorkspaceProvider; 74 | 75 | impl WorkspaceProvider for RustWorkspaceProvider { 76 | fn search(&self, path: &Path) -> Option { 77 | let manifest_path = path.join("Cargo.toml"); 78 | if manifest_path.exists() { 79 | // Check if the package is a member of a workspace. Only return a `Workspace` if it 80 | // isn't and is the root of a workspace. 81 | let output = Command::new("cargo") 82 | .args(["metadata", "--no-deps", "--format-version", "1"]) 83 | .current_dir(path) 84 | .output() 85 | .ok()?; 86 | let json = serde_json::from_slice::(&output.stdout).ok()?; 87 | let workspace_root = PathBuf::from(json["workspace_root"].as_str()?); 88 | if path == workspace_root { 89 | Some(Workspace::new(WorkspaceKind::Rust)) 90 | } else { 91 | None 92 | } 93 | } else { 94 | None 95 | } 96 | } 97 | } 98 | 99 | /// Returns a list of workspaces at the given path. 100 | pub fn get_workspaces_at(path: &Path) -> Vec { 101 | let workspace_providers: Vec> = 102 | vec![Box::new(RustWorkspaceProvider)]; 103 | workspace_providers 104 | .into_iter() 105 | .filter_map(|p| p.search(path)) 106 | .collect() 107 | } 108 | -------------------------------------------------------------------------------- /src/lsp.rs: -------------------------------------------------------------------------------- 1 | use std::{env, ops::ControlFlow, path::Path, process::Stdio}; 2 | 3 | use async_lsp::{ 4 | LanguageClient, LanguageServer, ResponseError, ServerSocket, concurrency::ConcurrencyLayer, 5 | panic::CatchUnwindLayer, router::Router, 6 | }; 7 | use async_trait::async_trait; 8 | use lsp_types::{ 9 | ClientCapabilities, InitializeParams, InitializedParams, WindowClientCapabilities, 10 | WorkspaceFolder, 11 | }; 12 | use thiserror::Error; 13 | use tokio::{sync::oneshot, task::JoinHandle}; 14 | use tower::ServiceBuilder; 15 | use url::Url; 16 | 17 | pub mod rust; 18 | 19 | #[derive(Error, Debug)] 20 | pub enum Error { 21 | #[error("Language server error: {0}")] 22 | LanguageServer(#[from] async_lsp::Error), 23 | #[error("Language server IO error: {0}")] 24 | Io(#[from] std::io::Error), 25 | #[error("Message receiving error: {0}")] 26 | Recv(#[from] oneshot::error::RecvError), 27 | #[error("Join error: {0}")] 28 | Join(#[from] tokio::task::JoinError), 29 | #[error("Invalid path")] 30 | InvalidPath, 31 | } 32 | 33 | #[async_trait] 34 | pub trait LspAdapter { 35 | type State: LanguageClient>, Error = ResponseError> 36 | + Send 37 | + 'static; 38 | 39 | fn create_state(&self, indexed_tx: oneshot::Sender<()>) -> Self::State; 40 | 41 | fn get_server_command(&self) -> async_process::Command; 42 | 43 | fn initialize_params(&self, root: &Path) -> Result { 44 | let uri = Url::from_file_path(root).map_err(|_| Error::InvalidPath)?; 45 | Ok(InitializeParams { 46 | workspace_folders: Some(vec![WorkspaceFolder { 47 | uri, 48 | name: "root".to_string(), 49 | }]), 50 | capabilities: ClientCapabilities { 51 | window: Some(WindowClientCapabilities { 52 | work_done_progress: Some(true), 53 | ..Default::default() 54 | }), 55 | ..Default::default() 56 | }, 57 | ..Default::default() 58 | }) 59 | } 60 | 61 | async fn start(&self, path: &Path) -> Result { 62 | let (indexed_tx, indexed_rx) = oneshot::channel(); 63 | let state = self.create_state(indexed_tx); 64 | 65 | let router = new_router(state); 66 | let (mainloop, mut server) = async_lsp::MainLoop::new_client(|_server| { 67 | ServiceBuilder::new() 68 | .layer(CatchUnwindLayer::default()) 69 | .layer(ConcurrencyLayer::default()) 70 | .service(router) 71 | }); 72 | 73 | let cwd = env::current_dir()?; 74 | let mut command = self.get_server_command(); 75 | let mut child = command 76 | .stdin(Stdio::piped()) 77 | .stdout(Stdio::piped()) 78 | .stderr(Stdio::inherit()) 79 | .current_dir(cwd) 80 | .spawn()?; 81 | 82 | let stdin = child.stdin.take().unwrap(); 83 | let stdout = child.stdout.take().unwrap(); 84 | 85 | let mainloop_fut = tokio::spawn(async move { 86 | mainloop.run_buffered(stdout, stdin).await.unwrap(); 87 | }); 88 | 89 | server.initialize(self.initialize_params(path)?).await?; 90 | 91 | server.initialized(InitializedParams {})?; 92 | 93 | indexed_rx.await?; 94 | 95 | let process = LspProcess::new(server, mainloop_fut); 96 | 97 | Ok(process) 98 | } 99 | } 100 | 101 | fn new_router(state: S) -> Router 102 | where 103 | S: LanguageClient>, Error = ResponseError>, 104 | { 105 | let mut router = Router::from_language_client(state); 106 | router.event(|_, _: Stop| ControlFlow::Break(Ok(()))); 107 | router 108 | } 109 | 110 | pub struct LspProcess { 111 | pub server: ServerSocket, 112 | handle: JoinHandle<()>, 113 | } 114 | 115 | impl LspProcess { 116 | fn new(server: ServerSocket, handle: JoinHandle<()>) -> Self { 117 | Self { server, handle } 118 | } 119 | 120 | pub async fn shutdown(mut self) -> Result<(), Error> { 121 | self.server.shutdown(()).await?; 122 | self.server.exit(())?; 123 | self.server.emit(Stop)?; 124 | self.handle.await?; 125 | Ok(()) 126 | } 127 | } 128 | 129 | struct Stop; 130 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod lsp; 2 | pub mod project; 3 | 4 | /// Represents a 1-indexed position in a document. 5 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 6 | pub struct Position { 7 | pub line: usize, 8 | pub character: usize, 9 | } 10 | 11 | impl Position { 12 | pub fn new(line: usize, character: usize) -> Self { 13 | Self { line, character } 14 | } 15 | } 16 | 17 | impl From for Position { 18 | /// Converts a 0-indexed Tree-sitter point to a 1-indexed position. 19 | fn from(point: tree_sitter::Point) -> Self { 20 | Position { 21 | line: point.row + 1, 22 | character: point.column + 1, 23 | } 24 | } 25 | } 26 | 27 | impl From for Position { 28 | /// Converts a 0-indexed LSP position to a 1-indexed position. 29 | fn from(position: lsp_types::Position) -> Self { 30 | Self { 31 | line: position.line as usize + 1, 32 | character: position.character as usize + 1, 33 | } 34 | } 35 | } 36 | 37 | impl From for lsp_types::Position { 38 | /// Converts a 1-indexed position to a 0-indexed LSP position. 39 | fn from(position: Position) -> Self { 40 | Self { 41 | line: position.line as u32 - 1, 42 | character: position.character as u32 - 1, 43 | } 44 | } 45 | } 46 | 47 | /// Represents a half-open range in a document (i.e., [start, end)). 48 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 49 | pub struct Span { 50 | pub start: Position, 51 | pub end: Position, 52 | pub start_byte: usize, 53 | pub end_byte: usize, 54 | } 55 | 56 | impl Span { 57 | pub fn new(start: Position, end: Position, start_byte: usize, end_byte: usize) -> Self { 58 | Self { 59 | start, 60 | end, 61 | start_byte, 62 | end_byte, 63 | } 64 | } 65 | 66 | /// Returns whether the current span contains the other span. 67 | pub fn contains(&self, other: &Self) -> bool { 68 | self.start <= other.start && other.end <= self.end 69 | } 70 | } 71 | 72 | impl PartialOrd for Span { 73 | fn partial_cmp(&self, other: &Self) -> Option { 74 | if self.start == other.start && self.end == other.end { 75 | Some(std::cmp::Ordering::Equal) 76 | } else if self.end <= other.start { 77 | Some(std::cmp::Ordering::Less) 78 | } else if self.start >= other.end { 79 | Some(std::cmp::Ordering::Greater) 80 | } else { 81 | None 82 | } 83 | } 84 | } 85 | 86 | impl From for Span { 87 | fn from(range: tree_sitter::Range) -> Self { 88 | Self { 89 | start: range.start_point.into(), 90 | end: range.end_point.into(), 91 | start_byte: range.start_byte, 92 | end_byte: range.end_byte, 93 | } 94 | } 95 | } 96 | 97 | #[cfg(test)] 98 | mod tests { 99 | use std::cmp::Ordering; 100 | 101 | use super::*; 102 | 103 | #[test] 104 | fn treesitter_point_to_position() { 105 | let point = tree_sitter::Point::new(0, 0); 106 | let position = Position::from(point); 107 | assert_eq!(position, Position::new(1, 1)); 108 | } 109 | 110 | #[test] 111 | fn lsp_position_to_position() { 112 | let position = lsp_types::Position::new(0, 0); 113 | let position = Position::from(position); 114 | assert_eq!(position, Position::new(1, 1)); 115 | } 116 | 117 | #[test] 118 | fn position_to_lsp_position() { 119 | let position = Position::new(1, 1); 120 | let position = lsp_types::Position::from(position); 121 | assert_eq!(position, lsp_types::Position::new(0, 0)); 122 | } 123 | 124 | #[test] 125 | fn span_partial_cmp() { 126 | let a = Span::new(Position::new(0, 0), Position::new(2, 0), 0, 2); 127 | let b = Span::new(Position::new(0, 0), Position::new(1, 0), 0, 1); 128 | let c = Span::new(Position::new(1, 0), Position::new(2, 0), 1, 2); 129 | 130 | assert!(a.partial_cmp(&a).unwrap() == Ordering::Equal); 131 | assert!(a.partial_cmp(&b).is_none()); 132 | assert!(a.partial_cmp(&c).is_none()); 133 | 134 | assert!(b.partial_cmp(&a).is_none()); 135 | assert!(b.partial_cmp(&b).unwrap() == Ordering::Equal); 136 | assert!(b.partial_cmp(&c).unwrap() == Ordering::Less); 137 | 138 | assert!(c.partial_cmp(&a).is_none()); 139 | assert!(c.partial_cmp(&b).unwrap() == Ordering::Greater); 140 | assert!(c.partial_cmp(&c).unwrap() == Ordering::Equal); 141 | } 142 | 143 | #[test] 144 | fn span_overlapping_partial_cmp() { 145 | let a = Span::new(Position::new(0, 0), Position::new(10, 0), 0, 10); 146 | let b = Span::new(Position::new(5, 0), Position::new(15, 0), 5, 15); 147 | let c = Span::new(Position::new(10, 0), Position::new(20, 0), 10, 20); 148 | 149 | assert!(a.partial_cmp(&a).unwrap() == Ordering::Equal); 150 | assert!(a.partial_cmp(&b).is_none()); 151 | assert!(a.partial_cmp(&c).unwrap() == Ordering::Less); 152 | 153 | assert!(b.partial_cmp(&a).is_none()); 154 | assert!(b.partial_cmp(&b).unwrap() == Ordering::Equal); 155 | assert!(b.partial_cmp(&c).is_none()); 156 | 157 | assert!(c.partial_cmp(&a).unwrap() == Ordering::Greater); 158 | assert!(c.partial_cmp(&b).is_none()); 159 | assert!(c.partial_cmp(&c).unwrap() == Ordering::Equal); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/project/dependency_graph.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashSet, path::Path}; 2 | 3 | use indexmap::{IndexMap, IndexSet}; 4 | use lsp_types::{ 5 | PartialResultParams, ReferenceContext, ReferenceParams, TextDocumentIdentifier, 6 | TextDocumentPositionParams, WorkDoneProgressParams, 7 | }; 8 | use thiserror::Error; 9 | use url::Url; 10 | 11 | use crate::{ 12 | Position, 13 | lsp::{self, LspAdapter, LspProcess, rust::RustAnalyzerAdapter}, 14 | project::{Project, ProjectRef}, 15 | }; 16 | 17 | use super::workspace::WorkspaceKind; 18 | 19 | #[derive(Error, Debug)] 20 | pub enum Error { 21 | #[error("LSP error: {0}")] 22 | Lsp(#[from] lsp::Error), 23 | #[error("Invalid path")] 24 | InvalidPath, 25 | } 26 | 27 | #[derive(Debug, Default)] 28 | pub struct DependencyGraph { 29 | graph: IndexMap>, 30 | } 31 | 32 | impl DependencyGraph { 33 | /// Creates a new `DependencyGraph`. 34 | pub fn new() -> Self { 35 | Self::default() 36 | } 37 | 38 | /// Returns the set of nodes that `project_ref` depends on, including itself. 39 | pub fn get_dependencies(&self, project_ref: ProjectRef) -> HashSet { 40 | let mut dependencies = HashSet::new(); 41 | let mut refs_visited = HashSet::new(); 42 | let mut refs_to_visit = vec![project_ref]; 43 | while let Some(current_ref) = refs_to_visit.pop() { 44 | if !refs_visited.insert(current_ref) { 45 | continue; 46 | } 47 | 48 | dependencies.insert(current_ref); 49 | 50 | if let Some(dependencies) = self.graph.get(¤t_ref) { 51 | refs_to_visit.extend(dependencies); 52 | } 53 | } 54 | dependencies 55 | } 56 | 57 | /// Adds a dependency from `from` to `to`. 58 | pub fn add_dependency(&mut self, from: ProjectRef, to: ProjectRef) -> bool { 59 | self.graph.entry(from).or_default().insert(to) 60 | } 61 | } 62 | 63 | impl Project { 64 | /// Returns a dependency graph for the project. 65 | pub async fn dependency_graph(&self) -> Result { 66 | let mut graph = DependencyGraph::new(); 67 | 68 | for (workspace_path, workspace) in self.iter() { 69 | let lsp = new_lsp(&workspace.kind, workspace_path).await?; 70 | 71 | for (document_path, document) in workspace.iter() { 72 | for node in document.iter() { 73 | if let Some(identifier) = node.identifier { 74 | let to = ProjectRef( 75 | workspace.id.unwrap(), 76 | document.id.unwrap(), 77 | node.id.unwrap(), 78 | ); 79 | 80 | for reference in 81 | get_references(&lsp, document_path, identifier.start.into()).await? 82 | { 83 | let reference_path = reference 84 | .uri 85 | .to_file_path() 86 | .map_err(|_| Error::InvalidPath)?; 87 | let reference_position = Position::from(reference.range.start); 88 | 89 | if let Some(from) = self.find_node(&reference_path, reference_position) 90 | { 91 | graph.add_dependency(from, to); 92 | } else { 93 | println!( 94 | "No node found at {}, {:?} for {}, {:?}", 95 | reference_path.display(), 96 | reference_position, 97 | document_path.display(), 98 | identifier.start 99 | ); 100 | } 101 | } 102 | } 103 | } 104 | } 105 | 106 | lsp.shutdown().await?; 107 | } 108 | 109 | Ok(graph) 110 | } 111 | 112 | /// Returns the node at the given position in the project. 113 | pub fn find_node(&self, path: &Path, position: Position) -> Option { 114 | if let Some(workspace) = self.get_workspace(path) { 115 | if let Some(document) = workspace.get_document(path) { 116 | if let Some(node) = document.find_node(position) { 117 | return Some(ProjectRef( 118 | workspace.id.unwrap(), 119 | document.id.unwrap(), 120 | node.id.unwrap(), 121 | )); 122 | } 123 | } 124 | } 125 | None 126 | } 127 | } 128 | 129 | async fn new_lsp(kind: &WorkspaceKind, path: &Path) -> Result { 130 | match kind { 131 | WorkspaceKind::Rust => RustAnalyzerAdapter::default().start(path).await, 132 | } 133 | } 134 | 135 | async fn get_references( 136 | lsp: &LspProcess, 137 | path: &Path, 138 | position: lsp_types::Position, 139 | ) -> Result, lsp::Error> { 140 | let uri = Url::from_file_path(path).map_err(|_| lsp::Error::InvalidPath)?; 141 | let params = ReferenceParams { 142 | text_document_position: TextDocumentPositionParams { 143 | position, 144 | text_document: TextDocumentIdentifier { uri }, 145 | }, 146 | work_done_progress_params: WorkDoneProgressParams::default(), 147 | partial_result_params: PartialResultParams::default(), 148 | context: ReferenceContext { 149 | include_declaration: false, 150 | }, 151 | }; 152 | Ok(lsp 153 | .server 154 | .request::(params) 155 | .await? 156 | .unwrap_or_default()) 157 | } 158 | 159 | #[cfg(test)] 160 | mod tests { 161 | use crate::project::{ 162 | document::{DocumentRef, NodeRef}, 163 | workspace::WorkspaceRef, 164 | }; 165 | 166 | use super::*; 167 | 168 | fn dependency_ref(workspace: usize, document: usize, node: usize) -> ProjectRef { 169 | ProjectRef( 170 | WorkspaceRef(workspace, 0), 171 | DocumentRef(document), 172 | NodeRef(node), 173 | ) 174 | } 175 | 176 | #[test] 177 | fn dependency_graph_returns_all_dependencies() { 178 | let mut graph = DependencyGraph::default(); 179 | graph.add_dependency(dependency_ref(0, 0, 0), dependency_ref(0, 0, 1)); 180 | graph.add_dependency(dependency_ref(0, 0, 1), dependency_ref(0, 0, 2)); 181 | graph.add_dependency(dependency_ref(0, 0, 2), dependency_ref(0, 0, 3)); 182 | 183 | let dependencies = graph.get_dependencies(dependency_ref(0, 0, 0)); 184 | assert_eq!(dependencies.len(), 4); 185 | assert!(dependencies.contains(&dependency_ref(0, 0, 0))); 186 | assert!(dependencies.contains(&dependency_ref(0, 0, 1))); 187 | assert!(dependencies.contains(&dependency_ref(0, 0, 2))); 188 | assert!(dependencies.contains(&dependency_ref(0, 0, 3))); 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/project.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::HashSet, 3 | path::{Path, PathBuf}, 4 | }; 5 | 6 | use document::{DocumentRef, NodeRef}; 7 | use ignore::WalkBuilder; 8 | use indexmap::IndexMap; 9 | use itertools::Itertools; 10 | use thiserror::Error; 11 | use workspace::{Workspace, WorkspaceRef}; 12 | 13 | pub mod dependency_graph; 14 | pub mod document; 15 | pub mod workspace; 16 | 17 | #[derive(Error, Debug)] 18 | pub enum Error { 19 | #[error("Document error: {0}")] 20 | Document(#[from] document::Error), 21 | #[error("IO error: {0}")] 22 | Io(#[from] std::io::Error), 23 | } 24 | 25 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 26 | pub struct ProjectRef( 27 | pub(crate) WorkspaceRef, 28 | pub(crate) DocumentRef, 29 | pub(crate) NodeRef, 30 | ); 31 | 32 | #[derive(Debug)] 33 | pub struct Project { 34 | workspaces: IndexMap>, 35 | } 36 | 37 | impl Project { 38 | /// Constructs a new `Project` from the given directory. 39 | pub fn new(path: &Path) -> Result { 40 | let mut project = Self { 41 | workspaces: IndexMap::new(), 42 | }; 43 | let path = path.canonicalize()?; 44 | let walker = WalkBuilder::new(path).build(); 45 | 46 | for entry in walker.into_iter().filter_map(|e| e.ok()) { 47 | match entry.file_type() { 48 | Some(file_type) if file_type.is_dir() => { 49 | let new_workspaces = workspace::get_workspaces_at(entry.path()); 50 | for workspace in new_workspaces { 51 | project.add_workspace(entry.path(), workspace); 52 | } 53 | } 54 | Some(file_type) if file_type.is_file() => { 55 | let document = document::parse_document(entry.path())?; 56 | if let Some(document) = document { 57 | match project.get_workspace_mut(entry.path()) { 58 | Some(workspace) => { 59 | workspace.add_document(entry.path(), document); 60 | } 61 | None => { 62 | println!("No workspace found for file {}", entry.path().display()); 63 | } 64 | } 65 | } 66 | } 67 | _ => {} 68 | } 69 | } 70 | 71 | Ok(project) 72 | } 73 | 74 | fn add_workspace(&mut self, path: &Path, mut workspace: Workspace) { 75 | let map_index = self.workspaces.len(); 76 | let workspaces = self.workspaces.entry(path.to_path_buf()).or_default(); 77 | let vec_index = workspaces.len(); 78 | let wr = WorkspaceRef(map_index, vec_index); 79 | workspace.id = Some(wr); 80 | workspaces.push(workspace); 81 | } 82 | 83 | /// Returns the workspace that contains the given file. 84 | pub fn get_workspace(&self, file: &Path) -> Option<&Workspace> { 85 | let mut path = file.to_path_buf(); 86 | loop { 87 | let workspace = self 88 | .workspaces 89 | .get(&path) 90 | .and_then(|ws| ws.iter().find(|w| w.matches(file))); 91 | 92 | if let Some(workspace) = workspace { 93 | return Some(workspace); 94 | } 95 | 96 | if !path.pop() { 97 | break; 98 | } 99 | } 100 | None 101 | } 102 | 103 | /// Returns a mutable reference to the workspace that contains the given file. 104 | pub fn get_workspace_mut(&mut self, file: &Path) -> Option<&mut Workspace> { 105 | let mut path = file.to_path_buf(); 106 | loop { 107 | let index = self 108 | .workspaces 109 | .get(&path) 110 | .and_then(|ws| ws.iter().position(|w| w.matches(file))); 111 | 112 | if let Some(i) = index { 113 | return self.workspaces.get_mut(&path).and_then(|ws| ws.get_mut(i)); 114 | } 115 | 116 | if !path.pop() { 117 | break; 118 | } 119 | } 120 | None 121 | } 122 | 123 | /// Returns the workspace at the workspace reference. 124 | pub fn get_workspace_index(&self, wr: WorkspaceRef) -> (&PathBuf, &Workspace) { 125 | self.workspaces 126 | .get_index(wr.0) 127 | .map(|(path, ws)| (path, &ws[wr.1])) 128 | .unwrap() 129 | } 130 | 131 | /// Returns an iterator over the workspaces in the project. 132 | pub fn iter(&self) -> impl Iterator { 133 | self.workspaces 134 | .iter() 135 | .flat_map(|(path, workspaces)| workspaces.iter().map(|w| (path.as_path(), w))) 136 | } 137 | 138 | /// Partially renders the components of the project that are represented by the given set of 139 | /// references. 140 | pub fn render(&self, refs: &HashSet) -> Result { 141 | let mut output = String::new(); 142 | 143 | for ((wr, dr), prs) in &refs.iter().sorted().chunk_by(|r| (r.0, r.1)) { 144 | let (_, workspace) = self.get_workspace_index(wr); 145 | let (path, document) = workspace.get_document_index(dr); 146 | let nrs = prs.map(|pr| pr.2).collect_vec(); 147 | output.push_str(&document.render(path, &nrs)?); 148 | } 149 | 150 | Ok(output) 151 | } 152 | } 153 | 154 | #[cfg(test)] 155 | mod tests { 156 | use super::{workspace::WorkspaceKind, *}; 157 | 158 | #[test] 159 | fn get_workspace_returns_most_specific() { 160 | let mut project = Project { 161 | workspaces: IndexMap::new(), 162 | }; 163 | project.add_workspace(&PathBuf::from("a"), Workspace::new(WorkspaceKind::Rust)); 164 | project.add_workspace(&PathBuf::from("a/b"), Workspace::new(WorkspaceKind::Rust)); 165 | 166 | assert_eq!(workspace_id(&project, PathBuf::from("test.rs")), None); 167 | assert_eq!( 168 | workspace_id(&project, PathBuf::from("a/test.rs")), 169 | Some(WorkspaceRef(0, 0)) 170 | ); 171 | assert_eq!( 172 | workspace_id(&project, PathBuf::from("a/b/test.rs")), 173 | Some(WorkspaceRef(1, 0)) 174 | ); 175 | assert_eq!( 176 | workspace_id(&project, PathBuf::from("a/b/c/test.rs")), 177 | Some(WorkspaceRef(1, 0)) 178 | ); 179 | } 180 | 181 | #[test] 182 | fn get_workspace_equivalent_to_get_workspace_mut() { 183 | let mut project = Project { 184 | workspaces: IndexMap::new(), 185 | }; 186 | project.add_workspace(&PathBuf::from("a"), Workspace::new(WorkspaceKind::Rust)); 187 | project.add_workspace(&PathBuf::from("a/b"), Workspace::new(WorkspaceKind::Rust)); 188 | 189 | assert_eq!( 190 | workspace_id(&project, PathBuf::from("test.rs")), 191 | workspace_id_mut(&mut project, PathBuf::from("test.rs")) 192 | ); 193 | assert_eq!( 194 | workspace_id(&project, PathBuf::from("a/test.rs")), 195 | workspace_id_mut(&mut project, PathBuf::from("a/test.rs")) 196 | ); 197 | assert_eq!( 198 | workspace_id(&project, PathBuf::from("a/b/test.rs")), 199 | workspace_id_mut(&mut project, PathBuf::from("a/b/test.rs")) 200 | ); 201 | assert_eq!( 202 | workspace_id(&project, PathBuf::from("a/b/c/test.rs")), 203 | workspace_id_mut(&mut project, PathBuf::from("a/b/c/test.rs")) 204 | ); 205 | } 206 | 207 | fn workspace_id(project: &Project, path: PathBuf) -> Option { 208 | project.get_workspace(&path).and_then(|w| w.id) 209 | } 210 | 211 | fn workspace_id_mut(project: &mut Project, path: PathBuf) -> Option { 212 | project.get_workspace_mut(&path).and_then(|w| w.id) 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /src/project/document.rs: -------------------------------------------------------------------------------- 1 | use core::str; 2 | use std::{ 3 | collections::{HashMap, HashSet, VecDeque}, 4 | fs, 5 | path::Path, 6 | }; 7 | 8 | use indexmap::IndexSet; 9 | use thiserror::Error; 10 | use tree_sitter::{Parser, Tree}; 11 | use tree_sitter_traversal::{Order, traverse_tree}; 12 | 13 | use crate::{Position, Span}; 14 | 15 | #[derive(Error, Debug)] 16 | pub enum Error { 17 | #[error("Incompatible Tree-sitter language: {0}")] 18 | Language(#[from] tree_sitter::LanguageError), 19 | #[error("IO error: {0}")] 20 | Io(#[from] std::io::Error), 21 | #[error("UTF-8 error: {0}")] 22 | Utf8(#[from] std::str::Utf8Error), 23 | } 24 | 25 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 26 | pub struct DocumentRef(pub(crate) usize); 27 | 28 | #[derive(Debug)] 29 | pub struct Document { 30 | pub id: Option, 31 | nodes: Vec, 32 | } 33 | 34 | impl Document { 35 | fn new() -> Self { 36 | Self { 37 | id: None, 38 | nodes: Vec::new(), 39 | } 40 | } 41 | 42 | /// Returns the node at the given position. 43 | pub fn find_node(&self, position: Position) -> Option<&Node> { 44 | // Assert that nodes are in increasing span order. 45 | debug_assert!(self.nodes.iter().is_sorted_by_key(|n| n.span.start)); 46 | // Assert that nodes are contained by their parents. 47 | debug_assert!(self.nodes.iter().all(|n| n.parent.is_none_or(|parent_ref| { 48 | let parent = &self.nodes[parent_ref.0]; 49 | parent.span.contains(&n.span) 50 | }))); 51 | 52 | let mut left = 0; 53 | let mut right = self.nodes.len(); 54 | let mut best = None; 55 | 56 | while left < right { 57 | let mid = left + (right - left) / 2; 58 | let node = &self.nodes[mid]; 59 | if node.span.start <= position { 60 | if position < node.span.end { 61 | best = Some(node); 62 | } 63 | left = mid + 1; 64 | } else { 65 | right = mid; 66 | } 67 | } 68 | 69 | best 70 | } 71 | 72 | /// Returns an iterator over the nodes in the document. 73 | pub fn iter(&self) -> impl Iterator { 74 | self.nodes.iter() 75 | } 76 | 77 | fn get_node(&self, nr: NodeRef) -> &Node { 78 | &self.nodes[nr.0] 79 | } 80 | 81 | fn get_node_mut(&mut self, nr: NodeRef) -> &mut Node { 82 | &mut self.nodes[nr.0] 83 | } 84 | 85 | fn add_node(&mut self, mut node: Node) -> NodeRef { 86 | let index = self.nodes.len(); 87 | let nr = NodeRef(index); 88 | node.id = Some(nr); 89 | self.nodes.push(node); 90 | nr 91 | } 92 | 93 | /// Partially renders the document. 94 | pub(crate) fn render(&self, path: &Path, leaf_refs: &[NodeRef]) -> Result { 95 | let text = fs::read_to_string(path)?; 96 | let bytes = text.as_bytes(); 97 | let refs = self.create_render_tree(leaf_refs); 98 | let mut output = String::new(); 99 | 100 | // Render the document from the parent nodes. 101 | for nr in &refs { 102 | let node = self.get_node(*nr); 103 | if node.parent.is_none() { 104 | output.push_str(&self.render_node(bytes, *nr, &refs, node.span.start_byte)?); 105 | output.push_str("\n\n"); 106 | } 107 | } 108 | 109 | Ok(output) 110 | } 111 | 112 | fn create_render_tree(&self, leaf_refs: &[NodeRef]) -> IndexSet { 113 | let mut render_tree = IndexSet::new(); 114 | let mut refs_visited = HashSet::new(); 115 | let mut refs_to_visit = VecDeque::from(leaf_refs.to_vec()); 116 | 117 | while let Some(current_ref) = refs_to_visit.pop_front() { 118 | if !refs_visited.insert(current_ref) { 119 | continue; 120 | } 121 | 122 | render_tree.insert(current_ref); 123 | 124 | let node = self.get_node(current_ref); 125 | if let Some(parent_ref) = node.parent { 126 | refs_to_visit.push_front(parent_ref); 127 | } 128 | } 129 | 130 | render_tree 131 | } 132 | 133 | fn render_node( 134 | &self, 135 | bytes: &[u8], 136 | nr: NodeRef, 137 | refs: &IndexSet, 138 | start_byte: usize, 139 | ) -> Result { 140 | let mut output = String::new(); 141 | let node = self.get_node(nr); 142 | 143 | // The text before the node which may contain leading whitespace, comments, etc. 144 | let leading = str::from_utf8(&bytes[start_byte..node.span.start_byte])?; 145 | output.push_str(leading); 146 | 147 | if refs.contains(&nr) { 148 | let mut byte_index = node.span.start_byte; 149 | 150 | for child_ref in &node.children { 151 | let child = self.get_node(*child_ref); 152 | output.push_str(&self.render_node(bytes, *child_ref, refs, byte_index)?); 153 | byte_index = child.span.end_byte; 154 | } 155 | 156 | // The text after the last child. 157 | let trailing = str::from_utf8(&bytes[byte_index..node.span.end_byte])?; 158 | output.push_str(trailing); 159 | } else { 160 | output.push_str("// Unused code omitted for brevity."); 161 | } 162 | 163 | Ok(output) 164 | } 165 | } 166 | 167 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 168 | pub struct NodeRef(pub(crate) usize); 169 | 170 | #[derive(Debug)] 171 | pub struct Node { 172 | pub id: Option, 173 | parent: Option, 174 | children: Vec, 175 | pub span: Span, 176 | pub identifier: Option, 177 | } 178 | 179 | impl Node { 180 | pub fn new(parent: Option, span: Span, identifier: Option) -> Self { 181 | Self { 182 | parent, 183 | span, 184 | identifier, 185 | id: None, 186 | children: Vec::new(), 187 | } 188 | } 189 | 190 | fn add_child(&mut self, child: NodeRef) { 191 | self.children.push(child); 192 | } 193 | } 194 | 195 | trait DocumentParser { 196 | /// Returns whether the document parser may parse the file at the given path. 197 | fn matches(&self, path: &Path) -> bool; 198 | /// Returns a Tree-sitter parser for the document parser. 199 | fn parser(&self) -> Result; 200 | /// Parses the Tree-sitter tree into a document. 201 | fn parse_tree(&self, tree: &Tree) -> Result; 202 | } 203 | 204 | struct RustDocumentParser; 205 | 206 | impl DocumentParser for RustDocumentParser { 207 | fn matches(&self, path: &Path) -> bool { 208 | path.extension().is_some_and(|ext| ext == "rs") 209 | } 210 | 211 | fn parser(&self) -> Result { 212 | let mut parser = Parser::new(); 213 | parser.set_language(&tree_sitter_rust::LANGUAGE.into())?; 214 | Ok(parser) 215 | } 216 | 217 | fn parse_tree(&self, tree: &Tree) -> Result { 218 | let mut document = Document::new(); 219 | let mut nr_by_ts_id = HashMap::new(); 220 | 221 | for node in traverse_tree(tree, Order::Pre) { 222 | match node.kind() { 223 | "mod_item" | "struct_item" | "enum_item" | "trait_item" | "impl_item" 224 | | "function_item" | "const_item" | "type_item" => { 225 | let parent_nr = { 226 | let mut parent_nr = None; 227 | let mut cursor = node; 228 | while let Some(parent) = cursor.parent() { 229 | if let Some(nr) = nr_by_ts_id.get(&parent.id()) { 230 | parent_nr = Some(*nr); 231 | break; 232 | } 233 | cursor = parent; 234 | } 235 | parent_nr 236 | }; 237 | let span = node.range().into(); 238 | let identifier = node.child_by_field_name("name").map(|n| n.range().into()); 239 | let nr = document.add_node(Node::new(parent_nr, span, identifier)); 240 | 241 | if let Some(parent) = parent_nr { 242 | document.get_node_mut(parent).add_child(nr); 243 | } 244 | 245 | nr_by_ts_id.insert(node.id(), nr); 246 | } 247 | _ => {} 248 | } 249 | } 250 | 251 | Ok(document) 252 | } 253 | } 254 | 255 | /// Parses the file at the given path into a document. 256 | pub fn parse_document(path: &Path) -> Result, Error> { 257 | let document_parsers: Vec> = vec![Box::new(RustDocumentParser)]; 258 | let document_parser = match document_parsers.into_iter().find(|p| p.matches(path)) { 259 | Some(parser) => parser, 260 | None => return Ok(None), 261 | }; 262 | 263 | let text = fs::read_to_string(path)?; 264 | let mut parser = document_parser.parser()?; 265 | if let Some(tree) = parser.parse(&text, None) { 266 | let document = document_parser.parse_tree(&tree)?; 267 | Ok(Some(document)) 268 | } else { 269 | Ok(None) 270 | } 271 | } 272 | 273 | #[cfg(test)] 274 | mod tests { 275 | use super::*; 276 | 277 | #[test] 278 | fn find_node_most_specific() { 279 | let mut document = Document::new(); 280 | document.add_node(Node::new(None, new_span((0, 0), (30, 0)), None)); 281 | document.add_node(Node::new( 282 | Some(NodeRef(0)), 283 | new_span((10, 0), (20, 0)), 284 | None, 285 | )); 286 | document.add_node(Node::new( 287 | Some(NodeRef(0)), 288 | new_span((20, 0), (30, 0)), 289 | None, 290 | )); 291 | 292 | assert_eq!(node_id(&document, Position::new(5, 0)), Some(NodeRef(0))); 293 | assert_eq!(node_id(&document, Position::new(15, 0)), Some(NodeRef(1))); 294 | assert_eq!(node_id(&document, Position::new(25, 0)), Some(NodeRef(2))); 295 | } 296 | 297 | #[test] 298 | fn find_node_span_inclusive_start_and_exclusive_end() { 299 | let mut document = Document::new(); 300 | document.add_node(Node::new(None, new_span((1, 0), (2, 0)), None)); 301 | document.add_node(Node::new(None, new_span((2, 0), (3, 0)), None)); 302 | document.add_node(Node::new(None, new_span((3, 0), (4, 0)), None)); 303 | 304 | assert_eq!(node_id(&document, Position::new(0, 0)), None); 305 | assert_eq!(node_id(&document, Position::new(1, 0)), Some(NodeRef(0))); 306 | assert_eq!(node_id(&document, Position::new(2, 0)), Some(NodeRef(1))); 307 | assert_eq!(node_id(&document, Position::new(3, 0)), Some(NodeRef(2))); 308 | assert_eq!(node_id(&document, Position::new(4, 0)), None); 309 | } 310 | 311 | fn new_span( 312 | (start_line, start_character): (usize, usize), 313 | (end_line, end_character): (usize, usize), 314 | ) -> Span { 315 | Span::new( 316 | Position::new(start_line, start_character), 317 | Position::new(end_line, end_character), 318 | start_line, 319 | end_line, 320 | ) 321 | } 322 | 323 | fn node_id(document: &Document, position: Position) -> Option { 324 | document.find_node(position).and_then(|n| n.id) 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.24.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler2" 16 | version = "2.0.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 19 | 20 | [[package]] 21 | name = "aho-corasick" 22 | version = "1.1.3" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 25 | dependencies = [ 26 | "memchr", 27 | ] 28 | 29 | [[package]] 30 | name = "anstream" 31 | version = "0.6.18" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" 34 | dependencies = [ 35 | "anstyle", 36 | "anstyle-parse", 37 | "anstyle-query", 38 | "anstyle-wincon", 39 | "colorchoice", 40 | "is_terminal_polyfill", 41 | "utf8parse", 42 | ] 43 | 44 | [[package]] 45 | name = "anstyle" 46 | version = "1.0.10" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" 49 | 50 | [[package]] 51 | name = "anstyle-parse" 52 | version = "0.2.6" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" 55 | dependencies = [ 56 | "utf8parse", 57 | ] 58 | 59 | [[package]] 60 | name = "anstyle-query" 61 | version = "1.1.2" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" 64 | dependencies = [ 65 | "windows-sys 0.59.0", 66 | ] 67 | 68 | [[package]] 69 | name = "anstyle-wincon" 70 | version = "3.0.7" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" 73 | dependencies = [ 74 | "anstyle", 75 | "once_cell", 76 | "windows-sys 0.59.0", 77 | ] 78 | 79 | [[package]] 80 | name = "anyhow" 81 | version = "1.0.97" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" 84 | 85 | [[package]] 86 | name = "async-channel" 87 | version = "2.3.1" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" 90 | dependencies = [ 91 | "concurrent-queue", 92 | "event-listener-strategy", 93 | "futures-core", 94 | "pin-project-lite", 95 | ] 96 | 97 | [[package]] 98 | name = "async-io" 99 | version = "2.4.0" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" 102 | dependencies = [ 103 | "async-lock", 104 | "cfg-if", 105 | "concurrent-queue", 106 | "futures-io", 107 | "futures-lite", 108 | "parking", 109 | "polling", 110 | "rustix 0.38.44", 111 | "slab", 112 | "tracing", 113 | "windows-sys 0.59.0", 114 | ] 115 | 116 | [[package]] 117 | name = "async-lock" 118 | version = "3.4.0" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" 121 | dependencies = [ 122 | "event-listener", 123 | "event-listener-strategy", 124 | "pin-project-lite", 125 | ] 126 | 127 | [[package]] 128 | name = "async-lsp" 129 | version = "0.2.2" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "6c1c85c4bb41706ad1f8338e39fa725a24bc642be41140a38d818c93b9ae91f5" 132 | dependencies = [ 133 | "futures", 134 | "lsp-types", 135 | "pin-project-lite", 136 | "rustix 1.0.3", 137 | "serde", 138 | "serde_json", 139 | "thiserror", 140 | "tokio", 141 | "tower-layer", 142 | "tower-service", 143 | "tracing", 144 | "waitpid-any", 145 | ] 146 | 147 | [[package]] 148 | name = "async-process" 149 | version = "2.3.0" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" 152 | dependencies = [ 153 | "async-channel", 154 | "async-io", 155 | "async-lock", 156 | "async-signal", 157 | "async-task", 158 | "blocking", 159 | "cfg-if", 160 | "event-listener", 161 | "futures-lite", 162 | "rustix 0.38.44", 163 | "tracing", 164 | ] 165 | 166 | [[package]] 167 | name = "async-signal" 168 | version = "0.2.10" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" 171 | dependencies = [ 172 | "async-io", 173 | "async-lock", 174 | "atomic-waker", 175 | "cfg-if", 176 | "futures-core", 177 | "futures-io", 178 | "rustix 0.38.44", 179 | "signal-hook-registry", 180 | "slab", 181 | "windows-sys 0.59.0", 182 | ] 183 | 184 | [[package]] 185 | name = "async-task" 186 | version = "4.7.1" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" 189 | 190 | [[package]] 191 | name = "async-trait" 192 | version = "0.1.88" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" 195 | dependencies = [ 196 | "proc-macro2", 197 | "quote", 198 | "syn", 199 | ] 200 | 201 | [[package]] 202 | name = "atomic-waker" 203 | version = "1.1.2" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 206 | 207 | [[package]] 208 | name = "autocfg" 209 | version = "1.4.0" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 212 | 213 | [[package]] 214 | name = "backtrace" 215 | version = "0.3.74" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" 218 | dependencies = [ 219 | "addr2line", 220 | "cfg-if", 221 | "libc", 222 | "miniz_oxide", 223 | "object", 224 | "rustc-demangle", 225 | "windows-targets", 226 | ] 227 | 228 | [[package]] 229 | name = "baz-tree-sitter-traversal" 230 | version = "0.1.4" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "b2ae77a4ce9f364f49c87fdaf55d1ed79fb301003788acd26b64a46bda8f2a62" 233 | dependencies = [ 234 | "tree-sitter", 235 | ] 236 | 237 | [[package]] 238 | name = "bitflags" 239 | version = "1.3.2" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 242 | 243 | [[package]] 244 | name = "bitflags" 245 | version = "2.9.0" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" 248 | 249 | [[package]] 250 | name = "blocking" 251 | version = "1.6.1" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" 254 | dependencies = [ 255 | "async-channel", 256 | "async-task", 257 | "futures-io", 258 | "futures-lite", 259 | "piper", 260 | ] 261 | 262 | [[package]] 263 | name = "bstr" 264 | version = "1.11.3" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" 267 | dependencies = [ 268 | "memchr", 269 | "serde", 270 | ] 271 | 272 | [[package]] 273 | name = "cc" 274 | version = "1.2.17" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" 277 | dependencies = [ 278 | "shlex", 279 | ] 280 | 281 | [[package]] 282 | name = "cfg-if" 283 | version = "1.0.0" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 286 | 287 | [[package]] 288 | name = "clap" 289 | version = "4.5.32" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" 292 | dependencies = [ 293 | "clap_builder", 294 | "clap_derive", 295 | ] 296 | 297 | [[package]] 298 | name = "clap_builder" 299 | version = "4.5.32" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" 302 | dependencies = [ 303 | "anstream", 304 | "anstyle", 305 | "clap_lex", 306 | "strsim", 307 | ] 308 | 309 | [[package]] 310 | name = "clap_derive" 311 | version = "4.5.32" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" 314 | dependencies = [ 315 | "heck", 316 | "proc-macro2", 317 | "quote", 318 | "syn", 319 | ] 320 | 321 | [[package]] 322 | name = "clap_lex" 323 | version = "0.7.4" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" 326 | 327 | [[package]] 328 | name = "colorchoice" 329 | version = "1.0.3" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" 332 | 333 | [[package]] 334 | name = "concurrent-queue" 335 | version = "2.5.0" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" 338 | dependencies = [ 339 | "crossbeam-utils", 340 | ] 341 | 342 | [[package]] 343 | name = "crossbeam-deque" 344 | version = "0.8.6" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" 347 | dependencies = [ 348 | "crossbeam-epoch", 349 | "crossbeam-utils", 350 | ] 351 | 352 | [[package]] 353 | name = "crossbeam-epoch" 354 | version = "0.9.18" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 357 | dependencies = [ 358 | "crossbeam-utils", 359 | ] 360 | 361 | [[package]] 362 | name = "crossbeam-utils" 363 | version = "0.8.21" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" 366 | 367 | [[package]] 368 | name = "displaydoc" 369 | version = "0.2.5" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 372 | dependencies = [ 373 | "proc-macro2", 374 | "quote", 375 | "syn", 376 | ] 377 | 378 | [[package]] 379 | name = "either" 380 | version = "1.15.0" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 383 | 384 | [[package]] 385 | name = "equivalent" 386 | version = "1.0.2" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 389 | 390 | [[package]] 391 | name = "errno" 392 | version = "0.3.10" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" 395 | dependencies = [ 396 | "libc", 397 | "windows-sys 0.59.0", 398 | ] 399 | 400 | [[package]] 401 | name = "event-listener" 402 | version = "5.4.0" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" 405 | dependencies = [ 406 | "concurrent-queue", 407 | "parking", 408 | "pin-project-lite", 409 | ] 410 | 411 | [[package]] 412 | name = "event-listener-strategy" 413 | version = "0.5.3" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" 416 | dependencies = [ 417 | "event-listener", 418 | "pin-project-lite", 419 | ] 420 | 421 | [[package]] 422 | name = "fastrand" 423 | version = "2.3.0" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 426 | 427 | [[package]] 428 | name = "form_urlencoded" 429 | version = "1.2.1" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 432 | dependencies = [ 433 | "percent-encoding", 434 | ] 435 | 436 | [[package]] 437 | name = "futures" 438 | version = "0.3.31" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 441 | dependencies = [ 442 | "futures-channel", 443 | "futures-core", 444 | "futures-io", 445 | "futures-sink", 446 | "futures-task", 447 | "futures-util", 448 | ] 449 | 450 | [[package]] 451 | name = "futures-channel" 452 | version = "0.3.31" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 455 | dependencies = [ 456 | "futures-core", 457 | "futures-sink", 458 | ] 459 | 460 | [[package]] 461 | name = "futures-core" 462 | version = "0.3.31" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 465 | 466 | [[package]] 467 | name = "futures-io" 468 | version = "0.3.31" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 471 | 472 | [[package]] 473 | name = "futures-lite" 474 | version = "2.6.0" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" 477 | dependencies = [ 478 | "fastrand", 479 | "futures-core", 480 | "futures-io", 481 | "parking", 482 | "pin-project-lite", 483 | ] 484 | 485 | [[package]] 486 | name = "futures-macro" 487 | version = "0.3.31" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 490 | dependencies = [ 491 | "proc-macro2", 492 | "quote", 493 | "syn", 494 | ] 495 | 496 | [[package]] 497 | name = "futures-sink" 498 | version = "0.3.31" 499 | source = "registry+https://github.com/rust-lang/crates.io-index" 500 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 501 | 502 | [[package]] 503 | name = "futures-task" 504 | version = "0.3.31" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 507 | 508 | [[package]] 509 | name = "futures-util" 510 | version = "0.3.31" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 513 | dependencies = [ 514 | "futures-channel", 515 | "futures-core", 516 | "futures-io", 517 | "futures-macro", 518 | "futures-sink", 519 | "futures-task", 520 | "memchr", 521 | "pin-project-lite", 522 | "pin-utils", 523 | "slab", 524 | ] 525 | 526 | [[package]] 527 | name = "gimli" 528 | version = "0.31.1" 529 | source = "registry+https://github.com/rust-lang/crates.io-index" 530 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 531 | 532 | [[package]] 533 | name = "globset" 534 | version = "0.4.16" 535 | source = "registry+https://github.com/rust-lang/crates.io-index" 536 | checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" 537 | dependencies = [ 538 | "aho-corasick", 539 | "bstr", 540 | "log", 541 | "regex-automata", 542 | "regex-syntax", 543 | ] 544 | 545 | [[package]] 546 | name = "hashbrown" 547 | version = "0.15.2" 548 | source = "registry+https://github.com/rust-lang/crates.io-index" 549 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 550 | 551 | [[package]] 552 | name = "heck" 553 | version = "0.5.0" 554 | source = "registry+https://github.com/rust-lang/crates.io-index" 555 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 556 | 557 | [[package]] 558 | name = "hermit-abi" 559 | version = "0.4.0" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" 562 | 563 | [[package]] 564 | name = "icu_collections" 565 | version = "1.5.0" 566 | source = "registry+https://github.com/rust-lang/crates.io-index" 567 | checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" 568 | dependencies = [ 569 | "displaydoc", 570 | "yoke", 571 | "zerofrom", 572 | "zerovec", 573 | ] 574 | 575 | [[package]] 576 | name = "icu_locid" 577 | version = "1.5.0" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" 580 | dependencies = [ 581 | "displaydoc", 582 | "litemap", 583 | "tinystr", 584 | "writeable", 585 | "zerovec", 586 | ] 587 | 588 | [[package]] 589 | name = "icu_locid_transform" 590 | version = "1.5.0" 591 | source = "registry+https://github.com/rust-lang/crates.io-index" 592 | checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" 593 | dependencies = [ 594 | "displaydoc", 595 | "icu_locid", 596 | "icu_locid_transform_data", 597 | "icu_provider", 598 | "tinystr", 599 | "zerovec", 600 | ] 601 | 602 | [[package]] 603 | name = "icu_locid_transform_data" 604 | version = "1.5.0" 605 | source = "registry+https://github.com/rust-lang/crates.io-index" 606 | checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" 607 | 608 | [[package]] 609 | name = "icu_normalizer" 610 | version = "1.5.0" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" 613 | dependencies = [ 614 | "displaydoc", 615 | "icu_collections", 616 | "icu_normalizer_data", 617 | "icu_properties", 618 | "icu_provider", 619 | "smallvec", 620 | "utf16_iter", 621 | "utf8_iter", 622 | "write16", 623 | "zerovec", 624 | ] 625 | 626 | [[package]] 627 | name = "icu_normalizer_data" 628 | version = "1.5.0" 629 | source = "registry+https://github.com/rust-lang/crates.io-index" 630 | checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" 631 | 632 | [[package]] 633 | name = "icu_properties" 634 | version = "1.5.1" 635 | source = "registry+https://github.com/rust-lang/crates.io-index" 636 | checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" 637 | dependencies = [ 638 | "displaydoc", 639 | "icu_collections", 640 | "icu_locid_transform", 641 | "icu_properties_data", 642 | "icu_provider", 643 | "tinystr", 644 | "zerovec", 645 | ] 646 | 647 | [[package]] 648 | name = "icu_properties_data" 649 | version = "1.5.0" 650 | source = "registry+https://github.com/rust-lang/crates.io-index" 651 | checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" 652 | 653 | [[package]] 654 | name = "icu_provider" 655 | version = "1.5.0" 656 | source = "registry+https://github.com/rust-lang/crates.io-index" 657 | checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" 658 | dependencies = [ 659 | "displaydoc", 660 | "icu_locid", 661 | "icu_provider_macros", 662 | "stable_deref_trait", 663 | "tinystr", 664 | "writeable", 665 | "yoke", 666 | "zerofrom", 667 | "zerovec", 668 | ] 669 | 670 | [[package]] 671 | name = "icu_provider_macros" 672 | version = "1.5.0" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" 675 | dependencies = [ 676 | "proc-macro2", 677 | "quote", 678 | "syn", 679 | ] 680 | 681 | [[package]] 682 | name = "idna" 683 | version = "1.0.3" 684 | source = "registry+https://github.com/rust-lang/crates.io-index" 685 | checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" 686 | dependencies = [ 687 | "idna_adapter", 688 | "smallvec", 689 | "utf8_iter", 690 | ] 691 | 692 | [[package]] 693 | name = "idna_adapter" 694 | version = "1.2.0" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" 697 | dependencies = [ 698 | "icu_normalizer", 699 | "icu_properties", 700 | ] 701 | 702 | [[package]] 703 | name = "ignore" 704 | version = "0.4.23" 705 | source = "registry+https://github.com/rust-lang/crates.io-index" 706 | checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" 707 | dependencies = [ 708 | "crossbeam-deque", 709 | "globset", 710 | "log", 711 | "memchr", 712 | "regex-automata", 713 | "same-file", 714 | "walkdir", 715 | "winapi-util", 716 | ] 717 | 718 | [[package]] 719 | name = "indexmap" 720 | version = "2.8.0" 721 | source = "registry+https://github.com/rust-lang/crates.io-index" 722 | checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" 723 | dependencies = [ 724 | "equivalent", 725 | "hashbrown", 726 | ] 727 | 728 | [[package]] 729 | name = "is_terminal_polyfill" 730 | version = "1.70.1" 731 | source = "registry+https://github.com/rust-lang/crates.io-index" 732 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 733 | 734 | [[package]] 735 | name = "itertools" 736 | version = "0.14.0" 737 | source = "registry+https://github.com/rust-lang/crates.io-index" 738 | checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" 739 | dependencies = [ 740 | "either", 741 | ] 742 | 743 | [[package]] 744 | name = "itoa" 745 | version = "1.0.15" 746 | source = "registry+https://github.com/rust-lang/crates.io-index" 747 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 748 | 749 | [[package]] 750 | name = "libc" 751 | version = "0.2.171" 752 | source = "registry+https://github.com/rust-lang/crates.io-index" 753 | checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" 754 | 755 | [[package]] 756 | name = "linux-raw-sys" 757 | version = "0.4.15" 758 | source = "registry+https://github.com/rust-lang/crates.io-index" 759 | checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" 760 | 761 | [[package]] 762 | name = "linux-raw-sys" 763 | version = "0.9.3" 764 | source = "registry+https://github.com/rust-lang/crates.io-index" 765 | checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" 766 | 767 | [[package]] 768 | name = "litemap" 769 | version = "0.7.5" 770 | source = "registry+https://github.com/rust-lang/crates.io-index" 771 | checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" 772 | 773 | [[package]] 774 | name = "log" 775 | version = "0.4.26" 776 | source = "registry+https://github.com/rust-lang/crates.io-index" 777 | checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" 778 | 779 | [[package]] 780 | name = "lopper" 781 | version = "0.1.0" 782 | dependencies = [ 783 | "anyhow", 784 | "async-lsp", 785 | "async-process", 786 | "async-trait", 787 | "baz-tree-sitter-traversal", 788 | "clap", 789 | "ignore", 790 | "indexmap", 791 | "itertools", 792 | "lsp-types", 793 | "serde_json", 794 | "thiserror", 795 | "tokio", 796 | "tower", 797 | "tree-sitter", 798 | "tree-sitter-rust", 799 | "url", 800 | ] 801 | 802 | [[package]] 803 | name = "lsp-types" 804 | version = "0.95.1" 805 | source = "registry+https://github.com/rust-lang/crates.io-index" 806 | checksum = "8e34d33a8e9b006cd3fc4fe69a921affa097bae4bb65f76271f4644f9a334365" 807 | dependencies = [ 808 | "bitflags 1.3.2", 809 | "serde", 810 | "serde_json", 811 | "serde_repr", 812 | "url", 813 | ] 814 | 815 | [[package]] 816 | name = "memchr" 817 | version = "2.7.4" 818 | source = "registry+https://github.com/rust-lang/crates.io-index" 819 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 820 | 821 | [[package]] 822 | name = "miniz_oxide" 823 | version = "0.8.5" 824 | source = "registry+https://github.com/rust-lang/crates.io-index" 825 | checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" 826 | dependencies = [ 827 | "adler2", 828 | ] 829 | 830 | [[package]] 831 | name = "mio" 832 | version = "1.0.3" 833 | source = "registry+https://github.com/rust-lang/crates.io-index" 834 | checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" 835 | dependencies = [ 836 | "libc", 837 | "wasi", 838 | "windows-sys 0.52.0", 839 | ] 840 | 841 | [[package]] 842 | name = "object" 843 | version = "0.36.7" 844 | source = "registry+https://github.com/rust-lang/crates.io-index" 845 | checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 846 | dependencies = [ 847 | "memchr", 848 | ] 849 | 850 | [[package]] 851 | name = "once_cell" 852 | version = "1.21.1" 853 | source = "registry+https://github.com/rust-lang/crates.io-index" 854 | checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" 855 | 856 | [[package]] 857 | name = "parking" 858 | version = "2.2.1" 859 | source = "registry+https://github.com/rust-lang/crates.io-index" 860 | checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" 861 | 862 | [[package]] 863 | name = "percent-encoding" 864 | version = "2.3.1" 865 | source = "registry+https://github.com/rust-lang/crates.io-index" 866 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 867 | 868 | [[package]] 869 | name = "pin-project-lite" 870 | version = "0.2.16" 871 | source = "registry+https://github.com/rust-lang/crates.io-index" 872 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 873 | 874 | [[package]] 875 | name = "pin-utils" 876 | version = "0.1.0" 877 | source = "registry+https://github.com/rust-lang/crates.io-index" 878 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 879 | 880 | [[package]] 881 | name = "piper" 882 | version = "0.2.4" 883 | source = "registry+https://github.com/rust-lang/crates.io-index" 884 | checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" 885 | dependencies = [ 886 | "atomic-waker", 887 | "fastrand", 888 | "futures-io", 889 | ] 890 | 891 | [[package]] 892 | name = "polling" 893 | version = "3.7.4" 894 | source = "registry+https://github.com/rust-lang/crates.io-index" 895 | checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" 896 | dependencies = [ 897 | "cfg-if", 898 | "concurrent-queue", 899 | "hermit-abi", 900 | "pin-project-lite", 901 | "rustix 0.38.44", 902 | "tracing", 903 | "windows-sys 0.59.0", 904 | ] 905 | 906 | [[package]] 907 | name = "proc-macro2" 908 | version = "1.0.94" 909 | source = "registry+https://github.com/rust-lang/crates.io-index" 910 | checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" 911 | dependencies = [ 912 | "unicode-ident", 913 | ] 914 | 915 | [[package]] 916 | name = "quote" 917 | version = "1.0.40" 918 | source = "registry+https://github.com/rust-lang/crates.io-index" 919 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 920 | dependencies = [ 921 | "proc-macro2", 922 | ] 923 | 924 | [[package]] 925 | name = "regex" 926 | version = "1.11.1" 927 | source = "registry+https://github.com/rust-lang/crates.io-index" 928 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 929 | dependencies = [ 930 | "aho-corasick", 931 | "memchr", 932 | "regex-automata", 933 | "regex-syntax", 934 | ] 935 | 936 | [[package]] 937 | name = "regex-automata" 938 | version = "0.4.9" 939 | source = "registry+https://github.com/rust-lang/crates.io-index" 940 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 941 | dependencies = [ 942 | "aho-corasick", 943 | "memchr", 944 | "regex-syntax", 945 | ] 946 | 947 | [[package]] 948 | name = "regex-syntax" 949 | version = "0.8.5" 950 | source = "registry+https://github.com/rust-lang/crates.io-index" 951 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 952 | 953 | [[package]] 954 | name = "rustc-demangle" 955 | version = "0.1.24" 956 | source = "registry+https://github.com/rust-lang/crates.io-index" 957 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 958 | 959 | [[package]] 960 | name = "rustix" 961 | version = "0.38.44" 962 | source = "registry+https://github.com/rust-lang/crates.io-index" 963 | checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" 964 | dependencies = [ 965 | "bitflags 2.9.0", 966 | "errno", 967 | "libc", 968 | "linux-raw-sys 0.4.15", 969 | "windows-sys 0.59.0", 970 | ] 971 | 972 | [[package]] 973 | name = "rustix" 974 | version = "1.0.3" 975 | source = "registry+https://github.com/rust-lang/crates.io-index" 976 | checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96" 977 | dependencies = [ 978 | "bitflags 2.9.0", 979 | "errno", 980 | "libc", 981 | "linux-raw-sys 0.9.3", 982 | "windows-sys 0.59.0", 983 | ] 984 | 985 | [[package]] 986 | name = "ryu" 987 | version = "1.0.20" 988 | source = "registry+https://github.com/rust-lang/crates.io-index" 989 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 990 | 991 | [[package]] 992 | name = "same-file" 993 | version = "1.0.6" 994 | source = "registry+https://github.com/rust-lang/crates.io-index" 995 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 996 | dependencies = [ 997 | "winapi-util", 998 | ] 999 | 1000 | [[package]] 1001 | name = "serde" 1002 | version = "1.0.219" 1003 | source = "registry+https://github.com/rust-lang/crates.io-index" 1004 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 1005 | dependencies = [ 1006 | "serde_derive", 1007 | ] 1008 | 1009 | [[package]] 1010 | name = "serde_derive" 1011 | version = "1.0.219" 1012 | source = "registry+https://github.com/rust-lang/crates.io-index" 1013 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 1014 | dependencies = [ 1015 | "proc-macro2", 1016 | "quote", 1017 | "syn", 1018 | ] 1019 | 1020 | [[package]] 1021 | name = "serde_json" 1022 | version = "1.0.140" 1023 | source = "registry+https://github.com/rust-lang/crates.io-index" 1024 | checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" 1025 | dependencies = [ 1026 | "indexmap", 1027 | "itoa", 1028 | "memchr", 1029 | "ryu", 1030 | "serde", 1031 | ] 1032 | 1033 | [[package]] 1034 | name = "serde_repr" 1035 | version = "0.1.20" 1036 | source = "registry+https://github.com/rust-lang/crates.io-index" 1037 | checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" 1038 | dependencies = [ 1039 | "proc-macro2", 1040 | "quote", 1041 | "syn", 1042 | ] 1043 | 1044 | [[package]] 1045 | name = "shlex" 1046 | version = "1.3.0" 1047 | source = "registry+https://github.com/rust-lang/crates.io-index" 1048 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 1049 | 1050 | [[package]] 1051 | name = "signal-hook-registry" 1052 | version = "1.4.2" 1053 | source = "registry+https://github.com/rust-lang/crates.io-index" 1054 | checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" 1055 | dependencies = [ 1056 | "libc", 1057 | ] 1058 | 1059 | [[package]] 1060 | name = "slab" 1061 | version = "0.4.9" 1062 | source = "registry+https://github.com/rust-lang/crates.io-index" 1063 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 1064 | dependencies = [ 1065 | "autocfg", 1066 | ] 1067 | 1068 | [[package]] 1069 | name = "smallvec" 1070 | version = "1.14.0" 1071 | source = "registry+https://github.com/rust-lang/crates.io-index" 1072 | checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" 1073 | 1074 | [[package]] 1075 | name = "socket2" 1076 | version = "0.5.8" 1077 | source = "registry+https://github.com/rust-lang/crates.io-index" 1078 | checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" 1079 | dependencies = [ 1080 | "libc", 1081 | "windows-sys 0.52.0", 1082 | ] 1083 | 1084 | [[package]] 1085 | name = "stable_deref_trait" 1086 | version = "1.2.0" 1087 | source = "registry+https://github.com/rust-lang/crates.io-index" 1088 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1089 | 1090 | [[package]] 1091 | name = "streaming-iterator" 1092 | version = "0.1.9" 1093 | source = "registry+https://github.com/rust-lang/crates.io-index" 1094 | checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520" 1095 | 1096 | [[package]] 1097 | name = "strsim" 1098 | version = "0.11.1" 1099 | source = "registry+https://github.com/rust-lang/crates.io-index" 1100 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 1101 | 1102 | [[package]] 1103 | name = "syn" 1104 | version = "2.0.100" 1105 | source = "registry+https://github.com/rust-lang/crates.io-index" 1106 | checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" 1107 | dependencies = [ 1108 | "proc-macro2", 1109 | "quote", 1110 | "unicode-ident", 1111 | ] 1112 | 1113 | [[package]] 1114 | name = "synstructure" 1115 | version = "0.13.1" 1116 | source = "registry+https://github.com/rust-lang/crates.io-index" 1117 | checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" 1118 | dependencies = [ 1119 | "proc-macro2", 1120 | "quote", 1121 | "syn", 1122 | ] 1123 | 1124 | [[package]] 1125 | name = "thiserror" 1126 | version = "2.0.12" 1127 | source = "registry+https://github.com/rust-lang/crates.io-index" 1128 | checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" 1129 | dependencies = [ 1130 | "thiserror-impl", 1131 | ] 1132 | 1133 | [[package]] 1134 | name = "thiserror-impl" 1135 | version = "2.0.12" 1136 | source = "registry+https://github.com/rust-lang/crates.io-index" 1137 | checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" 1138 | dependencies = [ 1139 | "proc-macro2", 1140 | "quote", 1141 | "syn", 1142 | ] 1143 | 1144 | [[package]] 1145 | name = "tinystr" 1146 | version = "0.7.6" 1147 | source = "registry+https://github.com/rust-lang/crates.io-index" 1148 | checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" 1149 | dependencies = [ 1150 | "displaydoc", 1151 | "zerovec", 1152 | ] 1153 | 1154 | [[package]] 1155 | name = "tokio" 1156 | version = "1.44.1" 1157 | source = "registry+https://github.com/rust-lang/crates.io-index" 1158 | checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" 1159 | dependencies = [ 1160 | "backtrace", 1161 | "libc", 1162 | "mio", 1163 | "pin-project-lite", 1164 | "socket2", 1165 | "tokio-macros", 1166 | "windows-sys 0.52.0", 1167 | ] 1168 | 1169 | [[package]] 1170 | name = "tokio-macros" 1171 | version = "2.5.0" 1172 | source = "registry+https://github.com/rust-lang/crates.io-index" 1173 | checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 1174 | dependencies = [ 1175 | "proc-macro2", 1176 | "quote", 1177 | "syn", 1178 | ] 1179 | 1180 | [[package]] 1181 | name = "tower" 1182 | version = "0.5.2" 1183 | source = "registry+https://github.com/rust-lang/crates.io-index" 1184 | checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" 1185 | dependencies = [ 1186 | "tower-layer", 1187 | "tower-service", 1188 | ] 1189 | 1190 | [[package]] 1191 | name = "tower-layer" 1192 | version = "0.3.3" 1193 | source = "registry+https://github.com/rust-lang/crates.io-index" 1194 | checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" 1195 | 1196 | [[package]] 1197 | name = "tower-service" 1198 | version = "0.3.3" 1199 | source = "registry+https://github.com/rust-lang/crates.io-index" 1200 | checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 1201 | 1202 | [[package]] 1203 | name = "tracing" 1204 | version = "0.1.41" 1205 | source = "registry+https://github.com/rust-lang/crates.io-index" 1206 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 1207 | dependencies = [ 1208 | "pin-project-lite", 1209 | "tracing-attributes", 1210 | "tracing-core", 1211 | ] 1212 | 1213 | [[package]] 1214 | name = "tracing-attributes" 1215 | version = "0.1.28" 1216 | source = "registry+https://github.com/rust-lang/crates.io-index" 1217 | checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" 1218 | dependencies = [ 1219 | "proc-macro2", 1220 | "quote", 1221 | "syn", 1222 | ] 1223 | 1224 | [[package]] 1225 | name = "tracing-core" 1226 | version = "0.1.33" 1227 | source = "registry+https://github.com/rust-lang/crates.io-index" 1228 | checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" 1229 | dependencies = [ 1230 | "once_cell", 1231 | ] 1232 | 1233 | [[package]] 1234 | name = "tree-sitter" 1235 | version = "0.25.3" 1236 | source = "registry+https://github.com/rust-lang/crates.io-index" 1237 | checksum = "b9ac5ea5e7f2f1700842ec071401010b9c59bf735295f6e9fa079c3dc035b167" 1238 | dependencies = [ 1239 | "cc", 1240 | "regex", 1241 | "regex-syntax", 1242 | "serde_json", 1243 | "streaming-iterator", 1244 | "tree-sitter-language", 1245 | ] 1246 | 1247 | [[package]] 1248 | name = "tree-sitter-language" 1249 | version = "0.1.5" 1250 | source = "registry+https://github.com/rust-lang/crates.io-index" 1251 | checksum = "c4013970217383f67b18aef68f6fb2e8d409bc5755227092d32efb0422ba24b8" 1252 | 1253 | [[package]] 1254 | name = "tree-sitter-rust" 1255 | version = "0.23.2" 1256 | source = "registry+https://github.com/rust-lang/crates.io-index" 1257 | checksum = "a4d64d449ca63e683c562c7743946a646671ca23947b9c925c0cfbe65051a4af" 1258 | dependencies = [ 1259 | "cc", 1260 | "tree-sitter-language", 1261 | ] 1262 | 1263 | [[package]] 1264 | name = "unicode-ident" 1265 | version = "1.0.18" 1266 | source = "registry+https://github.com/rust-lang/crates.io-index" 1267 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 1268 | 1269 | [[package]] 1270 | name = "url" 1271 | version = "2.5.4" 1272 | source = "registry+https://github.com/rust-lang/crates.io-index" 1273 | checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" 1274 | dependencies = [ 1275 | "form_urlencoded", 1276 | "idna", 1277 | "percent-encoding", 1278 | "serde", 1279 | ] 1280 | 1281 | [[package]] 1282 | name = "utf16_iter" 1283 | version = "1.0.5" 1284 | source = "registry+https://github.com/rust-lang/crates.io-index" 1285 | checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" 1286 | 1287 | [[package]] 1288 | name = "utf8_iter" 1289 | version = "1.0.4" 1290 | source = "registry+https://github.com/rust-lang/crates.io-index" 1291 | checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 1292 | 1293 | [[package]] 1294 | name = "utf8parse" 1295 | version = "0.2.2" 1296 | source = "registry+https://github.com/rust-lang/crates.io-index" 1297 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 1298 | 1299 | [[package]] 1300 | name = "waitpid-any" 1301 | version = "0.3.0" 1302 | source = "registry+https://github.com/rust-lang/crates.io-index" 1303 | checksum = "18aa3ce681e189f125c4c1e1388c03285e2fd434ef52c7203084012ac29c5e4a" 1304 | dependencies = [ 1305 | "rustix 1.0.3", 1306 | "windows-sys 0.59.0", 1307 | ] 1308 | 1309 | [[package]] 1310 | name = "walkdir" 1311 | version = "2.5.0" 1312 | source = "registry+https://github.com/rust-lang/crates.io-index" 1313 | checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 1314 | dependencies = [ 1315 | "same-file", 1316 | "winapi-util", 1317 | ] 1318 | 1319 | [[package]] 1320 | name = "wasi" 1321 | version = "0.11.0+wasi-snapshot-preview1" 1322 | source = "registry+https://github.com/rust-lang/crates.io-index" 1323 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1324 | 1325 | [[package]] 1326 | name = "winapi-util" 1327 | version = "0.1.9" 1328 | source = "registry+https://github.com/rust-lang/crates.io-index" 1329 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 1330 | dependencies = [ 1331 | "windows-sys 0.59.0", 1332 | ] 1333 | 1334 | [[package]] 1335 | name = "windows-sys" 1336 | version = "0.52.0" 1337 | source = "registry+https://github.com/rust-lang/crates.io-index" 1338 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1339 | dependencies = [ 1340 | "windows-targets", 1341 | ] 1342 | 1343 | [[package]] 1344 | name = "windows-sys" 1345 | version = "0.59.0" 1346 | source = "registry+https://github.com/rust-lang/crates.io-index" 1347 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 1348 | dependencies = [ 1349 | "windows-targets", 1350 | ] 1351 | 1352 | [[package]] 1353 | name = "windows-targets" 1354 | version = "0.52.6" 1355 | source = "registry+https://github.com/rust-lang/crates.io-index" 1356 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 1357 | dependencies = [ 1358 | "windows_aarch64_gnullvm", 1359 | "windows_aarch64_msvc", 1360 | "windows_i686_gnu", 1361 | "windows_i686_gnullvm", 1362 | "windows_i686_msvc", 1363 | "windows_x86_64_gnu", 1364 | "windows_x86_64_gnullvm", 1365 | "windows_x86_64_msvc", 1366 | ] 1367 | 1368 | [[package]] 1369 | name = "windows_aarch64_gnullvm" 1370 | version = "0.52.6" 1371 | source = "registry+https://github.com/rust-lang/crates.io-index" 1372 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 1373 | 1374 | [[package]] 1375 | name = "windows_aarch64_msvc" 1376 | version = "0.52.6" 1377 | source = "registry+https://github.com/rust-lang/crates.io-index" 1378 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 1379 | 1380 | [[package]] 1381 | name = "windows_i686_gnu" 1382 | version = "0.52.6" 1383 | source = "registry+https://github.com/rust-lang/crates.io-index" 1384 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 1385 | 1386 | [[package]] 1387 | name = "windows_i686_gnullvm" 1388 | version = "0.52.6" 1389 | source = "registry+https://github.com/rust-lang/crates.io-index" 1390 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 1391 | 1392 | [[package]] 1393 | name = "windows_i686_msvc" 1394 | version = "0.52.6" 1395 | source = "registry+https://github.com/rust-lang/crates.io-index" 1396 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 1397 | 1398 | [[package]] 1399 | name = "windows_x86_64_gnu" 1400 | version = "0.52.6" 1401 | source = "registry+https://github.com/rust-lang/crates.io-index" 1402 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 1403 | 1404 | [[package]] 1405 | name = "windows_x86_64_gnullvm" 1406 | version = "0.52.6" 1407 | source = "registry+https://github.com/rust-lang/crates.io-index" 1408 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1409 | 1410 | [[package]] 1411 | name = "windows_x86_64_msvc" 1412 | version = "0.52.6" 1413 | source = "registry+https://github.com/rust-lang/crates.io-index" 1414 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1415 | 1416 | [[package]] 1417 | name = "write16" 1418 | version = "1.0.0" 1419 | source = "registry+https://github.com/rust-lang/crates.io-index" 1420 | checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" 1421 | 1422 | [[package]] 1423 | name = "writeable" 1424 | version = "0.5.5" 1425 | source = "registry+https://github.com/rust-lang/crates.io-index" 1426 | checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" 1427 | 1428 | [[package]] 1429 | name = "yoke" 1430 | version = "0.7.5" 1431 | source = "registry+https://github.com/rust-lang/crates.io-index" 1432 | checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" 1433 | dependencies = [ 1434 | "serde", 1435 | "stable_deref_trait", 1436 | "yoke-derive", 1437 | "zerofrom", 1438 | ] 1439 | 1440 | [[package]] 1441 | name = "yoke-derive" 1442 | version = "0.7.5" 1443 | source = "registry+https://github.com/rust-lang/crates.io-index" 1444 | checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" 1445 | dependencies = [ 1446 | "proc-macro2", 1447 | "quote", 1448 | "syn", 1449 | "synstructure", 1450 | ] 1451 | 1452 | [[package]] 1453 | name = "zerofrom" 1454 | version = "0.1.6" 1455 | source = "registry+https://github.com/rust-lang/crates.io-index" 1456 | checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" 1457 | dependencies = [ 1458 | "zerofrom-derive", 1459 | ] 1460 | 1461 | [[package]] 1462 | name = "zerofrom-derive" 1463 | version = "0.1.6" 1464 | source = "registry+https://github.com/rust-lang/crates.io-index" 1465 | checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" 1466 | dependencies = [ 1467 | "proc-macro2", 1468 | "quote", 1469 | "syn", 1470 | "synstructure", 1471 | ] 1472 | 1473 | [[package]] 1474 | name = "zerovec" 1475 | version = "0.10.4" 1476 | source = "registry+https://github.com/rust-lang/crates.io-index" 1477 | checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" 1478 | dependencies = [ 1479 | "yoke", 1480 | "zerofrom", 1481 | "zerovec-derive", 1482 | ] 1483 | 1484 | [[package]] 1485 | name = "zerovec-derive" 1486 | version = "0.10.3" 1487 | source = "registry+https://github.com/rust-lang/crates.io-index" 1488 | checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" 1489 | dependencies = [ 1490 | "proc-macro2", 1491 | "quote", 1492 | "syn", 1493 | ] 1494 | --------------------------------------------------------------------------------