Merge "Add persistent line history"
diff --git a/.clang-format b/.clang-format
index 5c14162..f9b078b 100644
--- a/.clang-format
+++ b/.clang-format
@@ -18,4 +18,5 @@
 AlignAfterOpenBracket: Align
 BinPackArguments: false
 BinPackParameters: false
+SpaceBeforeCpp11BracedList: false
 ...
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/ast_path.cpp b/src/ast_path.cpp
index 1f01a72..3547f04 100644
--- a/src/ast_path.cpp
+++ b/src/ast_path.cpp
@@ -231,11 +231,11 @@
 
 schemaPath_ dataPathToSchemaPath(const dataPath_& path)
 {
-        schemaPath_ res{path.m_scope, {}};
+    schemaPath_ res{path.m_scope, {}};
 
-        std::transform(path.m_nodes.begin(), path.m_nodes.end(),
-                       std::back_inserter(res.m_nodes),
-                       [](const dataNode_& node) { return dataNodeToSchemaNode(node); });
+    std::transform(path.m_nodes.begin(), path.m_nodes.end(),
+                   std::back_inserter(res.m_nodes),
+                   [](const dataNode_& node) { return dataNodeToSchemaNode(node); });
 
-        return res;
+    return res;
 }
diff --git a/src/grammars.hpp b/src/grammars.hpp
index cfd5203..3742e35 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
@@ -65,183 +66,187 @@
 #endif
 
 auto const key_identifier_def =
-        lexeme[
-                ((alpha | char_("_")) >> *(alnum | char_("_") | char_("-") | char_(".")))
-        ];
+    lexeme[
+        ((alpha | char_("_")) >> *(alnum | char_("_") | char_("-") | char_(".")))
+    ];
 
 auto const quotedValue =
-        ('\'' > +(char_-'\'') > '\'') |
-        ('\"' > +(char_-'\"') > '\"');
+    ('\'' > +(char_-'\'') > '\'') |
+    ('\"' > +(char_-'\"') > '\"');
 
 auto const number =
-        +x3::digit;
+    +x3::digit;
 
 auto const createKeySuggestions_def =
-        x3::eps;
+    x3::eps;
 
 auto const suggestKeysEnd_def =
-        x3::eps;
+    x3::eps;
 
 auto const keyValue_def =
-        key_identifier > '=' > (quotedValue | number);
+    key_identifier > '=' > (quotedValue | number);
 
 auto const keyValueWrapper =
-        lexeme['[' > createKeySuggestions > keyValue > suggestKeysEnd > ']'];
+    lexeme['[' > createKeySuggestions > keyValue > suggestKeysEnd > ']'];
 
 auto const module_identifier_def =
-        lexeme[
-                ((alpha | char_("_")) >> *(alnum | char_("_") | char_("-") | char_(".")))
-        ];
+    lexeme[
+            ((alpha | char_("_")) >> *(alnum | char_("_") | char_("-") | char_(".")))
+    ];
 
 auto const node_identifier_def =
-        lexeme[
-                ((alpha | char_("_")) >> *(alnum | char_("_") | char_("-") | char_(".")))
-        ];
+    lexeme[
+            ((alpha | char_("_")) >> *(alnum | char_("_") | char_("-") | char_(".")))
+    ];
 
 auto const listPrefix_def =
-        node_identifier >> &char_('[');
+    node_identifier >> &char_('[');
 
 // even though we don't allow no keys to be supplied, the star allows me to check which keys are missing
 auto const listSuffix_def =
-        *keyValueWrapper;
+    *keyValueWrapper;
 
 auto const listElement_def =
-        listPrefix > listSuffix;
+    listPrefix > listSuffix;
 
 auto const list_def =
-        node_identifier >> !char_('[');
+    node_identifier >> !char_('[');
 
 auto const nodeup_def =
-        lit("..") > x3::attr(nodeup_());
+    lit("..") > x3::attr(nodeup_());
 
 auto const container_def =
-        node_identifier;
+    node_identifier;
 
 auto const module_def =
-        module_identifier >> x3::no_skip[':'] >> !x3::no_skip[space];
+    module_identifier >> x3::no_skip[':'] >> !x3::no_skip[space];
 
 auto const leaf_def =
-        node_identifier;
+    node_identifier;
 
 auto const createPathSuggestions_def =
-        x3::eps;
+    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);
+    createPathSuggestions >> -(module) >> (container | list | nodeup | leaf);
 
 auto const dataNode_def =
-        createPathSuggestions >> -(module) >> (container | listElement | nodeup | leaf);
+    createPathSuggestions >> -(module) >> (container | listElement | nodeup | leaf);
 
 auto const absoluteStart_def =
-        x3::omit['/'] >> x3::attr(Scope::Absolute);
+    x3::omit['/'] >> x3::attr(Scope::Absolute);
 
 auto const trailingSlash_def =
-        x3::omit['/'] >> x3::attr(TrailingSlash::Present);
+    x3::omit['/'] >> x3::attr(TrailingSlash::Present);
 
 auto const space_separator =
-        x3::omit[x3::no_skip[space]];
+    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 >> x3::attr(decltype(dataPath_::m_nodes)()) >> x3::attr(TrailingSlash::NonPresent) >> x3::eoi |
+    initializePath >> -(absoluteStart >> createPathSuggestions) >> dataNode % '/' >> (trailingSlash >> createPathSuggestions >> (completing | x3::eoi) | (&space_separator | x3::eoi));
 
 auto const dataNodeList_def =
-        -(module) >> createPathSuggestions >> 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 =
-        initializePath >> dataNode % '/' >> '/' >> dataNodeList >> -(&char_('/') >> createPathSuggestions) |
-        initializePath >> x3::attr(decltype(dataPath_::m_nodes)()) >> dataNodeList;
+    initializePath >> dataNode % '/' >> '/' >> dataNodeList >> -(&char_('/') >> createPathSuggestions) |
+    initializePath >> x3::attr(decltype(dataPath_::m_nodes)()) >> dataNodeList;
 
 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 >> x3::attr(decltype(dataPath_::m_nodes)()) >> x3::attr(TrailingSlash::NonPresent) >> 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 >> x3::attr(decltype(schemaPath_::m_nodes)()) >> x3::attr(TrailingSlash::NonPresent) >> x3::eoi |
+    initializePath >> -(absoluteStart >> createPathSuggestions) >> schemaNode % '/' >> (trailingSlash >> createPathSuggestions >> (completing | x3::eoi) | (&space_separator | x3::eoi));
 
 auto const leafPath_def =
-        dataPath;
+    dataPath;
 
 auto const leaf_data_enum_def =
-        +char_;
+    +char_;
 auto const leaf_data_decimal_def =
-        double_;
+    double_;
 
 struct bool_symbol_table : x3::symbols<bool> {
     bool_symbol_table()
     {
-        add
-            ("true", true)
-            ("false", false);
+    add
+        ("true", true)
+        ("false", false);
     }
 } const bool_rule;
 
 auto const leaf_data_bool_def =
-        bool_rule;
+    bool_rule;
 auto const leaf_data_int_def =
-        int_;
+    int_;
 auto const leaf_data_uint_def =
-        uint_;
+    uint_;
 auto const leaf_data_string_def =
-        *char_;
+    *char_;
 
 auto const leaf_data_def =
 x3::expect[
-        leaf_data_enum |
-        leaf_data_decimal |
-        leaf_data_bool |
-        leaf_data_int |
-        leaf_data_uint |
-        leaf_data_string];
+    leaf_data_enum |
+    leaf_data_decimal |
+    leaf_data_bool |
+    leaf_data_int |
+    leaf_data_uint |
+    leaf_data_string];
 
 struct ls_options_table : x3::symbols<LsOption> {
     ls_options_table()
     {
-        add
-            ("--recursive", LsOption::Recursive);
+    add
+        ("--recursive", LsOption::Recursive);
     }
 } const ls_options;
 
 // A "nothing" parser, which is used to indicate we tried to parse a path
 auto const initializePath_def =
-        x3::eps;
+    x3::eps;
 
 auto const ls_def =
-        ls_::name >> *(space_separator >> ls_options) >> -(space_separator >> (dataPathListEnd | dataPath | schemaPath));
+    ls_::name >> *(space_separator >> ls_options) >> -(space_separator >> (dataPathListEnd | dataPath | schemaPath));
 
 auto const cd_def =
-        cd_::name >> space_separator > dataPath;
+    cd_::name >> space_separator > dataPath;
 
 auto const create_def =
-        create_::name >> space_separator > dataPath;
+    create_::name >> space_separator > dataPath;
 
 auto const delete_rule_def =
-        delete_::name >> space_separator > dataPath;
+    delete_::name >> space_separator > dataPath;
 
 auto const get_def =
-        get_::name >> -(space_separator >> (dataPathListEnd | dataPath));
+    get_::name >> -(space_separator >> (dataPathListEnd | dataPath));
 
 auto const set_def =
-        set_::name >> space_separator > leafPath > leaf_data;
+    set_::name >> space_separator > leafPath > leaf_data;
 
 auto const commit_def =
-        commit_::name >> x3::attr(commit_());
+    commit_::name >> x3::attr(commit_());
 
 auto const discard_def =
-        discard_::name >> x3::attr(discard_());
+    discard_::name >> x3::attr(discard_());
 
 auto const createCommandSuggestions_def =
-        x3::eps;
+    x3::eps;
 
 auto const command_def =
-        createCommandSuggestions >> x3::expect[cd | create | delete_rule | set | commit | get | ls | discard];
+    createCommandSuggestions >> x3::expect[cd | create | delete_rule | set | commit | get | ls | discard];
 
 #if __clang__
 #pragma GCC diagnostic pop
@@ -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..a19add2 100644
--- a/tests/ls.cpp
+++ b/tests/ls.cpp
@@ -58,7 +58,7 @@
             SECTION("ls /example:a")
             {
                 SECTION("cwd: /") {}
-                SECTION("cwd: /example:a") {parser.changeNode(dataPath_{Scope::Relative, {dataNode_(module_{"example"}, container_{"a"})}});}
+                SECTION("cwd: /example:a") { parser.changeNode(dataPath_{Scope::Relative, {dataNode_(module_{"example"}, container_{"a"})}}); }
 
                 input = "ls /example:a";
                 expected.m_path = dataPath_{Scope::Absolute, {dataNode_(module_{"example"}, container_{"a"})}};
@@ -67,7 +67,7 @@
             SECTION("ls /")
             {
                 SECTION("cwd: /") {}
-                SECTION("cwd: /example:a") {parser.changeNode(dataPath_{Scope::Relative, {dataNode_(module_{"example"}, container_{"a"})}});}
+                SECTION("cwd: /example:a") { parser.changeNode(dataPath_{Scope::Relative, {dataNode_(module_{"example"}, container_{"a"})}}); }
                 input = "ls /";
                 expected.m_path = dataPath_{Scope::Absolute, {}};
             }
@@ -89,7 +89,7 @@
             SECTION("ls /example:a/a2")
             {
                 SECTION("cwd: /") {}
-                SECTION("cwd: /example:a") {parser.changeNode(dataPath_{Scope::Relative, {dataNode_(module_{"example"}, container_{"a"})}});}
+                SECTION("cwd: /example:a") { parser.changeNode(dataPath_{Scope::Relative, {dataNode_(module_{"example"}, container_{"a"})}}); }
                 input = "ls /example:a/a2";
                 expected.m_path = dataPath_{Scope::Absolute, {dataNode_(module_{"example"}, container_{"a"}),
                                                           dataNode_(container_{"a2"})}};
@@ -112,7 +112,7 @@
             SECTION("ls /example:a/example:a2")
             {
                 SECTION("cwd: /") {}
-                SECTION("cwd: /example:a") {parser.changeNode(dataPath_{Scope::Relative, {dataNode_(module_{"example"}, container_{"a"})}});}
+                SECTION("cwd: /example:a") { parser.changeNode(dataPath_{Scope::Relative, {dataNode_(module_{"example"}, container_{"a"})}}); }
 
                 input = "ls /example:a/example:a2";
                 expected.m_path = dataPath_{Scope::Absolute, {dataNode_(module_{"example"}, container_{"a"}),
@@ -122,7 +122,7 @@
             SECTION("ls --recursive /example:a")
             {
                 SECTION("cwd: /") {}
-                SECTION("cwd: /example:a") {parser.changeNode(dataPath_{Scope::Relative, {dataNode_(module_{"example"}, container_{"a"})}});}
+                SECTION("cwd: /example:a") { parser.changeNode(dataPath_{Scope::Relative, {dataNode_(module_{"example"}, container_{"a"})}}); }
 
                 input = "ls --recursive /example:a";
                 expected.m_options.push_back(LsOption::Recursive);
@@ -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);
diff --git a/tests/sysrepo.cpp b/tests/sysrepo.cpp
index fb9a3c3..a0e791a 100644
--- a/tests/sysrepo.cpp
+++ b/tests/sysrepo.cpp
@@ -12,8 +12,7 @@
 #include "sysrepo_subscription.hpp"
 #include "sysrepo_vars.hpp"
 
-class MockRecorder : public Recorder
-{
+class MockRecorder : public Recorder {
 public:
     MAKE_MOCK3(write, void(const std::string&, const std::string&, const std::string&), override);
 };
diff --git a/tests/utils.cpp b/tests/utils.cpp
index b7ce8fb..e24b0d5 100644
--- a/tests/utils.cpp
+++ b/tests/utils.cpp
@@ -22,5 +22,4 @@
         REQUIRE((filterByPrefix(set, "polivkax") == std::set<std::string>{}));
         REQUIRE((filterByPrefix(set, "co") == std::set<std::string>{"copak", "coze"}));
     }
-
 }