[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