plugin: plumb file token with acme isfilec expansion
Replace Document::wordAt() in plumbAtCursor() with fileTokenAt(), an acme-style button-3 file expansion: grow over isFileChar() (alnum, '_', and '. - + / : @', matching plan9port acme's isfilec), with ':' ending the file name and only a digit-led ':line[:col]' suffix kept attached. This grabs whole paths like /a/b/dpg_identity.php:584 under the caret. Also reverts the earlier ^W change so acmeEraseWord again matches acme's C isalnum (which excludes '_').
This commit is contained in:
parent
08a75c06af
commit
019fed6db4
|
|
@ -348,7 +348,10 @@ Kate flow through the real plumber rules.
|
||||||
- **Plugin wiring** (`src/plugin/ollieplugin.cpp`): an `ollie_plumb` QAction on
|
- **Plugin wiring** (`src/plugin/ollieplugin.cpp`): an `ollie_plumb` QAction on
|
||||||
**F2** (testing trigger; a function key, so no collision with the Alt+letter
|
**F2** (testing trigger; a function key, so no collision with the Alt+letter
|
||||||
door policy — final gesture will be RMB). `plumbAtCursor()` plumbs the
|
door policy — final gesture will be RMB). `plumbAtCursor()` plumbs the
|
||||||
selection, else `Document::wordAt(cursorPosition)`, with the active doc's dir
|
selection, else the acme-style file-path token under the caret
|
||||||
|
(`fileTokenAt` + `isFileChar`, matching plan9port acme's `isfilec`: alnum,
|
||||||
|
`_`, and `. - + / : @`; `:` ends the file name and only a digit-led
|
||||||
|
`:line[:col]` suffix is kept), with the active doc's dir
|
||||||
as `wdir`; on send failure it falls back to an internal resolver (URL →
|
as `wdir`; on send failure it falls back to an internal resolver (URL →
|
||||||
`QDesktopServices`, path[:line] → open here). Incoming `edit` messages are
|
`QDesktopServices`, path[:line] → open here). Incoming `edit` messages are
|
||||||
opened via `MainWindow::openUrl` + `activateView` + `setCursorPosition`; a
|
opened via `MainWindow::openUrl` + `activateView` + `setCursorPosition`; a
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,29 @@ bool isQuote(QChar c)
|
||||||
return u == '\'' || u == '"' || u == '`';
|
return u == '\'' || u == '"' || u == '`';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Characters that may appear in a plumbable file path, mirroring plan9port
|
||||||
|
// acme's isfilec() (look.c). Acme treats a rune as a file char when its
|
||||||
|
// isalnum() is true OR it is one of ". - + / : @". Acme's isalnum() counts '_'
|
||||||
|
// as alphanumeric (it is not in acme's punctuation set), so '_' is a file char;
|
||||||
|
// QChar::isLetterOrNumber() excludes it, hence the explicit test.
|
||||||
|
bool isFileChar(QChar c)
|
||||||
|
{
|
||||||
|
if (c.isLetterOrNumber() || c == QLatin1Char('_')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
switch (c.unicode()) {
|
||||||
|
case '.':
|
||||||
|
case '-':
|
||||||
|
case '+':
|
||||||
|
case '/':
|
||||||
|
case ':':
|
||||||
|
case '@':
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Advance a cursor by one character, wrapping across line ends. Returns false
|
// Advance a cursor by one character, wrapping across line ends. Returns false
|
||||||
// at end of document.
|
// at end of document.
|
||||||
bool stepForward(KTextEditor::Document *doc, KTextEditor::Cursor &c)
|
bool stepForward(KTextEditor::Document *doc, KTextEditor::Cursor &c)
|
||||||
|
|
@ -213,6 +236,51 @@ KTextEditor::Cursor findQuoteBackward(KTextEditor::Document *doc, int line, int
|
||||||
return KTextEditor::Cursor::invalid();
|
return KTextEditor::Cursor::invalid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Expand the caret position into the surrounding file-path token, mirroring
|
||||||
|
// plan9port acme's button-3 file expansion (look.c). Grow left and right over
|
||||||
|
// isFileChar() runs, but treat ':' as acme does: it ends the file name and may
|
||||||
|
// introduce a numeric ":line[:col]" address, which is kept attached to the
|
||||||
|
// token (the plumber / onPlumbEdit splits it downstream). Expansion is
|
||||||
|
// line-local; acme stops at white space and a newline is never a file char.
|
||||||
|
QString fileTokenAt(KTextEditor::Document *doc, const KTextEditor::Cursor &at)
|
||||||
|
{
|
||||||
|
const int line = at.line();
|
||||||
|
const int len = doc->lineLength(line);
|
||||||
|
auto ch = [&](int c) { return doc->characterAt(KTextEditor::Cursor(line, c)); };
|
||||||
|
auto isFileNoColon = [&](QChar c) { return isFileChar(c) && c != QLatin1Char(':'); };
|
||||||
|
|
||||||
|
// File name: grow left and right over file chars, stopping before any ':'
|
||||||
|
// (acme ends the name at the colon and parses an address after it).
|
||||||
|
int lo = at.column();
|
||||||
|
int hi = at.column();
|
||||||
|
while (hi < len && isFileNoColon(ch(hi))) {
|
||||||
|
++hi;
|
||||||
|
}
|
||||||
|
while (lo > 0 && isFileNoColon(ch(lo - 1))) {
|
||||||
|
--lo;
|
||||||
|
}
|
||||||
|
if (hi <= lo) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional ":line[:col]" address: only when the colon is immediately
|
||||||
|
// followed by a digit, matching acme's isaddrc gate and the numeric suffix
|
||||||
|
// onPlumbEdit expects. Consume up to two colon-separated digit runs.
|
||||||
|
int end = hi;
|
||||||
|
for (int part = 0; part < 2 && end < len && ch(end) == QLatin1Char(':'); ++part) {
|
||||||
|
int scan = end + 1;
|
||||||
|
if (scan >= len || !ch(scan).isDigit()) {
|
||||||
|
break; // ':' not introducing a number — leave it out of the token
|
||||||
|
}
|
||||||
|
while (scan < len && ch(scan).isDigit()) {
|
||||||
|
++scan;
|
||||||
|
}
|
||||||
|
end = scan;
|
||||||
|
}
|
||||||
|
|
||||||
|
return doc->text(KTextEditor::Range(line, lo, line, end));
|
||||||
|
}
|
||||||
|
|
||||||
// Prefix marking a palette id that is a ":" command string to execute rather
|
// Prefix marking a palette id that is a ":" command string to execute rather
|
||||||
// than a QAction to trigger.
|
// than a QAction to trigger.
|
||||||
const QLatin1String kCmdPrefix("ollie:cmd:");
|
const QLatin1String kCmdPrefix("ollie:cmd:");
|
||||||
|
|
@ -873,14 +941,15 @@ void OllieView::plumbAtCursor()
|
||||||
}
|
}
|
||||||
|
|
||||||
// What to plumb: an explicit selection wins (lets the user plumb a path
|
// What to plumb: an explicit selection wins (lets the user plumb a path
|
||||||
// with a line suffix like "foo.cpp:42"), otherwise the word under the
|
// with a line suffix like "foo.cpp:42"), otherwise the file-path token
|
||||||
// caret.
|
// under the caret, expanded acme-style (isFileChar: alnum, '_', and
|
||||||
|
// ". - + / : @") so paths and their ":line" address are grabbed whole.
|
||||||
QString data;
|
QString data;
|
||||||
if (view->selection()) {
|
if (view->selection()) {
|
||||||
data = view->selectionText().trimmed();
|
data = view->selectionText().trimmed();
|
||||||
}
|
}
|
||||||
if (data.isEmpty()) {
|
if (data.isEmpty()) {
|
||||||
data = doc->wordAt(view->cursorPosition());
|
data = fileTokenAt(doc, view->cursorPosition());
|
||||||
}
|
}
|
||||||
if (data.isEmpty()) {
|
if (data.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -1702,9 +1771,7 @@ void OllieView::acmeEraseWord()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const QChar r = doc->characterAt(prev);
|
const QChar r = doc->characterAt(prev);
|
||||||
// Acme's isalnum() treats '_' as a word character (it is not in acme's
|
const bool alnum = r.isLetterOrNumber();
|
||||||
// punctuation set), so ^W does not stop on underscores.
|
|
||||||
const bool alnum = r.isLetterOrNumber() || r == QLatin1Char('_');
|
|
||||||
if (alnum && skipping) {
|
if (alnum && skipping) {
|
||||||
skipping = false;
|
skipping = false;
|
||||||
} else if (!alnum && !skipping) {
|
} else if (!alnum && !skipping) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue