aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_lsp_server/src/bin
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_lsp_server/src/bin')
-rw-r--r--crates/ra_lsp_server/src/bin/args.rs242
-rw-r--r--crates/ra_lsp_server/src/bin/main.rs98
2 files changed, 340 insertions, 0 deletions
diff --git a/crates/ra_lsp_server/src/bin/args.rs b/crates/ra_lsp_server/src/bin/args.rs
new file mode 100644
index 000000000..3890fe13a
--- /dev/null
+++ b/crates/ra_lsp_server/src/bin/args.rs
@@ -0,0 +1,242 @@
1//! Command like parsing for rust-analyzer.
2//!
3//! If run started args, we run the LSP server loop. With a subcommand, we do a
4//! one-time batch processing.
5
6use anyhow::{bail, Result};
7use pico_args::Arguments;
8use ra_lsp_server::cli::{BenchWhat, Position, Verbosity};
9
10use std::{fmt::Write, path::PathBuf};
11
12pub(crate) struct Args {
13 pub(crate) verbosity: Verbosity,
14 pub(crate) command: Command,
15}
16
17pub(crate) enum Command {
18 Parse {
19 no_dump: bool,
20 },
21 Symbols,
22 Highlight {
23 rainbow: bool,
24 },
25 Stats {
26 randomize: bool,
27 memory_usage: bool,
28 only: Option<String>,
29 with_deps: bool,
30 path: PathBuf,
31 },
32 Bench {
33 path: PathBuf,
34 what: BenchWhat,
35 },
36 RunServer,
37 Version,
38}
39
40impl Args {
41 pub(crate) fn parse() -> Result<Result<Args, HelpPrinted>> {
42 let mut matches = Arguments::from_env();
43
44 if matches.contains("--version") {
45 matches.finish().or_else(handle_extra_flags)?;
46 return Ok(Ok(Args { verbosity: Verbosity::Normal, command: Command::Version }));
47 }
48
49 let verbosity = match (
50 matches.contains(["-vv", "--spammy"]),
51 matches.contains(["-v", "--verbose"]),
52 matches.contains(["-q", "--quiet"]),
53 ) {
54 (true, _, true) => bail!("Invalid flags: -q conflicts with -vv"),
55 (true, _, false) => Verbosity::Spammy,
56 (false, false, false) => Verbosity::Normal,
57 (false, false, true) => Verbosity::Quiet,
58 (false, true, false) => Verbosity::Verbose,
59 (false, true, true) => bail!("Invalid flags: -q conflicts with -v"),
60 };
61
62 let subcommand = match matches.subcommand()? {
63 Some(it) => it,
64 None => {
65 matches.finish().or_else(handle_extra_flags)?;
66 return Ok(Ok(Args { verbosity, command: Command::RunServer }));
67 }
68 };
69 let command = match subcommand.as_str() {
70 "parse" => {
71 if matches.contains(["-h", "--help"]) {
72 eprintln!(
73 "\
74ra-cli-parse
75
76USAGE:
77 ra_lsp_server parse [FLAGS]
78
79FLAGS:
80 -h, --help Prints help inforamtion
81 --no-dump"
82 );
83 return Ok(Err(HelpPrinted));
84 }
85
86 let no_dump = matches.contains("--no-dump");
87 matches.finish().or_else(handle_extra_flags)?;
88 Command::Parse { no_dump }
89 }
90 "symbols" => {
91 if matches.contains(["-h", "--help"]) {
92 eprintln!(
93 "\
94ra-cli-symbols
95
96USAGE:
97 ra_lsp_server highlight [FLAGS]
98
99FLAGS:
100 -h, --help Prints help inforamtion"
101 );
102 return Ok(Err(HelpPrinted));
103 }
104
105 matches.finish().or_else(handle_extra_flags)?;
106
107 Command::Symbols
108 }
109 "highlight" => {
110 if matches.contains(["-h", "--help"]) {
111 eprintln!(
112 "\
113ra-cli-highlight
114
115USAGE:
116 ra_lsp_server highlight [FLAGS]
117
118FLAGS:
119 -h, --help Prints help information
120 -r, --rainbow"
121 );
122 return Ok(Err(HelpPrinted));
123 }
124
125 let rainbow = matches.contains(["-r", "--rainbow"]);
126 matches.finish().or_else(handle_extra_flags)?;
127 Command::Highlight { rainbow }
128 }
129 "analysis-stats" => {
130 if matches.contains(["-h", "--help"]) {
131 eprintln!(
132 "\
133ra-cli-analysis-stats
134
135USAGE:
136 ra_lsp_server analysis-stats [FLAGS] [OPTIONS] [PATH]
137
138FLAGS:
139 -h, --help Prints help information
140 --memory-usage
141 -v, --verbose
142 -q, --quiet
143
144OPTIONS:
145 -o <ONLY>
146
147ARGS:
148 <PATH>"
149 );
150 return Ok(Err(HelpPrinted));
151 }
152
153 let randomize = matches.contains("--randomize");
154 let memory_usage = matches.contains("--memory-usage");
155 let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?;
156 let with_deps: bool = matches.contains("--with-deps");
157 let path = {
158 let mut trailing = matches.free()?;
159 if trailing.len() != 1 {
160 bail!("Invalid flags");
161 }
162 trailing.pop().unwrap().into()
163 };
164
165 Command::Stats { randomize, memory_usage, only, with_deps, path }
166 }
167 "analysis-bench" => {
168 if matches.contains(["-h", "--help"]) {
169 eprintln!(
170 "\
171ra_lsp_server-analysis-bench
172
173USAGE:
174 ra_lsp_server analysis-bench [FLAGS] [OPTIONS] [PATH]
175
176FLAGS:
177 -h, --help Prints help information
178 -v, --verbose
179
180OPTIONS:
181 --complete <PATH:LINE:COLUMN> Compute completions at this location
182 --highlight <PATH> Hightlight this file
183
184ARGS:
185 <PATH> Project to analyse"
186 );
187 return Ok(Err(HelpPrinted));
188 }
189
190 let path: PathBuf = matches.opt_value_from_str("--path")?.unwrap_or_default();
191 let highlight_path: Option<String> = matches.opt_value_from_str("--highlight")?;
192 let complete_path: Option<Position> = matches.opt_value_from_str("--complete")?;
193 let goto_def_path: Option<Position> = matches.opt_value_from_str("--goto-def")?;
194 let what = match (highlight_path, complete_path, goto_def_path) {
195 (Some(path), None, None) => BenchWhat::Highlight { path: path.into() },
196 (None, Some(position), None) => BenchWhat::Complete(position),
197 (None, None, Some(position)) => BenchWhat::GotoDef(position),
198 _ => panic!(
199 "exactly one of `--highlight`, `--complete` or `--goto-def` must be set"
200 ),
201 };
202 Command::Bench { path, what }
203 }
204 _ => {
205 eprintln!(
206 "\
207ra-cli
208
209USAGE:
210 ra_lsp_server <SUBCOMMAND>
211
212FLAGS:
213 -h, --help Prints help information
214
215SUBCOMMANDS:
216 analysis-bench
217 analysis-stats
218 highlight
219 parse
220 symbols"
221 );
222 return Ok(Err(HelpPrinted));
223 }
224 };
225 Ok(Ok(Args { verbosity, command }))
226 }
227}
228
229pub(crate) struct HelpPrinted;
230
231fn handle_extra_flags(e: pico_args::Error) -> Result<()> {
232 if let pico_args::Error::UnusedArgsLeft(flags) = e {
233 let mut invalid_flags = String::new();
234 for flag in flags {
235 write!(&mut invalid_flags, "{}, ", flag)?;
236 }
237 let (invalid_flags, _) = invalid_flags.split_at(invalid_flags.len() - 2);
238 bail!("Invalid flags: {}", invalid_flags);
239 } else {
240 bail!(e);
241 }
242}
diff --git a/crates/ra_lsp_server/src/bin/main.rs b/crates/ra_lsp_server/src/bin/main.rs
new file mode 100644
index 000000000..e25d54a0d
--- /dev/null
+++ b/crates/ra_lsp_server/src/bin/main.rs
@@ -0,0 +1,98 @@
1//! Driver for rust-analyzer.
2//!
3//! Based on cli flags, either spawns an LSP server, or runs a batch analysis
4mod args;
5
6use lsp_server::Connection;
7use ra_lsp_server::{cli, from_json, show_message, Result, ServerConfig};
8use ra_prof;
9
10use crate::args::HelpPrinted;
11
12fn main() -> Result<()> {
13 setup_logging()?;
14 let args = match args::Args::parse()? {
15 Ok(it) => it,
16 Err(HelpPrinted) => return Ok(()),
17 };
18 match args.command {
19 args::Command::Parse { no_dump } => cli::parse(no_dump)?,
20 args::Command::Symbols => cli::symbols()?,
21 args::Command::Highlight { rainbow } => cli::highlight(rainbow)?,
22 args::Command::Stats { randomize, memory_usage, only, with_deps, path } => {
23 cli::analysis_stats(
24 args.verbosity,
25 memory_usage,
26 path.as_ref(),
27 only.as_ref().map(String::as_ref),
28 with_deps,
29 randomize,
30 )?
31 }
32
33 args::Command::Bench { path, what } => {
34 cli::analysis_bench(args.verbosity, path.as_ref(), what)?
35 }
36
37 args::Command::RunServer => run_server()?,
38 args::Command::Version => println!("rust-analyzer {}", env!("REV")),
39 }
40 Ok(())
41}
42
43fn setup_logging() -> Result<()> {
44 std::env::set_var("RUST_BACKTRACE", "short");
45 env_logger::try_init()?;
46 ra_prof::init();
47 Ok(())
48}
49
50fn run_server() -> Result<()> {
51 log::info!("lifecycle: server started");
52
53 let (connection, io_threads) = Connection::stdio();
54 let server_capabilities = serde_json::to_value(ra_lsp_server::server_capabilities()).unwrap();
55
56 let initialize_params = connection.initialize(server_capabilities)?;
57 let initialize_params =
58 from_json::<lsp_types::InitializeParams>("InitializeParams", initialize_params)?;
59
60 if let Some(client_info) = initialize_params.client_info {
61 log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default());
62 }
63
64 let cwd = std::env::current_dir()?;
65 let root = initialize_params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd);
66
67 let workspace_roots = initialize_params
68 .workspace_folders
69 .map(|workspaces| {
70 workspaces.into_iter().filter_map(|it| it.uri.to_file_path().ok()).collect::<Vec<_>>()
71 })
72 .filter(|workspaces| !workspaces.is_empty())
73 .unwrap_or_else(|| vec![root]);
74
75 let server_config = initialize_params
76 .initialization_options
77 .and_then(|v| {
78 from_json::<ServerConfig>("config", v)
79 .map_err(|e| {
80 log::error!("{}", e);
81 show_message(lsp_types::MessageType::Error, e.to_string(), &connection.sender);
82 })
83 .ok()
84 })
85 .unwrap_or_default();
86
87 ra_lsp_server::main_loop(
88 workspace_roots,
89 initialize_params.capabilities,
90 server_config,
91 connection,
92 )?;
93
94 log::info!("shutting down IO...");
95 io_threads.join()?;
96 log::info!("... IO is down");
97 Ok(())
98}