mirror of
https://github.com/stablyai/orca.git
synced 2026-10-20 00:03:45 +00:00
* Add project Windows runtime selection * Fix project Windows runtime selection Co-authored-by: Orca <help@stably.ai> * fix: preserve WSL shell variables --------- Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com> Co-authored-by: Orca <help@stably.ai> Co-authored-by: Neil <neil@stably.ai>
98 lines
2.7 KiB
TypeScript
98 lines
2.7 KiB
TypeScript
import type { SearchOptions, SearchResult } from '../../shared/types'
|
|
import {
|
|
buildGitGrepArgs,
|
|
buildSubmatchRegex,
|
|
createAccumulator,
|
|
finalize,
|
|
ingestGitGrepLine,
|
|
SEARCH_TIMEOUT_MS
|
|
} from '../../shared/text-search'
|
|
import { gitSpawn } from '../git/runner'
|
|
|
|
/**
|
|
* Fallback text search using git grep. Used when rg is not available.
|
|
*
|
|
* Why: On Linux, rg may not be installed or may not be in PATH when the app
|
|
* is launched from a desktop entry (which inherits a minimal system PATH).
|
|
* git grep is always available since this is a git-focused app.
|
|
*/
|
|
export function searchWithGitGrep(
|
|
rootPath: string,
|
|
args: SearchOptions,
|
|
maxResults: number,
|
|
localGitOptions: { wslDistro?: string } = {}
|
|
): Promise<SearchResult> {
|
|
return new Promise((resolve) => {
|
|
const gitArgs = buildGitGrepArgs(args.query, args)
|
|
const matchRegex = buildSubmatchRegex(args.query, args)
|
|
const acc = createAccumulator()
|
|
let stdoutBuffer = ''
|
|
let done = false
|
|
|
|
const child = gitSpawn(gitArgs, {
|
|
cwd: rootPath,
|
|
...(localGitOptions.wslDistro ? { wslDistro: localGitOptions.wslDistro } : {}),
|
|
stdio: ['ignore', 'pipe', 'pipe']
|
|
})
|
|
let killTimeout: ReturnType<typeof setTimeout>
|
|
|
|
function resolveOnce(): void {
|
|
if (done) {
|
|
return
|
|
}
|
|
done = true
|
|
clearTimeout(killTimeout)
|
|
// Why: child.kill() is advisory. If git ignores it, detach our
|
|
// closures so repeated fallback searches do not retain old scans.
|
|
child.stdout!.off('data', handleStdoutData)
|
|
child.stderr!.off('data', handleStderrData)
|
|
child.off('error', handleError)
|
|
child.off('close', handleClose)
|
|
resolve(finalize(acc))
|
|
}
|
|
|
|
function processLine(line: string): void {
|
|
const verdict = ingestGitGrepLine(line, rootPath, matchRegex, acc, maxResults)
|
|
if (verdict === 'stop') {
|
|
child.kill()
|
|
}
|
|
}
|
|
|
|
function handleStdoutData(chunk: string): void {
|
|
stdoutBuffer += chunk
|
|
const lines = stdoutBuffer.split('\n')
|
|
stdoutBuffer = lines.pop() ?? ''
|
|
for (const l of lines) {
|
|
processLine(l)
|
|
}
|
|
}
|
|
|
|
function handleStderrData(): void {
|
|
/* drain */
|
|
}
|
|
|
|
function handleError(): void {
|
|
resolveOnce()
|
|
}
|
|
|
|
function handleClose(): void {
|
|
if (stdoutBuffer) {
|
|
processLine(stdoutBuffer)
|
|
}
|
|
resolveOnce()
|
|
}
|
|
|
|
child.stdout!.setEncoding('utf-8')
|
|
child.stdout!.on('data', handleStdoutData)
|
|
child.stderr!.on('data', handleStderrData)
|
|
child.once('error', handleError)
|
|
child.once('close', handleClose)
|
|
|
|
killTimeout = setTimeout(() => {
|
|
acc.truncated = true
|
|
child.kill()
|
|
resolveOnce()
|
|
}, SEARCH_TIMEOUT_MS)
|
|
})
|
|
}
|