-
Notifications
You must be signed in to change notification settings - Fork 0
[codex] Fix path-like filename search #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -137,9 +137,9 @@ function buildWindowsSearchCommand(search) { | |
| "if ($scopeClauses.Count -eq 0) { exit 0 }", | ||
| "$whereClauses = @()", | ||
| "$whereClauses += '(' + ($scopeClauses -join ' OR ') + ')'", | ||
| "$whereClauses += \"System.ItemTypeText <> 'File folder'\"", | ||
| "foreach ($term in $terms) {", | ||
| " $whereClauses += \"System.FileName LIKE '%\" + (Escape-LikeLiteral $term) + \"%'\"", | ||
| " $pathTerm = $term.Replace('/', [string][char]92)", | ||
| " $whereClauses += \"System.ItemPathDisplay LIKE '%\" + (Escape-LikeLiteral $pathTerm) + \"%'\"", | ||
| "}", | ||
| "if ($modifiedWithinDays) {", | ||
| " $cutoff = (Get-Date).AddDays(-[int]$modifiedWithinDays).ToString('yyyy-MM-ddTHH:mm:ss')", | ||
|
|
@@ -157,7 +157,6 @@ function buildWindowsSearchCommand(search) { | |
| " while ($reader.Read() -and $count -lt $maxResults) {", | ||
| " $path = [string]$reader.GetValue(0)", | ||
| " try { $file = Get-Item -LiteralPath $path -ErrorAction Stop } catch { continue }", | ||
| " if ($file.PSIsContainer) { continue }", | ||
| " [Console]::Out.Write($file.FullName + [char]0)", | ||
| " $count += 1", | ||
| " }", | ||
|
|
@@ -294,16 +293,18 @@ function resolveRipgrepPath(options = {}) { | |
| return candidates.find((candidate) => candidate && fileExists(candidate)) || "rg"; | ||
| } | ||
|
|
||
| function normalizeFilenameComparable(value) { | ||
| return String(value || "").trim().toLowerCase().replace(/\\/g, "/"); | ||
| } | ||
|
|
||
| function splitSearchTerms(query) { | ||
| return String(query || "") | ||
| .trim() | ||
| .toLowerCase() | ||
| return normalizeFilenameComparable(query) | ||
| .split(/\s+/) | ||
| .filter(Boolean); | ||
| } | ||
|
|
||
| function matchesFilenameSearch(filePath, terms) { | ||
| const candidate = String(filePath || "").toLowerCase(); | ||
| const candidate = normalizeFilenameComparable(filePath); | ||
| return terms.every((term) => candidate.includes(term)); | ||
| } | ||
|
|
||
|
|
@@ -331,7 +332,7 @@ function directFilePathResult(search, roots) { | |
|
|
||
| try { | ||
| const stats = statSync(filePath); | ||
| if (!stats.isFile() || !isModifiedWithin(filePath, search.modifiedWithinDays, stats)) { | ||
| if ((!stats.isFile() && !stats.isDirectory()) || !isModifiedWithin(filePath, search.modifiedWithinDays, stats)) { | ||
| return []; | ||
| } | ||
| } catch { | ||
|
|
@@ -341,6 +342,11 @@ function directFilePathResult(search, roots) { | |
| return [{ source: "windows", target: driveTargetForPath(filePath), path: filePath }]; | ||
| } | ||
|
|
||
| function isPathLikeFilenameQuery(query) { | ||
| const normalized = String(query || "").trim().replace(/\\/g, "/"); | ||
| return normalized.includes("/") || normalized.startsWith("."); | ||
| } | ||
|
|
||
| function runCommand(command, timeoutMs) { | ||
| return new Promise((resolve, reject) => { | ||
| const controller = new AbortController(); | ||
|
|
@@ -388,7 +394,28 @@ function runFilenameSearch(search, timeoutMs = SEARCH_TIMEOUT_MS) { | |
| const results = []; | ||
| let buffer = ""; | ||
| let settled = false; | ||
| const child = spawn(resolveRipgrepPath(), ["--files", "-0", ...roots], { windowsHide: true }); | ||
| const child = spawn( | ||
| resolveRipgrepPath(), | ||
| [ | ||
| "--files", | ||
| "-0", | ||
| "--hidden", | ||
| "--glob", | ||
| "!.git", | ||
| "--glob", | ||
| "!node_modules", | ||
| "--glob", | ||
| "!.cache", | ||
| "--glob", | ||
| "!.venv", | ||
| "--glob", | ||
| "!__pycache__", | ||
| "--glob", | ||
| "!dist", | ||
| ...roots, | ||
| ], | ||
| { windowsHide: true } | ||
| ); | ||
|
Comment on lines
+397
to
+418
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Filesystem fallback still drops directory matches.
Also applies to: 481-485 |
||
| const timeout = setTimeout(() => finish(new Error("Command timed out.")), timeoutMs); | ||
|
|
||
| function finish(error) { | ||
|
|
@@ -451,7 +478,11 @@ function shouldUseFilesystemFilenameSearch(search, roots) { | |
| return false; | ||
| } | ||
| const profileRoot = process.env.USERPROFILE ? normalizeComparableWindowsRoot(process.env.USERPROFILE) : ""; | ||
| return search.root === ALL_WINDOWS_DRIVES || roots.some((root) => normalizeComparableWindowsRoot(root) !== profileRoot); | ||
| return ( | ||
| search.root === ALL_WINDOWS_DRIVES || | ||
| isPathLikeFilenameQuery(search.query) || | ||
| roots.some((root) => normalizeComparableWindowsRoot(root) !== profileRoot) | ||
| ); | ||
| } | ||
|
|
||
| function createWindowsSearchAdapter(options = {}) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,10 @@ function shellQuote(value) { | |
| return `'${String(value).replace(/'/g, `'\\''`)}'`; | ||
| } | ||
|
|
||
| function escapeWslCommandArgument(value) { | ||
| return String(value).replace(/\$/g, "\\$"); | ||
| } | ||
|
Comment on lines
+18
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Smallest correct fix: apply
Comment on lines
+18
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
search_command='candidate=/tmp/demo; query='\''price$list.txt'\''; printf "candidate=%s\nquery=%s\n" "$candidate" "$query"'
wrapped_without_escape="timeout 1s bash -lc '$search_command'"
wrapped_with_escape="timeout 1s bash -lc '${search_command//$/\\$}'"
echo "without post-escape:"
bash -lc "$wrapped_without_escape"
echo
echo "with post-escape:"
bash -lc "$wrapped_with_escape"Repository: future3OOO/wsl-search-tool Length of output: 251 🏁 Script executed: # First, locate and examine the file
find . -name "wslSearchAdapter.js" -type fRepository: future3OOO/wsl-search-tool Length of output: 103 🏁 Script executed: # Read the entire file to understand the context
wc -l ./src/search/wslSearchAdapter.jsRepository: future3OOO/wsl-search-tool Length of output: 107 🏁 Script executed: # Read the critical sections
echo "=== Lines 15-25 (function definition) ==="
sed -n '15,25p' ./src/search/wslSearchAdapter.js
echo
echo "=== Lines 139-160 (buildDirectPathPrefix and variable references) ==="
sed -n '139,160p' ./src/search/wslSearchAdapter.js
echo
echo "=== Lines 185-200 (call sites) ==="
sed -n '185,200p' ./src/search/wslSearchAdapter.jsRepository: future3OOO/wsl-search-tool Length of output: 2933 🏁 Script executed: # Check how buildSearchScript is used (where it's called)
echo "=== Searching for buildSearchScript calls ==="
rg "buildSearchScript" ./src/search/wslSearchAdapter.js -B 2 -A 2
echo
echo "=== Check for execSync or similar execution ==="
rg "execSync|spawn|exec" ./src/search/wslSearchAdapter.js -B 2 -A 2Repository: future3OOO/wsl-search-tool Length of output: 1018 🏁 Script executed: # Check how the result of buildSearchScript is used
echo "=== Full buildSearchCommand function ==="
sed -n '173,180p' ./src/search/wslSearchAdapter.js
echo
echo "=== Look for test files ==="
find . -name "*test*" -o -name "*.test.js" -o -name "*.spec.js" | head -20Repository: future3OOO/wsl-search-tool Length of output: 685 🏁 Script executed: # Get the complete buildSearchCommand function
sed -n '160,205p' ./src/search/wslSearchAdapter.jsRepository: future3OOO/wsl-search-tool Length of output: 2698 🏁 Script executed: # Check if there are tests for the functions being modified
grep -r "buildSearchScript\|buildSearchCommand\|escapeWslCommandArgument" ./test/ -lRepository: future3OOO/wsl-search-tool Length of output: 92 🏁 Script executed: # Check the search test file for relevant tests
grep -n "buildSearchScript\|buildSearchCommand\|escapeWslCommandArgument" ./test/search.test.js -B 2 -A 5Repository: future3OOO/wsl-search-tool Length of output: 2929 🏁 Script executed: # Read test cases to understand expected behavior
sed -n '36,51p' ./test/search.test.js
echo "---"
sed -n '104,116p' ./test/search.test.js
echo "---"
sed -n '145,157p' ./test/search.test.jsRepository: future3OOO/wsl-search-tool Length of output: 1590 🏁 Script executed: # Let's verify what the current output actually produces
# Look at the full test for the exact-path scenario
sed -n '145,157p' ./test/search.test.js
echo "---"
echo "Let's examine what escapeWslCommandArgument is actually doing to the shell command:"
echo "Looking at lines 18-20 again:"
sed -n '18,20p' ./src/search/wslSearchAdapter.jsRepository: future3OOO/wsl-search-tool Length of output: 778 🏁 Script executed: # Check if there are any tests that actually execute the shell commands
grep -n "spawn\|execSync\|exec" ./test/search.test.js
echo "---"
echo "Let's understand the quoting flow:"
echo "The test expects escaped dollars. Let's trace what buildSearchScript actually produces."
echo "---"
# Let's run a Node snippet to see what buildSearchScript produces
node <<'EOF'
const { buildSearchCommand } = require('./src/search/wslSearch');
const command = buildSearchCommand({
distro: "OpenCockLab",
root: "/home",
query: "/home/test.txt",
mode: "filename",
maxResults: 10,
});
console.log("Full command array:");
command.forEach((arg, i) => {
if (arg.length > 100) {
console.log(`[${i}]: ${arg.substring(0, 100)}...`);
} else {
console.log(`[${i}]: ${arg}`);
}
});
console.log("\n=== Command[6] (the bash script) ===");
console.log(command[6]);
console.log("\n=== Looking for $candidate or \\$candidate ===");
if (command[6].includes('$candidate')) {
console.log("Found: $candidate (unescaped)");
}
if (command[6].includes('\\$candidate')) {
console.log("Found: \\$candidate (escaped)");
}
EOFRepository: future3OOO/wsl-search-tool Length of output: 1090 🏁 Script executed: # Check what's exported from wslSearchAdapter
grep -n "^module.exports\|^exports" ./src/search/wslSearchAdapter.js
echo "---"
echo "Check if there's a wslSearch.js file:"
find . -name "wslSearch.js" -type f
echo "---"
echo "Check the test imports:"
head -20 ./test/search.test.jsRepository: future3OOO/wsl-search-tool Length of output: 682 🏁 Script executed: # Check the root wslSearch.js file to understand exports
head -30 ./wslSearch.js
echo "---"
echo "Now let's run the actual test to see if it passes or fails"
cd ./test && npm test -- search.test.js 2>&1 | head -100Repository: future3OOO/wsl-search-tool Length of output: 284 🏁 Script executed: # Run the test from the root
npm test 2>&1 | grep -A 50 "search.test.js\|buildSearchCommand"Repository: future3OOO/wsl-search-tool Length of output: 4563 🏁 Script executed: # Let's manually trace through what buildSearchScript produces with the exact-path query
node <<'EOF'
const fs = require('fs');
const path = require('path');
// Read and eval the adapter to get the functions
const adapterCode = fs.readFileSync('./src/search/wslSearchAdapter.js', 'utf8');
// Create a context with necessary functions
const module = { exports: {} };
const require_local = (name) => {
if (name === 'node:child_process') return {};
return {};
};
// Execute only the relevant parts
eval(adapterCode.replace('require("node:child_process")', '{}'));
const functions = module.exports;
// Test the exact-path scenario
const result = functions.buildSearchCommand({
distro: "OpenCockLab",
root: "/home",
query: "/home/test.txt",
mode: "filename",
maxResults: 10,
});
console.log("=== buildSearchCommand result ===");
console.log("Command[6] (the bash -lc argument):");
const bashArg = result[6];
console.log(bashArg.substring(0, 200) + "...");
console.log("\n=== Checking for variable references ===");
if (bashArg.includes('$candidate')) {
console.log("✓ Found unescaped: $candidate");
} else if (bashArg.includes('\\$candidate')) {
console.log("✗ Found escaped: \\$candidate");
} else {
console.log("? Neither found");
}
if (bashArg.includes('$root_path')) {
console.log("✓ Found unescaped: $root_path");
} else if (bashArg.includes('\\$root_path')) {
console.log("✗ Found escaped: \\$root_path");
}
EOFRepository: future3OOO/wsl-search-tool Length of output: 446 🏁 Script executed: # Let's check the complete bash script to understand the variable expansion issue
node <<'EOF'
const fs = require('fs');
const adapterCode = fs.readFileSync('./src/search/wslSearchAdapter.js', 'utf8');
const module = { exports: {} };
eval(adapterCode.replace('require("node:child_process")', '{}'));
const functions = module.exports;
// Test the exact-path scenario
const result = functions.buildSearchCommand({
distro: "OpenCockLab",
root: "/home",
query: "/home/test.txt",
mode: "filename",
maxResults: 10,
});
const bashArg = result[6];
console.log("=== Full bash -lc argument ===");
console.log(bashArg);
console.log("\n=== Key observation ===");
console.log("In bash, \\$root_path means literal $root_path (backslash escapes the dollar)");
console.log("So when bash interprets the command, [ \"\\$root_path\" = \"/\" ] compares");
console.log("the literal string '$root_path' instead of expanding the variable.");
EOFRepository: future3OOO/wsl-search-tool Length of output: 1430 Remove the post-quoting
Suggested fix-function escapeWslCommandArgument(value) {
- return String(value).replace(/\$/g, "\\$");
-}
-
...
- return escapeWslCommandArgument(`timeout ${WSL_SEARCH_TIMEOUT_SECONDS}s bash -lc ${shellQuote(searchCommand)}`);
+ return `timeout ${WSL_SEARCH_TIMEOUT_SECONDS}s bash -lc ${shellQuote(searchCommand)}`;
...
- return escapeWslCommandArgument(`timeout ${WSL_SEARCH_TIMEOUT_SECONDS}s bash -lc ${shellQuote(searchCommand)}`);
+ return `timeout ${WSL_SEARCH_TIMEOUT_SECONDS}s bash -lc ${shellQuote(searchCommand)}`;
...
- return escapeWslCommandArgument(`timeout ${WSL_SEARCH_TIMEOUT_SECONDS}s bash -lc ${shellQuote(searchCommand)}`);
+ return `timeout ${WSL_SEARCH_TIMEOUT_SECONDS}s bash -lc ${shellQuote(searchCommand)}`;Also applies to: Lines 139–154, 188–198 🤖 Prompt for AI Agents |
||
|
|
||
| function parsePositiveInteger(value, fallback) { | ||
| const parsed = Number.parseInt(value, 10); | ||
| if (!Number.isFinite(parsed) || parsed < 1) { | ||
|
|
@@ -27,13 +31,34 @@ function splitSearchTerms(query) { | |
| return query.split(/\s+/).filter(Boolean); | ||
| } | ||
|
|
||
| function parseWslUncPath(value) { | ||
| const normalized = String(value || "").trim().replace(/\\/g, "/"); | ||
| const match = normalized.match(/^\/\/wsl(?:\.localhost|\$)\/([^/]+)(\/.*)?$/i); | ||
| if (!match || !match[1]) { | ||
| return null; | ||
| } | ||
| return { distro: match[1], path: match[2] || "/" }; | ||
| } | ||
|
|
||
| function normalizeWslRoot(root) { | ||
| if (root === "/") { | ||
| return root; | ||
| } | ||
| return root.replace(/\/+$/, "") || "/"; | ||
| } | ||
|
|
||
| function buildPruneExpression() { | ||
| return PRUNED_DIRECTORY_NAMES.map((name) => `-name ${shellQuote(name)}`).join(" -o "); | ||
| } | ||
|
|
||
| function buildFindFilesCommand(root, filePredicates) { | ||
| function buildFindPathsCommand(root) { | ||
| const pruneExpression = buildPruneExpression(); | ||
| return `find ${root} \\( -type d \\( ${pruneExpression} \\) -prune \\) -o -type f${filePredicates} -print0 2>/dev/null`; | ||
| return `find ${root} \\( -type d \\( ${pruneExpression} \\) -prune \\) -o \\( -type f -o -type d \\) -print0 2>/dev/null`; | ||
| } | ||
|
|
||
| function buildFindDirectoriesCommand(root) { | ||
| const pruneExpression = buildPruneExpression(); | ||
| return `find ${root} \\( -type d \\( ${pruneExpression} \\) -prune \\) -o -type d -print0 2>/dev/null`; | ||
| } | ||
|
|
||
| function buildRipgrepFilesCommand(root) { | ||
|
|
@@ -84,9 +109,11 @@ function buildPythonMtimeFilter(modifiedWithinDays, maxResults) { | |
|
|
||
| function validateSearchInput(input) { | ||
| const root = String(input.root || DEFAULT_ROOT).trim(); | ||
| const query = String(input.query || "").trim(); | ||
| const rawQuery = String(input.query || "").trim(); | ||
| const wslUncPath = parseWslUncPath(rawQuery); | ||
| const query = wslUncPath ? wslUncPath.path : rawQuery; | ||
| const mode = String(input.mode || "content").trim(); | ||
| const distro = String(input.distro || ALL_DISTROS).trim(); | ||
| const distro = wslUncPath ? wslUncPath.distro : String(input.distro || ALL_DISTROS).trim(); | ||
| const modifiedWithinDays = String(input.modifiedWithinDays || "").trim(); | ||
| const maxResults = parsePositiveInteger(input.maxResults, DEFAULT_MAX_RESULTS); | ||
|
|
||
|
|
@@ -109,6 +136,24 @@ function validateSearchInput(input) { | |
| return { distro, root, query, mode, modifiedWithinDays, maxResults }; | ||
| } | ||
|
|
||
| function buildDirectPathPrefix(search) { | ||
| const root = shellQuote(normalizeWslRoot(search.root)); | ||
| const query = shellQuote(search.query); | ||
| const output = search.modifiedWithinDays | ||
| ? `find "$candidate" -maxdepth 0 -mtime -${search.modifiedWithinDays} -print0 2>/dev/null` | ||
| : `printf '%s\\0' "$candidate"`; | ||
|
|
||
| return [ | ||
| `root_path=${root}`, | ||
| `candidate=${query}`, | ||
| "under_root=0", | ||
| "[ \"$root_path\" = \"/\" ] && under_root=1", | ||
| "[ \"$candidate\" = \"$root_path\" ] && under_root=1", | ||
| "case \"$candidate\" in \"$root_path\"/*) under_root=1 ;; esac", | ||
| `if [ -e "$candidate" ] && [ "$under_root" = "1" ]; then ${output}; exit 0; fi`, | ||
| ].join("; "); | ||
| } | ||
|
|
||
| function validateOpenInput(input) { | ||
| const distro = String(input.distro || "").trim(); | ||
| const path = String(input.path || "").trim(); | ||
|
|
@@ -134,26 +179,23 @@ function buildSearchScript(search) { | |
| let searchCommand; | ||
|
|
||
| if (search.mode === "filename") { | ||
| const namePredicates = splitSearchTerms(search.query) | ||
| .map((term) => `-iname ${shellQuote(`*${term}*`)}`) | ||
| .join(" "); | ||
| const fastFileList = `${buildRipgrepFilesCommand(root)} | ${buildNullSeparatedTermFilters(search.query)}`; | ||
| const fallbackFileList = buildFindFilesCommand(root, ` ${namePredicates}${modifiedPredicate}`); | ||
| const fastPathList = `{ ${buildRipgrepFilesCommand(root)}; ${buildFindDirectoriesCommand(root)}; } | ${buildNullSeparatedTermFilters(search.query)}`; | ||
| const fallbackPathList = `${buildFindPathsCommand(root)} | ${buildNullSeparatedTermFilters(search.query)}`; | ||
| const filteredFileList = search.modifiedWithinDays | ||
| ? `${fastFileList} | ${buildPythonMtimeFilter(search.modifiedWithinDays, maxResults)}` | ||
| : fastFileList; | ||
| searchCommand = `if command -v rg >/dev/null 2>&1; then ${filteredFileList}; else ${fallbackFileList}; fi | head -z -n ${maxResults}`; | ||
| return `timeout ${WSL_SEARCH_TIMEOUT_SECONDS}s bash -lc ${shellQuote(searchCommand)}`; | ||
| ? `${fastPathList} | ${buildPythonMtimeFilter(search.modifiedWithinDays, maxResults)}` | ||
| : fastPathList; | ||
| searchCommand = `${buildDirectPathPrefix(search)}; if command -v rg >/dev/null 2>&1; then ${filteredFileList}; else ${fallbackPathList}${modifiedPredicate ? ` | ${buildPythonMtimeFilter(search.modifiedWithinDays, maxResults)}` : ""}; fi | head -z -n ${maxResults}`; | ||
| return escapeWslCommandArgument(`timeout ${WSL_SEARCH_TIMEOUT_SECONDS}s bash -lc ${shellQuote(searchCommand)}`); | ||
| } | ||
|
|
||
| if (search.modifiedWithinDays) { | ||
| const mtimeFilter = buildPythonMtimeFilter(search.modifiedWithinDays, maxResults); | ||
| searchCommand = `if command -v rg >/dev/null 2>&1; then rg -i -l -0 -F --hidden --glob '!.git' --glob '!node_modules' --glob '!.cache' --glob '!.venv' --glob '!__pycache__' -- ${query} ${root}; else grep -RIlZ --exclude-dir=.git --exclude-dir=node_modules --exclude-dir=.cache --exclude-dir=.venv --exclude-dir=__pycache__ -- ${query} ${root} 2>/dev/null; fi | ${mtimeFilter} | head -z -n ${maxResults}`; | ||
| return `timeout ${WSL_SEARCH_TIMEOUT_SECONDS}s bash -lc ${shellQuote(searchCommand)}`; | ||
| return escapeWslCommandArgument(`timeout ${WSL_SEARCH_TIMEOUT_SECONDS}s bash -lc ${shellQuote(searchCommand)}`); | ||
| } | ||
|
|
||
| searchCommand = `if command -v rg >/dev/null 2>&1; then rg -i -l -0 -F --hidden --glob '!.git' --glob '!node_modules' --glob '!.cache' -- ${query} ${root}; else grep -RIlZ --exclude-dir=.git --exclude-dir=node_modules --exclude-dir=.cache -- ${query} ${root} 2>/dev/null; fi | head -z -n ${maxResults}`; | ||
| return `timeout ${WSL_SEARCH_TIMEOUT_SECONDS}s bash -lc ${shellQuote(searchCommand)}`; | ||
| return escapeWslCommandArgument(`timeout ${WSL_SEARCH_TIMEOUT_SECONDS}s bash -lc ${shellQuote(searchCommand)}`); | ||
| } | ||
|
|
||
| function buildSearchCommand(input) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
System.ItemPathDisplayexpands result set for simple word searchesSwitching from
System.FileName LIKE '%term%'toSystem.ItemPathDisplay LIKE '%term%'for all OleDb-backed filename searches causes full-path matching for every term, including simple non-path-like queries (e.g.photos,budget). Before this change, searching forphotosreturned files whose name containedphotos. Now it returns every file whose full path containsphotos— for example, every file underC:\Users\Me\Photos\— which can produce a large false-positive set.Path-like queries are now routed to
runFilenameSearch(rg), soSystem.ItemPathDisplayis only reached for simple, non-path-like, profile-root queries.rg'smatchesFilenameSearchalso does full-path matching, so the two branches are now consistent — but users who expect filename-only matching for simple terms will see significantly more results. If full-path matching for all queries is intentional, a comment explaining the deliberate semantic shift would prevent future regressions back toSystem.FileName.