diff --git a/src/mcp/literal_search.rs b/src/mcp/literal_search.rs index 965204b4..e1cc20bc 100644 --- a/src/mcp/literal_search.rs +++ b/src/mcp/literal_search.rs @@ -135,7 +135,7 @@ impl CodesearchService { &effective_query, snippet_regex.as_ref(), ) { - let match_line = chunk.start_line + match_offset + 1; + let match_line = chunk.start_line + match_offset; items.push(LiteralSearchResultItem { path: ctx.prefix_sourced_path(alias, &chunk.path), start_line: match_line, @@ -189,7 +189,7 @@ impl CodesearchService { &effective_query, snippet_regex.as_ref(), ) { - let match_line = chunk.start_line + match_offset + 1; + let match_line = chunk.start_line + match_offset; items.push(LiteralSearchResultItem { path: chunk.path, start_line: match_line, @@ -423,7 +423,7 @@ impl CodesearchService { let (match_offset, snippet) = match_info.unwrap_or_else(|| { (0, chunk.content.lines().next().unwrap_or("").to_string()) }); - let match_line = chunk.start_line + match_offset + 1; + let match_line = chunk.start_line + match_offset; items.push(LiteralSearchResultItem { path: ctx.prefix_sourced_path(&fts_hit.alias, &chunk.path), start_line: match_line, @@ -506,7 +506,7 @@ impl CodesearchService { let (match_offset, snippet) = match_info.unwrap_or_else(|| { (0, chunk.content.lines().next().unwrap_or("").to_string()) }); - let match_line = chunk.start_line + match_offset + 1; + let match_line = chunk.start_line + match_offset; Some(LiteralSearchResultItem { path: chunk.path, start_line: match_line, diff --git a/src/mcp/mod.rs b/src/mcp/mod.rs index 59290fb6..f0d7f207 100644 --- a/src/mcp/mod.rs +++ b/src/mcp/mod.rs @@ -977,7 +977,7 @@ fn parse_import_lines(content: &str, start_line: usize) -> Vec { imported, // `start_line` is the chunk's 0-based first line and `offset` // is 0-based within it; the response emits 1-based lines. - line: start_line + offset + 1, + line: start_line + offset, kind, }); } diff --git a/src/mcp/search.rs b/src/mcp/search.rs index f491a2cf..d8d0f441 100644 --- a/src/mcp/search.rs +++ b/src/mcp/search.rs @@ -1320,8 +1320,8 @@ impl CodesearchService { .map(|r| SearchResultItem { chunk_id: Some(r.id), path: r.path, - // Chunks store 0-indexed lines; every response emits 1-based - // editor lines, so the conversion happens once, here. + // Chunks store 1-based editor lines; responses pass them + // through unchanged. start_line: r.start_line, end_line: r.end_line, kind: r.kind, diff --git a/src/mcp/tests.rs b/src/mcp/tests.rs index 6a4c48a8..c1ee362b 100644 --- a/src/mcp/tests.rs +++ b/src/mcp/tests.rs @@ -1220,19 +1220,21 @@ fn test_match_line_for_literal_regex() { #[test] fn test_parse_import_lines_detects_common_forms() { let content = "use std::fs;\nimport os\nfrom pkg import thing\n#include \nconst x = require('x')\nlet y = 1;"; - // `10` is the chunk's 0-based first line; emitted lines are 1-based. + // `10` is the chunk's 1-based first line (the chunk starts on editor + // line 10); match lines are start_line + the 0-based offset within the + // chunk. let imports = super::parse_import_lines(content, 10); assert_eq!(imports.len(), 5); assert_eq!(imports[0].kind, "use"); - assert_eq!(imports[0].line, 11); + assert_eq!(imports[0].line, 10); assert_eq!(imports[1].kind, "import"); - assert_eq!(imports[1].line, 12); + assert_eq!(imports[1].line, 11); assert_eq!(imports[2].kind, "import"); - assert_eq!(imports[2].line, 13); + assert_eq!(imports[2].line, 12); assert_eq!(imports[3].kind, "include"); - assert_eq!(imports[3].line, 14); + assert_eq!(imports[3].line, 13); assert_eq!(imports[4].kind, "require"); - assert_eq!(imports[4].line, 15); + assert_eq!(imports[4].line, 14); } // === Project/group routing tests === diff --git a/src/serve/tests.rs b/src/serve/tests.rs index 673f6a6e..434d7394 100644 --- a/src/serve/tests.rs +++ b/src/serve/tests.rs @@ -5936,14 +5936,14 @@ async fn group_literal_search_relaxed_fallback_covers_partial_term_hits() { let mut vs = stores.vector_store.write().await; let rich = crate::chunker::Chunk::new( "create package documents quickly".to_string(), - 0, + 1, 3, crate::chunker::ChunkKind::Comment, "docs/create.md".to_string(), ); let poor = crate::chunker::Chunk::new( "package".to_string(), - 0, + 1, 1, crate::chunker::ChunkKind::Comment, "docs/other.md".to_string(), @@ -6016,7 +6016,7 @@ async fn group_literal_search_relaxed_fallback_covers_partial_term_hits() { assert_eq!(items[0]["path"], "partial/docs/create.md", "got: {text}"); assert_eq!( items[0]["start_line"], 1, - "responses must emit 1-based editor lines (chunk starts at 0-based line 0): {text}" + "responses must emit 1-based editor lines (the chunk starts on editor line 1): {text}" ); assert_eq!( parsed["relaxed_fallback"],