Add enum completion
Change-Id: I5ee7d6359812535d0eef9f70e1b66e205d93cf39
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b8ee606..75f0e7d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -182,6 +182,7 @@
cli_test(utils)
cli_test(path_completion)
cli_test(command_completion)
+ cli_test(enum_completion)
endif()
if(WITH_DOCS)
diff --git a/src/ast_handlers.hpp b/src/ast_handlers.hpp
index 943af19..7eed8be 100644
--- a/src/ast_handlers.hpp
+++ b/src/ast_handlers.hpp
@@ -569,3 +569,22 @@
_pass(context) = false;
}
};
+
+struct createEnumSuggestions_class {
+ template <typename T, typename Iterator, typename Context>
+ void on_success(Iterator const& begin, Iterator const&, T&, Context const& context)
+ {
+ auto& parserContext = x3::get<parser_context_tag>(context);
+ parserContext.m_completionIterator = begin;
+ const Schema& schema = parserContext.m_schema;
+
+ boost::optional<std::string> module;
+ if (parserContext.m_curPath.m_nodes.back().m_prefix)
+ module = parserContext.m_curPath.m_nodes.back().m_prefix.value().m_name;
+
+ leaf_ leaf = boost::get<leaf_>(parserContext.m_curPath.m_nodes.back().m_suffix);
+ schemaPath_ location = pathWithoutLastNode(parserContext.m_curPath);
+
+ parserContext.m_suggestions = schema.enumValues(location, {module, leaf.m_name});
+ }
+};
diff --git a/src/grammars.hpp b/src/grammars.hpp
index 3742e35..c12f922 100644
--- a/src/grammars.hpp
+++ b/src/grammars.hpp
@@ -59,6 +59,7 @@
x3::rule<suggestKeysEnd_class, x3::unused_type> const suggestKeysEnd = "suggestKeysEnd";
x3::rule<createCommandSuggestions_class, x3::unused_type> const createCommandSuggestions = "createCommandSuggestions";
x3::rule<completing_class, x3::unused_type> const completing = "completing";
+x3::rule<createEnumSuggestions_class, x3::unused_type> const createEnumSuggestions = "createEnumSuggestions";
#if __clang__
#pragma GCC diagnostic push
@@ -174,8 +175,11 @@
auto const leafPath_def =
dataPath;
+auto const createEnumSuggestions_def =
+ x3::eps;
+
auto const leaf_data_enum_def =
- +char_;
+ createEnumSuggestions >> +char_;
auto const leaf_data_decimal_def =
double_;
@@ -234,7 +238,7 @@
get_::name >> -(space_separator >> (dataPathListEnd | dataPath));
auto const set_def =
- set_::name >> space_separator > leafPath > leaf_data;
+ set_::name >> space_separator > leafPath > space_separator > leaf_data;
auto const commit_def =
commit_::name >> x3::attr(commit_());
@@ -296,3 +300,4 @@
BOOST_SPIRIT_DEFINE(suggestKeysEnd)
BOOST_SPIRIT_DEFINE(createCommandSuggestions)
BOOST_SPIRIT_DEFINE(completing)
+BOOST_SPIRIT_DEFINE(createEnumSuggestions)
diff --git a/src/schema.hpp b/src/schema.hpp
index e1f019f..c55e659 100644
--- a/src/schema.hpp
+++ b/src/schema.hpp
@@ -79,6 +79,7 @@
virtual bool nodeExists(const std::string& location, const std::string& node) const = 0;
virtual const std::set<std::string> listKeys(const schemaPath_& location, const ModuleNodePair& node) const = 0;
virtual yang::LeafDataTypes leafType(const schemaPath_& location, const ModuleNodePair& node) const = 0;
+ virtual const std::set<std::string> enumValues(const schemaPath_& location, const ModuleNodePair& node) const = 0;
virtual std::set<std::string> childNodes(const schemaPath_& path, const Recursion recursion) const = 0;
private:
diff --git a/src/static_schema.cpp b/src/static_schema.cpp
index 9be7751..d80ac7b 100644
--- a/src/static_schema.cpp
+++ b/src/static_schema.cpp
@@ -121,12 +121,8 @@
bool StaticSchema::leafEnumHasValue(const schemaPath_& location, const ModuleNodePair& node, const std::string& value) const
{
- std::string locationString = pathToAbsoluteSchemaString(location);
- assert(isLeaf(location, node));
-
- const auto& child = children(locationString).at(fullNodeName(location, node));
- const auto& list = boost::get<yang::leaf>(child);
- return list.m_enumValues.find(value) != list.m_enumValues.end();
+ auto enums = enumValues(location, node);
+ return enums.find(value) != enums.end();
}
bool StaticSchema::isLeaf(const schemaPath_& location, const ModuleNodePair& node) const
@@ -145,6 +141,16 @@
return boost::get<yang::leaf>(children(locationString).at(fullNodeName(location, node))).m_type;
}
+const std::set<std::string> StaticSchema::enumValues(const schemaPath_& location, const ModuleNodePair& node) const
+{
+ std::string locationString = pathToAbsoluteSchemaString(location);
+ assert(isLeaf(location, node));
+
+ const auto& child = children(locationString).at(fullNodeName(location, node));
+ const auto& leaf = boost::get<yang::leaf>(child);
+ return leaf.m_enumValues;
+}
+
// We do not test StaticSchema, so we don't need to implement recursive childNodes
// for this class.
std::set<std::string> StaticSchema::childNodes(const schemaPath_& path, const Recursion) const
diff --git a/src/static_schema.hpp b/src/static_schema.hpp
index 7326120..d07852e 100644
--- a/src/static_schema.hpp
+++ b/src/static_schema.hpp
@@ -33,6 +33,7 @@
bool nodeExists(const std::string& location, const std::string& node) const override;
const std::set<std::string> listKeys(const schemaPath_& location, const ModuleNodePair& node) const override;
yang::LeafDataTypes leafType(const schemaPath_& location, const ModuleNodePair& node) const override;
+ const std::set<std::string> enumValues(const schemaPath_& location, const ModuleNodePair& node) const override;
std::set<std::string> childNodes(const schemaPath_& path, const Recursion) const override;
void addContainer(const std::string& location, const std::string& name, yang::ContainerTraits isPresence = yang::ContainerTraits::None);
diff --git a/src/yang_schema.cpp b/src/yang_schema.cpp
index 522e58a..8b870fc 100644
--- a/src/yang_schema.cpp
+++ b/src/yang_schema.cpp
@@ -112,8 +112,15 @@
bool YangSchema::leafEnumHasValue(const schemaPath_& location, const ModuleNodePair& node, const std::string& value) const
{
+ auto enums = enumValues(location, node);
+
+ return std::any_of(enums.begin(), enums.end(), [=](const auto& x) { return x == value; });
+}
+
+const std::set<std::string> YangSchema::enumValues(const schemaPath_& location, const ModuleNodePair& node) const
+{
if (!isLeaf(location, node) || leafType(location, node) != yang::LeafDataTypes::Enum)
- return false;
+ return {};
libyang::Schema_Node_Leaf leaf(getSchemaNode(location, node));
auto type = leaf.type();
@@ -126,7 +133,9 @@
enm = type->info()->enums()->enm();
}
- return std::any_of(enm.begin(), enm.end(), [=](const auto& x) { return x->name() == value; });
+ std::set<std::string> enumSet;
+ std::transform(enm.begin(), enm.end(), std::inserter(enumSet, enumSet.end()), [](auto it) { return it->name(); });
+ return enumSet;
}
bool YangSchema::listHasKey(const schemaPath_& location, const ModuleNodePair& node, const std::string& key) const
diff --git a/src/yang_schema.hpp b/src/yang_schema.hpp
index e4d6d5d..e56c853 100644
--- a/src/yang_schema.hpp
+++ b/src/yang_schema.hpp
@@ -39,6 +39,7 @@
bool nodeExists(const std::string& location, const std::string& node) const override;
const std::set<std::string> listKeys(const schemaPath_& location, const ModuleNodePair& node) const override;
yang::LeafDataTypes leafType(const schemaPath_& location, const ModuleNodePair& node) const override;
+ const std::set<std::string> enumValues(const schemaPath_& location, const ModuleNodePair& node) const override;
std::set<std::string> childNodes(const schemaPath_& path, const Recursion recursion) const override;
void registerModuleCallback(const std::function<std::string(const char*, const char*, const char*)>& clb);
diff --git a/tests/enum_completion.cpp b/tests/enum_completion.cpp
new file mode 100644
index 0000000..e352c7f
--- /dev/null
+++ b/tests/enum_completion.cpp
@@ -0,0 +1,62 @@
+
+/*
+ * Copyright (C) 2018 CESNET, https://photonics.cesnet.cz/
+ * Copyright (C) 2018 FIT CVUT, https://fit.cvut.cz/
+ *
+ * Written by Václav Kubernát <kubervac@fit.cvut.cz>
+ *
+ */
+
+#include "trompeloeil_catch.h"
+#include "ast_commands.hpp"
+#include "parser.hpp"
+#include "static_schema.hpp"
+
+TEST_CASE("enum completion")
+{
+ auto schema = std::make_shared<StaticSchema>();
+ schema->addModule("mod");
+ schema->addContainer("", "mod:contA");
+ schema->addLeafEnum("", "mod:leafEnum", {"lala", "lol", "data", "coze"});
+ schema->addLeafEnum("mod:contA", "mod:leafInCont", {"abc", "def"});
+ schema->addList("", "mod:list", {"number"});
+ schema->addLeafEnum("mod:list", "mod:leafInList", {"ano", "anoda", "ne", "katoda"});
+ Parser parser(schema);
+ std::string input;
+ std::ostringstream errorStream;
+
+ std::set<std::string> expected;
+
+ SECTION("set mod:leafEnum ")
+ {
+ input = "set mod:leafEnum ";
+ expected = {"lala", "lol", "data", "coze"};
+ }
+
+ SECTION("set mod:leafEnum c")
+ {
+ input = "set mod:leafEnum c";
+ expected = {"coze"};
+ }
+
+ SECTION("set mod:leafEnum l")
+ {
+ input = "set mod:leafEnum l";
+ expected = {"lala", "lol"};
+ }
+
+
+ SECTION("set mod:contA/leafInCont ")
+ {
+ input = "set mod:contA/leafInCont ";
+ expected = {"abc", "def"};
+ }
+
+ SECTION("set mod:list[number=42]/leafInList ")
+ {
+ input = "set mod:list[number=42]/leafInList ";
+ expected = {"ano", "anoda", "ne", "katoda"};
+ }
+
+ REQUIRE(parser.completeCommand(input, errorStream) == expected);
+}