summaryrefslogtreecommitdiffstats
path: root/src/querycompleter.h
AgeCommit message (Collapse)AuthorFilesLines
4 hoursfix(completion): stop the key filter handing itself the key it forwardsDanilo M.1-0/+5
Pressing Tab or an arrow key with the completion popup open crashed the application outright. QCoreApplication::sendEvent re-runs APPLICATION-level event filters. The navigation branch forwarded the key to the popup from inside a filter installed on qApp, so the very same event came back to the filter that had just sent it. The popup was still visible, the popupVisible() guard still passed, and it forwarded again: unbounded recursion ending in a stack overflow rather than in any diagnosable error. Reproduced at 9176 recursive QueryCompleter::eventFilter frames, with a standalone Qt probe confirming the re-entry independently. Guard the filter with m_forwarding, checked before the switch so it covers every branch rather than the navigation keys alone. A key the filter is itself redelivering now falls through to the popup instead of being claimed a second time. The existing tests missed this because they exercised the accept path without ever forwarding an event. arrowNavigationDoesNotRecurse drives Down through the grabbing popup and asserts the selection actually moved, so it fails on a fix that merely swallows the key; against the old code it takes the process down with it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
4 hoursfix(completion): filter keys on the application, not the line editDanilo M.1-0/+4
Showing the popup takes focus away from the query bar and the popup window grabs the keyboard, so keys pressed while it is up are delivered to the popup. An event filter installed on the line edit therefore never ran at the one moment it had to, leaving Tab to move focus to the next widget and Return to reach the thread list and open a message. Install the key filter on the application instead, which sees events before any widget receives them. It returns immediately unless our own popup is visible, so it cannot affect keyboard handling elsewhere. The FocusIn filter stays on the line edit, where it is correctly scoped: it only fires with the popup down. Accepting a completion now also reopens the popup when the caret lands somewhere more can be offered, so taking "tag:" goes straight on to the tag list instead of needing a second complete_query. The chain stops on a stem that is already a complete candidate, which is what every accept produces. The mouse path chains identically. The previous tests passed against the broken code because they posted events straight to the line edit, bypassing the delivery path a real keypress takes. The new tests route keys through the active popup and run against a real X display; offscreen does not grab the keyboard and cannot reproduce this class of bug.
4 hoursfix(completion): drive the popup instead of QLineEdit::setCompleterDanilo M.1-0/+5
QLineEdit::setCompleter hands completion to the line edit, which then resets the completer's completionPrefix to the widget's entire text on every keystroke. The prefix has to be the stem, so once the query grew past its first token the whole-line prefix matched no candidate, the popup stopped appearing, and the two reported symptoms followed: nothing was there for Tab to accept, and Tab fell through to focus navigation. Attach the completer with setWidget instead, which keeps the popup anchored without ceding control of the prefix. complete() dereferences widget() unconditionally, so leaving it unset segfaults rather than degrading. Opening the popup then becomes ours to do on every edit. Extend the existing event filter to route the keys the popup needs while it is visible, and install it unconditionally now that it does more than the completion_on_focus case. Enter accepts a completion only while the popup is up, so returnPressed still runs the query when it is closed. The existing tests called acceptCompletion() directly and so never touched the widget, which is why neither bug was caught. Add four tests that drive the real widget path plus one covering both settings of completion_on_focus; the first two fail against the old code. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
4 hoursfeat(completion): wire query completion into the main windowDanilo M.1-0/+8
Adds the complete_query action, whose registration the assertion in registerActions() has been demanding since keymap gained the name: a known action nothing implements would have been a silently dead binding. Also lands the QueryCompleter half of the manual trigger, which the keymap commit left behind: triggerCompletion() and the focus-in filter gated on completion_on_focus. Tags refresh at startup, after a sync, and after a mutation introduces a tag not already known, since that is the tag most likely to be typed again. onAllTagsReady deliberately drops the generation the signal carries: a tag list is not an ordered query result, so a later one is always at least as good as an earlier one and discarding on staleness could only throw away a good list.
4 hoursfeat(completion): render the popup with descriptionsDanilo M.1-0/+20
The free-form date hint is a footer label rather than a model row: a row would be filtered away by the first non-matching keystroke and could be selected and inserted, producing a query that errors. QCompleter::setPopup takes a QAbstractItemView, so the label cannot be laid out beside the view in a container widget. The footer sits in space reserved with setViewportMargins inside the list view instead. setItemDelegate must run after setPopup, not before: setPopup installs a plain QStyledItemDelegate of its own and discards whatever was already set, which silently drops the description column. Accepting replaces exactly the span the tokenizer identified rather than QCompleter's own completion prefix, which is a different span once a prefix or a range bound is involved. Four tests drive that path directly instead of through synthetic key events, since whether a key needs Shift is a keyboard-layout property and could not decide the question. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
4 hoursfeat(completion): select candidates per contextDanilo M.1-0/+31
tag: and is: share the tag list, since notmuch aliases them. path: offers each account maildir in both bare and recursive forms, the latter being what Account::scopedQuery builds and not something a user would guess. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
4 hoursfeat(completion): add the prefix, date and mimetype vocabulariesDanilo M.1-0/+15
Keywords are syntax and stay untranslated; the descriptions beside them are prose and go through tr(). The tables are free functions with no QObject to inherit tr() from, so the file declares a VocabularyStrings context with Q_DECLARE_TR_FUNCTIONS rather than borrowing QObject::tr, which would file every string under the QObject context.
4 hoursfeat(completion): complete both bounds of a date rangeDanilo M.1-0/+7
Each side of '..' is an independent value against the same model. Entries that are themselves ranges are withheld once a range exists, since date:1week....today is malformed. The token is read to its full extent rather than truncated at the caret: the separator deciding which bound is being edited can sit to the right of the caret. Stems stay caret-bounded so matching never uses untyped text.
4 hoursfeat(completion): add the query cursor-context tokenizerDanilo M.1-0/+52
Prefix completion only so far: the token under the cursor, bounded by whitespace or an opening parenthesis rather than by the start of the line.