[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