From c53d4c78ad54a0eb2a117b5d47aa18b3c2bff484 Mon Sep 17 00:00:00 2001 From: Kajetan Puchalski Date: Mon, 30 Sep 2024 21:50:01 +0100 Subject: [PATCH 1/4] bootstrap: Add support for ./x setup emacs Add support for automatically setting up the recommended LSP config for Emacs. Additionally, refactor setup.rs to make it easier to add support for more editors in the future. --- src/bootstrap/src/core/build_steps/setup.rs | 182 ++++++++++++------ .../src/core/build_steps/setup/tests.rs | 9 +- src/bootstrap/src/core/builder.rs | 4 +- src/bootstrap/src/core/config/flags.rs | 3 +- src/etc/completions/x.py.sh | 2 +- src/etc/rust_analyzer_eglot.el | 50 ++--- 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 0ee2cb451f39c..7fbc5de06dfdf 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 { - 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 { + 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(¤t); let hash = hex_encode(hasher.finalize().as_slice()); @@ -585,20 +647,23 @@ fn create_vscode_settings_maybe(config: &Config) -> io::Result { } } 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 { } }; 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 { // 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 3552224f33b56..f3d4b6aa4db6e 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 6ba669f3b1034..91ac9cceb38db 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 87db5f93fb03c..73446382cc63a 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 = "|hook|vscode|link")] + #[arg(value_name = "|hook|vscode|emacs|link")] profile: Option, }, /// 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 2aa0d9f69cc87..78d59c229d7dc 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 [|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 [|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 e55d80d98dec3..7b4309f8e188f 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" ))))))) From dfc7e396a6426211078b24fe0a2c8f29adcbe2f2 Mon Sep 17 00:00:00 2001 From: Kajetan Puchalski Date: Mon, 30 Sep 2024 22:26:22 +0100 Subject: [PATCH 2/4] bootstrap: Add support for ./x setup helix --- src/bootstrap/src/core/build_steps/setup.rs | 9 +++++++++ src/bootstrap/src/core/builder.rs | 2 +- src/bootstrap/src/core/config/flags.rs | 3 ++- src/etc/completions/x.py.sh | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs index 7fbc5de06dfdf..2b5700699c482 100644 --- a/src/bootstrap/src/core/build_steps/setup.rs +++ b/src/bootstrap/src/core/build_steps/setup.rs @@ -523,6 +523,7 @@ undesirable, simply delete the `pre-push` file from .git/hooks." enum EditorKind { Vscode, Emacs, + Helix, } impl EditorKind { @@ -545,6 +546,9 @@ impl EditorKind { "51068d4747a13732440d1a8b8f432603badb1864fa431d83d0fd4f8fa57039e0", "d29af4d949bbe2371eac928a3c31cf9496b1701aa1c45f11cd6c759865ad5c45", ], + EditorKind::Helix => vec![ + "2d3069b8cf1b977e5d4023965eb6199597755e6c96c185ed5f2854f98b83d233" + ] } } @@ -556,6 +560,7 @@ impl EditorKind { self.settings_folder().join(match self { EditorKind::Vscode => "settings.json", EditorKind::Emacs => ".dir-locals.el", + EditorKind::Helix => "languages.toml", }) } @@ -563,6 +568,7 @@ impl EditorKind { match self { EditorKind::Vscode => PathBuf::new().join(".vscode"), EditorKind::Emacs => PathBuf::new(), + EditorKind::Helix => PathBuf::new().join(".helix"), } } @@ -570,6 +576,7 @@ impl EditorKind { match self { EditorKind::Vscode => include_str!("../../../../etc/rust_analyzer_settings.json"), EditorKind::Emacs => include_str!("../../../../etc/rust_analyzer_eglot.el"), + EditorKind::Helix => include_str!("../../../../etc/rust_analyzer_helix.toml"), } } @@ -577,6 +584,7 @@ impl EditorKind { match self { EditorKind::Vscode => "json.bak", EditorKind::Emacs => "el.bak", + EditorKind::Helix => "toml.bak", } } } @@ -620,6 +628,7 @@ macro_rules! impl_editor_support { impl_editor_support!(vscode, Vscode); impl_editor_support!(emacs, Emacs); +impl_editor_support!(helix, Helix); /// Create the recommended editor LSP config file for rustc development, or just print it /// If this method should be re-called, it returns `false`. diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 91ac9cceb38db..dcffeccbaff3e 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -1001,7 +1001,7 @@ impl<'a> Builder<'a> { run::GenerateCompletions, ), Kind::Setup => { - describe!(setup::Profile, setup::Hook, setup::Link, setup::Vscode, setup::Emacs) + describe!(setup::Profile, setup::Hook, setup::Link, setup::Vscode, setup::Emacs, setup::Helix) } Kind::Clean => describe!(clean::CleanAll, clean::Rustc, clean::Std), Kind::Vendor => describe!(vendor::Vendor), diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index 73446382cc63a..2957e47c77110 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -451,11 +451,12 @@ Arguments: ./x.py setup hook ./x.py setup vscode ./x.py setup emacs + ./x.py setup helix ./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 = "|hook|vscode|emacs|link")] + #[arg(value_name = "|hook|vscode|emacs|helix|link")] profile: Option, }, /// 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 78d59c229d7dc..15475b976ea50 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 [|hook|vscode|emacs|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 [|hook|vscode|emacs|helix|link] [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 From 9b24fae0bec325a3aa65e9373d018eb55a327d0e Mon Sep 17 00:00:00 2001 From: Kajetan Puchalski Date: Mon, 30 Sep 2024 22:38:36 +0100 Subject: [PATCH 3/4] bootstrap: Add support for ./x setup vim --- src/bootstrap/src/core/build_steps/setup.rs | 24 +++++++++++---------- src/bootstrap/src/core/builder.rs | 10 ++++++++- src/bootstrap/src/core/config/flags.rs | 3 ++- src/bootstrap/src/utils/change_tracker.rs | 5 +++++ src/etc/completions/x.py.sh | 2 +- 5 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs index 2b5700699c482..3aa8d3a67d8bd 100644 --- a/src/bootstrap/src/core/build_steps/setup.rs +++ b/src/bootstrap/src/core/build_steps/setup.rs @@ -522,6 +522,7 @@ undesirable, simply delete the `pre-push` file from .git/hooks." #[derive(Clone, Debug, Eq, PartialEq)] enum EditorKind { Vscode, + Vim, Emacs, Helix, } @@ -532,7 +533,7 @@ impl EditorKind { /// outdated vs. user-modified settings files. fn hashes(&self) -> Vec<&str> { match self { - EditorKind::Vscode => vec![ + EditorKind::Vscode | EditorKind::Vim => vec![ "ea67e259dedf60d4429b6c349a564ffcd1563cf41c920a856d1f5b16b4701ac8", "56e7bf011c71c5d81e0bf42e84938111847a810eee69d906bba494ea90b51922", "af1b5efe196aed007577899db9dae15d6dbc923d6fa42fa0934e68617ba9bbe0", @@ -546,9 +547,9 @@ impl EditorKind { "51068d4747a13732440d1a8b8f432603badb1864fa431d83d0fd4f8fa57039e0", "d29af4d949bbe2371eac928a3c31cf9496b1701aa1c45f11cd6c759865ad5c45", ], - EditorKind::Helix => vec![ - "2d3069b8cf1b977e5d4023965eb6199597755e6c96c185ed5f2854f98b83d233" - ] + EditorKind::Helix => { + vec!["2d3069b8cf1b977e5d4023965eb6199597755e6c96c185ed5f2854f98b83d233"] + } } } @@ -559,6 +560,7 @@ impl EditorKind { fn settings_short_path(&self) -> PathBuf { self.settings_folder().join(match self { EditorKind::Vscode => "settings.json", + EditorKind::Vim => "coc-settings.json", EditorKind::Emacs => ".dir-locals.el", EditorKind::Helix => "languages.toml", }) @@ -567,6 +569,7 @@ impl EditorKind { fn settings_folder(&self) -> PathBuf { match self { EditorKind::Vscode => PathBuf::new().join(".vscode"), + EditorKind::Vim => PathBuf::new().join(".vim"), EditorKind::Emacs => PathBuf::new(), EditorKind::Helix => PathBuf::new().join(".helix"), } @@ -574,18 +577,16 @@ impl EditorKind { fn settings_template(&self) -> &str { match self { - EditorKind::Vscode => include_str!("../../../../etc/rust_analyzer_settings.json"), + EditorKind::Vscode | EditorKind::Vim => { + include_str!("../../../../etc/rust_analyzer_settings.json") + } EditorKind::Emacs => include_str!("../../../../etc/rust_analyzer_eglot.el"), EditorKind::Helix => include_str!("../../../../etc/rust_analyzer_helix.toml"), } } - fn backup_extension(&self) -> &str { - match self { - EditorKind::Vscode => "json.bak", - EditorKind::Emacs => "el.bak", - EditorKind::Helix => "toml.bak", - } + fn backup_extension(&self) -> String { + format!("{}.bak", self.settings_short_path().extension().unwrap().to_str().unwrap()) } } @@ -627,6 +628,7 @@ macro_rules! impl_editor_support { } impl_editor_support!(vscode, Vscode); +impl_editor_support!(vim, Vim); impl_editor_support!(emacs, Emacs); impl_editor_support!(helix, Helix); diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index dcffeccbaff3e..67f9a3ab3edfc 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -1001,7 +1001,15 @@ impl<'a> Builder<'a> { run::GenerateCompletions, ), Kind::Setup => { - describe!(setup::Profile, setup::Hook, setup::Link, setup::Vscode, setup::Emacs, setup::Helix) + describe!( + setup::Profile, + setup::Hook, + setup::Link, + setup::Vscode, + setup::Emacs, + setup::Helix, + setup::Vim + ) } Kind::Clean => describe!(clean::CleanAll, clean::Rustc, clean::Std), Kind::Vendor => describe!(vendor::Vendor), diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index 2957e47c77110..fb27d1ebb182d 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -450,13 +450,14 @@ 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 vim ./x.py setup emacs ./x.py setup helix ./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 = "|hook|vscode|emacs|helix|link")] + #[arg(value_name = "|hook|vscode|vim|emacs|helix|link")] profile: Option, }, /// Suggest a subset of tests to run, based on modified files diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index e6f7f105fa27a..e6f5363dbf2f9 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -270,4 +270,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "If `llvm.download-ci-llvm` is not defined, it defaults to `true`.", }, + ChangeInfo { + change_id: 131075, + severity: ChangeSeverity::Info, + summary: "New options for ./x setup added - ./x setup [vim|emacs|helix]", + }, ]; diff --git a/src/etc/completions/x.py.sh b/src/etc/completions/x.py.sh index 15475b976ea50..913b0e820cb51 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 [|hook|vscode|emacs|helix|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 [|hook|vscode|vim|emacs|helix|link] [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 From 456be106b714b3b2f5f3d43c801e28bb69fcc789 Mon Sep 17 00:00:00 2001 From: Kajetan Puchalski Date: Thu, 3 Oct 2024 15:22:44 +0100 Subject: [PATCH 4/4] bootstrap: Consolidate editor LSP setup Consolidate LSP setup for different editors into one `./x setup editor`. --- src/bootstrap/src/core/build_steps/setup.rs | 116 ++++++++++++-------- src/bootstrap/src/core/builder.rs | 10 +- src/bootstrap/src/core/config/flags.rs | 9 +- src/bootstrap/src/utils/change_tracker.rs | 2 +- src/etc/completions/x.py.sh | 2 +- 5 files changed, 77 insertions(+), 62 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs index 3aa8d3a67d8bd..519649779336c 100644 --- a/src/bootstrap/src/core/build_steps/setup.rs +++ b/src/bootstrap/src/core/build_steps/setup.rs @@ -528,6 +528,35 @@ enum EditorKind { } impl EditorKind { + fn prompt_user() -> io::Result> { + let prompt_str = "Available editors: +1. vscode +2. vim +3. emacs +4. helix + +Select which editor you would like to set up [default: None]: "; + + let mut input = String::new(); + loop { + print!("{}", prompt_str); + io::stdout().flush()?; + input.clear(); + io::stdin().read_line(&mut input)?; + match input.trim().to_lowercase().as_str() { + "1" | "vscode" => return Ok(Some(EditorKind::Vscode)), + "2" | "vim" => return Ok(Some(EditorKind::Vim)), + "3" | "emacs" => return Ok(Some(EditorKind::Emacs)), + "4" | "helix" => return Ok(Some(EditorKind::Helix)), + "" => return Ok(None), + _ => { + eprintln!("ERROR: unrecognized option '{}'", input.trim()); + eprintln!("NOTE: press Ctrl+C to exit"); + } + }; + } + } + /// 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. @@ -568,10 +597,10 @@ impl EditorKind { fn settings_folder(&self) -> PathBuf { match self { - EditorKind::Vscode => PathBuf::new().join(".vscode"), - EditorKind::Vim => PathBuf::new().join(".vim"), + EditorKind::Vscode => PathBuf::from(".vscode"), + EditorKind::Vim => PathBuf::from(".vim"), EditorKind::Emacs => PathBuf::new(), - EditorKind::Helix => PathBuf::new().join(".helix"), + EditorKind::Helix => PathBuf::from(".helix"), } } @@ -590,48 +619,47 @@ impl EditorKind { } } -/// 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); - } - } +/// Sets up or displays the LSP config for one of the supported editors +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct Editor; + +impl Step for Editor { + type Output = (); + const DEFAULT: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.alias("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() == "editor" { + run.builder.ensure(Editor); } - fn run(self, builder: &Builder<'_>) -> Self::Output { - let config = &builder.config; - if config.dry_run() { - return; + } + } + + fn run(self, builder: &Builder<'_>) -> Self::Output { + let config = &builder.config; + if config.dry_run() { + return; + } + match EditorKind::prompt_user() { + Ok(editor_kind) => { + if let Some(editor_kind) = editor_kind { + while !t!(create_editor_settings_maybe(config, editor_kind.clone())) {} + } else { + println!("Ok, skipping editor setup!"); } - while !t!(create_editor_settings_maybe(config, EditorKind::$kind)) {} } + Err(e) => eprintln!("Could not determine the editor: {e}"), } - )+}; + } } -impl_editor_support!(vscode, Vscode); -impl_editor_support!(vim, Vim); -impl_editor_support!(emacs, Emacs); -impl_editor_support!(helix, Helix); - /// 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_editor_settings_maybe(config: &Config, editor: EditorKind) -> io::Result { @@ -662,13 +690,11 @@ fn create_editor_settings_maybe(config: &Config, editor: EditorKind) -> io::Resu ); match mismatched_settings { - Some(true) => eprintln!( - "WARNING: existing `{}` is out of date, x.py will update it", - settings_filename - ), + Some(true) => { + eprintln!("WARNING: existing `{settings_filename}` is out of date, x.py will update it") + } Some(false) => eprintln!( - "WARNING: existing `{}` has been modified by user, x.py will back it up and replace it", - settings_filename + "WARNING: existing `{settings_filename}` has been modified by user, x.py will back it up and replace it" ), _ => (), } diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 67f9a3ab3edfc..abc7a22889357 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -1001,15 +1001,7 @@ impl<'a> Builder<'a> { run::GenerateCompletions, ), Kind::Setup => { - describe!( - setup::Profile, - setup::Hook, - setup::Link, - setup::Vscode, - setup::Emacs, - setup::Helix, - setup::Vim - ) + describe!(setup::Profile, setup::Hook, setup::Link, setup::Editor) } Kind::Clean => describe!(clean::CleanAll, clean::Rustc, clean::Std), Kind::Vendor => describe!(vendor::Vendor), diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index fb27d1ebb182d..3aefe517a5be6 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -447,17 +447,14 @@ Arguments: The profile is optional and you will be prompted interactively if it is not given. The following profiles are available: {} - To only set up the git hook, VS Code config or toolchain link, you may use + To only set up the git hook, editor config or toolchain link, you may use ./x.py setup hook - ./x.py setup vscode - ./x.py setup vim - ./x.py setup emacs - ./x.py setup helix + ./x.py setup editor ./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 = "|hook|vscode|vim|emacs|helix|link")] + #[arg(value_name = "|hook|editor|link")] profile: Option, }, /// Suggest a subset of tests to run, based on modified files diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index e6f5363dbf2f9..b37786496cb56 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -273,6 +273,6 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ ChangeInfo { change_id: 131075, severity: ChangeSeverity::Info, - summary: "New options for ./x setup added - ./x setup [vim|emacs|helix]", + summary: "New option `./x setup editor` added, replacing `./x setup vscode` and adding support for vim, emacs and helix.", }, ]; diff --git a/src/etc/completions/x.py.sh b/src/etc/completions/x.py.sh index 913b0e820cb51..ba7f2c9fb5d66 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 [|hook|vscode|vim|emacs|helix|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 [|hook|editor|link] [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0