Switch from linenoise to replxx
Change-Id: Id3e04eca8dbd7e68cef080713296fef3fdc683c5
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5656ee8..b8ee606 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -50,6 +50,11 @@
find_package(docopt REQUIRED)
find_package(spdlog REQUIRED)
find_package(Boost REQUIRED)
+find_library(REPLXX_LIBRARY replxx REQUIRED)
+find_path(REPLXX_PATH replxx.hxx)
+if("${REPLXX_PATH}" STREQUAL REPLXX_PATH-NOTFOUND)
+ message(FATAL_ERROR "Cannot find the \"replxx.hxx\" include file for the replxx library.")
+endif()
find_package(PkgConfig)
pkg_check_modules(LIBYANG REQUIRED libyang-cpp>=0.15.111)
@@ -108,7 +113,8 @@
add_executable(netconf-cli
src/main.cpp
)
-target_link_libraries(netconf-cli sysrepoaccess yangschema docopt parser)
+target_link_libraries(netconf-cli sysrepoaccess yangschema docopt parser ${REPLXX_LIBRARY})
+target_include_directories(netconf-cli PRIVATE ${REPLXX_PATH})
if(CMAKE_CXX_FLAGS MATCHES "-stdlib=libc\\+\\+")
target_link_libraries(netconf-cli c++experimental)
else()
diff --git a/src/main.cpp b/src/main.cpp
index 3c973dd..4fe798f 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -5,11 +5,10 @@
* Written by Václav Kubernát <kubervac@fit.cvut.cz>
*
*/
-
#include <docopt.h>
#include <experimental/filesystem>
#include <iostream>
-#include <linenoise.hpp>
+#include <replxx.hxx>
#include "NETCONF_CLI_VERSION.h"
#include "interpreter.hpp"
#include "sysrepo_access.hpp"
@@ -37,24 +36,27 @@
SysrepoAccess datastore("netconf-cli");
Parser parser(datastore.schema());
+ replxx::Replxx lineEditor;
+ lineEditor.set_completion_callback([&parser](const std::string& input, int&) {
+ std::stringstream stream;
+ auto completionsSet = parser.completeCommand(input, stream);
+
+ std::vector<std::string> res;
+ std::transform(completionsSet.begin(), completionsSet.end(), std::back_inserter(res),
+ [input](auto it) { return it; });
+ return res;
+ });
+ lineEditor.set_word_break_characters(" '/[");
while (true) {
- linenoise::SetCompletionCallback([&parser](const char* editBuffer, std::vector<std::string>& completions) {
- std::stringstream stream;
- auto completionsSet = parser.completeCommand(editBuffer, stream);
- std::transform(completionsSet.begin(), completionsSet.end(), std::back_inserter(completions),
- [editBuffer](auto it) { return std::string(editBuffer) + it; });
- });
- linenoise::SetHistoryMaxLen(4);
- linenoise::SetMultiLine(true);
- std::string line;
- auto quit = linenoise::Readline((parser.currentNode() + "> ").c_str(), line);
- if (quit) {
+ auto line = lineEditor.input(parser.currentNode() + "> ");
+ if (!line) {
break;
}
std::locale C_locale("C");
- if (std::all_of(line.begin(), line.end(),
+ std::string_view view{line};
+ if (std::all_of(view.begin(), view.end(),
[C_locale](const auto c) { return std::isspace(c, C_locale);})) {
continue;
}
@@ -66,7 +68,7 @@
std::cerr << ex.what() << std::endl;
}
- linenoise::AddHistory(line.c_str());
+ lineEditor.history_add(line);
}
return 0;
diff --git a/src/parser.cpp b/src/parser.cpp
index 2dbea6b..ba6690d 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -53,7 +53,7 @@
];
x3::phrase_parse(it, line.end(), grammar, space, parsedCommand);
- return filterAndErasePrefix(ctx.m_suggestions, std::string(ctx.m_completionIterator, line.end()));
+ return filterByPrefix(ctx.m_suggestions, std::string(ctx.m_completionIterator, line.end()));
}
void Parser::changeNode(const dataPath_& name)
diff --git a/src/utils.cpp b/src/utils.cpp
index 358bc05..595dc7a 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -67,17 +67,11 @@
return fullNodeName(dataPathToSchemaPath(location), pair);
}
-std::set<std::string> filterAndErasePrefix(const std::set<std::string>& set, const std::string_view prefix)
+std::set<std::string> filterByPrefix(const std::set<std::string>& set, const std::string_view prefix)
{
std::set<std::string> filtered;
std::copy_if(set.begin(), set.end(),
std::inserter(filtered, filtered.end()),
[prefix] (auto it) { return boost::starts_with(it, prefix); });
-
- std::set<std::string> withoutPrefix;
- std::transform(filtered.begin(), filtered.end(),
- std::inserter(withoutPrefix, withoutPrefix.end()),
- [prefix] (auto it) { boost::erase_first(it, prefix); return it; });
- return withoutPrefix;
+ return filtered;
}
-
diff --git a/src/utils.hpp b/src/utils.hpp
index 38db0af..dcaefb3 100644
--- a/src/utils.hpp
+++ b/src/utils.hpp
@@ -19,7 +19,5 @@
std::string leafDataTypeToString(yang::LeafDataTypes type);
std::string fullNodeName(const schemaPath_& location, const ModuleNodePair& pair);
std::string fullNodeName(const dataPath_& location, const ModuleNodePair& pair);
-/** Returns a subset of the original set with only the strings starting with prefix
- * and with the actual prefix deleted from the string
- */
-std::set<std::string> filterAndErasePrefix(const std::set<std::string>& set, const std::string_view prefix);
+/** Returns a subset of the original set with only the strings starting with prefix */
+std::set<std::string> filterByPrefix(const std::set<std::string>& set, const std::string_view prefix);
diff --git a/tests/command_completion.cpp b/tests/command_completion.cpp
index f3cc868..21410c8 100644
--- a/tests/command_completion.cpp
+++ b/tests/command_completion.cpp
@@ -32,13 +32,13 @@
SECTION("c")
{
input = "c";
- expected = {"d", "ommit", "reate"};
+ expected = {"cd", "commit", "create"};
}
SECTION("d")
{
input = "d";
- expected = {"elete", "iscard"};
+ expected = {"delete", "discard"};
}
SECTION("x")
@@ -51,13 +51,13 @@
{
input = "cd";
// TODO: depending on how Readline works, this will have to be changed to include a space
- expected = {""};
+ expected = {"cd"};
}
SECTION("create")
{
input = "create";
- expected = {""};
+ expected = {"create"};
}
REQUIRE(parser.completeCommand(input, errorStream) == expected);
diff --git a/tests/path_completion.cpp b/tests/path_completion.cpp
index 7a9090b..e0faf15 100644
--- a/tests/path_completion.cpp
+++ b/tests/path_completion.cpp
@@ -44,19 +44,19 @@
SECTION("ls e")
{
input = "ls e";
- expected = {"xample:ano", "xample:anoda", "xample:bota", "xample:list", "xample:twoKeyList"};
+ expected = {"example:ano", "example:anoda", "example:bota", "example:list", "example:twoKeyList"};
}
SECTION("ls example:ano")
{
input = "ls example:ano";
- expected = {"", "da"};
+ expected = {"example:ano", "example:anoda"};
}
SECTION("ls example:ano/example:a")
{
input = "ls example:ano/example:a";
- expected = {"2"};
+ expected = {"example:a2"};
}
SECTION("ls x")
@@ -74,13 +74,13 @@
SECTION("ls /e")
{
input = "ls /e";
- expected = {"xample:ano", "xample:anoda", "xample:bota", "xample:list", "xample:twoKeyList"};
+ expected = {"example:ano", "example:anoda", "example:bota", "example:list", "example:twoKeyList"};
}
SECTION("ls /s")
{
input = "ls /s";
- expected = {"econd:amelie"};
+ expected = {"second:amelie"};
}
SECTION("ls /example:list[number=3]/")
@@ -92,7 +92,7 @@
SECTION("ls /example:list[number=3]/c")
{
input = "ls /example:list[number=3]/e";
- expected = {"xample:contInList"};
+ expected = {"example:contInList"};
}
SECTION("ls /example:list[number=3]/a")
@@ -107,7 +107,7 @@
SECTION("cd example:lis")
{
input = "cd example:lis";
- expected = {"t"};
+ expected = {"example:list"};
}
SECTION("cd example:list[")
@@ -119,13 +119,13 @@
SECTION("cd example:list[numb")
{
input = "cd example:list[numb";
- expected = {"er="};
+ expected = {"number="};
}
SECTION("cd example:list[number")
{
input = "cd example:list[number";
- expected = {"="};
+ expected = {"number="};
}
SECTION("cd example:list[number=12")
@@ -137,7 +137,7 @@
SECTION("cd example:list[number=12]")
{
input = "cd example:list[number=12]";
- expected = {"/"};
+ expected = {"]/"};
}
SECTION("cd example:twoKeyList[")
@@ -167,7 +167,7 @@
SECTION("cd example:twoKeyList[name=\"AHOJ\"][number=123]")
{
input = "cd example:twoKeyList[name=\"AHOJ\"][number=123]";
- expected = {"/"};
+ expected = {"]/"};
}
}
diff --git a/tests/utils.cpp b/tests/utils.cpp
index 62e4c23..b7ce8fb 100644
--- a/tests/utils.cpp
+++ b/tests/utils.cpp
@@ -11,17 +11,16 @@
TEST_CASE("utils")
{
- SECTION("filterAndErasePrefix")
+ SECTION("filterByPrefix")
{
std::set<std::string> set{"ahoj", "coze", "copak", "aha", "polivka"};
- REQUIRE((filterAndErasePrefix(set, "a") == std::set<std::string>{"hoj", "ha"}));
- REQUIRE((filterAndErasePrefix(set, "ah") == std::set<std::string>{"oj", "a"}));
- REQUIRE((filterAndErasePrefix(set, "aho") == std::set<std::string>{"j"}));
- REQUIRE((filterAndErasePrefix(set, "polivka") == std::set<std::string>{""}));
- REQUIRE((filterAndErasePrefix(set, "polivka") == std::set<std::string>{""}));
- REQUIRE((filterAndErasePrefix(set, "polivkax") == std::set<std::string>{}));
- REQUIRE((filterAndErasePrefix(set, "co") == std::set<std::string>{"pak", "ze"}));
+ REQUIRE((filterByPrefix(set, "a") == std::set<std::string>{"ahoj", "aha"}));
+ REQUIRE((filterByPrefix(set, "ah") == std::set<std::string>{"ahoj", "aha"}));
+ REQUIRE((filterByPrefix(set, "aho") == std::set<std::string>{"ahoj"}));
+ REQUIRE((filterByPrefix(set, "polivka") == std::set<std::string>{"polivka"}));
+ REQUIRE((filterByPrefix(set, "polivkax") == std::set<std::string>{}));
+ REQUIRE((filterByPrefix(set, "co") == std::set<std::string>{"copak", "coze"}));
}
}