feat: show line number in search results using match position
This commit is contained in:
parent
2b287ccb35
commit
c6cc9b7146
@ -36,14 +36,21 @@ class SearchClient:
|
||||
"highlightPostTag": "</em>",
|
||||
"attributesToCrop": ["content"],
|
||||
"cropLength": 30,
|
||||
"showMatchesPosition": True,
|
||||
})
|
||||
results = []
|
||||
for hit in response.get("hits", []):
|
||||
formatted = hit.get("_formatted", {})
|
||||
line_number = None
|
||||
matches = hit.get("_matchesPosition", {}).get("content", [])
|
||||
if matches and hit.get("content"):
|
||||
start = matches[0]["start"]
|
||||
line_number = hit["content"][:start].count("\n") + 1
|
||||
results.append({
|
||||
"key": hit["key"],
|
||||
"title": hit["title"],
|
||||
"path": hit["path"],
|
||||
"snippet": formatted.get("content", hit.get("content", "")),
|
||||
"lineNumber": line_number,
|
||||
})
|
||||
return results
|
||||
|
||||
@ -104,6 +104,7 @@ export interface SearchResult {
|
||||
title: string;
|
||||
path: string;
|
||||
snippet: string;
|
||||
lineNumber: number | null;
|
||||
}
|
||||
|
||||
export interface SearchResponse {
|
||||
|
||||
@ -32,6 +32,9 @@ export default function SearchResults({ results, query, onJumpToResult }: Search
|
||||
<div className="result-body">
|
||||
<div className="result-path">
|
||||
<span className="result-title">{result.title}</span>
|
||||
{result.lineNumber != null && (
|
||||
<span className="result-line">第 {result.lineNumber} 行</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="result-snippet" dangerouslySetInnerHTML={{ __html: result.snippet }} />
|
||||
</div>
|
||||
|
||||
@ -664,6 +664,17 @@ body {
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.result-line {
|
||||
font-family: 'IBM Plex Mono', monospace;
|
||||
font-size: 11px;
|
||||
color: var(--ash);
|
||||
background: var(--paper-alt);
|
||||
border: 1px solid var(--rule);
|
||||
border-radius: 4px;
|
||||
padding: 1px 6px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.result-snippet {
|
||||
font-size: 12.5px;
|
||||
color: var(--ash);
|
||||
|
||||
@ -85,7 +85,7 @@ test('selecting a file from the tree loads it into the editor', async () => {
|
||||
test('searching shows the results overlay, and clicking a result opens the document', async () => {
|
||||
vi.mocked(listDocuments).mockResolvedValue({ folders: [], files: [] });
|
||||
vi.mocked(search).mockResolvedValue({
|
||||
results: [{ key: '产品文档/架构设计.md', title: '架构设计.md', path: '产品文档', snippet: '...<em>检索</em>...' }],
|
||||
results: [{ key: '产品文档/架构设计.md', title: '架构设计.md', path: '产品文档', snippet: '...<em>检索</em>...', lineNumber: 5 }],
|
||||
});
|
||||
vi.mocked(getDocument).mockResolvedValue({
|
||||
key: '产品文档/架构设计.md',
|
||||
@ -121,7 +121,7 @@ test('searching shows the results overlay, and clicking a result opens the docum
|
||||
test('jumping to a search result clears the search input so it does not show stale text', async () => {
|
||||
vi.mocked(listDocuments).mockResolvedValue({ folders: [], files: [] });
|
||||
vi.mocked(search).mockResolvedValue({
|
||||
results: [{ key: '产品文档/架构设计.md', title: '架构设计.md', path: '产品文档', snippet: '...<em>检索</em>...' }],
|
||||
results: [{ key: '产品文档/架构设计.md', title: '架构设计.md', path: '产品文档', snippet: '...<em>检索</em>...', lineNumber: 5 }],
|
||||
});
|
||||
vi.mocked(getDocument).mockResolvedValue({
|
||||
key: '产品文档/架构设计.md',
|
||||
|
||||
@ -12,6 +12,7 @@ test('renders result count, titles, and highlighted snippets', () => {
|
||||
title: '架构设计.md',
|
||||
path: '产品文档',
|
||||
snippet: '检索:Meilisearch,支持 <em>中文全文检索</em>。',
|
||||
lineNumber: null,
|
||||
},
|
||||
]}
|
||||
onJumpToResult={vi.fn()}
|
||||
@ -28,7 +29,7 @@ test('clicking a result calls onJumpToResult with its key', () => {
|
||||
render(
|
||||
<SearchResults
|
||||
query="检索"
|
||||
results={[{ key: '产品文档/架构设计.md', title: '架构设计.md', path: '产品文档', snippet: '...' }]}
|
||||
results={[{ key: '产品文档/架构设计.md', title: '架构设计.md', path: '产品文档', snippet: '...', lineNumber: null }]}
|
||||
onJumpToResult={onJumpToResult}
|
||||
/>
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user