Go-to-File: match and show the full project-relative path

The palette model matches only against PaletteItem::label (and the
delegate renders only the label; group was never shown). The file
switcher put the base name in label and the dir in group, so the
directory was neither matched nor displayed. Use the full relative path
as the label: FuzzyRanker already treats '/' as a word boundary and
length-penalises longer paths, so base-name hits still rank first while
orderless path-spanning queries work. 2 path-ranking tests added.
This commit is contained in:
Levi Neely 2026-10-07 20:10:31 +02:00
parent 42363157d3
commit 707e7c7250
3 changed files with 49 additions and 15 deletions

View File

@ -198,11 +198,16 @@ Therefore the palette is replaced, not extended, and the replacement is built on
(`walkDirectory`) that skips noise dirs (node_modules, build, target, …) and
symlinks when there is no git (or git yields nothing).
- Wired into the plugin: **Alt+P** "Go to File (project)" opens a second
PaletteWidget populated with the project's files (base name as the label,
directory as the trailing hint). The root is derived from the active
document's directory (fallback: cwd). Activation opens the file via
`MainWindow::openUrl` + `activateView`. Also reachable from the M-x palette
(`ollie:switch:file`) and, by objectName `ollie_goto_file`, from a radial.
PaletteWidget populated with the project's files. The **full project-relative
path** is the matched/displayed label, so FuzzyRanker scores across the
whole path (orderless: "palette model" matches `src/palette/palettemodel.cpp`)
and highlights matched characters along it; a base-name hit still ranks first
because `/` is a word boundary and the length penalty favours shorter paths.
The root is derived from the active document's directory (fallback: cwd).
Activation opens the file via `MainWindow::openUrl` + `activateView`. Also
reachable from the M-x palette (`ollie:switch:file`) and, by objectName
`ollie_goto_file`, from a radial. (2 path-ranking tests in
`test_palettemodel`.)
- Fuzzy symbol switcher scoped to the folder: DONE.
`src/project/symbolindex.{h,cpp}` (in `project_lib`, 7 unit tests incl. a
live ctags run):

View File

@ -125,6 +125,39 @@ private Q_SLOTS:
m.setQuery(QStringLiteral("ren"));
QCOMPARE(spy.count(), 1);
}
// Full project-relative paths are matched as labels. A base-name query must
// rank the file whose base name it hits above files that only match the
// query as a scattered subsequence through their directory.
void pathLabelsRankBaseNameFirst()
{
PaletteModel m;
m.setItems({
{QStringLiteral("a"), QStringLiteral("src/plugin/ollieplugin.cpp"), QString(), 0},
{QStringLiteral("b"), QStringLiteral("src/palette/palettemodel.cpp"), QString(), 0},
{QStringLiteral("c"), QStringLiteral("src/main.cpp"), QString(), 0},
});
m.setQuery(QStringLiteral("main"));
QVERIFY(m.rowCount() >= 1);
// "src/main.cpp" has 'main' as a word-start substring; it must win.
QCOMPARE(m.index(0, 0).data(PaletteModel::LabelRole).toString(),
QStringLiteral("src/main.cpp"));
}
// Orderless, path-spanning tokens: "palette model" matches the directory and
// the base name in any order.
void pathLabelsMatchOrderlessTokens()
{
PaletteModel m;
m.setItems({
{QStringLiteral("a"), QStringLiteral("src/plugin/ollieplugin.cpp"), QString(), 0},
{QStringLiteral("b"), QStringLiteral("src/palette/palettemodel.cpp"), QString(), 0},
});
m.setQuery(QStringLiteral("palette model"));
QCOMPARE(m.rowCount(), 1);
QCOMPARE(m.index(0, 0).data(PaletteModel::LabelRole).toString(),
QStringLiteral("src/palette/palettemodel.cpp"));
}
};
QTEST_MAIN(TestPaletteModel)

View File

@ -503,16 +503,12 @@ void OllieView::showFileSwitcher()
for (const QString &rel : files) {
PaletteItem item;
item.id = kFilePrefix + rel;
// Show the file name as the label and the directory as the trailing
// hint, so fuzzy matching favours the base name while the path stays
// visible for disambiguation.
const int slash = rel.lastIndexOf(QLatin1Char('/'));
if (slash >= 0) {
item.label = rel.mid(slash + 1);
item.group = rel.left(slash);
} else {
item.label = rel;
}
// Match and display the full project-relative path: FuzzyRanker scores
// against the label (orderless tokens, so "src main" matches
// "src/main.cpp"), and the delegate highlights matched characters across
// the whole path. The base name still ranks well because an exact or
// substring hit on it outscores a scattered subsequence.
item.label = rel;
items.push_back(item);
}
m_filePalette->setItems(items);