[education/rkward] rkward: Implement selection of chunks

Thomas Friedrichsmeier null at kde.org
Mon May 26 15:06:38 BST 2025


Git commit 907570e1ca43eff0959c8a2bca10f36d5e9fc36f by Thomas Friedrichsmeier.
Committed on 26/05/2025 at 14:06.
Pushed by tfry into branch 'master'.

Implement selection of chunks

M  +6    -0    rkward/autotests/rkparsedscript_test.cpp
M  +32   -1    rkward/misc/rkparsedscript.cpp
M  +2    -0    rkward/misc/rkparsedscript.h
M  +10   -0    rkward/windows/rkcommandeditorwindow.cpp

https://invent.kde.org/education/rkward/-/commit/907570e1ca43eff0959c8a2bca10f36d5e9fc36f

diff --git a/rkward/autotests/rkparsedscript_test.cpp b/rkward/autotests/rkparsedscript_test.cpp
index 47ccb57bf..876c61775 100644
--- a/rkward/autotests/rkparsedscript_test.cpp
+++ b/rkward/autotests/rkparsedscript_test.cpp
@@ -84,6 +84,7 @@ class RKParsedScriptTest : public QObject {
 			while (ctx.valid())
 				ctx = ps.nextStatement(ctx);
 			ctx = ps.firstContextInStatement(ctx0); // NOTE: This one may stay at the same position
+			ctx = ps.firstContextInChunk(ctx0);
 			ctx = ctx0;
 			while (ctx.valid())
 				ctx = ps.nextOuter(ctx);
@@ -110,6 +111,7 @@ class RKParsedScriptTest : public QObject {
 			while (ctx.valid())
 				ctx = ps.prevStatement(ctx);
 			ctx = ps.lastContextInStatement(ctx0); // May stay in same position
+			ps.lastPositionInChunk(ctx0);
 			ctx = ctx0;
 			while (ctx.valid())
 				ctx = ps.prevOuter(ctx);
@@ -302,6 +304,10 @@ class RKParsedScriptTest : public QObject {
 		ctx = moveAndCheck(ps.prevStatement(ctx), u"symb11"_s);
 		QVERIFY(!ps.prevStatement(ctx).valid());
 
+		ctx = ps.contextAtPos(script.indexOf(u"symb14"));
+		ctx = moveAndCheck(ps.firstContextInChunk(ctx), u"symb11"_s);
+		QCOMPARE(script.mid(ps.lastPositionInChunk(ctx) - 6, 6), u"symb17"); // NOTE: inclusion or not of final newline is not critical
+
 		ctx = ps.contextAtPos(script.indexOf(u"This is markdown"));
 		ctx = moveAndCheck(ps.nextCodeChunk(ctx), u".some"_s);
 
diff --git a/rkward/misc/rkparsedscript.cpp b/rkward/misc/rkparsedscript.cpp
index 521a6f546..83f3de328 100644
--- a/rkward/misc/rkparsedscript.cpp
+++ b/rkward/misc/rkparsedscript.cpp
@@ -357,7 +357,7 @@ RKParsedScript::ContextIndex RKParsedScript::prevCodeChunk(const ContextIndex fr
 	if (!from.valid()) return ContextIndex();
 
 	// For general logic, see nextCodeChunk(), above. We need to reverse across two chunk starts, here
-	// If we are *inside* a code chunk, we need to reverse to starts, else just one.
+	// If we are *inside* a code chunk, we need to reverse two starts, else just one.
 	int chunkstarts = 1;
 	auto parent = from;
 	while (parent.valid()) {
@@ -386,6 +386,37 @@ RKParsedScript::ContextIndex RKParsedScript::prevCodeChunk(const ContextIndex fr
 	return ContextIndex();
 }
 
+RKParsedScript::ContextIndex RKParsedScript::firstContextInChunk(const ContextIndex from) const {
+	RK_TRACE(MISC);
+	auto parent = from;
+	while (parent.valid()) {
+		parent = parentRegion(parent);
+		if (getContext(parent).type == Top) {
+			break;
+		}
+	}
+	if (!parent.valid()) return ContextIndex();
+	int i = parent.index;
+	do {
+		++i;
+	} while (i < context_list.size() && context_list.at(i).type == Delimiter);
+	if (i >= context_list.size()) return ContextIndex();
+	return ContextIndex(i);
+}
+
+int RKParsedScript::lastPositionInChunk(const ContextIndex from) const {
+	RK_TRACE(MISC);
+	auto ctx = from;
+	while (ctx.valid()) {
+		auto nctx = nextContext(ctx);
+		if (getContext(nctx).type == Top) {
+			break;
+		}
+		ctx = nctx;
+	}
+	return getContext(ctx).end;
+}
+
 RKParsedScript::ContextIndex RKParsedScript::nextToplevel(const ContextIndex from) const {
 	RK_TRACE(MISC);
 	auto ctx = from;
diff --git a/rkward/misc/rkparsedscript.h b/rkward/misc/rkparsedscript.h
index 4d1262690..800f05bc2 100644
--- a/rkward/misc/rkparsedscript.h
+++ b/rkward/misc/rkparsedscript.h
@@ -110,6 +110,8 @@ class RKParsedScript {
 	/** Next code Region in R Markdown document */
 	ContextIndex nextCodeChunk(const ContextIndex from) const;
 	ContextIndex prevCodeChunk(const ContextIndex from) const;
+	ContextIndex firstContextInChunk(const ContextIndex from) const;
+	int lastPositionInChunk(const ContextIndex from) const;
 
 	/** retrieve the context at the given index. Safe to call, even with an invalid index
 	 *  (in which case the outermost context will be returned). */
diff --git a/rkward/windows/rkcommandeditorwindow.cpp b/rkward/windows/rkcommandeditorwindow.cpp
index d612db000..e161dacd1 100644
--- a/rkward/windows/rkcommandeditorwindow.cpp
+++ b/rkward/windows/rkcommandeditorwindow.cpp
@@ -196,6 +196,16 @@ class RKCodeNavigation : public QWidget {
 			auto posa = ps.getContext(ps.firstContextInStatement(ci)).start;
 			auto posb = ps.lastPositionInStatement(ci);
 			newpos.selection = KTextEditor::Range(positionToCursor(posa), positionToCursor(posb + 1));
+		} else if (command == u'S') {
+			if (!rmdmode) {
+				message->setText(i18n("Command 'S' is for R Markdown, only"));
+				message->show();
+				updatePos();
+				return;
+			}
+			auto posa = ps.getContext(ps.firstContextInChunk(ci)).start;
+			auto posb = ps.lastPositionInChunk(ci);
+			newpos.selection = KTextEditor::Range(positionToCursor(posa), positionToCursor(posb + 1));
 		} else {
 			RK_DEBUG(COMMANDEDITOR, DL_WARNING, "unknown navigation commmand");
 			message->setText(i18n("Unknown command '%1'").arg(command));



More information about the rkward-tracker mailing list