[education/rkward] rkward: Fix Rmd navigation
Thomas Friedrichsmeier
null at kde.org
Mon May 26 15:06:38 BST 2025
Git commit d420dc20af698c82af69cda2756298352af6ced3 by Thomas Friedrichsmeier.
Committed on 26/05/2025 at 14:06.
Pushed by tfry into branch 'master'.
Fix Rmd navigation
M +1 -1 rkward/autotests/data/script1.Rmd
M +23 -3 rkward/autotests/rkparsedscript_test.cpp
M +26 -21 rkward/misc/rkparsedscript.cpp
https://invent.kde.org/education/rkward/-/commit/d420dc20af698c82af69cda2756298352af6ced3
diff --git a/rkward/autotests/data/script1.Rmd b/rkward/autotests/data/script1.Rmd
index 370d57432..f85836835 100644
--- a/rkward/autotests/data/script1.Rmd
+++ b/rkward/autotests/data/script1.Rmd
@@ -5,7 +5,7 @@ SPDX-FileContributor: The RKWard Team <rkward-devel at kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
-->
-This is markdown ``{r} inline <- `markdown` `` and this is too, with some `inline(code)`.
+This is markdown ``{r} .some <- `markdown` `` and this is too, with some `inline(code)`.
Usually, there would be lot of mardown (and this may contain many relevant markers, such as {}, and []).
diff --git a/rkward/autotests/rkparsedscript_test.cpp b/rkward/autotests/rkparsedscript_test.cpp
index 80cdf823f..d9f0e0a15 100644
--- a/rkward/autotests/rkparsedscript_test.cpp
+++ b/rkward/autotests/rkparsedscript_test.cpp
@@ -62,7 +62,6 @@ class RKParsedScriptTest : public QObject {
void sanityTestHelper() {
for (int startpos = 0; startpos < script.length(); ++startpos) {
const auto ctx0 = ps.contextAtPos(startpos);
- testLog("%d", startpos);
auto ctx = ctx0;
while (ctx.valid())
ctx = ps.nextContext(ctx);
@@ -253,8 +252,29 @@ class RKParsedScriptTest : public QObject {
void rmdTest() {
loadScript(u"script1.Rmd"_s, true);
sanityTestHelper();
- testLog("%s", qPrintable(ps.serialize()));
- // TODO real test
+ auto ctx = ps.contextAtPos(script.indexOf(u".some"));
+ QVERIFY(!ps.nextStatement(ctx).valid());
+
+ ctx = ps.contextAtPos(script.indexOf(u"inline"));
+ ctx = moveAndCheck(ps.nextStatementOrInner(ctx), u"code)"_s);
+ QVERIFY(!ps.nextStatement(ctx).valid());
+
+ ctx = ps.contextAtPos(script.indexOf(u"code)"));
+ ctx = moveAndCheck(ps.prevStatement(ctx), u"inline"_s);
+ QVERIFY(!ps.prevStatement(ctx).valid());
+
+ ctx = ps.contextAtPos(script.indexOf(u"symb01"));
+ ctx = moveAndCheck(ps.nextStatement(ctx), u"symb03"_s);
+ ctx = moveAndCheck(ps.nextStatement(ctx), u"symb05"_s);
+ ctx = moveAndCheck(ps.nextStatement(ctx), u"symb07"_s);
+ QVERIFY(!ps.nextStatement(ctx).valid());
+
+ ctx = ps.contextAtPos(script.indexOf(u"symb14"));
+ ctx = moveAndCheck(ps.nextStatement(ctx), u"symb15"_s);
+ ctx = moveAndCheck(ps.prevStatement(ctx), u"symb13"_s);
+ ctx = moveAndCheck(ps.prevStatement(ctx), u"symb11"_s);
+ QVERIFY(!ps.prevStatement(ctx).valid());
+ //testLog("%s", qPrintable(ps.serialize()));
}
};
diff --git a/rkward/misc/rkparsedscript.cpp b/rkward/misc/rkparsedscript.cpp
index 9e1446a4d..a744b742e 100644
--- a/rkward/misc/rkparsedscript.cpp
+++ b/rkward/misc/rkparsedscript.cpp
@@ -63,10 +63,10 @@ int RKParsedScript::addNextMarkdownChunk(int start, const QString &content) {
if (chunkend < 0) chunkend = content.size();
context_list.emplace_back(Comment, start, chunkstart-1);
- addContext(Delimiter, chunkstart-1, content);
- addContext(Top, chunkstart, content.left(chunkend)); // in case mardown region has incomplete syntax
+ prevtype = Comment; // causes insertion of a delimiter in the following addContext
+ addContext(Top, chunkstart-1, content.left(chunkend)); // in case markdown region has incomplete syntax
// limit parsing to the actual markdown region
- addContext(Delimiter, chunkend, content);
+ context_list.emplace_back(Top, chunkend, chunkend); // HACK: Used as a dummy separtor, here...
return chunkend + chunk_barrier.length();
}
@@ -167,6 +167,7 @@ RKParsedScript::ContextIndex RKParsedScript::contextAtPos(int pos) const {
RKParsedScript::ContextIndex RKParsedScript::nextContext(const ContextIndex from) const {
if (!from.valid()) return ContextIndex();
+ if (context_list.at(from.index).type == Top) return ContextIndex();
int i = from.index + 1;
if (i >= (int)context_list.size()) i = -1;
return ContextIndex(i);
@@ -174,6 +175,7 @@ RKParsedScript::ContextIndex RKParsedScript::nextContext(const ContextIndex from
RKParsedScript::ContextIndex RKParsedScript::prevContext(const ContextIndex from) const {
if (!from.valid()) return ContextIndex();
+ if (context_list.at(from.index).type == Top) return ContextIndex();
int i = from.index - 1;
return ContextIndex(i); // automatically invalid, if reversing past 0
}
@@ -182,11 +184,14 @@ RKParsedScript::ContextIndex RKParsedScript::prevContext(const ContextIndex from
RKParsedScript::ContextIndex RKParsedScript::parentRegion(const ContextIndex from) const {
RK_TRACE(MISC);
if (from.valid()) {
- int startpos = context_list.at(from.index).start;
- for (int i = from.index - 1; i >= 0; --i) {
- if (context_list.at(i).maybeNesting() && context_list.at(i).end >= startpos) {
- return ContextIndex(i);
+ const int startpos = getContext(from).start;
+ auto ci = prevContext(from);
+ while (ci.valid()) {
+ auto ctx = getContext(ci);
+ if (ctx.maybeNesting() && ctx.end >= startpos) {
+ return ci;
}
+ ci = prevContext(ci);
}
}
return ContextIndex();
@@ -197,11 +202,11 @@ RKParsedScript::ContextIndex RKParsedScript::nextSiblingOrOuter(const ContextInd
RK_TRACE(MISC);
if (from.valid()) {
int endpos = context_list.at(from.index).end;
- unsigned int i = from.index;
+ auto ci = from;
do {
- ++i;
- } while (i < context_list.size() && context_list.at(i).start <= endpos); // advance, skipping child regions
- return ContextIndex(i < context_list.size() ? i : -1);
+ ci = nextContext(ci);
+ } while (ci.valid() && context_list.at(ci.index).start <= endpos); // advance, skipping child regions
+ return ci;
}
return ContextIndex();
}
@@ -218,18 +223,18 @@ RKParsedScript::ContextIndex RKParsedScript::prevSiblingOrOuter(const ContextInd
if (from.valid()) {
int startpos = context_list.at(from.index).start;
const auto parent = parentRegion(from);
- int i = from.index;
+ auto ci = from;
while (true) {
- --i;
- if (i < 0) break;
- const auto cparent = parentRegion(ContextIndex(i));
+ ci = prevContext(ci);
+ if (!ci.valid()) break;
+ const auto cparent = parentRegion(ci);
// sibling must have the same parent as us (consider reversing from "c" in "a(b)c"; we shall not stop at b)
if (cparent == parent) break;
// but we also allow outer contexts, i.e. parent of previous is a grandparent of us
if (!cparent.valid()) break;
if (context_list.at(cparent.index).end >= startpos) break;
}
- return ContextIndex(i);
+ return ci;
}
return ContextIndex();
}
@@ -291,9 +296,9 @@ RKParsedScript::ContextIndex RKParsedScript::nextStatement(const ContextIndex fr
// consider advancing from "b" in "a = (b + c) + d; e" -> should be e, not "+ d"
while (ni.valid() && getContext(ni).type != Delimiter) ni = nextContext(lastContextInStatement(ni));
// skip over any following non-interesting contexts
- while (true) {
+ while (ni.valid()) {
auto type = getContext(ni).type;
- if (type != Delimiter && type != Comment) break;
+ if (type != Delimiter && type != Comment && type != Top) break;
ni = nextSiblingOrOuter(ni);
}
return ni;
@@ -305,9 +310,9 @@ RKParsedScript::ContextIndex RKParsedScript::prevStatement(const ContextIndex fr
// start at the first context preceeding the current statement
auto pi = prevContext(firstContextInStatement(from));
// from here, skip over any preceding non-interesting contexts (entering the previous statement)
- while (true) {
+ while (pi.valid()) {
auto type = getContext(pi).type;
- if (type != Delimiter && type != Comment) break;
+ if (type != Delimiter && type != Comment && type != Top) break;
pi = prevSiblingOrOuter(pi);
}
// now find the start of the previous statement
@@ -353,7 +358,7 @@ RKParsedScript::ContextIndex RKParsedScript::nextStatementOrInner(const ContextI
auto nsi = nextStatement(from);
auto ci = nextContext(from);
- while (ci.valid() && ci.index < nsi.index) {
+ while (ci.valid() && (ci.index < nsi.index || !nsi.valid())) {
auto ctx = getContext(ci);
// NOTE: We want to move into inner contexts, *unless* they are empty
if (ctx.maybeNesting()) {
More information about the rkward-tracker
mailing list