Tab completion for paths

There were several problems with the implementation of the completion.
Firstly, a way to propagate completions back from the parser was needed.
That was rather easy to solve with the ParserContext class. Next problem
was prefix matching, or specifically, where will it happen. There are
three possible situations, that can happen, after the input goes through
the parser:

1) The parsing was successful and there is no more input. Then there is
no need for prefix matching and completions are returned as they are:
if there was a valid path "first/second" and the user entered "cd
first/second/". Then the whole input would be parsed and completions
would be the nodes contained within "first/second".

2) The parsing was successful and there is more unparsed input. Then the
completions need to be matched with the input and the common prefix
deleted: if there were valid paths "first/second", "first/service" and
"first/something", and the user inputs "cd first/se", then "se" would be
left unparsed and it would be used as a prefix to change the
completions: they would be "cond" and "rvice".

3) The parsing was successful, there is no more input, but the parsed
input could still be completed. This means I need to know, where exactly
(in the input string) were the completions generated. Example: if there
were valid paths "first/min" and "first/minister", and the user inputs
"cd first/min". Then the parsing is completed successfully, the user
could press enter and execute the command, or generate completions and
"minister" would pop up.

Because there is no way to see the whole input in the AST handlers, the
matching had to be done outside the grammar (that is, in the
completeCommand method). The problem is mainly with the third situation.
Fortunately, I can save the position, where the completions were
generated, and use this information in the completeCommand method. This
solution also has the advantage of less duplicate code since we do all
matching in the same place.

The last problem was when do we exactly create the suggestions. One
option was to create them right after the "node" parser succeeds (in its
AST handler). Unfortunately, that created some problems if the parsing
succeeded but no additional user input was present. To solve this, a
pseudo-parser (a nothing parser) called createSuggestions was created.
This parser generates suggestions in-between nodes, so it's not
dependent on the "node" parser.

Change-Id: I02d9b5d23d7e695bfe7a5e168d9fefe931bc04ef
diff --git a/src/grammars.hpp b/src/grammars.hpp
index d0101fb..79cd17a 100644
--- a/src/grammars.hpp
+++ b/src/grammars.hpp
@@ -53,7 +53,8 @@
 x3::rule<commit_class, commit_> const commit = "commit";
 x3::rule<command_class, command_> const command = "command";
 
-x3::rule<initializeContext_class, x3::unused_type> const initializeContext = "initializeContext";
+x3::rule<initializePath_class, x3::unused_type> const initializePath = "initializePath";
+x3::rule<createPathSuggestions_class, x3::unused_type> const createPathSuggestions = "createPathSuggestions";
 
 #if __clang__
 #pragma GCC diagnostic push
@@ -110,12 +111,15 @@
 auto const leaf_def =
         node_identifier;
 
+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 =
-        -(module) >> x3::expect[container | list | nodeup | leaf];
+        createPathSuggestions >> -(module) >> (container | list | nodeup | leaf);
 
 auto const dataNode_def =
-        -(module) >> (container | listElement | nodeup | leaf);
+        createPathSuggestions >> -(module) >> (container | listElement | nodeup | leaf);
 
 auto const absoluteStart_def =
         x3::omit['/'] >> x3::attr(Scope::Absolute);
@@ -123,29 +127,32 @@
 auto const trailingSlash_def =
         x3::omit['/'] >> x3::attr(TrailingSlash::Present);
 
+auto const space_separator =
+        x3::omit[x3::no_skip[space]];
+
 // I have to insert an empty vector to the first alternative, otherwise they won't have the same attribute
 auto const dataPath_def =
-        absoluteStart >> x3::attr(decltype(dataPath_::m_nodes)()) >> x3::attr(TrailingSlash::NonPresent) >> x3::eoi |
-        -(absoluteStart) >> dataNode % '/' >> -trailingSlash;
+        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));
 
 auto const dataNodeList_def =
-        -(module) >> list;
+        -(module) >> createPathSuggestions >> list;
 
 // This intermediate rule is mandatory, because we need the first alternative
 // to be collapsed to a vector. If we didn't use the intermediate rule,
 // Spirit wouldn't know we want it to collapse.
 // https://github.com/boostorg/spirit/issues/408
 auto const dataNodesListEnd_def =
-        dataNode % '/' >> '/' >> dataNodeList |
-        initializeContext >> x3::attr(decltype(dataPath_::m_nodes)()) >> dataNodeList;
+        initializePath >> dataNode % '/' >> '/' >> dataNodeList >> -(&char_('/') >> createPathSuggestions) |
+        initializePath >> x3::attr(decltype(dataPath_::m_nodes)()) >> dataNodeList;
 
 auto const dataPathListEnd_def =
-        absoluteStart >> x3::attr(decltype(dataPath_::m_nodes)()) >> x3::attr(TrailingSlash::NonPresent) >> x3::eoi |
-        -(absoluteStart) >> dataNodesListEnd >> -trailingSlash;
+        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));
 
 auto const schemaPath_def =
-        absoluteStart >> x3::attr(decltype(schemaPath_::m_nodes)()) >> x3::attr(TrailingSlash::NonPresent) >> x3::eoi |
-        -(absoluteStart) >> schemaNode % '/' >> -trailingSlash;
+        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));
 
 auto const leafPath_def =
         dataPath;
@@ -182,9 +189,6 @@
         leaf_data_uint |
         leaf_data_string];
 
-auto const space_separator =
-        x3::omit[x3::no_skip[space]];
-
 struct ls_options_table : x3::symbols<LsOption> {
     ls_options_table()
     {
@@ -193,12 +197,12 @@
     }
 } const ls_options;
 
-// A "nothing" parser, which is used to reset the context (when trying to parse different types of paths)
-auto const initializeContext_def =
+// A "nothing" parser, which is used to indicate we tried to parse a path
+auto const initializePath_def =
         x3::eps;
 
 auto const ls_def =
-        lit("ls") >> *(space_separator >> ls_options) >> -(space_separator >> (dataPathListEnd | initializeContext >> dataPath));
+        lit("ls") >> *(space_separator >> ls_options) >> -(space_separator >> (dataPathListEnd | dataPath | schemaPath));
 
 auto const cd_def =
         lit("cd") >> space_separator > dataPath;
@@ -210,7 +214,7 @@
         lit("delete") >> space_separator > dataPath;
 
 auto const get_def =
-        lit("get") >> -(space_separator >> (dataPathListEnd | initializeContext >> dataPath));
+        lit("get") >> -(space_separator >> (dataPathListEnd | dataPath));
 
 auto const set_def =
         lit("set") >> space_separator > leafPath > leaf_data;
@@ -222,7 +226,7 @@
         lit("discard") >> x3::attr(discard_());
 
 auto const command_def =
-        x3::expect[cd | create | delete_rule | set | commit | get | ls | discard] >> x3::eoi;
+        x3::expect[cd | create | delete_rule | set | commit | get | ls | discard];
 
 #if __clang__
 #pragma GCC diagnostic pop
@@ -257,7 +261,7 @@
 BOOST_SPIRIT_DEFINE(leaf_data_int)
 BOOST_SPIRIT_DEFINE(leaf_data_uint)
 BOOST_SPIRIT_DEFINE(leaf_data_string)
-BOOST_SPIRIT_DEFINE(initializeContext)
+BOOST_SPIRIT_DEFINE(initializePath)
 BOOST_SPIRIT_DEFINE(set)
 BOOST_SPIRIT_DEFINE(commit)
 BOOST_SPIRIT_DEFINE(get)
@@ -267,3 +271,4 @@
 BOOST_SPIRIT_DEFINE(create)
 BOOST_SPIRIT_DEFINE(delete_rule)
 BOOST_SPIRIT_DEFINE(command)
+BOOST_SPIRIT_DEFINE(createPathSuggestions)