Rework schemaNode parsing

Change-Id: I7c98b9a2c70370f68ef48b31caf70f6974474364
diff --git a/src/path_parser.hpp b/src/path_parser.hpp
index af98208..e2af35e 100644
--- a/src/path_parser.hpp
+++ b/src/path_parser.hpp
@@ -26,7 +26,6 @@
 x3::rule<createPathSuggestions_class, x3::unused_type> const createPathSuggestions = "createPathSuggestions";
 x3::rule<trailingSlash_class, TrailingSlash> const trailingSlash = "trailingSlash";
 x3::rule<dataNode_class, dataNode_> const dataNode = "dataNode";
-x3::rule<schemaNode_class, schemaNode_> const schemaNode = "schemaNode";
 x3::rule<absoluteStart_class, Scope> const absoluteStart = "absoluteStart";
 x3::rule<keyValue_class, keyValue_> const keyValue = "keyValue";
 x3::rule<key_identifier_class, std::string> const key_identifier = "key_identifier";
@@ -42,6 +41,60 @@
 x3::rule<suggestKeysEnd_class, x3::unused_type> const suggestKeysEnd = "suggestKeysEnd";
 
 
+struct schemaNode : x3::parser<schemaNode> {
+    using attribute_type = schemaNode_;
+    template <typename It, typename Ctx, typename RCtx, typename Attr>
+    bool parse(It& begin, It end, Ctx const& ctx, RCtx& rctx, Attr& attr) const
+    {
+        x3::symbols<schemaNode_> table(std::string{"schemaNode"}); // The constructor doesn't work with just the string literal
+
+        ParserContext& parserContext = x3::get<parser_context_tag>(ctx);
+        parserContext.m_suggestions.clear();
+        for (const auto& child : parserContext.m_schema.availableNodes(parserContext.currentSchemaPath(), Recursion::NonRecursive)) {
+            schemaNode_ out;
+            std::string parseString;
+            if (child.first) {
+                out.m_prefix = module_{*child.first};
+                parseString = *child.first + ":";
+            }
+            parseString += child.second;
+            switch (parserContext.m_schema.nodeType(parserContext.currentSchemaPath(), child)) {
+                case yang::NodeTypes::Container:
+                case yang::NodeTypes::PresenceContainer:
+                    out.m_suffix = container_{child.second};
+                    parserContext.m_suggestions.emplace(Completion{parseString + "/"});
+                    break;
+                case yang::NodeTypes::Leaf:
+                    out.m_suffix = leaf_{child.second};
+                    parserContext.m_suggestions.emplace(Completion{parseString + " "});
+                    break;
+                case yang::NodeTypes::List:
+                    out.m_suffix = list_{child.second};
+                    parserContext.m_suggestions.emplace(Completion{parseString, "[", Completion::WhenToAdd::IfFullMatch});
+                    break;
+                case yang::NodeTypes::Action:
+                case yang::NodeTypes::AnyXml:
+                case yang::NodeTypes::LeafList:
+                case yang::NodeTypes::Notification:
+                case yang::NodeTypes::Rpc:
+                    continue;
+            }
+            table.add(parseString, out);
+            table.add("..", attribute_type{nodeup_{}});
+            if (!child.first) {
+                auto topLevelModule = parserContext.currentSchemaPath().m_nodes.begin()->m_prefix;
+                out.m_prefix = topLevelModule;
+                table.add(topLevelModule->m_name + ":" + parseString, out);
+            }
+        }
+        auto res = table.parse(begin, end, ctx, rctx, attr);
+        if (res) {
+            parserContext.pushPathFragment(attr);
+        }
+        return res;
+    }
+} schemaNode;
+
 #if __clang__
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Woverloaded-shift-op-parentheses"
@@ -101,10 +154,6 @@
 auto const createPathSuggestions_def =
     x3::eps;
 
-// leaf cannot be in the middle of a path, however, I need the grammar's attribute to be a vector of variants
-auto const schemaNode_def =
-    createPathSuggestions >> -(module) >> (container | list | nodeup | leaf);
-
 auto const dataNode_def =
     createPathSuggestions >> -(module) >> (container | listElement | nodeup | leaf);
 
@@ -152,7 +201,6 @@
 BOOST_SPIRIT_DEFINE(listElement)
 BOOST_SPIRIT_DEFINE(list)
 BOOST_SPIRIT_DEFINE(nodeup)
-BOOST_SPIRIT_DEFINE(schemaNode)
 BOOST_SPIRIT_DEFINE(dataNode)
 BOOST_SPIRIT_DEFINE(container)
 BOOST_SPIRIT_DEFINE(leaf)