From 1a6d7a3af466dad946e6fc0e969927a8f246c13a Mon Sep 17 00:00:00 2001
From: Goldstein <root@goldstein.rs>
Date: Mon, 15 Jul 2024 23:28:55 +0300
Subject: [PATCH] src/libcmd/repl.cc: avoid unneeded reload after :e

If `:edit`ing a store path, don't reload repl afterwards
to avoid losing local variables: store is immutable,
so "editing" a store path is always just viewing it.

Resolves: https://git.lix.systems/lix-project/lix/issues/341
Change-Id: I3747f75ce26e0595e953069c39ddc3ee80699718
---
 doc/manual/change-authors.yml         |  5 +++++
 doc/manual/rl-next/repl-edit-store.md | 11 +++++++++++
 src/libcmd/repl.cc                    |  9 ++++++---
 tests/functional/repl.sh              | 27 +++++++++++++++++++++++++++
 4 files changed, 49 insertions(+), 3 deletions(-)
 create mode 100644 doc/manual/rl-next/repl-edit-store.md

diff --git a/doc/manual/change-authors.yml b/doc/manual/change-authors.yml
index f56a1e6fb..775149180 100644
--- a/doc/manual/change-authors.yml
+++ b/doc/manual/change-authors.yml
@@ -57,6 +57,11 @@ ericson:
   display_name: John Ericson
   github: ericson2314
 
+goldstein:
+  display_name: goldstein
+  forgejo: goldstein
+  github: GoldsteinE
+
 horrors:
   display_name: eldritch horrors
   forgejo: pennae
diff --git a/doc/manual/rl-next/repl-edit-store.md b/doc/manual/rl-next/repl-edit-store.md
new file mode 100644
index 000000000..e93bde07d
--- /dev/null
+++ b/doc/manual/rl-next/repl-edit-store.md
@@ -0,0 +1,11 @@
+---
+synopsis: "`:edit`ing a file in Nix store no longer reloads the repl"
+issues: [fj#341]
+cls: [1620]
+category: Improvements
+credits: [goldstein]
+---
+
+Calling `:edit` from the repl now only reloads if the file being edited was outside of Nix store.
+That means that all the local variables are now preserved across `:edit`s of store paths.
+This is always safe because the store is read-only.
diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc
index 635b54958..5086e9999 100644
--- a/src/libcmd/repl.cc
+++ b/src/libcmd/repl.cc
@@ -652,9 +652,12 @@ ProcessLineResult NixRepl::processLine(std::string line)
         // using runProgram2 to allow editors to display their UI
         runProgram2(RunOptions { .program = editor, .searchPath = true, .args = args }).wait();
 
-        // Reload right after exiting the editor
-        state->resetFileCache();
-        reloadFiles();
+        // Reload right after exiting the editor if path is not in store
+        // Store is immutable, so there could be no changes, so there's no need to reload
+        if (!state->store->isInStore(path.resolveSymlinks().path.abs())) {
+            state->resetFileCache();
+            reloadFiles();
+        }
     }
 
     else if (command == ":t") {
diff --git a/tests/functional/repl.sh b/tests/functional/repl.sh
index cd56b4d92..22c69e20b 100644
--- a/tests/functional/repl.sh
+++ b/tests/functional/repl.sh
@@ -244,3 +244,30 @@ testReplResponseNoRegex '
   y = { a = 1; };
 }
 '
+
+# Test that editing a store path does not reload...
+echo '{ identity = a: a; }' > repl-test.nix
+repl_test_store="$(nix-store --add repl-test.nix)"
+EDITOR=true testReplResponseNoRegex "
+a = ''test string that we'll grep later''
+:l $repl_test_store
+:e identity
+a
+" "test string that we'll grep later"
+
+# ...even through symlinks
+ln -s "$repl_test_store" repl-test-link.nix
+EDITOR=true testReplResponseNoRegex "
+a = ''test string that we'll grep later''
+:l repl-test-link.nix
+:e identity
+a
+" "test string that we'll grep later"
+
+# Test that editing a local file does reload
+EDITOR=true testReplResponseNoRegex "
+a = ''test string that we'll grep later''
+:l repl-test.nix
+:e identity
+a
+" "undefined variable"