Fix a bug with path parsing

It is now required that the whole path is parsed (that is, there is a
space after the path, or a EOI), when not completing, so that all path
types are tried. Before this, the parser could stop before trying
different path types.

Change-Id: I1964f892418508e6356c4a8811ab00ce1acfa24d
diff --git a/src/ast_handlers.hpp b/src/ast_handlers.hpp
index f0bbf4c..943af19 100644
--- a/src/ast_handlers.hpp
+++ b/src/ast_handlers.hpp
@@ -558,3 +558,14 @@
         });
     }
 };
+
+struct completing_class {
+    template <typename T, typename Iterator, typename Context>
+    void on_success(Iterator const&, Iterator const&, T&, Context const& context)
+    {
+        auto& parserContext = x3::get<parser_context_tag>(context);
+
+        if (!parserContext.m_completing)
+            _pass(context) = false;
+    }
+};
diff --git a/src/grammars.hpp b/src/grammars.hpp
index cfd5203..f3dd6f9 100644
--- a/src/grammars.hpp
+++ b/src/grammars.hpp
@@ -58,6 +58,7 @@
 x3::rule<createKeySuggestions_class, x3::unused_type> const createKeySuggestions = "createKeySuggestions";
 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";
 
 #if __clang__
 #pragma GCC diagnostic push
@@ -142,10 +143,14 @@
 auto const space_separator =
         x3::omit[x3::no_skip[space]];
 
+// This is a pseudo-parser, that fails if we're not completing a command
+auto const completing_def =
+        x3::eps;
+
 // I have to insert an empty vector to the first alternative, otherwise they won't have the same attribute
 auto const dataPath_def =
         initializePath >> absoluteStart >> createPathSuggestions >> x3::attr(decltype(dataPath_::m_nodes)()) >> x3::attr(TrailingSlash::NonPresent) >> x3::eoi |
-        initializePath >> -(absoluteStart >> createPathSuggestions) >> dataNode % '/' >> (trailingSlash >> createPathSuggestions | (&space_separator | x3::eoi));
+        initializePath >> -(absoluteStart >> createPathSuggestions) >> dataNode % '/' >> (trailingSlash >> createPathSuggestions >> (completing | x3::eoi) | (&space_separator | x3::eoi));
 
 auto const dataNodeList_def =
         -(module) >> createPathSuggestions >> list;
@@ -160,11 +165,11 @@
 
 auto const dataPathListEnd_def =
         initializePath >> absoluteStart >> createPathSuggestions >> x3::attr(decltype(dataPath_::m_nodes)()) >> x3::attr(TrailingSlash::NonPresent) >> x3::eoi |
-        initializePath >> -(absoluteStart >> createPathSuggestions) >> dataNodesListEnd >> (trailingSlash >> createPathSuggestions | (&space_separator | x3::eoi));
+        initializePath >> -(absoluteStart >> createPathSuggestions) >> dataNodesListEnd >> (trailingSlash >> createPathSuggestions >> (completing | x3::eoi) | (&space_separator | x3::eoi));
 
 auto const schemaPath_def =
         initializePath >> absoluteStart >> createPathSuggestions >> x3::attr(decltype(schemaPath_::m_nodes)()) >> x3::attr(TrailingSlash::NonPresent) >> x3::eoi |
-        initializePath >> -(absoluteStart >> createPathSuggestions) >> schemaNode % '/' >> (trailingSlash >> createPathSuggestions | (&space_separator | x3::eoi));
+        initializePath >> -(absoluteStart >> createPathSuggestions) >> schemaNode % '/' >> (trailingSlash >> createPathSuggestions >> (completing | x3::eoi) | (&space_separator | x3::eoi));
 
 auto const leafPath_def =
         dataPath;
@@ -290,3 +295,4 @@
 BOOST_SPIRIT_DEFINE(createKeySuggestions)
 BOOST_SPIRIT_DEFINE(suggestKeysEnd)
 BOOST_SPIRIT_DEFINE(createCommandSuggestions)
+BOOST_SPIRIT_DEFINE(completing)
diff --git a/src/parser.cpp b/src/parser.cpp
index ba6690d..cf433e2 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -44,6 +44,7 @@
     std::set<std::string> completions;
     command_ parsedCommand;
     ParserContext ctx(*m_schema, dataPathToSchemaPath(m_curDir));
+    ctx.m_completing = true;
     auto it = line.begin();
     boost::spirit::x3::error_handler<std::string::const_iterator> errorHandler(it, line.end(), errorStream);
 
diff --git a/src/parser_context.hpp b/src/parser_context.hpp
index 5d0fe38..3871192 100644
--- a/src/parser_context.hpp
+++ b/src/parser_context.hpp
@@ -18,6 +18,7 @@
     bool m_topLevelModulePresent = false;
     std::set<std::string> m_tmpListKeys;
     bool m_errorHandled = false;
+    bool m_completing = false;
     std::set<std::string> m_suggestions;
     // Iterator pointing to where suggestions were created
     std::string::const_iterator m_completionIterator;
diff --git a/tests/ls.cpp b/tests/ls.cpp
index e42152d..4ce78fd 100644
--- a/tests/ls.cpp
+++ b/tests/ls.cpp
@@ -150,6 +150,13 @@
                 expected.m_path = dataPath_{Scope::Relative, {dataNode_(module_{"example"}, listElement_{"list", keys}),
                                                                 dataNode_(container_{"contInList"})}};
             }
+
+            SECTION("ls example:list/contInList")
+            {
+                input = "ls example:list/contInList";
+                expected.m_path = schemaPath_{Scope::Relative, {schemaNode_(module_{"example"}, list_{"list"}),
+                                                                schemaNode_(container_{"contInList"})}};
+            }
         }
 
         command_ command = parser.parseCommand(input, errorStream);