Auto merge of #131075 - mrkajetanp:bootstrap-editors, r=Kobzol
bootstrap: Consolidate editor setup into ./x setup editor & add support for vim, emacs & helix Add support for automatically setting up the recommended LSP config for Vim (coc-nvim), Emacs (eglot) and Helix. Additionally, refactor setup.rs to make it easier to add support for more editors in the future. As suggested, r? `@jieyouxu`
This commit is contained in:
commit
68301a6a96
7 changed files with 194 additions and 81 deletions
|
@ -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,162 @@ 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,
|
||||
Vim,
|
||||
Emacs,
|
||||
Helix,
|
||||
}
|
||||
|
||||
impl Step for Vscode {
|
||||
impl EditorKind {
|
||||
fn prompt_user() -> io::Result<Option<EditorKind>> {
|
||||
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.
|
||||
fn hashes(&self) -> Vec<&str> {
|
||||
match self {
|
||||
EditorKind::Vscode | EditorKind::Vim => vec![
|
||||
"ea67e259dedf60d4429b6c349a564ffcd1563cf41c920a856d1f5b16b4701ac8",
|
||||
"56e7bf011c71c5d81e0bf42e84938111847a810eee69d906bba494ea90b51922",
|
||||
"af1b5efe196aed007577899db9dae15d6dbc923d6fa42fa0934e68617ba9bbe0",
|
||||
"3468fea433c25fff60be6b71e8a215a732a7b1268b6a83bf10d024344e140541",
|
||||
"47d227f424bf889b0d899b9cc992d5695e1b78c406e183cd78eafefbe5488923",
|
||||
"b526bd58d0262dd4dda2bff5bc5515b705fb668a46235ace3e057f807963a11a",
|
||||
"828666b021d837a33e78d870b56d34c88a5e2c85de58b693607ec574f0c27000",
|
||||
"811fb3b063c739d261fd8590dd30242e117908f5a095d594fa04585daa18ec4d",
|
||||
],
|
||||
EditorKind::Emacs => vec![
|
||||
"51068d4747a13732440d1a8b8f432603badb1864fa431d83d0fd4f8fa57039e0",
|
||||
"d29af4d949bbe2371eac928a3c31cf9496b1701aa1c45f11cd6c759865ad5c45",
|
||||
],
|
||||
EditorKind::Helix => {
|
||||
vec!["2d3069b8cf1b977e5d4023965eb6199597755e6c96c185ed5f2854f98b83d233"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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::Vim => "coc-settings.json",
|
||||
EditorKind::Emacs => ".dir-locals.el",
|
||||
EditorKind::Helix => "languages.toml",
|
||||
})
|
||||
}
|
||||
|
||||
fn settings_folder(&self) -> PathBuf {
|
||||
match self {
|
||||
EditorKind::Vscode => PathBuf::from(".vscode"),
|
||||
EditorKind::Vim => PathBuf::from(".vim"),
|
||||
EditorKind::Emacs => PathBuf::new(),
|
||||
EditorKind::Helix => PathBuf::from(".helix"),
|
||||
}
|
||||
}
|
||||
|
||||
fn settings_template(&self) -> &str {
|
||||
match self {
|
||||
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) -> String {
|
||||
format!("{}.bak", self.settings_short_path().extension().unwrap().to_str().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
/// 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("vscode")
|
||||
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() == "vscode" {
|
||||
run.builder.ensure(Vscode);
|
||||
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;
|
||||
}
|
||||
while !t!(create_vscode_settings_maybe(config)) {}
|
||||
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!");
|
||||
}
|
||||
}
|
||||
Err(e) => eprintln!("Could not determine the editor: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `.vscode/settings.json` file for rustc development, or just print it
|
||||
/// 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(¤t);
|
||||
let hash = hex_encode(hasher.finalize().as_slice());
|
||||
|
@ -585,20 +686,21 @@ 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"
|
||||
),
|
||||
Some(true) => {
|
||||
eprintln!("WARNING: existing `{settings_filename}` is out of date, x.py will update it")
|
||||
}
|
||||
Some(false) => eprintln!(
|
||||
"WARNING: existing `.vscode/settings.json` has been modified by user, x.py will back it up and replace it"
|
||||
"WARNING: existing `{settings_filename}` has been modified by user, x.py will back it up and replace it"
|
||||
),
|
||||
_ => (),
|
||||
}
|
||||
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 +709,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 +719,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)
|
||||
}
|
||||
|
|
|
@ -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`"
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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::Editor)
|
||||
}
|
||||
Kind::Clean => describe!(clean::CleanAll, clean::Rustc, clean::Std),
|
||||
Kind::Vendor => describe!(vendor::Vendor),
|
||||
// special-cased in Build::build()
|
||||
|
|
|
@ -447,14 +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 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 = "<PROFILE>|hook|vscode|link")]
|
||||
#[arg(value_name = "<PROFILE>|hook|editor|link")]
|
||||
profile: Option<PathBuf>,
|
||||
},
|
||||
/// Suggest a subset of tests to run, based on modified files
|
||||
|
|
|
@ -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 option `./x setup editor` added, replacing `./x setup vscode` and adding support for vim, emacs and helix.",
|
||||
},
|
||||
];
|
||||
|
|
|
@ -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|editor|link] [PATHS]... [ARGS]..."
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
|
|
|
@ -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" )))))))
|
||||
|
|
Loading…
Add table
Reference in a new issue