Add caller/reference analysis, C# imports, and de-noised search
Agent-workflow tooling and search quality improvements: - find_callers / find_references (by id or exact name), with caller attribution to the innermost enclosing symbol and import-graph narrowing - index file (single-file re-index) and workspace glob member resolution - search symbols: exact > prefix > fulltext > substring fallback, inline source and line numbers, --limit on all search paths - search text: reads live from disk (no stale FTS snapshot), collapses duplicate snippets, correct multi-word phrase matching - find dead-symbols: excludes framework-wired entry points (pytest test_*, #[test] tests, FastAPI/Flask routes, Celery tasks, click/typer commands) - find references: filters the definition line and string/comment occurrences (incl. Rust raw strings) - C# import edges via new extract_csharp (using directives) - richer MCP tool descriptions; brace-in-string tokenizer fix Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
1 parent
a416a9fe52
commit
f1531a64d4
12 files changed
+1317
-214
No files matched your search
+42
-21
@@ -1,19 +1,21 @@
|
||||
//! SQLite schema + minimal migration (schema_version in metadata).
|
||||
//! SQLite schema + migration (schema_version in metadata).
|
||||
|
||||
use anyhow::Result;
|
||||
use anyhow::{Context, Result};
|
||||
use rusqlite::Connection;
|
||||
|
||||
const SCHEMA_VERSION: i64 = 1;
|
||||
const SCHEMA_VERSION: i64 = 2;
|
||||
|
||||
/// Fresh-install schema at the current version. Everything is `IF NOT EXISTS`
|
||||
/// so it can be re-run safely (the old version re-ran it on every `version <
|
||||
/// SCHEMA_VERSION`, which broke the moment any table already existed).
|
||||
const SCHEMA: &str = r#"
|
||||
CREATE TABLE files (
|
||||
CREATE TABLE IF NOT EXISTS files (
|
||||
id INTEGER PRIMARY KEY,
|
||||
path TEXT NOT NULL UNIQUE,
|
||||
content_hash TEXT NOT NULL,
|
||||
language TEXT NOT NULL,
|
||||
mtime INTEGER NOT NULL
|
||||
language TEXT NOT NULL
|
||||
);
|
||||
CREATE TABLE symbols (
|
||||
CREATE TABLE IF NOT EXISTS symbols (
|
||||
id INTEGER PRIMARY KEY,
|
||||
file_id INTEGER NOT NULL REFERENCES files(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
@@ -23,31 +25,40 @@ CREATE TABLE symbols (
|
||||
line_start INTEGER NOT NULL,
|
||||
line_end INTEGER NOT NULL
|
||||
);
|
||||
CREATE INDEX idx_symbols_file ON symbols(file_id);
|
||||
CREATE INDEX idx_symbols_name ON symbols(name);
|
||||
CREATE TABLE imports (
|
||||
CREATE INDEX IF NOT EXISTS idx_symbols_file ON symbols(file_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_symbols_name ON symbols(name);
|
||||
CREATE TABLE IF NOT EXISTS imports (
|
||||
id INTEGER PRIMARY KEY,
|
||||
from_file_id INTEGER NOT NULL REFERENCES files(id) ON DELETE CASCADE,
|
||||
to_file_id INTEGER NOT NULL REFERENCES files(id) ON DELETE CASCADE,
|
||||
import_type TEXT NOT NULL
|
||||
);
|
||||
CREATE INDEX idx_imports_from ON imports(from_file_id);
|
||||
CREATE INDEX idx_imports_to ON imports(to_file_id);
|
||||
CREATE VIRTUAL TABLE symbols_fts USING fts5(
|
||||
CREATE INDEX IF NOT EXISTS idx_imports_from ON imports(from_file_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_imports_to ON imports(to_file_id);
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS symbols_fts USING fts5(
|
||||
name, qualified_name, kind, signature,
|
||||
content='symbols', content_rowid='id'
|
||||
);
|
||||
CREATE TRIGGER symbols_ai AFTER INSERT ON symbols BEGIN
|
||||
CREATE TRIGGER IF NOT EXISTS symbols_ai AFTER INSERT ON symbols BEGIN
|
||||
INSERT INTO symbols_fts(rowid, name, qualified_name, kind, signature)
|
||||
VALUES (new.id, new.name, new.qualified_name, new.kind, new.signature);
|
||||
END;
|
||||
CREATE TRIGGER symbols_ad AFTER DELETE ON symbols BEGIN
|
||||
CREATE TRIGGER IF NOT EXISTS symbols_ad AFTER DELETE ON symbols BEGIN
|
||||
INSERT INTO symbols_fts(symbols_fts, rowid, name, qualified_name, kind, signature)
|
||||
VALUES ('delete', old.id, old.name, old.qualified_name, old.kind, old.signature);
|
||||
END;
|
||||
CREATE VIRTUAL TABLE content_fts USING fts5(path UNINDEXED, content);
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS content_fts USING fts5(path UNINDEXED, content);
|
||||
"#;
|
||||
|
||||
/// Incremental migrations. `MIGRATIONS[i]` upgrades from schema version `i+1`
|
||||
/// to `i+2`; version 0 is a fresh database and is created via [`SCHEMA`]
|
||||
/// directly.
|
||||
const MIGRATIONS: &[&str] = &[
|
||||
// v1 -> v2: the `mtime` column was written but never read (change detection
|
||||
// is content-hash based). Drop the dead column.
|
||||
"ALTER TABLE files DROP COLUMN mtime;",
|
||||
];
|
||||
|
||||
/// Create schema if missing, or migrate to the current version.
|
||||
pub fn init(conn: &mut Connection) -> Result<()> {
|
||||
conn.execute_batch(
|
||||
@@ -60,12 +71,22 @@ pub fn init(conn: &mut Connection) -> Result<()> {
|
||||
|r| r.get(0),
|
||||
)
|
||||
.unwrap_or(0);
|
||||
if version < SCHEMA_VERSION {
|
||||
|
||||
if version == 0 {
|
||||
conn.execute_batch(SCHEMA)?;
|
||||
conn.execute(
|
||||
"INSERT OR REPLACE INTO metadata (key, value) VALUES ('schema_version', ?1)",
|
||||
[SCHEMA_VERSION.to_string()],
|
||||
)?;
|
||||
} else {
|
||||
for (i, migration) in MIGRATIONS.iter().enumerate() {
|
||||
// i-th migration upgrades version (i+1) -> (i+2).
|
||||
if version <= (i as i64) + 1 {
|
||||
conn.execute_batch(migration)
|
||||
.with_context(|| format!("migration to schema v{} failed", i + 2))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
conn.execute(
|
||||
"INSERT OR REPLACE INTO metadata (key, value) VALUES ('schema_version', ?1)",
|
||||
[SCHEMA_VERSION.to_string()],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
Reference in new issue
Block a user