Skip to content

Commit

Permalink
system/nix: switch to Lix
Browse files Browse the repository at this point in the history
This might be temporary, it might not. I still prefer the functionality of nix-super but my repo never explicitly relied on it other than default flake anyway. If the eval time improvements are good, I will switch.
  • Loading branch information
NotAShelf committed Jul 7, 2024
1 parent bb7fa56 commit 32a45e3
Show file tree
Hide file tree
Showing 2 changed files with 307 additions and 5 deletions.
23 changes: 18 additions & 5 deletions modules/core/common/system/nix/overlays/default.nix
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
{
inputs',
config,
pkgs,
lib,
...
}: {
}: let
inherit (lib.trivial) const;
in {
# Overlays are by far the most obscure and annoying feature of Nix, and if you have
# interacted with me on a personal level before, you will find that I actively discourage
# using them. The below section contains my personal overlays, which are used to add
Expand All @@ -16,7 +18,17 @@
nixSchemas = inputs'.nixSchemas.packages.default;
})

(final: prev: {
(const (prev: {
lix = prev.lix.overrideAttrs (old: {
patches = [./patches/0001-nix-default-flake.patch];

postInstall =
(old.postInstall or "")
+ ''
ln -s $out/bin/nix $out/bin/lix
'';
});

# nixos-rebuild provides its own nix package, which is not the same as the one
# we use in the system closure - which causes an extra Nix package to be added
# even if it's not the one we need want.
Expand All @@ -29,15 +41,16 @@
zsh = prev.zsh.overrideAttrs (old: {
patches = [
./patches/0001-zsh-globquote.patch
./patches/0001-zsh-completion-remote-files.patch
];

configureFlags = old.configureFlags or [] ++ ["--disable-site-fndir" "--without-tcsetpgrp"];
configureFlags = (old.configureFlags or []) ++ ["--disable-site-fndir" "--without-tcsetpgrp"];
postConfigure =
(old.postConfigure or "")
+ ''
sed -i -e '/^name=zsh\/newuser/d' config.modules
'';
});
})
}))
];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,289 @@
diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc
index ab0e4fd1c..e56c59712 100644
--- a/src/libcmd/installables.cc
+++ b/src/libcmd/installables.cc
@@ -37,6 +37,23 @@ void completeFlakeInputPath(
}
}

+std::string InstallablesSettings::getDefaultFlake(std::string_view url)
+{
+ std::string res = defaultFlake;
+ if (res == "") {
+ throw UsageError("don't know how to handle installable '%s' without flake URL, because the option 'default-flake' is not set", url);
+ }
+ return res;
+}
+
+InstallablesSettings installablesSettings;
+
+static GlobalConfig::Register rInstallablesSettings(&installablesSettings);
+
+const static std::regex attrPathRegex(
+ R"((?:[a-zA-Z0-9_"-][a-zA-Z0-9_".-]*(?:\^((\*)|([a-z]+(,[a-z]+)*)))?))",
+ std::regex::ECMAScript);
+
MixFlakeOptions::MixFlakeOptions()
{
auto category = "Common flake-related options";
@@ -276,20 +293,34 @@ void completeFlakeRefWithFragment(
/* Look for flake output attributes that match the
prefix. */
try {
+ bool isAttrPath = std::regex_match(prefix.begin(), prefix.end(), attrPathRegex);
auto hash = prefix.find('#');
+
if (hash == std::string::npos) {
- completeFlakeRef(completions, evalState->store, prefix);
- } else {
+ }
+
+ if (isAttrPath || hash != std::string::npos) {
completions.setType(AddCompletions::Type::Attrs);

- auto fragment = prefix.substr(hash + 1);
+ auto fragment =
+ isAttrPath
+ ? prefix
+ : prefix.substr(hash + 1);
+
std::string prefixRoot = "";
if (fragment.starts_with(".")){
fragment = fragment.substr(1);
prefixRoot = ".";
}
- auto flakeRefS = std::string(prefix.substr(0, hash));
- auto flakeRef = parseFlakeRef(expandTilde(flakeRefS), absPath("."));
+ auto flakeRefS =
+ isAttrPath
+ ? std::string(installablesSettings.getDefaultFlake(prefix))
+ : expandTilde(std::string(prefix.substr(0, hash)));
+
+ // FIXME: do tilde expansion.
+ auto flakeRef = parseFlakeRef(
+ flakeRefS,
+ isAttrPath ? std::optional<std::string>{} : absPath("."));

auto evalCache = openEvalCache(*evalState,
std::make_shared<flake::LockedFlake>(lockFlake(*evalState, flakeRef, lockFlags)));
@@ -299,6 +330,7 @@ void completeFlakeRefWithFragment(
if (prefixRoot == "."){
attrPathPrefixes.clear();
}
+
/* Complete 'fragment' relative to all the
attrpath prefixes as well as the root of the
flake. */
@@ -323,7 +355,13 @@ void completeFlakeRefWithFragment(
auto attrPath2 = (*attr)->getAttrPath(attr2);
/* Strip the attrpath prefix. */
attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size());
- completions.add(flakeRefS + "#" + prefixRoot + concatStringsSep(".", evalState->symbols.resolve(attrPath2)));
+
+ std::string resolvedAttrPath2 = prefixRoot + concatStringsSep(".", evalState->symbols.resolve(attrPath2));
+
+ if (isAttrPath)
+ completions.add(resolvedAttrPath2);
+ else
+ completions.add(flakeRefS + "#" + resolvedAttrPath2);
}
}
}
@@ -360,7 +398,7 @@ void completeFlakeRef(AddCompletions & completions, ref<Store> store, std::strin
if (!prefix.starts_with("flake:") && from.starts_with("flake:")) {
std::string from2(from, 6);
if (from2.starts_with(prefix))
- completions.add(from2);
+ completions.add(from2 + "#");
} else {
if (from.starts_with(prefix))
completions.add(from);
@@ -475,7 +513,13 @@ Installables SourceExprCommand::parseInstallables(
}

try {
- auto [flakeRef, fragment] = parseFlakeRefWithFragment(std::string { prefix }, absPath("."));
+ bool isAttrPath = std::regex_match(std::string { prefix }, attrPathRegex);
+
+ auto [flakeRef, fragment] =
+ isAttrPath
+ ? std::pair { parseFlakeRef(installablesSettings.getDefaultFlake(s), {}), std::string { prefix } }
+ : parseFlakeRefWithFragment(std::string { prefix }, absPath("."));
+
result.push_back(make_ref<InstallableFlake>(
this,
getEvalState(),
diff --git a/src/libcmd/installables.hh b/src/libcmd/installables.hh
index 95e8841ca..3e43b859d 100644
--- a/src/libcmd/installables.hh
+++ b/src/libcmd/installables.hh
@@ -12,6 +12,16 @@

namespace nix {

+struct InstallablesSettings : Config
+{
+ Setting<std::string> defaultFlake{this, "", "default-flake",
+ "The default flake URL when using the command line interface"};
+
+ std::string getDefaultFlake(std::string_view url);
+};
+
+extern InstallablesSettings installablesSettings;
+
struct DrvInfo;

enum class Realise {
diff --git a/src/nix/search.cc b/src/nix/search.cc
index 97ef1375e..41d6f1495 100644
--- a/src/nix/search.cc
+++ b/src/nix/search.cc
@@ -23,8 +23,9 @@ std::string wrap(std::string prefix, std::string s)
return concatStrings(prefix, s, ANSI_NORMAL);
}

-struct CmdSearch : InstallableValueCommand, MixJSON
+struct CmdSearch : SourceExprCommand, MixJSON
{
+ std::string _installable{installablesSettings.defaultFlake};
std::vector<std::string> res;
std::vector<std::string> excludeRes;

@@ -40,6 +41,24 @@ struct CmdSearch : InstallableValueCommand, MixJSON
excludeRes.push_back(s);
}},
});
+
+ bool hasInstallable = false;
+
+ addFlag({
+ .longName = "installable",
+ .shortName = 'i',
+ .description = "Search within this installable",
+ .labels = {"installable"},
+ .handler = {[this, &hasInstallable](std::string ss) {
+ hasInstallable = true;
+ _installable = ss;
+ }},
+ .completer = completePath
+ });
+
+ if (hasInstallable && (file || expr)) {
+ throw UsageError("'--installable' cannot be used together with '--file' or '--expr'");
+ }
}

std::string description() override
@@ -62,14 +81,21 @@ struct CmdSearch : InstallableValueCommand, MixJSON
};
}

- void run(ref<Store> store, ref<InstallableValue> installable) override
+ void run(ref<Store> store) override
{
+ if (_installable == "" && ! file && ! expr) {
+ throw UsageError("nothing to search from, set 'default-flake' option or specify one of '--installable', '--file', '--expr'");
+ }
+
settings.readOnlyMode = true;
evalSettings.enableImportFromDerivation.setDefault(false);

+ auto installable = InstallableValue::require(
+ parseInstallable(store, (file || expr) ? "" : _installable));
+
// Recommend "^" here instead of ".*" due to differences in resulting highlighting
if (res.empty())
- throw UsageError("Must provide at least one regex! To match all packages, use '%s'.", "nix search <installable> ^");
+ throw UsageError("Must provide at least one regex! To match all packages, use '%s'.", "nix search ^");

std::vector<std::regex> regexes;
std::vector<std::regex> excludeRegexes;
diff --git a/tests/functional/completions.sh b/tests/functional/completions.sh
index d3d5bbd48..a5ab90220 100644
--- a/tests/functional/completions.sh
+++ b/tests/functional/completions.sh
@@ -38,8 +38,8 @@ EOF
[[ "$(NIX_GET_COMPLETIONS=2 nix flake metad)" == $'normal\nmetadata\t' ]]

# Filename completion
-[[ "$(NIX_GET_COMPLETIONS=2 nix build ./f)" == $'filenames\n./foo\t' ]]
-[[ "$(NIX_GET_COMPLETIONS=2 nix build ./nonexistent)" == $'filenames' ]]
+#[[ "$(NIX_GET_COMPLETIONS=2 nix build ./f)" == $'filenames\n./foo\t' ]]
+#[[ "$(NIX_GET_COMPLETIONS=2 nix build ./nonexistent)" == $'filenames' ]]

# Input override completion
[[ "$(NIX_GET_COMPLETIONS=4 nix build ./foo --override-input '')" == $'normal\na\t' ]]
diff --git a/tests/functional/flakes/flakes.sh b/tests/functional/flakes/flakes.sh
index 3ef518b23..8b88dbb8f 100644
--- a/tests/functional/flakes/flakes.sh
+++ b/tests/functional/flakes/flakes.sh
@@ -116,7 +116,7 @@ nix build -o $TEST_ROOT/result flake1#foo
[[ -e $TEST_ROOT/result/hello ]]

# Test packages.default.
-nix build -o $TEST_ROOT/result flake1
+nix build -o $TEST_ROOT/result flake1#
[[ -e $TEST_ROOT/result/hello ]]

nix build -o $TEST_ROOT/result $flake1Dir
diff --git a/tests/functional/search.sh b/tests/functional/search.sh
index d9c7a75da..35f665db6 100644
--- a/tests/functional/search.sh
+++ b/tests/functional/search.sh
@@ -3,43 +3,42 @@ source common.sh
clearStore
clearCache

-(( $(nix search -f search.nix '' hello | wc -l) > 0 ))
+(( $(nix search -f search.nix hello | wc -l) > 0 ))

# Check descriptions are searched
-(( $(nix search -f search.nix '' broken | wc -l) > 0 ))
+(( $(nix search -f search.nix broken | wc -l) > 0 ))

# Check search that matches nothing
-(( $(nix search -f search.nix '' nosuchpackageexists | wc -l) == 0 ))
+(( $(nix search -f search.nix nosuchpackageexists | wc -l) == 0 ))

# Search for multiple arguments
-(( $(nix search -f search.nix '' hello empty | wc -l) == 2 ))
+(( $(nix search -f search.nix hello empty | wc -l) == 2 ))

# Multiple arguments will not exist
-(( $(nix search -f search.nix '' hello broken | wc -l) == 0 ))
+(( $(nix search -f search.nix hello broken | wc -l) == 0 ))

# No regex should return an error
-(( $(nix search -f search.nix '' | wc -l) == 0 ))
+(( $(nix search -f search.nix | wc -l) == 0 ))

## Search expressions

-# Check that empty search string matches all
-nix search -f search.nix '' ^ | grepQuiet foo
-nix search -f search.nix '' ^ | grepQuiet bar
-nix search -f search.nix '' ^ | grepQuiet hello
+nix search -f search.nix ^ | grepQuiet foo
+nix search -f search.nix ^ | grepQuiet bar
+nix search -f search.nix ^ | grepQuiet hello

## Tests for multiple regex/match highlighting

e=$'\x1b' # grep doesn't support \e, \033 or even \x1b
# Multiple overlapping regexes
-(( $(nix search -f search.nix '' 'oo' 'foo' 'oo' | grep -c "$e\[32;1mfoo$e\\[0;1m") == 1 ))
-(( $(nix search -f search.nix '' 'broken b' 'en bar' | grep -c "$e\[32;1mbroken bar$e\\[0m") == 1 ))
+(( $(nix search -f search.nix 'oo' 'foo' 'oo' | grep -c "$e\[32;1mfoo$e\\[0;1m") == 1 ))
+(( $(nix search -f search.nix 'broken b' 'en bar' | grep -c "$e\[32;1mbroken bar$e\\[0m") == 1 ))

# Multiple matches
# Searching for 'o' should yield the 'o' in 'broken bar', the 'oo' in foo and 'o' in hello
-(( $(nix search -f search.nix '' 'o' | grep -Eoc "$e\[32;1mo{1,2}$e\[(0|0;1)m") == 3 ))
+(( $(nix search -f search.nix 'o' | grep -Eoc "$e\[32;1mo{1,2}$e\[(0|0;1)m") == 3 ))
# Searching for 'b' should yield the 'b' in bar and the two 'b's in 'broken bar'
# NOTE: This does not work with `grep -c` because it counts the two 'b's in 'broken bar' as one matched line
-(( $(nix search -f search.nix '' 'b' | grep -Eo "$e\[32;1mb$e\[(0|0;1)m" | wc -l) == 3 ))
+(( $(nix search -f search.nix 'b' | grep -Eo "$e\[32;1mb$e\[(0|0;1)m" | wc -l) == 3 ))

## Tests for --exclude
(( $(nix search -f search.nix ^ -e hello | grep -c hello) == 0 ))

1 comment on commit 32a45e3

@itslychee
Copy link
Contributor

Choose a reason for hiding this comment

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

image

Please sign in to comment.