Skip to content

Commit

Permalink
repl: Replace editline by readline
Browse files Browse the repository at this point in the history
  • Loading branch information
KaiHa committed Aug 25, 2018
1 parent af4f014 commit 731f4da
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 73 deletions.
1 change: 0 additions & 1 deletion Makefile.config.in
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ SODIUM_LIBS = @SODIUM_LIBS@
LIBLZMA_LIBS = @LIBLZMA_LIBS@
SQLITE3_LIBS = @SQLITE3_LIBS@
LIBBROTLI_LIBS = @LIBBROTLI_LIBS@
EDITLINE_LIBS = @EDITLINE_LIBS@
bash = @bash@
bindir = @bindir@
brotli = @brotli@
Expand Down
3 changes: 0 additions & 3 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,6 @@ PKG_CHECK_MODULES([SQLITE3], [sqlite3 >= 3.6.19], [CXXFLAGS="$SQLITE3_CFLAGS $CX
# Look for libcurl, a required dependency.
PKG_CHECK_MODULES([LIBCURL], [libcurl], [CXXFLAGS="$LIBCURL_CFLAGS $CXXFLAGS"])

# Look for editline, a required dependency.
PKG_CHECK_MODULES([EDITLINE], [libeditline], [CXXFLAGS="$EDITLINE_CFLAGS $CXXFLAGS"])

# Look for libsodium, an optional dependency.
PKG_CHECK_MODULES([SODIUM], [libsodium],
[AC_DEFINE([HAVE_SODIUM], [1], [Whether to use libsodium for cryptography.])
Expand Down
2 changes: 1 addition & 1 deletion src/nix/local.mk
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ nix_SOURCES := $(wildcard $(d)/*.cc)

nix_LIBS = libexpr libmain libstore libutil libformat

nix_LDFLAGS = $(EDITLINE_LIBS) -pthread
nix_LDFLAGS = -lreadline -pthread

$(eval $(call install-symlink, nix, $(bindir)/nix-hash))
101 changes: 33 additions & 68 deletions src/nix/repl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

#include <setjmp.h>

#include <editline.h>
#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>

#include "shared.hh"
#include "eval.hh"
Expand Down Expand Up @@ -47,7 +49,7 @@ struct NixRepl
NixRepl(const Strings & searchPath, nix::ref<Store> store);
~NixRepl();
void mainLoop(const std::vector<std::string> & files);
StringSet completePrefix(string prefix);
StringSet * completePrefix(string prefix);
bool getLine(string & input, const std::string &prompt);
Path getDerivationPath(Value & v);
bool processLine(string line);
Expand Down Expand Up @@ -124,66 +126,29 @@ NixRepl::~NixRepl()

static NixRepl * curRepl; // ugly

static char * completionCallback(char * s, int *match) {
auto possible = curRepl->completePrefix(s);
if (possible.size() == 1) {
*match = 1;
auto *res = strdup(possible.begin()->c_str() + strlen(s));
if (!res) throw Error("allocation failure");
return res;
} else if (possible.size() > 1) {
auto checkAllHaveSameAt = [&](size_t pos) {
auto &first = *possible.begin();
for (auto &p : possible) {
if (p.size() <= pos || p[pos] != first[pos])
return false;
}
return true;
};
size_t start = strlen(s);
size_t len = 0;
while (checkAllHaveSameAt(start + len)) ++len;
if (len > 0) {
*match = 1;
auto *res = strdup(std::string(*possible.begin(), start, len).c_str());
if (!res) throw Error("allocation failure");
return res;
}
}

*match = 0;
return nullptr;
}

static int listPossibleCallback(char *s, char ***avp) {
auto possible = curRepl->completePrefix(s);

if (possible.size() > (INT_MAX / sizeof(char*)))
throw Error("too many completions");

int ac = 0;
char **vp = nullptr;
static char * completionFunc(const char * text, int state)
{
static StringSet * matches = nullptr;

auto check = [&](auto *p) {
if (!p) {
if (vp) {
while (--ac >= 0)
free(vp[ac]);
free(vp);
}
throw Error("allocation failure");
if (state == 0 || !matches) {
if (matches) {
delete matches;
}
matches = curRepl->completePrefix(text);
}
return p;
};

vp = check((char **)malloc(possible.size() * sizeof(char*)));

for (auto & p : possible)
vp[ac++] = check(strdup(p.c_str()));

*avp = vp;
auto it = matches->begin();
while (state > 0 && it != matches->end()) {
it++;
state--;
}

return ac;
if (it == matches->end()) {
return nullptr;
}
else {
return strdup(it->c_str());
}
}


Expand All @@ -200,14 +165,10 @@ void NixRepl::mainLoop(const std::vector<std::string> & files)

// Allow nix-repl specific settings in .inputrc
rl_readline_name = "nix-repl";
rl_completion_entry_function = completionFunc;
createDirs(dirOf(historyFile));
el_hist_size = 1000;
read_history(historyFile.c_str());
// rl_initialize();
// linenoiseSetCompletionCallback(completionCallback);
curRepl = this;
rl_set_complete_func(completionCallback);
rl_set_list_possib_func(listPossibleCallback);

std::string input;

Expand Down Expand Up @@ -247,15 +208,19 @@ bool NixRepl::getLine(string & input, const std::string &prompt)
Finally doFree([&]() { free(s); });
if (!s)
return false;
// TODO: is it really necessary to return true on empty strings???
if (*s) {
add_history(s);
}
input += s;
input += '\n';
return true;
}


StringSet NixRepl::completePrefix(string prefix)
StringSet * NixRepl::completePrefix(string prefix)
{
StringSet completions;
StringSet * completions = new StringSet;

size_t start = prefix.find_last_of(" \n\r\t(){}[]");
std::string prev, cur;
Expand All @@ -275,7 +240,7 @@ StringSet NixRepl::completePrefix(string prefix)
auto prefix2 = std::string(cur, slash + 1);
for (auto & entry : readDirectory(dir == "" ? "/" : dir)) {
if (entry.name[0] != '.' && hasPrefix(entry.name, prefix2))
completions.insert(prev + dir + "/" + entry.name);
completions->insert(prev + dir + "/" + entry.name);
}
} catch (Error &) {
}
Expand All @@ -284,7 +249,7 @@ StringSet NixRepl::completePrefix(string prefix)
StringSet::iterator i = varNames.lower_bound(cur);
while (i != varNames.end()) {
if (string(*i, 0, cur.size()) != cur) break;
completions.insert(prev + *i);
completions->insert(prev + *i);
i++;
}
} else {
Expand All @@ -303,7 +268,7 @@ StringSet NixRepl::completePrefix(string prefix)
for (auto & i : *v.attrs) {
string name = i.name;
if (string(name, 0, cur2.size()) != cur2) continue;
completions.insert(prev + expr + "." + name);
completions->insert(prev + expr + "." + name);
}

} catch (ParseError & e) {
Expand Down

2 comments on commit 731f4da

@thkoch2001
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently Nix just made the opposite move away from readline a year ago because readline is licensed under the GPL:
https://en.wikipedia.org/wiki/GNU_Readline#Choice_of_the_GPL_as_GNU_Readline's_license
NixOS@c5f23f1
NixOS#1356 (comment)

Readlines GPL licence would force nix to move from LGPL to GPL or at least force projects linking against nix and thus transitively to readline to use the GPL license.

@KaiHa
Copy link
Owner Author

@KaiHa KaiHa commented on 731f4da Aug 27, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Readlines GPL licence would force nix to move from LGPL to GPL or at least force projects linking against nix and thus transitively to readline to use the GPL license.

According to this the linked end product would be GPL but the nix source can stay LGPL. Let's wait that they answer to my comment.

Please sign in to comment.