Fix completion bug
The reason why completion didn't work is that the parser didn't reject
"/ietf-system:system-" properly. It was able to parse the
ietf-system:system part but didn't care about the dash. In the end the
parser still failed, but completions did get created as if we were
already inside ietf-system:system (which were not because of the dash).
The fix is this: after a path fragment there can only be two things, a
slash or a "pathEnd" (which is a space or EOI). So, when I parse
ietf-system:system I check if that's the case. Otherwise, the parsing
failed, because we didn't parse the whole path fragment (because of the
dash in this case).
Issue: https://tree.taiga.io/project/jktjkt-netconf-cli/issue/208
Change-Id: I180308658af41e0ae119fcb17c75be9cce6aa764
diff --git a/src/ast_path.cpp b/src/ast_path.cpp
index 7ab9b18..9c77f35 100644
--- a/src/ast_path.cpp
+++ b/src/ast_path.cpp
@@ -272,6 +272,9 @@
res = joinPaths(res, (prefixes == Prefixes::Always ? path.m_nodes.at(0).m_prefix.value().m_name + ":" : "") + std::visit(nodeToDataStringVisitor(), it.m_suffix));
}
}
+ if (path.m_trailingSlash == TrailingSlash::Present) {
+ res += "/";
+ }
return res;
}
diff --git a/src/path_parser.hpp b/src/path_parser.hpp
index c3df931..29dbd04 100644
--- a/src/path_parser.hpp
+++ b/src/path_parser.hpp
@@ -30,6 +30,7 @@
x3::rule<createValueSuggestions_class, x3::unused_type> const createValueSuggestions = "createValueSuggestions";
x3::rule<suggestKeysEnd_class, x3::unused_type> const suggestKeysEnd = "suggestKeysEnd";
x3::rule<class leafListValue_class, leaf_data_> const leafListValue = "leafListValue";
+auto pathEnd = x3::rule<class PathEnd>{"pathEnd"} = &space_separator | x3::eoi;
enum class NodeParserMode {
CompleteDataNode,
@@ -218,7 +219,15 @@
}
if (res) {
- parserContext.pushPathFragment(attr);
+ // After a path fragment, there can only be a slash or a "pathEnd". If this is not the case
+ // then that means there are other unparsed characters after the fragment. In that case the parsing
+ // needs to fail.
+ res = (pathEnd | &char_('/')).parse(begin, end, ctx, rctx, x3::unused);
+ if (!res) {
+ begin = saveIter;
+ } else {
+ parserContext.pushPathFragment(attr);
+ }
}
return res;
@@ -270,7 +279,6 @@
initializePath.parse(begin, end, ctx, rctx, x3::unused);
dataPath_ attrData;
- auto pathEnd = x3::rule<class PathEnd>{"pathEnd"} = &space_separator | x3::eoi;
// absoluteStart has to be separate from the dataPath parser,
// otherwise, if the "dataNode % '/'" parser fails, the begin iterator
// gets reverted to before the starting slash.
diff --git a/tests/cd.cpp b/tests/cd.cpp
index 1091bae..ee056ae 100644
--- a/tests/cd.cpp
+++ b/tests/cd.cpp
@@ -10,6 +10,7 @@
#include "ast_commands.hpp"
#include "leaf_data_helpers.hpp"
#include "parser.hpp"
+#include "pretty_printers.hpp"
#include "static_schema.hpp"
TEST_CASE("cd")
diff --git a/tests/path_completion.cpp b/tests/path_completion.cpp
index 6c7a8e6..511d922 100644
--- a/tests/path_completion.cpp
+++ b/tests/path_completion.cpp
@@ -41,6 +41,9 @@
schema->addLeafList("/", "example:addresses", yang::String{});
schema->addRpc("/", "second:fire");
schema->addLeaf("/second:fire", "second:whom", yang::String{});
+ schema->addContainer("/", "example:system");
+ schema->addContainer("/example:system", "example:thing");
+ schema->addContainer("/", "example:system-state");
auto mockDatastore = std::make_shared<MockDatastoreAccess>();
// The parser will use DataQuery for key value completion, but I'm not testing that here, so I don't return anything.
@@ -70,14 +73,14 @@
SECTION("ls ")
{
input = "ls ";
- expectedCompletions = {"example:addresses/", "example:ano/", "example:anoda/", "example:bota/", "example:leafInt ", "example:list/", "example:ovoce/", "example:readonly ", "example:ovocezelenina/", "example:twoKeyList/", "second:amelie/", "second:fire/"};
+ expectedCompletions = {"example:addresses/", "example:ano/", "example:anoda/", "example:bota/", "example:leafInt ", "example:list/", "example:ovoce/", "example:readonly ", "example:ovocezelenina/", "example:twoKeyList/", "second:amelie/", "second:fire/", "example:system/", "example:system-state/"};
expectedContextLength = 0;
}
SECTION("ls e")
{
input = "ls e";
- expectedCompletions = {"example:addresses/", "example:ano/", "example:anoda/", "example:bota/", "example:leafInt ", "example:list/", "example:ovoce/", "example:readonly ", "example:ovocezelenina/", "example:twoKeyList/"};
+ expectedCompletions = {"example:addresses/", "example:ano/", "example:anoda/", "example:bota/", "example:leafInt ", "example:list/", "example:ovoce/", "example:readonly ", "example:ovocezelenina/", "example:twoKeyList/", "example:system/", "example:system-state/"};
expectedContextLength = 1;
}
@@ -105,14 +108,14 @@
SECTION("ls /")
{
input = "ls /";
- expectedCompletions = {"example:addresses/", "example:ano/", "example:anoda/", "example:bota/", "example:leafInt ", "example:list/", "example:ovoce/", "example:readonly ", "example:ovocezelenina/", "example:twoKeyList/", "second:amelie/", "second:fire/"};
+ expectedCompletions = {"example:addresses/", "example:ano/", "example:anoda/", "example:bota/", "example:leafInt ", "example:list/", "example:ovoce/", "example:readonly ", "example:ovocezelenina/", "example:twoKeyList/", "second:amelie/", "second:fire/", "example:system/", "example:system-state/"};
expectedContextLength = 0;
}
SECTION("ls /e")
{
input = "ls /e";
- expectedCompletions = {"example:addresses/", "example:ano/", "example:anoda/", "example:bota/", "example:leafInt ", "example:list/", "example:ovoce/", "example:readonly ", "example:ovocezelenina/", "example:twoKeyList/"};
+ expectedCompletions = {"example:addresses/", "example:ano/", "example:anoda/", "example:bota/", "example:leafInt ", "example:list/", "example:ovoce/", "example:readonly ", "example:ovocezelenina/", "example:twoKeyList/", "example:system/", "example:system-state/"};
expectedContextLength = 1;
}
@@ -185,6 +188,13 @@
expectedCompletions = {};
expectedContextLength = 0;
}
+
+ SECTION("ls /example:system-")
+ {
+ input = "ls /example:system-";
+ expectedCompletions = {"example:system-state/"};
+ expectedContextLength = 15;
+ }
}
SECTION("get completion")
@@ -383,21 +393,21 @@
{
parser.changeNode({{}, {{module_{"second"}, rpcNode_{"fire"}}}});
input = "set ../";
- expectedCompletions = {"example:addresses", "example:ano/", "example:anoda/", "example:bota/", "example:leafInt ", "example:list", "example:ovoce", "example:ovocezelenina", "example:twoKeyList", "second:amelie/", "second:fire/"};
+ expectedCompletions = {"example:addresses", "example:ano/", "example:anoda/", "example:bota/", "example:leafInt ", "example:list", "example:ovoce", "example:ovocezelenina", "example:twoKeyList", "second:amelie/", "second:fire/", "example:system/", "example:system-state/"};
expectedContextLength = 0;
}
SECTION("rpc nodes not completed for the get command")
{
input = "get ";
- expectedCompletions = {"example:addresses", "example:ano/", "example:anoda/", "example:bota/", "example:leafInt ", "example:list", "example:ovoce", "example:ovocezelenina", "example:readonly ", "example:twoKeyList", "second:amelie/"};
+ expectedCompletions = {"example:addresses", "example:ano/", "example:anoda/", "example:bota/", "example:leafInt ", "example:list", "example:ovoce", "example:ovocezelenina", "example:readonly ", "example:twoKeyList", "second:amelie/", "example:system/", "example:system-state/"};
expectedContextLength = 0;
}
SECTION("leafs not completed for cd")
{
input = "cd ";
- expectedCompletions = {"example:addresses", "example:ano/", "example:anoda/", "example:bota/","example:list", "example:ovoce", "example:ovocezelenina", "example:twoKeyList", "second:amelie/"};
+ expectedCompletions = {"example:addresses", "example:ano/", "example:anoda/", "example:bota/","example:list", "example:ovoce", "example:ovocezelenina", "example:twoKeyList", "second:amelie/", "example:system/", "example:system-state/"};
expectedContextLength = 0;
}
diff --git a/tests/pretty_printers.hpp b/tests/pretty_printers.hpp
index 7f19a8e..cb9962e 100644
--- a/tests/pretty_printers.hpp
+++ b/tests/pretty_printers.hpp
@@ -181,6 +181,12 @@
return s;
}
+std::ostream& operator<<(std::ostream& s, const cd_& cd)
+{
+ s << "\ncd_ {\n " << cd.m_path << "}\n";
+ return s;
+}
+
std::ostream& operator<<(std::ostream& s, const move_& move)
{
s << "\nmove_ {\n";