Repository: lapce/lapce (language: rust)

Issue:
Direnv support
## Is your feature request related to a problem? Please describe.
In my projects I always use a flake.nix defined development environment, which provides fixed versions of compiler, debugger and clangd too. In vscode I can preload this environment using the direnv plugin, but I don't know if this is actually possible in lapce.

## Describe the solution you'd like
Automatically load direnv environment if a .direnv file is found in the project's root.



Discussion / hints:
Want to have this functionality too. 
Lets start discussion! Lets go by small steps.
Any ideas how we can implement this? 

I'll start:
- We have plugins like [lapce-rust](https://github.com/lapce/lapce-rust) that work with rust-analyzer binary. I believe it uses `serverPath` config to execute it or downloads its own rust-analyzer if cant find it. 
- So if we want to "override" this setting what we suppose to do?
- There are crates that work with direnv:
  - https://crates.io/crates/quickenv
  - https://crates.io/crates/direnv
 

> * So if we want to "override" this setting what we suppose to do?

You can just configure it in settings, there is no need for overriding 
@panekj it is dynamic. Per project this path will be different. So we need ability to change it "on fly".
One way of doing it is env variables. What if we had ability to use env variables in settings?
p.s. But we want PATH variable to be inherited, not some custom RUST_ANALYZER_BINARY whatever... So i think approach with env variables in settings is not correct...
Is there a way to manipulate Lapce env variables so extensions can search binaries from PATH variable for example? (we need to manipulate any variable, because in `flake.nix` you can set any variable for development you want)
Also is there a way to set extension load order
> Per project this path will be different.

project can have settings defined in `${PROJECT_DIR}/.lapce/settings.toml`, just like standard settings
> project can have settings defined in `${PROJECT_DIR}/.lapce/settings.toml`, just like standard settings

But this feature cannot be used to dynamically load a direnv environment.
> > project can have settings defined in `${PROJECT_DIR}/.lapce/settings.toml`, just like standard settings
> 
> But this feature cannot be used to dynamically load a direnv environment.

I did not say that it can be used in such way

Fix patch:
diff --git a/lapce-app/src/app.rs b/lapce-app/src/app.rs
index 7100f1c8b2..8a4850407c 100644
--- a/lapce-app/src/app.rs
+++ b/lapce-app/src/app.rs
@@ -1,7 +1,7 @@
 #[cfg(target_os = "windows")]
 use std::os::windows::process::CommandExt;
 use std::{
-    io::{BufReader, Read, Write},
+    io::{BufReader, IsTerminal, Read, Write},
     ops::Range,
     path::PathBuf,
     process::Stdio,
@@ -3573,9 +3573,9 @@ pub fn launch() {
             )));
     }
 
-    // if PWD is not set, then we are not being launched via a terminal
-    #[cfg(any(target_os = "macos", target_os = "linux"))]
-    if std::env::var("PWD").is_err() {
+    let stdin = std::io::stdin();
+    if !stdin.is_terminal() {
+        trace!(TraceLevel::INFO, "Loading custom environment from shell");
         load_shell_env();
     }
 
@@ -3770,12 +3770,12 @@ pub fn launch() {
 }
 
 /// Uses a login shell to load the correct shell environment for the current user.
-#[cfg(any(target_os = "macos", target_os = "linux"))]
 fn load_shell_env() {
     use std::process::Command;
 
     use tracing::warn;
 
+    #[cfg(not(windows))]
     let shell = match std::env::var("SHELL") {
         Ok(s) => s,
         Err(error) => {
@@ -3788,9 +3788,16 @@ fn load_shell_env() {
         }
     };
 
+    #[cfg(windows)]
+    let shell = "powershell";
+
     let mut command = Command::new(shell);
 
-    command.args(["--login"]).args(["-c", "printenv"]);
+    #[cfg(not(windows))]
+    command.args(["--login", "-c", "printenv"]);
+
+    #[cfg(windows)]
+    command.args(["{ ls env: | foreach { '{0}={1}' -f $_.Name, $_.Value } }"]);
 
     let env = match command.output() {
         Ok(output) => String::from_utf8(output.stdout).unwrap_or_default(),
@@ -3808,7 +3815,9 @@ fn load_shell_env() {
         .filter_map(|line| line.split_once('='))
         .for_each(|(key, value)| {
             if let Ok(v) = std::env::var(key) {
-                warn!("Overwriting '{key}', previous value: '{v}', new value '{value}'");
+                if v != value {
+                    warn!("Overwriting '{key}', previous value: '{v}', new value '{value}'");
+                }
             };
             std::env::set_var(key, value);
         })
diff --git a/lapce-app/src/app/logging.rs b/lapce-app/src/app/logging.rs
index 4a56133883..1f029a5759 100644
--- a/lapce-app/src/app/logging.rs
+++ b/lapce-app/src/app/logging.rs
@@ -27,7 +27,8 @@ pub(super) fn logging() -> (Handle<Targets>, Option<WorkerGuard>) {
     let log_file_filter_targets = filter::Targets::new()
         .with_target("lapce_app", LevelFilter::DEBUG)
         .with_target("lapce_proxy", LevelFilter::DEBUG)
-        .with_target("lapce_core", LevelFilter::DEBUG);
+        .with_target("lapce_core", LevelFilter::DEBUG)
+        .with_default(LevelFilter::from_level(TraceLevel::INFO));
     let (log_file_filter, reload_handle) =
         reload::Subscriber::new(log_file_filter_targets);
 
diff --git a/lapce-app/src/command.rs b/lapce-app/src/command.rs
index f22d2bc3f9..d812d90dc1 100644
--- a/lapce-app/src/command.rs
+++ b/lapce-app/src/command.rs
@@ -191,6 +191,10 @@ pub enum LapceWorkbenchCommand {
     #[strum(message = "Open Internal UI Inspector")]
     OpenUIInspector,
 
+    #[strum(serialize = "show_env")]
+    #[strum(message = "Show Environment")]
+    ShowEnvironment,
+
     #[strum(serialize = "change_color_theme")]
     #[strum(message = "Change Color Theme")]
     ChangeColorTheme,
diff --git a/lapce-app/src/main_split.rs b/lapce-app/src/main_split.rs
index c518b28ea9..c61cab4e7d 100644
--- a/lapce-app/src/main_split.rs
+++ b/lapce-app/src/main_split.rs
@@ -2832,6 +2832,21 @@ impl MainSplitData {
             }
         }
     }
+
+    pub fn show_env(&self) {
+        let child = self.new_file();
+        if let EditorTabChild::Editor(id) = child {
+            if let Some(editor) = self.editors.editor_untracked(id) {
+                let doc = editor.doc();
+                doc.reload(
+                    Rope::from(
+                        std::env::vars().map(|(k, v)| format!("{k}={v}")).join("\n"),
+                    ),
+                    true,
+                );
+            }
+        }
+    }
 }
 
 fn workspace_edits(edit: &WorkspaceEdit) -> Option<HashMap<Url, Vec<TextEdit>>> {
diff --git a/lapce-app/src/window_tab.rs b/lapce-app/src/window_tab.rs
index 20aade870d..68eb0a0bc0 100644
--- a/lapce-app/src/window_tab.rs
+++ b/lapce-app/src/window_tab.rs
@@ -1212,6 +1212,9 @@ impl WindowTabData {
             OpenUIInspector => {
                 self.common.view_id.get_untracked().inspect();
             }
+            ShowEnvironment => {
+                self.main_split.show_env();
+            }
 
             // ==== Source Control ====
             SourceControlInit => {


Test patch: