diff options
Diffstat (limited to 'crates')
26 files changed, 435 insertions, 608 deletions
diff --git a/crates/gen_lsp_server/Cargo.toml b/crates/gen_lsp_server/Cargo.toml index 36a29265c..d0ca19f52 100644 --- a/crates/gen_lsp_server/Cargo.toml +++ b/crates/gen_lsp_server/Cargo.toml | |||
@@ -15,4 +15,4 @@ serde = { version = "1.0.83", features = ["derive"] } | |||
15 | crossbeam-channel = "0.3.5" | 15 | crossbeam-channel = "0.3.5" |
16 | 16 | ||
17 | [dev-dependencies] | 17 | [dev-dependencies] |
18 | flexi_logger = "0.13.0" | 18 | flexi_logger = "0.14.0" |
diff --git a/crates/ra_cli/Cargo.toml b/crates/ra_cli/Cargo.toml index 4d9c41ae9..970cb1f82 100644 --- a/crates/ra_cli/Cargo.toml +++ b/crates/ra_cli/Cargo.toml | |||
@@ -7,7 +7,7 @@ publish = false | |||
7 | 7 | ||
8 | [dependencies] | 8 | [dependencies] |
9 | clap = "2.32.0" | 9 | clap = "2.32.0" |
10 | flexi_logger = "0.13.0" | 10 | flexi_logger = "0.14.0" |
11 | indicatif = "0.11.0" | 11 | indicatif = "0.11.0" |
12 | 12 | ||
13 | ra_syntax = { path = "../ra_syntax" } | 13 | ra_syntax = { path = "../ra_syntax" } |
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index fc9bc33d2..c2c6921cb 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs | |||
@@ -256,6 +256,14 @@ impl SourceAnalyzer { | |||
256 | Some(self.infer.as_ref()?[pat_id].clone()) | 256 | Some(self.infer.as_ref()?[pat_id].clone()) |
257 | } | 257 | } |
258 | 258 | ||
259 | pub fn type_of_pat_by_id( | ||
260 | &self, | ||
261 | _db: &impl HirDatabase, | ||
262 | pat_id: expr::PatId, | ||
263 | ) -> Option<crate::Ty> { | ||
264 | Some(self.infer.as_ref()?[pat_id].clone()) | ||
265 | } | ||
266 | |||
259 | pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> { | 267 | pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> { |
260 | let expr_id = self.body_source_map.as_ref()?.node_expr(&call.clone().into())?; | 268 | let expr_id = self.body_source_map.as_ref()?.node_expr(&call.clone().into())?; |
261 | self.infer.as_ref()?.method_resolution(expr_id) | 269 | self.infer.as_ref()?.method_resolution(expr_id) |
diff --git a/crates/ra_ide_api/src/completion/presentation.rs b/crates/ra_ide_api/src/completion/presentation.rs index 98060947a..5cf55a496 100644 --- a/crates/ra_ide_api/src/completion/presentation.rs +++ b/crates/ra_ide_api/src/completion/presentation.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | //! This modules takes care of rendering various defenitions as completion items. | 1 | //! This modules takes care of rendering various defenitions as completion items. |
2 | use hir::{Docs, HasSource, HirDisplay, PerNs, Resolution}; | 2 | use hir::{Docs, HasSource, HirDisplay, PerNs, Resolution, Ty}; |
3 | use join_to_string::join; | 3 | use join_to_string::join; |
4 | use ra_syntax::ast::NameOwner; | 4 | use ra_syntax::ast::NameOwner; |
5 | use test_utils::tested_by; | 5 | use test_utils::tested_by; |
@@ -80,10 +80,18 @@ impl Completions { | |||
80 | None, | 80 | None, |
81 | ), | 81 | ), |
82 | }; | 82 | }; |
83 | CompletionItem::new(completion_kind, ctx.source_range(), local_name) | 83 | |
84 | .kind(kind) | 84 | let mut completion_item = |
85 | .set_documentation(docs) | 85 | CompletionItem::new(completion_kind, ctx.source_range(), local_name); |
86 | .add_to(self) | 86 | if let Resolution::LocalBinding(pat_id) = def { |
87 | let ty = ctx | ||
88 | .analyzer | ||
89 | .type_of_pat_by_id(ctx.db, pat_id.clone()) | ||
90 | .filter(|t| t != &Ty::Unknown) | ||
91 | .map(|t| t.display(ctx.db).to_string()); | ||
92 | completion_item = completion_item.set_detail(ty); | ||
93 | }; | ||
94 | completion_item.kind(kind).set_documentation(docs).add_to(self) | ||
87 | } | 95 | } |
88 | 96 | ||
89 | pub(crate) fn add_function(&mut self, ctx: &CompletionContext, func: hir::Function) { | 97 | pub(crate) fn add_function(&mut self, ctx: &CompletionContext, func: hir::Function) { |
diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__bindings_from_if_let.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__bindings_from_if_let.snap index 2a22201ad..f94477b43 100644 --- a/crates/ra_ide_api/src/completion/snapshots/completion_item__bindings_from_if_let.snap +++ b/crates/ra_ide_api/src/completion/snapshots/completion_item__bindings_from_if_let.snap | |||
@@ -1,5 +1,5 @@ | |||
1 | --- | 1 | --- |
2 | created: "2019-05-23T22:23:35.122168608Z" | 2 | created: "2019-07-23T16:11:48.828805910Z" |
3 | creator: [email protected] | 3 | creator: [email protected] |
4 | source: crates/ra_ide_api/src/completion/completion_item.rs | 4 | source: crates/ra_ide_api/src/completion/completion_item.rs |
5 | expression: kind_completions | 5 | expression: kind_completions |
@@ -18,6 +18,7 @@ expression: kind_completions | |||
18 | delete: [214; 214), | 18 | delete: [214; 214), |
19 | insert: "b", | 19 | insert: "b", |
20 | kind: Binding, | 20 | kind: Binding, |
21 | detail: "i32", | ||
21 | }, | 22 | }, |
22 | CompletionItem { | 23 | CompletionItem { |
23 | label: "quux", | 24 | label: "quux", |
diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__bindings_from_let.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__bindings_from_let.snap index b9a5dc9c8..590e2a820 100644 --- a/crates/ra_ide_api/src/completion/snapshots/completion_item__bindings_from_let.snap +++ b/crates/ra_ide_api/src/completion/snapshots/completion_item__bindings_from_let.snap | |||
@@ -1,5 +1,5 @@ | |||
1 | --- | 1 | --- |
2 | created: "2019-05-23T22:23:35.122797188Z" | 2 | created: "2019-07-23T16:11:48.828811567Z" |
3 | creator: [email protected] | 3 | creator: [email protected] |
4 | source: crates/ra_ide_api/src/completion/completion_item.rs | 4 | source: crates/ra_ide_api/src/completion/completion_item.rs |
5 | expression: kind_completions | 5 | expression: kind_completions |
@@ -19,6 +19,7 @@ expression: kind_completions | |||
19 | delete: [79; 79), | 19 | delete: [79; 79), |
20 | insert: "x", | 20 | insert: "x", |
21 | kind: Binding, | 21 | kind: Binding, |
22 | detail: "i32", | ||
22 | }, | 23 | }, |
23 | CompletionItem { | 24 | CompletionItem { |
24 | label: "y", | 25 | label: "y", |
@@ -26,5 +27,6 @@ expression: kind_completions | |||
26 | delete: [79; 79), | 27 | delete: [79; 79), |
27 | insert: "y", | 28 | insert: "y", |
28 | kind: Binding, | 29 | kind: Binding, |
30 | detail: "i32", | ||
29 | }, | 31 | }, |
30 | ] | 32 | ] |
diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__dont_show_both_completions_for_shadowing.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__dont_show_both_completions_for_shadowing.snap index 57434210d..158a2e5b9 100644 --- a/crates/ra_ide_api/src/completion/snapshots/completion_item__dont_show_both_completions_for_shadowing.snap +++ b/crates/ra_ide_api/src/completion/snapshots/completion_item__dont_show_both_completions_for_shadowing.snap | |||
@@ -1,5 +1,5 @@ | |||
1 | --- | 1 | --- |
2 | created: "2019-05-23T22:23:35.142044205Z" | 2 | created: "2019-07-23T16:11:48.860949870Z" |
3 | creator: [email protected] | 3 | creator: [email protected] |
4 | source: crates/ra_ide_api/src/completion/completion_item.rs | 4 | source: crates/ra_ide_api/src/completion/completion_item.rs |
5 | expression: kind_completions | 5 | expression: kind_completions |
@@ -11,6 +11,7 @@ expression: kind_completions | |||
11 | delete: [126; 126), | 11 | delete: [126; 126), |
12 | insert: "bar", | 12 | insert: "bar", |
13 | kind: Binding, | 13 | kind: Binding, |
14 | detail: "i32", | ||
14 | }, | 15 | }, |
15 | CompletionItem { | 16 | CompletionItem { |
16 | label: "foo", | 17 | label: "foo", |
diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__self_in_methods.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__self_in_methods.snap index e1af94870..b7bcbe864 100644 --- a/crates/ra_ide_api/src/completion/snapshots/completion_item__self_in_methods.snap +++ b/crates/ra_ide_api/src/completion/snapshots/completion_item__self_in_methods.snap | |||
@@ -1,5 +1,5 @@ | |||
1 | --- | 1 | --- |
2 | created: "2019-05-23T22:23:35.141900902Z" | 2 | created: "2019-07-23T16:11:48.859812318Z" |
3 | creator: [email protected] | 3 | creator: [email protected] |
4 | source: crates/ra_ide_api/src/completion/completion_item.rs | 4 | source: crates/ra_ide_api/src/completion/completion_item.rs |
5 | expression: kind_completions | 5 | expression: kind_completions |
@@ -18,5 +18,6 @@ expression: kind_completions | |||
18 | delete: [25; 25), | 18 | delete: [25; 25), |
19 | insert: "self", | 19 | insert: "self", |
20 | kind: Binding, | 20 | kind: Binding, |
21 | detail: "&{unknown}", | ||
21 | }, | 22 | }, |
22 | ] | 23 | ] |
diff --git a/crates/ra_ide_api/src/inlay_hints.rs b/crates/ra_ide_api/src/inlay_hints.rs new file mode 100644 index 000000000..174662beb --- /dev/null +++ b/crates/ra_ide_api/src/inlay_hints.rs | |||
@@ -0,0 +1,180 @@ | |||
1 | use crate::{db::RootDatabase, FileId}; | ||
2 | use hir::{HirDisplay, Ty}; | ||
3 | use ra_syntax::ast::Pat; | ||
4 | use ra_syntax::{ | ||
5 | algo::visit::{visitor, Visitor}, | ||
6 | ast::{self, PatKind, TypeAscriptionOwner}, | ||
7 | AstNode, SmolStr, SourceFile, SyntaxNode, TextRange, | ||
8 | }; | ||
9 | |||
10 | #[derive(Debug, PartialEq, Eq)] | ||
11 | pub enum InlayKind { | ||
12 | LetBindingType, | ||
13 | ClosureParameterType, | ||
14 | } | ||
15 | |||
16 | #[derive(Debug)] | ||
17 | pub struct InlayHint { | ||
18 | pub range: TextRange, | ||
19 | pub kind: InlayKind, | ||
20 | pub label: SmolStr, | ||
21 | } | ||
22 | |||
23 | pub(crate) fn inlay_hints(db: &RootDatabase, file_id: FileId, file: &SourceFile) -> Vec<InlayHint> { | ||
24 | file.syntax() | ||
25 | .descendants() | ||
26 | .map(|node| get_inlay_hints(db, file_id, &node).unwrap_or_default()) | ||
27 | .flatten() | ||
28 | .collect() | ||
29 | } | ||
30 | |||
31 | fn get_inlay_hints( | ||
32 | db: &RootDatabase, | ||
33 | file_id: FileId, | ||
34 | node: &SyntaxNode, | ||
35 | ) -> Option<Vec<InlayHint>> { | ||
36 | visitor() | ||
37 | .visit(|let_statement: ast::LetStmt| { | ||
38 | let let_syntax = let_statement.syntax(); | ||
39 | |||
40 | if let_statement.ascribed_type().is_some() { | ||
41 | return None; | ||
42 | } | ||
43 | |||
44 | let let_pat = let_statement.pat()?; | ||
45 | let inlay_type_string = get_node_displayable_type(db, file_id, let_syntax, &let_pat)? | ||
46 | .display(db) | ||
47 | .to_string() | ||
48 | .into(); | ||
49 | |||
50 | let pat_range = match let_pat.kind() { | ||
51 | PatKind::BindPat(bind_pat) => bind_pat.syntax().text_range(), | ||
52 | PatKind::TuplePat(tuple_pat) => tuple_pat.syntax().text_range(), | ||
53 | _ => return None, | ||
54 | }; | ||
55 | |||
56 | Some(vec![InlayHint { | ||
57 | range: pat_range, | ||
58 | kind: InlayKind::LetBindingType, | ||
59 | label: inlay_type_string, | ||
60 | }]) | ||
61 | }) | ||
62 | .visit(|closure_parameter: ast::LambdaExpr| match closure_parameter.param_list() { | ||
63 | Some(param_list) => Some( | ||
64 | param_list | ||
65 | .params() | ||
66 | .filter(|closure_param| closure_param.ascribed_type().is_none()) | ||
67 | .filter_map(|closure_param| { | ||
68 | let closure_param_syntax = closure_param.syntax(); | ||
69 | let inlay_type_string = get_node_displayable_type( | ||
70 | db, | ||
71 | file_id, | ||
72 | closure_param_syntax, | ||
73 | &closure_param.pat()?, | ||
74 | )? | ||
75 | .display(db) | ||
76 | .to_string() | ||
77 | .into(); | ||
78 | |||
79 | Some(InlayHint { | ||
80 | range: closure_param_syntax.text_range(), | ||
81 | kind: InlayKind::ClosureParameterType, | ||
82 | label: inlay_type_string, | ||
83 | }) | ||
84 | }) | ||
85 | .collect(), | ||
86 | ), | ||
87 | None => None, | ||
88 | }) | ||
89 | .accept(&node)? | ||
90 | } | ||
91 | |||
92 | fn get_node_displayable_type( | ||
93 | db: &RootDatabase, | ||
94 | file_id: FileId, | ||
95 | node_syntax: &SyntaxNode, | ||
96 | node_pat: &Pat, | ||
97 | ) -> Option<Ty> { | ||
98 | let analyzer = hir::SourceAnalyzer::new(db, file_id, node_syntax, None); | ||
99 | analyzer.type_of_pat(db, node_pat).and_then(|resolved_type| { | ||
100 | if let Ty::Apply(_) = resolved_type { | ||
101 | Some(resolved_type) | ||
102 | } else { | ||
103 | None | ||
104 | } | ||
105 | }) | ||
106 | } | ||
107 | |||
108 | #[cfg(test)] | ||
109 | mod tests { | ||
110 | use crate::mock_analysis::single_file; | ||
111 | use insta::assert_debug_snapshot_matches; | ||
112 | |||
113 | #[test] | ||
114 | fn test_inlay_hints() { | ||
115 | let (analysis, file_id) = single_file( | ||
116 | r#" | ||
117 | struct OuterStruct {} | ||
118 | |||
119 | fn main() { | ||
120 | struct InnerStruct {} | ||
121 | |||
122 | let test = 54; | ||
123 | let test = InnerStruct {}; | ||
124 | let test = OuterStruct {}; | ||
125 | let test = vec![222]; | ||
126 | let mut test = Vec::new(); | ||
127 | test.push(333); | ||
128 | let test = test.into_iter().map(|i| i * i).collect::<Vec<_>>(); | ||
129 | let mut test = 33; | ||
130 | let _ = 22; | ||
131 | let test: Vec<_> = (0..3).collect(); | ||
132 | |||
133 | let _ = (0..23).map(|i: u32| { | ||
134 | let i_squared = i * i; | ||
135 | i_squared | ||
136 | }); | ||
137 | |||
138 | let test: i32 = 33; | ||
139 | |||
140 | let (x, c) = (42, 'a'); | ||
141 | let test = (42, 'a'); | ||
142 | } | ||
143 | "#, | ||
144 | ); | ||
145 | |||
146 | assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[ | ||
147 | InlayHint { | ||
148 | range: [71; 75), | ||
149 | kind: LetBindingType, | ||
150 | label: "i32", | ||
151 | }, | ||
152 | InlayHint { | ||
153 | range: [121; 125), | ||
154 | kind: LetBindingType, | ||
155 | label: "OuterStruct", | ||
156 | }, | ||
157 | InlayHint { | ||
158 | range: [297; 305), | ||
159 | kind: LetBindingType, | ||
160 | label: "i32", | ||
161 | }, | ||
162 | InlayHint { | ||
163 | range: [417; 426), | ||
164 | kind: LetBindingType, | ||
165 | label: "u32", | ||
166 | }, | ||
167 | InlayHint { | ||
168 | range: [496; 502), | ||
169 | kind: LetBindingType, | ||
170 | label: "(i32, char)", | ||
171 | }, | ||
172 | InlayHint { | ||
173 | range: [524; 528), | ||
174 | kind: LetBindingType, | ||
175 | label: "(i32, char)", | ||
176 | }, | ||
177 | ]"# | ||
178 | ); | ||
179 | } | ||
180 | } | ||
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index c54d574bc..16ffb03ce 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs | |||
@@ -38,6 +38,7 @@ mod join_lines; | |||
38 | mod typing; | 38 | mod typing; |
39 | mod matching_brace; | 39 | mod matching_brace; |
40 | mod display; | 40 | mod display; |
41 | mod inlay_hints; | ||
41 | 42 | ||
42 | #[cfg(test)] | 43 | #[cfg(test)] |
43 | mod marks; | 44 | mod marks; |
@@ -64,6 +65,7 @@ pub use crate::{ | |||
64 | display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, | 65 | display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, |
65 | folding_ranges::{Fold, FoldKind}, | 66 | folding_ranges::{Fold, FoldKind}, |
66 | hover::HoverResult, | 67 | hover::HoverResult, |
68 | inlay_hints::{InlayHint, InlayKind}, | ||
67 | line_index::{LineCol, LineIndex}, | 69 | line_index::{LineCol, LineIndex}, |
68 | line_index_utils::translate_offset_with_edit, | 70 | line_index_utils::translate_offset_with_edit, |
69 | references::ReferenceSearchResult, | 71 | references::ReferenceSearchResult, |
@@ -396,6 +398,11 @@ impl Analysis { | |||
396 | file_structure(&parse.tree()) | 398 | file_structure(&parse.tree()) |
397 | } | 399 | } |
398 | 400 | ||
401 | /// Returns a list of the places in the file where type hints can be displayed. | ||
402 | pub fn inlay_hints(&self, file_id: FileId) -> Cancelable<Vec<InlayHint>> { | ||
403 | self.with_db(|db| inlay_hints::inlay_hints(db, file_id, &db.parse(file_id).tree())) | ||
404 | } | ||
405 | |||
399 | /// Returns the set of folding ranges. | 406 | /// Returns the set of folding ranges. |
400 | pub fn folding_ranges(&self, file_id: FileId) -> Vec<Fold> { | 407 | pub fn folding_ranges(&self, file_id: FileId) -> Vec<Fold> { |
401 | let parse = self.db.parse(file_id); | 408 | let parse = self.db.parse(file_id); |
diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml index e73e0b419..cec360667 100644 --- a/crates/ra_lsp_server/Cargo.toml +++ b/crates/ra_lsp_server/Cargo.toml | |||
@@ -10,7 +10,7 @@ relative-path = "0.4.0" | |||
10 | serde_json = "1.0.34" | 10 | serde_json = "1.0.34" |
11 | serde = { version = "1.0.83", features = ["derive"] } | 11 | serde = { version = "1.0.83", features = ["derive"] } |
12 | crossbeam-channel = "0.3.5" | 12 | crossbeam-channel = "0.3.5" |
13 | flexi_logger = "0.13.0" | 13 | flexi_logger = "0.14.0" |
14 | log = "0.4.3" | 14 | log = "0.4.3" |
15 | url_serde = "0.2.0" | 15 | url_serde = "0.2.0" |
16 | lsp-types = { version = "0.59.0", features = ["proposed"] } | 16 | lsp-types = { version = "0.59.0", features = ["proposed"] } |
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs index 668d2fd72..8e830c8b8 100644 --- a/crates/ra_lsp_server/src/main_loop.rs +++ b/crates/ra_lsp_server/src/main_loop.rs | |||
@@ -362,6 +362,7 @@ fn on_request( | |||
362 | .on::<req::References>(handlers::handle_references)? | 362 | .on::<req::References>(handlers::handle_references)? |
363 | .on::<req::Formatting>(handlers::handle_formatting)? | 363 | .on::<req::Formatting>(handlers::handle_formatting)? |
364 | .on::<req::DocumentHighlightRequest>(handlers::handle_document_highlight)? | 364 | .on::<req::DocumentHighlightRequest>(handlers::handle_document_highlight)? |
365 | .on::<req::InlayHints>(handlers::handle_inlay_hints)? | ||
365 | .finish(); | 366 | .finish(); |
366 | Ok(()) | 367 | Ok(()) |
367 | } | 368 | } |
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 68865b755..5bf950a53 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -21,7 +21,7 @@ use url_serde::Ser; | |||
21 | use crate::{ | 21 | use crate::{ |
22 | cargo_target_spec::{runnable_args, CargoTargetSpec}, | 22 | cargo_target_spec::{runnable_args, CargoTargetSpec}, |
23 | conv::{to_location, Conv, ConvWith, MapConvWith, TryConvWith, TryConvWithToVec}, | 23 | conv::{to_location, Conv, ConvWith, MapConvWith, TryConvWith, TryConvWithToVec}, |
24 | req::{self, Decoration}, | 24 | req::{self, Decoration, InlayHint, InlayHintsParams, InlayKind}, |
25 | world::WorldSnapshot, | 25 | world::WorldSnapshot, |
26 | LspError, Result, | 26 | LspError, Result, |
27 | }; | 27 | }; |
@@ -874,3 +874,24 @@ fn to_diagnostic_severity(severity: Severity) -> DiagnosticSeverity { | |||
874 | WeakWarning => DiagnosticSeverity::Hint, | 874 | WeakWarning => DiagnosticSeverity::Hint, |
875 | } | 875 | } |
876 | } | 876 | } |
877 | |||
878 | pub fn handle_inlay_hints( | ||
879 | world: WorldSnapshot, | ||
880 | params: InlayHintsParams, | ||
881 | ) -> Result<Vec<InlayHint>> { | ||
882 | let file_id = params.text_document.try_conv_with(&world)?; | ||
883 | let analysis = world.analysis(); | ||
884 | let line_index = analysis.file_line_index(file_id); | ||
885 | Ok(analysis | ||
886 | .inlay_hints(file_id)? | ||
887 | .into_iter() | ||
888 | .map(|api_type| InlayHint { | ||
889 | label: api_type.label.to_string(), | ||
890 | range: api_type.range.conv_with(&line_index), | ||
891 | kind: match api_type.kind { | ||
892 | ra_ide_api::InlayKind::LetBindingType => InlayKind::LetBindingType, | ||
893 | ra_ide_api::InlayKind::ClosureParameterType => InlayKind::ClosureParameterType, | ||
894 | }, | ||
895 | }) | ||
896 | .collect()) | ||
897 | } | ||
diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs index 8d39b04a7..916185f99 100644 --- a/crates/ra_lsp_server/src/req.rs +++ b/crates/ra_lsp_server/src/req.rs | |||
@@ -196,3 +196,30 @@ pub struct SourceChange { | |||
196 | pub workspace_edit: WorkspaceEdit, | 196 | pub workspace_edit: WorkspaceEdit, |
197 | pub cursor_position: Option<TextDocumentPositionParams>, | 197 | pub cursor_position: Option<TextDocumentPositionParams>, |
198 | } | 198 | } |
199 | |||
200 | pub enum InlayHints {} | ||
201 | |||
202 | impl Request for InlayHints { | ||
203 | type Params = InlayHintsParams; | ||
204 | type Result = Vec<InlayHint>; | ||
205 | const METHOD: &'static str = "rust-analyzer/inlayHints"; | ||
206 | } | ||
207 | |||
208 | #[derive(Serialize, Deserialize, Debug)] | ||
209 | #[serde(rename_all = "camelCase")] | ||
210 | pub struct InlayHintsParams { | ||
211 | pub text_document: TextDocumentIdentifier, | ||
212 | } | ||
213 | |||
214 | #[derive(Debug, PartialEq, Eq, Deserialize, Serialize)] | ||
215 | pub enum InlayKind { | ||
216 | LetBindingType, | ||
217 | ClosureParameterType, | ||
218 | } | ||
219 | |||
220 | #[derive(Debug, Deserialize, Serialize)] | ||
221 | pub struct InlayHint { | ||
222 | pub range: Range, | ||
223 | pub kind: InlayKind, | ||
224 | pub label: String, | ||
225 | } | ||
diff --git a/crates/ra_syntax/Cargo.toml b/crates/ra_syntax/Cargo.toml index 97b6b047f..9ef8dee5d 100644 --- a/crates/ra_syntax/Cargo.toml +++ b/crates/ra_syntax/Cargo.toml | |||
@@ -11,6 +11,7 @@ repository = "https://github.com/rust-analyzer/rust-analyzer" | |||
11 | unicode-xid = "0.1.0" | 11 | unicode-xid = "0.1.0" |
12 | itertools = "0.8.0" | 12 | itertools = "0.8.0" |
13 | rowan = "0.6.0-pre.1" | 13 | rowan = "0.6.0-pre.1" |
14 | ra_rustc_lexer = { version = "0.1.0-pre.1", features = [ "unicode-xid" ] } | ||
14 | 15 | ||
15 | # ideally, `serde` should be enabled by `ra_lsp_server`, but we enable it here | 16 | # ideally, `serde` should be enabled by `ra_lsp_server`, but we enable it here |
16 | # to reduce number of compilations | 17 | # to reduce number of compilations |
diff --git a/crates/ra_syntax/src/parsing/lexer.rs b/crates/ra_syntax/src/parsing/lexer.rs index 60cf37047..2a4343b0a 100644 --- a/crates/ra_syntax/src/parsing/lexer.rs +++ b/crates/ra_syntax/src/parsing/lexer.rs | |||
@@ -1,22 +1,6 @@ | |||
1 | mod classes; | ||
2 | mod comments; | ||
3 | mod numbers; | ||
4 | mod ptr; | ||
5 | mod strings; | ||
6 | |||
7 | use crate::{ | 1 | use crate::{ |
8 | SyntaxKind::{self, *}, | 2 | SyntaxKind::{self, *}, |
9 | TextUnit, T, | 3 | TextUnit, |
10 | }; | ||
11 | |||
12 | use self::{ | ||
13 | classes::*, | ||
14 | comments::{scan_comment, scan_shebang}, | ||
15 | numbers::scan_number, | ||
16 | ptr::Ptr, | ||
17 | strings::{ | ||
18 | is_string_literal_start, scan_byte_char_or_string, scan_char, scan_raw_string, scan_string, | ||
19 | }, | ||
20 | }; | 4 | }; |
21 | 5 | ||
22 | /// A token of Rust source. | 6 | /// A token of Rust source. |
@@ -30,149 +14,134 @@ pub struct Token { | |||
30 | 14 | ||
31 | /// Break a string up into its component tokens | 15 | /// Break a string up into its component tokens |
32 | pub fn tokenize(text: &str) -> Vec<Token> { | 16 | pub fn tokenize(text: &str) -> Vec<Token> { |
17 | if text.is_empty() { | ||
18 | return vec![]; | ||
19 | } | ||
33 | let mut text = text; | 20 | let mut text = text; |
34 | let mut acc = Vec::new(); | 21 | let mut acc = Vec::new(); |
35 | while !text.is_empty() { | 22 | if let Some(len) = ra_rustc_lexer::strip_shebang(text) { |
36 | let token = next_token(text); | 23 | acc.push(Token { kind: SHEBANG, len: TextUnit::from_usize(len) }); |
37 | acc.push(token); | 24 | text = &text[len..]; |
38 | let len: u32 = token.len.into(); | ||
39 | text = &text[len as usize..]; | ||
40 | } | ||
41 | acc | ||
42 | } | ||
43 | |||
44 | /// Get the next token from a string | ||
45 | pub fn next_token(text: &str) -> Token { | ||
46 | assert!(!text.is_empty()); | ||
47 | let mut ptr = Ptr::new(text); | ||
48 | let c = ptr.bump().unwrap(); | ||
49 | let kind = next_token_inner(c, &mut ptr); | ||
50 | let len = ptr.into_len(); | ||
51 | Token { kind, len } | ||
52 | } | ||
53 | |||
54 | fn next_token_inner(c: char, ptr: &mut Ptr) -> SyntaxKind { | ||
55 | if is_whitespace(c) { | ||
56 | ptr.bump_while(is_whitespace); | ||
57 | return WHITESPACE; | ||
58 | } | 25 | } |
59 | 26 | while !text.is_empty() { | |
60 | match c { | 27 | let rustc_token = ra_rustc_lexer::first_token(text); |
61 | '#' => { | 28 | macro_rules! decompose { |
62 | if scan_shebang(ptr) { | 29 | ($t1:expr, $t2:expr) => {{ |
63 | return SHEBANG; | 30 | acc.push(Token { kind: $t1, len: 1.into() }); |
64 | } | 31 | acc.push(Token { kind: $t2, len: 1.into() }); |
65 | } | 32 | text = &text[2..]; |
66 | '/' => { | 33 | continue; |
67 | if let Some(kind) = scan_comment(ptr) { | 34 | }}; |
68 | return kind; | 35 | ($t1:expr, $t2:expr, $t3:expr) => {{ |
69 | } | 36 | acc.push(Token { kind: $t1, len: 1.into() }); |
37 | acc.push(Token { kind: $t2, len: 1.into() }); | ||
38 | acc.push(Token { kind: $t3, len: 1.into() }); | ||
39 | text = &text[3..]; | ||
40 | continue; | ||
41 | }}; | ||
70 | } | 42 | } |
71 | _ => (), | 43 | let kind = match rustc_token.kind { |
72 | } | 44 | ra_rustc_lexer::TokenKind::LineComment => COMMENT, |
73 | 45 | ra_rustc_lexer::TokenKind::BlockComment { .. } => COMMENT, | |
74 | let ident_start = is_ident_start(c) && !is_string_literal_start(c, ptr.current(), ptr.nth(1)); | 46 | ra_rustc_lexer::TokenKind::Whitespace => WHITESPACE, |
75 | if ident_start { | 47 | ra_rustc_lexer::TokenKind::Ident => { |
76 | return scan_ident(c, ptr); | 48 | let token_text = &text[..rustc_token.len]; |
77 | } | 49 | if token_text == "_" { |
78 | 50 | UNDERSCORE | |
79 | if is_dec_digit(c) { | 51 | } else { |
80 | let kind = scan_number(c, ptr); | 52 | SyntaxKind::from_keyword(&text[..rustc_token.len]).unwrap_or(IDENT) |
81 | scan_literal_suffix(ptr); | ||
82 | return kind; | ||
83 | } | ||
84 | |||
85 | // One-byte tokens. | ||
86 | if let Some(kind) = SyntaxKind::from_char(c) { | ||
87 | return kind; | ||
88 | } | ||
89 | |||
90 | match c { | ||
91 | // Possiblily multi-byte tokens, | ||
92 | // but we only produce single byte token now | ||
93 | // T![...], T![..], T![..=], T![.] | ||
94 | '.' => return T![.], | ||
95 | // T![::] T![:] | ||
96 | ':' => return T![:], | ||
97 | // T![==] FATARROW T![=] | ||
98 | '=' => return T![=], | ||
99 | // T![!=] T![!] | ||
100 | '!' => return T![!], | ||
101 | // T![->] T![-] | ||
102 | '-' => return T![-], | ||
103 | |||
104 | // If the character is an ident start not followed by another single | ||
105 | // quote, then this is a lifetime name: | ||
106 | '\'' => { | ||
107 | return if ptr.at_p(is_ident_start) && !ptr.at_str("''") { | ||
108 | ptr.bump(); | ||
109 | while ptr.at_p(is_ident_continue) { | ||
110 | ptr.bump(); | ||
111 | } | 53 | } |
112 | // lifetimes shouldn't end with a single quote | 54 | } |
113 | // if we find one, then this is an invalid character literal | 55 | ra_rustc_lexer::TokenKind::RawIdent => IDENT, |
114 | if ptr.at('\'') { | 56 | ra_rustc_lexer::TokenKind::Literal { kind, .. } => match kind { |
115 | ptr.bump(); | 57 | ra_rustc_lexer::LiteralKind::Int { .. } => INT_NUMBER, |
116 | return CHAR; | 58 | ra_rustc_lexer::LiteralKind::Float { .. } => FLOAT_NUMBER, |
117 | } | 59 | ra_rustc_lexer::LiteralKind::Char { .. } => CHAR, |
118 | LIFETIME | 60 | ra_rustc_lexer::LiteralKind::Byte { .. } => BYTE, |
119 | } else { | 61 | ra_rustc_lexer::LiteralKind::Str { .. } => STRING, |
120 | scan_char(ptr); | 62 | ra_rustc_lexer::LiteralKind::ByteStr { .. } => BYTE_STRING, |
121 | scan_literal_suffix(ptr); | 63 | ra_rustc_lexer::LiteralKind::RawStr { .. } => RAW_STRING, |
122 | CHAR | 64 | ra_rustc_lexer::LiteralKind::RawByteStr { .. } => RAW_BYTE_STRING, |
123 | }; | 65 | }, |
124 | } | 66 | ra_rustc_lexer::TokenKind::Lifetime { .. } => LIFETIME, |
125 | 'b' => { | 67 | ra_rustc_lexer::TokenKind::Semi => SEMI, |
126 | let kind = scan_byte_char_or_string(ptr); | 68 | ra_rustc_lexer::TokenKind::Comma => COMMA, |
127 | scan_literal_suffix(ptr); | 69 | ra_rustc_lexer::TokenKind::DotDotDot => decompose!(DOT, DOT, DOT), |
128 | return kind; | 70 | ra_rustc_lexer::TokenKind::DotDotEq => decompose!(DOT, DOT, EQ), |
129 | } | 71 | ra_rustc_lexer::TokenKind::DotDot => decompose!(DOT, DOT), |
130 | '"' => { | 72 | ra_rustc_lexer::TokenKind::Dot => DOT, |
131 | scan_string(ptr); | 73 | ra_rustc_lexer::TokenKind::OpenParen => L_PAREN, |
132 | scan_literal_suffix(ptr); | 74 | ra_rustc_lexer::TokenKind::CloseParen => R_PAREN, |
133 | return STRING; | 75 | ra_rustc_lexer::TokenKind::OpenBrace => L_CURLY, |
134 | } | 76 | ra_rustc_lexer::TokenKind::CloseBrace => R_CURLY, |
135 | 'r' => { | 77 | ra_rustc_lexer::TokenKind::OpenBracket => L_BRACK, |
136 | scan_raw_string(ptr); | 78 | ra_rustc_lexer::TokenKind::CloseBracket => R_BRACK, |
137 | scan_literal_suffix(ptr); | 79 | ra_rustc_lexer::TokenKind::At => AT, |
138 | return RAW_STRING; | 80 | ra_rustc_lexer::TokenKind::Pound => POUND, |
139 | } | 81 | ra_rustc_lexer::TokenKind::Tilde => TILDE, |
140 | _ => (), | 82 | ra_rustc_lexer::TokenKind::Question => QUESTION, |
141 | } | 83 | ra_rustc_lexer::TokenKind::ColonColon => decompose!(COLON, COLON), |
142 | ERROR | 84 | ra_rustc_lexer::TokenKind::Colon => COLON, |
143 | } | 85 | ra_rustc_lexer::TokenKind::Dollar => DOLLAR, |
144 | 86 | ra_rustc_lexer::TokenKind::EqEq => decompose!(EQ, EQ), | |
145 | fn scan_ident(c: char, ptr: &mut Ptr) -> SyntaxKind { | 87 | ra_rustc_lexer::TokenKind::Eq => EQ, |
146 | let is_raw = match (c, ptr.current()) { | 88 | ra_rustc_lexer::TokenKind::FatArrow => decompose!(EQ, R_ANGLE), |
147 | ('r', Some('#')) => { | 89 | ra_rustc_lexer::TokenKind::Ne => decompose!(EXCL, EQ), |
148 | ptr.bump(); | 90 | ra_rustc_lexer::TokenKind::Not => EXCL, |
149 | true | 91 | ra_rustc_lexer::TokenKind::Le => decompose!(L_ANGLE, EQ), |
150 | } | 92 | ra_rustc_lexer::TokenKind::LArrow => decompose!(COLON, MINUS), |
151 | ('_', None) => return T![_], | 93 | ra_rustc_lexer::TokenKind::Lt => L_ANGLE, |
152 | ('_', Some(c)) if !is_ident_continue(c) => return T![_], | 94 | ra_rustc_lexer::TokenKind::ShlEq => decompose!(L_ANGLE, L_ANGLE, EQ), |
153 | _ => false, | 95 | ra_rustc_lexer::TokenKind::Shl => decompose!(L_ANGLE, L_ANGLE), |
154 | }; | 96 | ra_rustc_lexer::TokenKind::Ge => decompose!(R_ANGLE, EQ), |
155 | ptr.bump_while(is_ident_continue); | 97 | ra_rustc_lexer::TokenKind::Gt => R_ANGLE, |
156 | if !is_raw { | 98 | ra_rustc_lexer::TokenKind::ShrEq => decompose!(R_ANGLE, R_ANGLE, EQ), |
157 | if let Some(kind) = SyntaxKind::from_keyword(ptr.current_token_text()) { | 99 | ra_rustc_lexer::TokenKind::Shr => decompose!(R_ANGLE, R_ANGLE), |
158 | return kind; | 100 | ra_rustc_lexer::TokenKind::RArrow => decompose!(MINUS, R_ANGLE), |
159 | } | 101 | ra_rustc_lexer::TokenKind::Minus => MINUS, |
160 | } | 102 | ra_rustc_lexer::TokenKind::MinusEq => decompose!(MINUS, EQ), |
161 | IDENT | 103 | ra_rustc_lexer::TokenKind::And => AMP, |
162 | } | 104 | ra_rustc_lexer::TokenKind::AndAnd => decompose!(AMP, AMP), |
163 | 105 | ra_rustc_lexer::TokenKind::AndEq => decompose!(AMP, EQ), | |
164 | fn scan_literal_suffix(ptr: &mut Ptr) { | 106 | ra_rustc_lexer::TokenKind::Or => PIPE, |
165 | if ptr.at_p(is_ident_start) { | 107 | ra_rustc_lexer::TokenKind::OrOr => decompose!(PIPE, PIPE), |
166 | ptr.bump(); | 108 | ra_rustc_lexer::TokenKind::OrEq => decompose!(PIPE, EQ), |
109 | ra_rustc_lexer::TokenKind::PlusEq => decompose!(PLUS, EQ), | ||
110 | ra_rustc_lexer::TokenKind::Plus => PLUS, | ||
111 | ra_rustc_lexer::TokenKind::StarEq => decompose!(STAR, EQ), | ||
112 | ra_rustc_lexer::TokenKind::Star => STAR, | ||
113 | ra_rustc_lexer::TokenKind::SlashEq => decompose!(SLASH, EQ), | ||
114 | ra_rustc_lexer::TokenKind::Slash => SLASH, | ||
115 | ra_rustc_lexer::TokenKind::CaretEq => decompose!(CARET, EQ), | ||
116 | ra_rustc_lexer::TokenKind::Caret => CARET, | ||
117 | ra_rustc_lexer::TokenKind::PercentEq => decompose!(PERCENT, EQ), | ||
118 | ra_rustc_lexer::TokenKind::Percent => PERCENT, | ||
119 | ra_rustc_lexer::TokenKind::Unknown => ERROR, | ||
120 | }; | ||
121 | let token = Token { kind, len: TextUnit::from_usize(rustc_token.len) }; | ||
122 | acc.push(token); | ||
123 | text = &text[rustc_token.len..]; | ||
167 | } | 124 | } |
168 | ptr.bump_while(is_ident_continue); | 125 | acc |
169 | } | 126 | } |
170 | 127 | ||
171 | pub fn classify_literal(text: &str) -> Option<Token> { | 128 | pub fn classify_literal(text: &str) -> Option<Token> { |
172 | let tkn = next_token(text); | 129 | let t = ra_rustc_lexer::first_token(text); |
173 | if !tkn.kind.is_literal() || tkn.len.to_usize() != text.len() { | 130 | if t.len != text.len() { |
174 | return None; | 131 | return None; |
175 | } | 132 | } |
176 | 133 | let kind = match t.kind { | |
177 | Some(tkn) | 134 | ra_rustc_lexer::TokenKind::Literal { kind, .. } => match kind { |
135 | ra_rustc_lexer::LiteralKind::Int { .. } => INT_NUMBER, | ||
136 | ra_rustc_lexer::LiteralKind::Float { .. } => FLOAT_NUMBER, | ||
137 | ra_rustc_lexer::LiteralKind::Char { .. } => CHAR, | ||
138 | ra_rustc_lexer::LiteralKind::Byte { .. } => BYTE, | ||
139 | ra_rustc_lexer::LiteralKind::Str { .. } => STRING, | ||
140 | ra_rustc_lexer::LiteralKind::ByteStr { .. } => BYTE_STRING, | ||
141 | ra_rustc_lexer::LiteralKind::RawStr { .. } => RAW_STRING, | ||
142 | ra_rustc_lexer::LiteralKind::RawByteStr { .. } => RAW_BYTE_STRING, | ||
143 | }, | ||
144 | _ => return None, | ||
145 | }; | ||
146 | Some(Token { kind, len: TextUnit::from_usize(t.len) }) | ||
178 | } | 147 | } |
diff --git a/crates/ra_syntax/src/parsing/lexer/classes.rs b/crates/ra_syntax/src/parsing/lexer/classes.rs deleted file mode 100644 index 4235d2648..000000000 --- a/crates/ra_syntax/src/parsing/lexer/classes.rs +++ /dev/null | |||
@@ -1,26 +0,0 @@ | |||
1 | use unicode_xid::UnicodeXID; | ||
2 | |||
3 | pub fn is_ident_start(c: char) -> bool { | ||
4 | (c >= 'a' && c <= 'z') | ||
5 | || (c >= 'A' && c <= 'Z') | ||
6 | || c == '_' | ||
7 | || (c > '\x7f' && UnicodeXID::is_xid_start(c)) | ||
8 | } | ||
9 | |||
10 | pub fn is_ident_continue(c: char) -> bool { | ||
11 | (c >= 'a' && c <= 'z') | ||
12 | || (c >= 'A' && c <= 'Z') | ||
13 | || (c >= '0' && c <= '9') | ||
14 | || c == '_' | ||
15 | || (c > '\x7f' && UnicodeXID::is_xid_continue(c)) | ||
16 | } | ||
17 | |||
18 | pub fn is_whitespace(c: char) -> bool { | ||
19 | //FIXME: use is_pattern_whitespace | ||
20 | //https://github.com/behnam/rust-unic/issues/192 | ||
21 | c.is_whitespace() | ||
22 | } | ||
23 | |||
24 | pub fn is_dec_digit(c: char) -> bool { | ||
25 | '0' <= c && c <= '9' | ||
26 | } | ||
diff --git a/crates/ra_syntax/src/parsing/lexer/comments.rs b/crates/ra_syntax/src/parsing/lexer/comments.rs deleted file mode 100644 index 8bbbe659b..000000000 --- a/crates/ra_syntax/src/parsing/lexer/comments.rs +++ /dev/null | |||
@@ -1,57 +0,0 @@ | |||
1 | use crate::parsing::lexer::ptr::Ptr; | ||
2 | |||
3 | use crate::SyntaxKind::{self, *}; | ||
4 | |||
5 | pub(crate) fn scan_shebang(ptr: &mut Ptr) -> bool { | ||
6 | if ptr.at_str("!/") { | ||
7 | ptr.bump(); | ||
8 | ptr.bump(); | ||
9 | bump_until_eol(ptr); | ||
10 | true | ||
11 | } else { | ||
12 | false | ||
13 | } | ||
14 | } | ||
15 | |||
16 | fn scan_block_comment(ptr: &mut Ptr) -> Option<SyntaxKind> { | ||
17 | if ptr.at('*') { | ||
18 | ptr.bump(); | ||
19 | let mut depth: u32 = 1; | ||
20 | while depth > 0 { | ||
21 | if ptr.at_str("*/") { | ||
22 | depth -= 1; | ||
23 | ptr.bump(); | ||
24 | ptr.bump(); | ||
25 | } else if ptr.at_str("/*") { | ||
26 | depth += 1; | ||
27 | ptr.bump(); | ||
28 | ptr.bump(); | ||
29 | } else if ptr.bump().is_none() { | ||
30 | break; | ||
31 | } | ||
32 | } | ||
33 | Some(COMMENT) | ||
34 | } else { | ||
35 | None | ||
36 | } | ||
37 | } | ||
38 | |||
39 | pub(crate) fn scan_comment(ptr: &mut Ptr) -> Option<SyntaxKind> { | ||
40 | if ptr.at('/') { | ||
41 | bump_until_eol(ptr); | ||
42 | Some(COMMENT) | ||
43 | } else { | ||
44 | scan_block_comment(ptr) | ||
45 | } | ||
46 | } | ||
47 | |||
48 | fn bump_until_eol(ptr: &mut Ptr) { | ||
49 | loop { | ||
50 | if ptr.at('\n') || ptr.at_str("\r\n") { | ||
51 | return; | ||
52 | } | ||
53 | if ptr.bump().is_none() { | ||
54 | break; | ||
55 | } | ||
56 | } | ||
57 | } | ||
diff --git a/crates/ra_syntax/src/parsing/lexer/numbers.rs b/crates/ra_syntax/src/parsing/lexer/numbers.rs deleted file mode 100644 index e53ae231b..000000000 --- a/crates/ra_syntax/src/parsing/lexer/numbers.rs +++ /dev/null | |||
@@ -1,66 +0,0 @@ | |||
1 | use crate::parsing::lexer::{classes::*, ptr::Ptr}; | ||
2 | |||
3 | use crate::SyntaxKind::{self, *}; | ||
4 | |||
5 | pub(crate) fn scan_number(c: char, ptr: &mut Ptr) -> SyntaxKind { | ||
6 | if c == '0' { | ||
7 | match ptr.current().unwrap_or('\0') { | ||
8 | 'b' | 'o' => { | ||
9 | ptr.bump(); | ||
10 | scan_digits(ptr, false); | ||
11 | } | ||
12 | 'x' => { | ||
13 | ptr.bump(); | ||
14 | scan_digits(ptr, true); | ||
15 | } | ||
16 | '0'..='9' | '_' | '.' | 'e' | 'E' => { | ||
17 | scan_digits(ptr, true); | ||
18 | } | ||
19 | _ => return INT_NUMBER, | ||
20 | } | ||
21 | } else { | ||
22 | scan_digits(ptr, false); | ||
23 | } | ||
24 | |||
25 | // might be a float, but don't be greedy if this is actually an | ||
26 | // integer literal followed by field/method access or a range pattern | ||
27 | // (`0..2` and `12.foo()`) | ||
28 | if ptr.at('.') && !(ptr.at_str("..") || ptr.nth_is_p(1, is_ident_start)) { | ||
29 | // might have stuff after the ., and if it does, it needs to start | ||
30 | // with a number | ||
31 | ptr.bump(); | ||
32 | scan_digits(ptr, false); | ||
33 | scan_float_exponent(ptr); | ||
34 | return FLOAT_NUMBER; | ||
35 | } | ||
36 | // it might be a float if it has an exponent | ||
37 | if ptr.at('e') || ptr.at('E') { | ||
38 | scan_float_exponent(ptr); | ||
39 | return FLOAT_NUMBER; | ||
40 | } | ||
41 | INT_NUMBER | ||
42 | } | ||
43 | |||
44 | fn scan_digits(ptr: &mut Ptr, allow_hex: bool) { | ||
45 | while let Some(c) = ptr.current() { | ||
46 | match c { | ||
47 | '_' | '0'..='9' => { | ||
48 | ptr.bump(); | ||
49 | } | ||
50 | 'a'..='f' | 'A'..='F' if allow_hex => { | ||
51 | ptr.bump(); | ||
52 | } | ||
53 | _ => return, | ||
54 | } | ||
55 | } | ||
56 | } | ||
57 | |||
58 | fn scan_float_exponent(ptr: &mut Ptr) { | ||
59 | if ptr.at('e') || ptr.at('E') { | ||
60 | ptr.bump(); | ||
61 | if ptr.at('-') || ptr.at('+') { | ||
62 | ptr.bump(); | ||
63 | } | ||
64 | scan_digits(ptr, false); | ||
65 | } | ||
66 | } | ||
diff --git a/crates/ra_syntax/src/parsing/lexer/ptr.rs b/crates/ra_syntax/src/parsing/lexer/ptr.rs deleted file mode 100644 index c341c4176..000000000 --- a/crates/ra_syntax/src/parsing/lexer/ptr.rs +++ /dev/null | |||
@@ -1,162 +0,0 @@ | |||
1 | use crate::TextUnit; | ||
2 | |||
3 | use std::str::Chars; | ||
4 | |||
5 | /// A simple view into the characters of a string. | ||
6 | pub(crate) struct Ptr<'s> { | ||
7 | text: &'s str, | ||
8 | len: TextUnit, | ||
9 | } | ||
10 | |||
11 | impl<'s> Ptr<'s> { | ||
12 | /// Creates a new `Ptr` from a string. | ||
13 | pub fn new(text: &'s str) -> Ptr<'s> { | ||
14 | Ptr { text, len: 0.into() } | ||
15 | } | ||
16 | |||
17 | /// Gets the length of the remaining string. | ||
18 | pub fn into_len(self) -> TextUnit { | ||
19 | self.len | ||
20 | } | ||
21 | |||
22 | /// Gets the current character, if one exists. | ||
23 | pub fn current(&self) -> Option<char> { | ||
24 | self.chars().next() | ||
25 | } | ||
26 | |||
27 | /// Gets the nth character from the current. | ||
28 | /// For example, 0 will return the current character, 1 will return the next, etc. | ||
29 | pub fn nth(&self, n: u32) -> Option<char> { | ||
30 | self.chars().nth(n as usize) | ||
31 | } | ||
32 | |||
33 | /// Checks whether the current character is `c`. | ||
34 | pub fn at(&self, c: char) -> bool { | ||
35 | self.current() == Some(c) | ||
36 | } | ||
37 | |||
38 | /// Checks whether the next characters match `s`. | ||
39 | pub fn at_str(&self, s: &str) -> bool { | ||
40 | let chars = self.chars(); | ||
41 | chars.as_str().starts_with(s) | ||
42 | } | ||
43 | |||
44 | /// Checks whether the current character satisfies the predicate `p`. | ||
45 | pub fn at_p<P: Fn(char) -> bool>(&self, p: P) -> bool { | ||
46 | self.current().map(p) == Some(true) | ||
47 | } | ||
48 | |||
49 | /// Checks whether the nth character satisfies the predicate `p`. | ||
50 | pub fn nth_is_p<P: Fn(char) -> bool>(&self, n: u32, p: P) -> bool { | ||
51 | self.nth(n).map(p) == Some(true) | ||
52 | } | ||
53 | |||
54 | /// Moves to the next character. | ||
55 | pub fn bump(&mut self) -> Option<char> { | ||
56 | let ch = self.chars().next()?; | ||
57 | self.len += TextUnit::of_char(ch); | ||
58 | Some(ch) | ||
59 | } | ||
60 | |||
61 | /// Moves to the next character as long as `pred` is satisfied. | ||
62 | pub fn bump_while<F: Fn(char) -> bool>(&mut self, pred: F) { | ||
63 | loop { | ||
64 | match self.current() { | ||
65 | Some(c) if pred(c) => { | ||
66 | self.bump(); | ||
67 | } | ||
68 | _ => return, | ||
69 | } | ||
70 | } | ||
71 | } | ||
72 | |||
73 | /// Returns the text up to the current point. | ||
74 | pub fn current_token_text(&self) -> &str { | ||
75 | let len: u32 = self.len.into(); | ||
76 | &self.text[..len as usize] | ||
77 | } | ||
78 | |||
79 | /// Returns an iterator over the remaining characters. | ||
80 | fn chars(&self) -> Chars { | ||
81 | let len: u32 = self.len.into(); | ||
82 | self.text[len as usize..].chars() | ||
83 | } | ||
84 | } | ||
85 | |||
86 | #[cfg(test)] | ||
87 | mod tests { | ||
88 | use super::*; | ||
89 | |||
90 | #[test] | ||
91 | fn test_current() { | ||
92 | let ptr = Ptr::new("test"); | ||
93 | assert_eq!(ptr.current(), Some('t')); | ||
94 | } | ||
95 | |||
96 | #[test] | ||
97 | fn test_nth() { | ||
98 | let ptr = Ptr::new("test"); | ||
99 | assert_eq!(ptr.nth(0), Some('t')); | ||
100 | assert_eq!(ptr.nth(1), Some('e')); | ||
101 | assert_eq!(ptr.nth(2), Some('s')); | ||
102 | assert_eq!(ptr.nth(3), Some('t')); | ||
103 | assert_eq!(ptr.nth(4), None); | ||
104 | } | ||
105 | |||
106 | #[test] | ||
107 | fn test_at() { | ||
108 | let ptr = Ptr::new("test"); | ||
109 | assert!(ptr.at('t')); | ||
110 | assert!(!ptr.at('a')); | ||
111 | } | ||
112 | |||
113 | #[test] | ||
114 | fn test_at_str() { | ||
115 | let ptr = Ptr::new("test"); | ||
116 | assert!(ptr.at_str("t")); | ||
117 | assert!(ptr.at_str("te")); | ||
118 | assert!(ptr.at_str("test")); | ||
119 | assert!(!ptr.at_str("tests")); | ||
120 | assert!(!ptr.at_str("rust")); | ||
121 | } | ||
122 | |||
123 | #[test] | ||
124 | fn test_at_p() { | ||
125 | let ptr = Ptr::new("test"); | ||
126 | assert!(ptr.at_p(|c| c == 't')); | ||
127 | assert!(!ptr.at_p(|c| c == 'e')); | ||
128 | } | ||
129 | |||
130 | #[test] | ||
131 | fn test_nth_is_p() { | ||
132 | let ptr = Ptr::new("test"); | ||
133 | assert!(ptr.nth_is_p(0, |c| c == 't')); | ||
134 | assert!(!ptr.nth_is_p(1, |c| c == 't')); | ||
135 | assert!(ptr.nth_is_p(3, |c| c == 't')); | ||
136 | assert!(!ptr.nth_is_p(150, |c| c == 't')); | ||
137 | } | ||
138 | |||
139 | #[test] | ||
140 | fn test_bump() { | ||
141 | let mut ptr = Ptr::new("test"); | ||
142 | assert_eq!(ptr.current(), Some('t')); | ||
143 | ptr.bump(); | ||
144 | assert_eq!(ptr.current(), Some('e')); | ||
145 | ptr.bump(); | ||
146 | assert_eq!(ptr.current(), Some('s')); | ||
147 | ptr.bump(); | ||
148 | assert_eq!(ptr.current(), Some('t')); | ||
149 | ptr.bump(); | ||
150 | assert_eq!(ptr.current(), None); | ||
151 | ptr.bump(); | ||
152 | assert_eq!(ptr.current(), None); | ||
153 | } | ||
154 | |||
155 | #[test] | ||
156 | fn test_bump_while() { | ||
157 | let mut ptr = Ptr::new("test"); | ||
158 | assert_eq!(ptr.current(), Some('t')); | ||
159 | ptr.bump_while(|c| c != 's'); | ||
160 | assert_eq!(ptr.current(), Some('s')); | ||
161 | } | ||
162 | } | ||
diff --git a/crates/ra_syntax/src/parsing/lexer/strings.rs b/crates/ra_syntax/src/parsing/lexer/strings.rs deleted file mode 100644 index f74acff9e..000000000 --- a/crates/ra_syntax/src/parsing/lexer/strings.rs +++ /dev/null | |||
@@ -1,112 +0,0 @@ | |||
1 | use crate::{ | ||
2 | parsing::lexer::ptr::Ptr, | ||
3 | SyntaxKind::{self, *}, | ||
4 | }; | ||
5 | |||
6 | pub(crate) fn is_string_literal_start(c: char, c1: Option<char>, c2: Option<char>) -> bool { | ||
7 | match (c, c1, c2) { | ||
8 | ('r', Some('"'), _) | ||
9 | | ('r', Some('#'), Some('"')) | ||
10 | | ('r', Some('#'), Some('#')) | ||
11 | | ('b', Some('"'), _) | ||
12 | | ('b', Some('\''), _) | ||
13 | | ('b', Some('r'), Some('"')) | ||
14 | | ('b', Some('r'), Some('#')) => true, | ||
15 | _ => false, | ||
16 | } | ||
17 | } | ||
18 | |||
19 | pub(crate) fn scan_char(ptr: &mut Ptr) { | ||
20 | while let Some(c) = ptr.current() { | ||
21 | match c { | ||
22 | '\\' => { | ||
23 | ptr.bump(); | ||
24 | if ptr.at('\\') || ptr.at('\'') { | ||
25 | ptr.bump(); | ||
26 | } | ||
27 | } | ||
28 | '\'' => { | ||
29 | ptr.bump(); | ||
30 | return; | ||
31 | } | ||
32 | '\n' => return, | ||
33 | _ => { | ||
34 | ptr.bump(); | ||
35 | } | ||
36 | } | ||
37 | } | ||
38 | } | ||
39 | |||
40 | pub(crate) fn scan_byte_char_or_string(ptr: &mut Ptr) -> SyntaxKind { | ||
41 | // unwrapping and not-exhaustive match are ok | ||
42 | // because of string_literal_start | ||
43 | let c = ptr.bump().unwrap(); | ||
44 | match c { | ||
45 | '\'' => { | ||
46 | scan_byte(ptr); | ||
47 | BYTE | ||
48 | } | ||
49 | '"' => { | ||
50 | scan_byte_string(ptr); | ||
51 | BYTE_STRING | ||
52 | } | ||
53 | 'r' => { | ||
54 | scan_raw_string(ptr); | ||
55 | RAW_BYTE_STRING | ||
56 | } | ||
57 | _ => unreachable!(), | ||
58 | } | ||
59 | } | ||
60 | |||
61 | pub(crate) fn scan_string(ptr: &mut Ptr) { | ||
62 | while let Some(c) = ptr.current() { | ||
63 | match c { | ||
64 | '\\' => { | ||
65 | ptr.bump(); | ||
66 | if ptr.at('\\') || ptr.at('"') { | ||
67 | ptr.bump(); | ||
68 | } | ||
69 | } | ||
70 | '"' => { | ||
71 | ptr.bump(); | ||
72 | return; | ||
73 | } | ||
74 | _ => { | ||
75 | ptr.bump(); | ||
76 | } | ||
77 | } | ||
78 | } | ||
79 | } | ||
80 | |||
81 | pub(crate) fn scan_raw_string(ptr: &mut Ptr) { | ||
82 | let mut hashes = 0; | ||
83 | while ptr.at('#') { | ||
84 | hashes += 1; | ||
85 | ptr.bump(); | ||
86 | } | ||
87 | if !ptr.at('"') { | ||
88 | return; | ||
89 | } | ||
90 | ptr.bump(); | ||
91 | |||
92 | while let Some(c) = ptr.bump() { | ||
93 | if c == '"' { | ||
94 | let mut hashes_left = hashes; | ||
95 | while ptr.at('#') && hashes_left > 0 { | ||
96 | hashes_left -= 1; | ||
97 | ptr.bump(); | ||
98 | } | ||
99 | if hashes_left == 0 { | ||
100 | return; | ||
101 | } | ||
102 | } | ||
103 | } | ||
104 | } | ||
105 | |||
106 | fn scan_byte(ptr: &mut Ptr) { | ||
107 | scan_char(ptr) | ||
108 | } | ||
109 | |||
110 | fn scan_byte_string(ptr: &mut Ptr) { | ||
111 | scan_string(ptr) | ||
112 | } | ||
diff --git a/crates/ra_syntax/tests/data/lexer/0004_numbers.txt b/crates/ra_syntax/tests/data/lexer/0004_numbers.txt index 39988aedc..7bb89b8ae 100644 --- a/crates/ra_syntax/tests/data/lexer/0004_numbers.txt +++ b/crates/ra_syntax/tests/data/lexer/0004_numbers.txt | |||
@@ -12,9 +12,9 @@ INT_NUMBER 2 "0_" | |||
12 | WHITESPACE 1 " " | 12 | WHITESPACE 1 " " |
13 | FLOAT_NUMBER 2 "0." | 13 | FLOAT_NUMBER 2 "0." |
14 | WHITESPACE 1 " " | 14 | WHITESPACE 1 " " |
15 | INT_NUMBER 2 "0e" | 15 | FLOAT_NUMBER 2 "0e" |
16 | WHITESPACE 1 " " | 16 | WHITESPACE 1 " " |
17 | INT_NUMBER 2 "0E" | 17 | FLOAT_NUMBER 2 "0E" |
18 | WHITESPACE 1 " " | 18 | WHITESPACE 1 " " |
19 | INT_NUMBER 2 "0z" | 19 | INT_NUMBER 2 "0z" |
20 | WHITESPACE 1 "\n" | 20 | WHITESPACE 1 "\n" |
@@ -32,9 +32,9 @@ INT_NUMBER 6 "0_1279" | |||
32 | WHITESPACE 1 " " | 32 | WHITESPACE 1 " " |
33 | FLOAT_NUMBER 6 "0.1279" | 33 | FLOAT_NUMBER 6 "0.1279" |
34 | WHITESPACE 1 " " | 34 | WHITESPACE 1 " " |
35 | INT_NUMBER 6 "0e1279" | 35 | FLOAT_NUMBER 6 "0e1279" |
36 | WHITESPACE 1 " " | 36 | WHITESPACE 1 " " |
37 | INT_NUMBER 6 "0E1279" | 37 | FLOAT_NUMBER 6 "0E1279" |
38 | WHITESPACE 1 "\n" | 38 | WHITESPACE 1 "\n" |
39 | INT_NUMBER 1 "0" | 39 | INT_NUMBER 1 "0" |
40 | DOT 1 "." | 40 | DOT 1 "." |
@@ -47,9 +47,7 @@ IDENT 3 "foo" | |||
47 | L_PAREN 1 "(" | 47 | L_PAREN 1 "(" |
48 | R_PAREN 1 ")" | 48 | R_PAREN 1 ")" |
49 | WHITESPACE 1 "\n" | 49 | WHITESPACE 1 "\n" |
50 | INT_NUMBER 2 "0e" | 50 | FLOAT_NUMBER 4 "0e+1" |
51 | PLUS 1 "+" | ||
52 | INT_NUMBER 1 "1" | ||
53 | WHITESPACE 1 "\n" | 51 | WHITESPACE 1 "\n" |
54 | INT_NUMBER 1 "0" | 52 | INT_NUMBER 1 "0" |
55 | DOT 1 "." | 53 | DOT 1 "." |
diff --git a/crates/ra_syntax/tests/data/lexer/0014_unclosed_char.txt b/crates/ra_syntax/tests/data/lexer/0014_unclosed_char.txt index 812dfbc18..737a300ee 100644 --- a/crates/ra_syntax/tests/data/lexer/0014_unclosed_char.txt +++ b/crates/ra_syntax/tests/data/lexer/0014_unclosed_char.txt | |||
@@ -1 +1 @@ | |||
CHAR 2 "\'1" | LIFETIME 2 "\'1" | ||
diff --git a/crates/ra_syntax/tests/data/parser/err/0002_duplicate_shebang.txt b/crates/ra_syntax/tests/data/parser/err/0002_duplicate_shebang.txt index 76d186a3c..84867026f 100644 --- a/crates/ra_syntax/tests/data/parser/err/0002_duplicate_shebang.txt +++ b/crates/ra_syntax/tests/data/parser/err/0002_duplicate_shebang.txt | |||
@@ -1,7 +1,39 @@ | |||
1 | SOURCE_FILE@[0; 42) | 1 | SOURCE_FILE@[0; 42) |
2 | SHEBANG@[0; 20) "#!/use/bin/env rusti" | 2 | SHEBANG@[0; 20) "#!/use/bin/env rusti" |
3 | WHITESPACE@[20; 21) "\n" | 3 | WHITESPACE@[20; 21) "\n" |
4 | ERROR@[21; 41) | 4 | ATTR@[21; 23) |
5 | SHEBANG@[21; 41) "#!/use/bin/env rusti" | 5 | POUND@[21; 22) "#" |
6 | EXCL@[22; 23) "!" | ||
7 | ERROR@[23; 24) | ||
8 | SLASH@[23; 24) "/" | ||
9 | USE_ITEM@[24; 28) | ||
10 | USE_KW@[24; 27) "use" | ||
11 | ERROR@[27; 28) | ||
12 | SLASH@[27; 28) "/" | ||
13 | MACRO_CALL@[28; 31) | ||
14 | PATH@[28; 31) | ||
15 | PATH_SEGMENT@[28; 31) | ||
16 | NAME_REF@[28; 31) | ||
17 | IDENT@[28; 31) "bin" | ||
18 | ERROR@[31; 32) | ||
19 | SLASH@[31; 32) "/" | ||
20 | MACRO_CALL@[32; 41) | ||
21 | PATH@[32; 35) | ||
22 | PATH_SEGMENT@[32; 35) | ||
23 | NAME_REF@[32; 35) | ||
24 | IDENT@[32; 35) "env" | ||
25 | WHITESPACE@[35; 36) " " | ||
26 | NAME@[36; 41) | ||
27 | IDENT@[36; 41) "rusti" | ||
6 | WHITESPACE@[41; 42) "\n" | 28 | WHITESPACE@[41; 42) "\n" |
7 | error 21: expected an item | 29 | error 23: expected `[` |
30 | error 23: expected an item | ||
31 | error 27: expected one of `*`, `::`, `{`, `self`, `super` or an indentifier | ||
32 | error 28: expected SEMI | ||
33 | error 31: expected EXCL | ||
34 | error 31: expected `{`, `[`, `(` | ||
35 | error 31: expected SEMI | ||
36 | error 31: expected an item | ||
37 | error 35: expected EXCL | ||
38 | error 41: expected `{`, `[`, `(` | ||
39 | error 41: expected SEMI | ||
diff --git a/crates/ra_syntax/tests/data/parser/err/0030_string_suffixes.rs b/crates/ra_syntax/tests/data/parser/ok/0030_string_suffixes.rs index 261aad1fb..261aad1fb 100644 --- a/crates/ra_syntax/tests/data/parser/err/0030_string_suffixes.rs +++ b/crates/ra_syntax/tests/data/parser/ok/0030_string_suffixes.rs | |||
diff --git a/crates/ra_syntax/tests/data/parser/err/0030_string_suffixes.txt b/crates/ra_syntax/tests/data/parser/ok/0030_string_suffixes.txt index b0acfa5d2..4f7e809c5 100644 --- a/crates/ra_syntax/tests/data/parser/err/0030_string_suffixes.txt +++ b/crates/ra_syntax/tests/data/parser/ok/0030_string_suffixes.txt | |||
@@ -11,7 +11,7 @@ SOURCE_FILE@[0; 112) | |||
11 | BLOCK@[10; 111) | 11 | BLOCK@[10; 111) |
12 | L_CURLY@[10; 11) "{" | 12 | L_CURLY@[10; 11) "{" |
13 | WHITESPACE@[11; 16) "\n " | 13 | WHITESPACE@[11; 16) "\n " |
14 | LET_STMT@[16; 27) | 14 | LET_STMT@[16; 31) |
15 | LET_KW@[16; 19) "let" | 15 | LET_KW@[16; 19) "let" |
16 | WHITESPACE@[19; 20) " " | 16 | WHITESPACE@[19; 20) " " |
17 | PLACEHOLDER_PAT@[20; 21) | 17 | PLACEHOLDER_PAT@[20; 21) |
@@ -19,14 +19,8 @@ SOURCE_FILE@[0; 112) | |||
19 | WHITESPACE@[21; 22) " " | 19 | WHITESPACE@[21; 22) " " |
20 | EQ@[22; 23) "=" | 20 | EQ@[22; 23) "=" |
21 | WHITESPACE@[23; 24) " " | 21 | WHITESPACE@[23; 24) " " |
22 | LITERAL@[24; 27) | 22 | LITERAL@[24; 30) |
23 | CHAR@[24; 27) "\'c\'" | 23 | CHAR@[24; 30) "\'c\'u32" |
24 | EXPR_STMT@[27; 31) | ||
25 | PATH_EXPR@[27; 30) | ||
26 | PATH@[27; 30) | ||
27 | PATH_SEGMENT@[27; 30) | ||
28 | NAME_REF@[27; 30) | ||
29 | IDENT@[27; 30) "u32" | ||
30 | SEMI@[30; 31) ";" | 24 | SEMI@[30; 31) ";" |
31 | WHITESPACE@[31; 36) "\n " | 25 | WHITESPACE@[31; 36) "\n " |
32 | LET_STMT@[36; 60) | 26 | LET_STMT@[36; 60) |
@@ -67,4 +61,3 @@ SOURCE_FILE@[0; 112) | |||
67 | WHITESPACE@[109; 110) "\n" | 61 | WHITESPACE@[109; 110) "\n" |
68 | R_CURLY@[110; 111) "}" | 62 | R_CURLY@[110; 111) "}" |
69 | WHITESPACE@[111; 112) "\n" | 63 | WHITESPACE@[111; 112) "\n" |
70 | error 27: expected SEMI | ||