about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/bootstrap/src/core/build_steps/setup.rs182
-rw-r--r--src/bootstrap/src/core/build_steps/setup/tests.rs9
-rw-r--r--src/bootstrap/src/core/builder.rs4
-rw-r--r--src/bootstrap/src/core/config/flags.rs3
-rw-r--r--src/etc/completions/x.py.sh2
-rw-r--r--src/etc/rust_analyzer_eglot.el50
6 files changed, 161 insertions, 89 deletions
diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs
index 0ee2cb451f3..7fbc5de06df 100644
--- a/src/bootstrap/src/core/build_steps/setup.rs
+++ b/src/bootstrap/src/core/build_steps/setup.rs
@@ -35,21 +35,6 @@ pub enum Profile {
 
 static PROFILE_DIR: &str = "src/bootstrap/defaults";
 
-/// A list of historical hashes of `src/etc/rust_analyzer_settings.json`.
-/// New entries should be appended whenever this is updated so we can detect
-/// outdated vs. user-modified settings files.
-static SETTINGS_HASHES: &[&str] = &[
-    "ea67e259dedf60d4429b6c349a564ffcd1563cf41c920a856d1f5b16b4701ac8",
-    "56e7bf011c71c5d81e0bf42e84938111847a810eee69d906bba494ea90b51922",
-    "af1b5efe196aed007577899db9dae15d6dbc923d6fa42fa0934e68617ba9bbe0",
-    "3468fea433c25fff60be6b71e8a215a732a7b1268b6a83bf10d024344e140541",
-    "47d227f424bf889b0d899b9cc992d5695e1b78c406e183cd78eafefbe5488923",
-    "b526bd58d0262dd4dda2bff5bc5515b705fb668a46235ace3e057f807963a11a",
-    "828666b021d837a33e78d870b56d34c88a5e2c85de58b693607ec574f0c27000",
-    "811fb3b063c739d261fd8590dd30242e117908f5a095d594fa04585daa18ec4d",
-];
-static RUST_ANALYZER_SETTINGS: &str = include_str!("../../../../etc/rust_analyzer_settings.json");
-
 impl Profile {
     fn include_path(&self, src_path: &Path) -> PathBuf {
         PathBuf::from(format!("{}/{PROFILE_DIR}/config.{}.toml", src_path.display(), self))
@@ -533,46 +518,123 @@ undesirable, simply delete the `pre-push` file from .git/hooks."
     Ok(())
 }
 
-/// Sets up or displays `src/etc/rust_analyzer_settings.json`
-#[derive(Clone, Debug, Eq, PartialEq, Hash)]
-pub struct Vscode;
+/// Handles editor-specific setup differences
+#[derive(Clone, Debug, Eq, PartialEq)]
+enum EditorKind {
+    Vscode,
+    Emacs,
+}
 
-impl Step for Vscode {
-    type Output = ();
-    const DEFAULT: bool = true;
-    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.alias("vscode")
+impl EditorKind {
+    /// A list of historical hashes of each LSP settings file
+    /// New entries should be appended whenever this is updated so we can detect
+    /// outdated vs. user-modified settings files.
+    fn hashes(&self) -> Vec<&str> {
+        match self {
+            EditorKind::Vscode => vec![
+                "ea67e259dedf60d4429b6c349a564ffcd1563cf41c920a856d1f5b16b4701ac8",
+                "56e7bf011c71c5d81e0bf42e84938111847a810eee69d906bba494ea90b51922",
+                "af1b5efe196aed007577899db9dae15d6dbc923d6fa42fa0934e68617ba9bbe0",
+                "3468fea433c25fff60be6b71e8a215a732a7b1268b6a83bf10d024344e140541",
+                "47d227f424bf889b0d899b9cc992d5695e1b78c406e183cd78eafefbe5488923",
+                "b526bd58d0262dd4dda2bff5bc5515b705fb668a46235ace3e057f807963a11a",
+                "828666b021d837a33e78d870b56d34c88a5e2c85de58b693607ec574f0c27000",
+                "811fb3b063c739d261fd8590dd30242e117908f5a095d594fa04585daa18ec4d",
+            ],
+            EditorKind::Emacs => vec![
+                "51068d4747a13732440d1a8b8f432603badb1864fa431d83d0fd4f8fa57039e0",
+                "d29af4d949bbe2371eac928a3c31cf9496b1701aa1c45f11cd6c759865ad5c45",
+            ],
+        }
     }
-    fn make_run(run: RunConfig<'_>) {
-        if run.builder.config.dry_run() {
-            return;
+
+    fn settings_path(&self, config: &Config) -> PathBuf {
+        config.src.join(self.settings_short_path())
+    }
+
+    fn settings_short_path(&self) -> PathBuf {
+        self.settings_folder().join(match self {
+            EditorKind::Vscode => "settings.json",
+            EditorKind::Emacs => ".dir-locals.el",
+        })
+    }
+
+    fn settings_folder(&self) -> PathBuf {
+        match self {
+            EditorKind::Vscode => PathBuf::new().join(".vscode"),
+            EditorKind::Emacs => PathBuf::new(),
         }
-        if let [cmd] = &run.paths[..] {
-            if cmd.assert_single_path().path.as_path().as_os_str() == "vscode" {
-                run.builder.ensure(Vscode);
-            }
+    }
+
+    fn settings_template(&self) -> &str {
+        match self {
+            EditorKind::Vscode => include_str!("../../../../etc/rust_analyzer_settings.json"),
+            EditorKind::Emacs => include_str!("../../../../etc/rust_analyzer_eglot.el"),
         }
     }
-    fn run(self, builder: &Builder<'_>) -> Self::Output {
-        let config = &builder.config;
-        if config.dry_run() {
-            return;
+
+    fn backup_extension(&self) -> &str {
+        match self {
+            EditorKind::Vscode => "json.bak",
+            EditorKind::Emacs => "el.bak",
         }
-        while !t!(create_vscode_settings_maybe(config)) {}
     }
 }
 
-/// Create a `.vscode/settings.json` file for rustc development, or just print it
+/// Helper macro for implementing the necessary Step to support editor LSP setup
+/// The first argument must match the argument set up in `flags.rs`
+/// The second argument must match the name of some `EditorKind` variant
+/// After using the macro, the editor needs to be registered in `builder.rs` with describe!()
+macro_rules! impl_editor_support {
+    ( $($editor:ident, $kind:ident),+ ) => {$(
+        #[doc = concat!(" Sets up or displays the LSP config for ", stringify!($editor), ".")]
+        #[derive(Clone, Debug, Eq, PartialEq, Hash)]
+        pub struct $kind;
+
+        impl Step for $kind {
+            type Output = ();
+            const DEFAULT: bool = true;
+            fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+                run.alias(stringify!($editor))
+            }
+            fn make_run(run: RunConfig<'_>) {
+                if run.builder.config.dry_run() {
+                    return;
+                }
+                if let [cmd] = &run.paths[..] {
+                    if cmd.assert_single_path().path.as_path().as_os_str() == stringify!($editor) {
+                        run.builder.ensure($kind);
+                    }
+                }
+            }
+            fn run(self, builder: &Builder<'_>) -> Self::Output {
+                let config = &builder.config;
+                if config.dry_run() {
+                    return;
+                }
+                while !t!(create_editor_settings_maybe(config, EditorKind::$kind)) {}
+            }
+        }
+    )+};
+}
+
+impl_editor_support!(vscode, Vscode);
+impl_editor_support!(emacs, Emacs);
+
+/// Create the recommended editor LSP config file for rustc development, or just print it
 /// If this method should be re-called, it returns `false`.
-fn create_vscode_settings_maybe(config: &Config) -> io::Result<bool> {
-    let (current_hash, historical_hashes) = SETTINGS_HASHES.split_last().unwrap();
-    let vscode_settings = config.src.join(".vscode").join("settings.json");
-    // If None, no settings.json exists
+fn create_editor_settings_maybe(config: &Config, editor: EditorKind) -> io::Result<bool> {
+    let hashes = editor.hashes();
+    let (current_hash, historical_hashes) = hashes.split_last().unwrap();
+    let settings_path = editor.settings_path(config);
+    let settings_short_path = editor.settings_short_path();
+    let settings_filename = settings_short_path.to_str().unwrap();
+    // If None, no settings file exists
     // If Some(true), is a previous version of settings.json
     // If Some(false), is not a previous version (i.e. user modified)
     // If it's up to date we can just skip this
     let mut mismatched_settings = None;
-    if let Ok(current) = fs::read_to_string(&vscode_settings) {
+    if let Ok(current) = fs::read_to_string(&settings_path) {
         let mut hasher = sha2::Sha256::new();
         hasher.update(&current);
         let hash = hex_encode(hasher.finalize().as_slice());
@@ -585,20 +647,23 @@ fn create_vscode_settings_maybe(config: &Config) -> io::Result<bool> {
         }
     }
     println!(
-        "\nx.py can automatically install the recommended `.vscode/settings.json` file for rustc development"
+        "\nx.py can automatically install the recommended `{settings_filename}` file for rustc development"
     );
+
     match mismatched_settings {
         Some(true) => eprintln!(
-            "WARNING: existing `.vscode/settings.json` is out of date, x.py will update it"
+            "WARNING: existing `{}` is out of date, x.py will update it",
+            settings_filename
         ),
         Some(false) => eprintln!(
-            "WARNING: existing `.vscode/settings.json` has been modified by user, x.py will back it up and replace it"
+            "WARNING: existing `{}` has been modified by user, x.py will back it up and replace it",
+            settings_filename
         ),
         _ => (),
     }
-    let should_create = match prompt_user(
-        "Would you like to create/update settings.json? (Press 'p' to preview values): [y/N]",
-    )? {
+    let should_create = match prompt_user(&format!(
+        "Would you like to create/update `{settings_filename}`? (Press 'p' to preview values): [y/N]"
+    ))? {
         Some(PromptResult::Yes) => true,
         Some(PromptResult::Print) => false,
         _ => {
@@ -607,9 +672,9 @@ fn create_vscode_settings_maybe(config: &Config) -> io::Result<bool> {
         }
     };
     if should_create {
-        let path = config.src.join(".vscode");
-        if !path.exists() {
-            fs::create_dir(&path)?;
+        let settings_folder_path = config.src.join(editor.settings_folder());
+        if !settings_folder_path.exists() {
+            fs::create_dir(settings_folder_path)?;
         }
         let verb = match mismatched_settings {
             // exists but outdated, we can replace this
@@ -617,18 +682,21 @@ fn create_vscode_settings_maybe(config: &Config) -> io::Result<bool> {
             // exists but user modified, back it up
             Some(false) => {
                 // exists and is not current version or outdated, so back it up
-                let mut backup = vscode_settings.clone();
-                backup.set_extension("json.bak");
-                eprintln!("WARNING: copying `settings.json` to `settings.json.bak`");
-                fs::copy(&vscode_settings, &backup)?;
+                let backup = settings_path.clone().with_extension(editor.backup_extension());
+                eprintln!(
+                    "WARNING: copying `{}` to `{}`",
+                    settings_path.file_name().unwrap().to_str().unwrap(),
+                    backup.file_name().unwrap().to_str().unwrap(),
+                );
+                fs::copy(&settings_path, &backup)?;
                 "Updated"
             }
             _ => "Created",
         };
-        fs::write(&vscode_settings, RUST_ANALYZER_SETTINGS)?;
-        println!("{verb} `.vscode/settings.json`");
+        fs::write(&settings_path, editor.settings_template())?;
+        println!("{verb} `{}`", settings_filename);
     } else {
-        println!("\n{RUST_ANALYZER_SETTINGS}");
+        println!("\n{}", editor.settings_template());
     }
     Ok(should_create)
 }
diff --git a/src/bootstrap/src/core/build_steps/setup/tests.rs b/src/bootstrap/src/core/build_steps/setup/tests.rs
index 3552224f33b..f3d4b6aa4db 100644
--- a/src/bootstrap/src/core/build_steps/setup/tests.rs
+++ b/src/bootstrap/src/core/build_steps/setup/tests.rs
@@ -1,16 +1,17 @@
 use sha2::Digest;
 
-use super::{RUST_ANALYZER_SETTINGS, SETTINGS_HASHES};
+use super::EditorKind;
 use crate::utils::helpers::hex_encode;
 
 #[test]
 fn check_matching_settings_hash() {
+    let editor = EditorKind::Vscode;
     let mut hasher = sha2::Sha256::new();
-    hasher.update(&RUST_ANALYZER_SETTINGS);
+    hasher.update(&editor.settings_template());
     let hash = hex_encode(hasher.finalize().as_slice());
     assert_eq!(
         &hash,
-        SETTINGS_HASHES.last().unwrap(),
-        "Update `SETTINGS_HASHES` with the new hash of `src/etc/rust_analyzer_settings.json`"
+        editor.hashes().last().unwrap(),
+        "Update `EditorKind::hashes()` with the new hash of `src/etc/rust_analyzer_settings.json`"
     );
 }
diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs
index 6ba669f3b10..91ac9cceb38 100644
--- a/src/bootstrap/src/core/builder.rs
+++ b/src/bootstrap/src/core/builder.rs
@@ -1000,7 +1000,9 @@ impl<'a> Builder<'a> {
                 run::GenerateWindowsSys,
                 run::GenerateCompletions,
             ),
-            Kind::Setup => describe!(setup::Profile, setup::Hook, setup::Link, setup::Vscode),
+            Kind::Setup => {
+                describe!(setup::Profile, setup::Hook, setup::Link, setup::Vscode, setup::Emacs)
+            }
             Kind::Clean => describe!(clean::CleanAll, clean::Rustc, clean::Std),
             Kind::Vendor => describe!(vendor::Vendor),
             // special-cased in Build::build()
diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs
index 87db5f93fb0..73446382cc6 100644
--- a/src/bootstrap/src/core/config/flags.rs
+++ b/src/bootstrap/src/core/config/flags.rs
@@ -450,11 +450,12 @@ Arguments:
     To only set up the git hook, VS Code config or toolchain link, you may use
         ./x.py setup hook
         ./x.py setup vscode
+        ./x.py setup emacs
         ./x.py setup link", Profile::all_for_help("        ").trim_end()))]
     Setup {
         /// Either the profile for `config.toml` or another setup action.
         /// May be omitted to set up interactively
-        #[arg(value_name = "<PROFILE>|hook|vscode|link")]
+        #[arg(value_name = "<PROFILE>|hook|vscode|emacs|link")]
         profile: Option<PathBuf>,
     },
     /// Suggest a subset of tests to run, based on modified files
diff --git a/src/etc/completions/x.py.sh b/src/etc/completions/x.py.sh
index 2aa0d9f69cc..78d59c229d7 100644
--- a/src/etc/completions/x.py.sh
+++ b/src/etc/completions/x.py.sh
@@ -2741,7 +2741,7 @@ _x.py() {
             return 0
             ;;
         x.py__setup)
-            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [<PROFILE>|hook|vscode|link] [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [<PROFILE>|hook|vscode|emacs|link] [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
diff --git a/src/etc/rust_analyzer_eglot.el b/src/etc/rust_analyzer_eglot.el
index e55d80d98de..7b4309f8e18 100644
--- a/src/etc/rust_analyzer_eglot.el
+++ b/src/etc/rust_analyzer_eglot.el
@@ -2,28 +2,28 @@
   .((eglot-workspace-configuration
      . (:rust-analyzer
         ( :check ( :invocationLocation "root"
-                   :invocationStrategy "once"
-                   :overrideCommand ["python3"
-                                     "x.py"
-                                     "check"
-                                     "--json-output"])
-          :linkedProjects ["Cargo.toml"
-                           "src/tools/x/Cargo.toml"
-                           "src/bootstrap/Cargo.toml"
-                           "src/tools/rust-analyzer/Cargo.toml"
-                           "compiler/rustc_codegen_cranelift/Cargo.toml"
-                           "compiler/rustc_codegen_gcc/Cargo.toml"]
-          :rustfmt ( :overrideCommand ["build/host/rustfmt/bin/rustfmt"
-                                       "--edition=2021"])
-          :procMacro ( :server "build/host/stage0/libexec/rust-analyzer-proc-macro-srv"
-                       :enable t)
-          :cargo ( :buildScripts ( :enable t
-                                   :invocationLocation "root"
-                                   :invocationStrategy "once"
-                                   :overrideCommand ["python3"
-                                                     "x.py"
-                                                     "check"
-                                                     "--json-output"])
-                   :sysrootSrc "./library"
-                   :extraEnv (:RUSTC_BOOTSTRAP "1"))
-          :rustc ( :source "./Cargo.toml" )))))))
+                                       :invocationStrategy "once"
+                                       :overrideCommand ["python3"
+                                                         "x.py"
+                                                         "check"
+                                                         "--json-output"])
+                 :linkedProjects ["Cargo.toml"
+                                  "src/tools/x/Cargo.toml"
+                                  "src/bootstrap/Cargo.toml"
+                                  "src/tools/rust-analyzer/Cargo.toml"
+                                  "compiler/rustc_codegen_cranelift/Cargo.toml"
+                                  "compiler/rustc_codegen_gcc/Cargo.toml"]
+                 :rustfmt ( :overrideCommand ["build/host/rustfmt/bin/rustfmt"
+                                              "--edition=2021"])
+                 :procMacro ( :server "build/host/stage0/libexec/rust-analyzer-proc-macro-srv"
+                                      :enable t)
+                 :cargo ( :buildScripts ( :enable t
+                                                  :invocationLocation "root"
+                                                  :invocationStrategy "once"
+                                                  :overrideCommand ["python3"
+                                                                    "x.py"
+                                                                    "check"
+                                                                    "--json-output"])
+                                        :sysrootSrc "./library"
+                                        :extraEnv (:RUSTC_BOOTSTRAP "1"))
+                 :rustc ( :source "./Cargo.toml" )))))))