[education/rkward] rkward: Add :::-completions (for non-loaded packages)

Thomas Friedrichsmeier null at kde.org
Wed Oct 5 16:33:03 BST 2022


Git commit 9888db34b7d05c6d6ae9a59f255df699f7c148c0 by Thomas Friedrichsmeier.
Committed on 03/10/2022 at 16:32.
Pushed by tfry into branch 'master'.

Add :::-completions (for non-loaded packages)

This still has some quirks. Importantly, completions are generated, but for some reason, they do not show, automatically.

Also cleanups still needed.

M  +1    -0    rkward/CMakeLists.txt
A  +47   -0    rkward/rbackend/rpackages/rkward/R/rk.completion-functions.R
M  +98   -6    rkward/windows/rkcodecompletion.cpp
M  +30   -7    rkward/windows/rkcodecompletion.h

https://invent.kde.org/education/rkward/commit/9888db34b7d05c6d6ae9a59f255df699f7c148c0

diff --git a/rkward/CMakeLists.txt b/rkward/CMakeLists.txt
index 0c879fb5..fbfc4d7c 100644
--- a/rkward/CMakeLists.txt
+++ b/rkward/CMakeLists.txt
@@ -81,6 +81,7 @@ IF(NOT WIN32)
 	# This is for running directly from the build tree. Not on windows for now (until cmake supports symlinks, there)
 	ADD_CUSTOM_TARGET(local_install ALL
 		COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/rkwardinstall
+		COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/icons ${CMAKE_CURRENT_BINARY_DIR}/rkwardinstall/icons
 		COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/pages ${CMAKE_CURRENT_BINARY_DIR}/rkwardinstall/pages
 		COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/plugins ${CMAKE_CURRENT_BINARY_DIR}/rkwardinstall/plugins
 		COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/resource.ver ${CMAKE_CURRENT_BINARY_DIR}/rkwardinstall/resource.ver
diff --git a/rkward/rbackend/rpackages/rkward/R/rk.completion-functions.R b/rkward/rbackend/rpackages/rkward/R/rk.completion-functions.R
new file mode 100644
index 00000000..f401a376
--- /dev/null
+++ b/rkward/rbackend/rpackages/rkward/R/rk.completion-functions.R
@@ -0,0 +1,47 @@
+#' Helper functions for querying completions
+#' 
+#' These are currently not exported, as they are not intended to be use in user code
+#' The API may or may not be stable.
+#'
+#' @rdname rk.get.completions
+#'
+".rk.completions" <- function(fragment, type) {
+#TODO: factor out all the common code
+	oldrcs <- utils::rc.settings()
+	oldrcopts <- utils::rc.options()
+	on.exit({do.call(utils::rc.settings, as.list(oldrcs)); utils::rc.options(oldrcopts)})
+	if (type == "funargs") {
+		utils::rc.settings(ops=FALSE, ns=FALSE, args=TRUE, dots=FALSE, func=FALSE, ipck=FALSE, S3=TRUE, data=FALSE, help=FALSE, argdb=TRUE, fuzzy=FALSE, quotes=FALSE, files=FALSE)
+		utils::rc.options(funarg.suffix="")
+		utils:::.assignLinebuffer(paste0(fragment, "("))
+		utils:::.assignToken("")
+		utils:::.assignStart(nchar(fragment)+1)
+		utils:::.assignEnd(nchar(fragment)+1)
+		utils:::.completeToken()
+		utils:::.retrieveCompletions()
+	} else if (type == "$" || type == "@") {
+		utils::rc.settings(ops = TRUE, ns=FALSE, args = FALSE, dots = FALSE, func = FALSE, ipck = FALSE, S3 = FALSE, data = FALSE, help = FALSE, argdb = FALSE, fuzzy = FALSE, quotes = FALSE, files = FALSE)
+		utils:::.assignLinebuffer(paste0(fragment, type))
+		utils:::.assignToken(paste0(fragment, type))
+		utils:::.assignStart(1)
+		utils:::.assignEnd(nchar(fragment) + nchar(type))
+		utils:::.completeToken()
+		sub(paste0("^", fragment, ifelse(type == "$", "\\$", type)),"", utils:::.retrieveCompletions())
+	} else if (type == "::" || type == ":::") {
+		utils::rc.settings(ops = FALSE, ns=TRUE, args = FALSE, dots = FALSE, func = FALSE, ipck = FALSE, S3 = FALSE, data = FALSE, help = FALSE, argdb = FALSE, fuzzy = FALSE, quotes = FALSE, files = FALSE)
+		utils:::.assignLinebuffer(paste0(fragment, type))
+		utils:::.assignToken(paste0(fragment, type))
+		utils:::.assignStart(1)
+		utils:::.assignEnd(nchar(fragment) + nchar(type))
+		utils:::.completeToken()
+		sub(paste0("^", fragment, type),"", utils:::.retrieveCompletions())
+	} else if (type == "?") {
+		utils::rc.settings(ops = FALSE, ns=FALSE, args = FALSE, dots = FALSE, func = FALSE, ipck = FALSE, S3 = FALSE, data = FALSE, help = TRUE, argdb = FALSE, fuzzy = FALSE, quotes = FALSE, files = FALSE)
+		utils:::.assignLinebuffer(paste0(type, fragment))
+		utils:::.assignToken(paste0(type, fragment))
+		utils:::.assignStart(1)
+		utils:::.assignEnd(nchar(fragment) + nchar(type))
+		utils:::.completeToken()
+		sub("^\\?","", utils:::.retrieveCompletions())
+	}
+}
diff --git a/rkward/windows/rkcodecompletion.cpp b/rkward/windows/rkcodecompletion.cpp
index c1823505..fffbe3ab 100644
--- a/rkward/windows/rkcodecompletion.cpp
+++ b/rkward/windows/rkcodecompletion.cpp
@@ -159,6 +159,7 @@ void RKCompletionManager::tryCompletion () {
 	int end;
 	RKCommonFunctions::getCurrentSymbolOffset (current_line, cursor_pos-1, false, &start, &end);
 	symbol_range = KTextEditor::Range (para, start, para, end);
+	bool is_help = (start >= 1) && (current_line.at(start-1) == '?');
 	if (!user_triggered) {
 		if (end > cursor_pos) {
 			symbol_range = KTextEditor::Range ();   // Only hint when at the end of a word/symbol: https://mail.kde.org/pipermail/rkward-devel/2015-April/004122.html
@@ -177,11 +178,11 @@ void RKCompletionManager::tryCompletion () {
 			word.clear ();
 		}
 
-		completion_model->updateCompletionList (word);
-		file_completion_model->updateCompletionList (filename);
+		completion_model->updateCompletionList(word, is_help);
+		file_completion_model->updateCompletionList(filename);
 	} else {
-		completion_model->updateCompletionList (QString ());
-		file_completion_model->updateCompletionList (QString ());
+		completion_model->updateCompletionList(QString(), false);
+		file_completion_model->updateCompletionList(QString());
 	}
 	RK_DEBUG(EDITOR, DL_DEBUG, "completion symbol range %d, %d -> %d, %d", symbol_range.start().line(), symbol_range.start().column(), symbol_range.end().line(), symbol_range.end().column());
 
@@ -514,13 +515,15 @@ void RKCompletionModelBase::executeCompletionItem (KTextEditor::View *view, cons
 
 RKCodeCompletionModel::RKCodeCompletionModel (RKCompletionManager *manager) : RKCompletionModelBase (manager) {
 	RK_TRACE (COMMANDEDITOR);
+	rcompletions = new RKDynamicCompletionsAddition(this);
+	connect(rcompletions, &RKDynamicCompletionsAddition::resultsComplete, this, &RKCodeCompletionModel::addRCompletions);
 }
 
 RKCodeCompletionModel::~RKCodeCompletionModel () {
 	RK_TRACE (COMMANDEDITOR);
 }
 
-void RKCodeCompletionModel::updateCompletionList (const QString& symbol) {
+void RKCodeCompletionModel::updateCompletionList(const QString& symbol, bool is_help) {
 	RK_TRACE (COMMANDEDITOR);
 
 	if (current_symbol == symbol) return;	// already up to date
@@ -542,11 +545,33 @@ void RKCodeCompletionModel::updateCompletionList (const QString& symbol) {
 		icons.append (RKStandardIcons::iconForObject (matches[i]));
 	}
 
+	if ((objectpath.size() == 2 || objectpath.size() == 3) && objectpath.at(1) == ":::") {
+		QStringList shortnames;
+		for (int i = 0; i < matches.count(); ++i) {
+			shortnames.append(matches[i]->getShortName());
+		}
+		rcompletions->update(":::", objectpath.at(0), objectpath.value(2), shortnames);
+	}
+
 	current_symbol = symbol;
 
 	endResetModel ();
 }
 
+void RKCodeCompletionModel::addRCompletions() {
+	RK_TRACE (COMMANDEDITOR);
+
+	QStringList addlist = rcompletions->results();
+	if (addlist.isEmpty()) return;
+	beginInsertRows(index(0, 0), n_completions, n_completions + addlist.size());
+	n_completions += addlist.size();
+	for (int i = 0; i < addlist.size(); ++i) {
+		names.append(rcompletions->fragment() + rcompletions->mode() + addlist.at(i));
+		icons.append(RKStandardIcons::getIcon(RKStandardIcons::WindowConsole));
+	}
+	endInsertRows();
+}
+
 KTextEditor::Range RKCodeCompletionModel::completionRange (KTextEditor::View *, const KTextEditor::Cursor&) {
 	return manager->currentSymbolRange ();
 }
@@ -770,7 +795,7 @@ void RKArgumentHintModel::fetchRCompletions() {
 	RK_TRACE(COMMANDEDITOR);
 
 	if (r_completions_function != nullptr) {
-		// an old (now obsolete) query is still running. Wait for it to complete, first, avoiding to stack up (pontentially costly) calls
+		// an old (now obsolete) query is still running. Wait for it to complete, first, avoiding to stack up (potentially costly) calls
 		return;
 	}
 
@@ -946,3 +971,70 @@ QString RKFileCompletionModel::partialCompletion (bool* exact) {
 
 	return (findCommonCompletion (names, current_fragment, exact));
 }
+
+
+
+RKDynamicCompletionsAddition::RKDynamicCompletionsAddition(RKCodeCompletionModel *parent) : QObject(parent) {
+	RK_TRACE(COMMANDEDITOR);
+	status = Ready;
+}
+
+RKDynamicCompletionsAddition::~RKDynamicCompletionsAddition() {
+	RK_TRACE(COMMANDEDITOR);
+}
+
+void RKDynamicCompletionsAddition::update(const QString &mode, const QString &fragment, const QString &filterprefix, const QStringList &filterlist) {
+	RK_TRACE(COMMANDEDITOR);
+
+	if ((mode != current_mode) || (fragment != current_fragment)) {
+		current_mode = mode;
+		current_fragment = fragment;
+		doUpdateFromR();
+	}
+	if (filterprefix != current_filterprefix || filterlist != current_filterlist) {
+		current_filterprefix = filterprefix;
+		current_filterlist = filterlist;
+		if (status == Ready) filterResults(); // no update was triggered, above, need to update filter, manually
+	}
+	if (status == Ready) emit resultsComplete();
+}
+
+void RKDynamicCompletionsAddition::doUpdateFromR() {
+	RK_TRACE(COMMANDEDITOR);
+	if (status != Ready) {
+		status = PendingUpdate;
+		return;
+	}
+
+	status = Updating;
+	RCommand *command = new RCommand(QString("rkward:::.rk.completions(%1, \"%2\")").arg(RObject::rQuote(current_fragment), current_mode), RCommand::Sync | RCommand::PriorityCommand | RCommand::GetStringVector);
+	command->whenFinished(this, [this](RCommand *command) {
+		if (status == PendingUpdate) {
+			QTimer::singleShot(0, this, &RKDynamicCompletionsAddition::doUpdateFromR);
+			return;
+		}
+		if (command->getDataType() == RCommand::StringVector) {
+			QStringList nargs;
+			current_raw_resultlist = command->stringVector();
+		} else {
+			RK_ASSERT(false);
+		}
+		filterResults();
+		status = Ready;
+		emit resultsComplete();
+	});
+	RInterface::issueCommand(command);
+}
+
+void RKDynamicCompletionsAddition::filterResults() {
+	RK_TRACE(COMMANDEDITOR);
+
+	QStringList res;
+	for (int i = 0; i < current_raw_resultlist.size(); ++i) {
+		const auto item = current_raw_resultlist.at(i);
+		if (!item.startsWith(current_filterprefix)) continue;
+		if (current_filterlist.contains(item)) continue;
+		res.append(item);
+	}
+	filtered_results = res;
+}
diff --git a/rkward/windows/rkcodecompletion.h b/rkward/windows/rkcodecompletion.h
index d67a30d0..3b62f837 100644
--- a/rkward/windows/rkcodecompletion.h
+++ b/rkward/windows/rkcodecompletion.h
@@ -105,6 +105,7 @@ private:
 	};
 };
 
+class RKDynamicCompletionsAddition;
 class RKCodeCompletionModel : public RKCompletionModelBase {
 	Q_OBJECT
 public:
@@ -113,13 +114,18 @@ public:
 
 	KTextEditor::Range completionRange (KTextEditor::View *view, const KTextEditor::Cursor &position) override;
 
-	void updateCompletionList (const QString& symbol);
+	void updateCompletionList(const QString& symbol, bool is_help);
 	QVariant data (const QModelIndex& index, int role=Qt::DisplayRole) const override;
 	QString partialCompletion (bool* exact_match);
 private:
 	QList<QIcon> icons;
 	QStringList names;
 	QString current_symbol;
+	void fetchRCompletions();
+	QString r_base_symbol;
+	bool is_help;
+	RKDynamicCompletionsAddition *rcompletions;
+	void addRCompletions();
 };
 
 class RObject;
@@ -190,14 +196,31 @@ private:
 	RKFileCompletionModelWorker *worker;
 };
 
-/*
-class RKRDynamicCompletionModel : public KTextEditor::CodeCompletionModel, public KTextEditor::CodeCompletionModelControllerInterface {
+class RKDynamicCompletionsAddition : public QObject {
 	Q_OBJECT
 public:
-	RKRDynamicCompletionModel(RKCompletionManager *manager);
-	void setFunction(RObject *function);
+	RKDynamicCompletionsAddition(RKCodeCompletionModel *parent);
+	~RKDynamicCompletionsAddition();
+	void update(const QString &mode, const QString &fragment, const QString &filterprefix, const QStringList &filterlist);
+	const QStringList results() const { return filtered_results; };
+	const QString fragment() const { return current_fragment; };
+	const QString mode() const { return current_mode; };
+signals:
+	void resultsComplete();
 private:
-	bool waiting_for_reply;
-}; */
+	void doUpdateFromR();
+	void filterResults();
+	QString current_mode;
+	QString current_fragment;
+	QString current_filterprefix;
+	QStringList current_filterlist;
+	QStringList current_raw_resultlist;
+	QStringList filtered_results;
+	enum {
+		Ready,
+		Updating,
+		PendingUpdate
+	} status;
+};
 
 #endif



More information about the rkward-tracker mailing list