use anyhow::{bail, Result};
use clap::Args;
use std::path::Path;
use crate::config::ProjectConfig;
use crate::core::graph::Graph;
use crate::core::searcher::Searcher;
use crate::output::formatter;
use crate::output::json::JsonOutput;
#[derive(Args, Debug)]
pub struct SimilarArgs {
pub symbol: String,
#[arg(long)]
pub kind: Option<String>,
#[arg(long, default_value = "10")]
pub limit: usize,
#[arg(long, short = 'j')]
pub json: bool,
}
pub fn run(args: &SimilarArgs, project_root: &Path) -> Result<()> {
let scope_dir = project_root.join(".scope");
if !scope_dir.exists() {
bail!("No .scope/ directory found. Run 'scope init' first.");
}
let db_path = scope_dir.join("graph.db");
if !db_path.exists() {
bail!("No index found. Run 'scope index' to build one first.");
}
let graph = Graph::open(&db_path)?;
crate::commands::warn_if_stale(&graph, project_root);
let searcher = Searcher::open(&db_path)?;
let symbol = crate::commands::resolve_symbol(&graph, &args.symbol)?;
let mut query_parts = Vec::new();
query_parts.push(format!("{} {}", symbol.kind, symbol.name));
let split = crate::core::embedder::split_camel_case(&symbol.name);
if split != symbol.name {
query_parts.push(split);
}
let snake = crate::core::embedder::split_snake_case(&symbol.name);
if snake != symbol.name {
query_parts.push(snake);
}
if let Some(sig) = &symbol.signature {
query_parts.push(sig.clone());
}
let query = query_parts.join(" ");
let kind_filter = args.kind.as_deref().or(Some(symbol.kind.as_str()));
let vendor_patterns = ProjectConfig::load(&scope_dir)
.map(|c| c.index.vendor_patterns)
.unwrap_or_default();
let mut results = searcher.search_with_vendor_derank(
&query,
args.limit + 1,
kind_filter,
&vendor_patterns,
)?;
results.retain(|r| r.id != symbol.id);
results.truncate(args.limit);
if args.json {
let total = results.len();
let output = JsonOutput {
command: "similar",
symbol: Some(args.symbol.clone()),
data: &results,
truncated: false,
total,
};
println!("{}", serde_json::to_string_pretty(&output)?);
} else {
formatter::print_find_results(&format!("similar to {}", args.symbol), &results);
}
Ok(())
}