feat: Enhance file search with core plugin and Omnisearch integration
- Introduces a new match_search_files tool for fuzzy/keyword search, integrating with Obsidian's core search plugin and updating Omnisearch integration for improved file search capabilities. - Adds settings for selecting search backends (core plugin, Omnisearch, ripgrep) for both regex and match searches. - Updates language files, prompts, and types to support the new functionality. - Restructures search-related files for better organization.
This commit is contained in:
parent
350a49cef9
commit
9984527e85
@ -29,8 +29,10 @@ import {
|
||||
LLMBaseUrlNotSetException,
|
||||
LLMModelNotSetException,
|
||||
} from '../../core/llm/exception'
|
||||
import { regexSearchFilesWithRipgrep } from '../../core/regex/ripgrep-index'
|
||||
import { regexSearchFilesWithOmnisearch } from '../../core/regex/omnisearch-index'
|
||||
import { searchFilesWithCorePlugin } from '../../core/search/match/coreplugin-match'
|
||||
import { searchFilesWithOmnisearch } from '../../core/search/match/omnisearch-match'
|
||||
import { regexSearchFilesWithRipgrep } from '../../core/search/regex/ripgrep-regex'
|
||||
import { regexSearchFilesWithCorePlugin } from '../../core/search/regex/coreplugin-regex'
|
||||
import { useChatHistory } from '../../hooks/use-chat-history'
|
||||
import { useCustomModes } from '../../hooks/use-custom-mode'
|
||||
import { t } from '../../lang/helpers'
|
||||
@ -608,15 +610,37 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
|
||||
mentionables: [],
|
||||
}
|
||||
}
|
||||
} else if (toolArgs.type === 'regex_search_files') {
|
||||
// @ts-expect-error Obsidian API type mismatch
|
||||
const searchBackend = settings.regexSearchBackend
|
||||
const baseVaultPath = String(app.vault.adapter.getBasePath())
|
||||
const absolutePath = path.join(baseVaultPath, toolArgs.filepath)
|
||||
} else if (toolArgs.type === 'match_search_files') {
|
||||
const searchBackend = settings.matchSearchBackend
|
||||
let results: string;
|
||||
if (searchBackend === 'omnisearch') {
|
||||
results = await regexSearchFilesWithOmnisearch(absolutePath, toolArgs.regex, app)
|
||||
results = await searchFilesWithOmnisearch(toolArgs.query, app)
|
||||
} else {
|
||||
results = await searchFilesWithCorePlugin(toolArgs.query, app)
|
||||
}
|
||||
const formattedContent = `[match_search_files for '${toolArgs.filepath}'] Result:\n${results}\n`;
|
||||
return {
|
||||
type: 'match_search_files',
|
||||
applyMsgId,
|
||||
applyStatus: ApplyStatus.Applied,
|
||||
returnMsg: {
|
||||
role: 'user',
|
||||
applyStatus: ApplyStatus.Idle,
|
||||
content: null,
|
||||
promptContent: formattedContent,
|
||||
id: uuidv4(),
|
||||
mentionables: [],
|
||||
}
|
||||
}
|
||||
} else if (toolArgs.type === 'regex_search_files') {
|
||||
const searchBackend = settings.regexSearchBackend
|
||||
let results: string;
|
||||
if (searchBackend === 'coreplugin') {
|
||||
results = await regexSearchFilesWithCorePlugin(toolArgs.regex, app)
|
||||
} else {
|
||||
// @ts-expect-error Obsidian API type mismatch
|
||||
const baseVaultPath = String(app.vault.adapter.getBasePath())
|
||||
const absolutePath = path.join(baseVaultPath, toolArgs.filepath)
|
||||
const ripgrepPath = settings.ripgrepPath
|
||||
results = await regexSearchFilesWithRipgrep(absolutePath, toolArgs.regex, ripgrepPath)
|
||||
}
|
||||
|
||||
@ -0,0 +1,52 @@
|
||||
import { FileSearch } from 'lucide-react'
|
||||
import React from 'react'
|
||||
|
||||
import { useApp } from "../../../contexts/AppContext"
|
||||
import { t } from '../../../lang/helpers'
|
||||
import { ApplyStatus, MatchSearchFilesToolArgs } from "../../../types/apply"
|
||||
import { openMarkdownFile } from "../../../utils/obsidian"
|
||||
|
||||
export default function MarkdownMatchSearchFilesBlock({
|
||||
applyStatus,
|
||||
onApply,
|
||||
path,
|
||||
query,
|
||||
finish
|
||||
}: {
|
||||
applyStatus: ApplyStatus
|
||||
onApply: (args: MatchSearchFilesToolArgs) => void
|
||||
path: string,
|
||||
query: string,
|
||||
finish: boolean
|
||||
}) {
|
||||
const app = useApp()
|
||||
|
||||
const handleClick = () => {
|
||||
openMarkdownFile(app, path)
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
if (finish && applyStatus === ApplyStatus.Idle) {
|
||||
onApply({
|
||||
type: 'match_search_files',
|
||||
filepath: path,
|
||||
query: query,
|
||||
file_pattern: ".md",
|
||||
})
|
||||
}
|
||||
}, [finish])
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`infio-chat-code-block ${path ? 'has-filename' : ''}`}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<div className={'infio-chat-code-block-header'}>
|
||||
<div className={'infio-chat-code-block-header-filename'}>
|
||||
<FileSearch size={14} className="infio-chat-code-block-header-icon" />
|
||||
<span>{t('chat.reactMarkdown.matchSearchInPath').replace('{query}', query).replace('{path}', path)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -13,6 +13,7 @@ import MarkdownFetchUrlsContentBlock from './Markdown/MarkdownFetchUrlsContentBl
|
||||
import MarkdownListFilesBlock from './Markdown/MarkdownListFilesBlock'
|
||||
import MarkdownReadFileBlock from './Markdown/MarkdownReadFileBlock'
|
||||
import MarkdownReasoningBlock from './Markdown/MarkdownReasoningBlock'
|
||||
import MarkdownMatchSearchFilesBlock from './Markdown/MarkdownMatchSearchFilesBlock'
|
||||
import MarkdownRegexSearchFilesBlock from './Markdown/MarkdownRegexSearchFilesBlock'
|
||||
import MarkdownSearchAndReplace from './Markdown/MarkdownSearchAndReplace'
|
||||
import MarkdownSearchWebBlock from './Markdown/MarkdownSearchWebBlock'
|
||||
@ -117,6 +118,15 @@ function ReactMarkdown({
|
||||
recursive={block.recursive}
|
||||
finish={block.finish}
|
||||
/>
|
||||
) : block.type === 'match_search_files' ? (
|
||||
<MarkdownMatchSearchFilesBlock
|
||||
key={"match-search-files-" + index}
|
||||
applyStatus={applyStatus}
|
||||
onApply={onApply}
|
||||
path={block.path}
|
||||
query={block.query}
|
||||
finish={block.finish}
|
||||
/>
|
||||
) : block.type === 'regex_search_files' ? (
|
||||
<MarkdownRegexSearchFilesBlock
|
||||
key={"regex-search-files-" + index}
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
const MatchSearchFilesInstructions = "\n- You can use match_search_files to perform fuzzy-based searches across files using keyword/phrase. This tool is ideal for finding similar texts in notes. It excels at finding similar contents with similar keywords and phrases quickly."
|
||||
|
||||
const RegexSearchFilesInstructions = "\n- You can use regex_search_files to perform pattern-based searches across files using regular expressions. This tool is ideal for finding exact text matches, specific patterns (like tags, links, dates, URLs), or structural elements in notes. It excels at locating precise format patterns and is perfect for finding connections between notes, frontmatter elements, or specific Markdown formatting."
|
||||
|
||||
const SemanticSearchFilesInstructions = "\n- You can use semantic_search_files to find content based on meaning rather than exact text matches. Semantic search uses embedding vectors to understand concepts and ideas, finding relevant content even when keywords differ. This is especially powerful for discovering thematically related notes, answering conceptual questions about your knowledge base, or finding content when you don't know the exact wording used in the notes."
|
||||
@ -6,12 +8,20 @@ function getObsidianCapabilitiesSection(
|
||||
cwd: string,
|
||||
searchFilesTool: string,
|
||||
): string {
|
||||
|
||||
const searchFilesInstructions = searchFilesTool === 'regex'
|
||||
? RegexSearchFilesInstructions
|
||||
: searchFilesTool === 'semantic'
|
||||
? SemanticSearchFilesInstructions
|
||||
: "";
|
||||
let searchFilesInstructions: string;
|
||||
switch (searchFilesTool) {
|
||||
case 'match':
|
||||
searchFilesInstructions = MatchSearchFilesInstructions;
|
||||
break;
|
||||
case 'regex':
|
||||
searchFilesInstructions = RegexSearchFilesInstructions;
|
||||
break;
|
||||
case 'semantic':
|
||||
searchFilesInstructions = SemanticSearchFilesInstructions;
|
||||
break;
|
||||
default:
|
||||
searchFilesInstructions = "";
|
||||
}
|
||||
|
||||
return `====
|
||||
|
||||
|
||||
@ -57,7 +57,9 @@ function getEditingInstructions(diffStrategy?: DiffStrategy): string {
|
||||
}
|
||||
|
||||
function getSearchInstructions(searchTool: string): string {
|
||||
if (searchTool === 'regex') {
|
||||
if (searchTool === 'match') {
|
||||
return `- When using the match_search_files tool, craft your keyword/phrase carefully to balance specificity and flexibility. Based on the user's task, you may use it to find specific content, notes, headings, connections between notes, tags, or any text-based information across the Obsidian vault. The results include context, so analyze the surrounding text to better understand the matches. Leverage the match_search_files tool in combination with other tools for comprehensive analysis. For example, use it to find specific keywords or phrases, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes.`
|
||||
} else if (searchTool === 'regex') {
|
||||
return `- When using the regex_search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task, you may use it to find specific content, notes, headings, connections between notes, tags, or any text-based information across the Obsidian vault. The results include context, so analyze the surrounding text to better understand the matches. Leverage the regex_search_files tool in combination with other tools for comprehensive analysis. For example, use it to find specific phrases or patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes.`
|
||||
} else if (searchTool === 'semantic') {
|
||||
return `- When using the semantic_search_files tool, craft your natural language query to describe concepts and ideas rather than specific patterns. Based on the user's task, you may use it to find thematically related content, conceptually similar notes, or knowledge connections across the Obsidian vault, even when exact keywords aren't present. The results include context, so analyze the surrounding text to understand the conceptual relevance of each match. Leverage the semantic_search_files tool in combination with other tools for comprehensive analysis. For example, use it to find specific phrases or patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes.`
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import { ToolArgs } from "./types"
|
||||
|
||||
export function getSearchFilesDescription(args: ToolArgs): string {
|
||||
if (args.searchTool === 'regex') {
|
||||
if (args.searchTool === 'match') {
|
||||
return getMatchSearchFilesDescription(args)
|
||||
} else if (args.searchTool === 'regex') {
|
||||
return getRegexSearchFilesDescription(args)
|
||||
} else if (args.searchTool === 'semantic') {
|
||||
return getSemanticSearchFilesDescription(args)
|
||||
@ -10,6 +12,26 @@ export function getSearchFilesDescription(args: ToolArgs): string {
|
||||
}
|
||||
}
|
||||
|
||||
export function getMatchSearchFilesDescription(args: ToolArgs): string {
|
||||
return `## match_search_files
|
||||
Description: Request to perform a match/fuzzy search across files in a specified directory, providing context-rich results. This tool searches for specific content across multiple files, displaying each match with encapsulating context.
|
||||
Parameters:
|
||||
- path: (required) The path of the directory to search in (relative to the current working directory ${args.cwd}). This directory will be recursively searched.
|
||||
- query: (required) The keyword, phrase to search for. The system will find documents with similar keywords/phrases.
|
||||
|
||||
Usage:
|
||||
<match_search_files>
|
||||
<path>Directory path here</path>
|
||||
<query>Your keyword/phrase here</query>
|
||||
</match_search_files>
|
||||
|
||||
Example: Requesting to search for all Markdown files containing 'test' in the current directory
|
||||
<match_search_files>
|
||||
<path>.</path>
|
||||
<query>test</query>
|
||||
</match_search_files>`
|
||||
}
|
||||
|
||||
export function getRegexSearchFilesDescription(args: ToolArgs): string {
|
||||
return `## regex_search_files
|
||||
Description: Request to perform a regex search across files in a specified directory, providing context-rich results. This tool searches for patterns or specific content across multiple files, displaying each match with encapsulating context.
|
||||
|
||||
85
src/core/search/match/coreplugin-match.ts
Normal file
85
src/core/search/match/coreplugin-match.ts
Normal file
@ -0,0 +1,85 @@
|
||||
import { App } from "obsidian";
|
||||
import {
|
||||
MAX_RESULTS,
|
||||
truncateLine,
|
||||
SearchResult,
|
||||
formatResults,
|
||||
} from '../search-common';
|
||||
|
||||
/**
|
||||
* Searches using Obsidian's core search plugin and builds context for each match.
|
||||
*
|
||||
* @param app The Obsidian App instance.
|
||||
* @param query The query to search for.
|
||||
* @returns A promise that resolves to a formatted string of search results.
|
||||
*/
|
||||
export async function searchFilesWithCorePlugin(
|
||||
query: string,
|
||||
app: App,
|
||||
): Promise<string> {
|
||||
const searchPlugin = (app as any).internalPlugins.plugins['global-search']?.instance;
|
||||
if (!searchPlugin) {
|
||||
throw new Error("Core search plugin is not available.");
|
||||
}
|
||||
|
||||
// The core search function is not officially documented and may change.
|
||||
// This is based on community findings and common usage in other plugins.
|
||||
const searchResults = await new Promise<any[]>((resolve) => {
|
||||
const unregister = searchPlugin.on("search-results", (results: any) => {
|
||||
unregister();
|
||||
resolve(results);
|
||||
});
|
||||
searchPlugin.openGlobalSearch(query);
|
||||
});
|
||||
|
||||
const results: SearchResult[] = [];
|
||||
const vault = app.vault;
|
||||
|
||||
for (const fileMatches of Object.values(searchResults) as any) {
|
||||
if (results.length >= MAX_RESULTS) {
|
||||
break;
|
||||
}
|
||||
|
||||
const file = vault.getAbstractFileByPath(fileMatches.file.path);
|
||||
if (!file || !('read' in file)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const content = await vault.cachedRead(file as any);
|
||||
const lines = content.split('\n');
|
||||
|
||||
for (const match of fileMatches.result.content) {
|
||||
if (results.length >= MAX_RESULTS) {
|
||||
break;
|
||||
}
|
||||
|
||||
const [matchText, startOffset] = match;
|
||||
let charCount = 0;
|
||||
let lineNumber = 0;
|
||||
let column = 0;
|
||||
let lineContent = "";
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const lineLength = lines[i].length + 1; // +1 for the newline character
|
||||
if (charCount + lineLength > startOffset) {
|
||||
lineNumber = i + 1;
|
||||
column = startOffset - charCount + 1;
|
||||
lineContent = lines[i];
|
||||
break;
|
||||
}
|
||||
charCount += lineLength;
|
||||
}
|
||||
|
||||
results.push({
|
||||
file: fileMatches.file.path,
|
||||
line: lineNumber,
|
||||
column: column,
|
||||
match: truncateLine(lineContent.trimEnd()),
|
||||
beforeContext: lineNumber > 1 ? [truncateLine(lines[lineNumber - 2].trimEnd())] : [],
|
||||
afterContext: lineNumber < lines.length ? [truncateLine(lines[lineNumber].trimEnd())] : [],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return formatResults(results, ".\\");
|
||||
}
|
||||
@ -4,9 +4,7 @@ import {
|
||||
truncateLine,
|
||||
SearchResult,
|
||||
formatResults,
|
||||
} from './regex-common';
|
||||
|
||||
// --- Omnisearch API and Helper Types ---
|
||||
} from '../search-common';
|
||||
|
||||
type SearchMatchApi = {
|
||||
match: string;
|
||||
@ -69,14 +67,12 @@ function findLineAndColumnFromOffset(
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches using Omnisearch and builds context for each match to replicate ripgrep's output.
|
||||
* @param vaultPath The absolute path of the vault for making relative paths.
|
||||
* Searches using Omnisearch and builds context for each match.
|
||||
* @param query The search query for Omnisearch. Note: Omnisearch does not support full regex.
|
||||
* @param app The Obsidian App instance.
|
||||
* @returns A formatted string of search results.
|
||||
*/
|
||||
export async function regexSearchFilesWithOmnisearch(
|
||||
vaultPath: string,
|
||||
export async function searchFilesWithOmnisearch(
|
||||
query: string,
|
||||
app: App,
|
||||
): Promise<string> {
|
||||
@ -87,8 +83,8 @@ export async function regexSearchFilesWithOmnisearch(
|
||||
);
|
||||
}
|
||||
|
||||
// Omnisearch is not a regex engine. The function name is kept for consistency
|
||||
// but the `query` will be treated as a keyword/fuzzy search by the plugin.
|
||||
// Omnisearch is not a regex engine.
|
||||
// The `query` will be treated as a keyword/fuzzy search by the plugin.
|
||||
const apiResults = await window.omnisearch.search(query);
|
||||
if (!apiResults || apiResults.length === 0) {
|
||||
throw new Error("No results found.");
|
||||
@ -132,7 +128,7 @@ export async function regexSearchFilesWithOmnisearch(
|
||||
}
|
||||
}
|
||||
|
||||
return formatResults(results, vaultPath);
|
||||
return formatResults(results, ".\\");
|
||||
} catch (error) {
|
||||
console.error("Error during Omnisearch processing:", error);
|
||||
return "An error occurred during the search.";
|
||||
17
src/core/search/regex/coreplugin-regex.ts
Normal file
17
src/core/search/regex/coreplugin-regex.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { App } from "obsidian";
|
||||
import { searchFilesWithCorePlugin } from '../match/coreplugin-match'
|
||||
|
||||
/**
|
||||
* Performs a regular expression search using Obsidian's core search plugin.
|
||||
*
|
||||
* @param app The Obsidian App instance.
|
||||
* @param regex The regular expression to search for.
|
||||
* @returns A promise that resolves to a formatted string of search results.
|
||||
*/
|
||||
export async function regexSearchFilesWithCorePlugin(
|
||||
regex: string,
|
||||
app: App,
|
||||
): Promise<string> {
|
||||
const query = "/" + regex + "/";
|
||||
return searchFilesWithCorePlugin(query, app);
|
||||
}
|
||||
@ -8,7 +8,7 @@ import {
|
||||
truncateLine,
|
||||
SearchResult,
|
||||
formatResults
|
||||
} from './regex-common'
|
||||
} from '../search-common'
|
||||
|
||||
const isWindows = /^win/.test(process.platform)
|
||||
const binName = isWindows ? "rg.exe" : "rg"
|
||||
@ -81,6 +81,7 @@ export default {
|
||||
copy: "Copy",
|
||||
editOrApplyDiff: "{mode}: {path}",
|
||||
loading: "Loading...",
|
||||
matchSearchInPath: 'match search files "{query}" in {path}',
|
||||
regexSearchInPath: 'regex search files "{regex}" in {path}',
|
||||
createNewNote: "Create new note",
|
||||
copyMsg: "Copy message",
|
||||
@ -227,10 +228,13 @@ export default {
|
||||
auto: 'Auto',
|
||||
semantic: 'Semantic',
|
||||
regex: 'Regex',
|
||||
match: 'Match',
|
||||
regexBackend: 'Regex search backend',
|
||||
regexBackendDescription: 'Choose the backend for regex search method.',
|
||||
matchBackend: 'Match search backend',
|
||||
matchBackendDescription: 'Choose the backend for match search method.',
|
||||
ripgrep: 'ripgrep',
|
||||
omnisearch: 'Omnisearch',
|
||||
coreplugin: 'Core plugin',
|
||||
ripgrepPath: 'ripgrep path',
|
||||
ripgrepPathDescription: 'Path to the ripgrep binary. When using ripgrep regex search, this is required.',
|
||||
},
|
||||
|
||||
@ -82,6 +82,7 @@ export default {
|
||||
copy: "复制",
|
||||
editOrApplyDiff: "{mode}:{path}",
|
||||
loading: "加载中...",
|
||||
matchSearchInPath: '在 {path} 中匹配搜索文件 "{query}"',
|
||||
regexSearchInPath: '在 {path} 中正则搜索文件 "{regex}"',
|
||||
createNewNote: "创建新笔记",
|
||||
copyMsg: "复制消息",
|
||||
@ -228,10 +229,13 @@ export default {
|
||||
auto: '自动',
|
||||
semantic: '语义',
|
||||
regex: '正则',
|
||||
match: '匹配',
|
||||
regexBackend: '正则搜索后端',
|
||||
regexBackendDescription: '选择正则搜索的后端。',
|
||||
matchBackend: '匹配搜索后端',
|
||||
matchBackendDescription: '选择匹配搜索的后端。',
|
||||
ripgrep: 'ripgrep',
|
||||
omnisearch: 'Omnisearch',
|
||||
coreplugin: '核心插件',
|
||||
ripgrepPath: 'ripgrep 路径',
|
||||
ripgrepPathDescription: 'ripgrep 二进制文件的路径。使用 ripgrep 正则搜索时需要此项。',
|
||||
},
|
||||
|
||||
@ -155,26 +155,42 @@ export class InfioSettingTab extends PluginSettingTab {
|
||||
.addOption('auto', t('settings.FilesSearch.auto'))
|
||||
.addOption('semantic', t('settings.FilesSearch.semantic'))
|
||||
.addOption('regex', t('settings.FilesSearch.regex'))
|
||||
.addOption('match', t('settings.FilesSearch.match'))
|
||||
.setValue(this.plugin.settings.filesSearchMethod)
|
||||
.onChange(async (value) => {
|
||||
await this.plugin.setSettings({
|
||||
...this.plugin.settings,
|
||||
filesSearchMethod: value as 'regex' | 'semantic' | 'auto',
|
||||
filesSearchMethod: value as 'match' | 'regex' | 'semantic' | 'auto',
|
||||
})
|
||||
}),
|
||||
)
|
||||
new Setting(containerEl)
|
||||
new Setting(containerEl)
|
||||
.setName(t('settings.FilesSearch.regexBackend'))
|
||||
.setDesc(t('settings.FilesSearch.regexBackendDescription'))
|
||||
.addDropdown((dropdown) =>
|
||||
dropdown
|
||||
.addOption('ripgrep', t('settings.FilesSearch.ripgrep'))
|
||||
.addOption('omnisearch', t('settings.FilesSearch.omnisearch'))
|
||||
.addOption('coreplugin', t('settings.FilesSearch.coreplugin'))
|
||||
.setValue(this.plugin.settings.regexSearchBackend)
|
||||
.onChange(async (value) => {
|
||||
await this.plugin.setSettings({
|
||||
...this.plugin.settings,
|
||||
regexSearchBackend: value as 'ripgrep' | 'omnisearch',
|
||||
regexSearchBackend: value as 'ripgrep' | 'coreplugin',
|
||||
})
|
||||
}),
|
||||
)
|
||||
new Setting(containerEl)
|
||||
.setName(t('settings.FilesSearch.matchBackend'))
|
||||
.setDesc(t('settings.FilesSearch.matchBackendDescription'))
|
||||
.addDropdown((dropdown) =>
|
||||
dropdown
|
||||
.addOption('coreplugin', t('settings.FilesSearch.coreplugin'))
|
||||
.addOption('omnisearch', t('settings.FilesSearch.omnisearch'))
|
||||
.setValue(this.plugin.settings.matchSearchBackend)
|
||||
.onChange(async (value) => {
|
||||
await this.plugin.setSettings({
|
||||
...this.plugin.settings,
|
||||
matchSearchBackend: value as 'coreplugin' | 'omnisearch',
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
@ -20,6 +20,14 @@ export type ListFilesToolArgs = {
|
||||
recursive?: boolean;
|
||||
}
|
||||
|
||||
export type MatchSearchFilesToolArgs = {
|
||||
type: 'match_search_files';
|
||||
filepath?: string;
|
||||
query?: string;
|
||||
file_pattern?: string;
|
||||
finish?: boolean;
|
||||
}
|
||||
|
||||
export type RegexSearchFilesToolArgs = {
|
||||
type: 'regex_search_files';
|
||||
filepath?: string;
|
||||
@ -97,4 +105,4 @@ export type UseMcpToolArgs = {
|
||||
parameters: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export type ToolArgs = ReadFileToolArgs | WriteToFileToolArgs | InsertContentToolArgs | SearchAndReplaceToolArgs | ListFilesToolArgs | RegexSearchFilesToolArgs | SemanticSearchFilesToolArgs | SearchWebToolArgs | FetchUrlsContentToolArgs | SwitchModeToolArgs | ApplyDiffToolArgs | UseMcpToolArgs;
|
||||
export type ToolArgs = ReadFileToolArgs | WriteToFileToolArgs | InsertContentToolArgs | SearchAndReplaceToolArgs | ListFilesToolArgs | MatchSearchFilesToolArgs | RegexSearchFilesToolArgs | SemanticSearchFilesToolArgs | SearchWebToolArgs | FetchUrlsContentToolArgs | SwitchModeToolArgs | ApplyDiffToolArgs | UseMcpToolArgs;
|
||||
|
||||
@ -260,8 +260,9 @@ export const InfioSettingsSchema = z.object({
|
||||
jinaApiKey: z.string().catch(''),
|
||||
|
||||
// Files Search
|
||||
filesSearchMethod: z.enum(['regex', 'semantic', 'auto']).catch('auto'),
|
||||
regexSearchBackend: z.enum(['omnisearch', 'ripgrep']).catch('ripgrep'),
|
||||
filesSearchMethod: z.enum(['match', 'regex', 'semantic', 'auto']).catch('auto'),
|
||||
regexSearchBackend: z.enum(['coreplugin', 'ripgrep']).catch('ripgrep'),
|
||||
matchSearchBackend: z.enum(['omnisearch', 'coreplugin']).catch('coreplugin'),
|
||||
ripgrepPath: z.string().catch(''),
|
||||
|
||||
/// [compatible]
|
||||
|
||||
@ -28,6 +28,10 @@ export const listFilesAndFolders = async (vault: Vault, path: string) => {
|
||||
return []
|
||||
}
|
||||
|
||||
export const matchSearchFiles = async (vault: Vault, path: string, query: string, file_pattern: string) => {
|
||||
|
||||
}
|
||||
|
||||
export const regexSearchFiles = async (vault: Vault, path: string, regex: string, file_pattern: string) => {
|
||||
|
||||
}
|
||||
|
||||
@ -59,6 +59,11 @@ export type ParsedMsgBlock =
|
||||
path: string
|
||||
recursive?: boolean
|
||||
finish: boolean
|
||||
} | {
|
||||
type: 'match_search_files'
|
||||
path: string
|
||||
query: string
|
||||
finish: boolean
|
||||
} | {
|
||||
type: 'regex_search_files'
|
||||
path: string
|
||||
@ -226,6 +231,36 @@ export function parseMsgBlocks(
|
||||
finish: node.sourceCodeLocation.endTag !== undefined
|
||||
})
|
||||
lastEndOffset = endOffset
|
||||
} else if (node.nodeName === 'match_search_files') {
|
||||
if (!node.sourceCodeLocation) {
|
||||
throw new Error('sourceCodeLocation is undefined')
|
||||
}
|
||||
const startOffset = node.sourceCodeLocation.startOffset
|
||||
const endOffset = node.sourceCodeLocation.endOffset
|
||||
if (startOffset > lastEndOffset) {
|
||||
parsedResult.push({
|
||||
type: 'string',
|
||||
content: input.slice(lastEndOffset, startOffset),
|
||||
})
|
||||
}
|
||||
let path: string | undefined
|
||||
let query: string | undefined
|
||||
|
||||
for (const childNode of node.childNodes) {
|
||||
if (childNode.nodeName === 'path' && childNode.childNodes.length > 0) {
|
||||
path = childNode.childNodes[0].value
|
||||
} else if (childNode.nodeName === 'query' && childNode.childNodes.length > 0) {
|
||||
query = childNode.childNodes[0].value
|
||||
}
|
||||
}
|
||||
|
||||
parsedResult.push({
|
||||
type: 'match_search_files',
|
||||
path: path,
|
||||
query: query,
|
||||
finish: node.sourceCodeLocation.endTag !== undefined
|
||||
})
|
||||
lastEndOffset = endOffset
|
||||
} else if (node.nodeName === 'regex_search_files') {
|
||||
if (!node.sourceCodeLocation) {
|
||||
throw new Error('sourceCodeLocation is undefined')
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user