diff options
-rw-r--r-- | crates/assists/src/handlers/inline_local_variable.rs | 31 | ||||
-rw-r--r-- | crates/rust-analyzer/src/bin/args.rs | 492 | ||||
-rw-r--r-- | crates/rust-analyzer/src/bin/logger.rs | 73 | ||||
-rw-r--r-- | crates/rust-analyzer/src/bin/main.rs | 29 | ||||
-rw-r--r-- | crates/rust-analyzer/src/cli/diagnostics.rs | 7 | ||||
-rw-r--r-- | docs/user/manual.adoc | 2 |
6 files changed, 289 insertions, 345 deletions
diff --git a/crates/assists/src/handlers/inline_local_variable.rs b/crates/assists/src/handlers/inline_local_variable.rs index 164bbce86..587eb5feb 100644 --- a/crates/assists/src/handlers/inline_local_variable.rs +++ b/crates/assists/src/handlers/inline_local_variable.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use ide_db::defs::Definition; | 1 | use ide_db::{defs::Definition, search::ReferenceKind}; |
2 | use syntax::{ | 2 | use syntax::{ |
3 | ast::{self, AstNode, AstToken}, | 3 | ast::{self, AstNode, AstToken}, |
4 | TextRange, | 4 | TextRange, |
@@ -119,7 +119,13 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O | |||
119 | for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) { | 119 | for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) { |
120 | let replacement = | 120 | let replacement = |
121 | if should_wrap { init_in_paren.clone() } else { init_str.clone() }; | 121 | if should_wrap { init_in_paren.clone() } else { init_str.clone() }; |
122 | builder.replace(desc.file_range.range, replacement) | 122 | match desc.kind { |
123 | ReferenceKind::FieldShorthandForLocal => { | ||
124 | mark::hit!(inline_field_shorthand); | ||
125 | builder.insert(desc.file_range.range.end(), format!(": {}", replacement)) | ||
126 | } | ||
127 | _ => builder.replace(desc.file_range.range, replacement), | ||
128 | } | ||
123 | } | 129 | } |
124 | }, | 130 | }, |
125 | ) | 131 | ) |
@@ -667,6 +673,27 @@ fn foo() { | |||
667 | } | 673 | } |
668 | 674 | ||
669 | #[test] | 675 | #[test] |
676 | fn inline_field_shorthand() { | ||
677 | mark::check!(inline_field_shorthand); | ||
678 | check_assist( | ||
679 | inline_local_variable, | ||
680 | r" | ||
681 | struct S { foo: i32} | ||
682 | fn main() { | ||
683 | let <|>foo = 92; | ||
684 | S { foo } | ||
685 | } | ||
686 | ", | ||
687 | r" | ||
688 | struct S { foo: i32} | ||
689 | fn main() { | ||
690 | S { foo: 92 } | ||
691 | } | ||
692 | ", | ||
693 | ); | ||
694 | } | ||
695 | |||
696 | #[test] | ||
670 | fn test_not_applicable_if_variable_unused() { | 697 | fn test_not_applicable_if_variable_unused() { |
671 | mark::check!(test_not_applicable_if_variable_unused); | 698 | mark::check!(test_not_applicable_if_variable_unused); |
672 | check_assist_not_applicable( | 699 | check_assist_not_applicable( |
diff --git a/crates/rust-analyzer/src/bin/args.rs b/crates/rust-analyzer/src/bin/args.rs index 0bc92431a..8ddf1e031 100644 --- a/crates/rust-analyzer/src/bin/args.rs +++ b/crates/rust-analyzer/src/bin/args.rs | |||
@@ -3,9 +3,9 @@ | |||
3 | //! If run started args, we run the LSP server loop. With a subcommand, we do a | 3 | //! If run started args, we run the LSP server loop. With a subcommand, we do a |
4 | //! one-time batch processing. | 4 | //! one-time batch processing. |
5 | 5 | ||
6 | use std::{env, fmt::Write, path::PathBuf}; | 6 | use std::{env, path::PathBuf}; |
7 | 7 | ||
8 | use anyhow::{bail, Result}; | 8 | use anyhow::{bail, format_err, Result}; |
9 | use pico_args::Arguments; | 9 | use pico_args::Arguments; |
10 | use rust_analyzer::cli::{AnalysisStatsCmd, BenchCmd, BenchWhat, Position, Verbosity}; | 10 | use rust_analyzer::cli::{AnalysisStatsCmd, BenchCmd, BenchWhat, Position, Verbosity}; |
11 | use ssr::{SsrPattern, SsrRule}; | 11 | use ssr::{SsrPattern, SsrRule}; |
@@ -13,47 +13,107 @@ use vfs::AbsPathBuf; | |||
13 | 13 | ||
14 | pub(crate) struct Args { | 14 | pub(crate) struct Args { |
15 | pub(crate) verbosity: Verbosity, | 15 | pub(crate) verbosity: Verbosity, |
16 | pub(crate) log_file: Option<PathBuf>, | ||
16 | pub(crate) command: Command, | 17 | pub(crate) command: Command, |
17 | } | 18 | } |
18 | 19 | ||
19 | pub(crate) enum Command { | 20 | pub(crate) enum Command { |
20 | Parse { | 21 | Parse { no_dump: bool }, |
21 | no_dump: bool, | ||
22 | }, | ||
23 | Symbols, | 22 | Symbols, |
24 | Highlight { | 23 | Highlight { rainbow: bool }, |
25 | rainbow: bool, | ||
26 | }, | ||
27 | AnalysisStats(AnalysisStatsCmd), | 24 | AnalysisStats(AnalysisStatsCmd), |
28 | Bench(BenchCmd), | 25 | Bench(BenchCmd), |
29 | Diagnostics { | 26 | Diagnostics { path: PathBuf, load_output_dirs: bool, with_proc_macro: bool }, |
30 | path: PathBuf, | 27 | Ssr { rules: Vec<SsrRule> }, |
31 | load_output_dirs: bool, | 28 | StructuredSearch { debug_snippet: Option<String>, patterns: Vec<SsrPattern> }, |
32 | with_proc_macro: bool, | ||
33 | /// Include files which are not modules. In rust-analyzer | ||
34 | /// this would include the parser test files. | ||
35 | all: bool, | ||
36 | }, | ||
37 | Ssr { | ||
38 | rules: Vec<SsrRule>, | ||
39 | }, | ||
40 | StructuredSearch { | ||
41 | debug_snippet: Option<String>, | ||
42 | patterns: Vec<SsrPattern>, | ||
43 | }, | ||
44 | ProcMacro, | 29 | ProcMacro, |
45 | RunServer, | 30 | RunServer, |
46 | Version, | 31 | Version, |
47 | Help, | 32 | Help, |
48 | } | 33 | } |
49 | 34 | ||
35 | const HELP: &str = "\ | ||
36 | rust-analyzer | ||
37 | |||
38 | USAGE: | ||
39 | rust-analyzer [FLAGS] [COMMAND] [COMMAND_OPTIONS] | ||
40 | |||
41 | FLAGS: | ||
42 | --version Print version | ||
43 | -h, --help Print this help | ||
44 | |||
45 | -v, --verbose | ||
46 | -vv, --spammy | ||
47 | -q, --quiet Set verbosity | ||
48 | |||
49 | --log-file <PATH> Log to the specified filed instead of stderr | ||
50 | |||
51 | ENVIRONMENTAL VARIABLES: | ||
52 | RA_LOG Set log filter in env_logger format | ||
53 | RA_PROFILE Enable hierarchical profiler | ||
54 | |||
55 | COMMANDS: | ||
56 | |||
57 | not specified Launch LSP server | ||
58 | |||
59 | parse < main.rs Parse tree | ||
60 | --no-dump Suppress printing | ||
61 | |||
62 | symbols < main.rs Parse input an print the list of symbols | ||
63 | |||
64 | highlight < main.rs Highlight input as html | ||
65 | --rainbow Enable rainbow highlighting of identifiers | ||
66 | |||
67 | analysis-stats <PATH> Batch typecheck project and print summary statistics | ||
68 | <PATH> Directory with Cargo.toml | ||
69 | --randomize Randomize order in which crates, modules, and items are processed | ||
70 | --parallel Run type inference in parallel | ||
71 | --memory-usage Collect memory usage statistics | ||
72 | -o, --only <PATH> Only analyze items matching this path | ||
73 | --with-deps Also analyze all dependencies | ||
74 | --load-output-dirs | ||
75 | Load OUT_DIR values by running `cargo check` before analysis | ||
76 | --with-proc-macro Use proc-macro-srv for proc-macro expanding | ||
77 | |||
78 | analysis-bench <PATH> Benchmark specific analysis operation | ||
79 | <PATH> Directory with Cargo.toml | ||
80 | --highlight <PATH> | ||
81 | Compute syntax highlighting for this file | ||
82 | --complete <PATH:LINE:COLUMN> | ||
83 | Compute completions at this location | ||
84 | --goto-def <PATH:LINE:COLUMN> | ||
85 | Compute goto definition at this location | ||
86 | --memory-usage Collect memory usage statistics | ||
87 | --load-output-dirs | ||
88 | Load OUT_DIR values by running `cargo check` before analysis | ||
89 | --with-proc-macro Use proc-macro-srv for proc-macro expanding | ||
90 | |||
91 | diagnostics <PATH> | ||
92 | <PATH> Directory with Cargo.toml | ||
93 | --load-output-dirs | ||
94 | Load OUT_DIR values by running `cargo check` before analysis | ||
95 | --with-proc-macro Use proc-macro-srv for proc-macro expanding | ||
96 | |||
97 | ssr [RULE...] | ||
98 | <RULE> A structured search replace rule (`$a.foo($b) ==> bar($a, $b)`) | ||
99 | |||
100 | search [PATTERN..] | ||
101 | <PATTERN> A structured search replace pattern (`$a.foo($b)`) | ||
102 | --debug <snippet> Prints debug information for any nodes with source exactly | ||
103 | equal to <snippet> | ||
104 | "; | ||
105 | |||
50 | impl Args { | 106 | impl Args { |
51 | pub(crate) fn parse() -> Result<Args> { | 107 | pub(crate) fn parse() -> Result<Args> { |
52 | let mut matches = Arguments::from_env(); | 108 | let mut matches = Arguments::from_env(); |
53 | 109 | ||
54 | if matches.contains("--version") { | 110 | if matches.contains("--version") { |
55 | matches.finish().or_else(handle_extra_flags)?; | 111 | matches.finish()?; |
56 | return Ok(Args { verbosity: Verbosity::Normal, command: Command::Version }); | 112 | return Ok(Args { |
113 | verbosity: Verbosity::Normal, | ||
114 | log_file: None, | ||
115 | command: Command::Version, | ||
116 | }); | ||
57 | } | 117 | } |
58 | 118 | ||
59 | let verbosity = match ( | 119 | let verbosity = match ( |
@@ -68,320 +128,96 @@ impl Args { | |||
68 | (false, true, false) => Verbosity::Verbose, | 128 | (false, true, false) => Verbosity::Verbose, |
69 | (false, true, true) => bail!("Invalid flags: -q conflicts with -v"), | 129 | (false, true, true) => bail!("Invalid flags: -q conflicts with -v"), |
70 | }; | 130 | }; |
131 | let log_file = matches.opt_value_from_str("--log-file")?; | ||
132 | |||
133 | if matches.contains(["-h", "--help"]) { | ||
134 | eprintln!("{}", HELP); | ||
135 | return Ok(Args { verbosity, log_file: None, command: Command::Help }); | ||
136 | } | ||
71 | 137 | ||
72 | let help = Ok(Args { verbosity, command: Command::Help }); | ||
73 | let subcommand = match matches.subcommand()? { | 138 | let subcommand = match matches.subcommand()? { |
74 | Some(it) => it, | 139 | Some(it) => it, |
75 | None => { | 140 | None => { |
76 | if matches.contains(["-h", "--help"]) { | 141 | matches.finish()?; |
77 | print_subcommands(); | 142 | return Ok(Args { verbosity, log_file, command: Command::RunServer }); |
78 | return help; | ||
79 | } | ||
80 | matches.finish().or_else(handle_extra_flags)?; | ||
81 | return Ok(Args { verbosity, command: Command::RunServer }); | ||
82 | } | 143 | } |
83 | }; | 144 | }; |
84 | let command = match subcommand.as_str() { | 145 | let command = match subcommand.as_str() { |
85 | "parse" => { | 146 | "parse" => Command::Parse { no_dump: matches.contains("--no-dump") }, |
86 | if matches.contains(["-h", "--help"]) { | 147 | "symbols" => Command::Symbols, |
87 | eprintln!( | 148 | "highlight" => Command::Highlight { rainbow: matches.contains("--rainbow") }, |
88 | "\ | 149 | "analysis-stats" => Command::AnalysisStats(AnalysisStatsCmd { |
89 | rust-analyzer parse | 150 | randomize: matches.contains("--randomize"), |
90 | 151 | parallel: matches.contains("--parallel"), | |
91 | USAGE: | 152 | memory_usage: matches.contains("--memory-usage"), |
92 | rust-analyzer parse [FLAGS] | 153 | only: matches.opt_value_from_str(["-o", "--only"])?, |
93 | 154 | with_deps: matches.contains("--with-deps"), | |
94 | FLAGS: | 155 | load_output_dirs: matches.contains("--load-output-dirs"), |
95 | -h, --help Prints help information | 156 | with_proc_macro: matches.contains("--with-proc-macro"), |
96 | --no-dump" | 157 | path: matches |
97 | ); | 158 | .free_from_str()? |
98 | return help; | 159 | .ok_or_else(|| format_err!("expected positional argument"))?, |
99 | } | 160 | }), |
100 | 161 | "analysis-bench" => Command::Bench(BenchCmd { | |
101 | let no_dump = matches.contains("--no-dump"); | 162 | what: { |
102 | matches.finish().or_else(handle_extra_flags)?; | 163 | let highlight_path: Option<String> = |
103 | Command::Parse { no_dump } | 164 | matches.opt_value_from_str("--highlight")?; |
104 | } | 165 | let complete_path: Option<Position> = |
105 | "symbols" => { | 166 | matches.opt_value_from_str("--complete")?; |
106 | if matches.contains(["-h", "--help"]) { | 167 | let goto_def_path: Option<Position> = |
107 | eprintln!( | 168 | matches.opt_value_from_str("--goto-def")?; |
108 | "\ | 169 | match (highlight_path, complete_path, goto_def_path) { |
109 | rust-analyzer symbols | 170 | (Some(path), None, None) => { |
110 | 171 | let path = env::current_dir().unwrap().join(path); | |
111 | USAGE: | 172 | BenchWhat::Highlight { path: AbsPathBuf::assert(path) } |
112 | rust-analyzer highlight [FLAGS] | 173 | } |
113 | 174 | (None, Some(position), None) => BenchWhat::Complete(position), | |
114 | FLAGS: | 175 | (None, None, Some(position)) => BenchWhat::GotoDef(position), |
115 | -h, --help Prints help inforamtion" | 176 | _ => panic!( |
116 | ); | 177 | "exactly one of `--highlight`, `--complete` or `--goto-def` must be set" |
117 | return help; | 178 | ), |
118 | } | 179 | } |
119 | 180 | }, | |
120 | matches.finish().or_else(handle_extra_flags)?; | 181 | memory_usage: matches.contains("--memory-usage"), |
121 | 182 | load_output_dirs: matches.contains("--load-output-dirs"), | |
122 | Command::Symbols | 183 | with_proc_macro: matches.contains("--with-proc-macro"), |
123 | } | 184 | path: matches |
124 | "highlight" => { | 185 | .free_from_str()? |
125 | if matches.contains(["-h", "--help"]) { | 186 | .ok_or_else(|| format_err!("expected positional argument"))?, |
126 | eprintln!( | 187 | }), |
127 | "\ | 188 | "diagnostics" => Command::Diagnostics { |
128 | rust-analyzer highlight | 189 | load_output_dirs: matches.contains("--load-output-dirs"), |
129 | 190 | with_proc_macro: matches.contains("--with-proc-macro"), | |
130 | USAGE: | 191 | path: matches |
131 | rust-analyzer highlight [FLAGS] | 192 | .free_from_str()? |
132 | 193 | .ok_or_else(|| format_err!("expected positional argument"))?, | |
133 | FLAGS: | 194 | }, |
134 | -h, --help Prints help information | 195 | "proc-macro" => Command::ProcMacro, |
135 | -r, --rainbow" | 196 | "ssr" => Command::Ssr { |
136 | ); | 197 | rules: { |
137 | return help; | 198 | let mut acc = Vec::new(); |
138 | } | 199 | while let Some(rule) = matches.free_from_str()? { |
139 | 200 | acc.push(rule); | |
140 | let rainbow = matches.contains(["-r", "--rainbow"]); | ||
141 | matches.finish().or_else(handle_extra_flags)?; | ||
142 | Command::Highlight { rainbow } | ||
143 | } | ||
144 | "analysis-stats" => { | ||
145 | if matches.contains(["-h", "--help"]) { | ||
146 | eprintln!( | ||
147 | "\ | ||
148 | rust-analyzer analysis-stats | ||
149 | |||
150 | USAGE: | ||
151 | rust-analyzer analysis-stats [FLAGS] [OPTIONS] [PATH] | ||
152 | |||
153 | FLAGS: | ||
154 | -o, --only Only analyze items matching this path | ||
155 | -h, --help Prints help information | ||
156 | --memory-usage Collect memory usage statistics | ||
157 | --randomize Randomize order in which crates, modules, and items are processed | ||
158 | --parallel Run type inference in parallel | ||
159 | --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis | ||
160 | --with-proc-macro Use ra-proc-macro-srv for proc-macro expanding | ||
161 | --with-deps Also analyze all dependencies | ||
162 | -v, --verbose | ||
163 | -q, --quiet | ||
164 | |||
165 | OPTIONS: | ||
166 | -o <ONLY> | ||
167 | |||
168 | ARGS: | ||
169 | <PATH>" | ||
170 | ); | ||
171 | return help; | ||
172 | } | ||
173 | |||
174 | let randomize = matches.contains("--randomize"); | ||
175 | let parallel = matches.contains("--parallel"); | ||
176 | let memory_usage = matches.contains("--memory-usage"); | ||
177 | let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?; | ||
178 | let with_deps: bool = matches.contains("--with-deps"); | ||
179 | let load_output_dirs = matches.contains("--load-output-dirs"); | ||
180 | let with_proc_macro = matches.contains("--with-proc-macro"); | ||
181 | let path = { | ||
182 | let mut trailing = matches.free()?; | ||
183 | if trailing.len() != 1 { | ||
184 | bail!("Invalid flags"); | ||
185 | } | ||
186 | trailing.pop().unwrap().into() | ||
187 | }; | ||
188 | |||
189 | Command::AnalysisStats(AnalysisStatsCmd { | ||
190 | randomize, | ||
191 | parallel, | ||
192 | memory_usage, | ||
193 | only, | ||
194 | with_deps, | ||
195 | path, | ||
196 | load_output_dirs, | ||
197 | with_proc_macro, | ||
198 | }) | ||
199 | } | ||
200 | "analysis-bench" => { | ||
201 | if matches.contains(["-h", "--help"]) { | ||
202 | eprintln!( | ||
203 | "\ | ||
204 | rust-analyzer analysis-bench | ||
205 | |||
206 | USAGE: | ||
207 | rust-analyzer analysis-bench [FLAGS] [OPTIONS] | ||
208 | |||
209 | FLAGS: | ||
210 | -h, --help Prints help information | ||
211 | --memory-usage Collect memory usage statistics | ||
212 | --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis | ||
213 | --with-proc-macro Use ra-proc-macro-srv for proc-macro expanding | ||
214 | -v, --verbose | ||
215 | |||
216 | OPTIONS: | ||
217 | --project <PATH> Path to directory with Cargo.toml | ||
218 | --complete <PATH:LINE:COLUMN> Compute completions at this location | ||
219 | --goto-def <PATH:LINE:COLUMN> Compute goto definition at this location | ||
220 | --highlight <PATH> Hightlight this file | ||
221 | |||
222 | ARGS: | ||
223 | <PATH> Project to analyse" | ||
224 | ); | ||
225 | return help; | ||
226 | } | ||
227 | |||
228 | let path: PathBuf = matches.opt_value_from_str("--project")?.unwrap_or_default(); | ||
229 | let highlight_path: Option<String> = matches.opt_value_from_str("--highlight")?; | ||
230 | let complete_path: Option<Position> = matches.opt_value_from_str("--complete")?; | ||
231 | let goto_def_path: Option<Position> = matches.opt_value_from_str("--goto-def")?; | ||
232 | let what = match (highlight_path, complete_path, goto_def_path) { | ||
233 | (Some(path), None, None) => { | ||
234 | let path = env::current_dir().unwrap().join(path); | ||
235 | BenchWhat::Highlight { path: AbsPathBuf::assert(path) } | ||
236 | } | 201 | } |
237 | (None, Some(position), None) => BenchWhat::Complete(position), | 202 | acc |
238 | (None, None, Some(position)) => BenchWhat::GotoDef(position), | 203 | }, |
239 | _ => panic!( | 204 | }, |
240 | "exactly one of `--highlight`, `--complete` or `--goto-def` must be set" | 205 | "search" => Command::StructuredSearch { |
241 | ), | 206 | debug_snippet: matches.opt_value_from_str("--debug")?, |
242 | }; | 207 | patterns: { |
243 | let memory_usage = matches.contains("--memory-usage"); | 208 | let mut acc = Vec::new(); |
244 | let load_output_dirs = matches.contains("--load-output-dirs"); | 209 | while let Some(rule) = matches.free_from_str()? { |
245 | let with_proc_macro = matches.contains("--with-proc-macro"); | 210 | acc.push(rule); |
246 | Command::Bench(BenchCmd { | ||
247 | memory_usage, | ||
248 | path, | ||
249 | what, | ||
250 | load_output_dirs, | ||
251 | with_proc_macro, | ||
252 | }) | ||
253 | } | ||
254 | "diagnostics" => { | ||
255 | if matches.contains(["-h", "--help"]) { | ||
256 | eprintln!( | ||
257 | "\ | ||
258 | rust-analyzer diagnostics | ||
259 | |||
260 | USAGE: | ||
261 | rust-analyzer diagnostics [FLAGS] [PATH] | ||
262 | |||
263 | FLAGS: | ||
264 | -h, --help Prints help information | ||
265 | --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis | ||
266 | --all Include all files rather than only modules | ||
267 | |||
268 | ARGS: | ||
269 | <PATH>" | ||
270 | ); | ||
271 | return help; | ||
272 | } | ||
273 | |||
274 | let load_output_dirs = matches.contains("--load-output-dirs"); | ||
275 | let with_proc_macro = matches.contains("--with-proc-macro"); | ||
276 | let all = matches.contains("--all"); | ||
277 | let path = { | ||
278 | let mut trailing = matches.free()?; | ||
279 | if trailing.len() != 1 { | ||
280 | bail!("Invalid flags"); | ||
281 | } | 211 | } |
282 | trailing.pop().unwrap().into() | 212 | acc |
283 | }; | 213 | }, |
284 | 214 | }, | |
285 | Command::Diagnostics { path, load_output_dirs, with_proc_macro, all } | ||
286 | } | ||
287 | "proc-macro" => Command::ProcMacro, | ||
288 | "ssr" => { | ||
289 | if matches.contains(["-h", "--help"]) { | ||
290 | eprintln!( | ||
291 | "\ | ||
292 | rust-analyzer ssr | ||
293 | |||
294 | USAGE: | ||
295 | rust-analyzer ssr [FLAGS] [RULE...] | ||
296 | |||
297 | EXAMPLE: | ||
298 | rust-analyzer ssr '$a.foo($b) ==> bar($a, $b)' | ||
299 | |||
300 | FLAGS: | ||
301 | --debug <snippet> Prints debug information for any nodes with source exactly equal to <snippet> | ||
302 | -h, --help Prints help information | ||
303 | |||
304 | ARGS: | ||
305 | <RULE> A structured search replace rule" | ||
306 | ); | ||
307 | return help; | ||
308 | } | ||
309 | let mut rules = Vec::new(); | ||
310 | while let Some(rule) = matches.free_from_str()? { | ||
311 | rules.push(rule); | ||
312 | } | ||
313 | Command::Ssr { rules } | ||
314 | } | ||
315 | "search" => { | ||
316 | if matches.contains(["-h", "--help"]) { | ||
317 | eprintln!( | ||
318 | "\ | ||
319 | rust-analyzer search | ||
320 | |||
321 | USAGE: | ||
322 | rust-analyzer search [FLAGS] [PATTERN...] | ||
323 | |||
324 | EXAMPLE: | ||
325 | rust-analyzer search '$a.foo($b)' | ||
326 | |||
327 | FLAGS: | ||
328 | --debug <snippet> Prints debug information for any nodes with source exactly equal to <snippet> | ||
329 | -h, --help Prints help information | ||
330 | |||
331 | ARGS: | ||
332 | <PATTERN> A structured search pattern" | ||
333 | ); | ||
334 | return help; | ||
335 | } | ||
336 | let debug_snippet = matches.opt_value_from_str("--debug")?; | ||
337 | let mut patterns = Vec::new(); | ||
338 | while let Some(rule) = matches.free_from_str()? { | ||
339 | patterns.push(rule); | ||
340 | } | ||
341 | Command::StructuredSearch { patterns, debug_snippet } | ||
342 | } | ||
343 | _ => { | 215 | _ => { |
344 | print_subcommands(); | 216 | eprintln!("{}", HELP); |
345 | return help; | 217 | return Ok(Args { verbosity, log_file: None, command: Command::Help }); |
346 | } | 218 | } |
347 | }; | 219 | }; |
348 | Ok(Args { verbosity, command }) | 220 | matches.finish()?; |
349 | } | 221 | Ok(Args { verbosity, log_file, command }) |
350 | } | ||
351 | |||
352 | fn print_subcommands() { | ||
353 | eprintln!( | ||
354 | "\ | ||
355 | rust-analyzer | ||
356 | |||
357 | USAGE: | ||
358 | rust-analyzer <SUBCOMMAND> | ||
359 | |||
360 | FLAGS: | ||
361 | -h, --help Prints help information | ||
362 | |||
363 | SUBCOMMANDS: | ||
364 | analysis-bench | ||
365 | analysis-stats | ||
366 | highlight | ||
367 | diagnostics | ||
368 | proc-macro | ||
369 | parse | ||
370 | search | ||
371 | ssr | ||
372 | symbols" | ||
373 | ) | ||
374 | } | ||
375 | |||
376 | fn handle_extra_flags(e: pico_args::Error) -> Result<()> { | ||
377 | if let pico_args::Error::UnusedArgsLeft(flags) = e { | ||
378 | let mut invalid_flags = String::new(); | ||
379 | for flag in flags { | ||
380 | write!(&mut invalid_flags, "{}, ", flag)?; | ||
381 | } | ||
382 | let (invalid_flags, _) = invalid_flags.split_at(invalid_flags.len() - 2); | ||
383 | bail!("Invalid flags: {}", invalid_flags); | ||
384 | } else { | ||
385 | bail!(e); | ||
386 | } | 222 | } |
387 | } | 223 | } |
diff --git a/crates/rust-analyzer/src/bin/logger.rs b/crates/rust-analyzer/src/bin/logger.rs new file mode 100644 index 000000000..3bcb1ae37 --- /dev/null +++ b/crates/rust-analyzer/src/bin/logger.rs | |||
@@ -0,0 +1,73 @@ | |||
1 | //! Simple logger that logs either to stderr or to a file, using `env_logger` | ||
2 | //! filter syntax. Amusingly, there's no crates.io crate that can do this and | ||
3 | //! only this. | ||
4 | |||
5 | use std::{ | ||
6 | fs::File, | ||
7 | io::{BufWriter, Write}, | ||
8 | }; | ||
9 | |||
10 | use env_logger::filter::{Builder, Filter}; | ||
11 | use log::{Log, Metadata, Record}; | ||
12 | use parking_lot::Mutex; | ||
13 | |||
14 | pub(crate) struct Logger { | ||
15 | filter: Filter, | ||
16 | file: Option<Mutex<BufWriter<File>>>, | ||
17 | } | ||
18 | |||
19 | impl Logger { | ||
20 | pub(crate) fn new(log_file: Option<File>, filter: Option<&str>) -> Logger { | ||
21 | let filter = { | ||
22 | let mut builder = Builder::new(); | ||
23 | if let Some(filter) = filter { | ||
24 | builder.parse(filter); | ||
25 | } | ||
26 | builder.build() | ||
27 | }; | ||
28 | |||
29 | let file = log_file.map(|it| Mutex::new(BufWriter::new(it))); | ||
30 | |||
31 | Logger { filter, file } | ||
32 | } | ||
33 | |||
34 | pub(crate) fn install(self) { | ||
35 | let max_level = self.filter.filter(); | ||
36 | let _ = log::set_boxed_logger(Box::new(self)).map(|()| log::set_max_level(max_level)); | ||
37 | } | ||
38 | } | ||
39 | |||
40 | impl Log for Logger { | ||
41 | fn enabled(&self, metadata: &Metadata) -> bool { | ||
42 | self.filter.enabled(metadata) | ||
43 | } | ||
44 | |||
45 | fn log(&self, record: &Record) { | ||
46 | if !self.filter.matches(record) { | ||
47 | return; | ||
48 | } | ||
49 | match &self.file { | ||
50 | Some(w) => { | ||
51 | let _ = writeln!( | ||
52 | w.lock(), | ||
53 | "[{} {}] {}", | ||
54 | record.level(), | ||
55 | record.module_path().unwrap_or_default(), | ||
56 | record.args(), | ||
57 | ); | ||
58 | } | ||
59 | None => eprintln!( | ||
60 | "[{} {}] {}", | ||
61 | record.level(), | ||
62 | record.module_path().unwrap_or_default(), | ||
63 | record.args(), | ||
64 | ), | ||
65 | } | ||
66 | } | ||
67 | |||
68 | fn flush(&self) { | ||
69 | if let Some(w) = &self.file { | ||
70 | let _ = w.lock().flush(); | ||
71 | } | ||
72 | } | ||
73 | } | ||
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 0e03a0ca8..ba4402ade 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs | |||
@@ -2,8 +2,9 @@ | |||
2 | //! | 2 | //! |
3 | //! Based on cli flags, either spawns an LSP server, or runs a batch analysis | 3 | //! Based on cli flags, either spawns an LSP server, or runs a batch analysis |
4 | mod args; | 4 | mod args; |
5 | mod logger; | ||
5 | 6 | ||
6 | use std::{convert::TryFrom, process}; | 7 | use std::{convert::TryFrom, env, fs, path::PathBuf, process}; |
7 | 8 | ||
8 | use lsp_server::Connection; | 9 | use lsp_server::Connection; |
9 | use project_model::ProjectManifest; | 10 | use project_model::ProjectManifest; |
@@ -26,8 +27,8 @@ fn main() { | |||
26 | } | 27 | } |
27 | 28 | ||
28 | fn try_main() -> Result<()> { | 29 | fn try_main() -> Result<()> { |
29 | setup_logging()?; | ||
30 | let args = args::Args::parse()?; | 30 | let args = args::Args::parse()?; |
31 | setup_logging(args.log_file)?; | ||
31 | match args.command { | 32 | match args.command { |
32 | args::Command::RunServer => run_server()?, | 33 | args::Command::RunServer => run_server()?, |
33 | args::Command::ProcMacro => proc_macro_srv::cli::run()?, | 34 | args::Command::ProcMacro => proc_macro_srv::cli::run()?, |
@@ -37,8 +38,8 @@ fn try_main() -> Result<()> { | |||
37 | args::Command::Highlight { rainbow } => cli::highlight(rainbow)?, | 38 | args::Command::Highlight { rainbow } => cli::highlight(rainbow)?, |
38 | args::Command::AnalysisStats(cmd) => cmd.run(args.verbosity)?, | 39 | args::Command::AnalysisStats(cmd) => cmd.run(args.verbosity)?, |
39 | args::Command::Bench(cmd) => cmd.run(args.verbosity)?, | 40 | args::Command::Bench(cmd) => cmd.run(args.verbosity)?, |
40 | args::Command::Diagnostics { path, load_output_dirs, with_proc_macro, all } => { | 41 | args::Command::Diagnostics { path, load_output_dirs, with_proc_macro } => { |
41 | cli::diagnostics(path.as_ref(), load_output_dirs, with_proc_macro, all)? | 42 | cli::diagnostics(path.as_ref(), load_output_dirs, with_proc_macro)? |
42 | } | 43 | } |
43 | args::Command::Ssr { rules } => { | 44 | args::Command::Ssr { rules } => { |
44 | cli::apply_ssr_rules(rules)?; | 45 | cli::apply_ssr_rules(rules)?; |
@@ -52,9 +53,21 @@ fn try_main() -> Result<()> { | |||
52 | Ok(()) | 53 | Ok(()) |
53 | } | 54 | } |
54 | 55 | ||
55 | fn setup_logging() -> Result<()> { | 56 | fn setup_logging(log_file: Option<PathBuf>) -> Result<()> { |
56 | std::env::set_var("RUST_BACKTRACE", "short"); | 57 | env::set_var("RUST_BACKTRACE", "short"); |
57 | env_logger::try_init_from_env("RA_LOG")?; | 58 | |
59 | let log_file = match log_file { | ||
60 | Some(path) => { | ||
61 | if let Some(parent) = path.parent() { | ||
62 | let _ = fs::create_dir_all(parent); | ||
63 | } | ||
64 | Some(fs::File::create(path)?) | ||
65 | } | ||
66 | None => None, | ||
67 | }; | ||
68 | let filter = env::var("RA_LOG").ok(); | ||
69 | logger::Logger::new(log_file, filter.as_deref()).install(); | ||
70 | |||
58 | profile::init(); | 71 | profile::init(); |
59 | Ok(()) | 72 | Ok(()) |
60 | } | 73 | } |
@@ -95,7 +108,7 @@ fn run_server() -> Result<()> { | |||
95 | { | 108 | { |
96 | Some(it) => it, | 109 | Some(it) => it, |
97 | None => { | 110 | None => { |
98 | let cwd = std::env::current_dir()?; | 111 | let cwd = env::current_dir()?; |
99 | AbsPathBuf::assert(cwd) | 112 | AbsPathBuf::assert(cwd) |
100 | } | 113 | } |
101 | }; | 114 | }; |
diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs index c424aa6e2..f3b6c900e 100644 --- a/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/crates/rust-analyzer/src/cli/diagnostics.rs | |||
@@ -12,12 +12,7 @@ use ide::{DiagnosticsConfig, Severity}; | |||
12 | 12 | ||
13 | use crate::cli::{load_cargo::load_cargo, Result}; | 13 | use crate::cli::{load_cargo::load_cargo, Result}; |
14 | 14 | ||
15 | pub fn diagnostics( | 15 | pub fn diagnostics(path: &Path, load_output_dirs: bool, with_proc_macro: bool) -> Result<()> { |
16 | path: &Path, | ||
17 | load_output_dirs: bool, | ||
18 | with_proc_macro: bool, | ||
19 | _all: bool, | ||
20 | ) -> Result<()> { | ||
21 | let (host, _vfs) = load_cargo(path, load_output_dirs, with_proc_macro)?; | 16 | let (host, _vfs) = load_cargo(path, load_output_dirs, with_proc_macro)?; |
22 | let db = host.raw_database(); | 17 | let db = host.raw_database(); |
23 | let analysis = host.analysis(); | 18 | let analysis = host.analysis(); |
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index 367bba546..b15c9ee7f 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc | |||
@@ -351,7 +351,7 @@ Relative paths are interpreted relative to `rust-project.json` file location or | |||
351 | 351 | ||
352 | See https://github.com/rust-analyzer/rust-project.json-example for a small example. | 352 | See https://github.com/rust-analyzer/rust-project.json-example for a small example. |
353 | 353 | ||
354 | You can set `RA_LOG` environmental variable to `"'rust_analyzer=info"` to inspect how rust-analyzer handles config and project loading. | 354 | You can set `RA_LOG` environmental variable to `rust_analyzer=info` to inspect how rust-analyzer handles config and project loading. |
355 | 355 | ||
356 | == Features | 356 | == Features |
357 | 357 | ||