mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-14 14:13:58 +00:00
commit
5c4c126832
11 changed files with 122 additions and 125 deletions
|
@ -494,7 +494,7 @@ impl CommandHandle {
|
||||||
let (sender, receiver) = unbounded();
|
let (sender, receiver) = unbounded();
|
||||||
let actor = CargoActor::new(sender, stdout, stderr);
|
let actor = CargoActor::new(sender, stdout, stderr);
|
||||||
let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
|
let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
|
||||||
.name("CargoHandle".to_owned())
|
.name("CommandHandle".to_owned())
|
||||||
.spawn(move || actor.run())
|
.spawn(move || actor.run())
|
||||||
.expect("failed to spawn thread");
|
.expect("failed to spawn thread");
|
||||||
Ok(CommandHandle { program, arguments, current_dir, child, thread, receiver })
|
Ok(CommandHandle { program, arguments, current_dir, child, thread, receiver })
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use hir::{self, HasVisibility};
|
use hir::HasVisibility;
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
assists::{AssistId, AssistKind},
|
assists::{AssistId, AssistKind},
|
||||||
defs::Definition,
|
defs::Definition,
|
||||||
|
|
|
@ -82,6 +82,7 @@ pub fn parallel_prime_caches(
|
||||||
|
|
||||||
stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
|
stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
|
||||||
.allow_leak(true)
|
.allow_leak(true)
|
||||||
|
.name("PrimeCaches".to_owned())
|
||||||
.spawn(move || Cancelled::catch(|| worker(db)))
|
.spawn(move || Cancelled::catch(|| worker(db)))
|
||||||
.expect("failed to spawn thread");
|
.expect("failed to spawn thread");
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,33 +54,33 @@ fn spacing_to_external(spacing: Spacing) -> proc_macro::Spacing {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LiteralFormatter<S>(bridge::Literal<S, Symbol>);
|
/// Invokes the callback with a `&[&str]` consisting of each part of the
|
||||||
|
/// literal's representation. This is done to allow the `ToString` and
|
||||||
impl<S> LiteralFormatter<S> {
|
/// `Display` implementations to borrow references to symbol values, and
|
||||||
/// Invokes the callback with a `&[&str]` consisting of each part of the
|
/// both be optimized to reduce overhead.
|
||||||
/// literal's representation. This is done to allow the `ToString` and
|
fn literal_with_stringify_parts<S, R>(
|
||||||
/// `Display` implementations to borrow references to symbol values, and
|
literal: &bridge::Literal<S, Symbol>,
|
||||||
/// both be optimized to reduce overhead.
|
interner: SymbolInternerRef,
|
||||||
fn with_stringify_parts<R>(
|
f: impl FnOnce(&[&str]) -> R,
|
||||||
&self,
|
) -> R {
|
||||||
interner: SymbolInternerRef,
|
/// Returns a string containing exactly `num` '#' characters.
|
||||||
f: impl FnOnce(&[&str]) -> R,
|
/// Uses a 256-character source string literal which is always safe to
|
||||||
) -> R {
|
/// index with a `u8` index.
|
||||||
/// Returns a string containing exactly `num` '#' characters.
|
fn get_hashes_str(num: u8) -> &'static str {
|
||||||
/// Uses a 256-character source string literal which is always safe to
|
const HASHES: &str = "\
|
||||||
/// index with a `u8` index.
|
|
||||||
fn get_hashes_str(num: u8) -> &'static str {
|
|
||||||
const HASHES: &str = "\
|
|
||||||
################################################################\
|
################################################################\
|
||||||
################################################################\
|
################################################################\
|
||||||
################################################################\
|
################################################################\
|
||||||
################################################################\
|
################################################################\
|
||||||
";
|
";
|
||||||
const _: () = assert!(HASHES.len() == 256);
|
const _: () = assert!(HASHES.len() == 256);
|
||||||
&HASHES[..num as usize]
|
&HASHES[..num as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
self.with_symbol_and_suffix(interner, |symbol, suffix| match self.0.kind {
|
{
|
||||||
|
let symbol = &*literal.symbol.text(interner);
|
||||||
|
let suffix = &*literal.suffix.map(|s| s.text(interner)).unwrap_or_default();
|
||||||
|
match literal.kind {
|
||||||
bridge::LitKind::Byte => f(&["b'", symbol, "'", suffix]),
|
bridge::LitKind::Byte => f(&["b'", symbol, "'", suffix]),
|
||||||
bridge::LitKind::Char => f(&["'", symbol, "'", suffix]),
|
bridge::LitKind::Char => f(&["'", symbol, "'", suffix]),
|
||||||
bridge::LitKind::Str => f(&["\"", symbol, "\"", suffix]),
|
bridge::LitKind::Str => f(&["\"", symbol, "\"", suffix]),
|
||||||
|
@ -101,16 +101,6 @@ impl<S> LiteralFormatter<S> {
|
||||||
bridge::LitKind::Integer | bridge::LitKind::Float | bridge::LitKind::ErrWithGuar => {
|
bridge::LitKind::Integer | bridge::LitKind::Float | bridge::LitKind::ErrWithGuar => {
|
||||||
f(&[symbol, suffix])
|
f(&[symbol, suffix])
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn with_symbol_and_suffix<R>(
|
|
||||||
&self,
|
|
||||||
interner: SymbolInternerRef,
|
|
||||||
f: impl FnOnce(&str, &str) -> R,
|
|
||||||
) -> R {
|
|
||||||
let symbol = self.0.symbol.text(interner);
|
|
||||||
let suffix = self.0.suffix.map(|s| s.text(interner)).unwrap_or_default();
|
|
||||||
f(symbol.as_str(), suffix.as_str())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,8 @@ use proc_macro::bridge::{self, server};
|
||||||
use span::{Span, FIXUP_ERASED_FILE_AST_ID_MARKER};
|
use span::{Span, FIXUP_ERASED_FILE_AST_ID_MARKER};
|
||||||
|
|
||||||
use crate::server::{
|
use crate::server::{
|
||||||
delim_to_external, delim_to_internal, token_stream::TokenStreamBuilder, LiteralFormatter,
|
delim_to_external, delim_to_internal, literal_with_stringify_parts,
|
||||||
Symbol, SymbolInternerRef, SYMBOL_INTERNER,
|
token_stream::TokenStreamBuilder, Symbol, SymbolInternerRef, SYMBOL_INTERNER,
|
||||||
};
|
};
|
||||||
mod tt {
|
mod tt {
|
||||||
pub use ::tt::*;
|
pub use ::tt::*;
|
||||||
|
@ -180,12 +180,11 @@ impl server::TokenStream for RaSpanServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
bridge::TokenTree::Literal(literal) => {
|
bridge::TokenTree::Literal(literal) => {
|
||||||
let literal = LiteralFormatter(literal);
|
let text = literal_with_stringify_parts(&literal, self.interner, |parts| {
|
||||||
let text = literal.with_stringify_parts(self.interner, |parts| {
|
|
||||||
::tt::SmolStr::from_iter(parts.iter().copied())
|
::tt::SmolStr::from_iter(parts.iter().copied())
|
||||||
});
|
});
|
||||||
|
|
||||||
let literal = tt::Literal { text, span: literal.0.span };
|
let literal = tt::Literal { text, span: literal.span };
|
||||||
let leaf: tt::Leaf = tt::Leaf::from(literal);
|
let leaf: tt::Leaf = tt::Leaf::from(literal);
|
||||||
let tree = tt::TokenTree::from(leaf);
|
let tree = tt::TokenTree::from(leaf);
|
||||||
Self::TokenStream::from_iter(iter::once(tree))
|
Self::TokenStream::from_iter(iter::once(tree))
|
||||||
|
@ -251,10 +250,17 @@ impl server::TokenStream for RaSpanServer {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|tree| match tree {
|
.map(|tree| match tree {
|
||||||
tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
|
tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
|
||||||
bridge::TokenTree::Ident(bridge::Ident {
|
bridge::TokenTree::Ident(match ident.text.strip_prefix("r#") {
|
||||||
sym: Symbol::intern(self.interner, ident.text.trim_start_matches("r#")),
|
Some(text) => bridge::Ident {
|
||||||
is_raw: ident.text.starts_with("r#"),
|
sym: Symbol::intern(self.interner, text),
|
||||||
span: ident.span,
|
is_raw: true,
|
||||||
|
span: ident.span,
|
||||||
|
},
|
||||||
|
None => bridge::Ident {
|
||||||
|
sym: Symbol::intern(self.interner, &ident.text),
|
||||||
|
is_raw: false,
|
||||||
|
span: ident.span,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
|
tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
|
||||||
|
@ -285,11 +291,12 @@ impl server::TokenStream for RaSpanServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl server::SourceFile for RaSpanServer {
|
impl server::SourceFile for RaSpanServer {
|
||||||
// FIXME these are all stubs
|
|
||||||
fn eq(&mut self, _file1: &Self::SourceFile, _file2: &Self::SourceFile) -> bool {
|
fn eq(&mut self, _file1: &Self::SourceFile, _file2: &Self::SourceFile) -> bool {
|
||||||
|
// FIXME
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
fn path(&mut self, _file: &Self::SourceFile) -> String {
|
fn path(&mut self, _file: &Self::SourceFile) -> String {
|
||||||
|
// FIXME
|
||||||
String::new()
|
String::new()
|
||||||
}
|
}
|
||||||
fn is_real(&mut self, _file: &Self::SourceFile) -> bool {
|
fn is_real(&mut self, _file: &Self::SourceFile) -> bool {
|
||||||
|
@ -306,11 +313,15 @@ impl server::Span for RaSpanServer {
|
||||||
SourceFile {}
|
SourceFile {}
|
||||||
}
|
}
|
||||||
fn save_span(&mut self, _span: Self::Span) -> usize {
|
fn save_span(&mut self, _span: Self::Span) -> usize {
|
||||||
// FIXME stub, requires builtin quote! implementation
|
// FIXME, quote is incompatible with third-party tools
|
||||||
|
// This is called by the quote proc-macro which is expanded when the proc-macro is compiled
|
||||||
|
// As such, r-a will never observe this
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
fn recover_proc_macro_span(&mut self, _id: usize) -> Self::Span {
|
fn recover_proc_macro_span(&mut self, _id: usize) -> Self::Span {
|
||||||
// FIXME stub, requires builtin quote! implementation
|
// FIXME, quote is incompatible with third-party tools
|
||||||
|
// This is called by the expansion of quote!, r-a will observe this, but we don't have
|
||||||
|
// access to the spans that were encoded
|
||||||
self.call_site
|
self.call_site
|
||||||
}
|
}
|
||||||
/// Recent feature, not yet in the proc_macro
|
/// Recent feature, not yet in the proc_macro
|
||||||
|
|
|
@ -8,8 +8,8 @@ use std::{
|
||||||
use proc_macro::bridge::{self, server};
|
use proc_macro::bridge::{self, server};
|
||||||
|
|
||||||
use crate::server::{
|
use crate::server::{
|
||||||
delim_to_external, delim_to_internal, token_stream::TokenStreamBuilder, LiteralFormatter,
|
delim_to_external, delim_to_internal, literal_with_stringify_parts,
|
||||||
Symbol, SymbolInternerRef, SYMBOL_INTERNER,
|
token_stream::TokenStreamBuilder, Symbol, SymbolInternerRef, SYMBOL_INTERNER,
|
||||||
};
|
};
|
||||||
mod tt {
|
mod tt {
|
||||||
pub use proc_macro_api::msg::TokenId;
|
pub use proc_macro_api::msg::TokenId;
|
||||||
|
@ -171,12 +171,12 @@ impl server::TokenStream for TokenIdServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
bridge::TokenTree::Literal(literal) => {
|
bridge::TokenTree::Literal(literal) => {
|
||||||
let literal = LiteralFormatter(literal);
|
let text = literal_with_stringify_parts(&literal, self.interner, |parts| {
|
||||||
let text = literal.with_stringify_parts(self.interner, |parts| {
|
|
||||||
::tt::SmolStr::from_iter(parts.iter().copied())
|
::tt::SmolStr::from_iter(parts.iter().copied())
|
||||||
});
|
});
|
||||||
|
|
||||||
let literal = tt::Literal { text, span: literal.0.span };
|
let literal = tt::Literal { text, span: literal.span };
|
||||||
|
|
||||||
let leaf = tt::Leaf::from(literal);
|
let leaf = tt::Leaf::from(literal);
|
||||||
let tree = TokenTree::from(leaf);
|
let tree = TokenTree::from(leaf);
|
||||||
Self::TokenStream::from_iter(iter::once(tree))
|
Self::TokenStream::from_iter(iter::once(tree))
|
||||||
|
|
|
@ -152,6 +152,13 @@ config_data! {
|
||||||
// FIXME(@poliorcetics): move to multiple targets here too, but this will need more work
|
// FIXME(@poliorcetics): move to multiple targets here too, but this will need more work
|
||||||
// than `checkOnSave_target`
|
// than `checkOnSave_target`
|
||||||
cargo_target: Option<String> = "null",
|
cargo_target: Option<String> = "null",
|
||||||
|
/// Optional path to a rust-analyzer specific target directory.
|
||||||
|
/// This prevents rust-analyzer's `cargo check` and initial build-script and proc-macro
|
||||||
|
/// building from locking the `Cargo.lock` at the expense of duplicating build artifacts.
|
||||||
|
///
|
||||||
|
/// Set to `true` to use a subdirectory of the existing target directory or
|
||||||
|
/// set to a path relative to the workspace to use that path.
|
||||||
|
cargo_targetDir | rust_analyzerTargetDir: Option<TargetDirectory> = "null",
|
||||||
/// Unsets the implicit `#[cfg(test)]` for the specified crates.
|
/// Unsets the implicit `#[cfg(test)]` for the specified crates.
|
||||||
cargo_unsetTest: Vec<String> = "[\"core\"]",
|
cargo_unsetTest: Vec<String> = "[\"core\"]",
|
||||||
|
|
||||||
|
@ -518,14 +525,6 @@ config_data! {
|
||||||
/// tests or binaries. For example, it may be `--release`.
|
/// tests or binaries. For example, it may be `--release`.
|
||||||
runnables_extraArgs: Vec<String> = "[]",
|
runnables_extraArgs: Vec<String> = "[]",
|
||||||
|
|
||||||
/// Optional path to a rust-analyzer specific target directory.
|
|
||||||
/// This prevents rust-analyzer's `cargo check` from locking the `Cargo.lock`
|
|
||||||
/// at the expense of duplicating build artifacts.
|
|
||||||
///
|
|
||||||
/// Set to `true` to use a subdirectory of the existing target directory or
|
|
||||||
/// set to a path relative to the workspace to use that path.
|
|
||||||
rust_analyzerTargetDir: Option<TargetDirectory> = "null",
|
|
||||||
|
|
||||||
/// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private
|
/// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private
|
||||||
/// projects, or "discover" to try to automatically find it if the `rustc-dev` component
|
/// projects, or "discover" to try to automatically find it if the `rustc-dev` component
|
||||||
/// is installed.
|
/// is installed.
|
||||||
|
@ -1401,14 +1400,12 @@ impl Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: This should be an AbsolutePathBuf
|
|
||||||
fn target_dir_from_config(&self) -> Option<PathBuf> {
|
fn target_dir_from_config(&self) -> Option<PathBuf> {
|
||||||
self.data.rust_analyzerTargetDir.as_ref().and_then(|target_dir| match target_dir {
|
self.data.cargo_targetDir.as_ref().and_then(|target_dir| match target_dir {
|
||||||
TargetDirectory::UseSubdirectory(yes) if *yes => {
|
TargetDirectory::UseSubdirectory(true) => Some(PathBuf::from("target/rust-analyzer")),
|
||||||
Some(PathBuf::from("target/rust-analyzer"))
|
TargetDirectory::UseSubdirectory(false) => None,
|
||||||
}
|
TargetDirectory::Directory(dir) if dir.is_relative() => Some(dir.clone()),
|
||||||
TargetDirectory::UseSubdirectory(_) => None,
|
TargetDirectory::Directory(_) => None,
|
||||||
TargetDirectory::Directory(dir) => Some(dir.clone()),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2745,7 +2742,7 @@ mod tests {
|
||||||
"rust": { "analyzerTargetDir": null }
|
"rust": { "analyzerTargetDir": null }
|
||||||
}))
|
}))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(config.data.rust_analyzerTargetDir, None);
|
assert_eq!(config.data.cargo_targetDir, None);
|
||||||
assert!(
|
assert!(
|
||||||
matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir.is_none())
|
matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir.is_none())
|
||||||
);
|
);
|
||||||
|
@ -2764,10 +2761,7 @@ mod tests {
|
||||||
"rust": { "analyzerTargetDir": true }
|
"rust": { "analyzerTargetDir": true }
|
||||||
}))
|
}))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(config.data.cargo_targetDir, Some(TargetDirectory::UseSubdirectory(true)));
|
||||||
config.data.rust_analyzerTargetDir,
|
|
||||||
Some(TargetDirectory::UseSubdirectory(true))
|
|
||||||
);
|
|
||||||
assert!(
|
assert!(
|
||||||
matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir == Some(PathBuf::from("target/rust-analyzer")))
|
matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir == Some(PathBuf::from("target/rust-analyzer")))
|
||||||
);
|
);
|
||||||
|
@ -2787,7 +2781,7 @@ mod tests {
|
||||||
}))
|
}))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
config.data.rust_analyzerTargetDir,
|
config.data.cargo_targetDir,
|
||||||
Some(TargetDirectory::Directory(PathBuf::from("other_folder")))
|
Some(TargetDirectory::Directory(PathBuf::from("other_folder")))
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
|
|
|
@ -90,18 +90,13 @@ pub(crate) fn handle_did_change_text_document(
|
||||||
let _p = tracing::span!(tracing::Level::INFO, "handle_did_change_text_document").entered();
|
let _p = tracing::span!(tracing::Level::INFO, "handle_did_change_text_document").entered();
|
||||||
|
|
||||||
if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) {
|
if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) {
|
||||||
let data = match state.mem_docs.get_mut(&path) {
|
let Some(DocumentData { version, data }) = state.mem_docs.get_mut(&path) else {
|
||||||
Some(doc) => {
|
tracing::error!(?path, "unexpected DidChangeTextDocument");
|
||||||
// The version passed in DidChangeTextDocument is the version after all edits are applied
|
return Ok(());
|
||||||
// so we should apply it before the vfs is notified.
|
|
||||||
doc.version = params.text_document.version;
|
|
||||||
&mut doc.data
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
tracing::error!("unexpected DidChangeTextDocument: {}", path);
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
// The version passed in DidChangeTextDocument is the version after all edits are applied
|
||||||
|
// so we should apply it before the vfs is notified.
|
||||||
|
*version = params.text_document.version;
|
||||||
|
|
||||||
let new_contents = apply_document_changes(
|
let new_contents = apply_document_changes(
|
||||||
state.config.position_encoding(),
|
state.config.position_encoding(),
|
||||||
|
|
|
@ -144,6 +144,16 @@ This option does not take effect until rust-analyzer is restarted.
|
||||||
--
|
--
|
||||||
Compilation target override (target triple).
|
Compilation target override (target triple).
|
||||||
--
|
--
|
||||||
|
[[rust-analyzer.cargo.targetDir]]rust-analyzer.cargo.targetDir (default: `null`)::
|
||||||
|
+
|
||||||
|
--
|
||||||
|
Optional path to a rust-analyzer specific target directory.
|
||||||
|
This prevents rust-analyzer's `cargo check` and initial build-script and proc-macro
|
||||||
|
building from locking the `Cargo.lock` at the expense of duplicating build artifacts.
|
||||||
|
|
||||||
|
Set to `true` to use a subdirectory of the existing target directory or
|
||||||
|
set to a path relative to the workspace to use that path.
|
||||||
|
--
|
||||||
[[rust-analyzer.cargo.unsetTest]]rust-analyzer.cargo.unsetTest (default: `["core"]`)::
|
[[rust-analyzer.cargo.unsetTest]]rust-analyzer.cargo.unsetTest (default: `["core"]`)::
|
||||||
+
|
+
|
||||||
--
|
--
|
||||||
|
@ -814,16 +824,6 @@ Command to be executed instead of 'cargo' for runnables.
|
||||||
Additional arguments to be passed to cargo for runnables such as
|
Additional arguments to be passed to cargo for runnables such as
|
||||||
tests or binaries. For example, it may be `--release`.
|
tests or binaries. For example, it may be `--release`.
|
||||||
--
|
--
|
||||||
[[rust-analyzer.rust.analyzerTargetDir]]rust-analyzer.rust.analyzerTargetDir (default: `null`)::
|
|
||||||
+
|
|
||||||
--
|
|
||||||
Optional path to a rust-analyzer specific target directory.
|
|
||||||
This prevents rust-analyzer's `cargo check` from locking the `Cargo.lock`
|
|
||||||
at the expense of duplicating build artifacts.
|
|
||||||
|
|
||||||
Set to `true` to use a subdirectory of the existing target directory or
|
|
||||||
set to a path relative to the workspace to use that path.
|
|
||||||
--
|
|
||||||
[[rust-analyzer.rustc.source]]rust-analyzer.rustc.source (default: `null`)::
|
[[rust-analyzer.rustc.source]]rust-analyzer.rustc.source (default: `null`)::
|
||||||
+
|
+
|
||||||
--
|
--
|
||||||
|
|
|
@ -671,6 +671,21 @@
|
||||||
"string"
|
"string"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"rust-analyzer.cargo.targetDir": {
|
||||||
|
"markdownDescription": "Optional path to a rust-analyzer specific target directory.\nThis prevents rust-analyzer's `cargo check` and initial build-script and proc-macro\nbuilding from locking the `Cargo.lock` at the expense of duplicating build artifacts.\n\nSet to `true` to use a subdirectory of the existing target directory or\nset to a path relative to the workspace to use that path.",
|
||||||
|
"default": null,
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"rust-analyzer.cargo.unsetTest": {
|
"rust-analyzer.cargo.unsetTest": {
|
||||||
"markdownDescription": "Unsets the implicit `#[cfg(test)]` for the specified crates.",
|
"markdownDescription": "Unsets the implicit `#[cfg(test)]` for the specified crates.",
|
||||||
"default": [
|
"default": [
|
||||||
|
@ -1543,21 +1558,6 @@
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rust-analyzer.rust.analyzerTargetDir": {
|
|
||||||
"markdownDescription": "Optional path to a rust-analyzer specific target directory.\nThis prevents rust-analyzer's `cargo check` from locking the `Cargo.lock`\nat the expense of duplicating build artifacts.\n\nSet to `true` to use a subdirectory of the existing target directory or\nset to a path relative to the workspace to use that path.",
|
|
||||||
"default": null,
|
|
||||||
"anyOf": [
|
|
||||||
{
|
|
||||||
"type": "null"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"rust-analyzer.rustc.source": {
|
"rust-analyzer.rustc.source": {
|
||||||
"markdownDescription": "Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private\nprojects, or \"discover\" to try to automatically find it if the `rustc-dev` component\nis installed.\n\nAny project which uses rust-analyzer with the rustcPrivate\ncrates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.\n\nThis option does not take effect until rust-analyzer is restarted.",
|
"markdownDescription": "Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private\nprojects, or \"discover\" to try to automatically find it if the `rustc-dev` component\nis installed.\n\nAny project which uses rust-analyzer with the rustcPrivate\ncrates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.\n\nThis option does not take effect until rust-analyzer is restarted.",
|
||||||
"default": null,
|
"default": null,
|
||||||
|
|
|
@ -12,27 +12,33 @@ use crate::Message;
|
||||||
/// Creates an LSP connection via stdio.
|
/// Creates an LSP connection via stdio.
|
||||||
pub(crate) fn stdio_transport() -> (Sender<Message>, Receiver<Message>, IoThreads) {
|
pub(crate) fn stdio_transport() -> (Sender<Message>, Receiver<Message>, IoThreads) {
|
||||||
let (writer_sender, writer_receiver) = bounded::<Message>(0);
|
let (writer_sender, writer_receiver) = bounded::<Message>(0);
|
||||||
let writer = thread::spawn(move || {
|
let writer = thread::Builder::new()
|
||||||
let stdout = stdout();
|
.name("LspServerWriter".to_owned())
|
||||||
let mut stdout = stdout.lock();
|
.spawn(move || {
|
||||||
writer_receiver.into_iter().try_for_each(|it| it.write(&mut stdout))
|
let stdout = stdout();
|
||||||
});
|
let mut stdout = stdout.lock();
|
||||||
|
writer_receiver.into_iter().try_for_each(|it| it.write(&mut stdout))
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
let (reader_sender, reader_receiver) = bounded::<Message>(0);
|
let (reader_sender, reader_receiver) = bounded::<Message>(0);
|
||||||
let reader = thread::spawn(move || {
|
let reader = thread::Builder::new()
|
||||||
let stdin = stdin();
|
.name("LspServerReader".to_owned())
|
||||||
let mut stdin = stdin.lock();
|
.spawn(move || {
|
||||||
while let Some(msg) = Message::read(&mut stdin)? {
|
let stdin = stdin();
|
||||||
let is_exit = matches!(&msg, Message::Notification(n) if n.is_exit());
|
let mut stdin = stdin.lock();
|
||||||
|
while let Some(msg) = Message::read(&mut stdin)? {
|
||||||
|
let is_exit = matches!(&msg, Message::Notification(n) if n.is_exit());
|
||||||
|
|
||||||
debug!("sending message {:#?}", msg);
|
debug!("sending message {:#?}", msg);
|
||||||
reader_sender.send(msg).expect("receiver was dropped, failed to send a message");
|
reader_sender.send(msg).expect("receiver was dropped, failed to send a message");
|
||||||
|
|
||||||
if is_exit {
|
if is_exit {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
Ok(())
|
||||||
Ok(())
|
})
|
||||||
});
|
.unwrap();
|
||||||
let threads = IoThreads { reader, writer };
|
let threads = IoThreads { reader, writer };
|
||||||
(writer_sender, reader_receiver, threads)
|
(writer_sender, reader_receiver, threads)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue