[rkward/work/unified_hinting2] rkward/windows: Add support for partial tab-completion.
Thomas Friedrichsmeier
null at kde.org
Sun Feb 17 15:29:52 GMT 2019
Git commit fbfc3f3372e83e2d6cf3d3b15fa399c3113179fe by Thomas Friedrichsmeier.
Committed on 17/02/2019 at 15:29.
Pushed by tfry into branch 'work/unified_hinting2'.
Add support for partial tab-completion.
M +107 -40 rkward/windows/rkcommandeditorwindow.cpp
M +4 -2 rkward/windows/rkcommandeditorwindow.h
https://commits.kde.org/rkward/fbfc3f3372e83e2d6cf3d3b15fa399c3113179fe
diff --git a/rkward/windows/rkcommandeditorwindow.cpp b/rkward/windows/rkcommandeditorwindow.cpp
index 71c6a7ae..20f40d28 100644
--- a/rkward/windows/rkcommandeditorwindow.cpp
+++ b/rkward/windows/rkcommandeditorwindow.cpp
@@ -1207,12 +1207,17 @@ RKCompletionManager::RKCompletionManager (KTextEditor::View* view) : QObject (vi
connect (view->document (), &KTextEditor::Document::lineWrapped, this, &RKCompletionManager::lineWrapped);
connect (view->document (), &KTextEditor::Document::lineUnwrapped, this, &RKCompletionManager::lineUnwrapped);
connect (view, &KTextEditor::View::cursorPositionChanged, this, &RKCompletionManager::cursorPositionChanged);
+ const QObjectList children = _view->children ();
+ for (QObjectList::const_iterator it = children.constBegin(); it != children.constEnd (); ++it) {
+ (*it)->installEventFilter (this); // to handle Tab-key; installing on the view, alone, is not enough.
+ }
// HACK: I just can't see to make the object name completion model play nice with automatic invocation.
// However, there is no official way to invoke all registered models, manually. So we try to hack our way
// to a pointer to the default kate keyword completion model
kate_keyword_completion_model = KTextEditor::Editor::instance ()->findChild<KTextEditor::CodeCompletionModel *> ();
if (!kate_keyword_completion_model) kate_keyword_completion_model = view->findChild<KTextEditor::CodeCompletionModel *> (QString());
+
} else {
RK_ASSERT (false); // Not a katepart?
}
@@ -1434,43 +1439,43 @@ KTextEditor::Range RKCompletionManager::currentCallRange () const {
return KTextEditor::Range (call_opening, _view->cursorPosition ());
}
-void RKCompletionManager::completeToNextUnambigous () {
- RK_TRACE (COMMANDEDITOR);
+bool RKCompletionManager::eventFilter (QObject* watched, QEvent* event) {
+ if (event->type () == QEvent::KeyPress || event->type () == QEvent::ShortcutOverride) {
+ RK_TRACE (COMMANDEDITOR); // avoid loads of empty traces, putting this here
+ QKeyEvent *k = static_cast<QKeyEvent *> (event);
+
+ if (k->key () == Qt::Key_Tab && (!k->modifiers ())) {
+ // TODO: It is not quite clear, what behavior is desirable, in case more than one completion model is active at a time.
+ // For now, we use the simplest solution (implemenation-wise), and complete from the topmost-model, only
+ // TODO: Handle the ktexteditor builtin models, too.
+ bool exact = false;
+ QString comp;
+ bool handled = false;
+ if (active_models.contains (arghint_model)) {
+ comp = arghint_model->partialCompletion (&exact);
+ handled = true;
+ } else if (active_models.contains (completion_model)) {
+ comp = completion_model->partialCompletion (&exact);
+ handled = true;
+ } else if (active_models.contains (file_completion_model)) {
+ comp = file_completion_model->partialCompletion (&exact);
+ handled = true;
+ }
- /*
-
- // do all entries have a common start?
- QString common;
- bool done = false;
- int i = 0;
- while (!done) {
- bool ok = true;
- QChar current;
- for (it = entries.constBegin (); it != entries.constEnd (); ++it) {
- if ((*it).length () <= i) {
- ok = false;
- break;
+ if (handled) {
+ RK_DEBUG(COMMANDEDITOR, DL_DEBUG, "Tab completion: %s", qPrintable (comp));
+ view ()->document ()->insertText (view ()->cursorPosition (), comp);
+ if (exact) cc_iface->abortCompletion ();
+ else if (comp.isEmpty ()) {
+ QApplication::beep (); // TODO: unfortunately, we catch *two* tab events, so this is not good, yet
}
- if (it == entries.constBegin ()) {
- current = (*it).at(i);
- } else if ((*it).at(i) != current) {
- ok = false;
- break;
- }
- }
- if (ok) common.append (current);
- else break;
- ++i;
- }
- if (i > 0) {
- if ((int) common.length() > (word_end - word_start)) { // more than there already is
- insertCompletion (line_num, word_start, word_end, common);
- return false; // will beep to signal completion is not complete
+ return true;
}
}
-*/
-}
+ }
+ return false;
+}
//////////////////////// RKCompletionModelBase ////////////////////
@@ -1545,17 +1550,10 @@ void RKCodeCompletionModel::updateCompletionList (const QString& symbol) {
endResetModel ();
}
-KTextEditor::Range RKCodeCompletionModel::completionRange (KTextEditor::View *, const KTextEditor::Cursor&) {
+KTextEditor::Range RKCodeCompletionModel::completionRange (KTextEditor::View *, const KTextEditor::Cursor&c) {
return manager->currentSymbolRange ();
}
-void RKCodeCompletionModel::executeCompletionItem (KTextEditor::View *view, const KTextEditor::Range &word, const QModelIndex &index) const {
- RK_TRACE (COMMANDEDITOR);
-
- RK_ASSERT (names.size () > index.row ());
- view->document ()->replaceText (word, names.value (index.row ()));
-}
-
QVariant RKCodeCompletionModel::data (const QModelIndex& index, int role) const {
if (isHeaderItem (index)) {
if (role == Qt::DisplayRole) return i18n ("Objects on search path");
@@ -1584,6 +1582,62 @@ QVariant RKCodeCompletionModel::data (const QModelIndex& index, int role) const
return QVariant ();
}
+QString findCommonCompletion (const QStringList &list, const QString &lead, bool *exact_match) {
+ RK_TRACE (COMMANDEDITOR);
+ RK_DEBUG(COMMANDEDITOR, DL_DEBUG, "Looking for commong completion among set of %d, starting with %s", list.size (), qPrintable (lead));
+
+ *exact_match = true;
+ QString ret;
+ bool first = true;
+ int lead_size = lead.count ();
+ for (int i = list.size () - 1; i >= 0; --i) {
+ if (!list[i].startsWith (lead)) continue;
+
+ QString candidate = list[i].mid (lead_size);
+ if (first) {
+ ret = candidate;
+ first = false;
+ } else {
+ if (ret.length () > candidate.length ()) {
+ ret = ret.left (candidate.length ());
+ *exact_match = false;
+ }
+
+ for (int c = 0; c < ret.length(); ++c) {
+ if (ret[c] != candidate[c]) {
+ *exact_match = false;
+ if (!c) return QString ();
+
+ ret = ret.left (c);
+ break;
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+QString RKCodeCompletionModel::partialCompletion (bool* exact_match) {
+ RK_TRACE (COMMANDEDITOR);
+
+ // Here, we need to do completion on the *last* portion of the object-path, only, so we will be able to complete "obj" to "object", even if "object" is present as
+ // both packageA::object and packageB::object.
+ // Thus as a first step, we look up the short names. We do this, lazily, as this function is only called on demand.
+ QStringList objectpath = RObject::parseObjectPath (current_symbol);
+ if (objectpath.isEmpty () || objectpath[0].isEmpty ()) return (QString ());
+ RObject::ObjectList matches = RObjectList::getObjectList ()->findObjectsMatching (current_symbol);
+
+ QStringList shortnames;
+ for (int i = 0; i < matches.count (); ++i) {
+ shortnames.append (matches[i]->getShortName ());
+ }
+ QString lead = objectpath.last ();
+ if (!shortnames.value (0).startsWith (lead)) lead.clear (); // This could happen if the current path ends with '$', for instance
+
+ return (findCommonCompletion (shortnames, lead, exact_match));
+}
+
//////////////////////// RKCallHintModel //////////////////////////
RKCallHintModel::RKCallHintModel (RKCompletionManager* manager) : RKCompletionModelBase (manager) {
RK_TRACE (COMMANDEDITOR);
@@ -1739,6 +1793,12 @@ KTextEditor::Range RKArgumentHintModel::completionRange (KTextEditor::View*, con
return manager->currentArgnameRange ();
}
+QString RKArgumentHintModel::partialCompletion (bool* exact) {
+ RK_TRACE (COMMANDEDITOR);
+
+ return (findCommonCompletion (args, fragment, exact));
+}
+
//////////////////////// RKFileCompletionModel ////////////////////
#include <KUrlCompletion>
RKFileCompletionModelWorker::RKFileCompletionModelWorker (const QString &_string) : QThread () {
@@ -1833,6 +1893,13 @@ QVariant RKFileCompletionModel::data (const QModelIndex& index, int role) const
return QVariant ();
}
+QString RKFileCompletionModel::partialCompletion (bool* exact) {
+ RK_TRACE (COMMANDEDITOR);
+
+ return (findCommonCompletion (names, current_fragment, exact));
+}
+
+
// static
KTextEditor::Document* RKCommandHighlighter::_doc = 0;
KTextEditor::View* RKCommandHighlighter::_view = 0;
diff --git a/rkward/windows/rkcommandeditorwindow.h b/rkward/windows/rkcommandeditorwindow.h
index b14a2df4..18b9f400 100644
--- a/rkward/windows/rkcommandeditorwindow.h
+++ b/rkward/windows/rkcommandeditorwindow.h
@@ -128,11 +128,11 @@ private slots:
/** show a code completion box if appropriate. Use tryCompletionProxy () instead, which will call this function after a timeout */
void tryCompletion ();
private:
+ bool eventFilter (QObject *watched, QEvent *event) override;
/** called whenever it might be appropriate to show a code completion box. The box is not shown immediately, but only after a timeout (if at all) */
void tryCompletionProxy ();
void updateVisibility ();
void updateCallHint ();
- void completeToNextUnambigous ();
KTextEditor::CodeCompletionInterface *cc_iface;
RKCodeCompletionModel *completion_model;
RKFileCompletionModel *file_completion_model;
@@ -190,8 +190,8 @@ public:
KTextEditor::Range completionRange (KTextEditor::View *view, const KTextEditor::Cursor &position) override;
void updateCompletionList (const QString& symbol);
- void executeCompletionItem (KTextEditor::View *view, const KTextEditor::Range &word, const QModelIndex &index) const override;
QVariant data (const QModelIndex& index, int role=Qt::DisplayRole) const override;
+ QString partialCompletion (bool* exact_match);
private:
QList<QIcon> icons;
QStringList names;
@@ -222,6 +222,7 @@ public:
QVariant data (const QModelIndex& index, int role=Qt::DisplayRole) const override;
KTextEditor::Range completionRange (KTextEditor::View *view, const KTextEditor::Cursor &position) override;
+ QString partialCompletion (bool *exact);
private:
RObject *function;
QStringList args;
@@ -250,6 +251,7 @@ public:
void updateCompletionList (const QString& fragment);
QVariant data (const QModelIndex& index, int role=Qt::DisplayRole) const override;
+ QString partialCompletion (bool *exact);
private slots:
void completionsReady (const QString &string, const QStringList &exes, const QStringList &files);
private:
More information about the rkward-tracker
mailing list