From 73ff84f6a820ac1c6e0fe502692432c8945fd8b0 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Wed, 23 Oct 2019 15:29:16 +0200 Subject: [PATCH 1/7] nix repl: add :edit command This allows to have a repl-centric workflow to working on nixpkgs. Usage: :edit - heuristic that find the package file path :edit - just open the editor on the file path Once invoked, `nix repl` will open $EDITOR on that file path. Once the editor exits, `nix repl` will automatically reload itself. --- src/nix/repl.cc | 57 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/nix/repl.cc b/src/nix/repl.cc index f857b2e89..79f365cdb 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -22,6 +22,7 @@ extern "C" { #include "shared.hh" #include "eval.hh" #include "eval-inline.hh" +#include "attr-path.hh" #include "store-api.hh" #include "common-eval-args.hh" #include "get-drvs.hh" @@ -440,6 +441,7 @@ bool NixRepl::processLine(string line) << " = Bind expression to variable\n" << " :a Add attributes from resulting set to scope\n" << " :b Build derivation\n" + << " :e Open the derivation in $EDITOR\n" << " :i Build derivation, then install result into current profile\n" << " :l Load Nix expression and add it to scope\n" << " :p Evaluate and print expression recursively\n" @@ -466,6 +468,61 @@ bool NixRepl::processLine(string line) reloadFiles(); } + else if (command == ":e" || command == ":edit") { + Value v; + evalString(arg, v); + + std::string filename; + int lineno = 0; + + if (v.type == tPath || v.type == tString) { + PathSet context; + filename = state.coerceToString(noPos, v, context); + lineno = 0; + } else { + // assume it's a derivation + Value * v2; + try { + auto dummyArgs = state.allocBindings(0); + v2 = findAlongAttrPath(state, "meta.position", *dummyArgs, v); + } catch (Error &) { + throw Error("package '%s' has no source location information", arg); + } + + auto pos = state.forceString(*v2); + debug("position is %s", pos); + + auto colon = pos.rfind(':'); + if (colon == std::string::npos) + throw Error("cannot parse meta.position attribute '%s'", pos); + + filename = std::string(pos, 0, colon); + try { + lineno = std::stoi(std::string(pos, colon + 1)); + } catch (std::invalid_argument & e) { + throw Error("cannot parse line number '%s'", pos); + } + + } + + // Open in EDITOR + auto editor = getEnv("EDITOR", "cat"); + auto args = tokenizeString(editor); + if (lineno > 0 && ( + editor.find("emacs") != std::string::npos || + editor.find("nano") != std::string::npos || + editor.find("vim") != std::string::npos)) + args.push_back(fmt("+%d", lineno)); + args.push_back(filename); + editor = args.front(); + args.pop_front(); + runProgram(editor, args); + + // Reload right after exiting the editor + state.resetFileCache(); + reloadFiles(); + } + else if (command == ":t") { Value v; evalString(arg, v); From 207a537343ace1d39ac125059456d832a5237f2e Mon Sep 17 00:00:00 2001 From: zimbatm Date: Wed, 23 Oct 2019 16:48:28 +0200 Subject: [PATCH 2/7] libutil: add editorFor heuristic --- src/libutil/args.cc | 13 +++++++++++++ src/libutil/args.hh | 3 +++ src/nix/edit.cc | 17 +++++------------ src/nix/repl.cc | 11 ++--------- 4 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/libutil/args.cc b/src/libutil/args.cc index 7af2a1bf7..35ec3e4ab 100644 --- a/src/libutil/args.cc +++ b/src/libutil/args.cc @@ -178,6 +178,19 @@ Strings argvToStrings(int argc, char * * argv) return args; } +Strings editorFor(std::string filename, int lineno) +{ + auto editor = getEnv("EDITOR", "cat"); + auto args = tokenizeString(editor); + if (lineno > 0 && ( + editor.find("emacs") != std::string::npos || + editor.find("nano") != std::string::npos || + editor.find("vim") != std::string::npos)) + args.push_back(fmt("+%d", lineno)); + args.push_back(filename); + return args; +} + std::string renderLabels(const Strings & labels) { std::string res; diff --git a/src/libutil/args.hh b/src/libutil/args.hh index ad5fcca39..22702c2d8 100644 --- a/src/libutil/args.hh +++ b/src/libutil/args.hh @@ -190,6 +190,9 @@ public: Strings argvToStrings(int argc, char * * argv); +/* Helper function to generate args that invoke $EDITOR on filename:lineno */ +Strings editorFor(std::string filename, int lineno); + /* Helper function for rendering argument labels. */ std::string renderLabels(const Strings & labels); diff --git a/src/nix/edit.cc b/src/nix/edit.cc index a6169f118..3a27b9cca 100644 --- a/src/nix/edit.cc +++ b/src/nix/edit.cc @@ -59,22 +59,15 @@ struct CmdEdit : InstallableCommand throw Error("cannot parse line number '%s'", pos); } - auto editor = getEnv("EDITOR", "cat"); - - auto args = tokenizeString(editor); - - if (editor.find("emacs") != std::string::npos || - editor.find("nano") != std::string::npos || - editor.find("vim") != std::string::npos) - args.push_back(fmt("+%d", lineno)); - - args.push_back(filename); - stopProgressBar(); + auto args = editorFor(filename, lineno); + execvp(args.front().c_str(), stringsToCharPtrs(args).data()); - throw SysError("cannot run editor '%s'", editor); + std::string command; + for (const auto &arg : args) command += " '" + arg + "'"; + throw SysError("cannot run command%s", command); } }; diff --git a/src/nix/repl.cc b/src/nix/repl.cc index 79f365cdb..d4334cf7f 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -506,15 +506,8 @@ bool NixRepl::processLine(string line) } // Open in EDITOR - auto editor = getEnv("EDITOR", "cat"); - auto args = tokenizeString(editor); - if (lineno > 0 && ( - editor.find("emacs") != std::string::npos || - editor.find("nano") != std::string::npos || - editor.find("vim") != std::string::npos)) - args.push_back(fmt("+%d", lineno)); - args.push_back(filename); - editor = args.front(); + auto args = editorFor(filename, lineno); + auto editor = args.front(); args.pop_front(); runProgram(editor, args); From 59c72497696eaafa294c34699795788d24d68c68 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Wed, 23 Oct 2019 17:21:10 +0200 Subject: [PATCH 3/7] libexpr: add findDerivationFilename extract the derivation to filename:lineno heuristic --- src/libexpr/attr-path.cc | 28 ++++++++++++++++++++++++++++ src/libexpr/attr-path.hh | 5 +++++ src/nix/edit.cc | 23 ++--------------------- src/nix/repl.cc | 23 +---------------------- 4 files changed, 36 insertions(+), 43 deletions(-) diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc index b0f80db32..7a6d8dfd0 100644 --- a/src/libexpr/attr-path.cc +++ b/src/libexpr/attr-path.cc @@ -93,4 +93,32 @@ Value * findAlongAttrPath(EvalState & state, const string & attrPath, } +std::tuple findDerivationFilename(EvalState & state, Value & v, std::string what) +{ + Value * v2; + try { + auto dummyArgs = state.allocBindings(0); + v2 = findAlongAttrPath(state, "meta.position", *dummyArgs, v); + } catch (Error &) { + throw Error("package '%s' has no source location information", what); + } + + auto pos = state.forceString(*v2); + + auto colon = pos.rfind(':'); + if (colon == std::string::npos) + throw Error("cannot parse meta.position attribute '%s'", pos); + + std::string filename(pos, 0, colon); + int lineno; + try { + lineno = std::stoi(std::string(pos, colon + 1)); + } catch (std::invalid_argument & e) { + throw Error("cannot parse line number '%s'", pos); + } + + return std::make_tuple(filename, lineno); +} + + } diff --git a/src/libexpr/attr-path.hh b/src/libexpr/attr-path.hh index 46a341950..dca94cc78 100644 --- a/src/libexpr/attr-path.hh +++ b/src/libexpr/attr-path.hh @@ -4,10 +4,15 @@ #include #include +#include namespace nix { Value * findAlongAttrPath(EvalState & state, const string & attrPath, Bindings & autoArgs, Value & vIn); +/* Heuristic to find the filename and lineno or a derivation. */ +std::tuple findDerivationFilename(EvalState & state, + Value & v, std::string what); + } diff --git a/src/nix/edit.cc b/src/nix/edit.cc index 3a27b9cca..a4aa40bed 100644 --- a/src/nix/edit.cc +++ b/src/nix/edit.cc @@ -36,28 +36,9 @@ struct CmdEdit : InstallableCommand auto v = installable->toValue(*state); - Value * v2; - try { - auto dummyArgs = state->allocBindings(0); - v2 = findAlongAttrPath(*state, "meta.position", *dummyArgs, *v); - } catch (Error &) { - throw Error("package '%s' has no source location information", installable->what()); - } - - auto pos = state->forceString(*v2); - debug("position is %s", pos); - - auto colon = pos.rfind(':'); - if (colon == std::string::npos) - throw Error("cannot parse meta.position attribute '%s'", pos); - - std::string filename(pos, 0, colon); + std::string filename; int lineno; - try { - lineno = std::stoi(std::string(pos, colon + 1)); - } catch (std::invalid_argument & e) { - throw Error("cannot parse line number '%s'", pos); - } + std::tie(filename, lineno) = findDerivationFilename(*state, *v, installable->what()); stopProgressBar(); diff --git a/src/nix/repl.cc b/src/nix/repl.cc index d4334cf7f..5cfb408f7 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -481,28 +481,7 @@ bool NixRepl::processLine(string line) lineno = 0; } else { // assume it's a derivation - Value * v2; - try { - auto dummyArgs = state.allocBindings(0); - v2 = findAlongAttrPath(state, "meta.position", *dummyArgs, v); - } catch (Error &) { - throw Error("package '%s' has no source location information", arg); - } - - auto pos = state.forceString(*v2); - debug("position is %s", pos); - - auto colon = pos.rfind(':'); - if (colon == std::string::npos) - throw Error("cannot parse meta.position attribute '%s'", pos); - - filename = std::string(pos, 0, colon); - try { - lineno = std::stoi(std::string(pos, colon + 1)); - } catch (std::invalid_argument & e) { - throw Error("cannot parse line number '%s'", pos); - } - + std::tie(filename, lineno) = findDerivationFilename(state, v, arg); } // Open in EDITOR From ec448f8bb694b6f9546e49fe6a79b86ff2b2f90a Mon Sep 17 00:00:00 2001 From: zimbatm Date: Mon, 28 Oct 2019 21:22:38 +0100 Subject: [PATCH 4/7] libexpr: findDerivationFilename return Pos instead of tuple --- src/libexpr/attr-path.cc | 8 +++++--- src/libexpr/attr-path.hh | 6 ++---- src/nix/edit.cc | 6 +++--- src/nix/repl.cc | 4 +++- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc index 7a6d8dfd0..c86dbeebb 100644 --- a/src/libexpr/attr-path.cc +++ b/src/libexpr/attr-path.cc @@ -93,7 +93,7 @@ Value * findAlongAttrPath(EvalState & state, const string & attrPath, } -std::tuple findDerivationFilename(EvalState & state, Value & v, std::string what) +Pos findDerivationFilename(EvalState & state, Value & v, std::string what) { Value * v2; try { @@ -110,14 +110,16 @@ std::tuple findDerivationFilename(EvalState & state, Value & v throw Error("cannot parse meta.position attribute '%s'", pos); std::string filename(pos, 0, colon); - int lineno; + unsigned int lineno; try { lineno = std::stoi(std::string(pos, colon + 1)); } catch (std::invalid_argument & e) { throw Error("cannot parse line number '%s'", pos); } - return std::make_tuple(filename, lineno); + Symbol file = state.symbols.create(filename); + + return { file, lineno, 0 }; } diff --git a/src/libexpr/attr-path.hh b/src/libexpr/attr-path.hh index dca94cc78..716e5ba27 100644 --- a/src/libexpr/attr-path.hh +++ b/src/libexpr/attr-path.hh @@ -4,15 +4,13 @@ #include #include -#include namespace nix { Value * findAlongAttrPath(EvalState & state, const string & attrPath, Bindings & autoArgs, Value & vIn); -/* Heuristic to find the filename and lineno or a derivation. */ -std::tuple findDerivationFilename(EvalState & state, - Value & v, std::string what); +/* Heuristic to find the filename and lineno or a nix value. */ +Pos findDerivationFilename(EvalState & state, Value & v, std::string what); } diff --git a/src/nix/edit.cc b/src/nix/edit.cc index a4aa40bed..d0607747d 100644 --- a/src/nix/edit.cc +++ b/src/nix/edit.cc @@ -36,9 +36,9 @@ struct CmdEdit : InstallableCommand auto v = installable->toValue(*state); - std::string filename; - int lineno; - std::tie(filename, lineno) = findDerivationFilename(*state, *v, installable->what()); + Pos pos = findDerivationFilename(*state, *v, installable->what()); + std::string filename(pos.file); + int lineno(pos.line); stopProgressBar(); diff --git a/src/nix/repl.cc b/src/nix/repl.cc index 5cfb408f7..ed67c285f 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -481,7 +481,9 @@ bool NixRepl::processLine(string line) lineno = 0; } else { // assume it's a derivation - std::tie(filename, lineno) = findDerivationFilename(state, v, arg); + Pos pos = findDerivationFilename(state, v, arg); + filename = pos.file; + lineno = pos.line; } // Open in EDITOR From 3774fe55fd6c96e80cc91e13fe0a231ce836ac47 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Mon, 28 Oct 2019 21:36:34 +0100 Subject: [PATCH 5/7] editorFor: take a pos object instead --- src/libutil/args.cc | 8 ++++---- src/libutil/args.hh | 3 ++- src/nix/edit.cc | 4 +--- src/nix/repl.cc | 13 +++++-------- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/libutil/args.cc b/src/libutil/args.cc index 35ec3e4ab..b7baad375 100644 --- a/src/libutil/args.cc +++ b/src/libutil/args.cc @@ -178,16 +178,16 @@ Strings argvToStrings(int argc, char * * argv) return args; } -Strings editorFor(std::string filename, int lineno) +Strings editorFor(Pos pos) { auto editor = getEnv("EDITOR", "cat"); auto args = tokenizeString(editor); - if (lineno > 0 && ( + if (pos.line > 0 && ( editor.find("emacs") != std::string::npos || editor.find("nano") != std::string::npos || editor.find("vim") != std::string::npos)) - args.push_back(fmt("+%d", lineno)); - args.push_back(filename); + args.push_back(fmt("+%d", pos.line)); + args.push_back(pos.file); return args; } diff --git a/src/libutil/args.hh b/src/libutil/args.hh index 22702c2d8..1e29bd4fa 100644 --- a/src/libutil/args.hh +++ b/src/libutil/args.hh @@ -5,6 +5,7 @@ #include #include "util.hh" +#include "nixexpr.hh" namespace nix { @@ -191,7 +192,7 @@ public: Strings argvToStrings(int argc, char * * argv); /* Helper function to generate args that invoke $EDITOR on filename:lineno */ -Strings editorFor(std::string filename, int lineno); +Strings editorFor(Pos pos); /* Helper function for rendering argument labels. */ std::string renderLabels(const Strings & labels); diff --git a/src/nix/edit.cc b/src/nix/edit.cc index d0607747d..553765f13 100644 --- a/src/nix/edit.cc +++ b/src/nix/edit.cc @@ -37,12 +37,10 @@ struct CmdEdit : InstallableCommand auto v = installable->toValue(*state); Pos pos = findDerivationFilename(*state, *v, installable->what()); - std::string filename(pos.file); - int lineno(pos.line); stopProgressBar(); - auto args = editorFor(filename, lineno); + auto args = editorFor(pos); execvp(args.front().c_str(), stringsToCharPtrs(args).data()); diff --git a/src/nix/repl.cc b/src/nix/repl.cc index ed67c285f..683a117f3 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -472,22 +472,19 @@ bool NixRepl::processLine(string line) Value v; evalString(arg, v); - std::string filename; - int lineno = 0; + Pos pos; if (v.type == tPath || v.type == tString) { PathSet context; - filename = state.coerceToString(noPos, v, context); - lineno = 0; + auto filename = state.coerceToString(noPos, v, context); + pos.file = state.symbols.create(filename); } else { // assume it's a derivation - Pos pos = findDerivationFilename(state, v, arg); - filename = pos.file; - lineno = pos.line; + pos = findDerivationFilename(state, v, arg); } // Open in EDITOR - auto args = editorFor(filename, lineno); + auto args = editorFor(pos); auto editor = args.front(); args.pop_front(); runProgram(editor, args); From d407f4d15f86aca585e0edebc5bc74ab8b1bebd1 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Mon, 28 Oct 2019 21:37:22 +0100 Subject: [PATCH 6/7] nix repl: also handle lambda edit --- src/nix/repl.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/nix/repl.cc b/src/nix/repl.cc index 683a117f3..35c7aec66 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -478,6 +478,8 @@ bool NixRepl::processLine(string line) PathSet context; auto filename = state.coerceToString(noPos, v, context); pos.file = state.symbols.create(filename); + } else if (v.type == tLambda) { + pos = v.lambda.fun->pos; } else { // assume it's a derivation pos = findDerivationFilename(state, v, arg); From 9a2505965667267f03a8385926f3b31a47732ed5 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Mon, 28 Oct 2019 21:40:02 +0100 Subject: [PATCH 7/7] findDerivationFilename: add FIXME --- src/libexpr/attr-path.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc index c86dbeebb..06b472d8b 100644 --- a/src/libexpr/attr-path.cc +++ b/src/libexpr/attr-path.cc @@ -103,6 +103,8 @@ Pos findDerivationFilename(EvalState & state, Value & v, std::string what) throw Error("package '%s' has no source location information", what); } + // FIXME: is it possible to extract the Pos object instead of doing this + // toString + parsing? auto pos = state.forceString(*v2); auto colon = pos.rfind(':');