diff options
116 files changed, 3695 insertions, 2394 deletions
diff --git a/Cargo.lock b/Cargo.lock index 57383491a..7b175ec67 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -26,9 +26,9 @@ dependencies = [ | |||
26 | 26 | ||
27 | [[package]] | 27 | [[package]] |
28 | name = "anyhow" | 28 | name = "anyhow" |
29 | version = "1.0.37" | 29 | version = "1.0.38" |
30 | source = "registry+https://github.com/rust-lang/crates.io-index" | 30 | source = "registry+https://github.com/rust-lang/crates.io-index" |
31 | checksum = "ee67c11feeac938fae061b232e38e0b6d94f97a9df10e6271319325ac4c56a86" | 31 | checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" |
32 | 32 | ||
33 | [[package]] | 33 | [[package]] |
34 | name = "anymap" | 34 | name = "anymap" |
@@ -122,17 +122,28 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" | |||
122 | 122 | ||
123 | [[package]] | 123 | [[package]] |
124 | name = "byteorder" | 124 | name = "byteorder" |
125 | version = "1.3.4" | 125 | version = "1.4.2" |
126 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
127 | checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" | ||
128 | |||
129 | [[package]] | ||
130 | name = "cargo-platform" | ||
131 | version = "0.1.1" | ||
126 | source = "registry+https://github.com/rust-lang/crates.io-index" | 132 | source = "registry+https://github.com/rust-lang/crates.io-index" |
127 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" | 133 | checksum = "0226944a63d1bf35a3b5f948dd7c59e263db83695c9e8bffc4037de02e30f1d7" |
134 | dependencies = [ | ||
135 | "serde", | ||
136 | ] | ||
128 | 137 | ||
129 | [[package]] | 138 | [[package]] |
130 | name = "cargo_metadata" | 139 | name = "cargo_metadata" |
131 | version = "0.12.0" | 140 | version = "0.12.2" |
132 | source = "registry+https://github.com/rust-lang/crates.io-index" | 141 | source = "registry+https://github.com/rust-lang/crates.io-index" |
133 | checksum = "d5a5f7b42f606b7f23674f6f4d877628350682bc40687d3fae65679a58d55345" | 142 | checksum = "11a47b6286279a9998588ef7050d1ebc2500c69892a557c90fe5d071c64415dc" |
134 | dependencies = [ | 143 | dependencies = [ |
144 | "cargo-platform", | ||
135 | "semver", | 145 | "semver", |
146 | "semver-parser", | ||
136 | "serde", | 147 | "serde", |
137 | "serde_json", | 148 | "serde_json", |
138 | ] | 149 | ] |
@@ -262,9 +273,9 @@ dependencies = [ | |||
262 | 273 | ||
263 | [[package]] | 274 | [[package]] |
264 | name = "const_fn" | 275 | name = "const_fn" |
265 | version = "0.4.4" | 276 | version = "0.4.5" |
266 | source = "registry+https://github.com/rust-lang/crates.io-index" | 277 | source = "registry+https://github.com/rust-lang/crates.io-index" |
267 | checksum = "cd51eab21ab4fd6a3bf889e2d0958c0a6e3a61ad04260325e919e652a2a62826" | 278 | checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6" |
268 | 279 | ||
269 | [[package]] | 280 | [[package]] |
270 | name = "crc32fast" | 281 | name = "crc32fast" |
@@ -774,9 +785,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" | |||
774 | 785 | ||
775 | [[package]] | 786 | [[package]] |
776 | name = "libc" | 787 | name = "libc" |
777 | version = "0.2.81" | 788 | version = "0.2.82" |
778 | source = "registry+https://github.com/rust-lang/crates.io-index" | 789 | source = "registry+https://github.com/rust-lang/crates.io-index" |
779 | checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb" | 790 | checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929" |
780 | 791 | ||
781 | [[package]] | 792 | [[package]] |
782 | name = "libloading" | 793 | name = "libloading" |
@@ -808,9 +819,9 @@ dependencies = [ | |||
808 | 819 | ||
809 | [[package]] | 820 | [[package]] |
810 | name = "log" | 821 | name = "log" |
811 | version = "0.4.11" | 822 | version = "0.4.13" |
812 | source = "registry+https://github.com/rust-lang/crates.io-index" | 823 | source = "registry+https://github.com/rust-lang/crates.io-index" |
813 | checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" | 824 | checksum = "fcf3805d4480bb5b86070dcfeb9e2cb2ebc148adb753c5cca5f884d1d65a42b2" |
814 | dependencies = [ | 825 | dependencies = [ |
815 | "cfg-if 0.1.10", | 826 | "cfg-if 0.1.10", |
816 | ] | 827 | ] |
@@ -1134,9 +1145,9 @@ checksum = "28b9b4df73455c861d7cbf8be42f01d3b373ed7f02e378d55fa84eafc6f638b1" | |||
1134 | 1145 | ||
1135 | [[package]] | 1146 | [[package]] |
1136 | name = "pin-project-lite" | 1147 | name = "pin-project-lite" |
1137 | version = "0.2.0" | 1148 | version = "0.2.4" |
1138 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1149 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1139 | checksum = "6b063f57ec186e6140e2b8b6921e5f1bd89c7356dda5b33acc5401203ca6131c" | 1150 | checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827" |
1140 | 1151 | ||
1141 | [[package]] | 1152 | [[package]] |
1142 | name = "proc-macro2" | 1153 | name = "proc-macro2" |
@@ -1275,9 +1286,9 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" | |||
1275 | 1286 | ||
1276 | [[package]] | 1287 | [[package]] |
1277 | name = "regex" | 1288 | name = "regex" |
1278 | version = "1.4.2" | 1289 | version = "1.4.3" |
1279 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1290 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1280 | checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" | 1291 | checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" |
1281 | dependencies = [ | 1292 | dependencies = [ |
1282 | "regex-syntax", | 1293 | "regex-syntax", |
1283 | ] | 1294 | ] |
@@ -1294,9 +1305,9 @@ dependencies = [ | |||
1294 | 1305 | ||
1295 | [[package]] | 1306 | [[package]] |
1296 | name = "regex-syntax" | 1307 | name = "regex-syntax" |
1297 | version = "0.6.21" | 1308 | version = "0.6.22" |
1298 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1309 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1299 | checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" | 1310 | checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" |
1300 | 1311 | ||
1301 | [[package]] | 1312 | [[package]] |
1302 | name = "rowan" | 1313 | name = "rowan" |
@@ -1449,9 +1460,9 @@ dependencies = [ | |||
1449 | 1460 | ||
1450 | [[package]] | 1461 | [[package]] |
1451 | name = "semver-parser" | 1462 | name = "semver-parser" |
1452 | version = "0.10.1" | 1463 | version = "0.10.2" |
1453 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1464 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1454 | checksum = "42ef146c2ad5e5f4b037cd6ce2ebb775401729b19a82040c1beac9d36c7d1428" | 1465 | checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" |
1455 | dependencies = [ | 1466 | dependencies = [ |
1456 | "pest", | 1467 | "pest", |
1457 | ] | 1468 | ] |
@@ -1525,9 +1536,9 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" | |||
1525 | 1536 | ||
1526 | [[package]] | 1537 | [[package]] |
1527 | name = "smallvec" | 1538 | name = "smallvec" |
1528 | version = "1.6.0" | 1539 | version = "1.6.1" |
1529 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1540 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1530 | checksum = "1a55ca5f3b68e41c979bf8c46a6f1da892ca4db8f94023ce0bd32407573b1ac0" | 1541 | checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" |
1531 | 1542 | ||
1532 | [[package]] | 1543 | [[package]] |
1533 | name = "smol_str" | 1544 | name = "smol_str" |
@@ -1558,9 +1569,9 @@ version = "0.0.0" | |||
1558 | 1569 | ||
1559 | [[package]] | 1570 | [[package]] |
1560 | name = "syn" | 1571 | name = "syn" |
1561 | version = "1.0.57" | 1572 | version = "1.0.58" |
1562 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1573 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1563 | checksum = "4211ce9909eb971f111059df92c45640aad50a619cf55cd76476be803c4c68e6" | 1574 | checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5" |
1564 | dependencies = [ | 1575 | dependencies = [ |
1565 | "proc-macro2", | 1576 | "proc-macro2", |
1566 | "quote", | 1577 | "quote", |
@@ -1624,9 +1635,9 @@ dependencies = [ | |||
1624 | 1635 | ||
1625 | [[package]] | 1636 | [[package]] |
1626 | name = "text-size" | 1637 | name = "text-size" |
1627 | version = "1.0.0" | 1638 | version = "1.1.0" |
1628 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1639 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1629 | checksum = "f03e7efdedc3bc78cb2337f1e2785c39e45f5ef762d9e4ebb137fff7380a6d8a" | 1640 | checksum = "288cb548dbe72b652243ea797201f3d481a0609a967980fcc5b2315ea811560a" |
1630 | 1641 | ||
1631 | [[package]] | 1642 | [[package]] |
1632 | name = "text_edit" | 1643 | name = "text_edit" |
@@ -1643,9 +1654,9 @@ checksum = "db3c46be180f1af9673ebb27bc1235396f61ef6965b3fe0dbb2e624deb604f0e" | |||
1643 | 1654 | ||
1644 | [[package]] | 1655 | [[package]] |
1645 | name = "thread_local" | 1656 | name = "thread_local" |
1646 | version = "1.0.1" | 1657 | version = "1.1.0" |
1647 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1658 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1648 | checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" | 1659 | checksum = "bb9bc092d0d51e76b2b19d9d85534ffc9ec2db959a2523cdae0697e2972cd447" |
1649 | dependencies = [ | 1660 | dependencies = [ |
1650 | "lazy_static", | 1661 | "lazy_static", |
1651 | ] | 1662 | ] |
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs index 40028fc01..e3ef04932 100644 --- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs | |||
@@ -2,12 +2,16 @@ use std::iter; | |||
2 | 2 | ||
3 | use either::Either; | 3 | use either::Either; |
4 | use hir::{AsName, Module, ModuleDef, Name, Variant}; | 4 | use hir::{AsName, Module, ModuleDef, Name, Variant}; |
5 | use ide_db::helpers::{ | 5 | use ide_db::{ |
6 | insert_use::{insert_use, ImportScope}, | 6 | defs::Definition, |
7 | mod_path_to_ast, | 7 | helpers::{ |
8 | insert_use::{insert_use, ImportScope}, | ||
9 | mod_path_to_ast, | ||
10 | }, | ||
11 | search::FileReference, | ||
12 | RootDatabase, | ||
8 | }; | 13 | }; |
9 | use ide_db::{defs::Definition, search::Reference, RootDatabase}; | 14 | use rustc_hash::FxHashSet; |
10 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
11 | use syntax::{ | 15 | use syntax::{ |
12 | algo::{find_node_at_offset, SyntaxRewriter}, | 16 | algo::{find_node_at_offset, SyntaxRewriter}, |
13 | ast::{self, edit::IndentLevel, make, AstNode, NameOwner, VisibilityOwner}, | 17 | ast::{self, edit::IndentLevel, make, AstNode, NameOwner, VisibilityOwner}, |
@@ -58,29 +62,29 @@ pub(crate) fn extract_struct_from_enum_variant( | |||
58 | let mut visited_modules_set = FxHashSet::default(); | 62 | let mut visited_modules_set = FxHashSet::default(); |
59 | let current_module = enum_hir.module(ctx.db()); | 63 | let current_module = enum_hir.module(ctx.db()); |
60 | visited_modules_set.insert(current_module); | 64 | visited_modules_set.insert(current_module); |
61 | let mut rewriters = FxHashMap::default(); | 65 | let mut def_rewriter = None; |
62 | for reference in usages { | 66 | for (file_id, references) in usages { |
63 | let rewriter = rewriters | 67 | let mut rewriter = SyntaxRewriter::default(); |
64 | .entry(reference.file_range.file_id) | 68 | let source_file = ctx.sema.parse(file_id); |
65 | .or_insert_with(SyntaxRewriter::default); | 69 | for reference in references { |
66 | let source_file = ctx.sema.parse(reference.file_range.file_id); | 70 | update_reference( |
67 | update_reference( | 71 | ctx, |
68 | ctx, | 72 | &mut rewriter, |
69 | rewriter, | 73 | reference, |
70 | reference, | 74 | &source_file, |
71 | &source_file, | 75 | &enum_module_def, |
72 | &enum_module_def, | 76 | &variant_hir_name, |
73 | &variant_hir_name, | 77 | &mut visited_modules_set, |
74 | &mut visited_modules_set, | 78 | ); |
75 | ); | 79 | } |
76 | } | 80 | if file_id == ctx.frange.file_id { |
77 | let mut rewriter = | 81 | def_rewriter = Some(rewriter); |
78 | rewriters.remove(&ctx.frange.file_id).unwrap_or_else(SyntaxRewriter::default); | 82 | continue; |
79 | for (file_id, rewriter) in rewriters { | 83 | } |
80 | builder.edit_file(file_id); | 84 | builder.edit_file(file_id); |
81 | builder.rewrite(rewriter); | 85 | builder.rewrite(rewriter); |
82 | } | 86 | } |
83 | builder.edit_file(ctx.frange.file_id); | 87 | let mut rewriter = def_rewriter.unwrap_or_default(); |
84 | update_variant(&mut rewriter, &variant); | 88 | update_variant(&mut rewriter, &variant); |
85 | extract_struct_def( | 89 | extract_struct_def( |
86 | &mut rewriter, | 90 | &mut rewriter, |
@@ -90,6 +94,7 @@ pub(crate) fn extract_struct_from_enum_variant( | |||
90 | &variant.parent_enum().syntax().clone().into(), | 94 | &variant.parent_enum().syntax().clone().into(), |
91 | enum_ast.visibility(), | 95 | enum_ast.visibility(), |
92 | ); | 96 | ); |
97 | builder.edit_file(ctx.frange.file_id); | ||
93 | builder.rewrite(rewriter); | 98 | builder.rewrite(rewriter); |
94 | }, | 99 | }, |
95 | ) | 100 | ) |
@@ -205,13 +210,13 @@ fn update_variant(rewriter: &mut SyntaxRewriter, variant: &ast::Variant) -> Opti | |||
205 | fn update_reference( | 210 | fn update_reference( |
206 | ctx: &AssistContext, | 211 | ctx: &AssistContext, |
207 | rewriter: &mut SyntaxRewriter, | 212 | rewriter: &mut SyntaxRewriter, |
208 | reference: Reference, | 213 | reference: FileReference, |
209 | source_file: &SourceFile, | 214 | source_file: &SourceFile, |
210 | enum_module_def: &ModuleDef, | 215 | enum_module_def: &ModuleDef, |
211 | variant_hir_name: &Name, | 216 | variant_hir_name: &Name, |
212 | visited_modules_set: &mut FxHashSet<Module>, | 217 | visited_modules_set: &mut FxHashSet<Module>, |
213 | ) -> Option<()> { | 218 | ) -> Option<()> { |
214 | let offset = reference.file_range.range.start(); | 219 | let offset = reference.range.start(); |
215 | let (segment, expr) = if let Some(path_expr) = | 220 | let (segment, expr) = if let Some(path_expr) = |
216 | find_node_at_offset::<ast::PathExpr>(source_file.syntax(), offset) | 221 | find_node_at_offset::<ast::PathExpr>(source_file.syntax(), offset) |
217 | { | 222 | { |
diff --git a/crates/assists/src/handlers/inline_local_variable.rs b/crates/assists/src/handlers/inline_local_variable.rs index d559be9cb..dc798daaa 100644 --- a/crates/assists/src/handlers/inline_local_variable.rs +++ b/crates/assists/src/handlers/inline_local_variable.rs | |||
@@ -1,4 +1,7 @@ | |||
1 | use ide_db::{defs::Definition, search::ReferenceKind}; | 1 | use ide_db::{ |
2 | defs::Definition, | ||
3 | search::{FileReference, ReferenceKind}, | ||
4 | }; | ||
2 | use syntax::{ | 5 | use syntax::{ |
3 | ast::{self, AstNode, AstToken}, | 6 | ast::{self, AstNode, AstToken}, |
4 | TextRange, | 7 | TextRange, |
@@ -44,8 +47,8 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O | |||
44 | 47 | ||
45 | let def = ctx.sema.to_def(&bind_pat)?; | 48 | let def = ctx.sema.to_def(&bind_pat)?; |
46 | let def = Definition::Local(def); | 49 | let def = Definition::Local(def); |
47 | let refs = def.usages(&ctx.sema).all(); | 50 | let usages = def.usages(&ctx.sema).all(); |
48 | if refs.is_empty() { | 51 | if usages.is_empty() { |
49 | mark::hit!(test_not_applicable_if_variable_unused); | 52 | mark::hit!(test_not_applicable_if_variable_unused); |
50 | return None; | 53 | return None; |
51 | }; | 54 | }; |
@@ -63,48 +66,45 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O | |||
63 | let_stmt.syntax().text_range() | 66 | let_stmt.syntax().text_range() |
64 | }; | 67 | }; |
65 | 68 | ||
66 | let mut wrap_in_parens = vec![true; refs.len()]; | 69 | let wrap_in_parens = usages |
67 | 70 | .references | |
68 | for (i, desc) in refs.iter().enumerate() { | 71 | .values() |
69 | let usage_node = ctx | 72 | .flatten() |
70 | .covering_node_for_range(desc.file_range.range) | 73 | .map(|&FileReference { range, .. }| { |
71 | .ancestors() | 74 | let usage_node = |
72 | .find_map(ast::PathExpr::cast)?; | 75 | ctx.covering_node_for_range(range).ancestors().find_map(ast::PathExpr::cast)?; |
73 | let usage_parent_option = usage_node.syntax().parent().and_then(ast::Expr::cast); | 76 | let usage_parent_option = usage_node.syntax().parent().and_then(ast::Expr::cast); |
74 | let usage_parent = match usage_parent_option { | 77 | let usage_parent = match usage_parent_option { |
75 | Some(u) => u, | 78 | Some(u) => u, |
76 | None => { | 79 | None => return Ok(false), |
77 | wrap_in_parens[i] = false; | 80 | }; |
78 | continue; | 81 | |
79 | } | 82 | Ok(!matches!((&initializer_expr, usage_parent), |
80 | }; | 83 | (ast::Expr::CallExpr(_), _) |
81 | 84 | | (ast::Expr::IndexExpr(_), _) | |
82 | wrap_in_parens[i] = match (&initializer_expr, usage_parent) { | 85 | | (ast::Expr::MethodCallExpr(_), _) |
83 | (ast::Expr::CallExpr(_), _) | 86 | | (ast::Expr::FieldExpr(_), _) |
84 | | (ast::Expr::IndexExpr(_), _) | 87 | | (ast::Expr::TryExpr(_), _) |
85 | | (ast::Expr::MethodCallExpr(_), _) | 88 | | (ast::Expr::RefExpr(_), _) |
86 | | (ast::Expr::FieldExpr(_), _) | 89 | | (ast::Expr::Literal(_), _) |
87 | | (ast::Expr::TryExpr(_), _) | 90 | | (ast::Expr::TupleExpr(_), _) |
88 | | (ast::Expr::RefExpr(_), _) | 91 | | (ast::Expr::ArrayExpr(_), _) |
89 | | (ast::Expr::Literal(_), _) | 92 | | (ast::Expr::ParenExpr(_), _) |
90 | | (ast::Expr::TupleExpr(_), _) | 93 | | (ast::Expr::PathExpr(_), _) |
91 | | (ast::Expr::ArrayExpr(_), _) | 94 | | (ast::Expr::BlockExpr(_), _) |
92 | | (ast::Expr::ParenExpr(_), _) | 95 | | (ast::Expr::EffectExpr(_), _) |
93 | | (ast::Expr::PathExpr(_), _) | 96 | | (_, ast::Expr::CallExpr(_)) |
94 | | (ast::Expr::BlockExpr(_), _) | 97 | | (_, ast::Expr::TupleExpr(_)) |
95 | | (ast::Expr::EffectExpr(_), _) | 98 | | (_, ast::Expr::ArrayExpr(_)) |
96 | | (_, ast::Expr::CallExpr(_)) | 99 | | (_, ast::Expr::ParenExpr(_)) |
97 | | (_, ast::Expr::TupleExpr(_)) | 100 | | (_, ast::Expr::ForExpr(_)) |
98 | | (_, ast::Expr::ArrayExpr(_)) | 101 | | (_, ast::Expr::WhileExpr(_)) |
99 | | (_, ast::Expr::ParenExpr(_)) | 102 | | (_, ast::Expr::BreakExpr(_)) |
100 | | (_, ast::Expr::ForExpr(_)) | 103 | | (_, ast::Expr::ReturnExpr(_)) |
101 | | (_, ast::Expr::WhileExpr(_)) | 104 | | (_, ast::Expr::MatchExpr(_)) |
102 | | (_, ast::Expr::BreakExpr(_)) | 105 | )) |
103 | | (_, ast::Expr::ReturnExpr(_)) | 106 | }) |
104 | | (_, ast::Expr::MatchExpr(_)) => false, | 107 | .collect::<Result<Vec<_>, _>>()?; |
105 | _ => true, | ||
106 | }; | ||
107 | } | ||
108 | 108 | ||
109 | let init_str = initializer_expr.syntax().text().to_string(); | 109 | let init_str = initializer_expr.syntax().text().to_string(); |
110 | let init_in_paren = format!("({})", &init_str); | 110 | let init_in_paren = format!("({})", &init_str); |
@@ -116,15 +116,16 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O | |||
116 | target, | 116 | target, |
117 | move |builder| { | 117 | move |builder| { |
118 | builder.delete(delete_range); | 118 | builder.delete(delete_range); |
119 | for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) { | 119 | for (reference, should_wrap) in usages.references.values().flatten().zip(wrap_in_parens) |
120 | { | ||
120 | let replacement = | 121 | let replacement = |
121 | if should_wrap { init_in_paren.clone() } else { init_str.clone() }; | 122 | if should_wrap { init_in_paren.clone() } else { init_str.clone() }; |
122 | match desc.kind { | 123 | match reference.kind { |
123 | ReferenceKind::FieldShorthandForLocal => { | 124 | ReferenceKind::FieldShorthandForLocal => { |
124 | mark::hit!(inline_field_shorthand); | 125 | mark::hit!(inline_field_shorthand); |
125 | builder.insert(desc.file_range.range.end(), format!(": {}", replacement)) | 126 | builder.insert(reference.range.end(), format!(": {}", replacement)) |
126 | } | 127 | } |
127 | _ => builder.replace(desc.file_range.range, replacement), | 128 | _ => builder.replace(reference.range, replacement), |
128 | } | 129 | } |
129 | } | 130 | } |
130 | }, | 131 | }, |
diff --git a/crates/assists/src/handlers/remove_dbg.rs b/crates/assists/src/handlers/remove_dbg.rs index 0320c2f12..6114091f2 100644 --- a/crates/assists/src/handlers/remove_dbg.rs +++ b/crates/assists/src/handlers/remove_dbg.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use syntax::{ | 1 | use syntax::{ |
2 | ast::{self, AstNode}, | 2 | ast::{self, AstNode}, |
3 | match_ast, SyntaxElement, SyntaxKind, TextRange, TextSize, T, | 3 | match_ast, SyntaxElement, TextRange, TextSize, T, |
4 | }; | 4 | }; |
5 | 5 | ||
6 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 6 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
@@ -136,14 +136,14 @@ fn needs_parentheses_around_macro_contents(macro_contents: Vec<SyntaxElement>) - | |||
136 | symbol_kind => { | 136 | symbol_kind => { |
137 | let symbol_not_in_bracket = unpaired_brackets_in_contents.is_empty(); | 137 | let symbol_not_in_bracket = unpaired_brackets_in_contents.is_empty(); |
138 | if symbol_not_in_bracket | 138 | if symbol_not_in_bracket |
139 | && symbol_kind != SyntaxKind::COLON // paths | 139 | && symbol_kind != T![:] // paths |
140 | && (symbol_kind != SyntaxKind::DOT // field/method access | 140 | && (symbol_kind != T![.] // field/method access |
141 | || macro_contents // range expressions consist of two SyntaxKind::Dot in macro invocations | 141 | || macro_contents // range expressions consist of two SyntaxKind::Dot in macro invocations |
142 | .peek() | 142 | .peek() |
143 | .map(|element| element.kind() == SyntaxKind::DOT) | 143 | .map(|element| element.kind() == T![.]) |
144 | .unwrap_or(false)) | 144 | .unwrap_or(false)) |
145 | && symbol_kind != SyntaxKind::QUESTION // try operator | 145 | && symbol_kind != T![?] // try operator |
146 | && (symbol_kind.is_punct() || symbol_kind == SyntaxKind::AS_KW) | 146 | && (symbol_kind.is_punct() || symbol_kind == T![as]) |
147 | { | 147 | { |
148 | return true; | 148 | return true; |
149 | } | 149 | } |
diff --git a/crates/assists/src/handlers/remove_unused_param.rs b/crates/assists/src/handlers/remove_unused_param.rs index 56e8b5229..c961680e2 100644 --- a/crates/assists/src/handlers/remove_unused_param.rs +++ b/crates/assists/src/handlers/remove_unused_param.rs | |||
@@ -1,8 +1,8 @@ | |||
1 | use ide_db::{defs::Definition, search::Reference}; | 1 | use ide_db::{base_db::FileId, defs::Definition, search::FileReference}; |
2 | use syntax::{ | 2 | use syntax::{ |
3 | algo::find_node_at_range, | 3 | algo::find_node_at_range, |
4 | ast::{self, ArgListOwner}, | 4 | ast::{self, ArgListOwner}, |
5 | AstNode, SyntaxKind, SyntaxNode, TextRange, T, | 5 | AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, T, |
6 | }; | 6 | }; |
7 | use test_utils::mark; | 7 | use test_utils::mark; |
8 | use SyntaxKind::WHITESPACE; | 8 | use SyntaxKind::WHITESPACE; |
@@ -58,32 +58,41 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext) -> Opt | |||
58 | param.syntax().text_range(), | 58 | param.syntax().text_range(), |
59 | |builder| { | 59 | |builder| { |
60 | builder.delete(range_to_remove(param.syntax())); | 60 | builder.delete(range_to_remove(param.syntax())); |
61 | for usage in fn_def.usages(&ctx.sema).all() { | 61 | for (file_id, references) in fn_def.usages(&ctx.sema).all() { |
62 | process_usage(ctx, builder, usage, param_position); | 62 | process_usages(ctx, builder, file_id, references, param_position); |
63 | } | 63 | } |
64 | }, | 64 | }, |
65 | ) | 65 | ) |
66 | } | 66 | } |
67 | 67 | ||
68 | fn process_usage( | 68 | fn process_usages( |
69 | ctx: &AssistContext, | 69 | ctx: &AssistContext, |
70 | builder: &mut AssistBuilder, | 70 | builder: &mut AssistBuilder, |
71 | usage: Reference, | 71 | file_id: FileId, |
72 | references: Vec<FileReference>, | ||
72 | arg_to_remove: usize, | 73 | arg_to_remove: usize, |
73 | ) -> Option<()> { | 74 | ) { |
74 | let source_file = ctx.sema.parse(usage.file_range.file_id); | 75 | let source_file = ctx.sema.parse(file_id); |
75 | let call_expr: ast::CallExpr = | 76 | builder.edit_file(file_id); |
76 | find_node_at_range(source_file.syntax(), usage.file_range.range)?; | 77 | for usage in references { |
78 | if let Some(text_range) = process_usage(&source_file, usage, arg_to_remove) { | ||
79 | builder.delete(text_range); | ||
80 | } | ||
81 | } | ||
82 | } | ||
83 | |||
84 | fn process_usage( | ||
85 | source_file: &SourceFile, | ||
86 | FileReference { range, .. }: FileReference, | ||
87 | arg_to_remove: usize, | ||
88 | ) -> Option<TextRange> { | ||
89 | let call_expr: ast::CallExpr = find_node_at_range(source_file.syntax(), range)?; | ||
77 | let call_expr_range = call_expr.expr()?.syntax().text_range(); | 90 | let call_expr_range = call_expr.expr()?.syntax().text_range(); |
78 | if !call_expr_range.contains_range(usage.file_range.range) { | 91 | if !call_expr_range.contains_range(range) { |
79 | return None; | 92 | return None; |
80 | } | 93 | } |
81 | let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?; | 94 | let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?; |
82 | 95 | Some(range_to_remove(arg.syntax())) | |
83 | builder.edit_file(usage.file_range.file_id); | ||
84 | builder.delete(range_to_remove(arg.syntax())); | ||
85 | |||
86 | Some(()) | ||
87 | } | 96 | } |
88 | 97 | ||
89 | fn range_to_remove(node: &SyntaxNode) -> TextRange { | 98 | fn range_to_remove(node: &SyntaxNode) -> TextRange { |
diff --git a/crates/assists/src/handlers/reorder_impl.rs b/crates/assists/src/handlers/reorder_impl.rs new file mode 100644 index 000000000..309f291c8 --- /dev/null +++ b/crates/assists/src/handlers/reorder_impl.rs | |||
@@ -0,0 +1,201 @@ | |||
1 | use itertools::Itertools; | ||
2 | use rustc_hash::FxHashMap; | ||
3 | |||
4 | use hir::{PathResolution, Semantics}; | ||
5 | use ide_db::RootDatabase; | ||
6 | use syntax::{ | ||
7 | algo, | ||
8 | ast::{self, NameOwner}, | ||
9 | AstNode, | ||
10 | }; | ||
11 | use test_utils::mark; | ||
12 | |||
13 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | ||
14 | |||
15 | // Assist: reorder_impl | ||
16 | // | ||
17 | // Reorder the methods of an `impl Trait`. The methods will be ordered | ||
18 | // in the same order as in the trait definition. | ||
19 | // | ||
20 | // ``` | ||
21 | // trait Foo { | ||
22 | // fn a() {} | ||
23 | // fn b() {} | ||
24 | // fn c() {} | ||
25 | // } | ||
26 | // | ||
27 | // struct Bar; | ||
28 | // $0impl Foo for Bar { | ||
29 | // fn b() {} | ||
30 | // fn c() {} | ||
31 | // fn a() {} | ||
32 | // } | ||
33 | // ``` | ||
34 | // -> | ||
35 | // ``` | ||
36 | // trait Foo { | ||
37 | // fn a() {} | ||
38 | // fn b() {} | ||
39 | // fn c() {} | ||
40 | // } | ||
41 | // | ||
42 | // struct Bar; | ||
43 | // impl Foo for Bar { | ||
44 | // fn a() {} | ||
45 | // fn b() {} | ||
46 | // fn c() {} | ||
47 | // } | ||
48 | // ``` | ||
49 | // | ||
50 | pub(crate) fn reorder_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
51 | let impl_ast = ctx.find_node_at_offset::<ast::Impl>()?; | ||
52 | let items = impl_ast.assoc_item_list()?; | ||
53 | let methods = get_methods(&items); | ||
54 | |||
55 | let path = impl_ast | ||
56 | .trait_() | ||
57 | .and_then(|t| match t { | ||
58 | ast::Type::PathType(path) => Some(path), | ||
59 | _ => None, | ||
60 | })? | ||
61 | .path()?; | ||
62 | |||
63 | let ranks = compute_method_ranks(&path, ctx)?; | ||
64 | let sorted: Vec<_> = methods | ||
65 | .iter() | ||
66 | .cloned() | ||
67 | .sorted_by_key(|f| { | ||
68 | f.name().and_then(|n| ranks.get(&n.to_string()).copied()).unwrap_or(usize::max_value()) | ||
69 | }) | ||
70 | .collect(); | ||
71 | |||
72 | // Don't edit already sorted methods: | ||
73 | if methods == sorted { | ||
74 | mark::hit!(not_applicable_if_sorted); | ||
75 | return None; | ||
76 | } | ||
77 | |||
78 | let target = items.syntax().text_range(); | ||
79 | acc.add(AssistId("reorder_impl", AssistKind::RefactorRewrite), "Sort methods", target, |edit| { | ||
80 | let mut rewriter = algo::SyntaxRewriter::default(); | ||
81 | for (old, new) in methods.iter().zip(&sorted) { | ||
82 | rewriter.replace(old.syntax(), new.syntax()); | ||
83 | } | ||
84 | edit.rewrite(rewriter); | ||
85 | }) | ||
86 | } | ||
87 | |||
88 | fn compute_method_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashMap<String, usize>> { | ||
89 | let td = trait_definition(path, &ctx.sema)?; | ||
90 | |||
91 | Some( | ||
92 | td.items(ctx.db()) | ||
93 | .iter() | ||
94 | .flat_map(|i| match i { | ||
95 | hir::AssocItem::Function(f) => Some(f), | ||
96 | _ => None, | ||
97 | }) | ||
98 | .enumerate() | ||
99 | .map(|(idx, func)| ((func.name(ctx.db()).to_string(), idx))) | ||
100 | .collect(), | ||
101 | ) | ||
102 | } | ||
103 | |||
104 | fn trait_definition(path: &ast::Path, sema: &Semantics<RootDatabase>) -> Option<hir::Trait> { | ||
105 | match sema.resolve_path(path)? { | ||
106 | PathResolution::Def(hir::ModuleDef::Trait(trait_)) => Some(trait_), | ||
107 | _ => None, | ||
108 | } | ||
109 | } | ||
110 | |||
111 | fn get_methods(items: &ast::AssocItemList) -> Vec<ast::Fn> { | ||
112 | items | ||
113 | .assoc_items() | ||
114 | .flat_map(|i| match i { | ||
115 | ast::AssocItem::Fn(f) => Some(f), | ||
116 | _ => None, | ||
117 | }) | ||
118 | .filter(|f| f.name().is_some()) | ||
119 | .collect() | ||
120 | } | ||
121 | |||
122 | #[cfg(test)] | ||
123 | mod tests { | ||
124 | use test_utils::mark; | ||
125 | |||
126 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
127 | |||
128 | use super::*; | ||
129 | |||
130 | #[test] | ||
131 | fn not_applicable_if_sorted() { | ||
132 | mark::check!(not_applicable_if_sorted); | ||
133 | check_assist_not_applicable( | ||
134 | reorder_impl, | ||
135 | r#" | ||
136 | trait Bar { | ||
137 | fn a() {} | ||
138 | fn z() {} | ||
139 | fn b() {} | ||
140 | } | ||
141 | struct Foo; | ||
142 | $0impl Bar for Foo { | ||
143 | fn a() {} | ||
144 | fn z() {} | ||
145 | fn b() {} | ||
146 | } | ||
147 | "#, | ||
148 | ) | ||
149 | } | ||
150 | |||
151 | #[test] | ||
152 | fn not_applicable_if_empty() { | ||
153 | check_assist_not_applicable( | ||
154 | reorder_impl, | ||
155 | r#" | ||
156 | trait Bar {}; | ||
157 | struct Foo; | ||
158 | $0impl Bar for Foo {} | ||
159 | "#, | ||
160 | ) | ||
161 | } | ||
162 | |||
163 | #[test] | ||
164 | fn reorder_impl_trait_methods() { | ||
165 | check_assist( | ||
166 | reorder_impl, | ||
167 | r#" | ||
168 | trait Bar { | ||
169 | fn a() {} | ||
170 | fn c() {} | ||
171 | fn b() {} | ||
172 | fn d() {} | ||
173 | } | ||
174 | |||
175 | struct Foo; | ||
176 | $0impl Bar for Foo { | ||
177 | fn d() {} | ||
178 | fn b() {} | ||
179 | fn c() {} | ||
180 | fn a() {} | ||
181 | } | ||
182 | "#, | ||
183 | r#" | ||
184 | trait Bar { | ||
185 | fn a() {} | ||
186 | fn c() {} | ||
187 | fn b() {} | ||
188 | fn d() {} | ||
189 | } | ||
190 | |||
191 | struct Foo; | ||
192 | impl Bar for Foo { | ||
193 | fn a() {} | ||
194 | fn c() {} | ||
195 | fn b() {} | ||
196 | fn d() {} | ||
197 | } | ||
198 | "#, | ||
199 | ) | ||
200 | } | ||
201 | } | ||
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs index 90009c55a..1080294ab 100644 --- a/crates/assists/src/lib.rs +++ b/crates/assists/src/lib.rs | |||
@@ -146,6 +146,7 @@ mod handlers { | |||
146 | mod remove_mut; | 146 | mod remove_mut; |
147 | mod remove_unused_param; | 147 | mod remove_unused_param; |
148 | mod reorder_fields; | 148 | mod reorder_fields; |
149 | mod reorder_impl; | ||
149 | mod replace_derive_with_manual_impl; | 150 | mod replace_derive_with_manual_impl; |
150 | mod replace_if_let_with_match; | 151 | mod replace_if_let_with_match; |
151 | mod replace_impl_trait_with_generic; | 152 | mod replace_impl_trait_with_generic; |
@@ -202,6 +203,7 @@ mod handlers { | |||
202 | remove_mut::remove_mut, | 203 | remove_mut::remove_mut, |
203 | remove_unused_param::remove_unused_param, | 204 | remove_unused_param::remove_unused_param, |
204 | reorder_fields::reorder_fields, | 205 | reorder_fields::reorder_fields, |
206 | reorder_impl::reorder_impl, | ||
205 | replace_derive_with_manual_impl::replace_derive_with_manual_impl, | 207 | replace_derive_with_manual_impl::replace_derive_with_manual_impl, |
206 | replace_if_let_with_match::replace_if_let_with_match, | 208 | replace_if_let_with_match::replace_if_let_with_match, |
207 | replace_if_let_with_match::replace_match_with_if_let, | 209 | replace_if_let_with_match::replace_match_with_if_let, |
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs index e28837b53..217f577eb 100644 --- a/crates/assists/src/tests/generated.rs +++ b/crates/assists/src/tests/generated.rs | |||
@@ -896,6 +896,41 @@ const test: Foo = Foo {foo: 1, bar: 0} | |||
896 | } | 896 | } |
897 | 897 | ||
898 | #[test] | 898 | #[test] |
899 | fn doctest_reorder_impl() { | ||
900 | check_doc_test( | ||
901 | "reorder_impl", | ||
902 | r#####" | ||
903 | trait Foo { | ||
904 | fn a() {} | ||
905 | fn b() {} | ||
906 | fn c() {} | ||
907 | } | ||
908 | |||
909 | struct Bar; | ||
910 | $0impl Foo for Bar { | ||
911 | fn b() {} | ||
912 | fn c() {} | ||
913 | fn a() {} | ||
914 | } | ||
915 | "#####, | ||
916 | r#####" | ||
917 | trait Foo { | ||
918 | fn a() {} | ||
919 | fn b() {} | ||
920 | fn c() {} | ||
921 | } | ||
922 | |||
923 | struct Bar; | ||
924 | impl Foo for Bar { | ||
925 | fn a() {} | ||
926 | fn b() {} | ||
927 | fn c() {} | ||
928 | } | ||
929 | "#####, | ||
930 | ) | ||
931 | } | ||
932 | |||
933 | #[test] | ||
899 | fn doctest_replace_derive_with_manual_impl() { | 934 | fn doctest_replace_derive_with_manual_impl() { |
900 | check_doc_test( | 935 | check_doc_test( |
901 | "replace_derive_with_manual_impl", | 936 | "replace_derive_with_manual_impl", |
diff --git a/crates/completion/src/completions/attribute.rs b/crates/completion/src/completions/attribute.rs index 3a29b5203..e5522980d 100644 --- a/crates/completion/src/completions/attribute.rs +++ b/crates/completion/src/completions/attribute.rs | |||
@@ -5,7 +5,7 @@ | |||
5 | 5 | ||
6 | use itertools::Itertools; | 6 | use itertools::Itertools; |
7 | use rustc_hash::FxHashSet; | 7 | use rustc_hash::FxHashSet; |
8 | use syntax::{ast, AstNode, SyntaxKind}; | 8 | use syntax::{ast, AstNode, T}; |
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{ |
11 | context::CompletionContext, | 11 | context::CompletionContext, |
@@ -21,15 +21,17 @@ pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) | |||
21 | 21 | ||
22 | let attribute = ctx.attribute_under_caret.as_ref()?; | 22 | let attribute = ctx.attribute_under_caret.as_ref()?; |
23 | match (attribute.path(), attribute.token_tree()) { | 23 | match (attribute.path(), attribute.token_tree()) { |
24 | (Some(path), Some(token_tree)) => match path.to_string().as_str() { | 24 | (Some(path), Some(token_tree)) => { |
25 | "derive" => complete_derive(acc, ctx, token_tree), | 25 | let path = path.syntax().text(); |
26 | "feature" => complete_lint(acc, ctx, token_tree, FEATURES), | 26 | if path == "derive" { |
27 | "allow" | "warn" | "deny" | "forbid" => { | 27 | complete_derive(acc, ctx, token_tree) |
28 | } else if path == "feature" { | ||
29 | complete_lint(acc, ctx, token_tree, FEATURES) | ||
30 | } else if path == "allow" || path == "warn" || path == "deny" || path == "forbid" { | ||
28 | complete_lint(acc, ctx, token_tree.clone(), DEFAULT_LINT_COMPLETIONS); | 31 | complete_lint(acc, ctx, token_tree.clone(), DEFAULT_LINT_COMPLETIONS); |
29 | complete_lint(acc, ctx, token_tree, CLIPPY_LINTS); | 32 | complete_lint(acc, ctx, token_tree, CLIPPY_LINTS); |
30 | } | 33 | } |
31 | _ => {} | 34 | } |
32 | }, | ||
33 | (_, Some(_token_tree)) => {} | 35 | (_, Some(_token_tree)) => {} |
34 | _ => complete_attribute_start(acc, ctx, attribute), | 36 | _ => complete_attribute_start(acc, ctx, attribute), |
35 | } | 37 | } |
@@ -203,8 +205,7 @@ fn complete_lint( | |||
203 | fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> { | 205 | fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> { |
204 | match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) { | 206 | match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) { |
205 | (Some(left_paren), Some(right_paren)) | 207 | (Some(left_paren), Some(right_paren)) |
206 | if left_paren.kind() == SyntaxKind::L_PAREN | 208 | if left_paren.kind() == T!['('] && right_paren.kind() == T![')'] => |
207 | && right_paren.kind() == SyntaxKind::R_PAREN => | ||
208 | { | 209 | { |
209 | let mut input_derives = FxHashSet::default(); | 210 | let mut input_derives = FxHashSet::default(); |
210 | let mut current_derive = String::new(); | 211 | let mut current_derive = String::new(); |
@@ -216,7 +217,7 @@ fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<Strin | |||
216 | .skip(1) | 217 | .skip(1) |
217 | .take_while(|token| token != &right_paren) | 218 | .take_while(|token| token != &right_paren) |
218 | { | 219 | { |
219 | if SyntaxKind::COMMA == token.kind() { | 220 | if T![,] == token.kind() { |
220 | if !current_derive.is_empty() { | 221 | if !current_derive.is_empty() { |
221 | input_derives.insert(current_derive); | 222 | input_derives.insert(current_derive); |
222 | current_derive = String::new(); | 223 | current_derive = String::new(); |
diff --git a/crates/completion/src/completions/dot.rs b/crates/completion/src/completions/dot.rs index 2e25c8ba2..d04eef65a 100644 --- a/crates/completion/src/completions/dot.rs +++ b/crates/completion/src/completions/dot.rs | |||
@@ -373,20 +373,20 @@ fn foo(a: A) { | |||
373 | fn macro_expansion_resilient() { | 373 | fn macro_expansion_resilient() { |
374 | check( | 374 | check( |
375 | r#" | 375 | r#" |
376 | macro_rules! dbg { | 376 | macro_rules! d { |
377 | () => {}; | 377 | () => {}; |
378 | ($val:expr) => { | 378 | ($val:expr) => { |
379 | match $val { tmp => { tmp } } | 379 | match $val { tmp => { tmp } } |
380 | }; | 380 | }; |
381 | // Trailing comma with single argument is ignored | 381 | // Trailing comma with single argument is ignored |
382 | ($val:expr,) => { $crate::dbg!($val) }; | 382 | ($val:expr,) => { $crate::d!($val) }; |
383 | ($($val:expr),+ $(,)?) => { | 383 | ($($val:expr),+ $(,)?) => { |
384 | ($($crate::dbg!($val)),+,) | 384 | ($($crate::d!($val)),+,) |
385 | }; | 385 | }; |
386 | } | 386 | } |
387 | struct A { the_field: u32 } | 387 | struct A { the_field: u32 } |
388 | fn foo(a: A) { | 388 | fn foo(a: A) { |
389 | dbg!(a.$0) | 389 | d!(a.$0) |
390 | } | 390 | } |
391 | "#, | 391 | "#, |
392 | expect![[r#" | 392 | expect![[r#" |
diff --git a/crates/completion/src/completions/postfix.rs b/crates/completion/src/completions/postfix.rs index 87f0c0b2a..9c34ed0b6 100644 --- a/crates/completion/src/completions/postfix.rs +++ b/crates/completion/src/completions/postfix.rs | |||
@@ -35,11 +35,14 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
35 | None => return, | 35 | None => return, |
36 | }; | 36 | }; |
37 | 37 | ||
38 | let ref_removed_ty = | ||
39 | std::iter::successors(Some(receiver_ty.clone()), |ty| ty.remove_ref()).last().unwrap(); | ||
40 | |||
38 | let cap = match ctx.config.snippet_cap { | 41 | let cap = match ctx.config.snippet_cap { |
39 | Some(it) => it, | 42 | Some(it) => it, |
40 | None => return, | 43 | None => return, |
41 | }; | 44 | }; |
42 | let try_enum = TryEnum::from_ty(&ctx.sema, &receiver_ty); | 45 | let try_enum = TryEnum::from_ty(&ctx.sema, &ref_removed_ty); |
43 | if let Some(try_enum) = &try_enum { | 46 | if let Some(try_enum) = &try_enum { |
44 | match try_enum { | 47 | match try_enum { |
45 | TryEnum::Result => { | 48 | TryEnum::Result => { |
@@ -496,6 +499,27 @@ fn main() { | |||
496 | fn postfix_completion_for_references() { | 499 | fn postfix_completion_for_references() { |
497 | check_edit("dbg", r#"fn main() { &&42.$0 }"#, r#"fn main() { dbg!(&&42) }"#); | 500 | check_edit("dbg", r#"fn main() { &&42.$0 }"#, r#"fn main() { dbg!(&&42) }"#); |
498 | check_edit("refm", r#"fn main() { &&42.$0 }"#, r#"fn main() { &&&mut 42 }"#); | 501 | check_edit("refm", r#"fn main() { &&42.$0 }"#, r#"fn main() { &&&mut 42 }"#); |
502 | check_edit( | ||
503 | "ifl", | ||
504 | r#" | ||
505 | enum Option<T> { Some(T), None } | ||
506 | |||
507 | fn main() { | ||
508 | let bar = &Option::Some(true); | ||
509 | bar.$0 | ||
510 | } | ||
511 | "#, | ||
512 | r#" | ||
513 | enum Option<T> { Some(T), None } | ||
514 | |||
515 | fn main() { | ||
516 | let bar = &Option::Some(true); | ||
517 | if let Some($1) = bar { | ||
518 | $0 | ||
519 | } | ||
520 | } | ||
521 | "#, | ||
522 | ) | ||
499 | } | 523 | } |
500 | 524 | ||
501 | #[test] | 525 | #[test] |
diff --git a/crates/completion/src/completions/trait_impl.rs b/crates/completion/src/completions/trait_impl.rs index aa9c845da..135ae49dc 100644 --- a/crates/completion/src/completions/trait_impl.rs +++ b/crates/completion/src/completions/trait_impl.rs | |||
@@ -93,11 +93,11 @@ fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, Synt | |||
93 | // `impl .. { const $0 }` | 93 | // `impl .. { const $0 }` |
94 | // ERROR 0 | 94 | // ERROR 0 |
95 | // CONST_KW <- * | 95 | // CONST_KW <- * |
96 | SyntaxKind::CONST_KW => 0, | 96 | T![const] => 0, |
97 | // `impl .. { fn/type $0 }` | 97 | // `impl .. { fn/type $0 }` |
98 | // FN/TYPE_ALIAS 0 | 98 | // FN/TYPE_ALIAS 0 |
99 | // FN_KW <- * | 99 | // FN_KW <- * |
100 | SyntaxKind::FN_KW | SyntaxKind::TYPE_KW => 0, | 100 | T![fn] | T![type] => 0, |
101 | // `impl .. { fn/type/const foo$0 }` | 101 | // `impl .. { fn/type/const foo$0 }` |
102 | // FN/TYPE_ALIAS/CONST 1 | 102 | // FN/TYPE_ALIAS/CONST 1 |
103 | // NAME 0 | 103 | // NAME 0 |
@@ -121,7 +121,7 @@ fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, Synt | |||
121 | let impl_def = ast::Impl::cast(impl_item.parent()?.parent()?)?; | 121 | let impl_def = ast::Impl::cast(impl_item.parent()?.parent()?)?; |
122 | let kind = match impl_item.kind() { | 122 | let kind = match impl_item.kind() { |
123 | // `impl ... { const $0 fn/type/const }` | 123 | // `impl ... { const $0 fn/type/const }` |
124 | _ if token.kind() == SyntaxKind::CONST_KW => ImplCompletionKind::Const, | 124 | _ if token.kind() == T![const] => ImplCompletionKind::Const, |
125 | SyntaxKind::CONST | SyntaxKind::ERROR => ImplCompletionKind::Const, | 125 | SyntaxKind::CONST | SyntaxKind::ERROR => ImplCompletionKind::Const, |
126 | SyntaxKind::TYPE_ALIAS => ImplCompletionKind::TypeAlias, | 126 | SyntaxKind::TYPE_ALIAS => ImplCompletionKind::TypeAlias, |
127 | SyntaxKind::FN => ImplCompletionKind::Fn, | 127 | SyntaxKind::FN => ImplCompletionKind::Fn, |
diff --git a/crates/completion/src/patterns.rs b/crates/completion/src/patterns.rs index f148b9402..f3ce91dd1 100644 --- a/crates/completion/src/patterns.rs +++ b/crates/completion/src/patterns.rs | |||
@@ -5,7 +5,7 @@ use syntax::{ | |||
5 | ast::{self, LoopBodyOwner}, | 5 | ast::{self, LoopBodyOwner}, |
6 | match_ast, AstNode, Direction, NodeOrToken, SyntaxElement, | 6 | match_ast, AstNode, Direction, NodeOrToken, SyntaxElement, |
7 | SyntaxKind::*, | 7 | SyntaxKind::*, |
8 | SyntaxNode, SyntaxToken, | 8 | SyntaxNode, SyntaxToken, T, |
9 | }; | 9 | }; |
10 | 10 | ||
11 | #[cfg(test)] | 11 | #[cfg(test)] |
@@ -119,7 +119,7 @@ pub(crate) fn unsafe_is_prev(element: SyntaxElement) -> bool { | |||
119 | element | 119 | element |
120 | .into_token() | 120 | .into_token() |
121 | .and_then(|it| previous_non_trivia_token(it)) | 121 | .and_then(|it| previous_non_trivia_token(it)) |
122 | .filter(|it| it.kind() == UNSAFE_KW) | 122 | .filter(|it| it.kind() == T![unsafe]) |
123 | .is_some() | 123 | .is_some() |
124 | } | 124 | } |
125 | #[test] | 125 | #[test] |
@@ -131,7 +131,7 @@ pub(crate) fn if_is_prev(element: SyntaxElement) -> bool { | |||
131 | element | 131 | element |
132 | .into_token() | 132 | .into_token() |
133 | .and_then(|it| previous_non_trivia_token(it)) | 133 | .and_then(|it| previous_non_trivia_token(it)) |
134 | .filter(|it| it.kind() == IF_KW) | 134 | .filter(|it| it.kind() == T![if]) |
135 | .is_some() | 135 | .is_some() |
136 | } | 136 | } |
137 | 137 | ||
@@ -139,7 +139,7 @@ pub(crate) fn fn_is_prev(element: SyntaxElement) -> bool { | |||
139 | element | 139 | element |
140 | .into_token() | 140 | .into_token() |
141 | .and_then(|it| previous_non_trivia_token(it)) | 141 | .and_then(|it| previous_non_trivia_token(it)) |
142 | .filter(|it| it.kind() == FN_KW) | 142 | .filter(|it| it.kind() == T![fn]) |
143 | .is_some() | 143 | .is_some() |
144 | } | 144 | } |
145 | #[test] | 145 | #[test] |
@@ -154,7 +154,7 @@ pub(crate) fn for_is_prev2(element: SyntaxElement) -> bool { | |||
154 | .into_token() | 154 | .into_token() |
155 | .and_then(|it| previous_non_trivia_token(it)) | 155 | .and_then(|it| previous_non_trivia_token(it)) |
156 | .and_then(|it| previous_non_trivia_token(it)) | 156 | .and_then(|it| previous_non_trivia_token(it)) |
157 | .filter(|it| it.kind() == FOR_KW) | 157 | .filter(|it| it.kind() == T![for]) |
158 | .is_some() | 158 | .is_some() |
159 | } | 159 | } |
160 | #[test] | 160 | #[test] |
diff --git a/crates/flycheck/Cargo.toml b/crates/flycheck/Cargo.toml index 1bad64a1b..1d19c7886 100644 --- a/crates/flycheck/Cargo.toml +++ b/crates/flycheck/Cargo.toml | |||
@@ -12,7 +12,7 @@ doctest = false | |||
12 | [dependencies] | 12 | [dependencies] |
13 | crossbeam-channel = "0.5.0" | 13 | crossbeam-channel = "0.5.0" |
14 | log = "0.4.8" | 14 | log = "0.4.8" |
15 | cargo_metadata = "=0.12.0" | 15 | cargo_metadata = "0.12.2" |
16 | serde_json = "1.0.48" | 16 | serde_json = "1.0.48" |
17 | jod-thread = "0.1.1" | 17 | jod-thread = "0.1.1" |
18 | 18 | ||
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs index cc1938333..6cbf5cecf 100644 --- a/crates/hir/src/code_model.rs +++ b/crates/hir/src/code_model.rs | |||
@@ -1263,6 +1263,24 @@ pub enum GenericParam { | |||
1263 | } | 1263 | } |
1264 | impl_from!(TypeParam, LifetimeParam, ConstParam for GenericParam); | 1264 | impl_from!(TypeParam, LifetimeParam, ConstParam for GenericParam); |
1265 | 1265 | ||
1266 | impl GenericParam { | ||
1267 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1268 | match self { | ||
1269 | GenericParam::TypeParam(it) => it.module(db), | ||
1270 | GenericParam::LifetimeParam(it) => it.module(db), | ||
1271 | GenericParam::ConstParam(it) => it.module(db), | ||
1272 | } | ||
1273 | } | ||
1274 | |||
1275 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
1276 | match self { | ||
1277 | GenericParam::TypeParam(it) => it.name(db), | ||
1278 | GenericParam::LifetimeParam(it) => it.name(db), | ||
1279 | GenericParam::ConstParam(it) => it.name(db), | ||
1280 | } | ||
1281 | } | ||
1282 | } | ||
1283 | |||
1266 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | 1284 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
1267 | pub struct TypeParam { | 1285 | pub struct TypeParam { |
1268 | pub(crate) id: TypeParamId, | 1286 | pub(crate) id: TypeParamId, |
diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs index 3e47a5e9d..c8c5fecd7 100644 --- a/crates/hir/src/from_id.rs +++ b/crates/hir/src/from_id.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! Utility module for converting between hir_def ids and code_model wrappers. | 1 | //! Utility module for converting between hir_def ids and code_model wrappers. |
2 | //! | 2 | //! |
3 | //! It's unclear if we need this long-term, but it's definitelly useful while we | 3 | //! It's unclear if we need this long-term, but it's definitely useful while we |
4 | //! are splitting the hir. | 4 | //! are splitting the hir. |
5 | 5 | ||
6 | use hir_def::{ | 6 | use hir_def::{ |
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 6be1eaade..3dc33f248 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs | |||
@@ -581,7 +581,7 @@ impl ExprCollector<'_> { | |||
581 | match res.value { | 581 | match res.value { |
582 | Some((mark, expansion)) => { | 582 | Some((mark, expansion)) => { |
583 | // FIXME: Statements are too complicated to recover from error for now. | 583 | // FIXME: Statements are too complicated to recover from error for now. |
584 | // It is because we don't have any hygenine for local variable expansion right now. | 584 | // It is because we don't have any hygiene for local variable expansion right now. |
585 | if T::can_cast(syntax::SyntaxKind::MACRO_STMTS) && res.err.is_some() { | 585 | if T::can_cast(syntax::SyntaxKind::MACRO_STMTS) && res.err.is_some() { |
586 | self.expander.exit(self.db, mark); | 586 | self.expander.exit(self.db, mark); |
587 | collector(self, None); | 587 | collector(self, None); |
@@ -959,7 +959,7 @@ impl ExprCollector<'_> { | |||
959 | 959 | ||
960 | fn collect_tuple_pat(&mut self, args: AstChildren<ast::Pat>) -> (Vec<PatId>, Option<usize>) { | 960 | fn collect_tuple_pat(&mut self, args: AstChildren<ast::Pat>) -> (Vec<PatId>, Option<usize>) { |
961 | // Find the location of the `..`, if there is one. Note that we do not | 961 | // Find the location of the `..`, if there is one. Note that we do not |
962 | // consider the possiblity of there being multiple `..` here. | 962 | // consider the possibility of there being multiple `..` here. |
963 | let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::RestPat(_))); | 963 | let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::RestPat(_))); |
964 | // We want to skip the `..` pattern here, since we account for it above. | 964 | // We want to skip the `..` pattern here, since we account for it above. |
965 | let args = args | 965 | let args = args |
diff --git a/crates/hir_def/src/expr.rs b/crates/hir_def/src/expr.rs index 6a481769d..76f5721e5 100644 --- a/crates/hir_def/src/expr.rs +++ b/crates/hir_def/src/expr.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! This module describes hir-level representation of expressions. | 1 | //! This module describes hir-level representation of expressions. |
2 | //! | 2 | //! |
3 | //! This representaion is: | 3 | //! This representation is: |
4 | //! | 4 | //! |
5 | //! 1. Identity-based. Each expression has an `id`, so we can distinguish | 5 | //! 1. Identity-based. Each expression has an `id`, so we can distinguish |
6 | //! between different `1` in `1 + 1`. | 6 | //! between different `1` in `1 + 1`. |
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 77017e4ea..f027fd48d 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -267,7 +267,7 @@ impl DefCollector<'_> { | |||
267 | 267 | ||
268 | // Resolve all indeterminate resolved imports again | 268 | // Resolve all indeterminate resolved imports again |
269 | // As some of the macros will expand newly import shadowing partial resolved imports | 269 | // As some of the macros will expand newly import shadowing partial resolved imports |
270 | // FIXME: We maybe could skip this, if we handle the Indetermine imports in `resolve_imports` | 270 | // FIXME: We maybe could skip this, if we handle the indeterminate imports in `resolve_imports` |
271 | // correctly | 271 | // correctly |
272 | let partial_resolved = self.resolved_imports.iter().filter_map(|directive| { | 272 | let partial_resolved = self.resolved_imports.iter().filter_map(|directive| { |
273 | if let PartialResolvedImport::Indeterminate(_) = directive.status { | 273 | if let PartialResolvedImport::Indeterminate(_) = directive.status { |
@@ -402,7 +402,7 @@ impl DefCollector<'_> { | |||
402 | 402 | ||
403 | /// Define a proc macro | 403 | /// Define a proc macro |
404 | /// | 404 | /// |
405 | /// A proc macro is similar to normal macro scope, but it would not visiable in legacy textual scoped. | 405 | /// A proc macro is similar to normal macro scope, but it would not visible in legacy textual scoped. |
406 | /// And unconditionally exported. | 406 | /// And unconditionally exported. |
407 | fn define_proc_macro(&mut self, name: Name, macro_: MacroDefId) { | 407 | fn define_proc_macro(&mut self, name: Name, macro_: MacroDefId) { |
408 | self.update( | 408 | self.update( |
@@ -592,7 +592,7 @@ impl DefCollector<'_> { | |||
592 | // XXX: urgh, so this works by accident! Here, we look at | 592 | // XXX: urgh, so this works by accident! Here, we look at |
593 | // the enum data, and, in theory, this might require us to | 593 | // the enum data, and, in theory, this might require us to |
594 | // look back at the crate_def_map, creating a cycle. For | 594 | // look back at the crate_def_map, creating a cycle. For |
595 | // example, `enum E { crate::some_macro!(); }`. Luckely, the | 595 | // example, `enum E { crate::some_macro!(); }`. Luckily, the |
596 | // only kind of macro that is allowed inside enum is a | 596 | // only kind of macro that is allowed inside enum is a |
597 | // `cfg_macro`, and we don't need to run name resolution for | 597 | // `cfg_macro`, and we don't need to run name resolution for |
598 | // it, but this is sheer luck! | 598 | // it, but this is sheer luck! |
@@ -655,7 +655,7 @@ impl DefCollector<'_> { | |||
655 | &mut self, | 655 | &mut self, |
656 | module_id: LocalModuleId, | 656 | module_id: LocalModuleId, |
657 | resolutions: &[(Option<Name>, PerNs)], | 657 | resolutions: &[(Option<Name>, PerNs)], |
658 | // All resolutions are imported with this visibility; the visibilies in | 658 | // All resolutions are imported with this visibility; the visibilities in |
659 | // the `PerNs` values are ignored and overwritten | 659 | // the `PerNs` values are ignored and overwritten |
660 | vis: Visibility, | 660 | vis: Visibility, |
661 | import_type: ImportType, | 661 | import_type: ImportType, |
diff --git a/crates/hir_def/src/per_ns.rs b/crates/hir_def/src/per_ns.rs index 74665c588..a594afce6 100644 --- a/crates/hir_def/src/per_ns.rs +++ b/crates/hir_def/src/per_ns.rs | |||
@@ -7,7 +7,7 @@ use hir_expand::MacroDefId; | |||
7 | 7 | ||
8 | use crate::{item_scope::ItemInNs, visibility::Visibility, ModuleDefId}; | 8 | use crate::{item_scope::ItemInNs, visibility::Visibility, ModuleDefId}; |
9 | 9 | ||
10 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | 10 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
11 | pub struct PerNs { | 11 | pub struct PerNs { |
12 | pub types: Option<(ModuleDefId, Visibility)>, | 12 | pub types: Option<(ModuleDefId, Visibility)>, |
13 | pub values: Option<(ModuleDefId, Visibility)>, | 13 | pub values: Option<(ModuleDefId, Visibility)>, |
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs index 129f1dbac..61059c349 100644 --- a/crates/hir_def/src/resolver.rs +++ b/crates/hir_def/src/resolver.rs | |||
@@ -27,7 +27,7 @@ use crate::{ | |||
27 | 27 | ||
28 | #[derive(Debug, Clone, Default)] | 28 | #[derive(Debug, Clone, Default)] |
29 | pub struct Resolver { | 29 | pub struct Resolver { |
30 | // FIXME: all usages generally call `.rev`, so maybe reverse once in consturciton? | 30 | // FIXME: all usages generally call `.rev`, so maybe reverse once in construction? |
31 | scopes: Vec<Scope>, | 31 | scopes: Vec<Scope>, |
32 | } | 32 | } |
33 | 33 | ||
@@ -490,6 +490,7 @@ pub enum ScopeDef { | |||
490 | 490 | ||
491 | impl Scope { | 491 | impl Scope { |
492 | fn process_names(&self, db: &dyn DefDatabase, f: &mut dyn FnMut(Name, ScopeDef)) { | 492 | fn process_names(&self, db: &dyn DefDatabase, f: &mut dyn FnMut(Name, ScopeDef)) { |
493 | let mut seen = FxHashSet::default(); | ||
493 | match self { | 494 | match self { |
494 | Scope::ModuleScope(m) => { | 495 | Scope::ModuleScope(m) => { |
495 | // FIXME: should we provide `self` here? | 496 | // FIXME: should we provide `self` here? |
@@ -503,7 +504,9 @@ impl Scope { | |||
503 | f(name.clone(), ScopeDef::PerNs(def)); | 504 | f(name.clone(), ScopeDef::PerNs(def)); |
504 | }); | 505 | }); |
505 | m.crate_def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| { | 506 | m.crate_def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| { |
506 | f(name.clone(), ScopeDef::PerNs(PerNs::macros(macro_, Visibility::Public))); | 507 | let scope = PerNs::macros(macro_, Visibility::Public); |
508 | seen.insert((name.clone(), scope)); | ||
509 | f(name.clone(), ScopeDef::PerNs(scope)); | ||
507 | }); | 510 | }); |
508 | m.crate_def_map.extern_prelude.iter().for_each(|(name, &def)| { | 511 | m.crate_def_map.extern_prelude.iter().for_each(|(name, &def)| { |
509 | f(name.clone(), ScopeDef::PerNs(PerNs::types(def, Visibility::Public))); | 512 | f(name.clone(), ScopeDef::PerNs(PerNs::types(def, Visibility::Public))); |
@@ -514,7 +517,10 @@ impl Scope { | |||
514 | if let Some(prelude) = m.crate_def_map.prelude { | 517 | if let Some(prelude) = m.crate_def_map.prelude { |
515 | let prelude_def_map = db.crate_def_map(prelude.krate); | 518 | let prelude_def_map = db.crate_def_map(prelude.krate); |
516 | prelude_def_map[prelude.local_id].scope.entries().for_each(|(name, def)| { | 519 | prelude_def_map[prelude.local_id].scope.entries().for_each(|(name, def)| { |
517 | f(name.clone(), ScopeDef::PerNs(def)); | 520 | let seen_tuple = (name.clone(), def); |
521 | if !seen.contains(&seen_tuple) { | ||
522 | f(seen_tuple.0, ScopeDef::PerNs(def)); | ||
523 | } | ||
518 | }); | 524 | }); |
519 | } | 525 | } |
520 | } | 526 | } |
diff --git a/crates/hir_def/src/visibility.rs b/crates/hir_def/src/visibility.rs index e6e0853a3..f3bc9d680 100644 --- a/crates/hir_def/src/visibility.rs +++ b/crates/hir_def/src/visibility.rs | |||
@@ -85,7 +85,7 @@ impl RawVisibility { | |||
85 | } | 85 | } |
86 | 86 | ||
87 | /// Visibility of an item, with the path resolved. | 87 | /// Visibility of an item, with the path resolved. |
88 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | 88 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
89 | pub enum Visibility { | 89 | pub enum Visibility { |
90 | /// Visibility is restricted to a certain module. | 90 | /// Visibility is restricted to a certain module. |
91 | Module(ModuleId), | 91 | Module(ModuleId), |
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index ab2637b8c..c62086390 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs | |||
@@ -40,7 +40,7 @@ impl TokenExpander { | |||
40 | // FIXME switch these to ExpandResult as well | 40 | // FIXME switch these to ExpandResult as well |
41 | TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), | 41 | TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), |
42 | TokenExpander::ProcMacro(_) => { | 42 | TokenExpander::ProcMacro(_) => { |
43 | // We store the result in salsa db to prevent non-determinisc behavior in | 43 | // We store the result in salsa db to prevent non-deterministic behavior in |
44 | // some proc-macro implementation | 44 | // some proc-macro implementation |
45 | // See #4315 for details | 45 | // See #4315 for details |
46 | db.expand_proc_macro(id.into()).into() | 46 | db.expand_proc_macro(id.into()).into() |
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs index a1c484fdf..107417c27 100644 --- a/crates/hir_ty/src/diagnostics/expr.rs +++ b/crates/hir_ty/src/diagnostics/expr.rs | |||
@@ -379,7 +379,7 @@ pub fn record_literal_missing_fields( | |||
379 | id: ExprId, | 379 | id: ExprId, |
380 | expr: &Expr, | 380 | expr: &Expr, |
381 | ) -> Option<(VariantId, Vec<LocalFieldId>, /*exhaustive*/ bool)> { | 381 | ) -> Option<(VariantId, Vec<LocalFieldId>, /*exhaustive*/ bool)> { |
382 | let (fields, exhausitve) = match expr { | 382 | let (fields, exhaustive) = match expr { |
383 | Expr::RecordLit { path: _, fields, spread } => (fields, spread.is_none()), | 383 | Expr::RecordLit { path: _, fields, spread } => (fields, spread.is_none()), |
384 | _ => return None, | 384 | _ => return None, |
385 | }; | 385 | }; |
@@ -400,7 +400,7 @@ pub fn record_literal_missing_fields( | |||
400 | if missed_fields.is_empty() { | 400 | if missed_fields.is_empty() { |
401 | return None; | 401 | return None; |
402 | } | 402 | } |
403 | Some((variant_def, missed_fields, exhausitve)) | 403 | Some((variant_def, missed_fields, exhaustive)) |
404 | } | 404 | } |
405 | 405 | ||
406 | pub fn record_pattern_missing_fields( | 406 | pub fn record_pattern_missing_fields( |
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs index 62c329731..61c47eec8 100644 --- a/crates/hir_ty/src/diagnostics/match_check.rs +++ b/crates/hir_ty/src/diagnostics/match_check.rs | |||
@@ -14,7 +14,7 @@ | |||
14 | //! The algorithm implemented here is a modified version of the one described in | 14 | //! The algorithm implemented here is a modified version of the one described in |
15 | //! <http://moscova.inria.fr/~maranget/papers/warn/index.html>. | 15 | //! <http://moscova.inria.fr/~maranget/papers/warn/index.html>. |
16 | //! However, to save future implementors from reading the original paper, we | 16 | //! However, to save future implementors from reading the original paper, we |
17 | //! summarise the algorithm here to hopefully save time and be a little clearer | 17 | //! summarize the algorithm here to hopefully save time and be a little clearer |
18 | //! (without being so rigorous). | 18 | //! (without being so rigorous). |
19 | //! | 19 | //! |
20 | //! The core of the algorithm revolves about a "usefulness" check. In particular, we | 20 | //! The core of the algorithm revolves about a "usefulness" check. In particular, we |
@@ -132,7 +132,7 @@ | |||
132 | //! The algorithm is inductive (on the number of columns: i.e., components of tuple patterns). | 132 | //! The algorithm is inductive (on the number of columns: i.e., components of tuple patterns). |
133 | //! That means we're going to check the components from left-to-right, so the algorithm | 133 | //! That means we're going to check the components from left-to-right, so the algorithm |
134 | //! operates principally on the first component of the matrix and new pattern-stack `p`. | 134 | //! operates principally on the first component of the matrix and new pattern-stack `p`. |
135 | //! This algorithm is realised in the `is_useful` function. | 135 | //! This algorithm is realized in the `is_useful` function. |
136 | //! | 136 | //! |
137 | //! Base case (`n = 0`, i.e., an empty tuple pattern): | 137 | //! Base case (`n = 0`, i.e., an empty tuple pattern): |
138 | //! - If `P` already contains an empty pattern (i.e., if the number of patterns `m > 0`), then | 138 | //! - If `P` already contains an empty pattern (i.e., if the number of patterns `m > 0`), then |
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs index e9e949c47..d2f1b4014 100644 --- a/crates/hir_ty/src/display.rs +++ b/crates/hir_ty/src/display.rs | |||
@@ -1,14 +1,15 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use std::fmt; | 3 | use std::{borrow::Cow, fmt}; |
4 | 4 | ||
5 | use crate::{ | 5 | use crate::{ |
6 | db::HirDatabase, utils::generics, ApplicationTy, CallableDefId, FnSig, GenericPredicate, | 6 | db::HirDatabase, utils::generics, ApplicationTy, CallableDefId, FnSig, GenericPredicate, |
7 | Lifetime, Obligation, OpaqueTyId, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, | 7 | Lifetime, Obligation, OpaqueTy, OpaqueTyId, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, |
8 | }; | 8 | }; |
9 | use arrayvec::ArrayVec; | ||
9 | use hir_def::{ | 10 | use hir_def::{ |
10 | find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId, AssocContainerId, | 11 | db::DefDatabase, find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId, |
11 | Lookup, ModuleId, | 12 | AssocContainerId, HasModule, Lookup, ModuleId, TraitId, |
12 | }; | 13 | }; |
13 | use hir_expand::name::Name; | 14 | use hir_expand::name::Name; |
14 | 15 | ||
@@ -257,25 +258,45 @@ impl HirDisplay for ApplicationTy { | |||
257 | t.hir_fmt(f)?; | 258 | t.hir_fmt(f)?; |
258 | write!(f, "; _]")?; | 259 | write!(f, "; _]")?; |
259 | } | 260 | } |
260 | TypeCtor::RawPtr(m) => { | 261 | TypeCtor::RawPtr(m) | TypeCtor::Ref(m) => { |
261 | let t = self.parameters.as_single(); | 262 | let t = self.parameters.as_single(); |
263 | let ty_display = | ||
264 | t.into_displayable(f.db, f.max_size, f.omit_verbose_types, f.display_target); | ||
262 | 265 | ||
263 | write!(f, "*{}", m.as_keyword_for_ptr())?; | 266 | if matches!(self.ctor, TypeCtor::RawPtr(_)) { |
264 | if matches!(t, Ty::Dyn(predicates) if predicates.len() > 1) { | 267 | write!(f, "*{}", m.as_keyword_for_ptr())?; |
265 | write!(f, "(")?; | ||
266 | t.hir_fmt(f)?; | ||
267 | write!(f, ")")?; | ||
268 | } else { | 268 | } else { |
269 | t.hir_fmt(f)?; | 269 | write!(f, "&{}", m.as_keyword_for_ref())?; |
270 | } | ||
271 | |||
272 | let datas; | ||
273 | let predicates = match t { | ||
274 | Ty::Dyn(predicates) if predicates.len() > 1 => { | ||
275 | Cow::Borrowed(predicates.as_ref()) | ||
276 | } | ||
277 | &Ty::Opaque(OpaqueTy { | ||
278 | opaque_ty_id: OpaqueTyId::ReturnTypeImplTrait(func, idx), | ||
279 | ref parameters, | ||
280 | }) => { | ||
281 | datas = | ||
282 | f.db.return_type_impl_traits(func).expect("impl trait id without data"); | ||
283 | let data = (*datas) | ||
284 | .as_ref() | ||
285 | .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); | ||
286 | let bounds = data.subst(parameters); | ||
287 | Cow::Owned(bounds.value) | ||
288 | } | ||
289 | _ => Cow::Borrowed(&[][..]), | ||
290 | }; | ||
291 | |||
292 | if let [GenericPredicate::Implemented(trait_ref), _] = predicates.as_ref() { | ||
293 | let trait_ = trait_ref.trait_; | ||
294 | if fn_traits(f.db.upcast(), trait_).any(|it| it == trait_) { | ||
295 | return write!(f, "{}", ty_display); | ||
296 | } | ||
270 | } | 297 | } |
271 | } | ||
272 | TypeCtor::Ref(m) => { | ||
273 | let t = self.parameters.as_single(); | ||
274 | let ty_display = | ||
275 | t.into_displayable(f.db, f.max_size, f.omit_verbose_types, f.display_target); | ||
276 | 298 | ||
277 | write!(f, "&{}", m.as_keyword_for_ref())?; | 299 | if predicates.len() > 1 { |
278 | if matches!(t, Ty::Dyn(predicates) if predicates.len() > 1) { | ||
279 | write!(f, "(")?; | 300 | write!(f, "(")?; |
280 | write!(f, "{}", ty_display)?; | 301 | write!(f, "{}", ty_display)?; |
281 | write!(f, ")")?; | 302 | write!(f, ")")?; |
@@ -595,6 +616,17 @@ impl HirDisplay for FnSig { | |||
595 | } | 616 | } |
596 | } | 617 | } |
597 | 618 | ||
619 | fn fn_traits(db: &dyn DefDatabase, trait_: TraitId) -> impl Iterator<Item = TraitId> { | ||
620 | let krate = trait_.lookup(db).container.module(db).krate; | ||
621 | let fn_traits = [ | ||
622 | db.lang_item(krate, "fn".into()), | ||
623 | db.lang_item(krate, "fn_mut".into()), | ||
624 | db.lang_item(krate, "fn_once".into()), | ||
625 | ]; | ||
626 | // FIXME: Replace ArrayVec when into_iter is a thing on arrays | ||
627 | ArrayVec::from(fn_traits).into_iter().flatten().flat_map(|it| it.as_trait()) | ||
628 | } | ||
629 | |||
598 | pub fn write_bounds_like_dyn_trait( | 630 | pub fn write_bounds_like_dyn_trait( |
599 | predicates: &[GenericPredicate], | 631 | predicates: &[GenericPredicate], |
600 | f: &mut HirFormatter, | 632 | f: &mut HirFormatter, |
@@ -607,10 +639,15 @@ pub fn write_bounds_like_dyn_trait( | |||
607 | // predicate for that trait). | 639 | // predicate for that trait). |
608 | let mut first = true; | 640 | let mut first = true; |
609 | let mut angle_open = false; | 641 | let mut angle_open = false; |
642 | let mut is_fn_trait = false; | ||
610 | for p in predicates.iter() { | 643 | for p in predicates.iter() { |
611 | match p { | 644 | match p { |
612 | GenericPredicate::Implemented(trait_ref) => { | 645 | GenericPredicate::Implemented(trait_ref) => { |
613 | if angle_open { | 646 | let trait_ = trait_ref.trait_; |
647 | if !is_fn_trait { | ||
648 | is_fn_trait = fn_traits(f.db.upcast(), trait_).any(|it| it == trait_); | ||
649 | } | ||
650 | if !is_fn_trait && angle_open { | ||
614 | write!(f, ">")?; | 651 | write!(f, ">")?; |
615 | angle_open = false; | 652 | angle_open = false; |
616 | } | 653 | } |
@@ -620,14 +657,27 @@ pub fn write_bounds_like_dyn_trait( | |||
620 | // We assume that the self type is $0 (i.e. the | 657 | // We assume that the self type is $0 (i.e. the |
621 | // existential) here, which is the only thing that's | 658 | // existential) here, which is the only thing that's |
622 | // possible in actual Rust, and hence don't print it | 659 | // possible in actual Rust, and hence don't print it |
623 | write!(f, "{}", f.db.trait_data(trait_ref.trait_).name)?; | 660 | write!(f, "{}", f.db.trait_data(trait_).name)?; |
624 | if trait_ref.substs.len() > 1 { | 661 | if let [_, params @ ..] = &*trait_ref.substs.0 { |
625 | write!(f, "<")?; | 662 | if is_fn_trait { |
626 | f.write_joined(&trait_ref.substs[1..], ", ")?; | 663 | if let Some(args) = params.first().and_then(|it| it.as_tuple()) { |
627 | // there might be assoc type bindings, so we leave the angle brackets open | 664 | write!(f, "(")?; |
628 | angle_open = true; | 665 | f.write_joined(&*args.0, ", ")?; |
666 | write!(f, ")")?; | ||
667 | } | ||
668 | } else if !params.is_empty() { | ||
669 | write!(f, "<")?; | ||
670 | f.write_joined(params, ", ")?; | ||
671 | // there might be assoc type bindings, so we leave the angle brackets open | ||
672 | angle_open = true; | ||
673 | } | ||
629 | } | 674 | } |
630 | } | 675 | } |
676 | GenericPredicate::Projection(projection_pred) if is_fn_trait => { | ||
677 | is_fn_trait = false; | ||
678 | write!(f, " -> ")?; | ||
679 | projection_pred.ty.hir_fmt(f)?; | ||
680 | } | ||
631 | GenericPredicate::Projection(projection_pred) => { | 681 | GenericPredicate::Projection(projection_pred) => { |
632 | // in types in actual Rust, these will always come | 682 | // in types in actual Rust, these will always come |
633 | // after the corresponding Implemented predicate | 683 | // after the corresponding Implemented predicate |
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs index 222f61a11..9594cce8b 100644 --- a/crates/hir_ty/src/lower.rs +++ b/crates/hir_ty/src/lower.rs | |||
@@ -491,16 +491,16 @@ impl Ty { | |||
491 | fn from_hir_path_inner( | 491 | fn from_hir_path_inner( |
492 | ctx: &TyLoweringContext<'_>, | 492 | ctx: &TyLoweringContext<'_>, |
493 | segment: PathSegment<'_>, | 493 | segment: PathSegment<'_>, |
494 | typable: TyDefId, | 494 | typeable: TyDefId, |
495 | infer_args: bool, | 495 | infer_args: bool, |
496 | ) -> Ty { | 496 | ) -> Ty { |
497 | let generic_def = match typable { | 497 | let generic_def = match typeable { |
498 | TyDefId::BuiltinType(_) => None, | 498 | TyDefId::BuiltinType(_) => None, |
499 | TyDefId::AdtId(it) => Some(it.into()), | 499 | TyDefId::AdtId(it) => Some(it.into()), |
500 | TyDefId::TypeAliasId(it) => Some(it.into()), | 500 | TyDefId::TypeAliasId(it) => Some(it.into()), |
501 | }; | 501 | }; |
502 | let substs = substs_from_path_segment(ctx, segment, generic_def, infer_args); | 502 | let substs = substs_from_path_segment(ctx, segment, generic_def, infer_args); |
503 | ctx.db.ty(typable).subst(&substs) | 503 | ctx.db.ty(typeable).subst(&substs) |
504 | } | 504 | } |
505 | 505 | ||
506 | /// Collect generic arguments from a path into a `Substs`. See also | 506 | /// Collect generic arguments from a path into a `Substs`. See also |
diff --git a/crates/hir_ty/src/tests/display_source_code.rs b/crates/hir_ty/src/tests/display_source_code.rs index b502135d8..3d29021aa 100644 --- a/crates/hir_ty/src/tests/display_source_code.rs +++ b/crates/hir_ty/src/tests/display_source_code.rs | |||
@@ -39,3 +39,18 @@ fn main() { | |||
39 | "#, | 39 | "#, |
40 | ); | 40 | ); |
41 | } | 41 | } |
42 | |||
43 | #[test] | ||
44 | fn render_raw_ptr_impl_ty() { | ||
45 | check_types_source_code( | ||
46 | r#" | ||
47 | trait Sized {} | ||
48 | trait Unpin {} | ||
49 | fn foo() -> *const (impl Unpin + Sized) { loop {} } | ||
50 | fn main() { | ||
51 | let foo = foo(); | ||
52 | foo; | ||
53 | } //^ *const (impl Unpin + Sized) | ||
54 | "#, | ||
55 | ); | ||
56 | } | ||
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs index 41d097519..e5a3f95a6 100644 --- a/crates/hir_ty/src/tests/traits.rs +++ b/crates/hir_ty/src/tests/traits.rs | |||
@@ -3038,16 +3038,16 @@ fn infer_box_fn_arg() { | |||
3038 | 406..417 '&self.inner': &*mut T | 3038 | 406..417 '&self.inner': &*mut T |
3039 | 407..411 'self': &Box<T> | 3039 | 407..411 'self': &Box<T> |
3040 | 407..417 'self.inner': *mut T | 3040 | 407..417 'self.inner': *mut T |
3041 | 478..575 '{ ...(&s) }': FnOnce::Output<dyn FnOnce<(&Option<i32>,)>, (&Option<i32>,)> | 3041 | 478..575 '{ ...(&s) }': FnOnce::Output<dyn FnOnce(&Option<i32>), (&Option<i32>,)> |
3042 | 488..489 's': Option<i32> | 3042 | 488..489 's': Option<i32> |
3043 | 492..504 'Option::None': Option<i32> | 3043 | 492..504 'Option::None': Option<i32> |
3044 | 514..515 'f': Box<dyn FnOnce<(&Option<i32>,)>> | 3044 | 514..515 'f': Box<dyn FnOnce(&Option<i32>)> |
3045 | 549..562 'box (|ps| {})': Box<|{unknown}| -> ()> | 3045 | 549..562 'box (|ps| {})': Box<|{unknown}| -> ()> |
3046 | 554..561 '|ps| {}': |{unknown}| -> () | 3046 | 554..561 '|ps| {}': |{unknown}| -> () |
3047 | 555..557 'ps': {unknown} | 3047 | 555..557 'ps': {unknown} |
3048 | 559..561 '{}': () | 3048 | 559..561 '{}': () |
3049 | 568..569 'f': Box<dyn FnOnce<(&Option<i32>,)>> | 3049 | 568..569 'f': Box<dyn FnOnce(&Option<i32>)> |
3050 | 568..573 'f(&s)': FnOnce::Output<dyn FnOnce<(&Option<i32>,)>, (&Option<i32>,)> | 3050 | 568..573 'f(&s)': FnOnce::Output<dyn FnOnce(&Option<i32>), (&Option<i32>,)> |
3051 | 570..572 '&s': &Option<i32> | 3051 | 570..572 '&s': &Option<i32> |
3052 | 571..572 's': Option<i32> | 3052 | 571..572 's': Option<i32> |
3053 | "#]], | 3053 | "#]], |
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs index 4d8983cb2..e8999a7f3 100644 --- a/crates/ide/src/call_hierarchy.rs +++ b/crates/ide/src/call_hierarchy.rs | |||
@@ -5,7 +5,7 @@ use indexmap::IndexMap; | |||
5 | use hir::Semantics; | 5 | use hir::Semantics; |
6 | use ide_db::call_info::FnCallNode; | 6 | use ide_db::call_info::FnCallNode; |
7 | use ide_db::RootDatabase; | 7 | use ide_db::RootDatabase; |
8 | use syntax::{ast, match_ast, AstNode, TextRange}; | 8 | use syntax::{ast, AstNode, TextRange}; |
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{ |
11 | display::TryToNav, goto_definition, references, FilePosition, NavigationTarget, RangeInfo, | 11 | display::TryToNav, goto_definition, references, FilePosition, NavigationTarget, RangeInfo, |
@@ -47,28 +47,23 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio | |||
47 | 47 | ||
48 | let mut calls = CallLocations::default(); | 48 | let mut calls = CallLocations::default(); |
49 | 49 | ||
50 | for reference in refs.info.references() { | 50 | for (&file_id, references) in refs.info.references().iter() { |
51 | let file_id = reference.file_range.file_id; | ||
52 | let file = sema.parse(file_id); | 51 | let file = sema.parse(file_id); |
53 | let file = file.syntax(); | 52 | let file = file.syntax(); |
54 | let token = file.token_at_offset(reference.file_range.range.start()).next()?; | 53 | for reference in references { |
55 | let token = sema.descend_into_macros(token); | 54 | let token = file.token_at_offset(reference.range.start()).next()?; |
56 | let syntax = token.parent(); | 55 | let token = sema.descend_into_macros(token); |
57 | 56 | let syntax = token.parent(); | |
58 | // This target is the containing function | 57 | |
59 | if let Some(nav) = syntax.ancestors().find_map(|node| { | 58 | // This target is the containing function |
60 | match_ast! { | 59 | if let Some(nav) = syntax.ancestors().find_map(|node| { |
61 | match node { | 60 | let fn_ = ast::Fn::cast(node)?; |
62 | ast::Fn(it) => { | 61 | let def = sema.to_def(&fn_)?; |
63 | let def = sema.to_def(&it)?; | 62 | def.try_to_nav(sema.db) |
64 | def.try_to_nav(sema.db) | 63 | }) { |
65 | }, | 64 | let relative_range = reference.range; |
66 | _ => None, | 65 | calls.add(&nav, relative_range); |
67 | } | ||
68 | } | 66 | } |
69 | }) { | ||
70 | let relative_range = reference.file_range.range; | ||
71 | calls.add(&nav, relative_range); | ||
72 | } | 67 | } |
73 | } | 68 | } |
74 | 69 | ||
@@ -91,17 +86,12 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio | |||
91 | .filter_map(|node| FnCallNode::with_node_exact(&node)) | 86 | .filter_map(|node| FnCallNode::with_node_exact(&node)) |
92 | .filter_map(|call_node| { | 87 | .filter_map(|call_node| { |
93 | let name_ref = call_node.name_ref()?; | 88 | let name_ref = call_node.name_ref()?; |
94 | 89 | let func_target = match call_node { | |
95 | if let Some(func_target) = match &call_node { | ||
96 | FnCallNode::CallExpr(expr) => { | 90 | FnCallNode::CallExpr(expr) => { |
97 | //FIXME: Type::as_callable is broken | 91 | //FIXME: Type::as_callable is broken |
98 | let callable = sema.type_of_expr(&expr.expr()?)?.as_callable(db)?; | 92 | let callable = sema.type_of_expr(&expr.expr()?)?.as_callable(db)?; |
99 | match callable.kind() { | 93 | match callable.kind() { |
100 | hir::CallableKind::Function(it) => { | 94 | hir::CallableKind::Function(it) => it.try_to_nav(db), |
101 | let fn_def: hir::Function = it.into(); | ||
102 | let nav = fn_def.try_to_nav(db)?; | ||
103 | Some(nav) | ||
104 | } | ||
105 | _ => None, | 95 | _ => None, |
106 | } | 96 | } |
107 | } | 97 | } |
@@ -109,11 +99,8 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio | |||
109 | let function = sema.resolve_method_call(&expr)?; | 99 | let function = sema.resolve_method_call(&expr)?; |
110 | function.try_to_nav(db) | 100 | function.try_to_nav(db) |
111 | } | 101 | } |
112 | } { | 102 | }?; |
113 | Some((func_target, name_ref.syntax().text_range())) | 103 | Some((func_target, name_ref.syntax().text_range())) |
114 | } else { | ||
115 | None | ||
116 | } | ||
117 | }) | 104 | }) |
118 | .for_each(|(nav, range)| calls.add(&nav, range)); | 105 | .for_each(|(nav, range)| calls.add(&nav, range)); |
119 | 106 | ||
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs index e24c78301..4eecae697 100644 --- a/crates/ide/src/display/navigation_target.rs +++ b/crates/ide/src/display/navigation_target.rs | |||
@@ -215,10 +215,8 @@ impl TryToNav for Definition { | |||
215 | Definition::ModuleDef(it) => it.try_to_nav(db), | 215 | Definition::ModuleDef(it) => it.try_to_nav(db), |
216 | Definition::SelfType(it) => it.try_to_nav(db), | 216 | Definition::SelfType(it) => it.try_to_nav(db), |
217 | Definition::Local(it) => Some(it.to_nav(db)), | 217 | Definition::Local(it) => Some(it.to_nav(db)), |
218 | Definition::TypeParam(it) => it.try_to_nav(db), | 218 | Definition::GenericParam(it) => it.try_to_nav(db), |
219 | Definition::LifetimeParam(it) => it.try_to_nav(db), | ||
220 | Definition::Label(it) => Some(it.to_nav(db)), | 219 | Definition::Label(it) => Some(it.to_nav(db)), |
221 | Definition::ConstParam(it) => it.try_to_nav(db), | ||
222 | } | 220 | } |
223 | } | 221 | } |
224 | } | 222 | } |
@@ -389,6 +387,16 @@ impl TryToNav for hir::AssocItem { | |||
389 | } | 387 | } |
390 | } | 388 | } |
391 | 389 | ||
390 | impl TryToNav for hir::GenericParam { | ||
391 | fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { | ||
392 | match self { | ||
393 | hir::GenericParam::TypeParam(it) => it.try_to_nav(db), | ||
394 | hir::GenericParam::ConstParam(it) => it.try_to_nav(db), | ||
395 | hir::GenericParam::LifetimeParam(it) => it.try_to_nav(db), | ||
396 | } | ||
397 | } | ||
398 | } | ||
399 | |||
392 | impl ToNav for hir::Local { | 400 | impl ToNav for hir::Local { |
393 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { | 401 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { |
394 | let src = self.source(db); | 402 | let src = self.source(db); |
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index 1ff818de2..de10406bc 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! Resolves and rewrites links in markdown documentation. | 1 | //! Resolves and rewrites links in markdown documentation. |
2 | 2 | ||
3 | use std::{convert::TryFrom, iter::once}; | 3 | use std::{convert::TryFrom, iter::once, ops::Range}; |
4 | 4 | ||
5 | use itertools::Itertools; | 5 | use itertools::Itertools; |
6 | use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag}; | 6 | use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag}; |
@@ -39,7 +39,7 @@ pub(crate) fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Defi | |||
39 | if target.contains("://") { | 39 | if target.contains("://") { |
40 | (target.to_string(), title.to_string()) | 40 | (target.to_string(), title.to_string()) |
41 | } else { | 41 | } else { |
42 | // Two posibilities: | 42 | // Two possibilities: |
43 | // * path-based links: `../../module/struct.MyStruct.html` | 43 | // * path-based links: `../../module/struct.MyStruct.html` |
44 | // * module-based links (AKA intra-doc links): `super::super::module::MyStruct` | 44 | // * module-based links (AKA intra-doc links): `super::super::module::MyStruct` |
45 | if let Some(rewritten) = rewrite_intra_doc_link(db, *definition, target, title) { | 45 | if let Some(rewritten) = rewrite_intra_doc_link(db, *definition, target, title) { |
@@ -61,6 +61,30 @@ pub(crate) fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Defi | |||
61 | out | 61 | out |
62 | } | 62 | } |
63 | 63 | ||
64 | pub(crate) fn extract_definitions_from_markdown( | ||
65 | markdown: &str, | ||
66 | ) -> Vec<(String, Option<hir::Namespace>, Range<usize>)> { | ||
67 | let mut res = vec![]; | ||
68 | let mut cb = |link: BrokenLink| { | ||
69 | Some(( | ||
70 | /*url*/ link.reference.to_owned().into(), | ||
71 | /*title*/ link.reference.to_owned().into(), | ||
72 | )) | ||
73 | }; | ||
74 | let doc = Parser::new_with_broken_link_callback(markdown, Options::empty(), Some(&mut cb)); | ||
75 | for (event, range) in doc.into_offset_iter() { | ||
76 | match event { | ||
77 | Event::Start(Tag::Link(_link_type, ref target, ref title)) => { | ||
78 | let link = if target.is_empty() { title } else { target }; | ||
79 | let (link, ns) = parse_link(link); | ||
80 | res.push((link.to_string(), ns, range)); | ||
81 | } | ||
82 | _ => {} | ||
83 | } | ||
84 | } | ||
85 | res | ||
86 | } | ||
87 | |||
64 | /// Remove all links in markdown documentation. | 88 | /// Remove all links in markdown documentation. |
65 | pub(crate) fn remove_links(markdown: &str) -> String { | 89 | pub(crate) fn remove_links(markdown: &str) -> String { |
66 | let mut drop_link = false; | 90 | let mut drop_link = false; |
@@ -192,9 +216,7 @@ fn rewrite_intra_doc_link( | |||
192 | Definition::Field(it) => it.resolve_doc_path(db, link, ns), | 216 | Definition::Field(it) => it.resolve_doc_path(db, link, ns), |
193 | Definition::SelfType(_) | 217 | Definition::SelfType(_) |
194 | | Definition::Local(_) | 218 | | Definition::Local(_) |
195 | | Definition::TypeParam(_) | 219 | | Definition::GenericParam(_) |
196 | | Definition::ConstParam(_) | ||
197 | | Definition::LifetimeParam(_) | ||
198 | | Definition::Label(_) => return None, | 220 | | Definition::Label(_) => return None, |
199 | }?; | 221 | }?; |
200 | let krate = resolved.module(db)?.krate(); | 222 | let krate = resolved.module(db)?.krate(); |
@@ -420,7 +442,7 @@ fn get_symbol_fragment(db: &dyn HirDatabase, field_or_assoc: &FieldOrAssocItem) | |||
420 | function.as_assoc_item(db).map(|assoc| assoc.container(db)), | 442 | function.as_assoc_item(db).map(|assoc| assoc.container(db)), |
421 | Some(AssocItemContainer::Trait(..)) | 443 | Some(AssocItemContainer::Trait(..)) |
422 | ); | 444 | ); |
423 | // This distinction may get more complicated when specialisation is available. | 445 | // This distinction may get more complicated when specialization is available. |
424 | // Rustdoc makes this decision based on whether a method 'has defaultness'. | 446 | // Rustdoc makes this decision based on whether a method 'has defaultness'. |
425 | // Currently this is only the case for provided trait methods. | 447 | // Currently this is only the case for provided trait methods. |
426 | if is_trait_method && !function.has_body(db) { | 448 | if is_trait_method && !function.has_body(db) { |
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 95b4cb9e3..cd4afc804 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs | |||
@@ -1,14 +1,18 @@ | |||
1 | use either::Either; | 1 | use either::Either; |
2 | use hir::Semantics; | 2 | use hir::{HasAttrs, ModuleDef, Semantics}; |
3 | use ide_db::{ | 3 | use ide_db::{ |
4 | base_db::FileId, | 4 | base_db::FileId, |
5 | defs::{NameClass, NameRefClass}, | 5 | defs::{Definition, NameClass, NameRefClass}, |
6 | symbol_index, RootDatabase, | 6 | symbol_index, RootDatabase, |
7 | }; | 7 | }; |
8 | use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; | 8 | use syntax::{ |
9 | ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextSize, TokenAtOffset, T, | ||
10 | }; | ||
9 | 11 | ||
10 | use crate::{ | 12 | use crate::{ |
11 | display::{ToNav, TryToNav}, | 13 | display::{ToNav, TryToNav}, |
14 | doc_links::extract_definitions_from_markdown, | ||
15 | runnables::doc_owner_to_def, | ||
12 | FilePosition, NavigationTarget, RangeInfo, SymbolKind, | 16 | FilePosition, NavigationTarget, RangeInfo, SymbolKind, |
13 | }; | 17 | }; |
14 | 18 | ||
@@ -30,6 +34,10 @@ pub(crate) fn goto_definition( | |||
30 | let original_token = pick_best(file.token_at_offset(position.offset))?; | 34 | let original_token = pick_best(file.token_at_offset(position.offset))?; |
31 | let token = sema.descend_into_macros(original_token.clone()); | 35 | let token = sema.descend_into_macros(original_token.clone()); |
32 | let parent = token.parent(); | 36 | let parent = token.parent(); |
37 | if let Some(comment) = ast::Comment::cast(token.clone()) { | ||
38 | let nav = def_for_doc_comment(&sema, position, &comment)?.try_to_nav(db)?; | ||
39 | return Some(RangeInfo::new(original_token.text_range(), vec![nav])); | ||
40 | } | ||
33 | 41 | ||
34 | let nav_targets = match_ast! { | 42 | let nav_targets = match_ast! { |
35 | match parent { | 43 | match parent { |
@@ -68,11 +76,58 @@ pub(crate) fn goto_definition( | |||
68 | Some(RangeInfo::new(original_token.text_range(), nav_targets)) | 76 | Some(RangeInfo::new(original_token.text_range(), nav_targets)) |
69 | } | 77 | } |
70 | 78 | ||
79 | fn def_for_doc_comment( | ||
80 | sema: &Semantics<RootDatabase>, | ||
81 | position: FilePosition, | ||
82 | doc_comment: &ast::Comment, | ||
83 | ) -> Option<hir::ModuleDef> { | ||
84 | let parent = doc_comment.syntax().parent(); | ||
85 | let (link, ns) = extract_positioned_link_from_comment(position, doc_comment)?; | ||
86 | |||
87 | let def = doc_owner_to_def(sema, parent)?; | ||
88 | match def { | ||
89 | Definition::ModuleDef(def) => match def { | ||
90 | ModuleDef::Module(it) => it.resolve_doc_path(sema.db, &link, ns), | ||
91 | ModuleDef::Function(it) => it.resolve_doc_path(sema.db, &link, ns), | ||
92 | ModuleDef::Adt(it) => it.resolve_doc_path(sema.db, &link, ns), | ||
93 | ModuleDef::Variant(it) => it.resolve_doc_path(sema.db, &link, ns), | ||
94 | ModuleDef::Const(it) => it.resolve_doc_path(sema.db, &link, ns), | ||
95 | ModuleDef::Static(it) => it.resolve_doc_path(sema.db, &link, ns), | ||
96 | ModuleDef::Trait(it) => it.resolve_doc_path(sema.db, &link, ns), | ||
97 | ModuleDef::TypeAlias(it) => it.resolve_doc_path(sema.db, &link, ns), | ||
98 | ModuleDef::BuiltinType(_) => return None, | ||
99 | }, | ||
100 | Definition::Macro(it) => it.resolve_doc_path(sema.db, &link, ns), | ||
101 | Definition::Field(it) => it.resolve_doc_path(sema.db, &link, ns), | ||
102 | Definition::SelfType(_) | ||
103 | | Definition::Local(_) | ||
104 | | Definition::GenericParam(_) | ||
105 | | Definition::Label(_) => return None, | ||
106 | } | ||
107 | } | ||
108 | |||
109 | fn extract_positioned_link_from_comment( | ||
110 | position: FilePosition, | ||
111 | comment: &ast::Comment, | ||
112 | ) -> Option<(String, Option<hir::Namespace>)> { | ||
113 | let comment_range = comment.syntax().text_range(); | ||
114 | let doc_comment = comment.doc_comment()?; | ||
115 | let def_links = extract_definitions_from_markdown(doc_comment); | ||
116 | let (def_link, ns, _) = def_links.iter().min_by_key(|(_, _, def_link_range)| { | ||
117 | let matched_position = comment_range.start() + TextSize::from(def_link_range.start as u32); | ||
118 | match position.offset.checked_sub(matched_position) { | ||
119 | Some(distance) => distance, | ||
120 | None => comment_range.end(), | ||
121 | } | ||
122 | })?; | ||
123 | Some((def_link.to_string(), ns.clone())) | ||
124 | } | ||
125 | |||
71 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | 126 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { |
72 | return tokens.max_by_key(priority); | 127 | return tokens.max_by_key(priority); |
73 | fn priority(n: &SyntaxToken) -> usize { | 128 | fn priority(n: &SyntaxToken) -> usize { |
74 | match n.kind() { | 129 | match n.kind() { |
75 | IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] => 2, | 130 | IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | COMMENT => 2, |
76 | kind if kind.is_trivia() => 0, | 131 | kind if kind.is_trivia() => 0, |
77 | _ => 1, | 132 | _ => 1, |
78 | } | 133 | } |
@@ -1145,4 +1200,34 @@ fn foo<'foo>(_: &'foo ()) { | |||
1145 | }"#, | 1200 | }"#, |
1146 | ) | 1201 | ) |
1147 | } | 1202 | } |
1203 | |||
1204 | #[test] | ||
1205 | fn goto_def_for_intra_doc_link_same_file() { | ||
1206 | check( | ||
1207 | r#" | ||
1208 | /// Blah, [`bar`](bar) .. [`foo`](foo)$0 has [`bar`](bar) | ||
1209 | pub fn bar() { } | ||
1210 | |||
1211 | /// You might want to see [`std::fs::read()`] too. | ||
1212 | pub fn foo() { } | ||
1213 | //^^^ | ||
1214 | |||
1215 | }"#, | ||
1216 | ) | ||
1217 | } | ||
1218 | |||
1219 | #[test] | ||
1220 | fn goto_def_for_intra_doc_link_inner() { | ||
1221 | check( | ||
1222 | r#" | ||
1223 | //- /main.rs | ||
1224 | mod m; | ||
1225 | struct S; | ||
1226 | //^ | ||
1227 | |||
1228 | //- /m.rs | ||
1229 | //! [`super::S$0`] | ||
1230 | "#, | ||
1231 | ) | ||
1232 | } | ||
1148 | } | 1233 | } |
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 8cb4a51d8..317b6f011 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use hir::{ | 1 | use hir::{ |
2 | Adt, AsAssocItem, AssocItemContainer, FieldSource, HasAttrs, HasSource, HirDisplay, Module, | 2 | Adt, AsAssocItem, AssocItemContainer, FieldSource, GenericParam, HasAttrs, HasSource, |
3 | ModuleDef, ModuleSource, Semantics, | 3 | HirDisplay, Module, ModuleDef, ModuleSource, Semantics, |
4 | }; | 4 | }; |
5 | use ide_db::base_db::SourceDatabase; | 5 | use ide_db::base_db::SourceDatabase; |
6 | use ide_db::{ | 6 | use ide_db::{ |
@@ -17,7 +17,7 @@ use crate::{ | |||
17 | doc_links::{remove_links, rewrite_links}, | 17 | doc_links::{remove_links, rewrite_links}, |
18 | markdown_remove::remove_markdown, | 18 | markdown_remove::remove_markdown, |
19 | markup::Markup, | 19 | markup::Markup, |
20 | runnables::{runnable, runnable_fn}, | 20 | runnables::{runnable_fn, runnable_mod}, |
21 | FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, | 21 | FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, |
22 | }; | 22 | }; |
23 | 23 | ||
@@ -175,12 +175,7 @@ fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<Hov | |||
175 | Definition::SelfType(it) => it.target_ty(db).as_adt(), | 175 | Definition::SelfType(it) => it.target_ty(db).as_adt(), |
176 | _ => None, | 176 | _ => None, |
177 | }?; | 177 | }?; |
178 | match adt { | 178 | adt.try_to_nav(db).map(to_action) |
179 | Adt::Struct(it) => it.try_to_nav(db), | ||
180 | Adt::Union(it) => it.try_to_nav(db), | ||
181 | Adt::Enum(it) => it.try_to_nav(db), | ||
182 | } | ||
183 | .map(to_action) | ||
184 | } | 179 | } |
185 | 180 | ||
186 | fn runnable_action( | 181 | fn runnable_action( |
@@ -192,7 +187,7 @@ fn runnable_action( | |||
192 | Definition::ModuleDef(it) => match it { | 187 | Definition::ModuleDef(it) => match it { |
193 | ModuleDef::Module(it) => match it.definition_source(sema.db).value { | 188 | ModuleDef::Module(it) => match it.definition_source(sema.db).value { |
194 | ModuleSource::Module(it) => { | 189 | ModuleSource::Module(it) => { |
195 | runnable(&sema, it.syntax().clone()).map(|it| HoverAction::Runnable(it)) | 190 | runnable_mod(&sema, it).map(|it| HoverAction::Runnable(it)) |
196 | } | 191 | } |
197 | _ => None, | 192 | _ => None, |
198 | }, | 193 | }, |
@@ -220,12 +215,12 @@ fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { | |||
220 | } | 215 | } |
221 | }; | 216 | }; |
222 | 217 | ||
223 | if let Definition::TypeParam(it) = def { | 218 | if let Definition::GenericParam(GenericParam::TypeParam(it)) = def { |
224 | it.trait_bounds(db).into_iter().for_each(|it| push_new_def(it.into())); | 219 | it.trait_bounds(db).into_iter().for_each(|it| push_new_def(it.into())); |
225 | } else { | 220 | } else { |
226 | let ty = match def { | 221 | let ty = match def { |
227 | Definition::Local(it) => it.ty(db), | 222 | Definition::Local(it) => it.ty(db), |
228 | Definition::ConstParam(it) => it.ty(db), | 223 | Definition::GenericParam(GenericParam::ConstParam(it)) => it.ty(db), |
229 | _ => return None, | 224 | _ => return None, |
230 | }; | 225 | }; |
231 | 226 | ||
@@ -357,9 +352,11 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> { | |||
357 | }) | 352 | }) |
358 | } | 353 | } |
359 | Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))), | 354 | Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))), |
360 | Definition::LifetimeParam(it) => Some(Markup::fenced_block(&it.name(db))), | 355 | Definition::GenericParam(it) => match it { |
361 | Definition::TypeParam(type_param) => Some(Markup::fenced_block(&type_param.display(db))), | 356 | GenericParam::TypeParam(it) => Some(Markup::fenced_block(&it.display(db))), |
362 | Definition::ConstParam(it) => from_def_source(db, it, None), | 357 | GenericParam::LifetimeParam(it) => Some(Markup::fenced_block(&it.name(db))), |
358 | GenericParam::ConstParam(it) => from_def_source(db, it, None), | ||
359 | }, | ||
363 | }; | 360 | }; |
364 | 361 | ||
365 | fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup> | 362 | fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup> |
@@ -1951,16 +1948,16 @@ struct S { | |||
1951 | /// Test cases: | 1948 | /// Test cases: |
1952 | /// case 1. bare URL: https://www.example.com/ | 1949 | /// case 1. bare URL: https://www.example.com/ |
1953 | /// case 2. inline URL with title: [example](https://www.example.com/) | 1950 | /// case 2. inline URL with title: [example](https://www.example.com/) |
1954 | /// case 3. code refrence: [`Result`] | 1951 | /// case 3. code reference: [`Result`] |
1955 | /// case 4. code refrence but miss footnote: [`String`] | 1952 | /// case 4. code reference but miss footnote: [`String`] |
1956 | /// case 5. autolink: <http://www.example.com/> | 1953 | /// case 5. autolink: <http://www.example.com/> |
1957 | /// case 6. email address: <[email protected]> | 1954 | /// case 6. email address: <[email protected]> |
1958 | /// case 7. refrence: [example][example] | 1955 | /// case 7. reference: [example][example] |
1959 | /// case 8. collapsed link: [example][] | 1956 | /// case 8. collapsed link: [example][] |
1960 | /// case 9. shortcut link: [example] | 1957 | /// case 9. shortcut link: [example] |
1961 | /// case 10. inline without URL: [example]() | 1958 | /// case 10. inline without URL: [example]() |
1962 | /// case 11. refrence: [foo][foo] | 1959 | /// case 11. reference: [foo][foo] |
1963 | /// case 12. refrence: [foo][bar] | 1960 | /// case 12. reference: [foo][bar] |
1964 | /// case 13. collapsed link: [foo][] | 1961 | /// case 13. collapsed link: [foo][] |
1965 | /// case 14. shortcut link: [foo] | 1962 | /// case 14. shortcut link: [foo] |
1966 | /// case 15. inline without URL: [foo]() | 1963 | /// case 15. inline without URL: [foo]() |
@@ -1987,16 +1984,16 @@ pub fn fo$0o() {} | |||
1987 | Test cases: | 1984 | Test cases: |
1988 | case 1. bare URL: https://www.example.com/ | 1985 | case 1. bare URL: https://www.example.com/ |
1989 | case 2. inline URL with title: [example](https://www.example.com/) | 1986 | case 2. inline URL with title: [example](https://www.example.com/) |
1990 | case 3. code refrence: `Result` | 1987 | case 3. code reference: `Result` |
1991 | case 4. code refrence but miss footnote: `String` | 1988 | case 4. code reference but miss footnote: `String` |
1992 | case 5. autolink: http://www.example.com/ | 1989 | case 5. autolink: http://www.example.com/ |
1993 | case 6. email address: [email protected] | 1990 | case 6. email address: [email protected] |
1994 | case 7. refrence: example | 1991 | case 7. reference: example |
1995 | case 8. collapsed link: example | 1992 | case 8. collapsed link: example |
1996 | case 9. shortcut link: example | 1993 | case 9. shortcut link: example |
1997 | case 10. inline without URL: example | 1994 | case 10. inline without URL: example |
1998 | case 11. refrence: foo | 1995 | case 11. reference: foo |
1999 | case 12. refrence: foo | 1996 | case 12. reference: foo |
2000 | case 13. collapsed link: foo | 1997 | case 13. collapsed link: foo |
2001 | case 14. shortcut link: foo | 1998 | case 14. shortcut link: foo |
2002 | case 15. inline without URL: foo | 1999 | case 15. inline without URL: foo |
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index fe60abfc8..a2039fcc7 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs | |||
@@ -353,9 +353,25 @@ fn is_argument_similar_to_param_name( | |||
353 | } | 353 | } |
354 | match get_string_representation(argument) { | 354 | match get_string_representation(argument) { |
355 | None => false, | 355 | None => false, |
356 | Some(repr) => { | 356 | Some(argument_string) => { |
357 | let argument_string = repr.trim_start_matches('_'); | 357 | let num_leading_underscores = |
358 | argument_string.starts_with(param_name) || argument_string.ends_with(param_name) | 358 | argument_string.bytes().take_while(|&c| c == b'_').count(); |
359 | |||
360 | // Does the argument name begin with the parameter name? Ignore leading underscores. | ||
361 | let mut arg_bytes = argument_string.bytes().skip(num_leading_underscores); | ||
362 | let starts_with_pattern = param_name.bytes().all( | ||
363 | |expected| matches!(arg_bytes.next(), Some(actual) if expected.eq_ignore_ascii_case(&actual)), | ||
364 | ); | ||
365 | |||
366 | if starts_with_pattern { | ||
367 | return true; | ||
368 | } | ||
369 | |||
370 | // Does the argument name end with the parameter name? | ||
371 | let mut arg_bytes = argument_string.bytes().skip(num_leading_underscores); | ||
372 | param_name.bytes().rev().all( | ||
373 | |expected| matches!(arg_bytes.next_back(), Some(actual) if expected.eq_ignore_ascii_case(&actual)), | ||
374 | ) | ||
359 | } | 375 | } |
360 | } | 376 | } |
361 | } | 377 | } |
@@ -901,6 +917,9 @@ fn main() { | |||
901 | twiddle(true); | 917 | twiddle(true); |
902 | doo(true); | 918 | doo(true); |
903 | 919 | ||
920 | const TWIDDLE_UPPERCASE: bool = true; | ||
921 | twiddle(TWIDDLE_UPPERCASE); | ||
922 | |||
904 | let mut param_begin: Param = Param {}; | 923 | let mut param_begin: Param = Param {}; |
905 | different_order(¶m_begin); | 924 | different_order(¶m_begin); |
906 | different_order(&mut param_begin); | 925 | different_order(&mut param_begin); |
@@ -1382,4 +1401,41 @@ fn main() { | |||
1382 | "#, | 1401 | "#, |
1383 | ) | 1402 | ) |
1384 | } | 1403 | } |
1404 | |||
1405 | #[test] | ||
1406 | fn fn_hints() { | ||
1407 | check( | ||
1408 | r#" | ||
1409 | trait Sized {} | ||
1410 | |||
1411 | fn foo() -> impl Fn() { loop {} } | ||
1412 | fn foo1() -> impl Fn(f64) { loop {} } | ||
1413 | fn foo2() -> impl Fn(f64, f64) { loop {} } | ||
1414 | fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} } | ||
1415 | fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} } | ||
1416 | fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} } | ||
1417 | fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} } | ||
1418 | fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} } | ||
1419 | |||
1420 | fn main() { | ||
1421 | let foo = foo(); | ||
1422 | // ^^^ impl Fn() | ||
1423 | let foo = foo1(); | ||
1424 | // ^^^ impl Fn(f64) | ||
1425 | let foo = foo2(); | ||
1426 | // ^^^ impl Fn(f64, f64) | ||
1427 | let foo = foo3(); | ||
1428 | // ^^^ impl Fn(f64, f64) -> u32 | ||
1429 | let foo = foo4(); | ||
1430 | // ^^^ &dyn Fn(f64, f64) -> u32 | ||
1431 | let foo = foo5(); | ||
1432 | // ^^^ &dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32 | ||
1433 | let foo = foo6(); | ||
1434 | // ^^^ impl Fn(f64, f64) -> u32 + Sized | ||
1435 | let foo = foo7(); | ||
1436 | // ^^^ *const (impl Fn(f64, f64) -> u32 + Sized) | ||
1437 | } | ||
1438 | "#, | ||
1439 | ) | ||
1440 | } | ||
1385 | } | 1441 | } |
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index cea2a13c8..1e03832ec 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -76,8 +76,8 @@ pub use crate::{ | |||
76 | references::{rename::RenameError, Declaration, ReferenceSearchResult}, | 76 | references::{rename::RenameError, Declaration, ReferenceSearchResult}, |
77 | runnables::{Runnable, RunnableKind, TestId}, | 77 | runnables::{Runnable, RunnableKind, TestId}, |
78 | syntax_highlighting::{ | 78 | syntax_highlighting::{ |
79 | tags::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag}, | 79 | tags::{Highlight, HlMod, HlMods, HlPunct, HlTag}, |
80 | HighlightedRange, | 80 | HlRange, |
81 | }, | 81 | }, |
82 | }; | 82 | }; |
83 | pub use assists::{Assist, AssistConfig, AssistId, AssistKind, InsertUseConfig}; | 83 | pub use assists::{Assist, AssistConfig, AssistId, AssistKind, InsertUseConfig}; |
@@ -92,7 +92,7 @@ pub use ide_db::base_db::{ | |||
92 | }; | 92 | }; |
93 | pub use ide_db::{ | 93 | pub use ide_db::{ |
94 | call_info::CallInfo, | 94 | call_info::CallInfo, |
95 | search::{Reference, ReferenceAccess, ReferenceKind}, | 95 | search::{FileReference, ReferenceAccess, ReferenceKind}, |
96 | }; | 96 | }; |
97 | pub use ide_db::{ | 97 | pub use ide_db::{ |
98 | label::Label, | 98 | label::Label, |
@@ -449,12 +449,12 @@ impl Analysis { | |||
449 | } | 449 | } |
450 | 450 | ||
451 | /// Computes syntax highlighting for the given file | 451 | /// Computes syntax highlighting for the given file |
452 | pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> { | 452 | pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HlRange>> { |
453 | self.with_db(|db| syntax_highlighting::highlight(db, file_id, None, false)) | 453 | self.with_db(|db| syntax_highlighting::highlight(db, file_id, None, false)) |
454 | } | 454 | } |
455 | 455 | ||
456 | /// Computes syntax highlighting for the given file range. | 456 | /// Computes syntax highlighting for the given file range. |
457 | pub fn highlight_range(&self, frange: FileRange) -> Cancelable<Vec<HighlightedRange>> { | 457 | pub fn highlight_range(&self, frange: FileRange) -> Cancelable<Vec<HlRange>> { |
458 | self.with_db(|db| { | 458 | self.with_db(|db| { |
459 | syntax_highlighting::highlight(db, frange.file_id, Some(frange.range), false) | 459 | syntax_highlighting::highlight(db, frange.file_id, Some(frange.range), false) |
460 | }) | 460 | }) |
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index c95ed669c..7d4757e02 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs | |||
@@ -3,7 +3,7 @@ | |||
3 | //! or `ast::NameRef`. If it's a `ast::NameRef`, at the classification step we | 3 | //! or `ast::NameRef`. If it's a `ast::NameRef`, at the classification step we |
4 | //! try to resolve the direct tree parent of this element, otherwise we | 4 | //! try to resolve the direct tree parent of this element, otherwise we |
5 | //! already have a definition and just need to get its HIR together with | 5 | //! already have a definition and just need to get its HIR together with |
6 | //! some information that is needed for futher steps of searching. | 6 | //! some information that is needed for further steps of searching. |
7 | //! After that, we collect files that might contain references and look | 7 | //! After that, we collect files that might contain references and look |
8 | //! for text occurrences of the identifier. If there's an `ast::NameRef` | 8 | //! for text occurrences of the identifier. If there's an `ast::NameRef` |
9 | //! at the index that the match starts at and its tree parent is | 9 | //! at the index that the match starts at and its tree parent is |
@@ -13,15 +13,15 @@ pub(crate) mod rename; | |||
13 | 13 | ||
14 | use hir::Semantics; | 14 | use hir::Semantics; |
15 | use ide_db::{ | 15 | use ide_db::{ |
16 | base_db::FileId, | ||
16 | defs::{Definition, NameClass, NameRefClass}, | 17 | defs::{Definition, NameClass, NameRefClass}, |
17 | search::Reference, | 18 | search::{FileReference, ReferenceAccess, ReferenceKind, SearchScope, UsageSearchResult}, |
18 | search::{ReferenceAccess, ReferenceKind, SearchScope}, | ||
19 | RootDatabase, | 19 | RootDatabase, |
20 | }; | 20 | }; |
21 | use syntax::{ | 21 | use syntax::{ |
22 | algo::find_node_at_offset, | 22 | algo::find_node_at_offset, |
23 | ast::{self, NameOwner}, | 23 | ast::{self, NameOwner}, |
24 | match_ast, AstNode, SyntaxKind, SyntaxNode, TextRange, TokenAtOffset, | 24 | match_ast, AstNode, SyntaxNode, TextRange, TokenAtOffset, T, |
25 | }; | 25 | }; |
26 | 26 | ||
27 | use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo, SymbolKind}; | 27 | use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo, SymbolKind}; |
@@ -29,7 +29,7 @@ use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeI | |||
29 | #[derive(Debug, Clone)] | 29 | #[derive(Debug, Clone)] |
30 | pub struct ReferenceSearchResult { | 30 | pub struct ReferenceSearchResult { |
31 | declaration: Declaration, | 31 | declaration: Declaration, |
32 | references: Vec<Reference>, | 32 | references: UsageSearchResult, |
33 | } | 33 | } |
34 | 34 | ||
35 | #[derive(Debug, Clone)] | 35 | #[derive(Debug, Clone)] |
@@ -48,10 +48,21 @@ impl ReferenceSearchResult { | |||
48 | &self.declaration.nav | 48 | &self.declaration.nav |
49 | } | 49 | } |
50 | 50 | ||
51 | pub fn references(&self) -> &[Reference] { | 51 | pub fn references(&self) -> &UsageSearchResult { |
52 | &self.references | 52 | &self.references |
53 | } | 53 | } |
54 | 54 | ||
55 | pub fn references_with_declaration(mut self) -> UsageSearchResult { | ||
56 | let decl_ref = FileReference { | ||
57 | range: self.declaration.nav.focus_or_full_range(), | ||
58 | kind: self.declaration.kind, | ||
59 | access: self.declaration.access, | ||
60 | }; | ||
61 | let file_id = self.declaration.nav.file_id; | ||
62 | self.references.references.entry(file_id).or_default().push(decl_ref); | ||
63 | self.references | ||
64 | } | ||
65 | |||
55 | /// Total number of references | 66 | /// Total number of references |
56 | /// At least 1 since all valid references should | 67 | /// At least 1 since all valid references should |
57 | /// Have a declaration | 68 | /// Have a declaration |
@@ -63,21 +74,11 @@ impl ReferenceSearchResult { | |||
63 | // allow turning ReferenceSearchResult into an iterator | 74 | // allow turning ReferenceSearchResult into an iterator |
64 | // over References | 75 | // over References |
65 | impl IntoIterator for ReferenceSearchResult { | 76 | impl IntoIterator for ReferenceSearchResult { |
66 | type Item = Reference; | 77 | type Item = (FileId, Vec<FileReference>); |
67 | type IntoIter = std::vec::IntoIter<Reference>; | 78 | type IntoIter = std::collections::hash_map::IntoIter<FileId, Vec<FileReference>>; |
68 | 79 | ||
69 | fn into_iter(mut self) -> Self::IntoIter { | 80 | fn into_iter(self) -> Self::IntoIter { |
70 | let mut v = Vec::with_capacity(self.len()); | 81 | self.references_with_declaration().into_iter() |
71 | v.push(Reference { | ||
72 | file_range: FileRange { | ||
73 | file_id: self.declaration.nav.file_id, | ||
74 | range: self.declaration.nav.focus_or_full_range(), | ||
75 | }, | ||
76 | kind: self.declaration.kind, | ||
77 | access: self.declaration.access, | ||
78 | }); | ||
79 | v.append(&mut self.references); | ||
80 | v.into_iter() | ||
81 | } | 82 | } |
82 | } | 83 | } |
83 | 84 | ||
@@ -109,13 +110,12 @@ pub(crate) fn find_all_refs( | |||
109 | 110 | ||
110 | let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?; | 111 | let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?; |
111 | 112 | ||
112 | let references = def | 113 | let mut usages = def.usages(sema).set_scope(search_scope).all(); |
113 | .usages(sema) | 114 | usages |
114 | .set_scope(search_scope) | 115 | .references |
115 | .all() | 116 | .values_mut() |
116 | .into_iter() | 117 | .for_each(|it| it.retain(|r| search_kind == ReferenceKind::Other || search_kind == r.kind)); |
117 | .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind) | 118 | usages.references.retain(|_, it| !it.is_empty()); |
118 | .collect(); | ||
119 | 119 | ||
120 | let nav = def.try_to_nav(sema.db)?; | 120 | let nav = def.try_to_nav(sema.db)?; |
121 | let decl_range = nav.focus_or_full_range(); | 121 | let decl_range = nav.focus_or_full_range(); |
@@ -130,13 +130,16 @@ pub(crate) fn find_all_refs( | |||
130 | kind = ReferenceKind::FieldShorthandForLocal; | 130 | kind = ReferenceKind::FieldShorthandForLocal; |
131 | } | 131 | } |
132 | } | 132 | } |
133 | } else if matches!(def, Definition::LifetimeParam(_) | Definition::Label(_)) { | 133 | } else if matches!( |
134 | def, | ||
135 | Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_) | ||
136 | ) { | ||
134 | kind = ReferenceKind::Lifetime; | 137 | kind = ReferenceKind::Lifetime; |
135 | }; | 138 | }; |
136 | 139 | ||
137 | let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) }; | 140 | let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) }; |
138 | 141 | ||
139 | Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references })) | 142 | Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references: usages })) |
140 | } | 143 | } |
141 | 144 | ||
142 | fn find_name( | 145 | fn find_name( |
@@ -200,7 +203,7 @@ fn get_struct_def_name_for_struct_literal_search( | |||
200 | position: FilePosition, | 203 | position: FilePosition, |
201 | ) -> Option<ast::Name> { | 204 | ) -> Option<ast::Name> { |
202 | if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) { | 205 | if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) { |
203 | if right.kind() != SyntaxKind::L_CURLY && right.kind() != SyntaxKind::L_PAREN { | 206 | if right.kind() != T!['{'] && right.kind() != T!['('] { |
204 | return None; | 207 | return None; |
205 | } | 208 | } |
206 | if let Some(name) = | 209 | if let Some(name) = |
@@ -227,7 +230,7 @@ fn get_enum_def_name_for_struct_literal_search( | |||
227 | position: FilePosition, | 230 | position: FilePosition, |
228 | ) -> Option<ast::Name> { | 231 | ) -> Option<ast::Name> { |
229 | if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) { | 232 | if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) { |
230 | if right.kind() != SyntaxKind::L_CURLY && right.kind() != SyntaxKind::L_PAREN { | 233 | if right.kind() != T!['{'] && right.kind() != T!['('] { |
231 | return None; | 234 | return None; |
232 | } | 235 | } |
233 | if let Some(name) = | 236 | if let Some(name) = |
@@ -252,8 +255,8 @@ fn try_find_self_references( | |||
252 | syntax: &SyntaxNode, | 255 | syntax: &SyntaxNode, |
253 | position: FilePosition, | 256 | position: FilePosition, |
254 | ) -> Option<RangeInfo<ReferenceSearchResult>> { | 257 | ) -> Option<RangeInfo<ReferenceSearchResult>> { |
255 | let self_token = | 258 | let FilePosition { file_id, offset } = position; |
256 | syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW)?; | 259 | let self_token = syntax.token_at_offset(offset).find(|t| t.kind() == T![self])?; |
257 | let parent = self_token.parent(); | 260 | let parent = self_token.parent(); |
258 | match_ast! { | 261 | match_ast! { |
259 | match parent { | 262 | match parent { |
@@ -274,7 +277,7 @@ fn try_find_self_references( | |||
274 | 277 | ||
275 | let declaration = Declaration { | 278 | let declaration = Declaration { |
276 | nav: NavigationTarget { | 279 | nav: NavigationTarget { |
277 | file_id: position.file_id, | 280 | file_id, |
278 | full_range: self_param.syntax().text_range(), | 281 | full_range: self_param.syntax().text_range(), |
279 | focus_range: Some(param_self_token.text_range()), | 282 | focus_range: Some(param_self_token.text_range()), |
280 | name: param_self_token.text().clone(), | 283 | name: param_self_token.text().clone(), |
@@ -290,7 +293,7 @@ fn try_find_self_references( | |||
290 | ReferenceAccess::Read | 293 | ReferenceAccess::Read |
291 | }), | 294 | }), |
292 | }; | 295 | }; |
293 | let references = function | 296 | let refs = function |
294 | .body() | 297 | .body() |
295 | .map(|body| { | 298 | .map(|body| { |
296 | body.syntax() | 299 | body.syntax() |
@@ -304,14 +307,16 @@ fn try_find_self_references( | |||
304 | None | 307 | None |
305 | } | 308 | } |
306 | }) | 309 | }) |
307 | .map(|token| Reference { | 310 | .map(|token| FileReference { |
308 | file_range: FileRange { file_id: position.file_id, range: token.text_range() }, | 311 | range: token.text_range(), |
309 | kind: ReferenceKind::SelfKw, | 312 | kind: ReferenceKind::SelfKw, |
310 | access: declaration.access, // FIXME: properly check access kind here instead of copying it from the declaration | 313 | access: declaration.access, // FIXME: properly check access kind here instead of copying it from the declaration |
311 | }) | 314 | }) |
312 | .collect() | 315 | .collect() |
313 | }) | 316 | }) |
314 | .unwrap_or_default(); | 317 | .unwrap_or_default(); |
318 | let mut references = UsageSearchResult::default(); | ||
319 | references.references.insert(file_id, refs); | ||
315 | 320 | ||
316 | Some(RangeInfo::new( | 321 | Some(RangeInfo::new( |
317 | param_self_token.text_range(), | 322 | param_self_token.text_range(), |
@@ -1016,12 +1021,14 @@ impl Foo { | |||
1016 | actual += "\n\n"; | 1021 | actual += "\n\n"; |
1017 | } | 1022 | } |
1018 | 1023 | ||
1019 | for r in &refs.references { | 1024 | for (file_id, references) in refs.references { |
1020 | format_to!(actual, "{:?} {:?} {:?}", r.file_range.file_id, r.file_range.range, r.kind); | 1025 | for r in references { |
1021 | if let Some(access) = r.access { | 1026 | format_to!(actual, "{:?} {:?} {:?}", file_id, r.range, r.kind); |
1022 | format_to!(actual, " {:?}", access); | 1027 | if let Some(access) = r.access { |
1028 | format_to!(actual, " {:?}", access); | ||
1029 | } | ||
1030 | actual += "\n"; | ||
1023 | } | 1031 | } |
1024 | actual += "\n"; | ||
1025 | } | 1032 | } |
1026 | expect.assert_eq(&actual) | 1033 | expect.assert_eq(&actual) |
1027 | } | 1034 | } |
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index 53d79333c..c3ae568c2 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -1,29 +1,30 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | use std::{ | 2 | use std::{ |
3 | convert::TryInto, | 3 | convert::TryInto, |
4 | error::Error, | ||
5 | fmt::{self, Display}, | 4 | fmt::{self, Display}, |
6 | }; | 5 | }; |
7 | 6 | ||
8 | use hir::{Module, ModuleDef, ModuleSource, Semantics}; | 7 | use hir::{Module, ModuleDef, ModuleSource, Semantics}; |
9 | use ide_db::base_db::{AnchoredPathBuf, FileId, FileRange, SourceDatabaseExt}; | ||
10 | use ide_db::{ | 8 | use ide_db::{ |
9 | base_db::{AnchoredPathBuf, FileId, FileRange, SourceDatabaseExt}, | ||
11 | defs::{Definition, NameClass, NameRefClass}, | 10 | defs::{Definition, NameClass, NameRefClass}, |
11 | search::FileReference, | ||
12 | RootDatabase, | 12 | RootDatabase, |
13 | }; | 13 | }; |
14 | use syntax::{ | 14 | use syntax::{ |
15 | algo::find_node_at_offset, | 15 | algo::find_node_at_offset, |
16 | ast::{self, NameOwner}, | 16 | ast::{self, NameOwner}, |
17 | lex_single_syntax_kind, match_ast, AstNode, SyntaxKind, SyntaxNode, SyntaxToken, | 17 | lex_single_syntax_kind, match_ast, AstNode, SyntaxKind, SyntaxNode, SyntaxToken, T, |
18 | }; | 18 | }; |
19 | use test_utils::mark; | 19 | use test_utils::mark; |
20 | use text_edit::TextEdit; | 20 | use text_edit::TextEdit; |
21 | 21 | ||
22 | use crate::{ | 22 | use crate::{ |
23 | references::find_all_refs, FilePosition, FileSystemEdit, RangeInfo, Reference, ReferenceKind, | 23 | FilePosition, FileSystemEdit, RangeInfo, ReferenceKind, ReferenceSearchResult, SourceChange, |
24 | SourceChange, SourceFileEdit, TextRange, TextSize, | 24 | SourceFileEdit, TextRange, TextSize, |
25 | }; | 25 | }; |
26 | 26 | ||
27 | type RenameResult<T> = Result<T, RenameError>; | ||
27 | #[derive(Debug)] | 28 | #[derive(Debug)] |
28 | pub struct RenameError(pub(crate) String); | 29 | pub struct RenameError(pub(crate) String); |
29 | 30 | ||
@@ -33,26 +34,30 @@ impl fmt::Display for RenameError { | |||
33 | } | 34 | } |
34 | } | 35 | } |
35 | 36 | ||
36 | impl Error for RenameError {} | 37 | macro_rules! format_err { |
38 | ($fmt:expr) => {RenameError(format!($fmt))}; | ||
39 | ($fmt:expr, $($arg:tt)+) => {RenameError(format!($fmt, $($arg)+))} | ||
40 | } | ||
41 | |||
42 | macro_rules! bail { | ||
43 | ($($tokens:tt)*) => {return Err(format_err!($($tokens)*))} | ||
44 | } | ||
37 | 45 | ||
38 | pub(crate) fn prepare_rename( | 46 | pub(crate) fn prepare_rename( |
39 | db: &RootDatabase, | 47 | db: &RootDatabase, |
40 | position: FilePosition, | 48 | position: FilePosition, |
41 | ) -> Result<RangeInfo<()>, RenameError> { | 49 | ) -> RenameResult<RangeInfo<()>> { |
42 | let sema = Semantics::new(db); | 50 | let sema = Semantics::new(db); |
43 | let source_file = sema.parse(position.file_id); | 51 | let source_file = sema.parse(position.file_id); |
44 | let syntax = source_file.syntax(); | 52 | let syntax = source_file.syntax(); |
45 | if let Some(module) = find_module_at_offset(&sema, position, syntax) { | 53 | if let Some(module) = find_module_at_offset(&sema, position, syntax) { |
46 | rename_mod(&sema, position, module, "dummy") | 54 | rename_mod(&sema, position, module, "dummy") |
47 | } else if let Some(self_token) = | 55 | } else if let Some(self_token) = |
48 | syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) | 56 | syntax.token_at_offset(position.offset).find(|t| t.kind() == T![self]) |
49 | { | 57 | { |
50 | rename_self_to_param(&sema, position, self_token, "dummy") | 58 | rename_self_to_param(&sema, position, self_token, "dummy") |
51 | } else { | 59 | } else { |
52 | let range = match find_all_refs(&sema, position, None) { | 60 | let RangeInfo { range, .. } = find_all_refs(&sema, position)?; |
53 | Some(RangeInfo { range, .. }) => range, | ||
54 | None => return Err(RenameError("No references found at position".to_string())), | ||
55 | }; | ||
56 | Ok(RangeInfo::new(range, SourceChange::from(vec![]))) | 61 | Ok(RangeInfo::new(range, SourceChange::from(vec![]))) |
57 | } | 62 | } |
58 | .map(|info| RangeInfo::new(info.range, ())) | 63 | .map(|info| RangeInfo::new(info.range, ())) |
@@ -62,7 +67,7 @@ pub(crate) fn rename( | |||
62 | db: &RootDatabase, | 67 | db: &RootDatabase, |
63 | position: FilePosition, | 68 | position: FilePosition, |
64 | new_name: &str, | 69 | new_name: &str, |
65 | ) -> Result<RangeInfo<SourceChange>, RenameError> { | 70 | ) -> RenameResult<RangeInfo<SourceChange>> { |
66 | let sema = Semantics::new(db); | 71 | let sema = Semantics::new(db); |
67 | rename_with_semantics(&sema, position, new_name) | 72 | rename_with_semantics(&sema, position, new_name) |
68 | } | 73 | } |
@@ -71,42 +76,18 @@ pub(crate) fn rename_with_semantics( | |||
71 | sema: &Semantics<RootDatabase>, | 76 | sema: &Semantics<RootDatabase>, |
72 | position: FilePosition, | 77 | position: FilePosition, |
73 | new_name: &str, | 78 | new_name: &str, |
74 | ) -> Result<RangeInfo<SourceChange>, RenameError> { | 79 | ) -> RenameResult<RangeInfo<SourceChange>> { |
75 | let is_lifetime_name = match lex_single_syntax_kind(new_name) { | ||
76 | Some(res) => match res { | ||
77 | (SyntaxKind::IDENT, _) => false, | ||
78 | (SyntaxKind::UNDERSCORE, _) => false, | ||
79 | (SyntaxKind::SELF_KW, _) => return rename_to_self(&sema, position), | ||
80 | (SyntaxKind::LIFETIME_IDENT, _) if new_name != "'static" && new_name != "'_" => true, | ||
81 | (SyntaxKind::LIFETIME_IDENT, _) => { | ||
82 | return Err(RenameError(format!( | ||
83 | "Invalid name `{0}`: Cannot rename lifetime to {0}", | ||
84 | new_name | ||
85 | ))) | ||
86 | } | ||
87 | (_, Some(syntax_error)) => { | ||
88 | return Err(RenameError(format!("Invalid name `{}`: {}", new_name, syntax_error))) | ||
89 | } | ||
90 | (_, None) => { | ||
91 | return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name))) | ||
92 | } | ||
93 | }, | ||
94 | None => return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name))), | ||
95 | }; | ||
96 | |||
97 | let source_file = sema.parse(position.file_id); | 80 | let source_file = sema.parse(position.file_id); |
98 | let syntax = source_file.syntax(); | 81 | let syntax = source_file.syntax(); |
99 | // this is here to prevent lifetime renames from happening on modules and self | 82 | |
100 | if is_lifetime_name { | 83 | if let Some(module) = find_module_at_offset(&sema, position, syntax) { |
101 | rename_reference(&sema, position, new_name, is_lifetime_name) | ||
102 | } else if let Some(module) = find_module_at_offset(&sema, position, syntax) { | ||
103 | rename_mod(&sema, position, module, new_name) | 84 | rename_mod(&sema, position, module, new_name) |
104 | } else if let Some(self_token) = | 85 | } else if let Some(self_token) = |
105 | syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) | 86 | syntax.token_at_offset(position.offset).find(|t| t.kind() == T![self]) |
106 | { | 87 | { |
107 | rename_self_to_param(&sema, position, self_token, new_name) | 88 | rename_self_to_param(&sema, position, self_token, new_name) |
108 | } else { | 89 | } else { |
109 | rename_reference(&sema, position, new_name, is_lifetime_name) | 90 | rename_reference(&sema, position, new_name) |
110 | } | 91 | } |
111 | } | 92 | } |
112 | 93 | ||
@@ -127,6 +108,33 @@ pub(crate) fn will_rename_file( | |||
127 | Some(change) | 108 | Some(change) |
128 | } | 109 | } |
129 | 110 | ||
111 | #[derive(Debug, PartialEq)] | ||
112 | enum IdentifierKind { | ||
113 | Ident, | ||
114 | Lifetime, | ||
115 | ToSelf, | ||
116 | Underscore, | ||
117 | } | ||
118 | |||
119 | fn check_identifier(new_name: &str) -> RenameResult<IdentifierKind> { | ||
120 | match lex_single_syntax_kind(new_name) { | ||
121 | Some(res) => match res { | ||
122 | (SyntaxKind::IDENT, _) => Ok(IdentifierKind::Ident), | ||
123 | (T![_], _) => Ok(IdentifierKind::Underscore), | ||
124 | (T![self], _) => Ok(IdentifierKind::ToSelf), | ||
125 | (SyntaxKind::LIFETIME_IDENT, _) if new_name != "'static" && new_name != "'_" => { | ||
126 | Ok(IdentifierKind::Lifetime) | ||
127 | } | ||
128 | (SyntaxKind::LIFETIME_IDENT, _) => { | ||
129 | bail!("Invalid name `{0}`: Cannot rename lifetime to {0}", new_name) | ||
130 | } | ||
131 | (_, Some(syntax_error)) => bail!("Invalid name `{}`: {}", new_name, syntax_error), | ||
132 | (_, None) => bail!("Invalid name `{}`: not an identifier", new_name), | ||
133 | }, | ||
134 | None => bail!("Invalid name `{}`: not an identifier", new_name), | ||
135 | } | ||
136 | } | ||
137 | |||
130 | fn find_module_at_offset( | 138 | fn find_module_at_offset( |
131 | sema: &Semantics<RootDatabase>, | 139 | sema: &Semantics<RootDatabase>, |
132 | position: FilePosition, | 140 | position: FilePosition, |
@@ -155,39 +163,54 @@ fn find_module_at_offset( | |||
155 | Some(module) | 163 | Some(module) |
156 | } | 164 | } |
157 | 165 | ||
158 | fn source_edit_from_reference( | 166 | fn find_all_refs( |
167 | sema: &Semantics<RootDatabase>, | ||
168 | position: FilePosition, | ||
169 | ) -> RenameResult<RangeInfo<ReferenceSearchResult>> { | ||
170 | crate::references::find_all_refs(sema, position, None) | ||
171 | .ok_or_else(|| format_err!("No references found at position")) | ||
172 | } | ||
173 | |||
174 | fn source_edit_from_references( | ||
159 | sema: &Semantics<RootDatabase>, | 175 | sema: &Semantics<RootDatabase>, |
160 | reference: Reference, | 176 | file_id: FileId, |
177 | references: &[FileReference], | ||
161 | new_name: &str, | 178 | new_name: &str, |
162 | ) -> SourceFileEdit { | 179 | ) -> SourceFileEdit { |
163 | let mut replacement_text = String::new(); | 180 | let mut edit = TextEdit::builder(); |
164 | let range = match reference.kind { | 181 | for reference in references { |
165 | ReferenceKind::FieldShorthandForField => { | 182 | let mut replacement_text = String::new(); |
166 | mark::hit!(test_rename_struct_field_for_shorthand); | 183 | let range = match reference.kind { |
167 | replacement_text.push_str(new_name); | 184 | ReferenceKind::FieldShorthandForField => { |
168 | replacement_text.push_str(": "); | 185 | mark::hit!(test_rename_struct_field_for_shorthand); |
169 | TextRange::new(reference.file_range.range.start(), reference.file_range.range.start()) | 186 | replacement_text.push_str(new_name); |
170 | } | 187 | replacement_text.push_str(": "); |
171 | ReferenceKind::FieldShorthandForLocal => { | 188 | TextRange::new(reference.range.start(), reference.range.start()) |
172 | mark::hit!(test_rename_local_for_field_shorthand); | 189 | } |
173 | replacement_text.push_str(": "); | 190 | ReferenceKind::FieldShorthandForLocal => { |
174 | replacement_text.push_str(new_name); | 191 | mark::hit!(test_rename_local_for_field_shorthand); |
175 | TextRange::new(reference.file_range.range.end(), reference.file_range.range.end()) | 192 | replacement_text.push_str(": "); |
176 | } | 193 | replacement_text.push_str(new_name); |
177 | ReferenceKind::RecordFieldExprOrPat => { | 194 | TextRange::new(reference.range.end(), reference.range.end()) |
178 | mark::hit!(test_rename_field_expr_pat); | 195 | } |
179 | replacement_text.push_str(new_name); | 196 | ReferenceKind::RecordFieldExprOrPat => { |
180 | edit_text_range_for_record_field_expr_or_pat(sema, reference.file_range, new_name) | 197 | mark::hit!(test_rename_field_expr_pat); |
181 | } | 198 | replacement_text.push_str(new_name); |
182 | _ => { | 199 | edit_text_range_for_record_field_expr_or_pat( |
183 | replacement_text.push_str(new_name); | 200 | sema, |
184 | reference.file_range.range | 201 | FileRange { file_id, range: reference.range }, |
185 | } | 202 | new_name, |
186 | }; | 203 | ) |
187 | SourceFileEdit { | 204 | } |
188 | file_id: reference.file_range.file_id, | 205 | _ => { |
189 | edit: TextEdit::replace(range, replacement_text), | 206 | replacement_text.push_str(new_name); |
207 | reference.range | ||
208 | } | ||
209 | }; | ||
210 | edit.replace(range, replacement_text); | ||
190 | } | 211 | } |
212 | |||
213 | SourceFileEdit { file_id, edit: edit.finish() } | ||
191 | } | 214 | } |
192 | 215 | ||
193 | fn edit_text_range_for_record_field_expr_or_pat( | 216 | fn edit_text_range_for_record_field_expr_or_pat( |
@@ -223,7 +246,10 @@ fn rename_mod( | |||
223 | position: FilePosition, | 246 | position: FilePosition, |
224 | module: Module, | 247 | module: Module, |
225 | new_name: &str, | 248 | new_name: &str, |
226 | ) -> Result<RangeInfo<SourceChange>, RenameError> { | 249 | ) -> RenameResult<RangeInfo<SourceChange>> { |
250 | if IdentifierKind::Ident != check_identifier(new_name)? { | ||
251 | bail!("Invalid name `{0}`: cannot rename module to {0}", new_name); | ||
252 | } | ||
227 | let mut source_file_edits = Vec::new(); | 253 | let mut source_file_edits = Vec::new(); |
228 | let mut file_system_edits = Vec::new(); | 254 | let mut file_system_edits = Vec::new(); |
229 | 255 | ||
@@ -254,12 +280,10 @@ fn rename_mod( | |||
254 | source_file_edits.push(edit); | 280 | source_file_edits.push(edit); |
255 | } | 281 | } |
256 | 282 | ||
257 | let RangeInfo { range, info: refs } = find_all_refs(sema, position, None) | 283 | let RangeInfo { range, info: refs } = find_all_refs(sema, position)?; |
258 | .ok_or_else(|| RenameError("No references found at position".to_string()))?; | 284 | let ref_edits = refs.references().iter().map(|(&file_id, references)| { |
259 | let ref_edits = refs | 285 | source_edit_from_references(sema, file_id, references, new_name) |
260 | .references | 286 | }); |
261 | .into_iter() | ||
262 | .map(|reference| source_edit_from_reference(sema, reference, new_name)); | ||
263 | source_file_edits.extend(ref_edits); | 287 | source_file_edits.extend(ref_edits); |
264 | 288 | ||
265 | Ok(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits))) | 289 | Ok(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits))) |
@@ -274,27 +298,26 @@ fn rename_to_self( | |||
274 | 298 | ||
275 | let (fn_def, fn_ast) = find_node_at_offset::<ast::Fn>(syn, position.offset) | 299 | let (fn_def, fn_ast) = find_node_at_offset::<ast::Fn>(syn, position.offset) |
276 | .and_then(|fn_ast| sema.to_def(&fn_ast).zip(Some(fn_ast))) | 300 | .and_then(|fn_ast| sema.to_def(&fn_ast).zip(Some(fn_ast))) |
277 | .ok_or_else(|| RenameError("No surrounding method declaration found".to_string()))?; | 301 | .ok_or_else(|| format_err!("No surrounding method declaration found"))?; |
278 | let param_range = fn_ast | 302 | let param_range = fn_ast |
279 | .param_list() | 303 | .param_list() |
280 | .and_then(|p| p.params().next()) | 304 | .and_then(|p| p.params().next()) |
281 | .ok_or_else(|| RenameError("Method has no parameters".to_string()))? | 305 | .ok_or_else(|| format_err!("Method has no parameters"))? |
282 | .syntax() | 306 | .syntax() |
283 | .text_range(); | 307 | .text_range(); |
284 | if !param_range.contains(position.offset) { | 308 | if !param_range.contains(position.offset) { |
285 | return Err(RenameError("Only the first parameter can be self".to_string())); | 309 | bail!("Only the first parameter can be self"); |
286 | } | 310 | } |
287 | 311 | ||
288 | let impl_block = find_node_at_offset::<ast::Impl>(syn, position.offset) | 312 | let impl_block = find_node_at_offset::<ast::Impl>(syn, position.offset) |
289 | .and_then(|def| sema.to_def(&def)) | 313 | .and_then(|def| sema.to_def(&def)) |
290 | .ok_or_else(|| RenameError("No impl block found for function".to_string()))?; | 314 | .ok_or_else(|| format_err!("No impl block found for function"))?; |
291 | if fn_def.self_param(sema.db).is_some() { | 315 | if fn_def.self_param(sema.db).is_some() { |
292 | return Err(RenameError("Method already has a self parameter".to_string())); | 316 | bail!("Method already has a self parameter"); |
293 | } | 317 | } |
294 | 318 | ||
295 | let params = fn_def.assoc_fn_params(sema.db); | 319 | let params = fn_def.assoc_fn_params(sema.db); |
296 | let first_param = | 320 | let first_param = params.first().ok_or_else(|| format_err!("Method has no parameters"))?; |
297 | params.first().ok_or_else(|| RenameError("Method has no parameters".into()))?; | ||
298 | let first_param_ty = first_param.ty(); | 321 | let first_param_ty = first_param.ty(); |
299 | let impl_ty = impl_block.target_ty(sema.db); | 322 | let impl_ty = impl_block.target_ty(sema.db); |
300 | let (ty, self_param) = if impl_ty.remove_ref().is_some() { | 323 | let (ty, self_param) = if impl_ty.remove_ref().is_some() { |
@@ -307,23 +330,17 @@ fn rename_to_self( | |||
307 | }; | 330 | }; |
308 | 331 | ||
309 | if ty != impl_ty { | 332 | if ty != impl_ty { |
310 | return Err(RenameError("Parameter type differs from impl block type".to_string())); | 333 | bail!("Parameter type differs from impl block type"); |
311 | } | 334 | } |
312 | 335 | ||
313 | let RangeInfo { range, info: refs } = find_all_refs(sema, position, None) | 336 | let RangeInfo { range, info: refs } = find_all_refs(sema, position)?; |
314 | .ok_or_else(|| RenameError("No reference found at position".to_string()))?; | ||
315 | |||
316 | let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs | ||
317 | .into_iter() | ||
318 | .partition(|reference| param_range.intersect(reference.file_range.range).is_some()); | ||
319 | |||
320 | if param_ref.is_empty() { | ||
321 | return Err(RenameError("Parameter to rename not found".to_string())); | ||
322 | } | ||
323 | 337 | ||
324 | let mut edits = usages | 338 | let mut edits = refs |
325 | .into_iter() | 339 | .references() |
326 | .map(|reference| source_edit_from_reference(sema, reference, "self")) | 340 | .iter() |
341 | .map(|(&file_id, references)| { | ||
342 | source_edit_from_references(sema, file_id, references, "self") | ||
343 | }) | ||
327 | .collect::<Vec<_>>(); | 344 | .collect::<Vec<_>>(); |
328 | 345 | ||
329 | edits.push(SourceFileEdit { | 346 | edits.push(SourceFileEdit { |
@@ -367,12 +384,22 @@ fn rename_self_to_param( | |||
367 | self_token: SyntaxToken, | 384 | self_token: SyntaxToken, |
368 | new_name: &str, | 385 | new_name: &str, |
369 | ) -> Result<RangeInfo<SourceChange>, RenameError> { | 386 | ) -> Result<RangeInfo<SourceChange>, RenameError> { |
387 | let ident_kind = check_identifier(new_name)?; | ||
388 | match ident_kind { | ||
389 | IdentifierKind::Lifetime => bail!("Invalid name `{}`: not an identifier", new_name), | ||
390 | IdentifierKind::ToSelf => { | ||
391 | // no-op | ||
392 | mark::hit!(rename_self_to_self); | ||
393 | return Ok(RangeInfo::new(self_token.text_range(), SourceChange::default())); | ||
394 | } | ||
395 | _ => (), | ||
396 | } | ||
370 | let source_file = sema.parse(position.file_id); | 397 | let source_file = sema.parse(position.file_id); |
371 | let syn = source_file.syntax(); | 398 | let syn = source_file.syntax(); |
372 | 399 | ||
373 | let text = sema.db.file_text(position.file_id); | 400 | let text = sema.db.file_text(position.file_id); |
374 | let fn_def = find_node_at_offset::<ast::Fn>(syn, position.offset) | 401 | let fn_def = find_node_at_offset::<ast::Fn>(syn, position.offset) |
375 | .ok_or_else(|| RenameError("No surrounding method declaration found".to_string()))?; | 402 | .ok_or_else(|| format_err!("No surrounding method declaration found"))?; |
376 | let search_range = fn_def.syntax().text_range(); | 403 | let search_range = fn_def.syntax().text_range(); |
377 | 404 | ||
378 | let mut edits: Vec<SourceFileEdit> = vec![]; | 405 | let mut edits: Vec<SourceFileEdit> = vec![]; |
@@ -382,12 +409,10 @@ fn rename_self_to_param( | |||
382 | if !search_range.contains_inclusive(offset) { | 409 | if !search_range.contains_inclusive(offset) { |
383 | continue; | 410 | continue; |
384 | } | 411 | } |
385 | if let Some(ref usage) = | 412 | if let Some(ref usage) = syn.token_at_offset(offset).find(|t| t.kind() == T![self]) { |
386 | syn.token_at_offset(offset).find(|t| t.kind() == SyntaxKind::SELF_KW) | ||
387 | { | ||
388 | let edit = if let Some(ref self_param) = ast::SelfParam::cast(usage.parent()) { | 413 | let edit = if let Some(ref self_param) = ast::SelfParam::cast(usage.parent()) { |
389 | text_edit_from_self_param(syn, self_param, new_name) | 414 | text_edit_from_self_param(syn, self_param, new_name) |
390 | .ok_or_else(|| RenameError("No target type found".to_string()))? | 415 | .ok_or_else(|| format_err!("No target type found"))? |
391 | } else { | 416 | } else { |
392 | TextEdit::replace(usage.text_range(), String::from(new_name)) | 417 | TextEdit::replace(usage.text_range(), String::from(new_name)) |
393 | }; | 418 | }; |
@@ -395,6 +420,10 @@ fn rename_self_to_param( | |||
395 | } | 420 | } |
396 | } | 421 | } |
397 | 422 | ||
423 | if edits.len() > 1 && ident_kind == IdentifierKind::Underscore { | ||
424 | bail!("Cannot rename reference to `_` as it is being referenced multiple times"); | ||
425 | } | ||
426 | |||
398 | let range = ast::SelfParam::cast(self_token.parent()) | 427 | let range = ast::SelfParam::cast(self_token.parent()) |
399 | .map_or(self_token.text_range(), |p| p.syntax().text_range()); | 428 | .map_or(self_token.text_range(), |p| p.syntax().text_range()); |
400 | 429 | ||
@@ -405,35 +434,43 @@ fn rename_reference( | |||
405 | sema: &Semantics<RootDatabase>, | 434 | sema: &Semantics<RootDatabase>, |
406 | position: FilePosition, | 435 | position: FilePosition, |
407 | new_name: &str, | 436 | new_name: &str, |
408 | is_lifetime_name: bool, | ||
409 | ) -> Result<RangeInfo<SourceChange>, RenameError> { | 437 | ) -> Result<RangeInfo<SourceChange>, RenameError> { |
410 | let RangeInfo { range, info: refs } = match find_all_refs(sema, position, None) { | 438 | let ident_kind = check_identifier(new_name)?; |
411 | Some(range_info) => range_info, | 439 | let RangeInfo { range, info: refs } = find_all_refs(sema, position)?; |
412 | None => return Err(RenameError("No references found at position".to_string())), | 440 | |
413 | }; | 441 | match (ident_kind, &refs.declaration.kind) { |
414 | 442 | (IdentifierKind::ToSelf, ReferenceKind::Lifetime) | |
415 | match (refs.declaration.kind == ReferenceKind::Lifetime, is_lifetime_name) { | 443 | | (IdentifierKind::Underscore, ReferenceKind::Lifetime) |
416 | (true, false) => { | 444 | | (IdentifierKind::Ident, ReferenceKind::Lifetime) => { |
417 | return Err(RenameError(format!( | 445 | mark::hit!(rename_not_a_lifetime_ident_ref); |
418 | "Invalid name `{}`: not a lifetime identifier", | 446 | bail!("Invalid name `{}`: not a lifetime identifier", new_name) |
419 | new_name | ||
420 | ))) | ||
421 | } | 447 | } |
422 | (false, true) => { | 448 | (IdentifierKind::Lifetime, ReferenceKind::Lifetime) => mark::hit!(rename_lifetime), |
423 | return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name))) | 449 | (IdentifierKind::Lifetime, _) => { |
450 | mark::hit!(rename_not_an_ident_ref); | ||
451 | bail!("Invalid name `{}`: not an identifier", new_name) | ||
424 | } | 452 | } |
425 | _ => (), | 453 | (IdentifierKind::ToSelf, ReferenceKind::SelfKw) => { |
454 | unreachable!("rename_self_to_param should've been called instead") | ||
455 | } | ||
456 | (IdentifierKind::ToSelf, _) => { | ||
457 | mark::hit!(rename_to_self); | ||
458 | return rename_to_self(sema, position); | ||
459 | } | ||
460 | (IdentifierKind::Underscore, _) if !refs.references.is_empty() => { | ||
461 | mark::hit!(rename_underscore_multiple); | ||
462 | bail!("Cannot rename reference to `_` as it is being referenced multiple times") | ||
463 | } | ||
464 | (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => mark::hit!(rename_ident), | ||
426 | } | 465 | } |
427 | 466 | ||
428 | let edit = refs | 467 | let edit = refs |
429 | .into_iter() | 468 | .into_iter() |
430 | .map(|reference| source_edit_from_reference(sema, reference, new_name)) | 469 | .map(|(file_id, references)| { |
470 | source_edit_from_references(sema, file_id, &references, new_name) | ||
471 | }) | ||
431 | .collect::<Vec<_>>(); | 472 | .collect::<Vec<_>>(); |
432 | 473 | ||
433 | if edit.is_empty() { | ||
434 | return Err(RenameError("No references found at position".to_string())); | ||
435 | } | ||
436 | |||
437 | Ok(RangeInfo::new(range, SourceChange::from(edit))) | 474 | Ok(RangeInfo::new(range, SourceChange::from(edit))) |
438 | } | 475 | } |
439 | 476 | ||
@@ -462,9 +499,11 @@ mod tests { | |||
462 | text_edit_builder.replace(indel.delete, indel.insert); | 499 | text_edit_builder.replace(indel.delete, indel.insert); |
463 | } | 500 | } |
464 | } | 501 | } |
465 | let mut result = analysis.file_text(file_id.unwrap()).unwrap().to_string(); | 502 | if let Some(file_id) = file_id { |
466 | text_edit_builder.finish().apply(&mut result); | 503 | let mut result = analysis.file_text(file_id).unwrap().to_string(); |
467 | assert_eq_text!(ra_fixture_after, &*result); | 504 | text_edit_builder.finish().apply(&mut result); |
505 | assert_eq_text!(ra_fixture_after, &*result); | ||
506 | } | ||
468 | } | 507 | } |
469 | Err(err) => { | 508 | Err(err) => { |
470 | if ra_fixture_after.starts_with("error:") { | 509 | if ra_fixture_after.starts_with("error:") { |
@@ -530,6 +569,7 @@ mod tests { | |||
530 | 569 | ||
531 | #[test] | 570 | #[test] |
532 | fn test_rename_to_invalid_identifier_lifetime() { | 571 | fn test_rename_to_invalid_identifier_lifetime() { |
572 | mark::check!(rename_not_an_ident_ref); | ||
533 | check( | 573 | check( |
534 | "'foo", | 574 | "'foo", |
535 | r#"fn main() { let i$0 = 1; }"#, | 575 | r#"fn main() { let i$0 = 1; }"#, |
@@ -539,6 +579,7 @@ mod tests { | |||
539 | 579 | ||
540 | #[test] | 580 | #[test] |
541 | fn test_rename_to_invalid_identifier_lifetime2() { | 581 | fn test_rename_to_invalid_identifier_lifetime2() { |
582 | mark::check!(rename_not_a_lifetime_ident_ref); | ||
542 | check( | 583 | check( |
543 | "foo", | 584 | "foo", |
544 | r#"fn main<'a>(_: &'a$0 ()) {}"#, | 585 | r#"fn main<'a>(_: &'a$0 ()) {}"#, |
@@ -547,7 +588,27 @@ mod tests { | |||
547 | } | 588 | } |
548 | 589 | ||
549 | #[test] | 590 | #[test] |
591 | fn test_rename_to_underscore_invalid() { | ||
592 | mark::check!(rename_underscore_multiple); | ||
593 | check( | ||
594 | "_", | ||
595 | r#"fn main(foo$0: ()) {foo;}"#, | ||
596 | "error: Cannot rename reference to `_` as it is being referenced multiple times", | ||
597 | ); | ||
598 | } | ||
599 | |||
600 | #[test] | ||
601 | fn test_rename_mod_invalid() { | ||
602 | check( | ||
603 | "'foo", | ||
604 | r#"mod foo$0 {}"#, | ||
605 | "error: Invalid name `'foo`: cannot rename module to 'foo", | ||
606 | ); | ||
607 | } | ||
608 | |||
609 | #[test] | ||
550 | fn test_rename_for_local() { | 610 | fn test_rename_for_local() { |
611 | mark::check!(rename_ident); | ||
551 | check( | 612 | check( |
552 | "k", | 613 | "k", |
553 | r#" | 614 | r#" |
@@ -945,7 +1006,7 @@ use crate::foo$0::FooContent; | |||
945 | //- /lib.rs | 1006 | //- /lib.rs |
946 | mod fo$0o; | 1007 | mod fo$0o; |
947 | //- /foo/mod.rs | 1008 | //- /foo/mod.rs |
948 | // emtpy | 1009 | // empty |
949 | "#, | 1010 | "#, |
950 | expect![[r#" | 1011 | expect![[r#" |
951 | RangeInfo { | 1012 | RangeInfo { |
@@ -995,7 +1056,7 @@ mod fo$0o; | |||
995 | mod outer { mod fo$0o; } | 1056 | mod outer { mod fo$0o; } |
996 | 1057 | ||
997 | //- /outer/foo.rs | 1058 | //- /outer/foo.rs |
998 | // emtpy | 1059 | // empty |
999 | "#, | 1060 | "#, |
1000 | expect![[r#" | 1061 | expect![[r#" |
1001 | RangeInfo { | 1062 | RangeInfo { |
@@ -1178,6 +1239,7 @@ fn foo(f: foo::Foo) { | |||
1178 | 1239 | ||
1179 | #[test] | 1240 | #[test] |
1180 | fn test_parameter_to_self() { | 1241 | fn test_parameter_to_self() { |
1242 | mark::check!(rename_to_self); | ||
1181 | check( | 1243 | check( |
1182 | "self", | 1244 | "self", |
1183 | r#" | 1245 | r#" |
@@ -1481,6 +1543,7 @@ fn foo(Foo { i: bar }: foo) -> i32 { | |||
1481 | 1543 | ||
1482 | #[test] | 1544 | #[test] |
1483 | fn test_rename_lifetimes() { | 1545 | fn test_rename_lifetimes() { |
1546 | mark::check!(rename_lifetime); | ||
1484 | check( | 1547 | check( |
1485 | "'yeeee", | 1548 | "'yeeee", |
1486 | r#" | 1549 | r#" |
@@ -1565,4 +1628,24 @@ fn foo<'a>() -> &'a () { | |||
1565 | "#, | 1628 | "#, |
1566 | ) | 1629 | ) |
1567 | } | 1630 | } |
1631 | |||
1632 | #[test] | ||
1633 | fn test_self_to_self() { | ||
1634 | mark::check!(rename_self_to_self); | ||
1635 | check( | ||
1636 | "self", | ||
1637 | r#" | ||
1638 | struct Foo; | ||
1639 | impl Foo { | ||
1640 | fn foo(self$0) {} | ||
1641 | } | ||
1642 | "#, | ||
1643 | r#" | ||
1644 | struct Foo; | ||
1645 | impl Foo { | ||
1646 | fn foo(self) {} | ||
1647 | } | ||
1648 | "#, | ||
1649 | ) | ||
1650 | } | ||
1568 | } | 1651 | } |
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 557563d7e..f5ee7de86 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs | |||
@@ -3,7 +3,7 @@ use std::fmt; | |||
3 | use assists::utils::test_related_attribute; | 3 | use assists::utils::test_related_attribute; |
4 | use cfg::CfgExpr; | 4 | use cfg::CfgExpr; |
5 | use hir::{AsAssocItem, HasAttrs, HasSource, Semantics}; | 5 | use hir::{AsAssocItem, HasAttrs, HasSource, Semantics}; |
6 | use ide_db::RootDatabase; | 6 | use ide_db::{defs::Definition, RootDatabase}; |
7 | use itertools::Itertools; | 7 | use itertools::Itertools; |
8 | use syntax::{ | 8 | use syntax::{ |
9 | ast::{self, AstNode, AttrsOwner, ModuleItemOwner}, | 9 | ast::{self, AstNode, AttrsOwner, ModuleItemOwner}, |
@@ -96,21 +96,26 @@ impl Runnable { | |||
96 | pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { | 96 | pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { |
97 | let sema = Semantics::new(db); | 97 | let sema = Semantics::new(db); |
98 | let source_file = sema.parse(file_id); | 98 | let source_file = sema.parse(file_id); |
99 | source_file.syntax().descendants().filter_map(|i| runnable(&sema, i)).collect() | 99 | source_file |
100 | } | 100 | .syntax() |
101 | 101 | .descendants() | |
102 | pub(crate) fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> { | 102 | .filter_map(|item| { |
103 | let runnable_item = match_ast! { | 103 | let runnable = match_ast! { |
104 | match (item.clone()) { | 104 | match item { |
105 | ast::Fn(func) => { | 105 | ast::Fn(func) => { |
106 | let def = sema.to_def(&func)?; | 106 | let def = sema.to_def(&func)?; |
107 | runnable_fn(sema, def) | 107 | runnable_fn(&sema, def) |
108 | }, | 108 | }, |
109 | ast::Module(it) => runnable_mod(sema, it), | 109 | ast::Module(it) => runnable_mod(&sema, it), |
110 | _ => None, | 110 | _ => None, |
111 | } | 111 | } |
112 | }; | 112 | }; |
113 | runnable_item.or_else(|| runnable_doctest(sema, item)) | 113 | runnable.or_else(|| match doc_owner_to_def(&sema, item)? { |
114 | Definition::ModuleDef(def) => module_def_doctest(&sema, def), | ||
115 | _ => None, | ||
116 | }) | ||
117 | }) | ||
118 | .collect() | ||
114 | } | 119 | } |
115 | 120 | ||
116 | pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> { | 121 | pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> { |
@@ -145,20 +150,49 @@ pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> | |||
145 | Some(Runnable { nav, kind, cfg }) | 150 | Some(Runnable { nav, kind, cfg }) |
146 | } | 151 | } |
147 | 152 | ||
148 | fn runnable_doctest(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> { | 153 | pub(crate) fn runnable_mod( |
149 | match_ast! { | 154 | sema: &Semantics<RootDatabase>, |
155 | module: ast::Module, | ||
156 | ) -> Option<Runnable> { | ||
157 | if !has_test_function_or_multiple_test_submodules(&module) { | ||
158 | return None; | ||
159 | } | ||
160 | let module_def = sema.to_def(&module)?; | ||
161 | |||
162 | let path = module_def | ||
163 | .path_to_root(sema.db) | ||
164 | .into_iter() | ||
165 | .rev() | ||
166 | .filter_map(|it| it.name(sema.db)) | ||
167 | .join("::"); | ||
168 | |||
169 | let def = sema.to_def(&module)?; | ||
170 | let attrs = def.attrs(sema.db); | ||
171 | let cfg = attrs.cfg(); | ||
172 | let nav = module_def.to_nav(sema.db); | ||
173 | Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg }) | ||
174 | } | ||
175 | |||
176 | // FIXME: figure out a proper API here. | ||
177 | pub(crate) fn doc_owner_to_def( | ||
178 | sema: &Semantics<RootDatabase>, | ||
179 | item: SyntaxNode, | ||
180 | ) -> Option<Definition> { | ||
181 | let res: hir::ModuleDef = match_ast! { | ||
150 | match item { | 182 | match item { |
151 | ast::Fn(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), | 183 | ast::SourceFile(it) => sema.scope(&item).module()?.into(), |
152 | ast::Struct(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), | 184 | ast::Fn(it) => sema.to_def(&it)?.into(), |
153 | ast::Enum(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), | 185 | ast::Struct(it) => sema.to_def(&it)?.into(), |
154 | ast::Union(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), | 186 | ast::Enum(it) => sema.to_def(&it)?.into(), |
155 | ast::Trait(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), | 187 | ast::Union(it) => sema.to_def(&it)?.into(), |
156 | ast::Const(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), | 188 | ast::Trait(it) => sema.to_def(&it)?.into(), |
157 | ast::Static(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), | 189 | ast::Const(it) => sema.to_def(&it)?.into(), |
158 | ast::TypeAlias(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), | 190 | ast::Static(it) => sema.to_def(&it)?.into(), |
159 | _ => None, | 191 | ast::TypeAlias(it) => sema.to_def(&it)?.into(), |
192 | _ => return None, | ||
160 | } | 193 | } |
161 | } | 194 | }; |
195 | Some(Definition::ModuleDef(res)) | ||
162 | } | 196 | } |
163 | 197 | ||
164 | fn module_def_doctest(sema: &Semantics<RootDatabase>, def: hir::ModuleDef) -> Option<Runnable> { | 198 | fn module_def_doctest(sema: &Semantics<RootDatabase>, def: hir::ModuleDef) -> Option<Runnable> { |
@@ -253,26 +287,6 @@ fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool { | |||
253 | }) | 287 | }) |
254 | } | 288 | } |
255 | 289 | ||
256 | fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<Runnable> { | ||
257 | if !has_test_function_or_multiple_test_submodules(&module) { | ||
258 | return None; | ||
259 | } | ||
260 | let module_def = sema.to_def(&module)?; | ||
261 | |||
262 | let path = module_def | ||
263 | .path_to_root(sema.db) | ||
264 | .into_iter() | ||
265 | .rev() | ||
266 | .filter_map(|it| it.name(sema.db)) | ||
267 | .join("::"); | ||
268 | |||
269 | let def = sema.to_def(&module)?; | ||
270 | let attrs = def.attrs(sema.db); | ||
271 | let cfg = attrs.cfg(); | ||
272 | let nav = module_def.to_nav(sema.db); | ||
273 | Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg }) | ||
274 | } | ||
275 | |||
276 | // We could create runnables for modules with number_of_test_submodules > 0, | 290 | // We could create runnables for modules with number_of_test_submodules > 0, |
277 | // but that bloats the runnables for no real benefit, since all tests can be run by the submodule already | 291 | // but that bloats the runnables for no real benefit, since all tests can be run by the submodule already |
278 | fn has_test_function_or_multiple_test_submodules(module: &ast::Module) -> bool { | 292 | fn has_test_function_or_multiple_test_submodules(module: &ast::Module) -> bool { |
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index ba0085244..f2d4da78d 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs | |||
@@ -1,35 +1,39 @@ | |||
1 | pub(crate) mod tags; | ||
2 | |||
3 | mod highlights; | ||
4 | mod injector; | ||
5 | |||
6 | mod highlight; | ||
1 | mod format; | 7 | mod format; |
2 | mod html; | ||
3 | mod injection; | ||
4 | mod macro_rules; | 8 | mod macro_rules; |
5 | pub(crate) mod tags; | 9 | mod inject; |
10 | |||
11 | mod html; | ||
6 | #[cfg(test)] | 12 | #[cfg(test)] |
7 | mod tests; | 13 | mod tests; |
8 | 14 | ||
9 | use hir::{AsAssocItem, Local, Name, Semantics, VariantDef}; | 15 | use hir::{Name, Semantics}; |
10 | use ide_db::{ | 16 | use ide_db::RootDatabase; |
11 | defs::{Definition, NameClass, NameRefClass}, | ||
12 | RootDatabase, | ||
13 | }; | ||
14 | use rustc_hash::FxHashMap; | 17 | use rustc_hash::FxHashMap; |
15 | use syntax::{ | 18 | use syntax::{ |
16 | ast::{self, HasFormatSpecifier}, | 19 | ast::{self, HasFormatSpecifier}, |
17 | AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, | 20 | AstNode, AstToken, Direction, NodeOrToken, |
18 | SyntaxKind::{self, *}, | 21 | SyntaxKind::*, |
19 | SyntaxNode, SyntaxToken, TextRange, WalkEvent, T, | 22 | SyntaxNode, TextRange, WalkEvent, T, |
20 | }; | 23 | }; |
21 | 24 | ||
22 | use crate::{ | 25 | use crate::{ |
23 | syntax_highlighting::{ | 26 | syntax_highlighting::{ |
24 | format::FormatStringHighlighter, macro_rules::MacroRulesHighlighter, tags::Highlight, | 27 | format::highlight_format_string, highlights::Highlights, |
28 | macro_rules::MacroRulesHighlighter, tags::Highlight, | ||
25 | }, | 29 | }, |
26 | FileId, HighlightModifier, HighlightTag, SymbolKind, | 30 | FileId, HlMod, HlTag, SymbolKind, |
27 | }; | 31 | }; |
28 | 32 | ||
29 | pub(crate) use html::highlight_as_html; | 33 | pub(crate) use html::highlight_as_html; |
30 | 34 | ||
31 | #[derive(Debug, Clone)] | 35 | #[derive(Debug, Clone, Copy)] |
32 | pub struct HighlightedRange { | 36 | pub struct HlRange { |
33 | pub range: TextRange, | 37 | pub range: TextRange, |
34 | pub highlight: Highlight, | 38 | pub highlight: Highlight, |
35 | pub binding_hash: Option<u64>, | 39 | pub binding_hash: Option<u64>, |
@@ -49,7 +53,7 @@ pub(crate) fn highlight( | |||
49 | file_id: FileId, | 53 | file_id: FileId, |
50 | range_to_highlight: Option<TextRange>, | 54 | range_to_highlight: Option<TextRange>, |
51 | syntactic_name_ref_highlighting: bool, | 55 | syntactic_name_ref_highlighting: bool, |
52 | ) -> Vec<HighlightedRange> { | 56 | ) -> Vec<HlRange> { |
53 | let _p = profile::span("highlight"); | 57 | let _p = profile::span("highlight"); |
54 | let sema = Semantics::new(db); | 58 | let sema = Semantics::new(db); |
55 | 59 | ||
@@ -68,28 +72,30 @@ pub(crate) fn highlight( | |||
68 | } | 72 | } |
69 | }; | 73 | }; |
70 | 74 | ||
75 | let mut hl = highlights::Highlights::new(range_to_highlight); | ||
76 | traverse(&mut hl, &sema, &root, range_to_highlight, syntactic_name_ref_highlighting); | ||
77 | hl.to_vec() | ||
78 | } | ||
79 | |||
80 | fn traverse( | ||
81 | hl: &mut Highlights, | ||
82 | sema: &Semantics<RootDatabase>, | ||
83 | root: &SyntaxNode, | ||
84 | range_to_highlight: TextRange, | ||
85 | syntactic_name_ref_highlighting: bool, | ||
86 | ) { | ||
71 | let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default(); | 87 | let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default(); |
72 | // We use a stack for the DFS traversal below. | ||
73 | // When we leave a node, the we use it to flatten the highlighted ranges. | ||
74 | let mut stack = HighlightedRangeStack::new(); | ||
75 | 88 | ||
76 | let mut current_macro_call: Option<ast::MacroCall> = None; | 89 | let mut current_macro_call: Option<ast::MacroCall> = None; |
77 | let mut current_macro_rules: Option<ast::MacroRules> = None; | 90 | let mut current_macro_rules: Option<ast::MacroRules> = None; |
78 | let mut format_string_highlighter = FormatStringHighlighter::default(); | ||
79 | let mut macro_rules_highlighter = MacroRulesHighlighter::default(); | 91 | let mut macro_rules_highlighter = MacroRulesHighlighter::default(); |
80 | let mut inside_attribute = false; | 92 | let mut inside_attribute = false; |
81 | 93 | ||
82 | // Walk all nodes, keeping track of whether we are inside a macro or not. | 94 | // Walk all nodes, keeping track of whether we are inside a macro or not. |
83 | // If in macro, expand it first and highlight the expanded code. | 95 | // If in macro, expand it first and highlight the expanded code. |
84 | for event in root.preorder_with_tokens() { | 96 | for event in root.preorder_with_tokens() { |
85 | match &event { | ||
86 | WalkEvent::Enter(_) => stack.push(), | ||
87 | WalkEvent::Leave(_) => stack.pop(), | ||
88 | }; | ||
89 | |||
90 | let event_range = match &event { | 97 | let event_range = match &event { |
91 | WalkEvent::Enter(it) => it.text_range(), | 98 | WalkEvent::Enter(it) | WalkEvent::Leave(it) => it.text_range(), |
92 | WalkEvent::Leave(it) => it.text_range(), | ||
93 | }; | 99 | }; |
94 | 100 | ||
95 | // Element outside of the viewport, no need to highlight | 101 | // Element outside of the viewport, no need to highlight |
@@ -101,9 +107,9 @@ pub(crate) fn highlight( | |||
101 | match event.clone().map(|it| it.into_node().and_then(ast::MacroCall::cast)) { | 107 | match event.clone().map(|it| it.into_node().and_then(ast::MacroCall::cast)) { |
102 | WalkEvent::Enter(Some(mc)) => { | 108 | WalkEvent::Enter(Some(mc)) => { |
103 | if let Some(range) = macro_call_range(&mc) { | 109 | if let Some(range) = macro_call_range(&mc) { |
104 | stack.add(HighlightedRange { | 110 | hl.add(HlRange { |
105 | range, | 111 | range, |
106 | highlight: HighlightTag::Symbol(SymbolKind::Macro).into(), | 112 | highlight: HlTag::Symbol(SymbolKind::Macro).into(), |
107 | binding_hash: None, | 113 | binding_hash: None, |
108 | }); | 114 | }); |
109 | } | 115 | } |
@@ -113,7 +119,6 @@ pub(crate) fn highlight( | |||
113 | WalkEvent::Leave(Some(mc)) => { | 119 | WalkEvent::Leave(Some(mc)) => { |
114 | assert_eq!(current_macro_call, Some(mc)); | 120 | assert_eq!(current_macro_call, Some(mc)); |
115 | current_macro_call = None; | 121 | current_macro_call = None; |
116 | format_string_highlighter = FormatStringHighlighter::default(); | ||
117 | } | 122 | } |
118 | _ => (), | 123 | _ => (), |
119 | } | 124 | } |
@@ -131,33 +136,24 @@ pub(crate) fn highlight( | |||
131 | } | 136 | } |
132 | _ => (), | 137 | _ => (), |
133 | } | 138 | } |
134 | |||
135 | match &event { | 139 | match &event { |
136 | // Check for Rust code in documentation | ||
137 | WalkEvent::Leave(NodeOrToken::Node(node)) => { | ||
138 | if ast::Attr::can_cast(node.kind()) { | ||
139 | inside_attribute = false | ||
140 | } | ||
141 | if let Some((doctest, range_mapping, new_comments)) = | ||
142 | injection::extract_doc_comments(node) | ||
143 | { | ||
144 | injection::highlight_doc_comment( | ||
145 | doctest, | ||
146 | range_mapping, | ||
147 | new_comments, | ||
148 | &mut stack, | ||
149 | ); | ||
150 | } | ||
151 | } | ||
152 | WalkEvent::Enter(NodeOrToken::Node(node)) if ast::Attr::can_cast(node.kind()) => { | 140 | WalkEvent::Enter(NodeOrToken::Node(node)) if ast::Attr::can_cast(node.kind()) => { |
153 | inside_attribute = true | 141 | inside_attribute = true |
154 | } | 142 | } |
143 | WalkEvent::Leave(NodeOrToken::Node(node)) if ast::Attr::can_cast(node.kind()) => { | ||
144 | inside_attribute = false | ||
145 | } | ||
155 | _ => (), | 146 | _ => (), |
156 | } | 147 | } |
157 | 148 | ||
158 | let element = match event { | 149 | let element = match event { |
159 | WalkEvent::Enter(it) => it, | 150 | WalkEvent::Enter(it) => it, |
160 | WalkEvent::Leave(_) => continue, | 151 | WalkEvent::Leave(it) => { |
152 | if let Some(node) = it.as_node() { | ||
153 | inject::doc_comment(hl, node); | ||
154 | } | ||
155 | continue; | ||
156 | } | ||
161 | }; | 157 | }; |
162 | 158 | ||
163 | let range = element.text_range(); | 159 | let range = element.text_range(); |
@@ -177,8 +173,6 @@ pub(crate) fn highlight( | |||
177 | let token = sema.descend_into_macros(token.clone()); | 173 | let token = sema.descend_into_macros(token.clone()); |
178 | let parent = token.parent(); | 174 | let parent = token.parent(); |
179 | 175 | ||
180 | format_string_highlighter.check_for_format_string(&parent); | ||
181 | |||
182 | // We only care Name and Name_ref | 176 | // We only care Name and Name_ref |
183 | match (token.kind(), parent.kind()) { | 177 | match (token.kind(), parent.kind()) { |
184 | (IDENT, NAME) | (IDENT, NAME_REF) => parent.into(), | 178 | (IDENT, NAME) | (IDENT, NAME_REF) => parent.into(), |
@@ -191,213 +185,45 @@ pub(crate) fn highlight( | |||
191 | if let Some(token) = element.as_token().cloned().and_then(ast::String::cast) { | 185 | if let Some(token) = element.as_token().cloned().and_then(ast::String::cast) { |
192 | if token.is_raw() { | 186 | if token.is_raw() { |
193 | let expanded = element_to_highlight.as_token().unwrap().clone(); | 187 | let expanded = element_to_highlight.as_token().unwrap().clone(); |
194 | if injection::highlight_injection(&mut stack, &sema, token, expanded).is_some() { | 188 | if inject::ra_fixture(hl, &sema, token, expanded).is_some() { |
195 | continue; | 189 | continue; |
196 | } | 190 | } |
197 | } | 191 | } |
198 | } | 192 | } |
199 | 193 | ||
200 | if let Some((mut highlight, binding_hash)) = highlight_element( | 194 | if let Some(_) = macro_rules_highlighter.highlight(element_to_highlight.clone()) { |
195 | continue; | ||
196 | } | ||
197 | |||
198 | if let Some((mut highlight, binding_hash)) = highlight::element( | ||
201 | &sema, | 199 | &sema, |
202 | &mut bindings_shadow_count, | 200 | &mut bindings_shadow_count, |
203 | syntactic_name_ref_highlighting, | 201 | syntactic_name_ref_highlighting, |
204 | element_to_highlight.clone(), | 202 | element_to_highlight.clone(), |
205 | ) { | 203 | ) { |
206 | if inside_attribute { | 204 | if inside_attribute { |
207 | highlight = highlight | HighlightModifier::Attribute; | 205 | highlight = highlight | HlMod::Attribute; |
208 | } | ||
209 | |||
210 | if macro_rules_highlighter.highlight(element_to_highlight.clone()).is_none() { | ||
211 | stack.add(HighlightedRange { range, highlight, binding_hash }); | ||
212 | } | 206 | } |
213 | 207 | ||
214 | if let Some(string) = | 208 | hl.add(HlRange { range, highlight, binding_hash }); |
215 | element_to_highlight.as_token().cloned().and_then(ast::String::cast) | ||
216 | { | ||
217 | format_string_highlighter.highlight_format_string(&mut stack, &string, range); | ||
218 | // Highlight escape sequences | ||
219 | if let Some(char_ranges) = string.char_ranges() { | ||
220 | stack.push(); | ||
221 | for (piece_range, _) in char_ranges.iter().filter(|(_, char)| char.is_ok()) { | ||
222 | if string.text()[piece_range.start().into()..].starts_with('\\') { | ||
223 | stack.add(HighlightedRange { | ||
224 | range: piece_range + range.start(), | ||
225 | highlight: HighlightTag::EscapeSequence.into(), | ||
226 | binding_hash: None, | ||
227 | }); | ||
228 | } | ||
229 | } | ||
230 | stack.pop_and_inject(None); | ||
231 | } | ||
232 | } | ||
233 | } | 209 | } |
234 | } | ||
235 | |||
236 | stack.flattened() | ||
237 | } | ||
238 | |||
239 | #[derive(Debug)] | ||
240 | struct HighlightedRangeStack { | ||
241 | stack: Vec<Vec<HighlightedRange>>, | ||
242 | } | ||
243 | |||
244 | /// We use a stack to implement the flattening logic for the highlighted | ||
245 | /// syntax ranges. | ||
246 | impl HighlightedRangeStack { | ||
247 | fn new() -> Self { | ||
248 | Self { stack: vec![Vec::new()] } | ||
249 | } | ||
250 | |||
251 | fn push(&mut self) { | ||
252 | self.stack.push(Vec::new()); | ||
253 | } | ||
254 | |||
255 | /// Flattens the highlighted ranges. | ||
256 | /// | ||
257 | /// For example `#[cfg(feature = "foo")]` contains the nested ranges: | ||
258 | /// 1) parent-range: Attribute [0, 23) | ||
259 | /// 2) child-range: String [16, 21) | ||
260 | /// | ||
261 | /// The following code implements the flattening, for our example this results to: | ||
262 | /// `[Attribute [0, 16), String [16, 21), Attribute [21, 23)]` | ||
263 | fn pop(&mut self) { | ||
264 | let children = self.stack.pop().unwrap(); | ||
265 | let prev = self.stack.last_mut().unwrap(); | ||
266 | let needs_flattening = !children.is_empty() | ||
267 | && !prev.is_empty() | ||
268 | && prev.last().unwrap().range.contains_range(children.first().unwrap().range); | ||
269 | if !needs_flattening { | ||
270 | prev.extend(children); | ||
271 | } else { | ||
272 | let mut parent = prev.pop().unwrap(); | ||
273 | for ele in children { | ||
274 | assert!(parent.range.contains_range(ele.range)); | ||
275 | |||
276 | let cloned = Self::intersect(&mut parent, &ele); | ||
277 | if !parent.range.is_empty() { | ||
278 | prev.push(parent); | ||
279 | } | ||
280 | prev.push(ele); | ||
281 | parent = cloned; | ||
282 | } | ||
283 | if !parent.range.is_empty() { | ||
284 | prev.push(parent); | ||
285 | } | ||
286 | } | ||
287 | } | ||
288 | |||
289 | /// Intersects the `HighlightedRange` `parent` with `child`. | ||
290 | /// `parent` is mutated in place, becoming the range before `child`. | ||
291 | /// Returns the range (of the same type as `parent`) *after* `child`. | ||
292 | fn intersect(parent: &mut HighlightedRange, child: &HighlightedRange) -> HighlightedRange { | ||
293 | assert!(parent.range.contains_range(child.range)); | ||
294 | |||
295 | let mut cloned = parent.clone(); | ||
296 | parent.range = TextRange::new(parent.range.start(), child.range.start()); | ||
297 | cloned.range = TextRange::new(child.range.end(), cloned.range.end()); | ||
298 | 210 | ||
299 | cloned | 211 | if let Some(string) = element_to_highlight.as_token().cloned().and_then(ast::String::cast) { |
300 | } | 212 | highlight_format_string(hl, &string, range); |
301 | 213 | // Highlight escape sequences | |
302 | /// Remove the `HighlightRange` of `parent` that's currently covered by `child`. | 214 | if let Some(char_ranges) = string.char_ranges() { |
303 | fn intersect_partial(parent: &mut HighlightedRange, child: &HighlightedRange) { | 215 | for (piece_range, _) in char_ranges.iter().filter(|(_, char)| char.is_ok()) { |
304 | assert!( | 216 | if string.text()[piece_range.start().into()..].starts_with('\\') { |
305 | parent.range.start() <= child.range.start() | 217 | hl.add(HlRange { |
306 | && parent.range.end() >= child.range.start() | 218 | range: piece_range + range.start(), |
307 | && child.range.end() > parent.range.end() | 219 | highlight: HlTag::EscapeSequence.into(), |
308 | ); | 220 | binding_hash: None, |
309 | 221 | }); | |
310 | parent.range = TextRange::new(parent.range.start(), child.range.start()); | ||
311 | } | ||
312 | |||
313 | /// Similar to `pop`, but can modify arbitrary prior ranges (where `pop`) | ||
314 | /// can only modify the last range currently on the stack. | ||
315 | /// Can be used to do injections that span multiple ranges, like the | ||
316 | /// doctest injection below. | ||
317 | /// If `overwrite_parent` is non-optional, the highlighting of the parent range | ||
318 | /// is overwritten with the argument. | ||
319 | /// | ||
320 | /// Note that `pop` can be simulated by `pop_and_inject(false)` but the | ||
321 | /// latter is computationally more expensive. | ||
322 | fn pop_and_inject(&mut self, overwrite_parent: Option<Highlight>) { | ||
323 | let mut children = self.stack.pop().unwrap(); | ||
324 | let prev = self.stack.last_mut().unwrap(); | ||
325 | children.sort_by_key(|range| range.range.start()); | ||
326 | prev.sort_by_key(|range| range.range.start()); | ||
327 | |||
328 | for child in children { | ||
329 | if let Some(idx) = | ||
330 | prev.iter().position(|parent| parent.range.contains_range(child.range)) | ||
331 | { | ||
332 | if let Some(tag) = overwrite_parent { | ||
333 | prev[idx].highlight = tag; | ||
334 | } | ||
335 | |||
336 | let cloned = Self::intersect(&mut prev[idx], &child); | ||
337 | let insert_idx = if prev[idx].range.is_empty() { | ||
338 | prev.remove(idx); | ||
339 | idx | ||
340 | } else { | ||
341 | idx + 1 | ||
342 | }; | ||
343 | prev.insert(insert_idx, child); | ||
344 | if !cloned.range.is_empty() { | ||
345 | prev.insert(insert_idx + 1, cloned); | ||
346 | } | ||
347 | } else { | ||
348 | let maybe_idx = | ||
349 | prev.iter().position(|parent| parent.range.contains(child.range.start())); | ||
350 | match (overwrite_parent, maybe_idx) { | ||
351 | (Some(_), Some(idx)) => { | ||
352 | Self::intersect_partial(&mut prev[idx], &child); | ||
353 | let insert_idx = if prev[idx].range.is_empty() { | ||
354 | prev.remove(idx); | ||
355 | idx | ||
356 | } else { | ||
357 | idx + 1 | ||
358 | }; | ||
359 | prev.insert(insert_idx, child); | ||
360 | } | ||
361 | (_, None) => { | ||
362 | let idx = prev | ||
363 | .binary_search_by_key(&child.range.start(), |range| range.range.start()) | ||
364 | .unwrap_or_else(|x| x); | ||
365 | prev.insert(idx, child); | ||
366 | } | ||
367 | _ => { | ||
368 | unreachable!("child range should be completely contained in parent range"); | ||
369 | } | 222 | } |
370 | } | 223 | } |
371 | } | 224 | } |
372 | } | 225 | } |
373 | } | 226 | } |
374 | |||
375 | fn add(&mut self, range: HighlightedRange) { | ||
376 | self.stack | ||
377 | .last_mut() | ||
378 | .expect("during DFS traversal, the stack must not be empty") | ||
379 | .push(range) | ||
380 | } | ||
381 | |||
382 | fn flattened(mut self) -> Vec<HighlightedRange> { | ||
383 | assert_eq!( | ||
384 | self.stack.len(), | ||
385 | 1, | ||
386 | "after DFS traversal, the stack should only contain a single element" | ||
387 | ); | ||
388 | let mut res = self.stack.pop().unwrap(); | ||
389 | res.sort_by_key(|range| range.range.start()); | ||
390 | // Check that ranges are sorted and disjoint | ||
391 | for (left, right) in res.iter().zip(res.iter().skip(1)) { | ||
392 | assert!( | ||
393 | left.range.end() <= right.range.start(), | ||
394 | "left: {:#?}, right: {:#?}", | ||
395 | left, | ||
396 | right | ||
397 | ); | ||
398 | } | ||
399 | res | ||
400 | } | ||
401 | } | 227 | } |
402 | 228 | ||
403 | fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> { | 229 | fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> { |
@@ -415,524 +241,3 @@ fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> { | |||
415 | 241 | ||
416 | Some(TextRange::new(range_start, range_end)) | 242 | Some(TextRange::new(range_start, range_end)) |
417 | } | 243 | } |
418 | |||
419 | /// Returns true if the parent nodes of `node` all match the `SyntaxKind`s in `kinds` exactly. | ||
420 | fn parents_match(mut node: NodeOrToken<SyntaxNode, SyntaxToken>, mut kinds: &[SyntaxKind]) -> bool { | ||
421 | while let (Some(parent), [kind, rest @ ..]) = (&node.parent(), kinds) { | ||
422 | if parent.kind() != *kind { | ||
423 | return false; | ||
424 | } | ||
425 | |||
426 | // FIXME: Would be nice to get parent out of the match, but binding by-move and by-value | ||
427 | // in the same pattern is unstable: rust-lang/rust#68354. | ||
428 | node = node.parent().unwrap().into(); | ||
429 | kinds = rest; | ||
430 | } | ||
431 | |||
432 | // Only true if we matched all expected kinds | ||
433 | kinds.len() == 0 | ||
434 | } | ||
435 | |||
436 | fn is_consumed_lvalue( | ||
437 | node: NodeOrToken<SyntaxNode, SyntaxToken>, | ||
438 | local: &Local, | ||
439 | db: &RootDatabase, | ||
440 | ) -> bool { | ||
441 | // When lvalues are passed as arguments and they're not Copy, then mark them as Consuming. | ||
442 | parents_match(node, &[PATH_SEGMENT, PATH, PATH_EXPR, ARG_LIST]) && !local.ty(db).is_copy(db) | ||
443 | } | ||
444 | |||
445 | fn highlight_element( | ||
446 | sema: &Semantics<RootDatabase>, | ||
447 | bindings_shadow_count: &mut FxHashMap<Name, u32>, | ||
448 | syntactic_name_ref_highlighting: bool, | ||
449 | element: SyntaxElement, | ||
450 | ) -> Option<(Highlight, Option<u64>)> { | ||
451 | let db = sema.db; | ||
452 | let mut binding_hash = None; | ||
453 | let highlight: Highlight = match element.kind() { | ||
454 | FN => { | ||
455 | bindings_shadow_count.clear(); | ||
456 | return None; | ||
457 | } | ||
458 | |||
459 | // Highlight definitions depending on the "type" of the definition. | ||
460 | NAME => { | ||
461 | let name = element.into_node().and_then(ast::Name::cast).unwrap(); | ||
462 | let name_kind = NameClass::classify(sema, &name); | ||
463 | |||
464 | if let Some(NameClass::Definition(Definition::Local(local))) = &name_kind { | ||
465 | if let Some(name) = local.name(db) { | ||
466 | let shadow_count = bindings_shadow_count.entry(name.clone()).or_default(); | ||
467 | *shadow_count += 1; | ||
468 | binding_hash = Some(calc_binding_hash(&name, *shadow_count)) | ||
469 | } | ||
470 | }; | ||
471 | |||
472 | match name_kind { | ||
473 | Some(NameClass::ExternCrate(_)) => HighlightTag::Symbol(SymbolKind::Module).into(), | ||
474 | Some(NameClass::Definition(def)) => { | ||
475 | highlight_def(db, def) | HighlightModifier::Definition | ||
476 | } | ||
477 | Some(NameClass::ConstReference(def)) => highlight_def(db, def), | ||
478 | Some(NameClass::PatFieldShorthand { field_ref, .. }) => { | ||
479 | let mut h = HighlightTag::Symbol(SymbolKind::Field).into(); | ||
480 | if let Definition::Field(field) = field_ref { | ||
481 | if let VariantDef::Union(_) = field.parent_def(db) { | ||
482 | h |= HighlightModifier::Unsafe; | ||
483 | } | ||
484 | } | ||
485 | |||
486 | h | ||
487 | } | ||
488 | None => highlight_name_by_syntax(name) | HighlightModifier::Definition, | ||
489 | } | ||
490 | } | ||
491 | |||
492 | // Highlight references like the definitions they resolve to | ||
493 | NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => { | ||
494 | // even though we track whether we are in an attribute or not we still need this special case | ||
495 | // as otherwise we would emit unresolved references for name refs inside attributes | ||
496 | Highlight::from(HighlightTag::Symbol(SymbolKind::Function)) | ||
497 | } | ||
498 | NAME_REF => { | ||
499 | let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); | ||
500 | highlight_func_by_name_ref(sema, &name_ref).unwrap_or_else(|| { | ||
501 | match NameRefClass::classify(sema, &name_ref) { | ||
502 | Some(name_kind) => match name_kind { | ||
503 | NameRefClass::ExternCrate(_) => { | ||
504 | HighlightTag::Symbol(SymbolKind::Module).into() | ||
505 | } | ||
506 | NameRefClass::Definition(def) => { | ||
507 | if let Definition::Local(local) = &def { | ||
508 | if let Some(name) = local.name(db) { | ||
509 | let shadow_count = | ||
510 | bindings_shadow_count.entry(name.clone()).or_default(); | ||
511 | binding_hash = Some(calc_binding_hash(&name, *shadow_count)) | ||
512 | } | ||
513 | }; | ||
514 | |||
515 | let mut h = highlight_def(db, def); | ||
516 | |||
517 | if let Definition::Local(local) = &def { | ||
518 | if is_consumed_lvalue(name_ref.syntax().clone().into(), local, db) { | ||
519 | h |= HighlightModifier::Consuming; | ||
520 | } | ||
521 | } | ||
522 | |||
523 | if let Some(parent) = name_ref.syntax().parent() { | ||
524 | if matches!(parent.kind(), FIELD_EXPR | RECORD_PAT_FIELD) { | ||
525 | if let Definition::Field(field) = def { | ||
526 | if let VariantDef::Union(_) = field.parent_def(db) { | ||
527 | h |= HighlightModifier::Unsafe; | ||
528 | } | ||
529 | } | ||
530 | } | ||
531 | } | ||
532 | |||
533 | h | ||
534 | } | ||
535 | NameRefClass::FieldShorthand { .. } => { | ||
536 | HighlightTag::Symbol(SymbolKind::Field).into() | ||
537 | } | ||
538 | }, | ||
539 | None if syntactic_name_ref_highlighting => { | ||
540 | highlight_name_ref_by_syntax(name_ref, sema) | ||
541 | } | ||
542 | None => HighlightTag::UnresolvedReference.into(), | ||
543 | } | ||
544 | }) | ||
545 | } | ||
546 | |||
547 | // Simple token-based highlighting | ||
548 | COMMENT => { | ||
549 | let comment = element.into_token().and_then(ast::Comment::cast)?; | ||
550 | let h = HighlightTag::Comment; | ||
551 | match comment.kind().doc { | ||
552 | Some(_) => h | HighlightModifier::Documentation, | ||
553 | None => h.into(), | ||
554 | } | ||
555 | } | ||
556 | STRING | BYTE_STRING => HighlightTag::StringLiteral.into(), | ||
557 | ATTR => HighlightTag::Attribute.into(), | ||
558 | INT_NUMBER | FLOAT_NUMBER => HighlightTag::NumericLiteral.into(), | ||
559 | BYTE => HighlightTag::ByteLiteral.into(), | ||
560 | CHAR => HighlightTag::CharLiteral.into(), | ||
561 | QUESTION => Highlight::new(HighlightTag::Operator) | HighlightModifier::ControlFlow, | ||
562 | LIFETIME => { | ||
563 | let lifetime = element.into_node().and_then(ast::Lifetime::cast).unwrap(); | ||
564 | |||
565 | match NameClass::classify_lifetime(sema, &lifetime) { | ||
566 | Some(NameClass::Definition(def)) => { | ||
567 | highlight_def(db, def) | HighlightModifier::Definition | ||
568 | } | ||
569 | None => match NameRefClass::classify_lifetime(sema, &lifetime) { | ||
570 | Some(NameRefClass::Definition(def)) => highlight_def(db, def), | ||
571 | _ => Highlight::new(HighlightTag::Symbol(SymbolKind::LifetimeParam)), | ||
572 | }, | ||
573 | _ => { | ||
574 | Highlight::new(HighlightTag::Symbol(SymbolKind::LifetimeParam)) | ||
575 | | HighlightModifier::Definition | ||
576 | } | ||
577 | } | ||
578 | } | ||
579 | p if p.is_punct() => match p { | ||
580 | T![&] => { | ||
581 | let h = HighlightTag::Operator.into(); | ||
582 | let is_unsafe = element | ||
583 | .parent() | ||
584 | .and_then(ast::RefExpr::cast) | ||
585 | .map(|ref_expr| sema.is_unsafe_ref_expr(&ref_expr)) | ||
586 | .unwrap_or(false); | ||
587 | if is_unsafe { | ||
588 | h | HighlightModifier::Unsafe | ||
589 | } else { | ||
590 | h | ||
591 | } | ||
592 | } | ||
593 | T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.] => { | ||
594 | HighlightTag::Operator.into() | ||
595 | } | ||
596 | T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => { | ||
597 | HighlightTag::Symbol(SymbolKind::Macro).into() | ||
598 | } | ||
599 | T![!] if element.parent().and_then(ast::NeverType::cast).is_some() => { | ||
600 | HighlightTag::BuiltinType.into() | ||
601 | } | ||
602 | T![*] if element.parent().and_then(ast::PtrType::cast).is_some() => { | ||
603 | HighlightTag::Keyword.into() | ||
604 | } | ||
605 | T![*] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => { | ||
606 | let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?; | ||
607 | |||
608 | let expr = prefix_expr.expr()?; | ||
609 | let ty = sema.type_of_expr(&expr)?; | ||
610 | if ty.is_raw_ptr() { | ||
611 | HighlightTag::Operator | HighlightModifier::Unsafe | ||
612 | } else if let Some(ast::PrefixOp::Deref) = prefix_expr.op_kind() { | ||
613 | HighlightTag::Operator.into() | ||
614 | } else { | ||
615 | HighlightTag::Punctuation.into() | ||
616 | } | ||
617 | } | ||
618 | T![-] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => { | ||
619 | let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?; | ||
620 | |||
621 | let expr = prefix_expr.expr()?; | ||
622 | match expr { | ||
623 | ast::Expr::Literal(_) => HighlightTag::NumericLiteral, | ||
624 | _ => HighlightTag::Operator, | ||
625 | } | ||
626 | .into() | ||
627 | } | ||
628 | _ if element.parent().and_then(ast::PrefixExpr::cast).is_some() => { | ||
629 | HighlightTag::Operator.into() | ||
630 | } | ||
631 | _ if element.parent().and_then(ast::BinExpr::cast).is_some() => { | ||
632 | HighlightTag::Operator.into() | ||
633 | } | ||
634 | _ if element.parent().and_then(ast::RangeExpr::cast).is_some() => { | ||
635 | HighlightTag::Operator.into() | ||
636 | } | ||
637 | _ if element.parent().and_then(ast::RangePat::cast).is_some() => { | ||
638 | HighlightTag::Operator.into() | ||
639 | } | ||
640 | _ if element.parent().and_then(ast::RestPat::cast).is_some() => { | ||
641 | HighlightTag::Operator.into() | ||
642 | } | ||
643 | _ if element.parent().and_then(ast::Attr::cast).is_some() => { | ||
644 | HighlightTag::Attribute.into() | ||
645 | } | ||
646 | _ => HighlightTag::Punctuation.into(), | ||
647 | }, | ||
648 | |||
649 | k if k.is_keyword() => { | ||
650 | let h = Highlight::new(HighlightTag::Keyword); | ||
651 | match k { | ||
652 | T![break] | ||
653 | | T![continue] | ||
654 | | T![else] | ||
655 | | T![if] | ||
656 | | T![loop] | ||
657 | | T![match] | ||
658 | | T![return] | ||
659 | | T![while] | ||
660 | | T![in] => h | HighlightModifier::ControlFlow, | ||
661 | T![for] if !is_child_of_impl(&element) => h | HighlightModifier::ControlFlow, | ||
662 | T![unsafe] => h | HighlightModifier::Unsafe, | ||
663 | T![true] | T![false] => HighlightTag::BoolLiteral.into(), | ||
664 | T![self] => { | ||
665 | let self_param_is_mut = element | ||
666 | .parent() | ||
667 | .and_then(ast::SelfParam::cast) | ||
668 | .and_then(|p| p.mut_token()) | ||
669 | .is_some(); | ||
670 | let self_path = &element | ||
671 | .parent() | ||
672 | .as_ref() | ||
673 | .and_then(SyntaxNode::parent) | ||
674 | .and_then(ast::Path::cast) | ||
675 | .and_then(|p| sema.resolve_path(&p)); | ||
676 | let mut h = HighlightTag::Symbol(SymbolKind::SelfParam).into(); | ||
677 | if self_param_is_mut | ||
678 | || matches!(self_path, | ||
679 | Some(hir::PathResolution::Local(local)) | ||
680 | if local.is_self(db) | ||
681 | && (local.is_mut(db) || local.ty(db).is_mutable_reference()) | ||
682 | ) | ||
683 | { | ||
684 | h |= HighlightModifier::Mutable | ||
685 | } | ||
686 | |||
687 | if let Some(hir::PathResolution::Local(local)) = self_path { | ||
688 | if is_consumed_lvalue(element, &local, db) { | ||
689 | h |= HighlightModifier::Consuming; | ||
690 | } | ||
691 | } | ||
692 | |||
693 | h | ||
694 | } | ||
695 | T![ref] => element | ||
696 | .parent() | ||
697 | .and_then(ast::IdentPat::cast) | ||
698 | .and_then(|ident_pat| { | ||
699 | if sema.is_unsafe_ident_pat(&ident_pat) { | ||
700 | Some(HighlightModifier::Unsafe) | ||
701 | } else { | ||
702 | None | ||
703 | } | ||
704 | }) | ||
705 | .map(|modifier| h | modifier) | ||
706 | .unwrap_or(h), | ||
707 | _ => h, | ||
708 | } | ||
709 | } | ||
710 | |||
711 | _ => return None, | ||
712 | }; | ||
713 | |||
714 | return Some((highlight, binding_hash)); | ||
715 | |||
716 | fn calc_binding_hash(name: &Name, shadow_count: u32) -> u64 { | ||
717 | fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 { | ||
718 | use std::{collections::hash_map::DefaultHasher, hash::Hasher}; | ||
719 | |||
720 | let mut hasher = DefaultHasher::new(); | ||
721 | x.hash(&mut hasher); | ||
722 | hasher.finish() | ||
723 | } | ||
724 | |||
725 | hash((name, shadow_count)) | ||
726 | } | ||
727 | } | ||
728 | |||
729 | fn is_child_of_impl(element: &SyntaxElement) -> bool { | ||
730 | match element.parent() { | ||
731 | Some(e) => e.kind() == IMPL, | ||
732 | _ => false, | ||
733 | } | ||
734 | } | ||
735 | |||
736 | fn highlight_func_by_name_ref( | ||
737 | sema: &Semantics<RootDatabase>, | ||
738 | name_ref: &ast::NameRef, | ||
739 | ) -> Option<Highlight> { | ||
740 | let method_call = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast)?; | ||
741 | highlight_method_call(sema, &method_call) | ||
742 | } | ||
743 | |||
744 | fn highlight_method_call( | ||
745 | sema: &Semantics<RootDatabase>, | ||
746 | method_call: &ast::MethodCallExpr, | ||
747 | ) -> Option<Highlight> { | ||
748 | let func = sema.resolve_method_call(&method_call)?; | ||
749 | let mut h = HighlightTag::Symbol(SymbolKind::Function).into(); | ||
750 | h |= HighlightModifier::Associated; | ||
751 | if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) { | ||
752 | h |= HighlightModifier::Unsafe; | ||
753 | } | ||
754 | if let Some(self_param) = func.self_param(sema.db) { | ||
755 | match self_param.access(sema.db) { | ||
756 | hir::Access::Shared => (), | ||
757 | hir::Access::Exclusive => h |= HighlightModifier::Mutable, | ||
758 | hir::Access::Owned => { | ||
759 | if let Some(receiver_ty) = | ||
760 | method_call.receiver().and_then(|it| sema.type_of_expr(&it)) | ||
761 | { | ||
762 | if !receiver_ty.is_copy(sema.db) { | ||
763 | h |= HighlightModifier::Consuming | ||
764 | } | ||
765 | } | ||
766 | } | ||
767 | } | ||
768 | } | ||
769 | Some(h) | ||
770 | } | ||
771 | |||
772 | fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight { | ||
773 | match def { | ||
774 | Definition::Macro(_) => HighlightTag::Symbol(SymbolKind::Macro), | ||
775 | Definition::Field(_) => HighlightTag::Symbol(SymbolKind::Field), | ||
776 | Definition::ModuleDef(def) => match def { | ||
777 | hir::ModuleDef::Module(_) => HighlightTag::Symbol(SymbolKind::Module), | ||
778 | hir::ModuleDef::Function(func) => { | ||
779 | let mut h = Highlight::new(HighlightTag::Symbol(SymbolKind::Function)); | ||
780 | if func.as_assoc_item(db).is_some() { | ||
781 | h |= HighlightModifier::Associated; | ||
782 | if func.self_param(db).is_none() { | ||
783 | h |= HighlightModifier::Static | ||
784 | } | ||
785 | } | ||
786 | if func.is_unsafe(db) { | ||
787 | h |= HighlightModifier::Unsafe; | ||
788 | } | ||
789 | return h; | ||
790 | } | ||
791 | hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Symbol(SymbolKind::Struct), | ||
792 | hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Symbol(SymbolKind::Enum), | ||
793 | hir::ModuleDef::Adt(hir::Adt::Union(_)) => HighlightTag::Symbol(SymbolKind::Union), | ||
794 | hir::ModuleDef::Variant(_) => HighlightTag::Symbol(SymbolKind::Variant), | ||
795 | hir::ModuleDef::Const(konst) => { | ||
796 | let mut h = Highlight::new(HighlightTag::Symbol(SymbolKind::Const)); | ||
797 | if konst.as_assoc_item(db).is_some() { | ||
798 | h |= HighlightModifier::Associated | ||
799 | } | ||
800 | return h; | ||
801 | } | ||
802 | hir::ModuleDef::Trait(_) => HighlightTag::Symbol(SymbolKind::Trait), | ||
803 | hir::ModuleDef::TypeAlias(type_) => { | ||
804 | let mut h = Highlight::new(HighlightTag::Symbol(SymbolKind::TypeAlias)); | ||
805 | if type_.as_assoc_item(db).is_some() { | ||
806 | h |= HighlightModifier::Associated | ||
807 | } | ||
808 | return h; | ||
809 | } | ||
810 | hir::ModuleDef::BuiltinType(_) => HighlightTag::BuiltinType, | ||
811 | hir::ModuleDef::Static(s) => { | ||
812 | let mut h = Highlight::new(HighlightTag::Symbol(SymbolKind::Static)); | ||
813 | if s.is_mut(db) { | ||
814 | h |= HighlightModifier::Mutable; | ||
815 | h |= HighlightModifier::Unsafe; | ||
816 | } | ||
817 | return h; | ||
818 | } | ||
819 | }, | ||
820 | Definition::SelfType(_) => HighlightTag::Symbol(SymbolKind::Impl), | ||
821 | Definition::TypeParam(_) => HighlightTag::Symbol(SymbolKind::TypeParam), | ||
822 | Definition::ConstParam(_) => HighlightTag::Symbol(SymbolKind::ConstParam), | ||
823 | Definition::Local(local) => { | ||
824 | let tag = if local.is_param(db) { | ||
825 | HighlightTag::Symbol(SymbolKind::ValueParam) | ||
826 | } else { | ||
827 | HighlightTag::Symbol(SymbolKind::Local) | ||
828 | }; | ||
829 | let mut h = Highlight::new(tag); | ||
830 | if local.is_mut(db) || local.ty(db).is_mutable_reference() { | ||
831 | h |= HighlightModifier::Mutable; | ||
832 | } | ||
833 | if local.ty(db).as_callable(db).is_some() || local.ty(db).impls_fnonce(db) { | ||
834 | h |= HighlightModifier::Callable; | ||
835 | } | ||
836 | return h; | ||
837 | } | ||
838 | Definition::LifetimeParam(_) => HighlightTag::Symbol(SymbolKind::LifetimeParam), | ||
839 | Definition::Label(_) => HighlightTag::Symbol(SymbolKind::Label), | ||
840 | } | ||
841 | .into() | ||
842 | } | ||
843 | |||
844 | fn highlight_name_by_syntax(name: ast::Name) -> Highlight { | ||
845 | let default = HighlightTag::UnresolvedReference; | ||
846 | |||
847 | let parent = match name.syntax().parent() { | ||
848 | Some(it) => it, | ||
849 | _ => return default.into(), | ||
850 | }; | ||
851 | |||
852 | let tag = match parent.kind() { | ||
853 | STRUCT => HighlightTag::Symbol(SymbolKind::Struct), | ||
854 | ENUM => HighlightTag::Symbol(SymbolKind::Enum), | ||
855 | VARIANT => HighlightTag::Symbol(SymbolKind::Variant), | ||
856 | UNION => HighlightTag::Symbol(SymbolKind::Union), | ||
857 | TRAIT => HighlightTag::Symbol(SymbolKind::Trait), | ||
858 | TYPE_ALIAS => HighlightTag::Symbol(SymbolKind::TypeAlias), | ||
859 | TYPE_PARAM => HighlightTag::Symbol(SymbolKind::TypeParam), | ||
860 | RECORD_FIELD => HighlightTag::Symbol(SymbolKind::Field), | ||
861 | MODULE => HighlightTag::Symbol(SymbolKind::Module), | ||
862 | FN => HighlightTag::Symbol(SymbolKind::Function), | ||
863 | CONST => HighlightTag::Symbol(SymbolKind::Const), | ||
864 | STATIC => HighlightTag::Symbol(SymbolKind::Static), | ||
865 | IDENT_PAT => HighlightTag::Symbol(SymbolKind::Local), | ||
866 | _ => default, | ||
867 | }; | ||
868 | |||
869 | tag.into() | ||
870 | } | ||
871 | |||
872 | fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabase>) -> Highlight { | ||
873 | let default = HighlightTag::UnresolvedReference; | ||
874 | |||
875 | let parent = match name.syntax().parent() { | ||
876 | Some(it) => it, | ||
877 | _ => return default.into(), | ||
878 | }; | ||
879 | |||
880 | match parent.kind() { | ||
881 | METHOD_CALL_EXPR => { | ||
882 | return ast::MethodCallExpr::cast(parent) | ||
883 | .and_then(|method_call| highlight_method_call(sema, &method_call)) | ||
884 | .unwrap_or_else(|| HighlightTag::Symbol(SymbolKind::Function).into()); | ||
885 | } | ||
886 | FIELD_EXPR => { | ||
887 | let h = HighlightTag::Symbol(SymbolKind::Field); | ||
888 | let is_union = ast::FieldExpr::cast(parent) | ||
889 | .and_then(|field_expr| { | ||
890 | let field = sema.resolve_field(&field_expr)?; | ||
891 | Some(if let VariantDef::Union(_) = field.parent_def(sema.db) { | ||
892 | true | ||
893 | } else { | ||
894 | false | ||
895 | }) | ||
896 | }) | ||
897 | .unwrap_or(false); | ||
898 | if is_union { | ||
899 | h | HighlightModifier::Unsafe | ||
900 | } else { | ||
901 | h.into() | ||
902 | } | ||
903 | } | ||
904 | PATH_SEGMENT => { | ||
905 | let path = match parent.parent().and_then(ast::Path::cast) { | ||
906 | Some(it) => it, | ||
907 | _ => return default.into(), | ||
908 | }; | ||
909 | let expr = match path.syntax().parent().and_then(ast::PathExpr::cast) { | ||
910 | Some(it) => it, | ||
911 | _ => { | ||
912 | // within path, decide whether it is module or adt by checking for uppercase name | ||
913 | return if name.text().chars().next().unwrap_or_default().is_uppercase() { | ||
914 | HighlightTag::Symbol(SymbolKind::Struct) | ||
915 | } else { | ||
916 | HighlightTag::Symbol(SymbolKind::Module) | ||
917 | } | ||
918 | .into(); | ||
919 | } | ||
920 | }; | ||
921 | let parent = match expr.syntax().parent() { | ||
922 | Some(it) => it, | ||
923 | None => return default.into(), | ||
924 | }; | ||
925 | |||
926 | match parent.kind() { | ||
927 | CALL_EXPR => HighlightTag::Symbol(SymbolKind::Function).into(), | ||
928 | _ => if name.text().chars().next().unwrap_or_default().is_uppercase() { | ||
929 | HighlightTag::Symbol(SymbolKind::Struct) | ||
930 | } else { | ||
931 | HighlightTag::Symbol(SymbolKind::Const) | ||
932 | } | ||
933 | .into(), | ||
934 | } | ||
935 | } | ||
936 | _ => default.into(), | ||
937 | } | ||
938 | } | ||
diff --git a/crates/ide/src/syntax_highlighting/format.rs b/crates/ide/src/syntax_highlighting/format.rs index 26416022b..a74ca844b 100644 --- a/crates/ide/src/syntax_highlighting/format.rs +++ b/crates/ide/src/syntax_highlighting/format.rs | |||
@@ -1,65 +1,51 @@ | |||
1 | //! Syntax highlighting for format macro strings. | 1 | //! Syntax highlighting for format macro strings. |
2 | use syntax::{ | 2 | use syntax::{ |
3 | ast::{self, FormatSpecifier, HasFormatSpecifier}, | 3 | ast::{self, FormatSpecifier, HasFormatSpecifier}, |
4 | AstNode, AstToken, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, | 4 | AstNode, AstToken, TextRange, |
5 | }; | 5 | }; |
6 | 6 | ||
7 | use crate::{ | 7 | use crate::{syntax_highlighting::highlights::Highlights, HlRange, HlTag, SymbolKind}; |
8 | syntax_highlighting::HighlightedRangeStack, HighlightTag, HighlightedRange, SymbolKind, | ||
9 | }; | ||
10 | |||
11 | #[derive(Default)] | ||
12 | pub(super) struct FormatStringHighlighter { | ||
13 | format_string: Option<SyntaxElement>, | ||
14 | } | ||
15 | 8 | ||
16 | impl FormatStringHighlighter { | 9 | pub(super) fn highlight_format_string( |
17 | pub(super) fn check_for_format_string(&mut self, parent: &SyntaxNode) { | 10 | stack: &mut Highlights, |
18 | // Check if macro takes a format string and remember it for highlighting later. | 11 | string: &ast::String, |
19 | // The macros that accept a format string expand to a compiler builtin macros | 12 | range: TextRange, |
20 | // `format_args` and `format_args_nl`. | 13 | ) { |
21 | if let Some(name) = parent | 14 | if is_format_string(string).is_none() { |
22 | .parent() | 15 | return; |
23 | .and_then(ast::MacroCall::cast) | ||
24 | .and_then(|mc| mc.path()) | ||
25 | .and_then(|p| p.segment()) | ||
26 | .and_then(|s| s.name_ref()) | ||
27 | { | ||
28 | match name.text().as_str() { | ||
29 | "format_args" | "format_args_nl" => { | ||
30 | self.format_string = parent | ||
31 | .children_with_tokens() | ||
32 | .filter(|t| t.kind() != SyntaxKind::WHITESPACE) | ||
33 | .nth(1) | ||
34 | .filter(|e| ast::String::can_cast(e.kind())) | ||
35 | } | ||
36 | _ => {} | ||
37 | } | ||
38 | } | ||
39 | } | 16 | } |
40 | pub(super) fn highlight_format_string( | 17 | |
41 | &self, | 18 | string.lex_format_specifier(|piece_range, kind| { |
42 | range_stack: &mut HighlightedRangeStack, | 19 | if let Some(highlight) = highlight_format_specifier(kind) { |
43 | string: &impl HasFormatSpecifier, | 20 | stack.add(HlRange { |
44 | range: TextRange, | 21 | range: piece_range + range.start(), |
45 | ) { | 22 | highlight: highlight.into(), |
46 | if self.format_string.as_ref() == Some(&SyntaxElement::from(string.syntax().clone())) { | 23 | binding_hash: None, |
47 | range_stack.push(); | ||
48 | string.lex_format_specifier(|piece_range, kind| { | ||
49 | if let Some(highlight) = highlight_format_specifier(kind) { | ||
50 | range_stack.add(HighlightedRange { | ||
51 | range: piece_range + range.start(), | ||
52 | highlight: highlight.into(), | ||
53 | binding_hash: None, | ||
54 | }); | ||
55 | } | ||
56 | }); | 24 | }); |
57 | range_stack.pop(); | ||
58 | } | 25 | } |
26 | }); | ||
27 | } | ||
28 | |||
29 | fn is_format_string(string: &ast::String) -> Option<()> { | ||
30 | let parent = string.syntax().parent(); | ||
31 | |||
32 | let name = parent.parent().and_then(ast::MacroCall::cast)?.path()?.segment()?.name_ref()?; | ||
33 | if !matches!(name.text().as_str(), "format_args" | "format_args_nl") { | ||
34 | return None; | ||
35 | } | ||
36 | |||
37 | let first_literal = parent | ||
38 | .children_with_tokens() | ||
39 | .filter_map(|it| it.as_token().cloned().and_then(ast::String::cast)) | ||
40 | .next()?; | ||
41 | if &first_literal != string { | ||
42 | return None; | ||
59 | } | 43 | } |
44 | |||
45 | Some(()) | ||
60 | } | 46 | } |
61 | 47 | ||
62 | fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HighlightTag> { | 48 | fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HlTag> { |
63 | Some(match kind { | 49 | Some(match kind { |
64 | FormatSpecifier::Open | 50 | FormatSpecifier::Open |
65 | | FormatSpecifier::Close | 51 | | FormatSpecifier::Close |
@@ -71,8 +57,10 @@ fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HighlightTag> { | |||
71 | | FormatSpecifier::DollarSign | 57 | | FormatSpecifier::DollarSign |
72 | | FormatSpecifier::Dot | 58 | | FormatSpecifier::Dot |
73 | | FormatSpecifier::Asterisk | 59 | | FormatSpecifier::Asterisk |
74 | | FormatSpecifier::QuestionMark => HighlightTag::FormatSpecifier, | 60 | | FormatSpecifier::QuestionMark => HlTag::FormatSpecifier, |
75 | FormatSpecifier::Integer | FormatSpecifier::Zero => HighlightTag::NumericLiteral, | 61 | |
76 | FormatSpecifier::Identifier => HighlightTag::Symbol(SymbolKind::Local), | 62 | FormatSpecifier::Integer | FormatSpecifier::Zero => HlTag::NumericLiteral, |
63 | |||
64 | FormatSpecifier::Identifier => HlTag::Symbol(SymbolKind::Local), | ||
77 | }) | 65 | }) |
78 | } | 66 | } |
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs new file mode 100644 index 000000000..34bae49a8 --- /dev/null +++ b/crates/ide/src/syntax_highlighting/highlight.rs | |||
@@ -0,0 +1,530 @@ | |||
1 | //! Computes color for a single element. | ||
2 | |||
3 | use hir::{AsAssocItem, Semantics, VariantDef}; | ||
4 | use ide_db::{ | ||
5 | defs::{Definition, NameClass, NameRefClass}, | ||
6 | RootDatabase, | ||
7 | }; | ||
8 | use rustc_hash::FxHashMap; | ||
9 | use syntax::{ | ||
10 | ast, AstNode, AstToken, NodeOrToken, SyntaxElement, | ||
11 | SyntaxKind::{self, *}, | ||
12 | SyntaxNode, SyntaxToken, T, | ||
13 | }; | ||
14 | |||
15 | use crate::{syntax_highlighting::tags::HlPunct, Highlight, HlMod, HlTag, SymbolKind}; | ||
16 | |||
17 | pub(super) fn element( | ||
18 | sema: &Semantics<RootDatabase>, | ||
19 | bindings_shadow_count: &mut FxHashMap<hir::Name, u32>, | ||
20 | syntactic_name_ref_highlighting: bool, | ||
21 | element: SyntaxElement, | ||
22 | ) -> Option<(Highlight, Option<u64>)> { | ||
23 | let db = sema.db; | ||
24 | let mut binding_hash = None; | ||
25 | let highlight: Highlight = match element.kind() { | ||
26 | FN => { | ||
27 | bindings_shadow_count.clear(); | ||
28 | return None; | ||
29 | } | ||
30 | |||
31 | // Highlight definitions depending on the "type" of the definition. | ||
32 | NAME => { | ||
33 | let name = element.into_node().and_then(ast::Name::cast).unwrap(); | ||
34 | let name_kind = NameClass::classify(sema, &name); | ||
35 | |||
36 | if let Some(NameClass::Definition(Definition::Local(local))) = &name_kind { | ||
37 | if let Some(name) = local.name(db) { | ||
38 | let shadow_count = bindings_shadow_count.entry(name.clone()).or_default(); | ||
39 | *shadow_count += 1; | ||
40 | binding_hash = Some(calc_binding_hash(&name, *shadow_count)) | ||
41 | } | ||
42 | }; | ||
43 | |||
44 | match name_kind { | ||
45 | Some(NameClass::ExternCrate(_)) => HlTag::Symbol(SymbolKind::Module).into(), | ||
46 | Some(NameClass::Definition(def)) => highlight_def(db, def) | HlMod::Definition, | ||
47 | Some(NameClass::ConstReference(def)) => highlight_def(db, def), | ||
48 | Some(NameClass::PatFieldShorthand { field_ref, .. }) => { | ||
49 | let mut h = HlTag::Symbol(SymbolKind::Field).into(); | ||
50 | if let Definition::Field(field) = field_ref { | ||
51 | if let VariantDef::Union(_) = field.parent_def(db) { | ||
52 | h |= HlMod::Unsafe; | ||
53 | } | ||
54 | } | ||
55 | |||
56 | h | ||
57 | } | ||
58 | None => highlight_name_by_syntax(name) | HlMod::Definition, | ||
59 | } | ||
60 | } | ||
61 | |||
62 | // Highlight references like the definitions they resolve to | ||
63 | NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => { | ||
64 | // even though we track whether we are in an attribute or not we still need this special case | ||
65 | // as otherwise we would emit unresolved references for name refs inside attributes | ||
66 | Highlight::from(HlTag::Symbol(SymbolKind::Function)) | ||
67 | } | ||
68 | NAME_REF => { | ||
69 | let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); | ||
70 | highlight_func_by_name_ref(sema, &name_ref).unwrap_or_else(|| { | ||
71 | match NameRefClass::classify(sema, &name_ref) { | ||
72 | Some(name_kind) => match name_kind { | ||
73 | NameRefClass::ExternCrate(_) => HlTag::Symbol(SymbolKind::Module).into(), | ||
74 | NameRefClass::Definition(def) => { | ||
75 | if let Definition::Local(local) = &def { | ||
76 | if let Some(name) = local.name(db) { | ||
77 | let shadow_count = | ||
78 | bindings_shadow_count.entry(name.clone()).or_default(); | ||
79 | binding_hash = Some(calc_binding_hash(&name, *shadow_count)) | ||
80 | } | ||
81 | }; | ||
82 | |||
83 | let mut h = highlight_def(db, def); | ||
84 | |||
85 | if let Definition::Local(local) = &def { | ||
86 | if is_consumed_lvalue(name_ref.syntax().clone().into(), local, db) { | ||
87 | h |= HlMod::Consuming; | ||
88 | } | ||
89 | } | ||
90 | |||
91 | if let Some(parent) = name_ref.syntax().parent() { | ||
92 | if matches!(parent.kind(), FIELD_EXPR | RECORD_PAT_FIELD) { | ||
93 | if let Definition::Field(field) = def { | ||
94 | if let VariantDef::Union(_) = field.parent_def(db) { | ||
95 | h |= HlMod::Unsafe; | ||
96 | } | ||
97 | } | ||
98 | } | ||
99 | } | ||
100 | |||
101 | h | ||
102 | } | ||
103 | NameRefClass::FieldShorthand { .. } => { | ||
104 | HlTag::Symbol(SymbolKind::Field).into() | ||
105 | } | ||
106 | }, | ||
107 | None if syntactic_name_ref_highlighting => { | ||
108 | highlight_name_ref_by_syntax(name_ref, sema) | ||
109 | } | ||
110 | None => HlTag::UnresolvedReference.into(), | ||
111 | } | ||
112 | }) | ||
113 | } | ||
114 | |||
115 | // Simple token-based highlighting | ||
116 | COMMENT => { | ||
117 | let comment = element.into_token().and_then(ast::Comment::cast)?; | ||
118 | let h = HlTag::Comment; | ||
119 | match comment.kind().doc { | ||
120 | Some(_) => h | HlMod::Documentation, | ||
121 | None => h.into(), | ||
122 | } | ||
123 | } | ||
124 | STRING | BYTE_STRING => HlTag::StringLiteral.into(), | ||
125 | ATTR => HlTag::Attribute.into(), | ||
126 | INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(), | ||
127 | BYTE => HlTag::ByteLiteral.into(), | ||
128 | CHAR => HlTag::CharLiteral.into(), | ||
129 | QUESTION => Highlight::new(HlTag::Operator) | HlMod::ControlFlow, | ||
130 | LIFETIME => { | ||
131 | let lifetime = element.into_node().and_then(ast::Lifetime::cast).unwrap(); | ||
132 | |||
133 | match NameClass::classify_lifetime(sema, &lifetime) { | ||
134 | Some(NameClass::Definition(def)) => highlight_def(db, def) | HlMod::Definition, | ||
135 | None => match NameRefClass::classify_lifetime(sema, &lifetime) { | ||
136 | Some(NameRefClass::Definition(def)) => highlight_def(db, def), | ||
137 | _ => Highlight::new(HlTag::Symbol(SymbolKind::LifetimeParam)), | ||
138 | }, | ||
139 | _ => Highlight::new(HlTag::Symbol(SymbolKind::LifetimeParam)) | HlMod::Definition, | ||
140 | } | ||
141 | } | ||
142 | p if p.is_punct() => match p { | ||
143 | T![&] => { | ||
144 | let h = HlTag::Operator.into(); | ||
145 | let is_unsafe = element | ||
146 | .parent() | ||
147 | .and_then(ast::RefExpr::cast) | ||
148 | .map(|ref_expr| sema.is_unsafe_ref_expr(&ref_expr)) | ||
149 | .unwrap_or(false); | ||
150 | if is_unsafe { | ||
151 | h | HlMod::Unsafe | ||
152 | } else { | ||
153 | h | ||
154 | } | ||
155 | } | ||
156 | T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.] => HlTag::Operator.into(), | ||
157 | T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => { | ||
158 | HlTag::Symbol(SymbolKind::Macro).into() | ||
159 | } | ||
160 | T![!] if element.parent().and_then(ast::NeverType::cast).is_some() => { | ||
161 | HlTag::BuiltinType.into() | ||
162 | } | ||
163 | T![*] if element.parent().and_then(ast::PtrType::cast).is_some() => { | ||
164 | HlTag::Keyword.into() | ||
165 | } | ||
166 | T![*] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => { | ||
167 | let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?; | ||
168 | |||
169 | let expr = prefix_expr.expr()?; | ||
170 | let ty = sema.type_of_expr(&expr)?; | ||
171 | if ty.is_raw_ptr() { | ||
172 | HlTag::Operator | HlMod::Unsafe | ||
173 | } else if let Some(ast::PrefixOp::Deref) = prefix_expr.op_kind() { | ||
174 | HlTag::Operator.into() | ||
175 | } else { | ||
176 | HlTag::Punctuation(HlPunct::Other).into() | ||
177 | } | ||
178 | } | ||
179 | T![-] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => { | ||
180 | let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?; | ||
181 | |||
182 | let expr = prefix_expr.expr()?; | ||
183 | match expr { | ||
184 | ast::Expr::Literal(_) => HlTag::NumericLiteral, | ||
185 | _ => HlTag::Operator, | ||
186 | } | ||
187 | .into() | ||
188 | } | ||
189 | _ if element.parent().and_then(ast::PrefixExpr::cast).is_some() => { | ||
190 | HlTag::Operator.into() | ||
191 | } | ||
192 | _ if element.parent().and_then(ast::BinExpr::cast).is_some() => HlTag::Operator.into(), | ||
193 | _ if element.parent().and_then(ast::RangeExpr::cast).is_some() => { | ||
194 | HlTag::Operator.into() | ||
195 | } | ||
196 | _ if element.parent().and_then(ast::RangePat::cast).is_some() => HlTag::Operator.into(), | ||
197 | _ if element.parent().and_then(ast::RestPat::cast).is_some() => HlTag::Operator.into(), | ||
198 | _ if element.parent().and_then(ast::Attr::cast).is_some() => HlTag::Attribute.into(), | ||
199 | kind => HlTag::Punctuation(match kind { | ||
200 | T!['['] | T![']'] => HlPunct::Bracket, | ||
201 | T!['{'] | T!['}'] => HlPunct::Brace, | ||
202 | T!['('] | T![')'] => HlPunct::Parenthesis, | ||
203 | T![<] | T![>] => HlPunct::Angle, | ||
204 | T![,] => HlPunct::Comma, | ||
205 | T![:] => HlPunct::Colon, | ||
206 | T![;] => HlPunct::Semi, | ||
207 | T![.] => HlPunct::Dot, | ||
208 | _ => HlPunct::Other, | ||
209 | }) | ||
210 | .into(), | ||
211 | }, | ||
212 | |||
213 | k if k.is_keyword() => { | ||
214 | let h = Highlight::new(HlTag::Keyword); | ||
215 | match k { | ||
216 | T![break] | ||
217 | | T![continue] | ||
218 | | T![else] | ||
219 | | T![if] | ||
220 | | T![loop] | ||
221 | | T![match] | ||
222 | | T![return] | ||
223 | | T![while] | ||
224 | | T![in] => h | HlMod::ControlFlow, | ||
225 | T![for] if !is_child_of_impl(&element) => h | HlMod::ControlFlow, | ||
226 | T![unsafe] => h | HlMod::Unsafe, | ||
227 | T![true] | T![false] => HlTag::BoolLiteral.into(), | ||
228 | T![self] => { | ||
229 | let self_param_is_mut = element | ||
230 | .parent() | ||
231 | .and_then(ast::SelfParam::cast) | ||
232 | .and_then(|p| p.mut_token()) | ||
233 | .is_some(); | ||
234 | let self_path = &element | ||
235 | .parent() | ||
236 | .as_ref() | ||
237 | .and_then(SyntaxNode::parent) | ||
238 | .and_then(ast::Path::cast) | ||
239 | .and_then(|p| sema.resolve_path(&p)); | ||
240 | let mut h = HlTag::Symbol(SymbolKind::SelfParam).into(); | ||
241 | if self_param_is_mut | ||
242 | || matches!(self_path, | ||
243 | Some(hir::PathResolution::Local(local)) | ||
244 | if local.is_self(db) | ||
245 | && (local.is_mut(db) || local.ty(db).is_mutable_reference()) | ||
246 | ) | ||
247 | { | ||
248 | h |= HlMod::Mutable | ||
249 | } | ||
250 | |||
251 | if let Some(hir::PathResolution::Local(local)) = self_path { | ||
252 | if is_consumed_lvalue(element, &local, db) { | ||
253 | h |= HlMod::Consuming; | ||
254 | } | ||
255 | } | ||
256 | |||
257 | h | ||
258 | } | ||
259 | T![ref] => element | ||
260 | .parent() | ||
261 | .and_then(ast::IdentPat::cast) | ||
262 | .and_then(|ident_pat| { | ||
263 | if sema.is_unsafe_ident_pat(&ident_pat) { | ||
264 | Some(HlMod::Unsafe) | ||
265 | } else { | ||
266 | None | ||
267 | } | ||
268 | }) | ||
269 | .map(|modifier| h | modifier) | ||
270 | .unwrap_or(h), | ||
271 | _ => h, | ||
272 | } | ||
273 | } | ||
274 | |||
275 | _ => return None, | ||
276 | }; | ||
277 | |||
278 | return Some((highlight, binding_hash)); | ||
279 | |||
280 | fn calc_binding_hash(name: &hir::Name, shadow_count: u32) -> u64 { | ||
281 | fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 { | ||
282 | use std::{collections::hash_map::DefaultHasher, hash::Hasher}; | ||
283 | |||
284 | let mut hasher = DefaultHasher::new(); | ||
285 | x.hash(&mut hasher); | ||
286 | hasher.finish() | ||
287 | } | ||
288 | |||
289 | hash((name, shadow_count)) | ||
290 | } | ||
291 | } | ||
292 | |||
293 | fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight { | ||
294 | match def { | ||
295 | Definition::Macro(_) => HlTag::Symbol(SymbolKind::Macro), | ||
296 | Definition::Field(_) => HlTag::Symbol(SymbolKind::Field), | ||
297 | Definition::ModuleDef(def) => match def { | ||
298 | hir::ModuleDef::Module(_) => HlTag::Symbol(SymbolKind::Module), | ||
299 | hir::ModuleDef::Function(func) => { | ||
300 | let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Function)); | ||
301 | if func.as_assoc_item(db).is_some() { | ||
302 | h |= HlMod::Associated; | ||
303 | if func.self_param(db).is_none() { | ||
304 | h |= HlMod::Static | ||
305 | } | ||
306 | } | ||
307 | if func.is_unsafe(db) { | ||
308 | h |= HlMod::Unsafe; | ||
309 | } | ||
310 | return h; | ||
311 | } | ||
312 | hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HlTag::Symbol(SymbolKind::Struct), | ||
313 | hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HlTag::Symbol(SymbolKind::Enum), | ||
314 | hir::ModuleDef::Adt(hir::Adt::Union(_)) => HlTag::Symbol(SymbolKind::Union), | ||
315 | hir::ModuleDef::Variant(_) => HlTag::Symbol(SymbolKind::Variant), | ||
316 | hir::ModuleDef::Const(konst) => { | ||
317 | let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Const)); | ||
318 | if konst.as_assoc_item(db).is_some() { | ||
319 | h |= HlMod::Associated | ||
320 | } | ||
321 | return h; | ||
322 | } | ||
323 | hir::ModuleDef::Trait(_) => HlTag::Symbol(SymbolKind::Trait), | ||
324 | hir::ModuleDef::TypeAlias(type_) => { | ||
325 | let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias)); | ||
326 | if type_.as_assoc_item(db).is_some() { | ||
327 | h |= HlMod::Associated | ||
328 | } | ||
329 | return h; | ||
330 | } | ||
331 | hir::ModuleDef::BuiltinType(_) => HlTag::BuiltinType, | ||
332 | hir::ModuleDef::Static(s) => { | ||
333 | let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Static)); | ||
334 | if s.is_mut(db) { | ||
335 | h |= HlMod::Mutable; | ||
336 | h |= HlMod::Unsafe; | ||
337 | } | ||
338 | return h; | ||
339 | } | ||
340 | }, | ||
341 | Definition::SelfType(_) => HlTag::Symbol(SymbolKind::Impl), | ||
342 | Definition::GenericParam(it) => match it { | ||
343 | hir::GenericParam::TypeParam(_) => HlTag::Symbol(SymbolKind::TypeParam), | ||
344 | hir::GenericParam::ConstParam(_) => HlTag::Symbol(SymbolKind::ConstParam), | ||
345 | hir::GenericParam::LifetimeParam(_) => HlTag::Symbol(SymbolKind::LifetimeParam), | ||
346 | }, | ||
347 | Definition::Local(local) => { | ||
348 | let tag = if local.is_param(db) { | ||
349 | HlTag::Symbol(SymbolKind::ValueParam) | ||
350 | } else { | ||
351 | HlTag::Symbol(SymbolKind::Local) | ||
352 | }; | ||
353 | let mut h = Highlight::new(tag); | ||
354 | if local.is_mut(db) || local.ty(db).is_mutable_reference() { | ||
355 | h |= HlMod::Mutable; | ||
356 | } | ||
357 | if local.ty(db).as_callable(db).is_some() || local.ty(db).impls_fnonce(db) { | ||
358 | h |= HlMod::Callable; | ||
359 | } | ||
360 | return h; | ||
361 | } | ||
362 | Definition::Label(_) => HlTag::Symbol(SymbolKind::Label), | ||
363 | } | ||
364 | .into() | ||
365 | } | ||
366 | |||
367 | fn highlight_func_by_name_ref( | ||
368 | sema: &Semantics<RootDatabase>, | ||
369 | name_ref: &ast::NameRef, | ||
370 | ) -> Option<Highlight> { | ||
371 | let mc = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast)?; | ||
372 | highlight_method_call(sema, &mc) | ||
373 | } | ||
374 | |||
375 | fn highlight_method_call( | ||
376 | sema: &Semantics<RootDatabase>, | ||
377 | method_call: &ast::MethodCallExpr, | ||
378 | ) -> Option<Highlight> { | ||
379 | let func = sema.resolve_method_call(&method_call)?; | ||
380 | let mut h = HlTag::Symbol(SymbolKind::Function).into(); | ||
381 | h |= HlMod::Associated; | ||
382 | if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) { | ||
383 | h |= HlMod::Unsafe; | ||
384 | } | ||
385 | if let Some(self_param) = func.self_param(sema.db) { | ||
386 | match self_param.access(sema.db) { | ||
387 | hir::Access::Shared => (), | ||
388 | hir::Access::Exclusive => h |= HlMod::Mutable, | ||
389 | hir::Access::Owned => { | ||
390 | if let Some(receiver_ty) = | ||
391 | method_call.receiver().and_then(|it| sema.type_of_expr(&it)) | ||
392 | { | ||
393 | if !receiver_ty.is_copy(sema.db) { | ||
394 | h |= HlMod::Consuming | ||
395 | } | ||
396 | } | ||
397 | } | ||
398 | } | ||
399 | } | ||
400 | Some(h) | ||
401 | } | ||
402 | |||
403 | fn highlight_name_by_syntax(name: ast::Name) -> Highlight { | ||
404 | let default = HlTag::UnresolvedReference; | ||
405 | |||
406 | let parent = match name.syntax().parent() { | ||
407 | Some(it) => it, | ||
408 | _ => return default.into(), | ||
409 | }; | ||
410 | |||
411 | let tag = match parent.kind() { | ||
412 | STRUCT => HlTag::Symbol(SymbolKind::Struct), | ||
413 | ENUM => HlTag::Symbol(SymbolKind::Enum), | ||
414 | VARIANT => HlTag::Symbol(SymbolKind::Variant), | ||
415 | UNION => HlTag::Symbol(SymbolKind::Union), | ||
416 | TRAIT => HlTag::Symbol(SymbolKind::Trait), | ||
417 | TYPE_ALIAS => HlTag::Symbol(SymbolKind::TypeAlias), | ||
418 | TYPE_PARAM => HlTag::Symbol(SymbolKind::TypeParam), | ||
419 | RECORD_FIELD => HlTag::Symbol(SymbolKind::Field), | ||
420 | MODULE => HlTag::Symbol(SymbolKind::Module), | ||
421 | FN => HlTag::Symbol(SymbolKind::Function), | ||
422 | CONST => HlTag::Symbol(SymbolKind::Const), | ||
423 | STATIC => HlTag::Symbol(SymbolKind::Static), | ||
424 | IDENT_PAT => HlTag::Symbol(SymbolKind::Local), | ||
425 | _ => default, | ||
426 | }; | ||
427 | |||
428 | tag.into() | ||
429 | } | ||
430 | |||
431 | fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabase>) -> Highlight { | ||
432 | let default = HlTag::UnresolvedReference; | ||
433 | |||
434 | let parent = match name.syntax().parent() { | ||
435 | Some(it) => it, | ||
436 | _ => return default.into(), | ||
437 | }; | ||
438 | |||
439 | match parent.kind() { | ||
440 | METHOD_CALL_EXPR => { | ||
441 | return ast::MethodCallExpr::cast(parent) | ||
442 | .and_then(|it| highlight_method_call(sema, &it)) | ||
443 | .unwrap_or_else(|| HlTag::Symbol(SymbolKind::Function).into()); | ||
444 | } | ||
445 | FIELD_EXPR => { | ||
446 | let h = HlTag::Symbol(SymbolKind::Field); | ||
447 | let is_union = ast::FieldExpr::cast(parent) | ||
448 | .and_then(|field_expr| { | ||
449 | let field = sema.resolve_field(&field_expr)?; | ||
450 | Some(if let VariantDef::Union(_) = field.parent_def(sema.db) { | ||
451 | true | ||
452 | } else { | ||
453 | false | ||
454 | }) | ||
455 | }) | ||
456 | .unwrap_or(false); | ||
457 | if is_union { | ||
458 | h | HlMod::Unsafe | ||
459 | } else { | ||
460 | h.into() | ||
461 | } | ||
462 | } | ||
463 | PATH_SEGMENT => { | ||
464 | let path = match parent.parent().and_then(ast::Path::cast) { | ||
465 | Some(it) => it, | ||
466 | _ => return default.into(), | ||
467 | }; | ||
468 | let expr = match path.syntax().parent().and_then(ast::PathExpr::cast) { | ||
469 | Some(it) => it, | ||
470 | _ => { | ||
471 | // within path, decide whether it is module or adt by checking for uppercase name | ||
472 | return if name.text().chars().next().unwrap_or_default().is_uppercase() { | ||
473 | HlTag::Symbol(SymbolKind::Struct) | ||
474 | } else { | ||
475 | HlTag::Symbol(SymbolKind::Module) | ||
476 | } | ||
477 | .into(); | ||
478 | } | ||
479 | }; | ||
480 | let parent = match expr.syntax().parent() { | ||
481 | Some(it) => it, | ||
482 | None => return default.into(), | ||
483 | }; | ||
484 | |||
485 | match parent.kind() { | ||
486 | CALL_EXPR => HlTag::Symbol(SymbolKind::Function).into(), | ||
487 | _ => if name.text().chars().next().unwrap_or_default().is_uppercase() { | ||
488 | HlTag::Symbol(SymbolKind::Struct) | ||
489 | } else { | ||
490 | HlTag::Symbol(SymbolKind::Const) | ||
491 | } | ||
492 | .into(), | ||
493 | } | ||
494 | } | ||
495 | _ => default.into(), | ||
496 | } | ||
497 | } | ||
498 | |||
499 | fn is_consumed_lvalue( | ||
500 | node: NodeOrToken<SyntaxNode, SyntaxToken>, | ||
501 | local: &hir::Local, | ||
502 | db: &RootDatabase, | ||
503 | ) -> bool { | ||
504 | // When lvalues are passed as arguments and they're not Copy, then mark them as Consuming. | ||
505 | parents_match(node, &[PATH_SEGMENT, PATH, PATH_EXPR, ARG_LIST]) && !local.ty(db).is_copy(db) | ||
506 | } | ||
507 | |||
508 | /// Returns true if the parent nodes of `node` all match the `SyntaxKind`s in `kinds` exactly. | ||
509 | fn parents_match(mut node: NodeOrToken<SyntaxNode, SyntaxToken>, mut kinds: &[SyntaxKind]) -> bool { | ||
510 | while let (Some(parent), [kind, rest @ ..]) = (&node.parent(), kinds) { | ||
511 | if parent.kind() != *kind { | ||
512 | return false; | ||
513 | } | ||
514 | |||
515 | // FIXME: Would be nice to get parent out of the match, but binding by-move and by-value | ||
516 | // in the same pattern is unstable: rust-lang/rust#68354. | ||
517 | node = node.parent().unwrap().into(); | ||
518 | kinds = rest; | ||
519 | } | ||
520 | |||
521 | // Only true if we matched all expected kinds | ||
522 | kinds.len() == 0 | ||
523 | } | ||
524 | |||
525 | fn is_child_of_impl(element: &SyntaxElement) -> bool { | ||
526 | match element.parent() { | ||
527 | Some(e) => e.kind() == IMPL, | ||
528 | _ => false, | ||
529 | } | ||
530 | } | ||
diff --git a/crates/ide/src/syntax_highlighting/highlights.rs b/crates/ide/src/syntax_highlighting/highlights.rs new file mode 100644 index 000000000..882a685a5 --- /dev/null +++ b/crates/ide/src/syntax_highlighting/highlights.rs | |||
@@ -0,0 +1,92 @@ | |||
1 | //! Collects a tree of highlighted ranges and flattens it. | ||
2 | use std::iter; | ||
3 | |||
4 | use stdx::equal_range_by; | ||
5 | use syntax::TextRange; | ||
6 | |||
7 | use crate::{HlRange, HlTag}; | ||
8 | |||
9 | pub(super) struct Highlights { | ||
10 | root: Node, | ||
11 | } | ||
12 | |||
13 | struct Node { | ||
14 | hl_range: HlRange, | ||
15 | nested: Vec<Node>, | ||
16 | } | ||
17 | |||
18 | impl Highlights { | ||
19 | pub(super) fn new(range: TextRange) -> Highlights { | ||
20 | Highlights { | ||
21 | root: Node::new(HlRange { range, highlight: HlTag::None.into(), binding_hash: None }), | ||
22 | } | ||
23 | } | ||
24 | |||
25 | pub(super) fn add(&mut self, hl_range: HlRange) { | ||
26 | self.root.add(hl_range); | ||
27 | } | ||
28 | |||
29 | pub(super) fn to_vec(self) -> Vec<HlRange> { | ||
30 | let mut res = Vec::new(); | ||
31 | self.root.flatten(&mut res); | ||
32 | res | ||
33 | } | ||
34 | } | ||
35 | |||
36 | impl Node { | ||
37 | fn new(hl_range: HlRange) -> Node { | ||
38 | Node { hl_range, nested: Vec::new() } | ||
39 | } | ||
40 | |||
41 | fn add(&mut self, hl_range: HlRange) { | ||
42 | assert!(self.hl_range.range.contains_range(hl_range.range)); | ||
43 | |||
44 | // Fast path | ||
45 | if let Some(last) = self.nested.last_mut() { | ||
46 | if last.hl_range.range.contains_range(hl_range.range) { | ||
47 | return last.add(hl_range); | ||
48 | } | ||
49 | if last.hl_range.range.end() <= hl_range.range.start() { | ||
50 | return self.nested.push(Node::new(hl_range)); | ||
51 | } | ||
52 | } | ||
53 | |||
54 | let overlapping = | ||
55 | equal_range_by(&self.nested, |n| TextRange::ordering(n.hl_range.range, hl_range.range)); | ||
56 | |||
57 | if overlapping.len() == 1 | ||
58 | && self.nested[overlapping.start].hl_range.range.contains_range(hl_range.range) | ||
59 | { | ||
60 | return self.nested[overlapping.start].add(hl_range); | ||
61 | } | ||
62 | |||
63 | let nested = self | ||
64 | .nested | ||
65 | .splice(overlapping.clone(), iter::once(Node::new(hl_range))) | ||
66 | .collect::<Vec<_>>(); | ||
67 | self.nested[overlapping.start].nested = nested; | ||
68 | } | ||
69 | |||
70 | fn flatten(&self, acc: &mut Vec<HlRange>) { | ||
71 | let mut start = self.hl_range.range.start(); | ||
72 | let mut nested = self.nested.iter(); | ||
73 | loop { | ||
74 | let next = nested.next(); | ||
75 | let end = next.map_or(self.hl_range.range.end(), |it| it.hl_range.range.start()); | ||
76 | if start < end { | ||
77 | acc.push(HlRange { | ||
78 | range: TextRange::new(start, end), | ||
79 | highlight: self.hl_range.highlight, | ||
80 | binding_hash: self.hl_range.binding_hash, | ||
81 | }); | ||
82 | } | ||
83 | start = match next { | ||
84 | Some(child) => { | ||
85 | child.flatten(acc); | ||
86 | child.hl_range.range.end() | ||
87 | } | ||
88 | None => break, | ||
89 | } | ||
90 | } | ||
91 | } | ||
92 | } | ||
diff --git a/crates/ide/src/syntax_highlighting/html.rs b/crates/ide/src/syntax_highlighting/html.rs index 99ba3a59d..0ee7bc96e 100644 --- a/crates/ide/src/syntax_highlighting/html.rs +++ b/crates/ide/src/syntax_highlighting/html.rs | |||
@@ -3,7 +3,7 @@ | |||
3 | use ide_db::base_db::SourceDatabase; | 3 | use ide_db::base_db::SourceDatabase; |
4 | use oorandom::Rand32; | 4 | use oorandom::Rand32; |
5 | use stdx::format_to; | 5 | use stdx::format_to; |
6 | use syntax::{AstNode, TextRange, TextSize}; | 6 | use syntax::AstNode; |
7 | 7 | ||
8 | use crate::{syntax_highlighting::highlight, FileId, RootDatabase}; | 8 | use crate::{syntax_highlighting::highlight, FileId, RootDatabase}; |
9 | 9 | ||
@@ -20,35 +20,27 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo | |||
20 | ) | 20 | ) |
21 | } | 21 | } |
22 | 22 | ||
23 | let ranges = highlight(db, file_id, None, false); | 23 | let hl_ranges = highlight(db, file_id, None, false); |
24 | let text = parse.tree().syntax().to_string(); | 24 | let text = parse.tree().syntax().to_string(); |
25 | let mut prev_pos = TextSize::from(0); | ||
26 | let mut buf = String::new(); | 25 | let mut buf = String::new(); |
27 | buf.push_str(&STYLE); | 26 | buf.push_str(&STYLE); |
28 | buf.push_str("<pre><code>"); | 27 | buf.push_str("<pre><code>"); |
29 | for range in &ranges { | 28 | for r in &hl_ranges { |
30 | if range.range.start() > prev_pos { | 29 | let chunk = html_escape(&text[r.range]); |
31 | let curr = &text[TextRange::new(prev_pos, range.range.start())]; | 30 | if r.highlight.is_empty() { |
32 | let text = html_escape(curr); | 31 | format_to!(buf, "{}", chunk); |
33 | buf.push_str(&text); | 32 | continue; |
34 | } | 33 | } |
35 | let curr = &text[TextRange::new(range.range.start(), range.range.end())]; | ||
36 | 34 | ||
37 | let class = range.highlight.to_string().replace('.', " "); | 35 | let class = r.highlight.to_string().replace('.', " "); |
38 | let color = match (rainbow, range.binding_hash) { | 36 | let color = match (rainbow, r.binding_hash) { |
39 | (true, Some(hash)) => { | 37 | (true, Some(hash)) => { |
40 | format!(" data-binding-hash=\"{}\" style=\"color: {};\"", hash, rainbowify(hash)) | 38 | format!(" data-binding-hash=\"{}\" style=\"color: {};\"", hash, rainbowify(hash)) |
41 | } | 39 | } |
42 | _ => "".into(), | 40 | _ => "".into(), |
43 | }; | 41 | }; |
44 | format_to!(buf, "<span class=\"{}\"{}>{}</span>", class, color, html_escape(curr)); | 42 | format_to!(buf, "<span class=\"{}\"{}>{}</span>", class, color, chunk); |
45 | |||
46 | prev_pos = range.range.end(); | ||
47 | } | 43 | } |
48 | // Add the remaining (non-highlighted) text | ||
49 | let curr = &text[TextRange::new(prev_pos, TextSize::of(&text))]; | ||
50 | let text = html_escape(curr); | ||
51 | buf.push_str(&text); | ||
52 | buf.push_str("</code></pre>"); | 44 | buf.push_str("</code></pre>"); |
53 | buf | 45 | buf |
54 | } | 46 | } |
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs new file mode 100644 index 000000000..281461493 --- /dev/null +++ b/crates/ide/src/syntax_highlighting/inject.rs | |||
@@ -0,0 +1,158 @@ | |||
1 | //! "Recursive" Syntax highlighting for code in doctests and fixtures. | ||
2 | |||
3 | use hir::Semantics; | ||
4 | use ide_db::call_info::ActiveParameter; | ||
5 | use syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize}; | ||
6 | |||
7 | use crate::{Analysis, HlMod, HlRange, HlTag, RootDatabase}; | ||
8 | |||
9 | use super::{highlights::Highlights, injector::Injector}; | ||
10 | |||
11 | pub(super) fn ra_fixture( | ||
12 | hl: &mut Highlights, | ||
13 | sema: &Semantics<RootDatabase>, | ||
14 | literal: ast::String, | ||
15 | expanded: SyntaxToken, | ||
16 | ) -> Option<()> { | ||
17 | let active_parameter = ActiveParameter::at_token(&sema, expanded)?; | ||
18 | if !active_parameter.name.starts_with("ra_fixture") { | ||
19 | return None; | ||
20 | } | ||
21 | let value = literal.value()?; | ||
22 | |||
23 | if let Some(range) = literal.open_quote_text_range() { | ||
24 | hl.add(HlRange { range, highlight: HlTag::StringLiteral.into(), binding_hash: None }) | ||
25 | } | ||
26 | |||
27 | let mut inj = Injector::default(); | ||
28 | |||
29 | let mut text = &*value; | ||
30 | let mut offset: TextSize = 0.into(); | ||
31 | |||
32 | while !text.is_empty() { | ||
33 | let marker = "$0"; | ||
34 | let idx = text.find(marker).unwrap_or(text.len()); | ||
35 | let (chunk, next) = text.split_at(idx); | ||
36 | inj.add(chunk, TextRange::at(offset, TextSize::of(chunk))); | ||
37 | |||
38 | text = next; | ||
39 | offset += TextSize::of(chunk); | ||
40 | |||
41 | if let Some(next) = text.strip_prefix(marker) { | ||
42 | if let Some(range) = literal.map_range_up(TextRange::at(offset, TextSize::of(marker))) { | ||
43 | hl.add(HlRange { range, highlight: HlTag::Keyword.into(), binding_hash: None }); | ||
44 | } | ||
45 | |||
46 | text = next; | ||
47 | |||
48 | let marker_len = TextSize::of(marker); | ||
49 | offset += marker_len; | ||
50 | } | ||
51 | } | ||
52 | |||
53 | let (analysis, tmp_file_id) = Analysis::from_single_file(inj.text().to_string()); | ||
54 | |||
55 | for mut hl_range in analysis.highlight(tmp_file_id).unwrap() { | ||
56 | for range in inj.map_range_up(hl_range.range) { | ||
57 | if let Some(range) = literal.map_range_up(range) { | ||
58 | hl_range.range = range; | ||
59 | hl.add(hl_range.clone()); | ||
60 | } | ||
61 | } | ||
62 | } | ||
63 | |||
64 | if let Some(range) = literal.close_quote_text_range() { | ||
65 | hl.add(HlRange { range, highlight: HlTag::StringLiteral.into(), binding_hash: None }) | ||
66 | } | ||
67 | |||
68 | Some(()) | ||
69 | } | ||
70 | |||
71 | const RUSTDOC_FENCE: &'static str = "```"; | ||
72 | const RUSTDOC_FENCE_TOKENS: &[&'static str] = &[ | ||
73 | "", | ||
74 | "rust", | ||
75 | "should_panic", | ||
76 | "ignore", | ||
77 | "no_run", | ||
78 | "compile_fail", | ||
79 | "edition2015", | ||
80 | "edition2018", | ||
81 | "edition2021", | ||
82 | ]; | ||
83 | |||
84 | /// Injection of syntax highlighting of doctests. | ||
85 | pub(super) fn doc_comment(hl: &mut Highlights, node: &SyntaxNode) { | ||
86 | let doc_comments = node | ||
87 | .children_with_tokens() | ||
88 | .filter_map(|it| it.into_token().and_then(ast::Comment::cast)) | ||
89 | .filter(|it| it.kind().doc.is_some()); | ||
90 | |||
91 | if !doc_comments.clone().any(|it| it.text().contains(RUSTDOC_FENCE)) { | ||
92 | return; | ||
93 | } | ||
94 | |||
95 | let mut inj = Injector::default(); | ||
96 | inj.add_unmapped("fn doctest() {\n"); | ||
97 | |||
98 | let mut is_codeblock = false; | ||
99 | let mut is_doctest = false; | ||
100 | |||
101 | // Replace the original, line-spanning comment ranges by new, only comment-prefix | ||
102 | // spanning comment ranges. | ||
103 | let mut new_comments = Vec::new(); | ||
104 | for comment in doc_comments { | ||
105 | match comment.text().find(RUSTDOC_FENCE) { | ||
106 | Some(idx) => { | ||
107 | is_codeblock = !is_codeblock; | ||
108 | // Check whether code is rust by inspecting fence guards | ||
109 | let guards = &comment.text()[idx + RUSTDOC_FENCE.len()..]; | ||
110 | let is_rust = | ||
111 | guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim())); | ||
112 | is_doctest = is_codeblock && is_rust; | ||
113 | continue; | ||
114 | } | ||
115 | None if !is_doctest => continue, | ||
116 | None => (), | ||
117 | } | ||
118 | |||
119 | let line: &str = comment.text().as_str(); | ||
120 | let range = comment.syntax().text_range(); | ||
121 | |||
122 | let mut pos = TextSize::of(comment.prefix()); | ||
123 | // whitespace after comment is ignored | ||
124 | if let Some(ws) = line[pos.into()..].chars().next().filter(|c| c.is_whitespace()) { | ||
125 | pos += TextSize::of(ws); | ||
126 | } | ||
127 | // lines marked with `#` should be ignored in output, we skip the `#` char | ||
128 | if let Some(ws) = line[pos.into()..].chars().next().filter(|&c| c == '#') { | ||
129 | pos += TextSize::of(ws); | ||
130 | } | ||
131 | |||
132 | new_comments.push(TextRange::at(range.start(), pos)); | ||
133 | |||
134 | inj.add(&line[pos.into()..], TextRange::new(range.start() + pos, range.end())); | ||
135 | inj.add_unmapped("\n"); | ||
136 | } | ||
137 | inj.add_unmapped("\n}"); | ||
138 | |||
139 | let (analysis, tmp_file_id) = Analysis::from_single_file(inj.text().to_string()); | ||
140 | |||
141 | for h in analysis.with_db(|db| super::highlight(db, tmp_file_id, None, true)).unwrap() { | ||
142 | for r in inj.map_range_up(h.range) { | ||
143 | hl.add(HlRange { | ||
144 | range: r, | ||
145 | highlight: h.highlight | HlMod::Injected, | ||
146 | binding_hash: h.binding_hash, | ||
147 | }); | ||
148 | } | ||
149 | } | ||
150 | |||
151 | for range in new_comments { | ||
152 | hl.add(HlRange { | ||
153 | range, | ||
154 | highlight: HlTag::Comment | HlMod::Documentation, | ||
155 | binding_hash: None, | ||
156 | }); | ||
157 | } | ||
158 | } | ||
diff --git a/crates/ide/src/syntax_highlighting/injection.rs b/crates/ide/src/syntax_highlighting/injection.rs deleted file mode 100644 index d6be9708d..000000000 --- a/crates/ide/src/syntax_highlighting/injection.rs +++ /dev/null | |||
@@ -1,240 +0,0 @@ | |||
1 | //! Syntax highlighting injections such as highlighting of documentation tests. | ||
2 | |||
3 | use std::{collections::BTreeMap, convert::TryFrom}; | ||
4 | |||
5 | use hir::Semantics; | ||
6 | use ide_db::call_info::ActiveParameter; | ||
7 | use itertools::Itertools; | ||
8 | use syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize}; | ||
9 | |||
10 | use crate::{Analysis, Highlight, HighlightModifier, HighlightTag, HighlightedRange, RootDatabase}; | ||
11 | |||
12 | use super::HighlightedRangeStack; | ||
13 | |||
14 | pub(super) fn highlight_injection( | ||
15 | acc: &mut HighlightedRangeStack, | ||
16 | sema: &Semantics<RootDatabase>, | ||
17 | literal: ast::String, | ||
18 | expanded: SyntaxToken, | ||
19 | ) -> Option<()> { | ||
20 | let active_parameter = ActiveParameter::at_token(&sema, expanded)?; | ||
21 | if !active_parameter.name.starts_with("ra_fixture") { | ||
22 | return None; | ||
23 | } | ||
24 | let value = literal.value()?; | ||
25 | let marker_info = MarkerInfo::new(&*value); | ||
26 | let (analysis, tmp_file_id) = Analysis::from_single_file(marker_info.cleaned_text.clone()); | ||
27 | |||
28 | if let Some(range) = literal.open_quote_text_range() { | ||
29 | acc.add(HighlightedRange { | ||
30 | range, | ||
31 | highlight: HighlightTag::StringLiteral.into(), | ||
32 | binding_hash: None, | ||
33 | }) | ||
34 | } | ||
35 | |||
36 | for mut h in analysis.highlight(tmp_file_id).unwrap() { | ||
37 | let range = marker_info.map_range_up(h.range); | ||
38 | if let Some(range) = literal.map_range_up(range) { | ||
39 | h.range = range; | ||
40 | acc.add(h); | ||
41 | } | ||
42 | } | ||
43 | |||
44 | if let Some(range) = literal.close_quote_text_range() { | ||
45 | acc.add(HighlightedRange { | ||
46 | range, | ||
47 | highlight: HighlightTag::StringLiteral.into(), | ||
48 | binding_hash: None, | ||
49 | }) | ||
50 | } | ||
51 | |||
52 | Some(()) | ||
53 | } | ||
54 | |||
55 | /// Data to remove `$0` from string and map ranges | ||
56 | #[derive(Default, Debug)] | ||
57 | struct MarkerInfo { | ||
58 | cleaned_text: String, | ||
59 | markers: Vec<TextRange>, | ||
60 | } | ||
61 | |||
62 | impl MarkerInfo { | ||
63 | fn new(mut text: &str) -> Self { | ||
64 | let marker = "$0"; | ||
65 | |||
66 | let mut res = MarkerInfo::default(); | ||
67 | let mut offset: TextSize = 0.into(); | ||
68 | while !text.is_empty() { | ||
69 | let idx = text.find(marker).unwrap_or(text.len()); | ||
70 | let (chunk, next) = text.split_at(idx); | ||
71 | text = next; | ||
72 | res.cleaned_text.push_str(chunk); | ||
73 | offset += TextSize::of(chunk); | ||
74 | |||
75 | if let Some(next) = text.strip_prefix(marker) { | ||
76 | text = next; | ||
77 | |||
78 | let marker_len = TextSize::of(marker); | ||
79 | res.markers.push(TextRange::at(offset, marker_len)); | ||
80 | offset += marker_len; | ||
81 | } | ||
82 | } | ||
83 | res | ||
84 | } | ||
85 | fn map_range_up(&self, range: TextRange) -> TextRange { | ||
86 | TextRange::new( | ||
87 | self.map_offset_up(range.start(), true), | ||
88 | self.map_offset_up(range.end(), false), | ||
89 | ) | ||
90 | } | ||
91 | fn map_offset_up(&self, mut offset: TextSize, start: bool) -> TextSize { | ||
92 | for r in &self.markers { | ||
93 | if r.start() < offset || (start && r.start() == offset) { | ||
94 | offset += r.len() | ||
95 | } | ||
96 | } | ||
97 | offset | ||
98 | } | ||
99 | } | ||
100 | |||
101 | /// Mapping from extracted documentation code to original code | ||
102 | type RangesMap = BTreeMap<TextSize, TextSize>; | ||
103 | |||
104 | const RUSTDOC_FENCE: &'static str = "```"; | ||
105 | const RUSTDOC_FENCE_TOKENS: &[&'static str] = &[ | ||
106 | "", | ||
107 | "rust", | ||
108 | "should_panic", | ||
109 | "ignore", | ||
110 | "no_run", | ||
111 | "compile_fail", | ||
112 | "edition2015", | ||
113 | "edition2018", | ||
114 | "edition2021", | ||
115 | ]; | ||
116 | |||
117 | /// Extracts Rust code from documentation comments as well as a mapping from | ||
118 | /// the extracted source code back to the original source ranges. | ||
119 | /// Lastly, a vector of new comment highlight ranges (spanning only the | ||
120 | /// comment prefix) is returned which is used in the syntax highlighting | ||
121 | /// injection to replace the previous (line-spanning) comment ranges. | ||
122 | pub(super) fn extract_doc_comments( | ||
123 | node: &SyntaxNode, | ||
124 | ) -> Option<(String, RangesMap, Vec<HighlightedRange>)> { | ||
125 | // wrap the doctest into function body to get correct syntax highlighting | ||
126 | let prefix = "fn doctest() {\n"; | ||
127 | let suffix = "}\n"; | ||
128 | // Mapping from extracted documentation code to original code | ||
129 | let mut range_mapping: RangesMap = BTreeMap::new(); | ||
130 | let mut line_start = TextSize::try_from(prefix.len()).unwrap(); | ||
131 | let mut is_codeblock = false; | ||
132 | let mut is_doctest = false; | ||
133 | // Replace the original, line-spanning comment ranges by new, only comment-prefix | ||
134 | // spanning comment ranges. | ||
135 | let mut new_comments = Vec::new(); | ||
136 | let doctest = node | ||
137 | .children_with_tokens() | ||
138 | .filter_map(|el| el.into_token().and_then(ast::Comment::cast)) | ||
139 | .filter(|comment| comment.kind().doc.is_some()) | ||
140 | .filter(|comment| { | ||
141 | if let Some(idx) = comment.text().find(RUSTDOC_FENCE) { | ||
142 | is_codeblock = !is_codeblock; | ||
143 | // Check whether code is rust by inspecting fence guards | ||
144 | let guards = &comment.text()[idx + RUSTDOC_FENCE.len()..]; | ||
145 | let is_rust = | ||
146 | guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim())); | ||
147 | is_doctest = is_codeblock && is_rust; | ||
148 | false | ||
149 | } else { | ||
150 | is_doctest | ||
151 | } | ||
152 | }) | ||
153 | .map(|comment| { | ||
154 | let prefix_len = comment.prefix().len(); | ||
155 | let line: &str = comment.text().as_str(); | ||
156 | let range = comment.syntax().text_range(); | ||
157 | |||
158 | // whitespace after comment is ignored | ||
159 | let pos = if let Some(ws) = line.chars().nth(prefix_len).filter(|c| c.is_whitespace()) { | ||
160 | prefix_len + ws.len_utf8() | ||
161 | } else { | ||
162 | prefix_len | ||
163 | }; | ||
164 | |||
165 | // lines marked with `#` should be ignored in output, we skip the `#` char | ||
166 | let pos = if let Some(ws) = line.chars().nth(pos).filter(|&c| c == '#') { | ||
167 | pos + ws.len_utf8() | ||
168 | } else { | ||
169 | pos | ||
170 | }; | ||
171 | |||
172 | range_mapping.insert(line_start, range.start() + TextSize::try_from(pos).unwrap()); | ||
173 | new_comments.push(HighlightedRange { | ||
174 | range: TextRange::new( | ||
175 | range.start(), | ||
176 | range.start() + TextSize::try_from(pos).unwrap(), | ||
177 | ), | ||
178 | highlight: HighlightTag::Comment | HighlightModifier::Documentation, | ||
179 | binding_hash: None, | ||
180 | }); | ||
181 | line_start += range.len() - TextSize::try_from(pos).unwrap(); | ||
182 | line_start += TextSize::try_from('\n'.len_utf8()).unwrap(); | ||
183 | |||
184 | line[pos..].to_owned() | ||
185 | }) | ||
186 | .join("\n"); | ||
187 | |||
188 | if doctest.is_empty() { | ||
189 | return None; | ||
190 | } | ||
191 | |||
192 | let doctest = format!("{}{}{}", prefix, doctest, suffix); | ||
193 | Some((doctest, range_mapping, new_comments)) | ||
194 | } | ||
195 | |||
196 | /// Injection of syntax highlighting of doctests. | ||
197 | pub(super) fn highlight_doc_comment( | ||
198 | text: String, | ||
199 | range_mapping: RangesMap, | ||
200 | new_comments: Vec<HighlightedRange>, | ||
201 | stack: &mut HighlightedRangeStack, | ||
202 | ) { | ||
203 | let (analysis, tmp_file_id) = Analysis::from_single_file(text); | ||
204 | |||
205 | stack.push(); | ||
206 | for mut h in analysis.with_db(|db| super::highlight(db, tmp_file_id, None, true)).unwrap() { | ||
207 | // Determine start offset and end offset in case of multi-line ranges | ||
208 | let mut start_offset = None; | ||
209 | let mut end_offset = None; | ||
210 | for (line_start, orig_line_start) in range_mapping.range(..h.range.end()).rev() { | ||
211 | // It's possible for orig_line_start - line_start to be negative. Add h.range.start() | ||
212 | // here and remove it from the end range after the loop below so that the values are | ||
213 | // always non-negative. | ||
214 | let offset = h.range.start() + orig_line_start - line_start; | ||
215 | if line_start <= &h.range.start() { | ||
216 | start_offset.get_or_insert(offset); | ||
217 | break; | ||
218 | } else { | ||
219 | end_offset.get_or_insert(offset); | ||
220 | } | ||
221 | } | ||
222 | if let Some(start_offset) = start_offset { | ||
223 | h.range = TextRange::new( | ||
224 | start_offset, | ||
225 | h.range.end() + end_offset.unwrap_or(start_offset) - h.range.start(), | ||
226 | ); | ||
227 | |||
228 | h.highlight |= HighlightModifier::Injected; | ||
229 | stack.add(h); | ||
230 | } | ||
231 | } | ||
232 | |||
233 | // Inject the comment prefix highlight ranges | ||
234 | stack.push(); | ||
235 | for comment in new_comments { | ||
236 | stack.add(comment); | ||
237 | } | ||
238 | stack.pop_and_inject(None); | ||
239 | stack.pop_and_inject(Some(Highlight::from(HighlightTag::Dummy) | HighlightModifier::Injected)); | ||
240 | } | ||
diff --git a/crates/ide/src/syntax_highlighting/injector.rs b/crates/ide/src/syntax_highlighting/injector.rs new file mode 100644 index 000000000..24ff473ec --- /dev/null +++ b/crates/ide/src/syntax_highlighting/injector.rs | |||
@@ -0,0 +1,78 @@ | |||
1 | //! Extracts a subsequence of a text document, remembering the mapping of ranges | ||
2 | //! between original and extracted texts. | ||
3 | use std::ops::{self, Sub}; | ||
4 | |||
5 | use stdx::equal_range_by; | ||
6 | use syntax::{TextRange, TextSize}; | ||
7 | |||
8 | #[derive(Default)] | ||
9 | pub(super) struct Injector { | ||
10 | buf: String, | ||
11 | ranges: Vec<(TextRange, Option<Delta<TextSize>>)>, | ||
12 | } | ||
13 | |||
14 | impl Injector { | ||
15 | pub(super) fn add(&mut self, text: &str, source_range: TextRange) { | ||
16 | let len = TextSize::of(text); | ||
17 | assert_eq!(len, source_range.len()); | ||
18 | self.add_impl(text, Some(source_range.start())); | ||
19 | } | ||
20 | pub(super) fn add_unmapped(&mut self, text: &str) { | ||
21 | self.add_impl(text, None); | ||
22 | } | ||
23 | fn add_impl(&mut self, text: &str, source: Option<TextSize>) { | ||
24 | let len = TextSize::of(text); | ||
25 | let target_range = TextRange::at(TextSize::of(&self.buf), len); | ||
26 | self.ranges.push((target_range, source.map(|it| Delta::new(target_range.start(), it)))); | ||
27 | self.buf.push_str(text); | ||
28 | } | ||
29 | |||
30 | pub(super) fn text(&self) -> &str { | ||
31 | &self.buf | ||
32 | } | ||
33 | pub(super) fn map_range_up(&self, range: TextRange) -> impl Iterator<Item = TextRange> + '_ { | ||
34 | equal_range_by(&self.ranges, |&(r, _)| TextRange::ordering(r, range)).filter_map(move |i| { | ||
35 | let (target_range, delta) = self.ranges[i]; | ||
36 | let intersection = target_range.intersect(range).unwrap(); | ||
37 | Some(intersection + delta?) | ||
38 | }) | ||
39 | } | ||
40 | } | ||
41 | |||
42 | #[derive(Clone, Copy)] | ||
43 | enum Delta<T> { | ||
44 | Add(T), | ||
45 | Sub(T), | ||
46 | } | ||
47 | |||
48 | impl<T> Delta<T> { | ||
49 | fn new(from: T, to: T) -> Delta<T> | ||
50 | where | ||
51 | T: Ord + Sub<Output = T>, | ||
52 | { | ||
53 | if to >= from { | ||
54 | Delta::Add(to - from) | ||
55 | } else { | ||
56 | Delta::Sub(from - to) | ||
57 | } | ||
58 | } | ||
59 | } | ||
60 | |||
61 | impl ops::Add<Delta<TextSize>> for TextSize { | ||
62 | type Output = TextSize; | ||
63 | |||
64 | fn add(self, rhs: Delta<TextSize>) -> TextSize { | ||
65 | match rhs { | ||
66 | Delta::Add(it) => self + it, | ||
67 | Delta::Sub(it) => self - it, | ||
68 | } | ||
69 | } | ||
70 | } | ||
71 | |||
72 | impl ops::Add<Delta<TextSize>> for TextRange { | ||
73 | type Output = TextRange; | ||
74 | |||
75 | fn add(self, rhs: Delta<TextSize>) -> TextRange { | ||
76 | TextRange::at(self.start() + rhs, self.len()) | ||
77 | } | ||
78 | } | ||
diff --git a/crates/ide/src/syntax_highlighting/macro_rules.rs b/crates/ide/src/syntax_highlighting/macro_rules.rs index 4462af47e..44620e912 100644 --- a/crates/ide/src/syntax_highlighting/macro_rules.rs +++ b/crates/ide/src/syntax_highlighting/macro_rules.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! Syntax highlighting for macro_rules!. | 1 | //! Syntax highlighting for macro_rules!. |
2 | use syntax::{SyntaxElement, SyntaxKind, SyntaxToken, TextRange, T}; | 2 | use syntax::{SyntaxElement, SyntaxKind, SyntaxToken, TextRange, T}; |
3 | 3 | ||
4 | use crate::{HighlightTag, HighlightedRange}; | 4 | use crate::{HlRange, HlTag}; |
5 | 5 | ||
6 | #[derive(Default)] | 6 | #[derive(Default)] |
7 | pub(super) struct MacroRulesHighlighter { | 7 | pub(super) struct MacroRulesHighlighter { |
@@ -19,13 +19,13 @@ impl MacroRulesHighlighter { | |||
19 | } | 19 | } |
20 | } | 20 | } |
21 | 21 | ||
22 | pub(super) fn highlight(&self, element: SyntaxElement) -> Option<HighlightedRange> { | 22 | pub(super) fn highlight(&self, element: SyntaxElement) -> Option<HlRange> { |
23 | if let Some(state) = self.state.as_ref() { | 23 | if let Some(state) = self.state.as_ref() { |
24 | if matches!(state.rule_state, RuleState::Matcher | RuleState::Expander) { | 24 | if matches!(state.rule_state, RuleState::Matcher | RuleState::Expander) { |
25 | if let Some(range) = is_metavariable(element) { | 25 | if let Some(range) = is_metavariable(element) { |
26 | return Some(HighlightedRange { | 26 | return Some(HlRange { |
27 | range, | 27 | range, |
28 | highlight: HighlightTag::UnresolvedReference.into(), | 28 | highlight: HlTag::UnresolvedReference.into(), |
29 | binding_hash: None, | 29 | binding_hash: None, |
30 | }); | 30 | }); |
31 | } | 31 | } |
@@ -119,7 +119,7 @@ fn is_metavariable(element: SyntaxElement) -> Option<TextRange> { | |||
119 | let tok = element.as_token()?; | 119 | let tok = element.as_token()?; |
120 | match tok.kind() { | 120 | match tok.kind() { |
121 | kind if kind == SyntaxKind::IDENT || kind.is_keyword() => { | 121 | kind if kind == SyntaxKind::IDENT || kind.is_keyword() => { |
122 | if let Some(_dollar) = tok.prev_token().filter(|t| t.kind() == SyntaxKind::DOLLAR) { | 122 | if let Some(_dollar) = tok.prev_token().filter(|t| t.kind() == T![$]) { |
123 | return Some(tok.text_range()); | 123 | return Some(tok.text_range()); |
124 | } | 124 | } |
125 | } | 125 | } |
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs index 8b8867079..8dd05ac52 100644 --- a/crates/ide/src/syntax_highlighting/tags.rs +++ b/crates/ide/src/syntax_highlighting/tags.rs | |||
@@ -7,15 +7,15 @@ use crate::SymbolKind; | |||
7 | 7 | ||
8 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] | 8 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] |
9 | pub struct Highlight { | 9 | pub struct Highlight { |
10 | pub tag: HighlightTag, | 10 | pub tag: HlTag, |
11 | pub modifiers: HighlightModifiers, | 11 | pub mods: HlMods, |
12 | } | 12 | } |
13 | 13 | ||
14 | #[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] | 14 | #[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] |
15 | pub struct HighlightModifiers(u32); | 15 | pub struct HlMods(u32); |
16 | 16 | ||
17 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] | 17 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] |
18 | pub enum HighlightTag { | 18 | pub enum HlTag { |
19 | Symbol(SymbolKind), | 19 | Symbol(SymbolKind), |
20 | 20 | ||
21 | BoolLiteral, | 21 | BoolLiteral, |
@@ -29,17 +29,17 @@ pub enum HighlightTag { | |||
29 | EscapeSequence, | 29 | EscapeSequence, |
30 | FormatSpecifier, | 30 | FormatSpecifier, |
31 | Keyword, | 31 | Keyword, |
32 | Punctuation, | 32 | Punctuation(HlPunct), |
33 | Operator, | 33 | Operator, |
34 | UnresolvedReference, | 34 | UnresolvedReference, |
35 | 35 | ||
36 | // For things which don't have proper Tag, but want to use modifiers. | 36 | // For things which don't have a specific highlight. |
37 | Dummy, | 37 | None, |
38 | } | 38 | } |
39 | 39 | ||
40 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] | 40 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] |
41 | #[repr(u8)] | 41 | #[repr(u8)] |
42 | pub enum HighlightModifier { | 42 | pub enum HlMod { |
43 | /// Used to differentiate individual elements within attributes. | 43 | /// Used to differentiate individual elements within attributes. |
44 | Attribute = 0, | 44 | Attribute = 0, |
45 | /// Used with keywords like `if` and `break`. | 45 | /// Used with keywords like `if` and `break`. |
@@ -61,10 +61,32 @@ pub enum HighlightModifier { | |||
61 | Unsafe, | 61 | Unsafe, |
62 | } | 62 | } |
63 | 63 | ||
64 | impl HighlightTag { | 64 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] |
65 | pub enum HlPunct { | ||
66 | /// [] | ||
67 | Bracket, | ||
68 | /// {} | ||
69 | Brace, | ||
70 | /// () | ||
71 | Parenthesis, | ||
72 | /// <> | ||
73 | Angle, | ||
74 | /// , | ||
75 | Comma, | ||
76 | /// . | ||
77 | Dot, | ||
78 | /// : | ||
79 | Colon, | ||
80 | /// ; | ||
81 | Semi, | ||
82 | /// | ||
83 | Other, | ||
84 | } | ||
85 | |||
86 | impl HlTag { | ||
65 | fn as_str(self) -> &'static str { | 87 | fn as_str(self) -> &'static str { |
66 | match self { | 88 | match self { |
67 | HighlightTag::Symbol(symbol) => match symbol { | 89 | HlTag::Symbol(symbol) => match symbol { |
68 | SymbolKind::Const => "constant", | 90 | SymbolKind::Const => "constant", |
69 | SymbolKind::Static => "static", | 91 | SymbolKind::Static => "static", |
70 | SymbolKind::Enum => "enum", | 92 | SymbolKind::Enum => "enum", |
@@ -86,59 +108,69 @@ impl HighlightTag { | |||
86 | SymbolKind::SelfParam => "self_keyword", | 108 | SymbolKind::SelfParam => "self_keyword", |
87 | SymbolKind::Impl => "self_type", | 109 | SymbolKind::Impl => "self_type", |
88 | }, | 110 | }, |
89 | HighlightTag::Attribute => "attribute", | 111 | HlTag::Attribute => "attribute", |
90 | HighlightTag::BoolLiteral => "bool_literal", | 112 | HlTag::BoolLiteral => "bool_literal", |
91 | HighlightTag::BuiltinType => "builtin_type", | 113 | HlTag::BuiltinType => "builtin_type", |
92 | HighlightTag::ByteLiteral => "byte_literal", | 114 | HlTag::ByteLiteral => "byte_literal", |
93 | HighlightTag::CharLiteral => "char_literal", | 115 | HlTag::CharLiteral => "char_literal", |
94 | HighlightTag::Comment => "comment", | 116 | HlTag::Comment => "comment", |
95 | HighlightTag::EscapeSequence => "escape_sequence", | 117 | HlTag::EscapeSequence => "escape_sequence", |
96 | HighlightTag::FormatSpecifier => "format_specifier", | 118 | HlTag::FormatSpecifier => "format_specifier", |
97 | HighlightTag::Dummy => "dummy", | 119 | HlTag::Keyword => "keyword", |
98 | HighlightTag::Keyword => "keyword", | 120 | HlTag::Punctuation(punct) => match punct { |
99 | HighlightTag::Punctuation => "punctuation", | 121 | HlPunct::Bracket => "bracket", |
100 | HighlightTag::NumericLiteral => "numeric_literal", | 122 | HlPunct::Brace => "brace", |
101 | HighlightTag::Operator => "operator", | 123 | HlPunct::Parenthesis => "parenthesis", |
102 | HighlightTag::StringLiteral => "string_literal", | 124 | HlPunct::Angle => "angle", |
103 | HighlightTag::UnresolvedReference => "unresolved_reference", | 125 | HlPunct::Comma => "comma", |
126 | HlPunct::Dot => "dot", | ||
127 | HlPunct::Colon => "colon", | ||
128 | HlPunct::Semi => "semicolon", | ||
129 | HlPunct::Other => "punctuation", | ||
130 | }, | ||
131 | HlTag::NumericLiteral => "numeric_literal", | ||
132 | HlTag::Operator => "operator", | ||
133 | HlTag::StringLiteral => "string_literal", | ||
134 | HlTag::UnresolvedReference => "unresolved_reference", | ||
135 | HlTag::None => "none", | ||
104 | } | 136 | } |
105 | } | 137 | } |
106 | } | 138 | } |
107 | 139 | ||
108 | impl fmt::Display for HighlightTag { | 140 | impl fmt::Display for HlTag { |
109 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 141 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
110 | fmt::Display::fmt(self.as_str(), f) | 142 | fmt::Display::fmt(self.as_str(), f) |
111 | } | 143 | } |
112 | } | 144 | } |
113 | 145 | ||
114 | impl HighlightModifier { | 146 | impl HlMod { |
115 | const ALL: &'static [HighlightModifier; HighlightModifier::Unsafe as u8 as usize + 1] = &[ | 147 | const ALL: &'static [HlMod; HlMod::Unsafe as u8 as usize + 1] = &[ |
116 | HighlightModifier::Attribute, | 148 | HlMod::Attribute, |
117 | HighlightModifier::ControlFlow, | 149 | HlMod::ControlFlow, |
118 | HighlightModifier::Definition, | 150 | HlMod::Definition, |
119 | HighlightModifier::Documentation, | 151 | HlMod::Documentation, |
120 | HighlightModifier::Injected, | 152 | HlMod::Injected, |
121 | HighlightModifier::Mutable, | 153 | HlMod::Mutable, |
122 | HighlightModifier::Consuming, | 154 | HlMod::Consuming, |
123 | HighlightModifier::Callable, | 155 | HlMod::Callable, |
124 | HighlightModifier::Static, | 156 | HlMod::Static, |
125 | HighlightModifier::Associated, | 157 | HlMod::Associated, |
126 | HighlightModifier::Unsafe, | 158 | HlMod::Unsafe, |
127 | ]; | 159 | ]; |
128 | 160 | ||
129 | fn as_str(self) -> &'static str { | 161 | fn as_str(self) -> &'static str { |
130 | match self { | 162 | match self { |
131 | HighlightModifier::Attribute => "attribute", | 163 | HlMod::Attribute => "attribute", |
132 | HighlightModifier::ControlFlow => "control", | 164 | HlMod::ControlFlow => "control", |
133 | HighlightModifier::Definition => "declaration", | 165 | HlMod::Definition => "declaration", |
134 | HighlightModifier::Documentation => "documentation", | 166 | HlMod::Documentation => "documentation", |
135 | HighlightModifier::Injected => "injected", | 167 | HlMod::Injected => "injected", |
136 | HighlightModifier::Mutable => "mutable", | 168 | HlMod::Mutable => "mutable", |
137 | HighlightModifier::Consuming => "consuming", | 169 | HlMod::Consuming => "consuming", |
138 | HighlightModifier::Unsafe => "unsafe", | 170 | HlMod::Unsafe => "unsafe", |
139 | HighlightModifier::Callable => "callable", | 171 | HlMod::Callable => "callable", |
140 | HighlightModifier::Static => "static", | 172 | HlMod::Static => "static", |
141 | HighlightModifier::Associated => "associated", | 173 | HlMod::Associated => "associated", |
142 | } | 174 | } |
143 | } | 175 | } |
144 | 176 | ||
@@ -147,7 +179,7 @@ impl HighlightModifier { | |||
147 | } | 179 | } |
148 | } | 180 | } |
149 | 181 | ||
150 | impl fmt::Display for HighlightModifier { | 182 | impl fmt::Display for HlMod { |
151 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 183 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
152 | fmt::Display::fmt(self.as_str(), f) | 184 | fmt::Display::fmt(self.as_str(), f) |
153 | } | 185 | } |
@@ -156,60 +188,63 @@ impl fmt::Display for HighlightModifier { | |||
156 | impl fmt::Display for Highlight { | 188 | impl fmt::Display for Highlight { |
157 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 189 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
158 | write!(f, "{}", self.tag)?; | 190 | write!(f, "{}", self.tag)?; |
159 | for modifier in self.modifiers.iter() { | 191 | for modifier in self.mods.iter() { |
160 | write!(f, ".{}", modifier)? | 192 | write!(f, ".{}", modifier)? |
161 | } | 193 | } |
162 | Ok(()) | 194 | Ok(()) |
163 | } | 195 | } |
164 | } | 196 | } |
165 | 197 | ||
166 | impl From<HighlightTag> for Highlight { | 198 | impl From<HlTag> for Highlight { |
167 | fn from(tag: HighlightTag) -> Highlight { | 199 | fn from(tag: HlTag) -> Highlight { |
168 | Highlight::new(tag) | 200 | Highlight::new(tag) |
169 | } | 201 | } |
170 | } | 202 | } |
171 | 203 | ||
172 | impl Highlight { | 204 | impl Highlight { |
173 | pub(crate) fn new(tag: HighlightTag) -> Highlight { | 205 | pub(crate) fn new(tag: HlTag) -> Highlight { |
174 | Highlight { tag, modifiers: HighlightModifiers::default() } | 206 | Highlight { tag, mods: HlMods::default() } |
207 | } | ||
208 | pub fn is_empty(&self) -> bool { | ||
209 | self.tag == HlTag::None && self.mods == HlMods::default() | ||
175 | } | 210 | } |
176 | } | 211 | } |
177 | 212 | ||
178 | impl ops::BitOr<HighlightModifier> for HighlightTag { | 213 | impl ops::BitOr<HlMod> for HlTag { |
179 | type Output = Highlight; | 214 | type Output = Highlight; |
180 | 215 | ||
181 | fn bitor(self, rhs: HighlightModifier) -> Highlight { | 216 | fn bitor(self, rhs: HlMod) -> Highlight { |
182 | Highlight::new(self) | rhs | 217 | Highlight::new(self) | rhs |
183 | } | 218 | } |
184 | } | 219 | } |
185 | 220 | ||
186 | impl ops::BitOrAssign<HighlightModifier> for HighlightModifiers { | 221 | impl ops::BitOrAssign<HlMod> for HlMods { |
187 | fn bitor_assign(&mut self, rhs: HighlightModifier) { | 222 | fn bitor_assign(&mut self, rhs: HlMod) { |
188 | self.0 |= rhs.mask(); | 223 | self.0 |= rhs.mask(); |
189 | } | 224 | } |
190 | } | 225 | } |
191 | 226 | ||
192 | impl ops::BitOrAssign<HighlightModifier> for Highlight { | 227 | impl ops::BitOrAssign<HlMod> for Highlight { |
193 | fn bitor_assign(&mut self, rhs: HighlightModifier) { | 228 | fn bitor_assign(&mut self, rhs: HlMod) { |
194 | self.modifiers |= rhs; | 229 | self.mods |= rhs; |
195 | } | 230 | } |
196 | } | 231 | } |
197 | 232 | ||
198 | impl ops::BitOr<HighlightModifier> for Highlight { | 233 | impl ops::BitOr<HlMod> for Highlight { |
199 | type Output = Highlight; | 234 | type Output = Highlight; |
200 | 235 | ||
201 | fn bitor(mut self, rhs: HighlightModifier) -> Highlight { | 236 | fn bitor(mut self, rhs: HlMod) -> Highlight { |
202 | self |= rhs; | 237 | self |= rhs; |
203 | self | 238 | self |
204 | } | 239 | } |
205 | } | 240 | } |
206 | 241 | ||
207 | impl HighlightModifiers { | 242 | impl HlMods { |
208 | pub fn contains(self, m: HighlightModifier) -> bool { | 243 | pub fn contains(self, m: HlMod) -> bool { |
209 | self.0 & m.mask() == m.mask() | 244 | self.0 & m.mask() == m.mask() |
210 | } | 245 | } |
211 | 246 | ||
212 | pub fn iter(self) -> impl Iterator<Item = HighlightModifier> { | 247 | pub fn iter(self) -> impl Iterator<Item = HlMod> { |
213 | HighlightModifier::ALL.iter().copied().filter(move |it| self.0 & it.mask() == it.mask()) | 248 | HlMod::ALL.iter().copied().filter(move |it| self.0 & it.mask() == it.mask()) |
214 | } | 249 | } |
215 | } | 250 | } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html index 506ebe60e..e36e6fc3f 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html | |||
@@ -36,22 +36,22 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
36 | 36 | ||
37 | .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } | 37 | .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } |
38 | </style> | 38 | </style> |
39 | <pre><code><span class="keyword">fn</span> <span class="function declaration">not_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> | 39 | <pre><code><span class="keyword">fn</span> <span class="function declaration">not_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> |
40 | 40 | ||
41 | <span class="keyword">struct</span> <span class="struct declaration">foo</span> <span class="punctuation">{</span><span class="punctuation">}</span> | 41 | <span class="keyword">struct</span> <span class="struct declaration">foo</span> <span class="brace">{</span><span class="brace">}</span> |
42 | 42 | ||
43 | <span class="keyword">impl</span> <span class="struct">foo</span> <span class="punctuation">{</span> | 43 | <span class="keyword">impl</span> <span class="struct">foo</span> <span class="brace">{</span> |
44 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static associated">is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> | 44 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static associated">is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> |
45 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">is_not_static</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> | 45 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> |
46 | <span class="punctuation">}</span> | 46 | <span class="brace">}</span> |
47 | 47 | ||
48 | <span class="keyword">trait</span> <span class="trait declaration">t</span> <span class="punctuation">{</span> | 48 | <span class="keyword">trait</span> <span class="trait declaration">t</span> <span class="brace">{</span> |
49 | <span class="keyword">fn</span> <span class="function declaration static associated">t_is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> | 49 | <span class="keyword">fn</span> <span class="function declaration static associated">t_is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> |
50 | <span class="keyword">fn</span> <span class="function declaration associated">t_is_not_static</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> | 50 | <span class="keyword">fn</span> <span class="function declaration associated">t_is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> |
51 | <span class="punctuation">}</span> | 51 | <span class="brace">}</span> |
52 | 52 | ||
53 | <span class="keyword">impl</span> <span class="trait">t</span> <span class="keyword">for</span> <span class="struct">foo</span> <span class="punctuation">{</span> | 53 | <span class="keyword">impl</span> <span class="trait">t</span> <span class="keyword">for</span> <span class="struct">foo</span> <span class="brace">{</span> |
54 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static associated">is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> | 54 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static associated">is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> |
55 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">is_not_static</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> | 55 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> |
56 | <span class="punctuation">}</span> | 56 | <span class="brace">}</span> |
57 | </code></pre> \ No newline at end of file | 57 | </code></pre> \ No newline at end of file |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index 4dd7413ba..6dadda1c1 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html | |||
@@ -37,67 +37,72 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
37 | .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } | 37 | .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } |
38 | </style> | 38 | </style> |
39 | <pre><code><span class="comment documentation">/// ```</span> | 39 | <pre><code><span class="comment documentation">/// ```</span> |
40 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="dummy injected"> </span><span class="punctuation injected">_</span><span class="dummy injected"> </span><span class="operator injected">=</span><span class="dummy injected"> </span><span class="string_literal injected">"early doctests should not go boom"</span><span class="punctuation injected">;</span><span class="punctuation injected"> | 40 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="string_literal injected">"early doctests should not go boom"</span><span class="semicolon injected">;</span> |
41 | </span><span class="comment documentation">/// ```</span> | 41 | <span class="comment documentation">/// ```</span> |
42 | <span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="punctuation">{</span> | 42 | <span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="brace">{</span> |
43 | <span class="field declaration">bar</span><span class="punctuation">:</span> <span class="builtin_type">bool</span><span class="punctuation">,</span> | 43 | <span class="field declaration">bar</span><span class="colon">:</span> <span class="builtin_type">bool</span><span class="comma">,</span> |
44 | <span class="punctuation">}</span> | 44 | <span class="brace">}</span> |
45 | 45 | ||
46 | <span class="keyword">impl</span> <span class="struct">Foo</span> <span class="punctuation">{</span> | 46 | <span class="keyword">impl</span> <span class="struct">Foo</span> <span class="brace">{</span> |
47 | <span class="keyword">pub</span> <span class="keyword">const</span> <span class="constant declaration associated">bar</span><span class="punctuation">:</span> <span class="builtin_type">bool</span> <span class="operator">=</span> <span class="bool_literal">true</span><span class="punctuation">;</span> | 47 | <span class="comment documentation">/// ```</span> |
48 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="string_literal injected">"Call me</span> | ||
49 | <span class="comment">// KILLER WHALE</span> | ||
50 | <span class="comment documentation">/// </span><span class="string_literal injected"> Ishmael."</span><span class="semicolon injected">;</span> | ||
51 | <span class="comment documentation">/// ```</span> | ||
52 | <span class="keyword">pub</span> <span class="keyword">const</span> <span class="constant declaration associated">bar</span><span class="colon">:</span> <span class="builtin_type">bool</span> <span class="operator">=</span> <span class="bool_literal">true</span><span class="semicolon">;</span> | ||
48 | 53 | ||
49 | <span class="comment documentation">/// Constructs a new `Foo`.</span> | 54 | <span class="comment documentation">/// Constructs a new `Foo`.</span> |
50 | <span class="comment documentation">///</span> | 55 | <span class="comment documentation">///</span> |
51 | <span class="comment documentation">/// # Examples</span> | 56 | <span class="comment documentation">/// # Examples</span> |
52 | <span class="comment documentation">///</span> | 57 | <span class="comment documentation">///</span> |
53 | <span class="comment documentation">/// ```</span> | 58 | <span class="comment documentation">/// ```</span> |
54 | <span class="comment documentation">/// #</span><span class="dummy injected"> </span><span class="attribute attribute injected">#</span><span class="attribute attribute injected">!</span><span class="attribute attribute injected">[</span><span class="function attribute injected">allow</span><span class="punctuation attribute injected">(</span><span class="attribute attribute injected">unused_mut</span><span class="punctuation attribute injected">)</span><span class="attribute attribute injected">]</span> | 59 | <span class="comment documentation">/// #</span><span class="none injected"> </span><span class="attribute attribute injected">#</span><span class="attribute attribute injected">!</span><span class="attribute attribute injected">[</span><span class="function attribute injected">allow</span><span class="parenthesis attribute injected">(</span><span class="attribute attribute injected">unused_mut</span><span class="parenthesis attribute injected">)</span><span class="attribute attribute injected">]</span> |
55 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="dummy injected"> </span><span class="keyword injected">mut</span><span class="dummy injected"> </span><span class="variable declaration injected mutable">foo</span><span class="punctuation injected">:</span><span class="dummy injected"> </span><span class="struct injected">Foo</span><span class="dummy injected"> </span><span class="operator injected">=</span><span class="dummy injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected"> | 60 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="keyword injected">mut</span><span class="none injected"> </span><span class="variable declaration injected mutable">foo</span><span class="colon injected">:</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> |
56 | </span> <span class="comment documentation">/// ```</span> | 61 | <span class="comment documentation">/// ```</span> |
57 | <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration static associated">new</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="struct">Foo</span> <span class="punctuation">{</span> | 62 | <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration static associated">new</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="struct">Foo</span> <span class="brace">{</span> |
58 | <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">bar</span><span class="punctuation">:</span> <span class="bool_literal">true</span> <span class="punctuation">}</span> | 63 | <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">bar</span><span class="colon">:</span> <span class="bool_literal">true</span> <span class="brace">}</span> |
59 | <span class="punctuation">}</span> | 64 | <span class="brace">}</span> |
60 | 65 | ||
61 | <span class="comment documentation">/// `bar` method on `Foo`.</span> | 66 | <span class="comment documentation">/// `bar` method on `Foo`.</span> |
62 | <span class="comment documentation">///</span> | 67 | <span class="comment documentation">///</span> |
63 | <span class="comment documentation">/// # Examples</span> | 68 | <span class="comment documentation">/// # Examples</span> |
64 | <span class="comment documentation">///</span> | 69 | <span class="comment documentation">///</span> |
65 | <span class="comment documentation">/// ```</span> | 70 | <span class="comment documentation">/// ```</span> |
66 | <span class="comment documentation">/// </span><span class="keyword injected">use</span><span class="dummy injected"> </span><span class="module injected">x</span><span class="operator injected">::</span><span class="module injected">y</span><span class="punctuation injected">;</span> | 71 | <span class="comment documentation">/// </span><span class="keyword injected">use</span><span class="none injected"> </span><span class="module injected">x</span><span class="operator injected">::</span><span class="module injected">y</span><span class="semicolon injected">;</span> |
67 | <span class="comment documentation">///</span> | 72 | <span class="comment documentation">///</span> |
68 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="dummy injected"> </span><span class="variable declaration injected">foo</span><span class="dummy injected"> </span><span class="operator injected">=</span><span class="dummy injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span> | 73 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">foo</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> |
69 | <span class="comment documentation">///</span> | 74 | <span class="comment documentation">///</span> |
70 | <span class="comment documentation">/// </span><span class="comment injected">// calls bar on foo</span> | 75 | <span class="comment documentation">/// </span><span class="comment injected">// calls bar on foo</span> |
71 | <span class="comment documentation">/// </span><span class="macro injected">assert!</span><span class="punctuation injected">(</span><span class="dummy injected">foo</span><span class="operator injected">.</span><span class="dummy injected">bar</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span> | 76 | <span class="comment documentation">/// </span><span class="macro injected">assert!</span><span class="parenthesis injected">(</span><span class="none injected">foo</span><span class="operator injected">.</span><span class="none injected">bar</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> |
72 | <span class="comment documentation">///</span> | 77 | <span class="comment documentation">///</span> |
73 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="dummy injected"> </span><span class="variable declaration injected">bar</span><span class="dummy injected"> </span><span class="operator injected">=</span><span class="dummy injected"> </span><span class="variable injected">foo</span><span class="operator injected">.</span><span class="field injected">bar</span><span class="dummy injected"> </span><span class="operator injected">||</span><span class="dummy injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="constant injected">bar</span><span class="punctuation injected">;</span> | 78 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">bar</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="variable injected">foo</span><span class="operator injected">.</span><span class="field injected">bar</span><span class="none injected"> </span><span class="operator injected">||</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="constant injected">bar</span><span class="semicolon injected">;</span> |
74 | <span class="comment documentation">///</span> | 79 | <span class="comment documentation">///</span> |
75 | <span class="comment documentation">/// </span><span class="comment injected">/* multi-line | 80 | <span class="comment documentation">/// </span><span class="comment injected">/* multi-line</span> |
76 | </span><span class="comment documentation">/// </span><span class="comment injected"> comment */</span> | 81 | <span class="comment documentation">/// </span><span class="comment injected"> comment */</span> |
77 | <span class="comment documentation">///</span> | 82 | <span class="comment documentation">///</span> |
78 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="dummy injected"> </span><span class="variable declaration injected">multi_line_string</span><span class="dummy injected"> </span><span class="operator injected">=</span><span class="dummy injected"> </span><span class="string_literal injected">"Foo | 83 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">multi_line_string</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="string_literal injected">"Foo</span> |
79 | </span><span class="comment documentation">/// </span><span class="string_literal injected"> bar | 84 | <span class="comment documentation">/// </span><span class="string_literal injected"> bar</span> |
80 | </span><span class="comment documentation">/// </span><span class="string_literal injected"> "</span><span class="punctuation injected">;</span> | 85 | <span class="comment documentation">/// </span><span class="string_literal injected"> "</span><span class="semicolon injected">;</span> |
81 | <span class="comment documentation">///</span> | 86 | <span class="comment documentation">///</span> |
82 | <span class="comment documentation">/// ```</span> | 87 | <span class="comment documentation">/// ```</span> |
83 | <span class="comment documentation">///</span> | 88 | <span class="comment documentation">///</span> |
84 | <span class="comment documentation">/// ```rust,no_run</span> | 89 | <span class="comment documentation">/// ```rust,no_run</span> |
85 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="dummy injected"> </span><span class="variable declaration injected">foobar</span><span class="dummy injected"> </span><span class="operator injected">=</span><span class="dummy injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="operator injected">.</span><span class="function injected">bar</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected"> | 90 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">foobar</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="operator injected">.</span><span class="function injected">bar</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> |
86 | </span> <span class="comment documentation">/// ```</span> | 91 | <span class="comment documentation">/// ```</span> |
87 | <span class="comment documentation">///</span> | 92 | <span class="comment documentation">///</span> |
88 | <span class="comment documentation">/// ```sh</span> | 93 | <span class="comment documentation">/// ```sh</span> |
89 | <span class="comment documentation">/// echo 1</span> | 94 | <span class="comment documentation">/// echo 1</span> |
90 | <span class="comment documentation">/// ```</span> | 95 | <span class="comment documentation">/// ```</span> |
91 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">foo</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="builtin_type">bool</span> <span class="punctuation">{</span> | 96 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">foo</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="builtin_type">bool</span> <span class="brace">{</span> |
92 | <span class="bool_literal">true</span> | 97 | <span class="bool_literal">true</span> |
93 | <span class="punctuation">}</span> | 98 | <span class="brace">}</span> |
94 | <span class="punctuation">}</span> | 99 | <span class="brace">}</span> |
95 | 100 | ||
96 | <span class="comment documentation">/// ```</span> | 101 | <span class="comment documentation">/// ```</span> |
97 | <span class="comment documentation">/// </span><span class="macro injected">noop!</span><span class="punctuation injected">(</span><span class="numeric_literal injected">1</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected"> | 102 | <span class="comment documentation">/// </span><span class="macro injected">noop!</span><span class="parenthesis injected">(</span><span class="numeric_literal injected">1</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> |
98 | </span><span class="comment documentation">/// ```</span> | 103 | <span class="comment documentation">/// ```</span> |
99 | <span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">noop</span> <span class="punctuation">{</span> | 104 | <span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span> |
100 | <span class="punctuation">(</span><span class="punctuation">$</span>expr<span class="punctuation">:</span>expr<span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">></span> <span class="punctuation">{</span> | 105 | <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="brace">{</span> |
101 | <span class="punctuation">$</span>expr | 106 | <span class="punctuation">$</span>expr |
102 | <span class="punctuation">}</span> | 107 | <span class="brace">}</span> |
103 | <span class="punctuation">}</span></code></pre> \ No newline at end of file | 108 | <span class="brace">}</span></code></pre> \ No newline at end of file |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html index ed452586a..6f7a7ffff 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html | |||
@@ -36,6 +36,6 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
36 | 36 | ||
37 | .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } | 37 | .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } |
38 | </style> | 38 | </style> |
39 | <pre><code><span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module">std</span><span class="punctuation">;</span> | 39 | <pre><code><span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module">std</span><span class="semicolon">;</span> |
40 | <span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module">alloc</span> <span class="keyword">as</span> <span class="module">abc</span><span class="punctuation">;</span> | 40 | <span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module">alloc</span> <span class="keyword">as</span> <span class="module">abc</span><span class="semicolon">;</span> |
41 | </code></pre> \ No newline at end of file | 41 | </code></pre> \ No newline at end of file |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html index 92e7dc3e4..753b535b5 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html | |||
@@ -36,14 +36,14 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
36 | 36 | ||
37 | .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } | 37 | .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } |
38 | </style> | 38 | </style> |
39 | <pre><code><span class="keyword">fn</span> <span class="function declaration">fixture</span><span class="punctuation">(</span><span class="value_param declaration">ra_fixture</span><span class="punctuation">:</span> <span class="operator">&</span><span class="builtin_type">str</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> | 39 | <pre><code><span class="keyword">fn</span> <span class="function declaration">fixture</span><span class="parenthesis">(</span><span class="value_param declaration">ra_fixture</span><span class="colon">:</span> <span class="operator">&</span><span class="builtin_type">str</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> |
40 | 40 | ||
41 | <span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> | 41 | <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> |
42 | <span class="function">fixture</span><span class="punctuation">(</span><span class="string_literal">r#"</span> | 42 | <span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r#"</span> |
43 | <span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="punctuation">{</span> | 43 | <span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="brace">{</span> |
44 | <span class="keyword">fn</span> <span class="function declaration static associated">foo</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> | 44 | <span class="keyword">fn</span> <span class="function declaration static associated">foo</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> |
45 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"2 + 2 = {}"</span><span class="punctuation">,</span> <span class="numeric_literal">4</span><span class="punctuation">)</span><span class="punctuation">;</span> | 45 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"2 + 2 = {}"</span><span class="comma">,</span> <span class="numeric_literal">4</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
46 | <span class="punctuation">}</span> | 46 | <span class="brace">}</span> |
47 | <span class="punctuation">}</span><span class="string_literal">"#</span> | 47 | <span class="brace">}</span><span class="string_literal">"#</span> |
48 | <span class="punctuation">)</span><span class="punctuation">;</span> | 48 | <span class="parenthesis">)</span><span class="semicolon">;</span> |
49 | <span class="punctuation">}</span></code></pre> \ No newline at end of file | 49 | <span class="brace">}</span></code></pre> \ No newline at end of file |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index 31dad5d42..66d80c4b6 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html | |||
@@ -36,64 +36,64 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
36 | 36 | ||
37 | .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } | 37 | .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } |
38 | </style> | 38 | </style> |
39 | <pre><code><span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">println</span> <span class="punctuation">{</span> | 39 | <pre><code><span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">println</span> <span class="brace">{</span> |
40 | <span class="punctuation">(</span><span class="punctuation">$</span><span class="punctuation">(</span><span class="punctuation">$</span>arg<span class="punctuation">:</span>tt<span class="punctuation">)</span><span class="punctuation">*</span><span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">></span> <span class="punctuation">(</span><span class="punctuation">{</span> | 40 | <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="parenthesis">(</span><span class="brace">{</span> |
41 | <span class="punctuation">$</span>crate<span class="punctuation">:</span><span class="punctuation">:</span>io<span class="punctuation">:</span><span class="punctuation">:</span>_print<span class="punctuation">(</span><span class="punctuation">$</span>crate<span class="punctuation">:</span><span class="punctuation">:</span>format_args_nl<span class="punctuation">!</span><span class="punctuation">(</span><span class="punctuation">$</span><span class="punctuation">(</span><span class="punctuation">$</span>arg<span class="punctuation">)</span><span class="punctuation">*</span><span class="punctuation">)</span><span class="punctuation">)</span><span class="punctuation">;</span> | 41 | <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>io<span class="colon">:</span><span class="colon">:</span>_print<span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>format_args_nl<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
42 | <span class="punctuation">}</span><span class="punctuation">)</span> | 42 | <span class="brace">}</span><span class="parenthesis">)</span> |
43 | <span class="punctuation">}</span> | 43 | <span class="brace">}</span> |
44 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">rustc_builtin_macro</span><span class="attribute attribute">]</span> | 44 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">rustc_builtin_macro</span><span class="attribute attribute">]</span> |
45 | <span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">format_args_nl</span> <span class="punctuation">{</span> | 45 | <span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">format_args_nl</span> <span class="brace">{</span> |
46 | <span class="punctuation">(</span><span class="punctuation">$</span>fmt<span class="punctuation">:</span>expr<span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">></span> <span class="punctuation">{</span><span class="punctuation">{</span> <span class="comment">/* compiler built-in */</span> <span class="punctuation">}</span><span class="punctuation">}</span><span class="punctuation">;</span> | 46 | <span class="parenthesis">(</span><span class="punctuation">$</span>fmt<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="brace">{</span><span class="brace">{</span> <span class="comment">/* compiler built-in */</span> <span class="brace">}</span><span class="brace">}</span><span class="semicolon">;</span> |
47 | <span class="punctuation">(</span><span class="punctuation">$</span>fmt<span class="punctuation">:</span>expr<span class="punctuation">,</span> <span class="punctuation">$</span><span class="punctuation">(</span><span class="punctuation">$</span>args<span class="punctuation">:</span>tt<span class="punctuation">)</span><span class="punctuation">*</span><span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">></span> <span class="punctuation">{</span><span class="punctuation">{</span> <span class="comment">/* compiler built-in */</span> <span class="punctuation">}</span><span class="punctuation">}</span><span class="punctuation">;</span> | 47 | <span class="parenthesis">(</span><span class="punctuation">$</span>fmt<span class="colon">:</span>expr<span class="comma">,</span> <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>args<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="brace">{</span><span class="brace">{</span> <span class="comment">/* compiler built-in */</span> <span class="brace">}</span><span class="brace">}</span><span class="semicolon">;</span> |
48 | <span class="punctuation">}</span> | 48 | <span class="brace">}</span> |
49 | 49 | ||
50 | <span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> | 50 | <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> |
51 | <span class="comment">// from https://doc.rust-lang.org/std/fmt/index.html</span> | 51 | <span class="comment">// from https://doc.rust-lang.org/std/fmt/index.html</span> |
52 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello"</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// => "Hello"</span> | 52 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello"</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "Hello"</span> |
53 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="punctuation">,</span> <span class="string_literal">"world"</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// => "Hello, world!"</span> | 53 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"world"</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "Hello, world!"</span> |
54 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> <span class="numeric_literal">1</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// => "The number is 1"</span> | 54 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "The number is 1"</span> |
55 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> <span class="punctuation">(</span><span class="numeric_literal">3</span><span class="punctuation">,</span> <span class="numeric_literal">4</span><span class="punctuation">)</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// => "(3, 4)"</span> | 55 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="parenthesis">(</span><span class="numeric_literal">3</span><span class="comma">,</span> <span class="numeric_literal">4</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "(3, 4)"</span> |
56 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> value<span class="operator">=</span><span class="numeric_literal">4</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// => "4"</span> | 56 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> value<span class="operator">=</span><span class="numeric_literal">4</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "4"</span> |
57 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> <span class="numeric_literal">1</span><span class="punctuation">,</span> <span class="numeric_literal">2</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// => "1 2"</span> | 57 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="comma">,</span> <span class="numeric_literal">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "1 2"</span> |
58 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> <span class="numeric_literal">42</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// => "0042" with leading zerosV</span> | 58 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">42</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "0042" with leading zerosV</span> |
59 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> <span class="numeric_literal">1</span><span class="punctuation">,</span> <span class="numeric_literal">2</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// => "2 1 1 2"</span> | 59 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="comma">,</span> <span class="numeric_literal">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "2 1 1 2"</span> |
60 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> argument <span class="operator">=</span> <span class="string_literal">"test"</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// => "test"</span> | 60 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> argument <span class="operator">=</span> <span class="string_literal">"test"</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "test"</span> |
61 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> <span class="numeric_literal">1</span><span class="punctuation">,</span> name <span class="operator">=</span> <span class="numeric_literal">2</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// => "2 1"</span> | 61 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="comma">,</span> name <span class="operator">=</span> <span class="numeric_literal">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "2 1"</span> |
62 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> a<span class="operator">=</span><span class="string_literal">"a"</span><span class="punctuation">,</span> b<span class="operator">=</span><span class="char_literal">'b'</span><span class="punctuation">,</span> c<span class="operator">=</span><span class="numeric_literal">3</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// => "a 3 b"</span> | 62 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> a<span class="operator">=</span><span class="string_literal">"a"</span><span class="comma">,</span> b<span class="operator">=</span><span class="char_literal">'b'</span><span class="comma">,</span> c<span class="operator">=</span><span class="numeric_literal">3</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "a 3 b"</span> |
63 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">}}"</span><span class="punctuation">,</span> <span class="numeric_literal">2</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// => "{2}"</span> | 63 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">}}"</span><span class="comma">,</span> <span class="numeric_literal">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "{2}"</span> |
64 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">)</span><span class="punctuation">;</span> | 64 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
65 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">,</span> <span class="numeric_literal">5</span><span class="punctuation">)</span><span class="punctuation">;</span> | 65 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
66 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="punctuation">,</span> <span class="numeric_literal">5</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">)</span><span class="punctuation">;</span> | 66 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
67 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">,</span> width <span class="operator">=</span> <span class="numeric_literal">5</span><span class="punctuation">)</span><span class="punctuation">;</span> | 67 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> width <span class="operator">=</span> <span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
68 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier"><</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">)</span><span class="punctuation">;</span> | 68 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier"><</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
69 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier"><</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">)</span><span class="punctuation">;</span> | 69 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier"><</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
70 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">)</span><span class="punctuation">;</span> | 70 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
71 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">></span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">)</span><span class="punctuation">;</span> | 71 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">></span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
72 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="punctuation">,</span> <span class="numeric_literal">5</span><span class="punctuation">)</span><span class="punctuation">;</span> | 72 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
73 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="punctuation">,</span> <span class="numeric_literal">27</span><span class="punctuation">)</span><span class="punctuation">;</span> | 73 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">27</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
74 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="punctuation">,</span> <span class="numeric_literal">5</span><span class="punctuation">)</span><span class="punctuation">;</span> | 74 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
75 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="punctuation">,</span> <span class="punctuation">-</span><span class="numeric_literal">5</span><span class="punctuation">)</span><span class="punctuation">;</span> | 75 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="punctuation">-</span><span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
76 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="punctuation">,</span> <span class="numeric_literal">27</span><span class="punctuation">)</span><span class="punctuation">;</span> | 76 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">27</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
77 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">,</span> <span class="numeric_literal">0.01</span><span class="punctuation">)</span><span class="punctuation">;</span> | 77 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
78 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> <span class="numeric_literal">5</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">,</span> <span class="numeric_literal">0.01</span><span class="punctuation">)</span><span class="punctuation">;</span> | 78 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
79 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">,</span> <span class="numeric_literal">5</span><span class="punctuation">,</span> <span class="numeric_literal">0.01</span><span class="punctuation">)</span><span class="punctuation">;</span> | 79 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
80 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">,</span> <span class="numeric_literal">5</span><span class="punctuation">,</span> <span class="numeric_literal">0.01</span><span class="punctuation">)</span><span class="punctuation">;</span> | 80 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
81 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">,</span> <span class="numeric_literal">5</span><span class="punctuation">,</span> <span class="numeric_literal">0.01</span><span class="punctuation">)</span><span class="punctuation">;</span> | 81 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
82 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">,</span> prec <span class="operator">=</span> <span class="numeric_literal">5</span><span class="punctuation">,</span> number <span class="operator">=</span> <span class="numeric_literal">0.01</span><span class="punctuation">)</span><span class="punctuation">;</span> | 82 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> prec <span class="operator">=</span> <span class="numeric_literal">5</span><span class="comma">,</span> number <span class="operator">=</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
83 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 fractional digits"</span><span class="punctuation">,</span> <span class="string_literal">"Hello"</span><span class="punctuation">,</span> <span class="numeric_literal">3</span><span class="punctuation">,</span> name<span class="operator">=</span><span class="numeric_literal">1234.56</span><span class="punctuation">)</span><span class="punctuation">;</span> | 83 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 fractional digits"</span><span class="comma">,</span> <span class="string_literal">"Hello"</span><span class="comma">,</span> <span class="numeric_literal">3</span><span class="comma">,</span> name<span class="operator">=</span><span class="numeric_literal">1234.56</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
84 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 characters"</span><span class="punctuation">,</span> <span class="string_literal">"Hello"</span><span class="punctuation">,</span> <span class="numeric_literal">3</span><span class="punctuation">,</span> name<span class="operator">=</span><span class="string_literal">"1234.56"</span><span class="punctuation">)</span><span class="punctuation">;</span> | 84 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 characters"</span><span class="comma">,</span> <span class="string_literal">"Hello"</span><span class="comma">,</span> <span class="numeric_literal">3</span><span class="comma">,</span> name<span class="operator">=</span><span class="string_literal">"1234.56"</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
85 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">></span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 right-aligned characters"</span><span class="punctuation">,</span> <span class="string_literal">"Hello"</span><span class="punctuation">,</span> <span class="numeric_literal">3</span><span class="punctuation">,</span> name<span class="operator">=</span><span class="string_literal">"1234.56"</span><span class="punctuation">)</span><span class="punctuation">;</span> | 85 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">></span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 right-aligned characters"</span><span class="comma">,</span> <span class="string_literal">"Hello"</span><span class="comma">,</span> <span class="numeric_literal">3</span><span class="comma">,</span> name<span class="operator">=</span><span class="string_literal">"1234.56"</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
86 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello {{}}"</span><span class="punctuation">)</span><span class="punctuation">;</span> | 86 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello {{}}"</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
87 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"{{ Hello"</span><span class="punctuation">)</span><span class="punctuation">;</span> | 87 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"{{ Hello"</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
88 | 88 | ||
89 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="punctuation">,</span> <span class="string_literal">"world"</span><span class="punctuation">)</span><span class="punctuation">;</span> | 89 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"world"</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
90 | 90 | ||
91 | <span class="comment">// escape sequences</span> | 91 | <span class="comment">// escape sequences</span> |
92 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello</span><span class="escape_sequence">\n</span><span class="string_literal">World"</span><span class="punctuation">)</span><span class="punctuation">;</span> | 92 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello</span><span class="escape_sequence">\n</span><span class="string_literal">World"</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
93 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="escape_sequence">\u{48}</span><span class="escape_sequence">\x65</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6F</span><span class="string_literal"> World"</span><span class="punctuation">)</span><span class="punctuation">;</span> | 93 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="escape_sequence">\u{48}</span><span class="escape_sequence">\x65</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6F</span><span class="string_literal"> World"</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
94 | 94 | ||
95 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> A <span class="operator">=</span> <span class="numeric_literal">92</span><span class="punctuation">)</span><span class="punctuation">;</span> | 95 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> A <span class="operator">=</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
96 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> ничоси <span class="operator">=</span> <span class="numeric_literal">92</span><span class="punctuation">)</span><span class="punctuation">;</span> | 96 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> ничоси <span class="operator">=</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
97 | 97 | ||
98 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">x</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> "</span><span class="punctuation">,</span> thingy<span class="punctuation">,</span> n2<span class="punctuation">)</span><span class="punctuation">;</span> | 98 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">x</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> "</span><span class="comma">,</span> thingy<span class="comma">,</span> n2<span class="parenthesis">)</span><span class="semicolon">;</span> |
99 | <span class="punctuation">}</span></code></pre> \ No newline at end of file | 99 | <span class="brace">}</span></code></pre> \ No newline at end of file |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html index e3a0aa317..9d4d6d4a0 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html | |||
@@ -36,65 +36,65 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
36 | 36 | ||
37 | .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } | 37 | .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } |
38 | </style> | 38 | </style> |
39 | <pre><code><span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_fn</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> | 39 | <pre><code><span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> |
40 | 40 | ||
41 | <span class="keyword">union</span> <span class="union declaration">Union</span> <span class="punctuation">{</span> | 41 | <span class="keyword">union</span> <span class="union declaration">Union</span> <span class="brace">{</span> |
42 | <span class="field declaration">a</span><span class="punctuation">:</span> <span class="builtin_type">u32</span><span class="punctuation">,</span> | 42 | <span class="field declaration">a</span><span class="colon">:</span> <span class="builtin_type">u32</span><span class="comma">,</span> |
43 | <span class="field declaration">b</span><span class="punctuation">:</span> <span class="builtin_type">f32</span><span class="punctuation">,</span> | 43 | <span class="field declaration">b</span><span class="colon">:</span> <span class="builtin_type">f32</span><span class="comma">,</span> |
44 | <span class="punctuation">}</span> | 44 | <span class="brace">}</span> |
45 | 45 | ||
46 | <span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span><span class="punctuation">;</span> | 46 | <span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span><span class="semicolon">;</span> |
47 | 47 | ||
48 | <span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> <span class="punctuation">{</span> | 48 | <span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> <span class="brace">{</span> |
49 | <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration associated unsafe">unsafe_method</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> | 49 | <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration associated unsafe">unsafe_method</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> |
50 | <span class="punctuation">}</span> | 50 | <span class="brace">}</span> |
51 | 51 | ||
52 | <span class="keyword">struct</span> <span class="struct declaration">TypeForStaticMut</span> <span class="punctuation">{</span> | 52 | <span class="keyword">struct</span> <span class="struct declaration">TypeForStaticMut</span> <span class="brace">{</span> |
53 | <span class="field declaration">a</span><span class="punctuation">:</span> <span class="builtin_type">u8</span> | 53 | <span class="field declaration">a</span><span class="colon">:</span> <span class="builtin_type">u8</span> |
54 | <span class="punctuation">}</span> | 54 | <span class="brace">}</span> |
55 | 55 | ||
56 | <span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">global_mut</span><span class="punctuation">:</span> <span class="struct">TypeForStaticMut</span> <span class="operator">=</span> <span class="struct">TypeForStaticMut</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span> | 56 | <span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">global_mut</span><span class="colon">:</span> <span class="struct">TypeForStaticMut</span> <span class="operator">=</span> <span class="struct">TypeForStaticMut</span> <span class="brace">{</span> <span class="field">a</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="semicolon">;</span> |
57 | 57 | ||
58 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">repr</span><span class="punctuation attribute">(</span><span class="attribute attribute">packed</span><span class="punctuation attribute">)</span><span class="attribute attribute">]</span> | 58 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">repr</span><span class="parenthesis attribute">(</span><span class="attribute attribute">packed</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span> |
59 | <span class="keyword">struct</span> <span class="struct declaration">Packed</span> <span class="punctuation">{</span> | 59 | <span class="keyword">struct</span> <span class="struct declaration">Packed</span> <span class="brace">{</span> |
60 | <span class="field declaration">a</span><span class="punctuation">:</span> <span class="builtin_type">u16</span><span class="punctuation">,</span> | 60 | <span class="field declaration">a</span><span class="colon">:</span> <span class="builtin_type">u16</span><span class="comma">,</span> |
61 | <span class="punctuation">}</span> | 61 | <span class="brace">}</span> |
62 | 62 | ||
63 | <span class="keyword">trait</span> <span class="trait declaration">DoTheAutoref</span> <span class="punctuation">{</span> | 63 | <span class="keyword">trait</span> <span class="trait declaration">DoTheAutoref</span> <span class="brace">{</span> |
64 | <span class="keyword">fn</span> <span class="function declaration associated">calls_autoref</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span><span class="punctuation">;</span> | 64 | <span class="keyword">fn</span> <span class="function declaration associated">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
65 | <span class="punctuation">}</span> | 65 | <span class="brace">}</span> |
66 | 66 | ||
67 | <span class="keyword">impl</span> <span class="trait">DoTheAutoref</span> <span class="keyword">for</span> <span class="builtin_type">u16</span> <span class="punctuation">{</span> | 67 | <span class="keyword">impl</span> <span class="trait">DoTheAutoref</span> <span class="keyword">for</span> <span class="builtin_type">u16</span> <span class="brace">{</span> |
68 | <span class="keyword">fn</span> <span class="function declaration associated">calls_autoref</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> | 68 | <span class="keyword">fn</span> <span class="function declaration associated">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> |
69 | <span class="punctuation">}</span> | 69 | <span class="brace">}</span> |
70 | 70 | ||
71 | <span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> | 71 | <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> |
72 | <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="operator">&</span><span class="numeric_literal">5</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="punctuation">_</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="builtin_type">usize</span><span class="punctuation">;</span> | 72 | <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="operator">&</span><span class="numeric_literal">5</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="punctuation">_</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="builtin_type">usize</span><span class="semicolon">;</span> |
73 | <span class="keyword">let</span> <span class="variable declaration">u</span> <span class="operator">=</span> <span class="union">Union</span> <span class="punctuation">{</span> <span class="field">b</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span> | 73 | <span class="keyword">let</span> <span class="variable declaration">u</span> <span class="operator">=</span> <span class="union">Union</span> <span class="brace">{</span> <span class="field">b</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="semicolon">;</span> |
74 | <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span> | 74 | <span class="keyword unsafe">unsafe</span> <span class="brace">{</span> |
75 | <span class="comment">// unsafe fn and method calls</span> | 75 | <span class="comment">// unsafe fn and method calls</span> |
76 | <span class="function unsafe">unsafe_fn</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 76 | <span class="function unsafe">unsafe_fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
77 | <span class="keyword">let</span> <span class="variable declaration">b</span> <span class="operator">=</span> <span class="variable">u</span><span class="operator">.</span><span class="field unsafe">b</span><span class="punctuation">;</span> | 77 | <span class="keyword">let</span> <span class="variable declaration">b</span> <span class="operator">=</span> <span class="variable">u</span><span class="operator">.</span><span class="field unsafe">b</span><span class="semicolon">;</span> |
78 | <span class="keyword control">match</span> <span class="variable">u</span> <span class="punctuation">{</span> | 78 | <span class="keyword control">match</span> <span class="variable">u</span> <span class="brace">{</span> |
79 | <span class="union">Union</span> <span class="punctuation">{</span> <span class="field unsafe">b</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span> <span class="operator">=></span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span> | 79 | <span class="union">Union</span> <span class="brace">{</span> <span class="field unsafe">b</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span> <span class="operator">=></span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span> |
80 | <span class="union">Union</span> <span class="punctuation">{</span> <span class="field unsafe">a</span> <span class="punctuation">}</span> <span class="operator">=></span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span> | 80 | <span class="union">Union</span> <span class="brace">{</span> <span class="field unsafe">a</span> <span class="brace">}</span> <span class="operator">=></span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span> |
81 | <span class="punctuation">}</span> | 81 | <span class="brace">}</span> |
82 | <span class="struct">HasUnsafeFn</span><span class="operator">.</span><span class="function associated unsafe">unsafe_method</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 82 | <span class="struct">HasUnsafeFn</span><span class="operator">.</span><span class="function associated unsafe">unsafe_method</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
83 | 83 | ||
84 | <span class="comment">// unsafe deref</span> | 84 | <span class="comment">// unsafe deref</span> |
85 | <span class="keyword">let</span> <span class="variable declaration">y</span> <span class="operator">=</span> <span class="operator unsafe">*</span><span class="variable">x</span><span class="punctuation">;</span> | 85 | <span class="keyword">let</span> <span class="variable declaration">y</span> <span class="operator">=</span> <span class="operator unsafe">*</span><span class="variable">x</span><span class="semicolon">;</span> |
86 | 86 | ||
87 | <span class="comment">// unsafe access to a static mut</span> | 87 | <span class="comment">// unsafe access to a static mut</span> |
88 | <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="static mutable unsafe">global_mut</span><span class="operator">.</span><span class="field">a</span><span class="punctuation">;</span> | 88 | <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="static mutable unsafe">global_mut</span><span class="operator">.</span><span class="field">a</span><span class="semicolon">;</span> |
89 | 89 | ||
90 | <span class="comment">// unsafe ref of packed fields</span> | 90 | <span class="comment">// unsafe ref of packed fields</span> |
91 | <span class="keyword">let</span> <span class="variable declaration">packed</span> <span class="operator">=</span> <span class="struct">Packed</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span> | 91 | <span class="keyword">let</span> <span class="variable declaration">packed</span> <span class="operator">=</span> <span class="struct">Packed</span> <span class="brace">{</span> <span class="field">a</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="semicolon">;</span> |
92 | <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="operator unsafe">&</span><span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="punctuation">;</span> | 92 | <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="operator unsafe">&</span><span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="semicolon">;</span> |
93 | <span class="keyword">let</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="punctuation">;</span> | 93 | <span class="keyword">let</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="semicolon">;</span> |
94 | <span class="keyword">let</span> <span class="struct">Packed</span> <span class="punctuation">{</span> <span class="keyword unsafe">ref</span> <span class="field">a</span> <span class="punctuation">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="punctuation">;</span> | 94 | <span class="keyword">let</span> <span class="struct">Packed</span> <span class="brace">{</span> <span class="keyword unsafe">ref</span> <span class="field">a</span> <span class="brace">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="semicolon">;</span> |
95 | <span class="keyword">let</span> <span class="struct">Packed</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">_a</span> <span class="punctuation">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="punctuation">;</span> | 95 | <span class="keyword">let</span> <span class="struct">Packed</span> <span class="brace">{</span> <span class="field">a</span><span class="colon">:</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">_a</span> <span class="brace">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="semicolon">;</span> |
96 | 96 | ||
97 | <span class="comment">// unsafe auto ref of packed field</span> | 97 | <span class="comment">// unsafe auto ref of packed field</span> |
98 | <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="operator">.</span><span class="function associated unsafe">calls_autoref</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 98 | <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="operator">.</span><span class="function associated unsafe">calls_autoref</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
99 | <span class="punctuation">}</span> | 99 | <span class="brace">}</span> |
100 | <span class="punctuation">}</span></code></pre> \ No newline at end of file | 100 | <span class="brace">}</span></code></pre> \ No newline at end of file |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html index 02270b077..6b7447c46 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html | |||
@@ -36,187 +36,187 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
36 | 36 | ||
37 | .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } | 37 | .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } |
38 | </style> | 38 | </style> |
39 | <pre><code><span class="keyword">use</span> <span class="module">inner</span><span class="operator">::</span><span class="punctuation">{</span><span class="self_keyword">self</span> <span class="keyword">as</span> <span class="module declaration">inner_mod</span><span class="punctuation">}</span><span class="punctuation">;</span> | 39 | <pre><code><span class="keyword">use</span> <span class="module">inner</span><span class="operator">::</span><span class="brace">{</span><span class="self_keyword">self</span> <span class="keyword">as</span> <span class="module declaration">inner_mod</span><span class="brace">}</span><span class="semicolon">;</span> |
40 | <span class="keyword">mod</span> <span class="module declaration">inner</span> <span class="punctuation">{</span><span class="punctuation">}</span> | 40 | <span class="keyword">mod</span> <span class="module declaration">inner</span> <span class="brace">{</span><span class="brace">}</span> |
41 | 41 | ||
42 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">rustc_builtin_macro</span><span class="attribute attribute">]</span> | 42 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">rustc_builtin_macro</span><span class="attribute attribute">]</span> |
43 | <span class="keyword">macro</span> <span class="unresolved_reference declaration">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span> | 43 | <span class="keyword">macro</span> <span class="unresolved_reference declaration">Copy</span> <span class="brace">{</span><span class="brace">}</span> |
44 | 44 | ||
45 | <span class="comment">// Needed for function consuming vs normal</span> | 45 | <span class="comment">// Needed for function consuming vs normal</span> |
46 | <span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">marker</span> <span class="punctuation">{</span> | 46 | <span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">marker</span> <span class="brace">{</span> |
47 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"copy"</span><span class="attribute attribute">]</span> | 47 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"copy"</span><span class="attribute attribute">]</span> |
48 | <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span> | 48 | <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Copy</span> <span class="brace">{</span><span class="brace">}</span> |
49 | <span class="punctuation">}</span> | 49 | <span class="brace">}</span> |
50 | 50 | ||
51 | <span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">ops</span> <span class="punctuation">{</span> | 51 | <span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">ops</span> <span class="brace">{</span> |
52 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn_once"</span><span class="attribute attribute">]</span> | 52 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn_once"</span><span class="attribute attribute">]</span> |
53 | <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">FnOnce</span><span class="punctuation"><</span><span class="type_param declaration">Args</span><span class="punctuation">></span> <span class="punctuation">{</span><span class="punctuation">}</span> | 53 | <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">FnOnce</span><span class="angle"><</span><span class="type_param declaration">Args</span><span class="angle">></span> <span class="brace">{</span><span class="brace">}</span> |
54 | 54 | ||
55 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn_mut"</span><span class="attribute attribute">]</span> | 55 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn_mut"</span><span class="attribute attribute">]</span> |
56 | <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">FnMut</span><span class="punctuation"><</span><span class="type_param declaration">Args</span><span class="punctuation">></span><span class="punctuation">:</span> <span class="trait">FnOnce</span><span class="punctuation"><</span><span class="type_param">Args</span><span class="punctuation">></span> <span class="punctuation">{</span><span class="punctuation">}</span> | 56 | <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">FnMut</span><span class="angle"><</span><span class="type_param declaration">Args</span><span class="angle">></span><span class="colon">:</span> <span class="trait">FnOnce</span><span class="angle"><</span><span class="type_param">Args</span><span class="angle">></span> <span class="brace">{</span><span class="brace">}</span> |
57 | 57 | ||
58 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn"</span><span class="attribute attribute">]</span> | 58 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn"</span><span class="attribute attribute">]</span> |
59 | <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Fn</span><span class="punctuation"><</span><span class="type_param declaration">Args</span><span class="punctuation">></span><span class="punctuation">:</span> <span class="trait">FnMut</span><span class="punctuation"><</span><span class="type_param">Args</span><span class="punctuation">></span> <span class="punctuation">{</span><span class="punctuation">}</span> | 59 | <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Fn</span><span class="angle"><</span><span class="type_param declaration">Args</span><span class="angle">></span><span class="colon">:</span> <span class="trait">FnMut</span><span class="angle"><</span><span class="type_param">Args</span><span class="angle">></span> <span class="brace">{</span><span class="brace">}</span> |
60 | <span class="punctuation">}</span> | 60 | <span class="brace">}</span> |
61 | 61 | ||
62 | 62 | ||
63 | <span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="punctuation">{</span> | 63 | <span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="brace">{</span> |
64 | <span class="keyword">pub</span> <span class="field declaration">x</span><span class="punctuation">:</span> <span class="builtin_type">i32</span><span class="punctuation">,</span> | 64 | <span class="keyword">pub</span> <span class="field declaration">x</span><span class="colon">:</span> <span class="builtin_type">i32</span><span class="comma">,</span> |
65 | <span class="keyword">pub</span> <span class="field declaration">y</span><span class="punctuation">:</span> <span class="builtin_type">i32</span><span class="punctuation">,</span> | 65 | <span class="keyword">pub</span> <span class="field declaration">y</span><span class="colon">:</span> <span class="builtin_type">i32</span><span class="comma">,</span> |
66 | <span class="punctuation">}</span> | 66 | <span class="brace">}</span> |
67 | 67 | ||
68 | <span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="punctuation">{</span> | 68 | <span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="brace">{</span> |
69 | <span class="keyword">fn</span> <span class="function declaration associated">bar</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="builtin_type">i32</span><span class="punctuation">;</span> | 69 | <span class="keyword">fn</span> <span class="function declaration associated">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="builtin_type">i32</span><span class="semicolon">;</span> |
70 | <span class="punctuation">}</span> | 70 | <span class="brace">}</span> |
71 | 71 | ||
72 | <span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="punctuation">{</span> | 72 | <span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="brace">{</span> |
73 | <span class="keyword">fn</span> <span class="function declaration associated">bar</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="builtin_type">i32</span> <span class="punctuation">{</span> | 73 | <span class="keyword">fn</span> <span class="function declaration associated">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="builtin_type">i32</span> <span class="brace">{</span> |
74 | <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> | 74 | <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> |
75 | <span class="punctuation">}</span> | 75 | <span class="brace">}</span> |
76 | <span class="punctuation">}</span> | 76 | <span class="brace">}</span> |
77 | 77 | ||
78 | <span class="keyword">impl</span> <span class="struct">Foo</span> <span class="punctuation">{</span> | 78 | <span class="keyword">impl</span> <span class="struct">Foo</span> <span class="brace">{</span> |
79 | <span class="keyword">fn</span> <span class="function declaration associated">baz</span><span class="punctuation">(</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">,</span> <span class="value_param declaration">f</span><span class="punctuation">:</span> <span class="struct">Foo</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="builtin_type">i32</span> <span class="punctuation">{</span> | 79 | <span class="keyword">fn</span> <span class="function declaration associated">baz</span><span class="parenthesis">(</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="comma">,</span> <span class="value_param declaration">f</span><span class="colon">:</span> <span class="struct">Foo</span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="builtin_type">i32</span> <span class="brace">{</span> |
80 | <span class="value_param">f</span><span class="operator">.</span><span class="function consuming associated">baz</span><span class="punctuation">(</span><span class="self_keyword mutable consuming">self</span><span class="punctuation">)</span> | 80 | <span class="value_param">f</span><span class="operator">.</span><span class="function consuming associated">baz</span><span class="parenthesis">(</span><span class="self_keyword mutable consuming">self</span><span class="parenthesis">)</span> |
81 | <span class="punctuation">}</span> | 81 | <span class="brace">}</span> |
82 | 82 | ||
83 | <span class="keyword">fn</span> <span class="function declaration associated">qux</span><span class="punctuation">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span> | 83 | <span class="keyword">fn</span> <span class="function declaration associated">qux</span><span class="parenthesis">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="parenthesis">)</span> <span class="brace">{</span> |
84 | <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span> | 84 | <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span> |
85 | <span class="punctuation">}</span> | 85 | <span class="brace">}</span> |
86 | 86 | ||
87 | <span class="keyword">fn</span> <span class="function declaration associated">quop</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="builtin_type">i32</span> <span class="punctuation">{</span> | 87 | <span class="keyword">fn</span> <span class="function declaration associated">quop</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="builtin_type">i32</span> <span class="brace">{</span> |
88 | <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> | 88 | <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> |
89 | <span class="punctuation">}</span> | 89 | <span class="brace">}</span> |
90 | <span class="punctuation">}</span> | 90 | <span class="brace">}</span> |
91 | 91 | ||
92 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">derive</span><span class="punctuation attribute">(</span><span class="attribute attribute">Copy</span><span class="punctuation attribute">)</span><span class="attribute attribute">]</span> | 92 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">derive</span><span class="parenthesis attribute">(</span><span class="attribute attribute">Copy</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span> |
93 | <span class="keyword">struct</span> <span class="struct declaration">FooCopy</span> <span class="punctuation">{</span> | 93 | <span class="keyword">struct</span> <span class="struct declaration">FooCopy</span> <span class="brace">{</span> |
94 | <span class="field declaration">x</span><span class="punctuation">:</span> <span class="builtin_type">u32</span><span class="punctuation">,</span> | 94 | <span class="field declaration">x</span><span class="colon">:</span> <span class="builtin_type">u32</span><span class="comma">,</span> |
95 | <span class="punctuation">}</span> | 95 | <span class="brace">}</span> |
96 | 96 | ||
97 | <span class="keyword">impl</span> <span class="struct">FooCopy</span> <span class="punctuation">{</span> | 97 | <span class="keyword">impl</span> <span class="struct">FooCopy</span> <span class="brace">{</span> |
98 | <span class="keyword">fn</span> <span class="function declaration associated">baz</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">,</span> <span class="value_param declaration">f</span><span class="punctuation">:</span> <span class="struct">FooCopy</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="builtin_type">u32</span> <span class="punctuation">{</span> | 98 | <span class="keyword">fn</span> <span class="function declaration associated">baz</span><span class="parenthesis">(</span><span class="self_keyword">self</span><span class="comma">,</span> <span class="value_param declaration">f</span><span class="colon">:</span> <span class="struct">FooCopy</span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="builtin_type">u32</span> <span class="brace">{</span> |
99 | <span class="value_param">f</span><span class="operator">.</span><span class="function associated">baz</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">)</span> | 99 | <span class="value_param">f</span><span class="operator">.</span><span class="function associated">baz</span><span class="parenthesis">(</span><span class="self_keyword">self</span><span class="parenthesis">)</span> |
100 | <span class="punctuation">}</span> | 100 | <span class="brace">}</span> |
101 | 101 | ||
102 | <span class="keyword">fn</span> <span class="function declaration associated">qux</span><span class="punctuation">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span> | 102 | <span class="keyword">fn</span> <span class="function declaration associated">qux</span><span class="parenthesis">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="parenthesis">)</span> <span class="brace">{</span> |
103 | <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span> | 103 | <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span> |
104 | <span class="punctuation">}</span> | 104 | <span class="brace">}</span> |
105 | 105 | ||
106 | <span class="keyword">fn</span> <span class="function declaration associated">quop</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="builtin_type">u32</span> <span class="punctuation">{</span> | 106 | <span class="keyword">fn</span> <span class="function declaration associated">quop</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="builtin_type">u32</span> <span class="brace">{</span> |
107 | <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> | 107 | <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> |
108 | <span class="punctuation">}</span> | 108 | <span class="brace">}</span> |
109 | <span class="punctuation">}</span> | 109 | <span class="brace">}</span> |
110 | 110 | ||
111 | <span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">STATIC_MUT</span><span class="punctuation">:</span> <span class="builtin_type">i32</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span> | 111 | <span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">STATIC_MUT</span><span class="colon">:</span> <span class="builtin_type">i32</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span> |
112 | 112 | ||
113 | <span class="keyword">fn</span> <span class="function declaration">foo</span><span class="punctuation"><</span><span class="lifetime declaration">'a</span><span class="punctuation">,</span> <span class="type_param declaration">T</span><span class="punctuation">></span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="type_param">T</span> <span class="punctuation">{</span> | 113 | <span class="keyword">fn</span> <span class="function declaration">foo</span><span class="angle"><</span><span class="lifetime declaration">'a</span><span class="comma">,</span> <span class="type_param declaration">T</span><span class="angle">></span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="type_param">T</span> <span class="brace">{</span> |
114 | <span class="function">foo</span><span class="operator">::</span><span class="punctuation"><</span><span class="lifetime">'a</span><span class="punctuation">,</span> <span class="builtin_type">i32</span><span class="punctuation">></span><span class="punctuation">(</span><span class="punctuation">)</span> | 114 | <span class="function">foo</span><span class="operator">::</span><span class="angle"><</span><span class="lifetime">'a</span><span class="comma">,</span> <span class="builtin_type">i32</span><span class="angle">></span><span class="parenthesis">(</span><span class="parenthesis">)</span> |
115 | <span class="punctuation">}</span> | 115 | <span class="brace">}</span> |
116 | 116 | ||
117 | <span class="keyword">fn</span> <span class="function declaration">never</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="builtin_type">!</span> <span class="punctuation">{</span> | 117 | <span class="keyword">fn</span> <span class="function declaration">never</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="builtin_type">!</span> <span class="brace">{</span> |
118 | <span class="keyword control">loop</span> <span class="punctuation">{</span><span class="punctuation">}</span> | 118 | <span class="keyword control">loop</span> <span class="brace">{</span><span class="brace">}</span> |
119 | <span class="punctuation">}</span> | 119 | <span class="brace">}</span> |
120 | 120 | ||
121 | <span class="keyword">fn</span> <span class="function declaration">const_param</span><span class="punctuation"><</span><span class="keyword">const</span> <span class="const_param declaration">FOO</span><span class="punctuation">:</span> <span class="builtin_type">usize</span><span class="punctuation">></span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="builtin_type">usize</span> <span class="punctuation">{</span> | 121 | <span class="keyword">fn</span> <span class="function declaration">const_param</span><span class="angle"><</span><span class="keyword">const</span> <span class="const_param declaration">FOO</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="angle">></span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="builtin_type">usize</span> <span class="brace">{</span> |
122 | <span class="const_param">FOO</span> | 122 | <span class="const_param">FOO</span> |
123 | <span class="punctuation">}</span> | 123 | <span class="brace">}</span> |
124 | 124 | ||
125 | <span class="keyword">use</span> <span class="module">ops</span><span class="operator">::</span><span class="trait">Fn</span><span class="punctuation">;</span> | 125 | <span class="keyword">use</span> <span class="module">ops</span><span class="operator">::</span><span class="trait">Fn</span><span class="semicolon">;</span> |
126 | <span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation"><</span><span class="type_param declaration">F</span><span class="punctuation">:</span> <span class="trait">Fn</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">></span><span class="punctuation">(</span><span class="value_param declaration callable">f</span><span class="punctuation">:</span> <span class="type_param">F</span><span class="punctuation">)</span> <span class="punctuation">{</span> | 126 | <span class="keyword">fn</span> <span class="function declaration">baz</span><span class="angle"><</span><span class="type_param declaration">F</span><span class="colon">:</span> <span class="trait">Fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="angle">></span><span class="parenthesis">(</span><span class="value_param declaration callable">f</span><span class="colon">:</span> <span class="type_param">F</span><span class="parenthesis">)</span> <span class="brace">{</span> |
127 | <span class="value_param callable">f</span><span class="punctuation">(</span><span class="punctuation">)</span> | 127 | <span class="value_param callable">f</span><span class="parenthesis">(</span><span class="parenthesis">)</span> |
128 | <span class="punctuation">}</span> | 128 | <span class="brace">}</span> |
129 | 129 | ||
130 | <span class="keyword">fn</span> <span class="function declaration">foobar</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="keyword">impl</span> <span class="macro">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span> | 130 | <span class="keyword">fn</span> <span class="function declaration">foobar</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="keyword">impl</span> <span class="macro">Copy</span> <span class="brace">{</span><span class="brace">}</span> |
131 | 131 | ||
132 | <span class="keyword">fn</span> <span class="function declaration">foo</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> | 132 | <span class="keyword">fn</span> <span class="function declaration">foo</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> |
133 | <span class="keyword">let</span> <span class="variable declaration">bar</span> <span class="operator">=</span> <span class="function">foobar</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 133 | <span class="keyword">let</span> <span class="variable declaration">bar</span> <span class="operator">=</span> <span class="function">foobar</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
134 | <span class="punctuation">}</span> | 134 | <span class="brace">}</span> |
135 | 135 | ||
136 | <span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">def_fn</span> <span class="punctuation">{</span> | 136 | <span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">def_fn</span> <span class="brace">{</span> |
137 | <span class="punctuation">(</span><span class="punctuation">$</span><span class="punctuation">(</span><span class="punctuation">$</span>tt<span class="punctuation">:</span>tt<span class="punctuation">)</span><span class="punctuation">*</span><span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">></span> <span class="punctuation">{</span><span class="punctuation">$</span><span class="punctuation">(</span><span class="punctuation">$</span>tt<span class="punctuation">)</span><span class="punctuation">*</span><span class="punctuation">}</span> | 137 | <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="brace">{</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="brace">}</span> |
138 | <span class="punctuation">}</span> | 138 | <span class="brace">}</span> |
139 | 139 | ||
140 | <span class="macro">def_fn!</span> <span class="punctuation">{</span> | 140 | <span class="macro">def_fn!</span> <span class="brace">{</span> |
141 | <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-</span><span class="operator">></span> <span class="builtin_type">u32</span> <span class="punctuation">{</span> | 141 | <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-</span><span class="operator">></span> <span class="builtin_type">u32</span> <span class="brace">{</span> |
142 | <span class="numeric_literal">100</span> | 142 | <span class="numeric_literal">100</span> |
143 | <span class="punctuation">}</span> | 143 | <span class="brace">}</span> |
144 | <span class="punctuation">}</span> | 144 | <span class="brace">}</span> |
145 | 145 | ||
146 | <span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">noop</span> <span class="punctuation">{</span> | 146 | <span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span> |
147 | <span class="punctuation">(</span><span class="punctuation">$</span>expr<span class="punctuation">:</span>expr<span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">></span> <span class="punctuation">{</span> | 147 | <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="brace">{</span> |
148 | <span class="punctuation">$</span>expr | 148 | <span class="punctuation">$</span>expr |
149 | <span class="punctuation">}</span> | 149 | <span class="brace">}</span> |
150 | <span class="punctuation">}</span> | 150 | <span class="brace">}</span> |
151 | 151 | ||
152 | <span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">keyword_frag</span> <span class="punctuation">{</span> | 152 | <span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">keyword_frag</span> <span class="brace">{</span> |
153 | <span class="punctuation">(</span><span class="punctuation">$</span>type<span class="punctuation">:</span>ty<span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">></span> <span class="punctuation">(</span><span class="punctuation">$</span>type<span class="punctuation">)</span> | 153 | <span class="parenthesis">(</span><span class="punctuation">$</span>type<span class="colon">:</span>ty<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="parenthesis">(</span><span class="punctuation">$</span>type<span class="parenthesis">)</span> |
154 | <span class="punctuation">}</span> | 154 | <span class="brace">}</span> |
155 | 155 | ||
156 | <span class="comment">// comment</span> | 156 | <span class="comment">// comment</span> |
157 | <span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> | 157 | <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> |
158 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello, {}!"</span><span class="punctuation">,</span> <span class="numeric_literal">92</span><span class="punctuation">)</span><span class="punctuation">;</span> | 158 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello, {}!"</span><span class="comma">,</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
159 | 159 | ||
160 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> <span class="operator">=</span> <span class="unresolved_reference">Vec</span><span class="operator">::</span><span class="unresolved_reference">new</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 160 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> <span class="operator">=</span> <span class="unresolved_reference">Vec</span><span class="operator">::</span><span class="unresolved_reference">new</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
161 | <span class="keyword control">if</span> <span class="bool_literal">true</span> <span class="punctuation">{</span> | 161 | <span class="keyword control">if</span> <span class="bool_literal">true</span> <span class="brace">{</span> |
162 | <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="numeric_literal">92</span><span class="punctuation">;</span> | 162 | <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="numeric_literal">92</span><span class="semicolon">;</span> |
163 | <span class="variable mutable">vec</span><span class="operator">.</span><span class="unresolved_reference">push</span><span class="punctuation">(</span><span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="numeric_literal">1</span> <span class="punctuation">}</span><span class="punctuation">)</span><span class="punctuation">;</span> | 163 | <span class="variable mutable">vec</span><span class="operator">.</span><span class="unresolved_reference">push</span><span class="parenthesis">(</span><span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="comma">,</span> <span class="field">y</span><span class="colon">:</span> <span class="numeric_literal">1</span> <span class="brace">}</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
164 | <span class="punctuation">}</span> | 164 | <span class="brace">}</span> |
165 | <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span> | 165 | <span class="keyword unsafe">unsafe</span> <span class="brace">{</span> |
166 | <span class="variable mutable">vec</span><span class="operator">.</span><span class="unresolved_reference">set_len</span><span class="punctuation">(</span><span class="numeric_literal">0</span><span class="punctuation">)</span><span class="punctuation">;</span> | 166 | <span class="variable mutable">vec</span><span class="operator">.</span><span class="unresolved_reference">set_len</span><span class="parenthesis">(</span><span class="numeric_literal">0</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
167 | <span class="static mutable unsafe">STATIC_MUT</span> <span class="operator">=</span> <span class="numeric_literal">1</span><span class="punctuation">;</span> | 167 | <span class="static mutable unsafe">STATIC_MUT</span> <span class="operator">=</span> <span class="numeric_literal">1</span><span class="semicolon">;</span> |
168 | <span class="punctuation">}</span> | 168 | <span class="brace">}</span> |
169 | 169 | ||
170 | <span class="keyword control">for</span> <span class="variable declaration">e</span> <span class="keyword control">in</span> <span class="variable mutable">vec</span> <span class="punctuation">{</span> | 170 | <span class="keyword control">for</span> <span class="variable declaration">e</span> <span class="keyword control">in</span> <span class="variable mutable">vec</span> <span class="brace">{</span> |
171 | <span class="comment">// Do nothing</span> | 171 | <span class="comment">// Do nothing</span> |
172 | <span class="punctuation">}</span> | 172 | <span class="brace">}</span> |
173 | 173 | ||
174 | <span class="macro">noop!</span><span class="punctuation">(</span><span class="macro">noop</span><span class="macro">!</span><span class="punctuation">(</span><span class="numeric_literal">1</span><span class="punctuation">)</span><span class="punctuation">)</span><span class="punctuation">;</span> | 174 | <span class="macro">noop!</span><span class="parenthesis">(</span><span class="macro">noop</span><span class="macro">!</span><span class="parenthesis">(</span><span class="numeric_literal">1</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
175 | 175 | ||
176 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">x</span> <span class="operator">=</span> <span class="numeric_literal">42</span><span class="punctuation">;</span> | 176 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">x</span> <span class="operator">=</span> <span class="numeric_literal">42</span><span class="semicolon">;</span> |
177 | <span class="keyword">let</span> <span class="variable declaration mutable">y</span> <span class="operator">=</span> <span class="operator">&</span><span class="keyword">mut</span> <span class="variable mutable">x</span><span class="punctuation">;</span> | 177 | <span class="keyword">let</span> <span class="variable declaration mutable">y</span> <span class="operator">=</span> <span class="operator">&</span><span class="keyword">mut</span> <span class="variable mutable">x</span><span class="semicolon">;</span> |
178 | <span class="keyword">let</span> <span class="variable declaration">z</span> <span class="operator">=</span> <span class="operator">&</span><span class="variable mutable">y</span><span class="punctuation">;</span> | 178 | <span class="keyword">let</span> <span class="variable declaration">z</span> <span class="operator">=</span> <span class="operator">&</span><span class="variable mutable">y</span><span class="semicolon">;</span> |
179 | 179 | ||
180 | <span class="keyword">let</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">:</span> <span class="variable declaration">z</span><span class="punctuation">,</span> <span class="field">y</span> <span class="punctuation">}</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">:</span> <span class="variable">z</span><span class="punctuation">,</span> <span class="field">y</span> <span class="punctuation">}</span><span class="punctuation">;</span> | 180 | <span class="keyword">let</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="colon">:</span> <span class="variable declaration">z</span><span class="comma">,</span> <span class="field">y</span> <span class="brace">}</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="colon">:</span> <span class="variable">z</span><span class="comma">,</span> <span class="field">y</span> <span class="brace">}</span><span class="semicolon">;</span> |
181 | 181 | ||
182 | <span class="variable">y</span><span class="punctuation">;</span> | 182 | <span class="variable">y</span><span class="semicolon">;</span> |
183 | 183 | ||
184 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="variable mutable">x</span> <span class="punctuation">}</span><span class="punctuation">;</span> | 184 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="comma">,</span> <span class="field">y</span><span class="colon">:</span> <span class="variable mutable">x</span> <span class="brace">}</span><span class="semicolon">;</span> |
185 | <span class="keyword">let</span> <span class="variable declaration">foo2</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="variable mutable">x</span> <span class="punctuation">}</span><span class="punctuation">;</span> | 185 | <span class="keyword">let</span> <span class="variable declaration">foo2</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="comma">,</span> <span class="field">y</span><span class="colon">:</span> <span class="variable mutable">x</span> <span class="brace">}</span><span class="semicolon">;</span> |
186 | <span class="variable mutable">foo</span><span class="operator">.</span><span class="function associated">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 186 | <span class="variable mutable">foo</span><span class="operator">.</span><span class="function associated">quop</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
187 | <span class="variable mutable">foo</span><span class="operator">.</span><span class="function mutable associated">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 187 | <span class="variable mutable">foo</span><span class="operator">.</span><span class="function mutable associated">qux</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
188 | <span class="variable mutable">foo</span><span class="operator">.</span><span class="function consuming associated">baz</span><span class="punctuation">(</span><span class="variable consuming">foo2</span><span class="punctuation">)</span><span class="punctuation">;</span> | 188 | <span class="variable mutable">foo</span><span class="operator">.</span><span class="function consuming associated">baz</span><span class="parenthesis">(</span><span class="variable consuming">foo2</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
189 | 189 | ||
190 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">copy</span> <span class="operator">=</span> <span class="struct">FooCopy</span> <span class="punctuation">{</span> <span class="field">x</span> <span class="punctuation">}</span><span class="punctuation">;</span> | 190 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">copy</span> <span class="operator">=</span> <span class="struct">FooCopy</span> <span class="brace">{</span> <span class="field">x</span> <span class="brace">}</span><span class="semicolon">;</span> |
191 | <span class="variable mutable">copy</span><span class="operator">.</span><span class="function associated">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 191 | <span class="variable mutable">copy</span><span class="operator">.</span><span class="function associated">quop</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
192 | <span class="variable mutable">copy</span><span class="operator">.</span><span class="function mutable associated">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 192 | <span class="variable mutable">copy</span><span class="operator">.</span><span class="function mutable associated">qux</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
193 | <span class="variable mutable">copy</span><span class="operator">.</span><span class="function associated">baz</span><span class="punctuation">(</span><span class="variable mutable">copy</span><span class="punctuation">)</span><span class="punctuation">;</span> | 193 | <span class="variable mutable">copy</span><span class="operator">.</span><span class="function associated">baz</span><span class="parenthesis">(</span><span class="variable mutable">copy</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
194 | 194 | ||
195 | <span class="keyword">let</span> <span class="variable declaration callable">a</span> <span class="operator">=</span> <span class="punctuation">|</span><span class="value_param declaration">x</span><span class="punctuation">|</span> <span class="value_param">x</span><span class="punctuation">;</span> | 195 | <span class="keyword">let</span> <span class="variable declaration callable">a</span> <span class="operator">=</span> <span class="punctuation">|</span><span class="value_param declaration">x</span><span class="punctuation">|</span> <span class="value_param">x</span><span class="semicolon">;</span> |
196 | <span class="keyword">let</span> <span class="variable declaration callable">bar</span> <span class="operator">=</span> <span class="struct">Foo</span><span class="operator">::</span><span class="function associated">baz</span><span class="punctuation">;</span> | 196 | <span class="keyword">let</span> <span class="variable declaration callable">bar</span> <span class="operator">=</span> <span class="struct">Foo</span><span class="operator">::</span><span class="function associated">baz</span><span class="semicolon">;</span> |
197 | 197 | ||
198 | <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="numeric_literal">-</span><span class="numeric_literal">42</span><span class="punctuation">;</span> | 198 | <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="numeric_literal">-</span><span class="numeric_literal">42</span><span class="semicolon">;</span> |
199 | <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="punctuation">;</span> | 199 | <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="semicolon">;</span> |
200 | 200 | ||
201 | <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="operator">!</span><span class="bool_literal">true</span><span class="punctuation">;</span> | 201 | <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="operator">!</span><span class="bool_literal">true</span><span class="semicolon">;</span> |
202 | 202 | ||
203 | <span class="label declaration">'foo</span><span class="punctuation">:</span> <span class="keyword control">loop</span> <span class="punctuation">{</span> | 203 | <span class="label declaration">'foo</span><span class="colon">:</span> <span class="keyword control">loop</span> <span class="brace">{</span> |
204 | <span class="keyword control">break</span> <span class="label">'foo</span><span class="punctuation">;</span> | 204 | <span class="keyword control">break</span> <span class="label">'foo</span><span class="semicolon">;</span> |
205 | <span class="keyword control">continue</span> <span class="label">'foo</span><span class="punctuation">;</span> | 205 | <span class="keyword control">continue</span> <span class="label">'foo</span><span class="semicolon">;</span> |
206 | <span class="punctuation">}</span> | 206 | <span class="brace">}</span> |
207 | <span class="punctuation">}</span> | 207 | <span class="brace">}</span> |
208 | 208 | ||
209 | <span class="keyword">enum</span> <span class="enum declaration">Option</span><span class="punctuation"><</span><span class="type_param declaration">T</span><span class="punctuation">></span> <span class="punctuation">{</span> | 209 | <span class="keyword">enum</span> <span class="enum declaration">Option</span><span class="angle"><</span><span class="type_param declaration">T</span><span class="angle">></span> <span class="brace">{</span> |
210 | <span class="enum_variant declaration">Some</span><span class="punctuation">(</span><span class="type_param">T</span><span class="punctuation">)</span><span class="punctuation">,</span> | 210 | <span class="enum_variant declaration">Some</span><span class="parenthesis">(</span><span class="type_param">T</span><span class="parenthesis">)</span><span class="comma">,</span> |
211 | <span class="enum_variant declaration">None</span><span class="punctuation">,</span> | 211 | <span class="enum_variant declaration">None</span><span class="comma">,</span> |
212 | <span class="punctuation">}</span> | 212 | <span class="brace">}</span> |
213 | <span class="keyword">use</span> <span class="enum">Option</span><span class="operator">::</span><span class="punctuation">*</span><span class="punctuation">;</span> | 213 | <span class="keyword">use</span> <span class="enum">Option</span><span class="operator">::</span><span class="punctuation">*</span><span class="semicolon">;</span> |
214 | 214 | ||
215 | <span class="keyword">impl</span><span class="punctuation"><</span><span class="type_param declaration">T</span><span class="punctuation">></span> <span class="enum">Option</span><span class="punctuation"><</span><span class="type_param">T</span><span class="punctuation">></span> <span class="punctuation">{</span> | 215 | <span class="keyword">impl</span><span class="angle"><</span><span class="type_param declaration">T</span><span class="angle">></span> <span class="enum">Option</span><span class="angle"><</span><span class="type_param">T</span><span class="angle">></span> <span class="brace">{</span> |
216 | <span class="keyword">fn</span> <span class="function declaration associated">and</span><span class="punctuation"><</span><span class="type_param declaration">U</span><span class="punctuation">></span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">,</span> <span class="value_param declaration">other</span><span class="punctuation">:</span> <span class="enum">Option</span><span class="punctuation"><</span><span class="type_param">U</span><span class="punctuation">></span><span class="punctuation">)</span> <span class="operator">-></span> <span class="enum">Option</span><span class="punctuation"><</span><span class="punctuation">(</span><span class="type_param">T</span><span class="punctuation">,</span> <span class="type_param">U</span><span class="punctuation">)</span><span class="punctuation">></span> <span class="punctuation">{</span> | 216 | <span class="keyword">fn</span> <span class="function declaration associated">and</span><span class="angle"><</span><span class="type_param declaration">U</span><span class="angle">></span><span class="parenthesis">(</span><span class="self_keyword">self</span><span class="comma">,</span> <span class="value_param declaration">other</span><span class="colon">:</span> <span class="enum">Option</span><span class="angle"><</span><span class="type_param">U</span><span class="angle">></span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="enum">Option</span><span class="angle"><</span><span class="parenthesis">(</span><span class="type_param">T</span><span class="comma">,</span> <span class="type_param">U</span><span class="parenthesis">)</span><span class="angle">></span> <span class="brace">{</span> |
217 | <span class="keyword control">match</span> <span class="value_param">other</span> <span class="punctuation">{</span> | 217 | <span class="keyword control">match</span> <span class="value_param">other</span> <span class="brace">{</span> |
218 | <span class="enum_variant">None</span> <span class="operator">=></span> <span class="macro">unimplemented!</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span> | 218 | <span class="enum_variant">None</span> <span class="operator">=></span> <span class="macro">unimplemented!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span> |
219 | <span class="variable declaration">Nope</span> <span class="operator">=></span> <span class="variable">Nope</span><span class="punctuation">,</span> | 219 | <span class="variable declaration">Nope</span> <span class="operator">=></span> <span class="variable">Nope</span><span class="comma">,</span> |
220 | <span class="punctuation">}</span> | 220 | <span class="brace">}</span> |
221 | <span class="punctuation">}</span> | 221 | <span class="brace">}</span> |
222 | <span class="punctuation">}</span></code></pre> \ No newline at end of file | 222 | <span class="brace">}</span></code></pre> \ No newline at end of file |
diff --git a/crates/ide/src/syntax_highlighting/test_data/injection.html b/crates/ide/src/syntax_highlighting/test_data/injection.html index a54d303b4..78dfec951 100644 --- a/crates/ide/src/syntax_highlighting/test_data/injection.html +++ b/crates/ide/src/syntax_highlighting/test_data/injection.html | |||
@@ -36,13 +36,13 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
36 | 36 | ||
37 | .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } | 37 | .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } |
38 | </style> | 38 | </style> |
39 | <pre><code><span class="keyword">fn</span> <span class="function declaration">f</span><span class="punctuation">(</span><span class="value_param declaration">ra_fixture</span><span class="punctuation">:</span> <span class="operator">&</span><span class="builtin_type">str</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> | 39 | <pre><code><span class="keyword">fn</span> <span class="function declaration">f</span><span class="parenthesis">(</span><span class="value_param declaration">ra_fixture</span><span class="colon">:</span> <span class="operator">&</span><span class="builtin_type">str</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> |
40 | <span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> | 40 | <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> |
41 | <span class="function">f</span><span class="punctuation">(</span><span class="string_literal">r"</span> | 41 | <span class="function">f</span><span class="parenthesis">(</span><span class="string_literal">r"</span> |
42 | <span class="keyword">fn</span> <span class="function declaration">foo</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> | 42 | <span class="keyword">fn</span> <span class="function declaration">foo</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> |
43 | <span class="function">foo</span><span class="punctuation">(</span>$0<span class="punctuation">{</span> | 43 | <span class="function">foo</span><span class="parenthesis">(</span><span class="keyword">$0</span><span class="brace">{</span> |
44 | <span class="numeric_literal">92</span> | 44 | <span class="numeric_literal">92</span> |
45 | <span class="punctuation">}</span>$0<span class="punctuation">)</span> | 45 | <span class="brace">}</span><span class="keyword">$0</span><span class="parenthesis">)</span> |
46 | <span class="punctuation">}</span><span class="string_literal">"</span><span class="punctuation">)</span><span class="punctuation">;</span> | 46 | <span class="brace">}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
47 | <span class="punctuation">}</span> | 47 | <span class="brace">}</span> |
48 | </code></pre> \ No newline at end of file | 48 | </code></pre> \ No newline at end of file |
diff --git a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html index 8b3dfa69f..e64f2e5e9 100644 --- a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html | |||
@@ -36,15 +36,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
36 | 36 | ||
37 | .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } | 37 | .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } |
38 | </style> | 38 | </style> |
39 | <pre><code><span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> | 39 | <pre><code><span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> |
40 | <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="punctuation">;</span> | 40 | <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="semicolon">;</span> |
41 | <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="2705725358298919760" style="color: hsl(76,47%,83%);">x</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 41 | <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="2705725358298919760" style="color: hsl(76,47%,83%);">x</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
42 | <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="3365759661443752373" style="color: hsl(15,86%,51%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 42 | <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="3365759661443752373" style="color: hsl(15,86%,51%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
43 | 43 | ||
44 | <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="794745962933817518" style="color: hsl(127,71%,87%);">x</span> <span class="operator">=</span> <span class="string_literal">"other color please!"</span><span class="punctuation">;</span> | 44 | <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="794745962933817518" style="color: hsl(127,71%,87%);">x</span> <span class="operator">=</span> <span class="string_literal">"other color please!"</span><span class="semicolon">;</span> |
45 | <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="6717528807933952652" style="color: hsl(90,74%,79%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(127,71%,87%);">x</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 45 | <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="6717528807933952652" style="color: hsl(90,74%,79%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(127,71%,87%);">x</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
46 | <span class="punctuation">}</span> | 46 | <span class="brace">}</span> |
47 | 47 | ||
48 | <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> | 48 | <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> |
49 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="punctuation">;</span> | 49 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="semicolon">;</span> |
50 | <span class="punctuation">}</span></code></pre> \ No newline at end of file | 50 | <span class="brace">}</span></code></pre> \ No newline at end of file |
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 9e1a3974c..a62704c39 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs | |||
@@ -446,6 +446,11 @@ struct Foo { | |||
446 | } | 446 | } |
447 | 447 | ||
448 | impl Foo { | 448 | impl Foo { |
449 | /// ``` | ||
450 | /// let _ = "Call me | ||
451 | // KILLER WHALE | ||
452 | /// Ishmael."; | ||
453 | /// ``` | ||
449 | pub const bar: bool = true; | 454 | pub const bar: bool = true; |
450 | 455 | ||
451 | /// Constructs a new `Foo`. | 456 | /// Constructs a new `Foo`. |
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs index be1c64b03..d68fe42b0 100644 --- a/crates/ide_db/src/defs.rs +++ b/crates/ide_db/src/defs.rs | |||
@@ -6,8 +6,8 @@ | |||
6 | // FIXME: this badly needs rename/rewrite (matklad, 2020-02-06). | 6 | // FIXME: this badly needs rename/rewrite (matklad, 2020-02-06). |
7 | 7 | ||
8 | use hir::{ | 8 | use hir::{ |
9 | db::HirDatabase, ConstParam, Crate, Field, HasVisibility, Impl, Label, LifetimeParam, Local, | 9 | db::HirDatabase, Crate, Field, GenericParam, HasVisibility, Impl, Label, Local, MacroDef, |
10 | MacroDef, Module, ModuleDef, Name, PathResolution, Semantics, TypeParam, Visibility, | 10 | Module, ModuleDef, Name, PathResolution, Semantics, Visibility, |
11 | }; | 11 | }; |
12 | use syntax::{ | 12 | use syntax::{ |
13 | ast::{self, AstNode}, | 13 | ast::{self, AstNode}, |
@@ -24,9 +24,7 @@ pub enum Definition { | |||
24 | ModuleDef(ModuleDef), | 24 | ModuleDef(ModuleDef), |
25 | SelfType(Impl), | 25 | SelfType(Impl), |
26 | Local(Local), | 26 | Local(Local), |
27 | TypeParam(TypeParam), | 27 | GenericParam(GenericParam), |
28 | LifetimeParam(LifetimeParam), | ||
29 | ConstParam(ConstParam), | ||
30 | Label(Label), | 28 | Label(Label), |
31 | } | 29 | } |
32 | 30 | ||
@@ -38,9 +36,7 @@ impl Definition { | |||
38 | Definition::ModuleDef(it) => it.module(db), | 36 | Definition::ModuleDef(it) => it.module(db), |
39 | Definition::SelfType(it) => Some(it.module(db)), | 37 | Definition::SelfType(it) => Some(it.module(db)), |
40 | Definition::Local(it) => Some(it.module(db)), | 38 | Definition::Local(it) => Some(it.module(db)), |
41 | Definition::TypeParam(it) => Some(it.module(db)), | 39 | Definition::GenericParam(it) => Some(it.module(db)), |
42 | Definition::LifetimeParam(it) => Some(it.module(db)), | ||
43 | Definition::ConstParam(it) => Some(it.module(db)), | ||
44 | Definition::Label(it) => Some(it.module(db)), | 40 | Definition::Label(it) => Some(it.module(db)), |
45 | } | 41 | } |
46 | } | 42 | } |
@@ -52,9 +48,7 @@ impl Definition { | |||
52 | Definition::ModuleDef(def) => def.definition_visibility(db), | 48 | Definition::ModuleDef(def) => def.definition_visibility(db), |
53 | Definition::SelfType(_) => None, | 49 | Definition::SelfType(_) => None, |
54 | Definition::Local(_) => None, | 50 | Definition::Local(_) => None, |
55 | Definition::TypeParam(_) => None, | 51 | Definition::GenericParam(_) => None, |
56 | Definition::LifetimeParam(_) => None, | ||
57 | Definition::ConstParam(_) => None, | ||
58 | Definition::Label(_) => None, | 52 | Definition::Label(_) => None, |
59 | } | 53 | } |
60 | } | 54 | } |
@@ -80,9 +74,7 @@ impl Definition { | |||
80 | }, | 74 | }, |
81 | Definition::SelfType(_) => return None, | 75 | Definition::SelfType(_) => return None, |
82 | Definition::Local(it) => it.name(db)?, | 76 | Definition::Local(it) => it.name(db)?, |
83 | Definition::TypeParam(it) => it.name(db), | 77 | Definition::GenericParam(it) => it.name(db), |
84 | Definition::LifetimeParam(it) => it.name(db), | ||
85 | Definition::ConstParam(it) => it.name(db), | ||
86 | Definition::Label(it) => it.name(db), | 78 | Definition::Label(it) => it.name(db), |
87 | }; | 79 | }; |
88 | Some(name) | 80 | Some(name) |
@@ -235,11 +227,11 @@ impl NameClass { | |||
235 | }, | 227 | }, |
236 | ast::TypeParam(it) => { | 228 | ast::TypeParam(it) => { |
237 | let def = sema.to_def(&it)?; | 229 | let def = sema.to_def(&it)?; |
238 | Some(NameClass::Definition(Definition::TypeParam(def))) | 230 | Some(NameClass::Definition(Definition::GenericParam(def.into()))) |
239 | }, | 231 | }, |
240 | ast::ConstParam(it) => { | 232 | ast::ConstParam(it) => { |
241 | let def = sema.to_def(&it)?; | 233 | let def = sema.to_def(&it)?; |
242 | Some(NameClass::Definition(Definition::ConstParam(def))) | 234 | Some(NameClass::Definition(Definition::GenericParam(def.into()))) |
243 | }, | 235 | }, |
244 | _ => None, | 236 | _ => None, |
245 | } | 237 | } |
@@ -257,7 +249,7 @@ impl NameClass { | |||
257 | match parent { | 249 | match parent { |
258 | ast::LifetimeParam(it) => { | 250 | ast::LifetimeParam(it) => { |
259 | let def = sema.to_def(&it)?; | 251 | let def = sema.to_def(&it)?; |
260 | Some(NameClass::Definition(Definition::LifetimeParam(def))) | 252 | Some(NameClass::Definition(Definition::GenericParam(def.into()))) |
261 | }, | 253 | }, |
262 | ast::Label(it) => { | 254 | ast::Label(it) => { |
263 | let def = sema.to_def(&it)?; | 255 | let def = sema.to_def(&it)?; |
@@ -393,7 +385,8 @@ impl NameRefClass { | |||
393 | | SyntaxKind::WHERE_PRED | 385 | | SyntaxKind::WHERE_PRED |
394 | | SyntaxKind::REF_TYPE => sema | 386 | | SyntaxKind::REF_TYPE => sema |
395 | .resolve_lifetime_param(lifetime) | 387 | .resolve_lifetime_param(lifetime) |
396 | .map(Definition::LifetimeParam) | 388 | .map(GenericParam::LifetimeParam) |
389 | .map(Definition::GenericParam) | ||
397 | .map(NameRefClass::Definition), | 390 | .map(NameRefClass::Definition), |
398 | // lifetime bounds, as in the 'b in 'a: 'b aren't wrapped in TypeBound nodes so we gotta check | 391 | // lifetime bounds, as in the 'b in 'a: 'b aren't wrapped in TypeBound nodes so we gotta check |
399 | // if our lifetime is in a LifetimeParam without being the constrained lifetime | 392 | // if our lifetime is in a LifetimeParam without being the constrained lifetime |
@@ -401,7 +394,8 @@ impl NameRefClass { | |||
401 | != Some(lifetime) => | 394 | != Some(lifetime) => |
402 | { | 395 | { |
403 | sema.resolve_lifetime_param(lifetime) | 396 | sema.resolve_lifetime_param(lifetime) |
404 | .map(Definition::LifetimeParam) | 397 | .map(GenericParam::LifetimeParam) |
398 | .map(Definition::GenericParam) | ||
405 | .map(NameRefClass::Definition) | 399 | .map(NameRefClass::Definition) |
406 | } | 400 | } |
407 | _ => None, | 401 | _ => None, |
@@ -422,10 +416,10 @@ impl From<PathResolution> for Definition { | |||
422 | Definition::ModuleDef(def) | 416 | Definition::ModuleDef(def) |
423 | } | 417 | } |
424 | PathResolution::Local(local) => Definition::Local(local), | 418 | PathResolution::Local(local) => Definition::Local(local), |
425 | PathResolution::TypeParam(par) => Definition::TypeParam(par), | 419 | PathResolution::TypeParam(par) => Definition::GenericParam(par.into()), |
426 | PathResolution::Macro(def) => Definition::Macro(def), | 420 | PathResolution::Macro(def) => Definition::Macro(def), |
427 | PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def), | 421 | PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def), |
428 | PathResolution::ConstParam(par) => Definition::ConstParam(par), | 422 | PathResolution::ConstParam(par) => Definition::GenericParam(par.into()), |
429 | } | 423 | } |
430 | } | 424 | } |
431 | } | 425 | } |
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs index e3e5670f1..c6763ae36 100644 --- a/crates/ide_db/src/helpers.rs +++ b/crates/ide_db/src/helpers.rs | |||
@@ -38,94 +38,7 @@ pub struct FamousDefs<'a, 'b>(pub &'a Semantics<'b, RootDatabase>, pub Option<Cr | |||
38 | 38 | ||
39 | #[allow(non_snake_case)] | 39 | #[allow(non_snake_case)] |
40 | impl FamousDefs<'_, '_> { | 40 | impl FamousDefs<'_, '_> { |
41 | pub const FIXTURE: &'static str = r#"//- /libcore.rs crate:core | 41 | pub const FIXTURE: &'static str = include_str!("helpers/famous_defs_fixture.rs"); |
42 | pub mod convert { | ||
43 | pub trait From<T> { | ||
44 | fn from(t: T) -> Self; | ||
45 | } | ||
46 | } | ||
47 | |||
48 | pub mod default { | ||
49 | pub trait Default { | ||
50 | fn default() -> Self; | ||
51 | } | ||
52 | } | ||
53 | |||
54 | pub mod iter { | ||
55 | pub use self::traits::{collect::IntoIterator, iterator::Iterator}; | ||
56 | mod traits { | ||
57 | pub(crate) mod iterator { | ||
58 | use crate::option::Option; | ||
59 | pub trait Iterator { | ||
60 | type Item; | ||
61 | fn next(&mut self) -> Option<Self::Item>; | ||
62 | fn by_ref(&mut self) -> &mut Self { | ||
63 | self | ||
64 | } | ||
65 | fn take(self, n: usize) -> crate::iter::Take<Self> { | ||
66 | crate::iter::Take { inner: self } | ||
67 | } | ||
68 | } | ||
69 | |||
70 | impl<I: Iterator> Iterator for &mut I { | ||
71 | type Item = I::Item; | ||
72 | fn next(&mut self) -> Option<I::Item> { | ||
73 | (**self).next() | ||
74 | } | ||
75 | } | ||
76 | } | ||
77 | pub(crate) mod collect { | ||
78 | pub trait IntoIterator { | ||
79 | type Item; | ||
80 | } | ||
81 | } | ||
82 | } | ||
83 | |||
84 | pub use self::sources::*; | ||
85 | pub(crate) mod sources { | ||
86 | use super::Iterator; | ||
87 | use crate::option::Option::{self, *}; | ||
88 | pub struct Repeat<A> { | ||
89 | element: A, | ||
90 | } | ||
91 | |||
92 | pub fn repeat<T>(elt: T) -> Repeat<T> { | ||
93 | Repeat { element: elt } | ||
94 | } | ||
95 | |||
96 | impl<A> Iterator for Repeat<A> { | ||
97 | type Item = A; | ||
98 | |||
99 | fn next(&mut self) -> Option<A> { | ||
100 | None | ||
101 | } | ||
102 | } | ||
103 | } | ||
104 | |||
105 | pub use self::adapters::*; | ||
106 | pub(crate) mod adapters { | ||
107 | use super::Iterator; | ||
108 | use crate::option::Option::{self, *}; | ||
109 | pub struct Take<I> { pub(crate) inner: I } | ||
110 | impl<I> Iterator for Take<I> where I: Iterator { | ||
111 | type Item = <I as Iterator>::Item; | ||
112 | fn next(&mut self) -> Option<<I as Iterator>::Item> { | ||
113 | None | ||
114 | } | ||
115 | } | ||
116 | } | ||
117 | } | ||
118 | |||
119 | pub mod option { | ||
120 | pub enum Option<T> { None, Some(T)} | ||
121 | } | ||
122 | |||
123 | pub mod prelude { | ||
124 | pub use crate::{convert::From, iter::{IntoIterator, Iterator}, option::Option::{self, *}, default::Default}; | ||
125 | } | ||
126 | #[prelude_import] | ||
127 | pub use prelude::*; | ||
128 | "#; | ||
129 | 42 | ||
130 | pub fn core(&self) -> Option<Crate> { | 43 | pub fn core(&self) -> Option<Crate> { |
131 | self.find_crate("core") | 44 | self.find_crate("core") |
diff --git a/crates/ide_db/src/helpers/famous_defs_fixture.rs b/crates/ide_db/src/helpers/famous_defs_fixture.rs new file mode 100644 index 000000000..5e88de64d --- /dev/null +++ b/crates/ide_db/src/helpers/famous_defs_fixture.rs | |||
@@ -0,0 +1,120 @@ | |||
1 | //- /libcore.rs crate:core | ||
2 | //! Signatures of traits, types and functions from the core lib for use in tests. | ||
3 | pub mod convert { | ||
4 | pub trait From<T> { | ||
5 | fn from(t: T) -> Self; | ||
6 | } | ||
7 | } | ||
8 | |||
9 | pub mod default { | ||
10 | pub trait Default { | ||
11 | fn default() -> Self; | ||
12 | } | ||
13 | } | ||
14 | |||
15 | pub mod iter { | ||
16 | pub use self::traits::{collect::IntoIterator, iterator::Iterator}; | ||
17 | mod traits { | ||
18 | pub(crate) mod iterator { | ||
19 | use crate::option::Option; | ||
20 | pub trait Iterator { | ||
21 | type Item; | ||
22 | fn next(&mut self) -> Option<Self::Item>; | ||
23 | fn by_ref(&mut self) -> &mut Self { | ||
24 | self | ||
25 | } | ||
26 | fn take(self, n: usize) -> crate::iter::Take<Self> { | ||
27 | crate::iter::Take { inner: self } | ||
28 | } | ||
29 | } | ||
30 | |||
31 | impl<I: Iterator> Iterator for &mut I { | ||
32 | type Item = I::Item; | ||
33 | fn next(&mut self) -> Option<I::Item> { | ||
34 | (**self).next() | ||
35 | } | ||
36 | } | ||
37 | } | ||
38 | pub(crate) mod collect { | ||
39 | pub trait IntoIterator { | ||
40 | type Item; | ||
41 | } | ||
42 | } | ||
43 | } | ||
44 | |||
45 | pub use self::sources::*; | ||
46 | pub(crate) mod sources { | ||
47 | use super::Iterator; | ||
48 | use crate::option::Option::{self, *}; | ||
49 | pub struct Repeat<A> { | ||
50 | element: A, | ||
51 | } | ||
52 | |||
53 | pub fn repeat<T>(elt: T) -> Repeat<T> { | ||
54 | Repeat { element: elt } | ||
55 | } | ||
56 | |||
57 | impl<A> Iterator for Repeat<A> { | ||
58 | type Item = A; | ||
59 | |||
60 | fn next(&mut self) -> Option<A> { | ||
61 | None | ||
62 | } | ||
63 | } | ||
64 | } | ||
65 | |||
66 | pub use self::adapters::*; | ||
67 | pub(crate) mod adapters { | ||
68 | use super::Iterator; | ||
69 | use crate::option::Option::{self, *}; | ||
70 | pub struct Take<I> { | ||
71 | pub(crate) inner: I, | ||
72 | } | ||
73 | impl<I> Iterator for Take<I> | ||
74 | where | ||
75 | I: Iterator, | ||
76 | { | ||
77 | type Item = <I as Iterator>::Item; | ||
78 | fn next(&mut self) -> Option<<I as Iterator>::Item> { | ||
79 | None | ||
80 | } | ||
81 | } | ||
82 | } | ||
83 | } | ||
84 | |||
85 | pub mod ops { | ||
86 | #[lang = "fn"] | ||
87 | pub trait Fn<Args>: FnMut<Args> { | ||
88 | extern "rust-call" fn call(&self, args: Args) -> Self::Output; | ||
89 | } | ||
90 | |||
91 | #[lang = "fn_mut"] | ||
92 | pub trait FnMut<Args>: FnOnce<Args> { | ||
93 | extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output; | ||
94 | } | ||
95 | #[lang = "fn_once"] | ||
96 | pub trait FnOnce<Args> { | ||
97 | #[lang = "fn_once_output"] | ||
98 | type Output; | ||
99 | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; | ||
100 | } | ||
101 | } | ||
102 | |||
103 | pub mod option { | ||
104 | pub enum Option<T> { | ||
105 | None, | ||
106 | Some(T), | ||
107 | } | ||
108 | } | ||
109 | |||
110 | pub mod prelude { | ||
111 | pub use crate::{ | ||
112 | convert::From, | ||
113 | default::Default, | ||
114 | iter::{IntoIterator, Iterator}, | ||
115 | ops::{Fn, FnMut, FnOnce}, | ||
116 | option::Option::{self, *}, | ||
117 | }; | ||
118 | } | ||
119 | #[prelude_import] | ||
120 | pub use prelude::*; | ||
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/imports_locator.rs index 0782ab070..e9f23adf8 100644 --- a/crates/ide_db/src/imports_locator.rs +++ b/crates/ide_db/src/imports_locator.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | //! This module contains an import search funcionality that is provided to the assists module. | 1 | //! This module contains an import search functionality that is provided to the assists module. |
2 | //! Later, this should be moved away to a separate crate that is accessible from the assists module. | 2 | //! Later, this should be moved away to a separate crate that is accessible from the assists module. |
3 | 3 | ||
4 | use hir::{import_map, AsAssocItem, Crate, MacroDef, ModuleDef, Semantics}; | 4 | use hir::{import_map, AsAssocItem, Crate, MacroDef, ModuleDef, Semantics}; |
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs index 37b06027c..b5fa46642 100644 --- a/crates/ide_db/src/search.rs +++ b/crates/ide_db/src/search.rs | |||
@@ -18,9 +18,43 @@ use crate::{ | |||
18 | RootDatabase, | 18 | RootDatabase, |
19 | }; | 19 | }; |
20 | 20 | ||
21 | #[derive(Debug, Default, Clone)] | ||
22 | pub struct UsageSearchResult { | ||
23 | pub references: FxHashMap<FileId, Vec<FileReference>>, | ||
24 | } | ||
25 | |||
26 | impl UsageSearchResult { | ||
27 | pub fn is_empty(&self) -> bool { | ||
28 | self.references.is_empty() | ||
29 | } | ||
30 | |||
31 | pub fn len(&self) -> usize { | ||
32 | self.references.len() | ||
33 | } | ||
34 | |||
35 | pub fn iter(&self) -> impl Iterator<Item = (&FileId, &Vec<FileReference>)> + '_ { | ||
36 | self.references.iter() | ||
37 | } | ||
38 | |||
39 | pub fn file_ranges(&self) -> impl Iterator<Item = FileRange> + '_ { | ||
40 | self.references.iter().flat_map(|(&file_id, refs)| { | ||
41 | refs.iter().map(move |&FileReference { range, .. }| FileRange { file_id, range }) | ||
42 | }) | ||
43 | } | ||
44 | } | ||
45 | |||
46 | impl IntoIterator for UsageSearchResult { | ||
47 | type Item = (FileId, Vec<FileReference>); | ||
48 | type IntoIter = <FxHashMap<FileId, Vec<FileReference>> as IntoIterator>::IntoIter; | ||
49 | |||
50 | fn into_iter(self) -> Self::IntoIter { | ||
51 | self.references.into_iter() | ||
52 | } | ||
53 | } | ||
54 | |||
21 | #[derive(Debug, Clone)] | 55 | #[derive(Debug, Clone)] |
22 | pub struct Reference { | 56 | pub struct FileReference { |
23 | pub file_range: FileRange, | 57 | pub range: TextRange, |
24 | pub kind: ReferenceKind, | 58 | pub kind: ReferenceKind, |
25 | pub access: Option<ReferenceAccess>, | 59 | pub access: Option<ReferenceAccess>, |
26 | } | 60 | } |
@@ -136,7 +170,7 @@ impl Definition { | |||
136 | return SearchScope::new(res); | 170 | return SearchScope::new(res); |
137 | } | 171 | } |
138 | 172 | ||
139 | if let Definition::LifetimeParam(param) = self { | 173 | if let Definition::GenericParam(hir::GenericParam::LifetimeParam(param)) = self { |
140 | let range = match param.parent(db) { | 174 | let range = match param.parent(db) { |
141 | hir::GenericDef::Function(it) => { | 175 | hir::GenericDef::Function(it) => { |
142 | it.source(db).and_then(|src| Some(src.value.syntax().text_range())) | 176 | it.source(db).and_then(|src| Some(src.value.syntax().text_range())) |
@@ -252,23 +286,23 @@ impl<'a> FindUsages<'a> { | |||
252 | 286 | ||
253 | pub fn at_least_one(self) -> bool { | 287 | pub fn at_least_one(self) -> bool { |
254 | let mut found = false; | 288 | let mut found = false; |
255 | self.search(&mut |_reference| { | 289 | self.search(&mut |_, _| { |
256 | found = true; | 290 | found = true; |
257 | true | 291 | true |
258 | }); | 292 | }); |
259 | found | 293 | found |
260 | } | 294 | } |
261 | 295 | ||
262 | pub fn all(self) -> Vec<Reference> { | 296 | pub fn all(self) -> UsageSearchResult { |
263 | let mut res = Vec::new(); | 297 | let mut res = UsageSearchResult::default(); |
264 | self.search(&mut |reference| { | 298 | self.search(&mut |file_id, reference| { |
265 | res.push(reference); | 299 | res.references.entry(file_id).or_default().push(reference); |
266 | false | 300 | false |
267 | }); | 301 | }); |
268 | res | 302 | res |
269 | } | 303 | } |
270 | 304 | ||
271 | fn search(self, sink: &mut dyn FnMut(Reference) -> bool) { | 305 | fn search(self, sink: &mut dyn FnMut(FileId, FileReference) -> bool) { |
272 | let _p = profile::span("FindUsages:search"); | 306 | let _p = profile::span("FindUsages:search"); |
273 | let sema = self.sema; | 307 | let sema = self.sema; |
274 | 308 | ||
@@ -320,16 +354,14 @@ impl<'a> FindUsages<'a> { | |||
320 | fn found_lifetime( | 354 | fn found_lifetime( |
321 | &self, | 355 | &self, |
322 | lifetime: &ast::Lifetime, | 356 | lifetime: &ast::Lifetime, |
323 | sink: &mut dyn FnMut(Reference) -> bool, | 357 | sink: &mut dyn FnMut(FileId, FileReference) -> bool, |
324 | ) -> bool { | 358 | ) -> bool { |
325 | match NameRefClass::classify_lifetime(self.sema, lifetime) { | 359 | match NameRefClass::classify_lifetime(self.sema, lifetime) { |
326 | Some(NameRefClass::Definition(def)) if &def == self.def => { | 360 | Some(NameRefClass::Definition(def)) if &def == self.def => { |
327 | let reference = Reference { | 361 | let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax()); |
328 | file_range: self.sema.original_range(lifetime.syntax()), | 362 | let reference = |
329 | kind: ReferenceKind::Lifetime, | 363 | FileReference { range, kind: ReferenceKind::Lifetime, access: None }; |
330 | access: None, | 364 | sink(file_id, reference) |
331 | }; | ||
332 | sink(reference) | ||
333 | } | 365 | } |
334 | _ => false, // not a usage | 366 | _ => false, // not a usage |
335 | } | 367 | } |
@@ -338,7 +370,7 @@ impl<'a> FindUsages<'a> { | |||
338 | fn found_name_ref( | 370 | fn found_name_ref( |
339 | &self, | 371 | &self, |
340 | name_ref: &ast::NameRef, | 372 | name_ref: &ast::NameRef, |
341 | sink: &mut dyn FnMut(Reference) -> bool, | 373 | sink: &mut dyn FnMut(FileId, FileReference) -> bool, |
342 | ) -> bool { | 374 | ) -> bool { |
343 | match NameRefClass::classify(self.sema, &name_ref) { | 375 | match NameRefClass::classify(self.sema, &name_ref) { |
344 | Some(NameRefClass::Definition(def)) if &def == self.def => { | 376 | Some(NameRefClass::Definition(def)) if &def == self.def => { |
@@ -352,46 +384,50 @@ impl<'a> FindUsages<'a> { | |||
352 | ReferenceKind::Other | 384 | ReferenceKind::Other |
353 | }; | 385 | }; |
354 | 386 | ||
355 | let reference = Reference { | 387 | let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); |
356 | file_range: self.sema.original_range(name_ref.syntax()), | 388 | let reference = |
357 | kind, | 389 | FileReference { range, kind, access: reference_access(&def, &name_ref) }; |
358 | access: reference_access(&def, &name_ref), | 390 | sink(file_id, reference) |
359 | }; | ||
360 | sink(reference) | ||
361 | } | 391 | } |
362 | Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { | 392 | Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { |
393 | let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); | ||
363 | let reference = match self.def { | 394 | let reference = match self.def { |
364 | Definition::Field(_) if &field == self.def => Reference { | 395 | Definition::Field(_) if &field == self.def => FileReference { |
365 | file_range: self.sema.original_range(name_ref.syntax()), | 396 | range, |
366 | kind: ReferenceKind::FieldShorthandForField, | 397 | kind: ReferenceKind::FieldShorthandForField, |
367 | access: reference_access(&field, &name_ref), | 398 | access: reference_access(&field, &name_ref), |
368 | }, | 399 | }, |
369 | Definition::Local(l) if &local == l => Reference { | 400 | Definition::Local(l) if &local == l => FileReference { |
370 | file_range: self.sema.original_range(name_ref.syntax()), | 401 | range, |
371 | kind: ReferenceKind::FieldShorthandForLocal, | 402 | kind: ReferenceKind::FieldShorthandForLocal, |
372 | access: reference_access(&Definition::Local(local), &name_ref), | 403 | access: reference_access(&Definition::Local(local), &name_ref), |
373 | }, | 404 | }, |
374 | _ => return false, // not a usage | 405 | _ => return false, // not a usage |
375 | }; | 406 | }; |
376 | sink(reference) | 407 | sink(file_id, reference) |
377 | } | 408 | } |
378 | _ => false, // not a usage | 409 | _ => false, // not a usage |
379 | } | 410 | } |
380 | } | 411 | } |
381 | 412 | ||
382 | fn found_name(&self, name: &ast::Name, sink: &mut dyn FnMut(Reference) -> bool) -> bool { | 413 | fn found_name( |
414 | &self, | ||
415 | name: &ast::Name, | ||
416 | sink: &mut dyn FnMut(FileId, FileReference) -> bool, | ||
417 | ) -> bool { | ||
383 | match NameClass::classify(self.sema, name) { | 418 | match NameClass::classify(self.sema, name) { |
384 | Some(NameClass::PatFieldShorthand { local_def: _, field_ref }) => { | 419 | Some(NameClass::PatFieldShorthand { local_def: _, field_ref }) => { |
385 | let reference = match self.def { | 420 | if !matches!(self.def, Definition::Field(_) if &field_ref == self.def) { |
386 | Definition::Field(_) if &field_ref == self.def => Reference { | 421 | return false; |
387 | file_range: self.sema.original_range(name.syntax()), | 422 | } |
388 | kind: ReferenceKind::FieldShorthandForField, | 423 | let FileRange { file_id, range } = self.sema.original_range(name.syntax()); |
389 | // FIXME: mutable patterns should have `Write` access | 424 | let reference = FileReference { |
390 | access: Some(ReferenceAccess::Read), | 425 | range, |
391 | }, | 426 | kind: ReferenceKind::FieldShorthandForField, |
392 | _ => return false, // not a usage | 427 | // FIXME: mutable patterns should have `Write` access |
428 | access: Some(ReferenceAccess::Read), | ||
393 | }; | 429 | }; |
394 | sink(reference) | 430 | sink(file_id, reference) |
395 | } | 431 | } |
396 | _ => false, // not a usage | 432 | _ => false, // not a usage |
397 | } | 433 | } |
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs index b3472879d..19543d777 100644 --- a/crates/mbe/src/lib.rs +++ b/crates/mbe/src/lib.rs | |||
@@ -24,7 +24,7 @@ use crate::{ | |||
24 | #[derive(Debug, PartialEq, Eq)] | 24 | #[derive(Debug, PartialEq, Eq)] |
25 | pub enum ParseError { | 25 | pub enum ParseError { |
26 | Expected(String), | 26 | Expected(String), |
27 | RepetitionEmtpyTokenTree, | 27 | RepetitionEmptyTokenTree, |
28 | } | 28 | } |
29 | 29 | ||
30 | #[derive(Debug, PartialEq, Eq, Clone)] | 30 | #[derive(Debug, PartialEq, Eq, Clone)] |
@@ -270,7 +270,7 @@ fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> { | |||
270 | } | 270 | } |
271 | false | 271 | false |
272 | }) { | 272 | }) { |
273 | return Err(ParseError::RepetitionEmtpyTokenTree); | 273 | return Err(ParseError::RepetitionEmptyTokenTree); |
274 | } | 274 | } |
275 | } | 275 | } |
276 | validate(subtree)? | 276 | validate(subtree)? |
diff --git a/crates/mbe/src/mbe_expander/matcher.rs b/crates/mbe/src/mbe_expander/matcher.rs index c6d615c81..d32e60521 100644 --- a/crates/mbe/src/mbe_expander/matcher.rs +++ b/crates/mbe/src/mbe_expander/matcher.rs | |||
@@ -378,7 +378,7 @@ pub(super) fn match_repeat( | |||
378 | src: &mut TtIter, | 378 | src: &mut TtIter, |
379 | ) -> Result<(), ExpandError> { | 379 | ) -> Result<(), ExpandError> { |
380 | // Dirty hack to make macro-expansion terminate. | 380 | // Dirty hack to make macro-expansion terminate. |
381 | // This should be replaced by a propper macro-by-example implementation | 381 | // This should be replaced by a proper macro-by-example implementation |
382 | let mut limit = 65536; | 382 | let mut limit = 65536; |
383 | let mut counter = 0; | 383 | let mut counter = 0; |
384 | 384 | ||
diff --git a/crates/mbe/src/mbe_expander/transcriber.rs b/crates/mbe/src/mbe_expander/transcriber.rs index 27b2ac777..59a3c80a8 100644 --- a/crates/mbe/src/mbe_expander/transcriber.rs +++ b/crates/mbe/src/mbe_expander/transcriber.rs | |||
@@ -67,7 +67,7 @@ struct NestingState { | |||
67 | /// because there is no variable in use by the current repetition | 67 | /// because there is no variable in use by the current repetition |
68 | hit: bool, | 68 | hit: bool, |
69 | /// `at_end` is currently necessary to tell `expand_repeat` if it should stop | 69 | /// `at_end` is currently necessary to tell `expand_repeat` if it should stop |
70 | /// because there is no more value avaible for the current repetition | 70 | /// because there is no more value available for the current repetition |
71 | at_end: bool, | 71 | at_end: bool, |
72 | } | 72 | } |
73 | 73 | ||
@@ -179,11 +179,7 @@ fn expand_repeat( | |||
179 | 179 | ||
180 | counter += 1; | 180 | counter += 1; |
181 | if counter == limit { | 181 | if counter == limit { |
182 | log::warn!( | 182 | log::warn!("expand_tt in repeat pattern exceed limit => {:#?}\n{:#?}", template, ctx); |
183 | "expand_tt excced in repeat pattern exceed limit => {:#?}\n{:#?}", | ||
184 | template, | ||
185 | ctx | ||
186 | ); | ||
187 | break; | 183 | break; |
188 | } | 184 | } |
189 | 185 | ||
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs index 671036e1c..e648519f9 100644 --- a/crates/mbe/src/syntax_bridge.rs +++ b/crates/mbe/src/syntax_bridge.rs | |||
@@ -149,7 +149,7 @@ impl TokenMap { | |||
149 | } | 149 | } |
150 | 150 | ||
151 | fn remove_delim(&mut self, idx: usize) { | 151 | fn remove_delim(&mut self, idx: usize) { |
152 | // FIXME: This could be accidently quadratic | 152 | // FIXME: This could be accidentally quadratic |
153 | self.entries.remove(idx); | 153 | self.entries.remove(idx); |
154 | } | 154 | } |
155 | } | 155 | } |
@@ -476,14 +476,14 @@ impl Convertor { | |||
476 | 476 | ||
477 | #[derive(Debug)] | 477 | #[derive(Debug)] |
478 | enum SynToken { | 478 | enum SynToken { |
479 | Ordiniary(SyntaxToken), | 479 | Ordinary(SyntaxToken), |
480 | Punch(SyntaxToken, TextSize), | 480 | Punch(SyntaxToken, TextSize), |
481 | } | 481 | } |
482 | 482 | ||
483 | impl SynToken { | 483 | impl SynToken { |
484 | fn token(&self) -> &SyntaxToken { | 484 | fn token(&self) -> &SyntaxToken { |
485 | match self { | 485 | match self { |
486 | SynToken::Ordiniary(it) => it, | 486 | SynToken::Ordinary(it) => it, |
487 | SynToken::Punch(it, _) => it, | 487 | SynToken::Punch(it, _) => it, |
488 | } | 488 | } |
489 | } | 489 | } |
@@ -495,7 +495,7 @@ impl SrcToken for SynToken { | |||
495 | } | 495 | } |
496 | fn to_char(&self) -> Option<char> { | 496 | fn to_char(&self) -> Option<char> { |
497 | match self { | 497 | match self { |
498 | SynToken::Ordiniary(_) => None, | 498 | SynToken::Ordinary(_) => None, |
499 | SynToken::Punch(it, i) => it.text().chars().nth((*i).into()), | 499 | SynToken::Punch(it, i) => it.text().chars().nth((*i).into()), |
500 | } | 500 | } |
501 | } | 501 | } |
@@ -535,7 +535,7 @@ impl TokenConvertor for Convertor { | |||
535 | } else { | 535 | } else { |
536 | self.punct_offset = None; | 536 | self.punct_offset = None; |
537 | let range = curr.text_range(); | 537 | let range = curr.text_range(); |
538 | (SynToken::Ordiniary(curr), range) | 538 | (SynToken::Ordinary(curr), range) |
539 | }; | 539 | }; |
540 | 540 | ||
541 | Some(token) | 541 | Some(token) |
@@ -557,7 +557,7 @@ impl TokenConvertor for Convertor { | |||
557 | let token = if curr.kind().is_punct() { | 557 | let token = if curr.kind().is_punct() { |
558 | SynToken::Punch(curr, 0.into()) | 558 | SynToken::Punch(curr, 0.into()) |
559 | } else { | 559 | } else { |
560 | SynToken::Ordiniary(curr) | 560 | SynToken::Ordinary(curr) |
561 | }; | 561 | }; |
562 | Some(token) | 562 | Some(token) |
563 | } | 563 | } |
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs index 17ddd0a9c..9ff901e97 100644 --- a/crates/mbe/src/tests.rs +++ b/crates/mbe/src/tests.rs | |||
@@ -1988,7 +1988,7 @@ fn test_no_space_after_semi_colon() { | |||
1988 | #[test] | 1988 | #[test] |
1989 | fn test_rustc_issue_57597() { | 1989 | fn test_rustc_issue_57597() { |
1990 | fn test_error(fixture: &str) { | 1990 | fn test_error(fixture: &str) { |
1991 | assert_eq!(parse_macro_error(fixture), ParseError::RepetitionEmtpyTokenTree); | 1991 | assert_eq!(parse_macro_error(fixture), ParseError::RepetitionEmptyTokenTree); |
1992 | } | 1992 | } |
1993 | 1993 | ||
1994 | test_error("macro_rules! foo { ($($($i:ident)?)+) => {}; }"); | 1994 | test_error("macro_rules! foo { ($($($i:ident)?)+) => {}; }"); |
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs index c7a3556a7..d61950b96 100644 --- a/crates/parser/src/grammar/expressions/atom.rs +++ b/crates/parser/src/grammar/expressions/atom.rs | |||
@@ -15,8 +15,16 @@ use super::*; | |||
15 | // let _ = b"e"; | 15 | // let _ = b"e"; |
16 | // let _ = br"f"; | 16 | // let _ = br"f"; |
17 | // } | 17 | // } |
18 | pub(crate) const LITERAL_FIRST: TokenSet = | 18 | pub(crate) const LITERAL_FIRST: TokenSet = TokenSet::new(&[ |
19 | TokenSet::new(&[TRUE_KW, FALSE_KW, INT_NUMBER, FLOAT_NUMBER, BYTE, CHAR, STRING, BYTE_STRING]); | 19 | T![true], |
20 | T![false], | ||
21 | INT_NUMBER, | ||
22 | FLOAT_NUMBER, | ||
23 | BYTE, | ||
24 | CHAR, | ||
25 | STRING, | ||
26 | BYTE_STRING, | ||
27 | ]); | ||
20 | 28 | ||
21 | pub(crate) fn literal(p: &mut Parser) -> Option<CompletedMarker> { | 29 | pub(crate) fn literal(p: &mut Parser) -> Option<CompletedMarker> { |
22 | if !p.at_ts(LITERAL_FIRST) { | 30 | if !p.at_ts(LITERAL_FIRST) { |
diff --git a/crates/parser/src/grammar/items.rs b/crates/parser/src/grammar/items.rs index cf4168d32..2070ce163 100644 --- a/crates/parser/src/grammar/items.rs +++ b/crates/parser/src/grammar/items.rs | |||
@@ -27,19 +27,19 @@ pub(super) fn mod_contents(p: &mut Parser, stop_on_r_curly: bool) { | |||
27 | } | 27 | } |
28 | 28 | ||
29 | pub(super) const ITEM_RECOVERY_SET: TokenSet = TokenSet::new(&[ | 29 | pub(super) const ITEM_RECOVERY_SET: TokenSet = TokenSet::new(&[ |
30 | FN_KW, | 30 | T![fn], |
31 | STRUCT_KW, | 31 | T![struct], |
32 | ENUM_KW, | 32 | T![enum], |
33 | IMPL_KW, | 33 | T![impl], |
34 | TRAIT_KW, | 34 | T![trait], |
35 | CONST_KW, | 35 | T![const], |
36 | STATIC_KW, | 36 | T![static], |
37 | LET_KW, | 37 | T![let], |
38 | MOD_KW, | 38 | T![mod], |
39 | PUB_KW, | 39 | T![pub], |
40 | CRATE_KW, | 40 | T![crate], |
41 | USE_KW, | 41 | T![use], |
42 | MACRO_KW, | 42 | T![macro], |
43 | T![;], | 43 | T![;], |
44 | ]); | 44 | ]); |
45 | 45 | ||
diff --git a/crates/parser/src/grammar/items/traits.rs b/crates/parser/src/grammar/items/traits.rs index ab9a12b4d..d076974ed 100644 --- a/crates/parser/src/grammar/items/traits.rs +++ b/crates/parser/src/grammar/items/traits.rs | |||
@@ -110,7 +110,7 @@ fn choose_type_params_over_qpath(p: &Parser) -> bool { | |||
110 | if !p.at(T![<]) { | 110 | if !p.at(T![<]) { |
111 | return false; | 111 | return false; |
112 | } | 112 | } |
113 | if p.nth(1) == T![#] || p.nth(1) == T![>] || p.nth(1) == CONST_KW { | 113 | if p.nth(1) == T![#] || p.nth(1) == T![>] || p.nth(1) == T![const] { |
114 | return true; | 114 | return true; |
115 | } | 115 | } |
116 | (p.nth(1) == LIFETIME_IDENT || p.nth(1) == IDENT) | 116 | (p.nth(1) == LIFETIME_IDENT || p.nth(1) == IDENT) |
diff --git a/crates/parser/src/grammar/items/use_item.rs b/crates/parser/src/grammar/items/use_item.rs index 20e6a13cf..5cb8b08e7 100644 --- a/crates/parser/src/grammar/items/use_item.rs +++ b/crates/parser/src/grammar/items/use_item.rs | |||
@@ -46,7 +46,7 @@ fn use_tree(p: &mut Parser, top_level: bool) { | |||
46 | // test use_tree_list | 46 | // test use_tree_list |
47 | // use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`) | 47 | // use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`) |
48 | // use {path::from::root}; // Rust 2015 | 48 | // use {path::from::root}; // Rust 2015 |
49 | // use ::{some::arbritrary::path}; // Rust 2015 | 49 | // use ::{some::arbitrary::path}; // Rust 2015 |
50 | // use ::{{{root::export}}}; // Nonsensical but perfectly legal nesting | 50 | // use ::{{{root::export}}}; // Nonsensical but perfectly legal nesting |
51 | T!['{'] => { | 51 | T!['{'] => { |
52 | use_tree_list(p); | 52 | use_tree_list(p); |
diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs index b53d5749f..da71498a8 100644 --- a/crates/parser/src/grammar/patterns.rs +++ b/crates/parser/src/grammar/patterns.rs | |||
@@ -83,7 +83,7 @@ fn pattern_single_r(p: &mut Parser, recovery_set: TokenSet) { | |||
83 | } | 83 | } |
84 | 84 | ||
85 | const PAT_RECOVERY_SET: TokenSet = | 85 | const PAT_RECOVERY_SET: TokenSet = |
86 | TokenSet::new(&[LET_KW, IF_KW, WHILE_KW, LOOP_KW, MATCH_KW, R_PAREN, COMMA]); | 86 | TokenSet::new(&[T![let], T![if], T![while], T![loop], T![match], T![')'], T![,]]); |
87 | 87 | ||
88 | fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option<CompletedMarker> { | 88 | fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option<CompletedMarker> { |
89 | let m = match p.nth(0) { | 89 | let m = match p.nth(0) { |
diff --git a/crates/parser/src/grammar/type_args.rs b/crates/parser/src/grammar/type_args.rs index a013c49b9..42cd426bd 100644 --- a/crates/parser/src/grammar/type_args.rs +++ b/crates/parser/src/grammar/type_args.rs | |||
@@ -26,7 +26,7 @@ pub(super) fn opt_generic_arg_list(p: &mut Parser, colon_colon_required: bool) { | |||
26 | } | 26 | } |
27 | 27 | ||
28 | // test type_arg | 28 | // test type_arg |
29 | // type A = B<'static, i32, 1, { 2 }, Item=u64>; | 29 | // type A = B<'static, i32, 1, { 2 }, Item=u64, true, false>; |
30 | fn generic_arg(p: &mut Parser) { | 30 | fn generic_arg(p: &mut Parser) { |
31 | let m = p.start(); | 31 | let m = p.start(); |
32 | match p.current() { | 32 | match p.current() { |
@@ -55,6 +55,10 @@ fn generic_arg(p: &mut Parser) { | |||
55 | expressions::literal(p); | 55 | expressions::literal(p); |
56 | m.complete(p, CONST_ARG); | 56 | m.complete(p, CONST_ARG); |
57 | } | 57 | } |
58 | T![true] | T![false] => { | ||
59 | expressions::literal(p); | ||
60 | m.complete(p, CONST_ARG); | ||
61 | } | ||
58 | _ => { | 62 | _ => { |
59 | types::type_(p); | 63 | types::type_(p); |
60 | m.complete(p, TYPE_ARG); | 64 | m.complete(p, TYPE_ARG); |
diff --git a/crates/parser/src/grammar/type_params.rs b/crates/parser/src/grammar/type_params.rs index 4aeccd193..3de5248da 100644 --- a/crates/parser/src/grammar/type_params.rs +++ b/crates/parser/src/grammar/type_params.rs | |||
@@ -25,7 +25,7 @@ fn generic_param_list(p: &mut Parser) { | |||
25 | match p.current() { | 25 | match p.current() { |
26 | LIFETIME_IDENT => lifetime_param(p, m), | 26 | LIFETIME_IDENT => lifetime_param(p, m), |
27 | IDENT => type_param(p, m), | 27 | IDENT => type_param(p, m), |
28 | CONST_KW => const_param(p, m), | 28 | T![const] => const_param(p, m), |
29 | _ => { | 29 | _ => { |
30 | m.abandon(p); | 30 | m.abandon(p); |
31 | p.err_and_bump("expected type parameter") | 31 | p.err_and_bump("expected type parameter") |
@@ -66,7 +66,7 @@ fn type_param(p: &mut Parser, m: Marker) { | |||
66 | // test const_param | 66 | // test const_param |
67 | // struct S<const N: u32>; | 67 | // struct S<const N: u32>; |
68 | fn const_param(p: &mut Parser, m: Marker) { | 68 | fn const_param(p: &mut Parser, m: Marker) { |
69 | assert!(p.at(CONST_KW)); | 69 | assert!(p.at(T![const])); |
70 | p.bump(T![const]); | 70 | p.bump(T![const]); |
71 | name(p); | 71 | name(p); |
72 | types::ascription(p); | 72 | types::ascription(p); |
diff --git a/crates/proc_macro_api/src/msg.rs b/crates/proc_macro_api/src/msg.rs index 4cd572101..970f165ed 100644 --- a/crates/proc_macro_api/src/msg.rs +++ b/crates/proc_macro_api/src/msg.rs | |||
@@ -79,7 +79,7 @@ impl Message for Response {} | |||
79 | fn read_json(inp: &mut impl BufRead) -> io::Result<Option<String>> { | 79 | fn read_json(inp: &mut impl BufRead) -> io::Result<Option<String>> { |
80 | let mut buf = String::new(); | 80 | let mut buf = String::new(); |
81 | inp.read_line(&mut buf)?; | 81 | inp.read_line(&mut buf)?; |
82 | buf.pop(); // Remove traling '\n' | 82 | buf.pop(); // Remove trailing '\n' |
83 | Ok(match buf.len() { | 83 | Ok(match buf.len() { |
84 | 0 => None, | 84 | 0 => None, |
85 | _ => Some(buf), | 85 | _ => Some(buf), |
diff --git a/crates/proc_macro_srv/Cargo.toml b/crates/proc_macro_srv/Cargo.toml index f78c17194..83f9ead17 100644 --- a/crates/proc_macro_srv/Cargo.toml +++ b/crates/proc_macro_srv/Cargo.toml | |||
@@ -20,7 +20,7 @@ proc_macro_api = { path = "../proc_macro_api", version = "0.0.0" } | |||
20 | test_utils = { path = "../test_utils", version = "0.0.0" } | 20 | test_utils = { path = "../test_utils", version = "0.0.0" } |
21 | 21 | ||
22 | [dev-dependencies] | 22 | [dev-dependencies] |
23 | cargo_metadata = "=0.12.0" | 23 | cargo_metadata = "0.12.2" |
24 | 24 | ||
25 | # used as proc macro test targets | 25 | # used as proc macro test targets |
26 | serde_derive = "1.0.106" | 26 | serde_derive = "1.0.106" |
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs b/crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs index 3528d5c99..bd1e7c2fc 100644 --- a/crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs +++ b/crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs | |||
@@ -251,7 +251,7 @@ impl<S> DecodeMut<'_, '_, S> for String { | |||
251 | } | 251 | } |
252 | } | 252 | } |
253 | 253 | ||
254 | /// Simplied version of panic payloads, ignoring | 254 | /// Simplified version of panic payloads, ignoring |
255 | /// types other than `&'static str` and `String`. | 255 | /// types other than `&'static str` and `String`. |
256 | #[derive(Debug)] | 256 | #[derive(Debug)] |
257 | pub enum PanicMessage { | 257 | pub enum PanicMessage { |
diff --git a/crates/proc_macro_srv/src/rustc_server.rs b/crates/proc_macro_srv/src/rustc_server.rs index b54aa1f3b..e6006a3c8 100644 --- a/crates/proc_macro_srv/src/rustc_server.rs +++ b/crates/proc_macro_srv/src/rustc_server.rs | |||
@@ -4,7 +4,7 @@ | |||
4 | //! The lib-proc-macro server backend is `TokenStream`-agnostic, such that | 4 | //! The lib-proc-macro server backend is `TokenStream`-agnostic, such that |
5 | //! we could provide any TokenStream implementation. | 5 | //! we could provide any TokenStream implementation. |
6 | //! The original idea from fedochet is using proc-macro2 as backend, | 6 | //! The original idea from fedochet is using proc-macro2 as backend, |
7 | //! we use tt instead for better intergation with RA. | 7 | //! we use tt instead for better integration with RA. |
8 | //! | 8 | //! |
9 | //! FIXME: No span and source file information is implemented yet | 9 | //! FIXME: No span and source file information is implemented yet |
10 | 10 | ||
diff --git a/crates/project_model/Cargo.toml b/crates/project_model/Cargo.toml index a65e42261..855fb83ea 100644 --- a/crates/project_model/Cargo.toml +++ b/crates/project_model/Cargo.toml | |||
@@ -12,7 +12,7 @@ doctest = false | |||
12 | [dependencies] | 12 | [dependencies] |
13 | log = "0.4.8" | 13 | log = "0.4.8" |
14 | rustc-hash = "1.1.0" | 14 | rustc-hash = "1.1.0" |
15 | cargo_metadata = "=0.12.0" | 15 | cargo_metadata = "0.12.2" |
16 | serde = { version = "1.0.106", features = ["derive"] } | 16 | serde = { version = "1.0.106", features = ["derive"] } |
17 | serde_json = "1.0.48" | 17 | serde_json = "1.0.48" |
18 | anyhow = "1.0.26" | 18 | anyhow = "1.0.26" |
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs index 3db0d55c5..f148521a2 100644 --- a/crates/rust-analyzer/src/caps.rs +++ b/crates/rust-analyzer/src/caps.rs | |||
@@ -75,14 +75,24 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti | |||
75 | will_create: None, | 75 | will_create: None, |
76 | did_rename: None, | 76 | did_rename: None, |
77 | will_rename: Some(FileOperationRegistrationOptions { | 77 | will_rename: Some(FileOperationRegistrationOptions { |
78 | filters: vec![FileOperationFilter { | 78 | filters: vec![ |
79 | scheme: Some(String::from("file")), | 79 | FileOperationFilter { |
80 | pattern: FileOperationPattern { | 80 | scheme: Some(String::from("file")), |
81 | glob: String::from("**/*.rs"), | 81 | pattern: FileOperationPattern { |
82 | matches: Some(FileOperationPatternKind::File), | 82 | glob: String::from("**/*.rs"), |
83 | options: None, | 83 | matches: Some(FileOperationPatternKind::File), |
84 | options: None, | ||
85 | }, | ||
84 | }, | 86 | }, |
85 | }], | 87 | FileOperationFilter { |
88 | scheme: Some(String::from("file")), | ||
89 | pattern: FileOperationPattern { | ||
90 | glob: String::from("**"), | ||
91 | matches: Some(FileOperationPatternKind::Folder), | ||
92 | options: None, | ||
93 | }, | ||
94 | }, | ||
95 | ], | ||
86 | }), | 96 | }), |
87 | did_delete: None, | 97 | did_delete: None, |
88 | will_delete: None, | 98 | will_delete: None, |
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 9445aec07..fd1407e60 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs | |||
@@ -58,7 +58,7 @@ impl AnalysisStatsCmd { | |||
58 | let mut db_load_sw = self.stop_watch(); | 58 | let mut db_load_sw = self.stop_watch(); |
59 | let (host, vfs) = load_cargo(&self.path, self.load_output_dirs, self.with_proc_macro)?; | 59 | let (host, vfs) = load_cargo(&self.path, self.load_output_dirs, self.with_proc_macro)?; |
60 | let db = host.raw_database(); | 60 | let db = host.raw_database(); |
61 | eprintln!("Database loaded {}", db_load_sw.elapsed()); | 61 | eprintln!("{:<20} {}", "Database loaded:", db_load_sw.elapsed()); |
62 | 62 | ||
63 | let mut analysis_sw = self.stop_watch(); | 63 | let mut analysis_sw = self.stop_watch(); |
64 | let mut num_crates = 0; | 64 | let mut num_crates = 0; |
@@ -85,7 +85,7 @@ impl AnalysisStatsCmd { | |||
85 | shuffle(&mut rng, &mut visit_queue); | 85 | shuffle(&mut rng, &mut visit_queue); |
86 | } | 86 | } |
87 | 87 | ||
88 | eprintln!("Crates in this dir: {}", num_crates); | 88 | eprint!(" crates: {}", num_crates); |
89 | let mut num_decls = 0; | 89 | let mut num_decls = 0; |
90 | let mut funcs = Vec::new(); | 90 | let mut funcs = Vec::new(); |
91 | while let Some(module) = visit_queue.pop() { | 91 | while let Some(module) = visit_queue.pop() { |
@@ -109,10 +109,8 @@ impl AnalysisStatsCmd { | |||
109 | } | 109 | } |
110 | } | 110 | } |
111 | } | 111 | } |
112 | eprintln!("Total modules found: {}", visited_modules.len()); | 112 | eprintln!(", mods: {}, decls: {}, fns: {}", visited_modules.len(), num_decls, funcs.len()); |
113 | eprintln!("Total declarations: {}", num_decls); | 113 | eprintln!("{:<20} {}", "Item Collection:", analysis_sw.elapsed()); |
114 | eprintln!("Total functions: {}", funcs.len()); | ||
115 | eprintln!("Item Collection: {}", analysis_sw.elapsed()); | ||
116 | 114 | ||
117 | if self.randomize { | 115 | if self.randomize { |
118 | shuffle(&mut rng, &mut funcs); | 116 | shuffle(&mut rng, &mut funcs); |
@@ -135,7 +133,7 @@ impl AnalysisStatsCmd { | |||
135 | snap.0.infer(f_id.into()); | 133 | snap.0.infer(f_id.into()); |
136 | }) | 134 | }) |
137 | .count(); | 135 | .count(); |
138 | eprintln!("Parallel Inference: {}", inference_sw.elapsed()); | 136 | eprintln!("{:<20} {}", "Parallel Inference:", inference_sw.elapsed()); |
139 | } | 137 | } |
140 | 138 | ||
141 | let mut inference_sw = self.stop_watch(); | 139 | let mut inference_sw = self.stop_watch(); |
@@ -273,27 +271,22 @@ impl AnalysisStatsCmd { | |||
273 | bar.inc(1); | 271 | bar.inc(1); |
274 | } | 272 | } |
275 | bar.finish_and_clear(); | 273 | bar.finish_and_clear(); |
276 | eprintln!("Total expressions: {}", num_exprs); | ||
277 | eprintln!( | 274 | eprintln!( |
278 | "Expressions of unknown type: {} ({}%)", | 275 | " exprs: {}, ??ty: {} ({}%), ?ty: {} ({}%), !ty: {}", |
276 | num_exprs, | ||
279 | num_exprs_unknown, | 277 | num_exprs_unknown, |
280 | if num_exprs > 0 { num_exprs_unknown * 100 / num_exprs } else { 100 } | 278 | percentage(num_exprs_unknown, num_exprs), |
281 | ); | ||
282 | report_metric("unknown type", num_exprs_unknown, "#"); | ||
283 | |||
284 | eprintln!( | ||
285 | "Expressions of partially unknown type: {} ({}%)", | ||
286 | num_exprs_partially_unknown, | 279 | num_exprs_partially_unknown, |
287 | if num_exprs > 0 { num_exprs_partially_unknown * 100 / num_exprs } else { 100 } | 280 | percentage(num_exprs_partially_unknown, num_exprs), |
281 | num_type_mismatches | ||
288 | ); | 282 | ); |
289 | 283 | report_metric("unknown type", num_exprs_unknown, "#"); | |
290 | eprintln!("Type mismatches: {}", num_type_mismatches); | ||
291 | report_metric("type mismatches", num_type_mismatches, "#"); | 284 | report_metric("type mismatches", num_type_mismatches, "#"); |
292 | 285 | ||
293 | eprintln!("Inference: {}", inference_sw.elapsed()); | 286 | eprintln!("{:<20} {}", "Inference:", inference_sw.elapsed()); |
294 | 287 | ||
295 | let total_span = analysis_sw.elapsed(); | 288 | let total_span = analysis_sw.elapsed(); |
296 | eprintln!("Total: {}", total_span); | 289 | eprintln!("{:<20} {}", "Total:", total_span); |
297 | report_metric("total time", total_span.time.as_millis() as u64, "ms"); | 290 | report_metric("total time", total_span.time.as_millis() as u64, "ms"); |
298 | if let Some(instructions) = total_span.instructions { | 291 | if let Some(instructions) = total_span.instructions { |
299 | report_metric("total instructions", instructions, "#instr"); | 292 | report_metric("total instructions", instructions, "#instr"); |
@@ -302,7 +295,7 @@ impl AnalysisStatsCmd { | |||
302 | report_metric("total memory", memory.allocated.megabytes() as u64, "MB"); | 295 | report_metric("total memory", memory.allocated.megabytes() as u64, "MB"); |
303 | } | 296 | } |
304 | 297 | ||
305 | if self.memory_usage { | 298 | if self.memory_usage && verbosity.is_verbose() { |
306 | print_memory_usage(host, vfs); | 299 | print_memory_usage(host, vfs); |
307 | } | 300 | } |
308 | 301 | ||
@@ -325,3 +318,7 @@ fn shuffle<T>(rng: &mut Rand32, slice: &mut [T]) { | |||
325 | slice.swap(0, idx); | 318 | slice.swap(0, idx); |
326 | } | 319 | } |
327 | } | 320 | } |
321 | |||
322 | fn percentage(n: u64, total: u64) -> u64 { | ||
323 | (n * 100).checked_div(total).unwrap_or(100) | ||
324 | } | ||
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 2d3e25cbf..27b92a5a9 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -334,6 +334,18 @@ impl Config { | |||
334 | } | 334 | } |
335 | } | 335 | } |
336 | 336 | ||
337 | pub fn did_save_text_document_dynamic_registration(&self) -> bool { | ||
338 | let caps = | ||
339 | try_or!(self.caps.text_document.as_ref()?.synchronization.clone()?, Default::default()); | ||
340 | caps.did_save == Some(true) && caps.dynamic_registration == Some(true) | ||
341 | } | ||
342 | pub fn did_change_watched_files_dynamic_registration(&self) -> bool { | ||
343 | try_or!( | ||
344 | self.caps.workspace.as_ref()?.did_change_watched_files.as_ref()?.dynamic_registration?, | ||
345 | false | ||
346 | ) | ||
347 | } | ||
348 | |||
337 | pub fn location_link(&self) -> bool { | 349 | pub fn location_link(&self) -> bool { |
338 | try_or!(self.caps.text_document.as_ref()?.definition?.link_support?, false) | 350 | try_or!(self.caps.text_document.as_ref()?.definition?.link_support?, false) |
339 | } | 351 | } |
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 1f6bf1c8c..442fbd14c 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs | |||
@@ -22,6 +22,7 @@ use crate::{ | |||
22 | from_proto, | 22 | from_proto, |
23 | line_endings::LineEndings, | 23 | line_endings::LineEndings, |
24 | main_loop::Task, | 24 | main_loop::Task, |
25 | op_queue::OpQueue, | ||
25 | reload::SourceRootConfig, | 26 | reload::SourceRootConfig, |
26 | request_metrics::{LatestRequests, RequestMetrics}, | 27 | request_metrics::{LatestRequests, RequestMetrics}, |
27 | thread_pool::TaskPool, | 28 | thread_pool::TaskPool, |
@@ -78,6 +79,7 @@ pub(crate) struct GlobalState { | |||
78 | pub(crate) source_root_config: SourceRootConfig, | 79 | pub(crate) source_root_config: SourceRootConfig, |
79 | pub(crate) proc_macro_client: Option<ProcMacroClient>, | 80 | pub(crate) proc_macro_client: Option<ProcMacroClient>, |
80 | pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>, | 81 | pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>, |
82 | pub(crate) fetch_workspaces_queue: OpQueue, | ||
81 | latest_requests: Arc<RwLock<LatestRequests>>, | 83 | latest_requests: Arc<RwLock<LatestRequests>>, |
82 | } | 84 | } |
83 | 85 | ||
@@ -130,6 +132,7 @@ impl GlobalState { | |||
130 | source_root_config: SourceRootConfig::default(), | 132 | source_root_config: SourceRootConfig::default(), |
131 | proc_macro_client: None, | 133 | proc_macro_client: None, |
132 | workspaces: Arc::new(Vec::new()), | 134 | workspaces: Arc::new(Vec::new()), |
135 | fetch_workspaces_queue: OpQueue::default(), | ||
133 | latest_requests: Default::default(), | 136 | latest_requests: Default::default(), |
134 | } | 137 | } |
135 | } | 138 | } |
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 29cc9051e..dc81f55d6 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -432,9 +432,27 @@ pub(crate) fn handle_will_rename_files( | |||
432 | // Limit to single-level moves for now. | 432 | // Limit to single-level moves for now. |
433 | match (from_path.parent(), to_path.parent()) { | 433 | match (from_path.parent(), to_path.parent()) { |
434 | (Some(p1), Some(p2)) if p1 == p2 => { | 434 | (Some(p1), Some(p2)) if p1 == p2 => { |
435 | let new_name = to_path.file_stem()?; | 435 | if from_path.is_dir() { |
436 | let new_name = new_name.to_str()?; | 436 | // add '/' to end of url -- from `file://path/to/folder` to `file://path/to/folder/` |
437 | Some((snap.url_to_file_id(&from).ok()?, new_name.to_string())) | 437 | let mut old_folder_name = from_path.file_stem()?.to_str()?.to_string(); |
438 | old_folder_name.push('/'); | ||
439 | let from_with_trailing_slash = from.join(&old_folder_name).ok()?; | ||
440 | |||
441 | let imitate_from_url = from_with_trailing_slash.join("mod.rs").ok()?; | ||
442 | let new_file_name = to_path.file_name()?.to_str()?; | ||
443 | Some(( | ||
444 | snap.url_to_file_id(&imitate_from_url).ok()?, | ||
445 | new_file_name.to_string(), | ||
446 | )) | ||
447 | } else { | ||
448 | let old_name = from_path.file_stem()?.to_str()?; | ||
449 | let new_name = to_path.file_stem()?.to_str()?; | ||
450 | match (old_name, new_name) { | ||
451 | ("mod", _) => None, | ||
452 | (_, "mod") => None, | ||
453 | _ => Some((snap.url_to_file_id(&from).ok()?, new_name.to_string())), | ||
454 | } | ||
455 | } | ||
438 | } | 456 | } |
439 | _ => None, | 457 | _ => None, |
440 | } | 458 | } |
@@ -773,7 +791,8 @@ pub(crate) fn handle_prepare_rename( | |||
773 | let _p = profile::span("handle_prepare_rename"); | 791 | let _p = profile::span("handle_prepare_rename"); |
774 | let position = from_proto::file_position(&snap, params)?; | 792 | let position = from_proto::file_position(&snap, params)?; |
775 | 793 | ||
776 | let change = snap.analysis.prepare_rename(position)??; | 794 | let change = snap.analysis.prepare_rename(position)?.map_err(to_proto::rename_error)?; |
795 | |||
777 | let line_index = snap.analysis.file_line_index(position.file_id)?; | 796 | let line_index = snap.analysis.file_line_index(position.file_id)?; |
778 | let range = to_proto::range(&line_index, change.range); | 797 | let range = to_proto::range(&line_index, change.range); |
779 | Ok(Some(PrepareRenameResponse::Range(range))) | 798 | Ok(Some(PrepareRenameResponse::Range(range))) |
@@ -786,15 +805,8 @@ pub(crate) fn handle_rename( | |||
786 | let _p = profile::span("handle_rename"); | 805 | let _p = profile::span("handle_rename"); |
787 | let position = from_proto::file_position(&snap, params.text_document_position)?; | 806 | let position = from_proto::file_position(&snap, params.text_document_position)?; |
788 | 807 | ||
789 | if params.new_name.is_empty() { | 808 | let change = |
790 | return Err(LspError::new( | 809 | snap.analysis.rename(position, &*params.new_name)?.map_err(to_proto::rename_error)?; |
791 | ErrorCode::InvalidParams as i32, | ||
792 | "New Name cannot be empty".into(), | ||
793 | ) | ||
794 | .into()); | ||
795 | } | ||
796 | |||
797 | let change = snap.analysis.rename(position, &*params.new_name)??; | ||
798 | let workspace_edit = to_proto::workspace_edit(&snap, change.info)?; | 810 | let workspace_edit = to_proto::workspace_edit(&snap, change.info)?; |
799 | Ok(Some(workspace_edit)) | 811 | Ok(Some(workspace_edit)) |
800 | } | 812 | } |
@@ -812,14 +824,15 @@ pub(crate) fn handle_references( | |||
812 | }; | 824 | }; |
813 | 825 | ||
814 | let locations = if params.context.include_declaration { | 826 | let locations = if params.context.include_declaration { |
815 | refs.into_iter() | 827 | refs.references_with_declaration() |
816 | .filter_map(|reference| to_proto::location(&snap, reference.file_range).ok()) | 828 | .file_ranges() |
829 | .filter_map(|frange| to_proto::location(&snap, frange).ok()) | ||
817 | .collect() | 830 | .collect() |
818 | } else { | 831 | } else { |
819 | // Only iterate over the references if include_declaration was false | 832 | // Only iterate over the references if include_declaration was false |
820 | refs.references() | 833 | refs.references() |
821 | .iter() | 834 | .file_ranges() |
822 | .filter_map(|reference| to_proto::location(&snap, reference.file_range).ok()) | 835 | .filter_map(|frange| to_proto::location(&snap, frange).ok()) |
823 | .collect() | 836 | .collect() |
824 | }; | 837 | }; |
825 | 838 | ||
@@ -1175,8 +1188,8 @@ pub(crate) fn handle_code_lens_resolve( | |||
1175 | .unwrap_or(None) | 1188 | .unwrap_or(None) |
1176 | .map(|r| { | 1189 | .map(|r| { |
1177 | r.references() | 1190 | r.references() |
1178 | .iter() | 1191 | .file_ranges() |
1179 | .filter_map(|it| to_proto::location(&snap, it.file_range).ok()) | 1192 | .filter_map(|frange| to_proto::location(&snap, frange).ok()) |
1180 | .collect_vec() | 1193 | .collect_vec() |
1181 | }) | 1194 | }) |
1182 | .unwrap_or_default(); | 1195 | .unwrap_or_default(); |
@@ -1220,13 +1233,19 @@ pub(crate) fn handle_document_highlight( | |||
1220 | }; | 1233 | }; |
1221 | 1234 | ||
1222 | let res = refs | 1235 | let res = refs |
1223 | .into_iter() | 1236 | .references_with_declaration() |
1224 | .filter(|reference| reference.file_range.file_id == position.file_id) | 1237 | .references |
1225 | .map(|reference| DocumentHighlight { | 1238 | .get(&position.file_id) |
1226 | range: to_proto::range(&line_index, reference.file_range.range), | 1239 | .map(|file_refs| { |
1227 | kind: reference.access.map(to_proto::document_highlight_kind), | 1240 | file_refs |
1241 | .into_iter() | ||
1242 | .map(|r| DocumentHighlight { | ||
1243 | range: to_proto::range(&line_index, r.range), | ||
1244 | kind: r.access.map(to_proto::document_highlight_kind), | ||
1245 | }) | ||
1246 | .collect() | ||
1228 | }) | 1247 | }) |
1229 | .collect(); | 1248 | .unwrap_or_default(); |
1230 | Ok(Some(res)) | 1249 | Ok(Some(res)) |
1231 | } | 1250 | } |
1232 | 1251 | ||
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs index c9494e300..2207b9a87 100644 --- a/crates/rust-analyzer/src/lib.rs +++ b/crates/rust-analyzer/src/lib.rs | |||
@@ -35,6 +35,7 @@ mod lsp_utils; | |||
35 | mod thread_pool; | 35 | mod thread_pool; |
36 | mod document; | 36 | mod document; |
37 | mod diff; | 37 | mod diff; |
38 | mod op_queue; | ||
38 | pub mod lsp_ext; | 39 | pub mod lsp_ext; |
39 | pub mod config; | 40 | pub mod config; |
40 | 41 | ||
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs index 40de56dad..2d06fe538 100644 --- a/crates/rust-analyzer/src/lsp_utils.rs +++ b/crates/rust-analyzer/src/lsp_utils.rs | |||
@@ -130,7 +130,7 @@ pub(crate) fn apply_document_changes( | |||
130 | } | 130 | } |
131 | 131 | ||
132 | /// Checks that the edits inside the completion and the additional edits do not overlap. | 132 | /// Checks that the edits inside the completion and the additional edits do not overlap. |
133 | /// LSP explicitly forbits the additional edits to overlap both with the main edit and themselves. | 133 | /// LSP explicitly forbids the additional edits to overlap both with the main edit and themselves. |
134 | pub(crate) fn all_edits_are_disjoint( | 134 | pub(crate) fn all_edits_are_disjoint( |
135 | completion: &lsp_types::CompletionItem, | 135 | completion: &lsp_types::CompletionItem, |
136 | additional_edits: &[lsp_types::TextEdit], | 136 | additional_edits: &[lsp_types::TextEdit], |
@@ -290,7 +290,7 @@ mod tests { | |||
290 | Some(vec![disjoint_edit.clone(), joint_edit.clone()]); | 290 | Some(vec![disjoint_edit.clone(), joint_edit.clone()]); |
291 | assert!( | 291 | assert!( |
292 | !all_edits_are_disjoint(&completion_with_joint_edits, &[]), | 292 | !all_edits_are_disjoint(&completion_with_joint_edits, &[]), |
293 | "Completion with disjoint edits fails the validaton even with empty extra edits" | 293 | "Completion with disjoint edits fails the validation even with empty extra edits" |
294 | ); | 294 | ); |
295 | 295 | ||
296 | completion_with_joint_edits.text_edit = | 296 | completion_with_joint_edits.text_edit = |
@@ -298,7 +298,7 @@ mod tests { | |||
298 | completion_with_joint_edits.additional_text_edits = Some(vec![joint_edit.clone()]); | 298 | completion_with_joint_edits.additional_text_edits = Some(vec![joint_edit.clone()]); |
299 | assert!( | 299 | assert!( |
300 | !all_edits_are_disjoint(&completion_with_joint_edits, &[]), | 300 | !all_edits_are_disjoint(&completion_with_joint_edits, &[]), |
301 | "Completion with disjoint edits fails the validaton even with empty extra edits" | 301 | "Completion with disjoint edits fails the validation even with empty extra edits" |
302 | ); | 302 | ); |
303 | 303 | ||
304 | completion_with_joint_edits.text_edit = | 304 | completion_with_joint_edits.text_edit = |
@@ -310,7 +310,7 @@ mod tests { | |||
310 | completion_with_joint_edits.additional_text_edits = None; | 310 | completion_with_joint_edits.additional_text_edits = None; |
311 | assert!( | 311 | assert!( |
312 | !all_edits_are_disjoint(&completion_with_joint_edits, &[]), | 312 | !all_edits_are_disjoint(&completion_with_joint_edits, &[]), |
313 | "Completion with disjoint edits fails the validaton even with empty extra edits" | 313 | "Completion with disjoint edits fails the validation even with empty extra edits" |
314 | ); | 314 | ); |
315 | 315 | ||
316 | completion_with_joint_edits.text_edit = | 316 | completion_with_joint_edits.text_edit = |
@@ -322,7 +322,7 @@ mod tests { | |||
322 | completion_with_joint_edits.additional_text_edits = Some(vec![joint_edit]); | 322 | completion_with_joint_edits.additional_text_edits = Some(vec![joint_edit]); |
323 | assert!( | 323 | assert!( |
324 | !all_edits_are_disjoint(&completion_with_joint_edits, &[]), | 324 | !all_edits_are_disjoint(&completion_with_joint_edits, &[]), |
325 | "Completion with disjoint edits fails the validaton even with empty extra edits" | 325 | "Completion with disjoint edits fails the validation even with empty extra edits" |
326 | ); | 326 | ); |
327 | } | 327 | } |
328 | 328 | ||
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 22ee96775..6d2475a59 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -11,7 +11,6 @@ use ide::{Canceled, FileId}; | |||
11 | use ide_db::base_db::VfsPath; | 11 | use ide_db::base_db::VfsPath; |
12 | use lsp_server::{Connection, Notification, Request, Response}; | 12 | use lsp_server::{Connection, Notification, Request, Response}; |
13 | use lsp_types::notification::Notification as _; | 13 | use lsp_types::notification::Notification as _; |
14 | use project_model::ProjectWorkspace; | ||
15 | use vfs::ChangeKind; | 14 | use vfs::ChangeKind; |
16 | 15 | ||
17 | use crate::{ | 16 | use crate::{ |
@@ -62,7 +61,6 @@ enum Event { | |||
62 | pub(crate) enum Task { | 61 | pub(crate) enum Task { |
63 | Response(Response), | 62 | Response(Response), |
64 | Diagnostics(Vec<(FileId, Vec<lsp_types::Diagnostic>)>), | 63 | Diagnostics(Vec<(FileId, Vec<lsp_types::Diagnostic>)>), |
65 | Workspaces(Vec<anyhow::Result<ProjectWorkspace>>), | ||
66 | PrimeCaches(PrimeCachesProgress), | 64 | PrimeCaches(PrimeCachesProgress), |
67 | FetchWorkspace(ProjectWorkspaceProgress), | 65 | FetchWorkspace(ProjectWorkspaceProgress), |
68 | } | 66 | } |
@@ -110,40 +108,43 @@ impl GlobalState { | |||
110 | ); | 108 | ); |
111 | }; | 109 | }; |
112 | 110 | ||
113 | let save_registration_options = lsp_types::TextDocumentSaveRegistrationOptions { | 111 | if self.config.did_save_text_document_dynamic_registration() { |
114 | include_text: Some(false), | 112 | let save_registration_options = lsp_types::TextDocumentSaveRegistrationOptions { |
115 | text_document_registration_options: lsp_types::TextDocumentRegistrationOptions { | 113 | include_text: Some(false), |
116 | document_selector: Some(vec![ | 114 | text_document_registration_options: lsp_types::TextDocumentRegistrationOptions { |
117 | lsp_types::DocumentFilter { | 115 | document_selector: Some(vec![ |
118 | language: None, | 116 | lsp_types::DocumentFilter { |
119 | scheme: None, | 117 | language: None, |
120 | pattern: Some("**/*.rs".into()), | 118 | scheme: None, |
121 | }, | 119 | pattern: Some("**/*.rs".into()), |
122 | lsp_types::DocumentFilter { | 120 | }, |
123 | language: None, | 121 | lsp_types::DocumentFilter { |
124 | scheme: None, | 122 | language: None, |
125 | pattern: Some("**/Cargo.toml".into()), | 123 | scheme: None, |
126 | }, | 124 | pattern: Some("**/Cargo.toml".into()), |
127 | lsp_types::DocumentFilter { | 125 | }, |
128 | language: None, | 126 | lsp_types::DocumentFilter { |
129 | scheme: None, | 127 | language: None, |
130 | pattern: Some("**/Cargo.lock".into()), | 128 | scheme: None, |
131 | }, | 129 | pattern: Some("**/Cargo.lock".into()), |
132 | ]), | 130 | }, |
133 | }, | 131 | ]), |
134 | }; | 132 | }, |
135 | 133 | }; | |
136 | let registration = lsp_types::Registration { | 134 | |
137 | id: "textDocument/didSave".to_string(), | 135 | let registration = lsp_types::Registration { |
138 | method: "textDocument/didSave".to_string(), | 136 | id: "textDocument/didSave".to_string(), |
139 | register_options: Some(serde_json::to_value(save_registration_options).unwrap()), | 137 | method: "textDocument/didSave".to_string(), |
140 | }; | 138 | register_options: Some(serde_json::to_value(save_registration_options).unwrap()), |
141 | self.send_request::<lsp_types::request::RegisterCapability>( | 139 | }; |
142 | lsp_types::RegistrationParams { registrations: vec![registration] }, | 140 | self.send_request::<lsp_types::request::RegisterCapability>( |
143 | |_, _| (), | 141 | lsp_types::RegistrationParams { registrations: vec![registration] }, |
144 | ); | 142 | |_, _| (), |
143 | ); | ||
144 | } | ||
145 | 145 | ||
146 | self.fetch_workspaces(); | 146 | self.fetch_workspaces_request(); |
147 | self.fetch_workspaces_if_needed(); | ||
147 | 148 | ||
148 | while let Some(event) = self.next_event(&inbox) { | 149 | while let Some(event) = self.next_event(&inbox) { |
149 | if let Event::Lsp(lsp_server::Message::Notification(not)) = &event { | 150 | if let Event::Lsp(lsp_server::Message::Notification(not)) = &event { |
@@ -204,7 +205,6 @@ impl GlobalState { | |||
204 | self.diagnostics.set_native_diagnostics(file_id, diagnostics) | 205 | self.diagnostics.set_native_diagnostics(file_id, diagnostics) |
205 | } | 206 | } |
206 | } | 207 | } |
207 | Task::Workspaces(workspaces) => self.switch_workspaces(workspaces), | ||
208 | Task::PrimeCaches(progress) => match progress { | 208 | Task::PrimeCaches(progress) => match progress { |
209 | PrimeCachesProgress::Started => prime_caches_progress.push(progress), | 209 | PrimeCachesProgress::Started => prime_caches_progress.push(progress), |
210 | PrimeCachesProgress::StartedOnCrate { .. } => { | 210 | PrimeCachesProgress::StartedOnCrate { .. } => { |
@@ -224,7 +224,11 @@ impl GlobalState { | |||
224 | ProjectWorkspaceProgress::Report(msg) => { | 224 | ProjectWorkspaceProgress::Report(msg) => { |
225 | (Progress::Report, Some(msg)) | 225 | (Progress::Report, Some(msg)) |
226 | } | 226 | } |
227 | ProjectWorkspaceProgress::End => (Progress::End, None), | 227 | ProjectWorkspaceProgress::End(workspaces) => { |
228 | self.fetch_workspaces_completed(); | ||
229 | self.switch_workspaces(workspaces); | ||
230 | (Progress::End, None) | ||
231 | } | ||
228 | }; | 232 | }; |
229 | self.report_progress("fetching", state, msg, None); | 233 | self.report_progress("fetching", state, msg, None); |
230 | } | 234 | } |
@@ -403,6 +407,8 @@ impl GlobalState { | |||
403 | } | 407 | } |
404 | } | 408 | } |
405 | 409 | ||
410 | self.fetch_workspaces_if_needed(); | ||
411 | |||
406 | let loop_duration = loop_start.elapsed(); | 412 | let loop_duration = loop_start.elapsed(); |
407 | if loop_duration > Duration::from_millis(100) { | 413 | if loop_duration > Duration::from_millis(100) { |
408 | log::warn!("overly long loop turn: {:?}", loop_duration); | 414 | log::warn!("overly long loop turn: {:?}", loop_duration); |
@@ -440,7 +446,7 @@ impl GlobalState { | |||
440 | } | 446 | } |
441 | 447 | ||
442 | RequestDispatcher { req: Some(req), global_state: self } | 448 | RequestDispatcher { req: Some(req), global_state: self } |
443 | .on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| Ok(s.fetch_workspaces()))? | 449 | .on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| Ok(s.fetch_workspaces_request()))? |
444 | .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))? | 450 | .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))? |
445 | .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))? | 451 | .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))? |
446 | .on_sync::<lsp_types::request::Shutdown>(|s, ()| { | 452 | .on_sync::<lsp_types::request::Shutdown>(|s, ()| { |
diff --git a/crates/rust-analyzer/src/markdown.rs b/crates/rust-analyzer/src/markdown.rs index a49a58c00..865eaae9b 100644 --- a/crates/rust-analyzer/src/markdown.rs +++ b/crates/rust-analyzer/src/markdown.rs | |||
@@ -106,7 +106,7 @@ mod tests { | |||
106 | 106 | ||
107 | #[test] | 107 | #[test] |
108 | fn test_format_docs_preserves_newlines() { | 108 | fn test_format_docs_preserves_newlines() { |
109 | let comment = "this\nis\nultiline"; | 109 | let comment = "this\nis\nmultiline"; |
110 | assert_eq!(format_docs(comment), comment); | 110 | assert_eq!(format_docs(comment), comment); |
111 | } | 111 | } |
112 | 112 | ||
diff --git a/crates/rust-analyzer/src/op_queue.rs b/crates/rust-analyzer/src/op_queue.rs new file mode 100644 index 000000000..51d66f4b3 --- /dev/null +++ b/crates/rust-analyzer/src/op_queue.rs | |||
@@ -0,0 +1,25 @@ | |||
1 | //! Bookkeeping to make sure only one long-running operation is executed. | ||
2 | |||
3 | #[derive(Default)] | ||
4 | pub(crate) struct OpQueue { | ||
5 | op_scheduled: bool, | ||
6 | op_in_progress: bool, | ||
7 | } | ||
8 | |||
9 | impl OpQueue { | ||
10 | pub(crate) fn request_op(&mut self) { | ||
11 | self.op_scheduled = true; | ||
12 | } | ||
13 | pub(crate) fn should_start_op(&mut self) -> bool { | ||
14 | if !self.op_in_progress && self.op_scheduled { | ||
15 | self.op_in_progress = true; | ||
16 | self.op_scheduled = false; | ||
17 | return true; | ||
18 | } | ||
19 | false | ||
20 | } | ||
21 | pub(crate) fn op_completed(&mut self) { | ||
22 | assert!(self.op_in_progress); | ||
23 | self.op_in_progress = false; | ||
24 | } | ||
25 | } | ||
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index f4e084741..97e20362f 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs | |||
@@ -19,7 +19,7 @@ use lsp_ext::StatusParams; | |||
19 | pub(crate) enum ProjectWorkspaceProgress { | 19 | pub(crate) enum ProjectWorkspaceProgress { |
20 | Begin, | 20 | Begin, |
21 | Report(String), | 21 | Report(String), |
22 | End, | 22 | End(Vec<anyhow::Result<ProjectWorkspace>>), |
23 | } | 23 | } |
24 | 24 | ||
25 | impl GlobalState { | 25 | impl GlobalState { |
@@ -30,7 +30,7 @@ impl GlobalState { | |||
30 | self.analysis_host.update_lru_capacity(self.config.lru_capacity()); | 30 | self.analysis_host.update_lru_capacity(self.config.lru_capacity()); |
31 | } | 31 | } |
32 | if self.config.linked_projects() != old_config.linked_projects() { | 32 | if self.config.linked_projects() != old_config.linked_projects() { |
33 | self.fetch_workspaces() | 33 | self.fetch_workspaces_request() |
34 | } else if self.config.flycheck() != old_config.flycheck() { | 34 | } else if self.config.flycheck() != old_config.flycheck() { |
35 | self.reload_flycheck(); | 35 | self.reload_flycheck(); |
36 | } | 36 | } |
@@ -44,7 +44,7 @@ impl GlobalState { | |||
44 | Status::Ready | Status::Invalid => (), | 44 | Status::Ready | Status::Invalid => (), |
45 | } | 45 | } |
46 | if self.config.cargo_autoreload() { | 46 | if self.config.cargo_autoreload() { |
47 | self.fetch_workspaces(); | 47 | self.fetch_workspaces_request(); |
48 | } else { | 48 | } else { |
49 | self.transition(Status::NeedsReload); | 49 | self.transition(Status::NeedsReload); |
50 | } | 50 | } |
@@ -98,8 +98,15 @@ impl GlobalState { | |||
98 | }); | 98 | }); |
99 | } | 99 | } |
100 | } | 100 | } |
101 | pub(crate) fn fetch_workspaces(&mut self) { | 101 | |
102 | pub(crate) fn fetch_workspaces_request(&mut self) { | ||
103 | self.fetch_workspaces_queue.request_op() | ||
104 | } | ||
105 | pub(crate) fn fetch_workspaces_if_needed(&mut self) { | ||
102 | log::info!("will fetch workspaces"); | 106 | log::info!("will fetch workspaces"); |
107 | if !self.fetch_workspaces_queue.should_start_op() { | ||
108 | return; | ||
109 | } | ||
103 | 110 | ||
104 | self.task_pool.handle.spawn_with_sender({ | 111 | self.task_pool.handle.spawn_with_sender({ |
105 | let linked_projects = self.config.linked_projects(); | 112 | let linked_projects = self.config.linked_projects(); |
@@ -133,12 +140,17 @@ impl GlobalState { | |||
133 | }) | 140 | }) |
134 | .collect::<Vec<_>>(); | 141 | .collect::<Vec<_>>(); |
135 | 142 | ||
136 | sender.send(Task::FetchWorkspace(ProjectWorkspaceProgress::End)).unwrap(); | ||
137 | log::info!("did fetch workspaces {:?}", workspaces); | 143 | log::info!("did fetch workspaces {:?}", workspaces); |
138 | sender.send(Task::Workspaces(workspaces)).unwrap() | 144 | sender |
145 | .send(Task::FetchWorkspace(ProjectWorkspaceProgress::End(workspaces))) | ||
146 | .unwrap(); | ||
139 | } | 147 | } |
140 | }); | 148 | }); |
141 | } | 149 | } |
150 | pub(crate) fn fetch_workspaces_completed(&mut self) { | ||
151 | self.fetch_workspaces_queue.op_completed() | ||
152 | } | ||
153 | |||
142 | pub(crate) fn switch_workspaces(&mut self, workspaces: Vec<anyhow::Result<ProjectWorkspace>>) { | 154 | pub(crate) fn switch_workspaces(&mut self, workspaces: Vec<anyhow::Result<ProjectWorkspace>>) { |
143 | let _p = profile::span("GlobalState::switch_workspaces"); | 155 | let _p = profile::span("GlobalState::switch_workspaces"); |
144 | log::info!("will switch workspaces: {:?}", workspaces); | 156 | log::info!("will switch workspaces: {:?}", workspaces); |
@@ -170,26 +182,31 @@ impl GlobalState { | |||
170 | } | 182 | } |
171 | 183 | ||
172 | if let FilesWatcher::Client = self.config.files().watcher { | 184 | if let FilesWatcher::Client = self.config.files().watcher { |
173 | let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions { | 185 | if self.config.did_change_watched_files_dynamic_registration() { |
174 | watchers: workspaces | 186 | let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions { |
175 | .iter() | 187 | watchers: workspaces |
176 | .flat_map(ProjectWorkspace::to_roots) | 188 | .iter() |
177 | .filter(|it| it.is_member) | 189 | .flat_map(ProjectWorkspace::to_roots) |
178 | .flat_map(|root| { | 190 | .filter(|it| it.is_member) |
179 | root.include.into_iter().map(|it| format!("{}/**/*.rs", it.display())) | 191 | .flat_map(|root| { |
180 | }) | 192 | root.include.into_iter().map(|it| format!("{}/**/*.rs", it.display())) |
181 | .map(|glob_pattern| lsp_types::FileSystemWatcher { glob_pattern, kind: None }) | 193 | }) |
182 | .collect(), | 194 | .map(|glob_pattern| lsp_types::FileSystemWatcher { |
183 | }; | 195 | glob_pattern, |
184 | let registration = lsp_types::Registration { | 196 | kind: None, |
185 | id: "workspace/didChangeWatchedFiles".to_string(), | 197 | }) |
186 | method: "workspace/didChangeWatchedFiles".to_string(), | 198 | .collect(), |
187 | register_options: Some(serde_json::to_value(registration_options).unwrap()), | 199 | }; |
188 | }; | 200 | let registration = lsp_types::Registration { |
189 | self.send_request::<lsp_types::request::RegisterCapability>( | 201 | id: "workspace/didChangeWatchedFiles".to_string(), |
190 | lsp_types::RegistrationParams { registrations: vec![registration] }, | 202 | method: "workspace/didChangeWatchedFiles".to_string(), |
191 | |_, _| (), | 203 | register_options: Some(serde_json::to_value(registration_options).unwrap()), |
192 | ); | 204 | }; |
205 | self.send_request::<lsp_types::request::RegisterCapability>( | ||
206 | lsp_types::RegistrationParams { registrations: vec![registration] }, | ||
207 | |_, _| (), | ||
208 | ); | ||
209 | } | ||
193 | } | 210 | } |
194 | 211 | ||
195 | let mut change = Change::new(); | 212 | let mut change = Change::new(); |
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs index 5c4366f16..7ce9a4ab6 100644 --- a/crates/rust-analyzer/src/semantic_tokens.rs +++ b/crates/rust-analyzer/src/semantic_tokens.rs | |||
@@ -38,17 +38,25 @@ macro_rules! define_semantic_token_types { | |||
38 | } | 38 | } |
39 | 39 | ||
40 | define_semantic_token_types![ | 40 | define_semantic_token_types![ |
41 | (ANGLE, "angle"), | ||
41 | (ATTRIBUTE, "attribute"), | 42 | (ATTRIBUTE, "attribute"), |
42 | (BOOLEAN, "boolean"), | 43 | (BOOLEAN, "boolean"), |
44 | (BRACE, "brace"), | ||
45 | (BRACKET, "bracket"), | ||
43 | (BUILTIN_TYPE, "builtinType"), | 46 | (BUILTIN_TYPE, "builtinType"), |
47 | (COMMA, "comma"), | ||
48 | (COLON, "colon"), | ||
49 | (DOT, "dot"), | ||
44 | (ESCAPE_SEQUENCE, "escapeSequence"), | 50 | (ESCAPE_SEQUENCE, "escapeSequence"), |
45 | (FORMAT_SPECIFIER, "formatSpecifier"), | 51 | (FORMAT_SPECIFIER, "formatSpecifier"), |
46 | (GENERIC, "generic"), | 52 | (GENERIC, "generic"), |
47 | (CONST_PARAMETER, "constParameter"), | 53 | (CONST_PARAMETER, "constParameter"), |
48 | (LIFETIME, "lifetime"), | 54 | (LIFETIME, "lifetime"), |
49 | (LABEL, "label"), | 55 | (LABEL, "label"), |
56 | (PARENTHESIS, "parenthesis"), | ||
50 | (PUNCTUATION, "punctuation"), | 57 | (PUNCTUATION, "punctuation"), |
51 | (SELF_KEYWORD, "selfKeyword"), | 58 | (SELF_KEYWORD, "selfKeyword"), |
59 | (SEMICOLON, "semicolon"), | ||
52 | (TYPE_ALIAS, "typeAlias"), | 60 | (TYPE_ALIAS, "typeAlias"), |
53 | (UNION, "union"), | 61 | (UNION, "union"), |
54 | (UNRESOLVED_REFERENCE, "unresolvedReference"), | 62 | (UNRESOLVED_REFERENCE, "unresolvedReference"), |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index bc9999ddc..a7ff8975a 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -6,10 +6,10 @@ use std::{ | |||
6 | 6 | ||
7 | use ide::{ | 7 | use ide::{ |
8 | Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, Documentation, FileId, | 8 | Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, Documentation, FileId, |
9 | FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HighlightModifier, HighlightTag, | 9 | FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlPunct, HlRange, HlTag, Indel, |
10 | HighlightedRange, Indel, InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, | 10 | InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, NavigationTarget, ReferenceAccess, |
11 | NavigationTarget, ReferenceAccess, Runnable, Severity, SourceChange, SourceFileEdit, | 11 | RenameError, Runnable, Severity, SourceChange, SourceFileEdit, SymbolKind, TextEdit, TextRange, |
12 | SymbolKind, TextEdit, TextRange, TextSize, | 12 | TextSize, |
13 | }; | 13 | }; |
14 | use itertools::Itertools; | 14 | use itertools::Itertools; |
15 | 15 | ||
@@ -337,12 +337,15 @@ static TOKEN_RESULT_COUNTER: AtomicU32 = AtomicU32::new(1); | |||
337 | pub(crate) fn semantic_tokens( | 337 | pub(crate) fn semantic_tokens( |
338 | text: &str, | 338 | text: &str, |
339 | line_index: &LineIndex, | 339 | line_index: &LineIndex, |
340 | highlights: Vec<HighlightedRange>, | 340 | highlights: Vec<HlRange>, |
341 | ) -> lsp_types::SemanticTokens { | 341 | ) -> lsp_types::SemanticTokens { |
342 | let id = TOKEN_RESULT_COUNTER.fetch_add(1, Ordering::SeqCst).to_string(); | 342 | let id = TOKEN_RESULT_COUNTER.fetch_add(1, Ordering::SeqCst).to_string(); |
343 | let mut builder = semantic_tokens::SemanticTokensBuilder::new(id); | 343 | let mut builder = semantic_tokens::SemanticTokensBuilder::new(id); |
344 | 344 | ||
345 | for highlight_range in highlights { | 345 | for highlight_range in highlights { |
346 | if highlight_range.highlight.is_empty() { | ||
347 | continue; | ||
348 | } | ||
346 | let (type_, mods) = semantic_token_type_and_modifiers(highlight_range.highlight); | 349 | let (type_, mods) = semantic_token_type_and_modifiers(highlight_range.highlight); |
347 | let token_index = semantic_tokens::type_index(type_); | 350 | let token_index = semantic_tokens::type_index(type_); |
348 | let modifier_bitset = mods.0; | 351 | let modifier_bitset = mods.0; |
@@ -374,7 +377,7 @@ fn semantic_token_type_and_modifiers( | |||
374 | ) -> (lsp_types::SemanticTokenType, semantic_tokens::ModifierSet) { | 377 | ) -> (lsp_types::SemanticTokenType, semantic_tokens::ModifierSet) { |
375 | let mut mods = semantic_tokens::ModifierSet::default(); | 378 | let mut mods = semantic_tokens::ModifierSet::default(); |
376 | let type_ = match highlight.tag { | 379 | let type_ = match highlight.tag { |
377 | HighlightTag::Symbol(symbol) => match symbol { | 380 | HlTag::Symbol(symbol) => match symbol { |
378 | SymbolKind::Module => lsp_types::SemanticTokenType::NAMESPACE, | 381 | SymbolKind::Module => lsp_types::SemanticTokenType::NAMESPACE, |
379 | SymbolKind::Impl => lsp_types::SemanticTokenType::TYPE, | 382 | SymbolKind::Impl => lsp_types::SemanticTokenType::TYPE, |
380 | SymbolKind::Field => lsp_types::SemanticTokenType::PROPERTY, | 383 | SymbolKind::Field => lsp_types::SemanticTokenType::PROPERTY, |
@@ -386,7 +389,7 @@ fn semantic_token_type_and_modifiers( | |||
386 | SymbolKind::SelfParam => semantic_tokens::SELF_KEYWORD, | 389 | SymbolKind::SelfParam => semantic_tokens::SELF_KEYWORD, |
387 | SymbolKind::Local => lsp_types::SemanticTokenType::VARIABLE, | 390 | SymbolKind::Local => lsp_types::SemanticTokenType::VARIABLE, |
388 | SymbolKind::Function => { | 391 | SymbolKind::Function => { |
389 | if highlight.modifiers.contains(HighlightModifier::Associated) { | 392 | if highlight.mods.contains(HlMod::Associated) { |
390 | lsp_types::SemanticTokenType::METHOD | 393 | lsp_types::SemanticTokenType::METHOD |
391 | } else { | 394 | } else { |
392 | lsp_types::SemanticTokenType::FUNCTION | 395 | lsp_types::SemanticTokenType::FUNCTION |
@@ -409,38 +412,44 @@ fn semantic_token_type_and_modifiers( | |||
409 | SymbolKind::Trait => lsp_types::SemanticTokenType::INTERFACE, | 412 | SymbolKind::Trait => lsp_types::SemanticTokenType::INTERFACE, |
410 | SymbolKind::Macro => lsp_types::SemanticTokenType::MACRO, | 413 | SymbolKind::Macro => lsp_types::SemanticTokenType::MACRO, |
411 | }, | 414 | }, |
412 | HighlightTag::BuiltinType => semantic_tokens::BUILTIN_TYPE, | 415 | HlTag::BuiltinType => semantic_tokens::BUILTIN_TYPE, |
413 | HighlightTag::Dummy => semantic_tokens::GENERIC, | 416 | HlTag::None => semantic_tokens::GENERIC, |
414 | HighlightTag::ByteLiteral | HighlightTag::NumericLiteral => { | 417 | HlTag::ByteLiteral | HlTag::NumericLiteral => lsp_types::SemanticTokenType::NUMBER, |
415 | lsp_types::SemanticTokenType::NUMBER | 418 | HlTag::BoolLiteral => semantic_tokens::BOOLEAN, |
416 | } | 419 | HlTag::CharLiteral | HlTag::StringLiteral => lsp_types::SemanticTokenType::STRING, |
417 | HighlightTag::BoolLiteral => semantic_tokens::BOOLEAN, | 420 | HlTag::Comment => lsp_types::SemanticTokenType::COMMENT, |
418 | HighlightTag::CharLiteral | HighlightTag::StringLiteral => { | 421 | HlTag::Attribute => semantic_tokens::ATTRIBUTE, |
419 | lsp_types::SemanticTokenType::STRING | 422 | HlTag::Keyword => lsp_types::SemanticTokenType::KEYWORD, |
420 | } | 423 | HlTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE, |
421 | HighlightTag::Comment => lsp_types::SemanticTokenType::COMMENT, | 424 | HlTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER, |
422 | HighlightTag::Attribute => semantic_tokens::ATTRIBUTE, | 425 | HlTag::Operator => lsp_types::SemanticTokenType::OPERATOR, |
423 | HighlightTag::Keyword => lsp_types::SemanticTokenType::KEYWORD, | 426 | HlTag::EscapeSequence => semantic_tokens::ESCAPE_SEQUENCE, |
424 | HighlightTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE, | 427 | HlTag::Punctuation(punct) => match punct { |
425 | HighlightTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER, | 428 | HlPunct::Bracket => semantic_tokens::BRACKET, |
426 | HighlightTag::Operator => lsp_types::SemanticTokenType::OPERATOR, | 429 | HlPunct::Brace => semantic_tokens::BRACE, |
427 | HighlightTag::EscapeSequence => semantic_tokens::ESCAPE_SEQUENCE, | 430 | HlPunct::Parenthesis => semantic_tokens::PARENTHESIS, |
428 | HighlightTag::Punctuation => semantic_tokens::PUNCTUATION, | 431 | HlPunct::Angle => semantic_tokens::ANGLE, |
432 | HlPunct::Comma => semantic_tokens::COMMA, | ||
433 | HlPunct::Dot => semantic_tokens::DOT, | ||
434 | HlPunct::Colon => semantic_tokens::COLON, | ||
435 | HlPunct::Semi => semantic_tokens::SEMICOLON, | ||
436 | HlPunct::Other => semantic_tokens::PUNCTUATION, | ||
437 | }, | ||
429 | }; | 438 | }; |
430 | 439 | ||
431 | for modifier in highlight.modifiers.iter() { | 440 | for modifier in highlight.mods.iter() { |
432 | let modifier = match modifier { | 441 | let modifier = match modifier { |
433 | HighlightModifier::Attribute => semantic_tokens::ATTRIBUTE_MODIFIER, | 442 | HlMod::Attribute => semantic_tokens::ATTRIBUTE_MODIFIER, |
434 | HighlightModifier::Definition => lsp_types::SemanticTokenModifier::DECLARATION, | 443 | HlMod::Definition => lsp_types::SemanticTokenModifier::DECLARATION, |
435 | HighlightModifier::Documentation => lsp_types::SemanticTokenModifier::DOCUMENTATION, | 444 | HlMod::Documentation => lsp_types::SemanticTokenModifier::DOCUMENTATION, |
436 | HighlightModifier::Injected => semantic_tokens::INJECTED, | 445 | HlMod::Injected => semantic_tokens::INJECTED, |
437 | HighlightModifier::ControlFlow => semantic_tokens::CONTROL_FLOW, | 446 | HlMod::ControlFlow => semantic_tokens::CONTROL_FLOW, |
438 | HighlightModifier::Mutable => semantic_tokens::MUTABLE, | 447 | HlMod::Mutable => semantic_tokens::MUTABLE, |
439 | HighlightModifier::Consuming => semantic_tokens::CONSUMING, | 448 | HlMod::Consuming => semantic_tokens::CONSUMING, |
440 | HighlightModifier::Unsafe => semantic_tokens::UNSAFE, | 449 | HlMod::Unsafe => semantic_tokens::UNSAFE, |
441 | HighlightModifier::Callable => semantic_tokens::CALLABLE, | 450 | HlMod::Callable => semantic_tokens::CALLABLE, |
442 | HighlightModifier::Static => lsp_types::SemanticTokenModifier::STATIC, | 451 | HlMod::Static => lsp_types::SemanticTokenModifier::STATIC, |
443 | HighlightModifier::Associated => continue, | 452 | HlMod::Associated => continue, |
444 | }; | 453 | }; |
445 | mods |= modifier; | 454 | mods |= modifier; |
446 | } | 455 | } |
@@ -847,6 +856,10 @@ pub(crate) fn markup_content(markup: Markup) -> lsp_types::MarkupContent { | |||
847 | lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value } | 856 | lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value } |
848 | } | 857 | } |
849 | 858 | ||
859 | pub(crate) fn rename_error(err: RenameError) -> crate::LspError { | ||
860 | crate::LspError { code: lsp_server::ErrorCode::InvalidParams as i32, message: err.to_string() } | ||
861 | } | ||
862 | |||
850 | #[cfg(test)] | 863 | #[cfg(test)] |
851 | mod tests { | 864 | mod tests { |
852 | use ide::Analysis; | 865 | use ide::Analysis; |
diff --git a/crates/rust-analyzer/tests/rust-analyzer/main.rs b/crates/rust-analyzer/tests/rust-analyzer/main.rs index f5c5c63e5..80bde29b9 100644 --- a/crates/rust-analyzer/tests/rust-analyzer/main.rs +++ b/crates/rust-analyzer/tests/rust-analyzer/main.rs | |||
@@ -16,11 +16,14 @@ use std::{collections::HashMap, path::PathBuf, time::Instant}; | |||
16 | use expect_test::expect; | 16 | use expect_test::expect; |
17 | use lsp_types::{ | 17 | use lsp_types::{ |
18 | notification::DidOpenTextDocument, | 18 | notification::DidOpenTextDocument, |
19 | request::{CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest}, | 19 | request::{ |
20 | CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest, | ||
21 | WillRenameFiles, | ||
22 | }, | ||
20 | CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams, | 23 | CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams, |
21 | DocumentFormattingParams, FormattingOptions, GotoDefinitionParams, HoverParams, | 24 | DocumentFormattingParams, FileRename, FormattingOptions, GotoDefinitionParams, HoverParams, |
22 | PartialResultParams, Position, Range, TextDocumentItem, TextDocumentPositionParams, | 25 | PartialResultParams, Position, Range, RenameFilesParams, TextDocumentItem, |
23 | WorkDoneProgressParams, | 26 | TextDocumentPositionParams, WorkDoneProgressParams, |
24 | }; | 27 | }; |
25 | use rust_analyzer::lsp_ext::{OnEnter, Runnables, RunnablesParams}; | 28 | use rust_analyzer::lsp_ext::{OnEnter, Runnables, RunnablesParams}; |
26 | use serde_json::json; | 29 | use serde_json::json; |
@@ -745,3 +748,136 @@ pub fn foo(_input: TokenStream) -> TokenStream { | |||
745 | ```"#]] | 748 | ```"#]] |
746 | .assert_eq(&value); | 749 | .assert_eq(&value); |
747 | } | 750 | } |
751 | |||
752 | #[test] | ||
753 | fn test_will_rename_files_same_level() { | ||
754 | if skip_slow_tests() { | ||
755 | return; | ||
756 | } | ||
757 | |||
758 | let tmp_dir = TestDir::new(); | ||
759 | let tmp_dir_path = tmp_dir.path().to_owned(); | ||
760 | let tmp_dir_str = tmp_dir_path.to_str().unwrap(); | ||
761 | let base_path = PathBuf::from(format!("file://{}", tmp_dir_str)); | ||
762 | |||
763 | let code = r#" | ||
764 | //- /Cargo.toml | ||
765 | [package] | ||
766 | name = "foo" | ||
767 | version = "0.0.0" | ||
768 | |||
769 | //- /src/lib.rs | ||
770 | mod old_file; | ||
771 | mod from_mod; | ||
772 | mod to_mod; | ||
773 | mod old_folder; | ||
774 | fn main() {} | ||
775 | |||
776 | //- /src/old_file.rs | ||
777 | |||
778 | //- /src/old_folder/mod.rs | ||
779 | |||
780 | //- /src/from_mod/mod.rs | ||
781 | |||
782 | //- /src/to_mod/foo.rs | ||
783 | |||
784 | "#; | ||
785 | let server = | ||
786 | Project::with_fixture(&code).tmp_dir(tmp_dir).server().wait_until_workspace_is_loaded(); | ||
787 | |||
788 | //rename same level file | ||
789 | server.request::<WillRenameFiles>( | ||
790 | RenameFilesParams { | ||
791 | files: vec![FileRename { | ||
792 | old_uri: base_path.join("src/old_file.rs").to_str().unwrap().to_string(), | ||
793 | new_uri: base_path.join("src/new_file.rs").to_str().unwrap().to_string(), | ||
794 | }], | ||
795 | }, | ||
796 | json!({ | ||
797 | "documentChanges": [ | ||
798 | { | ||
799 | "textDocument": { | ||
800 | "uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").to_str().unwrap().to_string().replace("C:\\", "/c:/").replace("\\", "/")), | ||
801 | "version": null | ||
802 | }, | ||
803 | "edits": [ | ||
804 | { | ||
805 | "range": { | ||
806 | "start": { | ||
807 | "line": 0, | ||
808 | "character": 4 | ||
809 | }, | ||
810 | "end": { | ||
811 | "line": 0, | ||
812 | "character": 12 | ||
813 | } | ||
814 | }, | ||
815 | "newText": "new_file" | ||
816 | } | ||
817 | ] | ||
818 | } | ||
819 | ] | ||
820 | }), | ||
821 | ); | ||
822 | |||
823 | //rename file from mod.rs to foo.rs | ||
824 | server.request::<WillRenameFiles>( | ||
825 | RenameFilesParams { | ||
826 | files: vec![FileRename { | ||
827 | old_uri: base_path.join("src/from_mod/mod.rs").to_str().unwrap().to_string(), | ||
828 | new_uri: base_path.join("src/from_mod/foo.rs").to_str().unwrap().to_string(), | ||
829 | }], | ||
830 | }, | ||
831 | json!({ | ||
832 | "documentChanges": [] | ||
833 | }), | ||
834 | ); | ||
835 | |||
836 | //rename file from foo.rs to mod.rs | ||
837 | server.request::<WillRenameFiles>( | ||
838 | RenameFilesParams { | ||
839 | files: vec![FileRename { | ||
840 | old_uri: base_path.join("src/to_mod/foo.rs").to_str().unwrap().to_string(), | ||
841 | new_uri: base_path.join("src/to_mod/mod.rs").to_str().unwrap().to_string(), | ||
842 | }], | ||
843 | }, | ||
844 | json!({ | ||
845 | "documentChanges": [] | ||
846 | }), | ||
847 | ); | ||
848 | |||
849 | //rename same level file | ||
850 | server.request::<WillRenameFiles>( | ||
851 | RenameFilesParams { | ||
852 | files: vec![FileRename { | ||
853 | old_uri: base_path.join("src/old_folder").to_str().unwrap().to_string(), | ||
854 | new_uri: base_path.join("src/new_folder").to_str().unwrap().to_string(), | ||
855 | }], | ||
856 | }, | ||
857 | json!({ | ||
858 | "documentChanges": [ | ||
859 | { | ||
860 | "textDocument": { | ||
861 | "uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").to_str().unwrap().to_string().replace("C:\\", "/c:/").replace("\\", "/")), | ||
862 | "version": null | ||
863 | }, | ||
864 | "edits": [ | ||
865 | { | ||
866 | "range": { | ||
867 | "start": { | ||
868 | "line": 3, | ||
869 | "character": 4 | ||
870 | }, | ||
871 | "end": { | ||
872 | "line": 3, | ||
873 | "character": 14 | ||
874 | } | ||
875 | }, | ||
876 | "newText": "new_folder" | ||
877 | } | ||
878 | ] | ||
879 | } | ||
880 | ] | ||
881 | }), | ||
882 | ); | ||
883 | } | ||
diff --git a/crates/ssr/src/search.rs b/crates/ssr/src/search.rs index 44b5db029..836eb94b2 100644 --- a/crates/ssr/src/search.rs +++ b/crates/ssr/src/search.rs | |||
@@ -5,10 +5,10 @@ use crate::{ | |||
5 | resolving::{ResolvedPath, ResolvedPattern, ResolvedRule}, | 5 | resolving::{ResolvedPath, ResolvedPattern, ResolvedRule}, |
6 | Match, MatchFinder, | 6 | Match, MatchFinder, |
7 | }; | 7 | }; |
8 | use ide_db::base_db::{FileId, FileRange}; | ||
9 | use ide_db::{ | 8 | use ide_db::{ |
9 | base_db::{FileId, FileRange}, | ||
10 | defs::Definition, | 10 | defs::Definition, |
11 | search::{Reference, SearchScope}, | 11 | search::{SearchScope, UsageSearchResult}, |
12 | }; | 12 | }; |
13 | use rustc_hash::FxHashSet; | 13 | use rustc_hash::FxHashSet; |
14 | use syntax::{ast, AstNode, SyntaxKind, SyntaxNode}; | 14 | use syntax::{ast, AstNode, SyntaxKind, SyntaxNode}; |
@@ -20,7 +20,7 @@ use test_utils::mark; | |||
20 | /// them more than once. | 20 | /// them more than once. |
21 | #[derive(Default)] | 21 | #[derive(Default)] |
22 | pub(crate) struct UsageCache { | 22 | pub(crate) struct UsageCache { |
23 | usages: Vec<(Definition, Vec<Reference>)>, | 23 | usages: Vec<(Definition, UsageSearchResult)>, |
24 | } | 24 | } |
25 | 25 | ||
26 | impl<'db> MatchFinder<'db> { | 26 | impl<'db> MatchFinder<'db> { |
@@ -58,8 +58,8 @@ impl<'db> MatchFinder<'db> { | |||
58 | ) { | 58 | ) { |
59 | if let Some(resolved_path) = pick_path_for_usages(pattern) { | 59 | if let Some(resolved_path) = pick_path_for_usages(pattern) { |
60 | let definition: Definition = resolved_path.resolution.clone().into(); | 60 | let definition: Definition = resolved_path.resolution.clone().into(); |
61 | for reference in self.find_usages(usage_cache, definition) { | 61 | for file_range in self.find_usages(usage_cache, definition).file_ranges() { |
62 | if let Some(node_to_match) = self.find_node_to_match(resolved_path, reference) { | 62 | if let Some(node_to_match) = self.find_node_to_match(resolved_path, file_range) { |
63 | if !is_search_permitted_ancestors(&node_to_match) { | 63 | if !is_search_permitted_ancestors(&node_to_match) { |
64 | mark::hit!(use_declaration_with_braces); | 64 | mark::hit!(use_declaration_with_braces); |
65 | continue; | 65 | continue; |
@@ -73,11 +73,11 @@ impl<'db> MatchFinder<'db> { | |||
73 | fn find_node_to_match( | 73 | fn find_node_to_match( |
74 | &self, | 74 | &self, |
75 | resolved_path: &ResolvedPath, | 75 | resolved_path: &ResolvedPath, |
76 | reference: &Reference, | 76 | file_range: FileRange, |
77 | ) -> Option<SyntaxNode> { | 77 | ) -> Option<SyntaxNode> { |
78 | let file = self.sema.parse(reference.file_range.file_id); | 78 | let file = self.sema.parse(file_range.file_id); |
79 | let depth = resolved_path.depth as usize; | 79 | let depth = resolved_path.depth as usize; |
80 | let offset = reference.file_range.range.start(); | 80 | let offset = file_range.range.start(); |
81 | if let Some(path) = | 81 | if let Some(path) = |
82 | self.sema.find_node_at_offset_with_descend::<ast::Path>(file.syntax(), offset) | 82 | self.sema.find_node_at_offset_with_descend::<ast::Path>(file.syntax(), offset) |
83 | { | 83 | { |
@@ -108,7 +108,7 @@ impl<'db> MatchFinder<'db> { | |||
108 | &self, | 108 | &self, |
109 | usage_cache: &'a mut UsageCache, | 109 | usage_cache: &'a mut UsageCache, |
110 | definition: Definition, | 110 | definition: Definition, |
111 | ) -> &'a [Reference] { | 111 | ) -> &'a UsageSearchResult { |
112 | // Logically if a lookup succeeds we should just return it. Unfortunately returning it would | 112 | // Logically if a lookup succeeds we should just return it. Unfortunately returning it would |
113 | // extend the lifetime of the borrow, then we wouldn't be able to do the insertion on a | 113 | // extend the lifetime of the borrow, then we wouldn't be able to do the insertion on a |
114 | // cache miss. This is a limitation of NLL and is fixed with Polonius. For now we do two | 114 | // cache miss. This is a limitation of NLL and is fixed with Polonius. For now we do two |
@@ -250,7 +250,7 @@ fn is_search_permitted(node: &SyntaxNode) -> bool { | |||
250 | } | 250 | } |
251 | 251 | ||
252 | impl UsageCache { | 252 | impl UsageCache { |
253 | fn find(&mut self, definition: &Definition) -> Option<&[Reference]> { | 253 | fn find(&mut self, definition: &Definition) -> Option<&UsageSearchResult> { |
254 | // We expect a very small number of cache entries (generally 1), so a linear scan should be | 254 | // We expect a very small number of cache entries (generally 1), so a linear scan should be |
255 | // fast enough and avoids the need to implement Hash for Definition. | 255 | // fast enough and avoids the need to implement Hash for Definition. |
256 | for (d, refs) in &self.usages { | 256 | for (d, refs) in &self.usages { |
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs index 5332edb09..13aab1451 100644 --- a/crates/stdx/src/lib.rs +++ b/crates/stdx/src/lib.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | //! Missing batteries for standard libraries. | 1 | //! Missing batteries for standard libraries. |
2 | use std::{ops, process, time::Instant}; | 2 | use std::{cmp::Ordering, ops, process, time::Instant}; |
3 | 3 | ||
4 | mod macros; | 4 | mod macros; |
5 | pub mod panic_context; | 5 | pub mod panic_context; |
@@ -117,7 +117,12 @@ impl<'a> Iterator for LinesWithEnds<'a> { | |||
117 | } | 117 | } |
118 | } | 118 | } |
119 | 119 | ||
120 | // https://github.com/rust-lang/rust/issues/73831 | 120 | /// Returns `idx` such that: |
121 | /// | ||
122 | /// ∀ x in slice[..idx]: pred(x) | ||
123 | /// && ∀ x in slice[idx..]: !pred(x) | ||
124 | /// | ||
125 | /// https://github.com/rust-lang/rust/issues/73831 | ||
121 | pub fn partition_point<T, P>(slice: &[T], mut pred: P) -> usize | 126 | pub fn partition_point<T, P>(slice: &[T], mut pred: P) -> usize |
122 | where | 127 | where |
123 | P: FnMut(&T) -> bool, | 128 | P: FnMut(&T) -> bool, |
@@ -147,6 +152,15 @@ where | |||
147 | left | 152 | left |
148 | } | 153 | } |
149 | 154 | ||
155 | pub fn equal_range_by<T, F>(slice: &[T], mut key: F) -> ops::Range<usize> | ||
156 | where | ||
157 | F: FnMut(&T) -> Ordering, | ||
158 | { | ||
159 | let start = partition_point(slice, |it| key(it) == Ordering::Less); | ||
160 | let len = partition_point(&slice[start..], |it| key(it) == Ordering::Equal); | ||
161 | start..start + len | ||
162 | } | ||
163 | |||
150 | pub struct JodChild(pub process::Child); | 164 | pub struct JodChild(pub process::Child); |
151 | 165 | ||
152 | impl ops::Deref for JodChild { | 166 | impl ops::Deref for JodChild { |
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs index 22ab36cd2..384d031e7 100644 --- a/crates/syntax/src/algo.rs +++ b/crates/syntax/src/algo.rs | |||
@@ -88,8 +88,8 @@ pub fn least_common_ancestor(u: &SyntaxNode, v: &SyntaxNode) -> Option<SyntaxNod | |||
88 | let keep = u_depth.min(v_depth); | 88 | let keep = u_depth.min(v_depth); |
89 | 89 | ||
90 | let u_candidates = u.ancestors().skip(u_depth - keep); | 90 | let u_candidates = u.ancestors().skip(u_depth - keep); |
91 | let v_canidates = v.ancestors().skip(v_depth - keep); | 91 | let v_candidates = v.ancestors().skip(v_depth - keep); |
92 | let (res, _) = u_candidates.zip(v_canidates).find(|(x, y)| x == y)?; | 92 | let (res, _) = u_candidates.zip(v_candidates).find(|(x, y)| x == y)?; |
93 | Some(res) | 93 | Some(res) |
94 | } | 94 | } |
95 | 95 | ||
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index cafa4c198..1ed8a96e5 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -241,7 +241,7 @@ pub fn wildcard_pat() -> ast::WildcardPat { | |||
241 | } | 241 | } |
242 | } | 242 | } |
243 | 243 | ||
244 | /// Creates a tuple of patterns from an interator of patterns. | 244 | /// Creates a tuple of patterns from an iterator of patterns. |
245 | /// | 245 | /// |
246 | /// Invariant: `pats` must be length > 1 | 246 | /// Invariant: `pats` must be length > 1 |
247 | /// | 247 | /// |
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index 2aa472fb4..27381ba80 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs | |||
@@ -133,7 +133,7 @@ impl ast::Attr { | |||
133 | first_token.and_then(|token| token.next_token()).as_ref().map(SyntaxToken::kind); | 133 | first_token.and_then(|token| token.next_token()).as_ref().map(SyntaxToken::kind); |
134 | 134 | ||
135 | match (first_token_kind, second_token_kind) { | 135 | match (first_token_kind, second_token_kind) { |
136 | (Some(SyntaxKind::POUND), Some(T![!])) => AttrKind::Inner, | 136 | (Some(T![#]), Some(T![!])) => AttrKind::Inner, |
137 | _ => AttrKind::Outer, | 137 | _ => AttrKind::Outer, |
138 | } | 138 | } |
139 | } | 139 | } |
diff --git a/crates/syntax/src/parsing/lexer.rs b/crates/syntax/src/parsing/lexer.rs index 0cbba73c5..7c8d0a4c4 100644 --- a/crates/syntax/src/parsing/lexer.rs +++ b/crates/syntax/src/parsing/lexer.rs | |||
@@ -24,7 +24,7 @@ pub struct Token { | |||
24 | /// Beware that it checks for shebang first and its length contributes to resulting | 24 | /// Beware that it checks for shebang first and its length contributes to resulting |
25 | /// tokens offsets. | 25 | /// tokens offsets. |
26 | pub fn tokenize(text: &str) -> (Vec<Token>, Vec<SyntaxError>) { | 26 | pub fn tokenize(text: &str) -> (Vec<Token>, Vec<SyntaxError>) { |
27 | // non-empty string is a precondtion of `rustc_lexer::strip_shebang()`. | 27 | // non-empty string is a precondition of `rustc_lexer::strip_shebang()`. |
28 | if text.is_empty() { | 28 | if text.is_empty() { |
29 | return Default::default(); | 29 | return Default::default(); |
30 | } | 30 | } |
@@ -76,7 +76,7 @@ pub fn lex_single_syntax_kind(text: &str) -> Option<(SyntaxKind, Option<SyntaxEr | |||
76 | } | 76 | } |
77 | 77 | ||
78 | /// The same as `lex_single_syntax_kind()` but returns only `SyntaxKind` and | 78 | /// The same as `lex_single_syntax_kind()` but returns only `SyntaxKind` and |
79 | /// returns `None` if any tokenization error occured. | 79 | /// returns `None` if any tokenization error occurred. |
80 | /// | 80 | /// |
81 | /// Beware that unescape errors are not checked at tokenization time. | 81 | /// Beware that unescape errors are not checked at tokenization time. |
82 | pub fn lex_single_valid_syntax_kind(text: &str) -> Option<SyntaxKind> { | 82 | pub fn lex_single_valid_syntax_kind(text: &str) -> Option<SyntaxKind> { |
@@ -96,7 +96,7 @@ pub fn lex_single_valid_syntax_kind(text: &str) -> Option<SyntaxKind> { | |||
96 | /// | 96 | /// |
97 | /// Beware that unescape errors are not checked at tokenization time. | 97 | /// Beware that unescape errors are not checked at tokenization time. |
98 | fn lex_first_token(text: &str) -> Option<(Token, Option<SyntaxError>)> { | 98 | fn lex_first_token(text: &str) -> Option<(Token, Option<SyntaxError>)> { |
99 | // non-empty string is a precondtion of `rustc_lexer::first_token()`. | 99 | // non-empty string is a precondition of `rustc_lexer::first_token()`. |
100 | if text.is_empty() { | 100 | if text.is_empty() { |
101 | return None; | 101 | return None; |
102 | } | 102 | } |
@@ -117,7 +117,7 @@ fn rustc_token_kind_to_syntax_kind( | |||
117 | token_text: &str, | 117 | token_text: &str, |
118 | ) -> (SyntaxKind, Option<&'static str>) { | 118 | ) -> (SyntaxKind, Option<&'static str>) { |
119 | // A note on an intended tradeoff: | 119 | // A note on an intended tradeoff: |
120 | // We drop some useful infromation here (see patterns with double dots `..`) | 120 | // We drop some useful information here (see patterns with double dots `..`) |
121 | // Storing that info in `SyntaxKind` is not possible due to its layout requirements of | 121 | // Storing that info in `SyntaxKind` is not possible due to its layout requirements of |
122 | // being `u16` that come from `rowan::SyntaxKind`. | 122 | // being `u16` that come from `rowan::SyntaxKind`. |
123 | 123 | ||
diff --git a/crates/syntax/src/validation.rs b/crates/syntax/src/validation.rs index 2ddaeb176..bfa2dc4ba 100644 --- a/crates/syntax/src/validation.rs +++ b/crates/syntax/src/validation.rs | |||
@@ -173,7 +173,7 @@ pub(crate) fn validate_block_structure(root: &SyntaxNode) { | |||
173 | assert_eq!( | 173 | assert_eq!( |
174 | node.parent(), | 174 | node.parent(), |
175 | pair.parent(), | 175 | pair.parent(), |
176 | "\nunpaired curleys:\n{}\n{:#?}\n", | 176 | "\nunpaired curlys:\n{}\n{:#?}\n", |
177 | root.text(), | 177 | root.text(), |
178 | root, | 178 | root, |
179 | ); | 179 | ); |
@@ -344,9 +344,9 @@ fn validate_trait_object_ty(ty: ast::DynTraitType) -> Option<SyntaxError> { | |||
344 | 344 | ||
345 | if tbl.bounds().count() > 1 { | 345 | if tbl.bounds().count() > 1 { |
346 | let dyn_token = ty.dyn_token()?; | 346 | let dyn_token = ty.dyn_token()?; |
347 | let potential_parentheses = | 347 | let potential_parenthesis = |
348 | algo::skip_trivia_token(dyn_token.prev_token()?, Direction::Prev)?; | 348 | algo::skip_trivia_token(dyn_token.prev_token()?, Direction::Prev)?; |
349 | let kind = potential_parentheses.kind(); | 349 | let kind = potential_parenthesis.kind(); |
350 | if !matches!(kind, T!['('] | T![<] | T![=]) { | 350 | if !matches!(kind, T!['('] | T![<] | T![=]) { |
351 | return Some(SyntaxError::new("ambiguous `+` in a type", ty.syntax().text_range())); | 351 | return Some(SyntaxError::new("ambiguous `+` in a type", ty.syntax().text_range())); |
352 | } | 352 | } |
diff --git a/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rast b/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rast index b1fb75ed1..f40500e38 100644 --- a/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rast +++ b/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rast | |||
@@ -1,4 +1,4 @@ | |||
1 | [email protected]9 | 1 | [email protected]8 |
2 | [email protected] | 2 | [email protected] |
3 | [email protected] "use" | 3 | [email protected] "use" |
4 | [email protected] " " | 4 | [email protected] " " |
@@ -75,62 +75,62 @@ [email protected] | |||
75 | [email protected] "}" | 75 | [email protected] "}" |
76 | [email protected] ";" | 76 | [email protected] ";" |
77 | [email protected] " " | 77 | [email protected] " " |
78 | [email protected]6 | 78 | [email protected]5 |
79 | [email protected] "// Rust 2015" | 79 | [email protected] "// Rust 2015" |
80 | [email protected] "\n" | 80 | [email protected] "\n" |
81 | [email protected] "use" | 81 | [email protected] "use" |
82 | [email protected] " " | 82 | [email protected] " " |
83 | [email protected]5 | 83 | [email protected]4 |
84 | [email protected] "::" | 84 | [email protected] "::" |
85 | [email protected]5 | 85 | [email protected]4 |
86 | [email protected] "{" | 86 | [email protected] "{" |
87 | [email protected]4 | 87 | [email protected]3 |
88 | [email protected]4 | 88 | [email protected]3 |
89 | [email protected]8 | 89 | [email protected]7 |
90 | [email protected] | 90 | [email protected] |
91 | [email protected] | 91 | [email protected] |
92 | [email protected] | 92 | [email protected] |
93 | [email protected] "some" | 93 | [email protected] "some" |
94 | [email protected] "::" | 94 | [email protected] "::" |
95 | [email protected]8 | 95 | [email protected]7 |
96 | [email protected]8 | 96 | [email protected]7 |
97 | [email protected]8 "arbritrary" | 97 | [email protected]7 "arbitrary" |
98 | COLON2@158..160 "::" | 98 | COLON2@157..159 "::" |
99 | PATH_SEGMENT@160..164 | 99 | PATH_SEGMENT@159..163 |
100 | NAME_REF@160..164 | 100 | NAME_REF@159..163 |
101 | IDENT@160..164 "path" | 101 | IDENT@159..163 "path" |
102 | R_CURLY@164..165 "}" | 102 | R_CURLY@163..164 "}" |
103 | SEMICOLON@165..166 ";" | 103 | SEMICOLON@164..165 ";" |
104 | WHITESPACE@166..167 " " | 104 | WHITESPACE@165..166 " " |
105 | USE@167..205 | 105 | USE@166..204 |
106 | COMMENT@167..179 "// Rust 2015" | 106 | COMMENT@166..178 "// Rust 2015" |
107 | WHITESPACE@179..180 "\n" | 107 | WHITESPACE@178..179 "\n" |
108 | USE_KW@180..183 "use" | 108 | USE_KW@179..182 "use" |
109 | WHITESPACE@183..184 " " | 109 | WHITESPACE@182..183 " " |
110 | USE_TREE@184..204 | 110 | USE_TREE@183..203 |
111 | COLON2@184..186 "::" | 111 | COLON2@183..185 "::" |
112 | USE_TREE_LIST@186..204 | 112 | USE_TREE_LIST@185..203 |
113 | L_CURLY@186..187 "{" | 113 | L_CURLY@185..186 "{" |
114 | USE_TREE@187..203 | 114 | USE_TREE@186..202 |
115 | USE_TREE_LIST@187..203 | 115 | USE_TREE_LIST@186..202 |
116 | L_CURLY@187..188 "{" | 116 | L_CURLY@186..187 "{" |
117 | USE_TREE@188..202 | 117 | USE_TREE@187..201 |
118 | USE_TREE_LIST@188..202 | 118 | USE_TREE_LIST@187..201 |
119 | L_CURLY@188..189 "{" | 119 | L_CURLY@187..188 "{" |
120 | USE_TREE@189..201 | 120 | USE_TREE@188..200 |
121 | PATH@189..201 | 121 | PATH@188..200 |
122 | PATH@189..193 | 122 | PATH@188..192 |
123 | PATH_SEGMENT@189..193 | 123 | PATH_SEGMENT@188..192 |
124 | NAME_REF@189..193 | 124 | NAME_REF@188..192 |
125 | IDENT@189..193 "root" | 125 | IDENT@188..192 "root" |
126 | COLON2@193..195 "::" | 126 | COLON2@192..194 "::" |
127 | PATH_SEGMENT@195..201 | 127 | PATH_SEGMENT@194..200 |
128 | NAME_REF@195..201 | 128 | NAME_REF@194..200 |
129 | IDENT@195..201 "export" | 129 | IDENT@194..200 "export" |
130 | R_CURLY@201..202 "}" | 130 | R_CURLY@200..201 "}" |
131 | R_CURLY@202..203 "}" | 131 | R_CURLY@201..202 "}" |
132 | R_CURLY@203..204 "}" | 132 | R_CURLY@202..203 "}" |
133 | SEMICOLON@204..205 ";" | 133 | SEMICOLON@203..204 ";" |
134 | WHITESPACE@205..206 " " | 134 | WHITESPACE@204..205 " " |
135 | COMMENT@206..248 "// Nonsensical but pe ..." | 135 | COMMENT@205..247 "// Nonsensical but pe ..." |
136 | WHITESPACE@248..249 "\n" | 136 | WHITESPACE@247..248 "\n" |
diff --git a/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rs b/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rs index 381cba1e2..02af4b446 100644 --- a/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rs +++ b/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`) | 1 | use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`) |
2 | use {path::from::root}; // Rust 2015 | 2 | use {path::from::root}; // Rust 2015 |
3 | use ::{some::arbritrary::path}; // Rust 2015 | 3 | use ::{some::arbitrary::path}; // Rust 2015 |
4 | use ::{{{root::export}}}; // Nonsensical but perfectly legal nesting | 4 | use ::{{{root::export}}}; // Nonsensical but perfectly legal nesting |
diff --git a/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rast b/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rast index 51e881a8e..68c0f1c66 100644 --- a/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rast +++ b/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rast | |||
@@ -1,5 +1,5 @@ | |||
1 | SOURCE_FILE@0..46 | 1 | SOURCE_FILE@0..59 |
2 | TYPE_ALIAS@0..45 | 2 | [email protected]8 |
3 | [email protected] "type" | 3 | [email protected] "type" |
4 | [email protected] " " | 4 | [email protected] " " |
5 | [email protected] | 5 | [email protected] |
@@ -7,12 +7,12 @@ [email protected] | |||
7 | [email protected] " " | 7 | [email protected] " " |
8 | [email protected] "=" | 8 | [email protected] "=" |
9 | [email protected] " " | 9 | [email protected] " " |
10 | PATH_TYPE@9..44 | 10 | PATH_TYPE@9..57 |
11 | PATH@9..44 | 11 | PATH@9..57 |
12 | PATH_SEGMENT@9..44 | 12 | PATH_SEGMENT@9..57 |
13 | [email protected] | 13 | [email protected] |
14 | [email protected] "B" | 14 | [email protected] "B" |
15 | GENERIC_ARG_LIST@10..44 | 15 | GENERIC_ARG_LIST@10..57 |
16 | [email protected] "<" | 16 | [email protected] "<" |
17 | [email protected] | 17 | [email protected] |
18 | [email protected] | 18 | [email protected] |
@@ -51,6 +51,16 @@ [email protected] | |||
51 | [email protected] | 51 | [email protected] |
52 | [email protected] | 52 | [email protected] |
53 | [email protected] "u64" | 53 | [email protected] "u64" |
54 | [email protected] ">" | 54 | [email protected] "," |
55 | [email protected] ";" | 55 | [email protected] " " |
56 | [email protected] "\n" | 56 | [email protected] |
57 | [email protected] | ||
58 | [email protected] "true" | ||
59 | [email protected] "," | ||
60 | [email protected] " " | ||
61 | [email protected] | ||
62 | [email protected] | ||
63 | [email protected] "false" | ||
64 | [email protected] ">" | ||
65 | [email protected] ";" | ||
66 | [email protected] "\n" | ||
diff --git a/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rs b/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rs index 0d07d7651..6a8721a73 100644 --- a/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rs +++ b/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rs | |||
@@ -1 +1 @@ | |||
type A = B<'static, i32, 1, { 2 }, Item=u64>; | type A = B<'static, i32, 1, { 2 }, Item=u64, true, false>; | ||
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index 84c1d7ebb..e19d2ad61 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs | |||
@@ -63,7 +63,7 @@ pub fn extract_offset(text: &str) -> (TextSize, String) { | |||
63 | } | 63 | } |
64 | } | 64 | } |
65 | 65 | ||
66 | /// Returns the offset of the first occurence of `$0` marker and the copy of `text` | 66 | /// Returns the offset of the first occurrence of `$0` marker and the copy of `text` |
67 | /// without the marker. | 67 | /// without the marker. |
68 | fn try_extract_offset(text: &str) -> Option<(TextSize, String)> { | 68 | fn try_extract_offset(text: &str) -> Option<(TextSize, String)> { |
69 | let cursor_pos = text.find(CURSOR_MARKER)?; | 69 | let cursor_pos = text.find(CURSOR_MARKER)?; |
diff --git a/crates/vfs/src/anchored_path.rs b/crates/vfs/src/anchored_path.rs index 02720a32e..db15a2a21 100644 --- a/crates/vfs/src/anchored_path.rs +++ b/crates/vfs/src/anchored_path.rs | |||
@@ -26,14 +26,24 @@ | |||
26 | //! from the anchor than. | 26 | //! from the anchor than. |
27 | use crate::FileId; | 27 | use crate::FileId; |
28 | 28 | ||
29 | /// Path relative to a file. | ||
30 | /// | ||
31 | /// Owned version of [`AnchoredPath`]. | ||
29 | #[derive(Clone, PartialEq, Eq, Debug)] | 32 | #[derive(Clone, PartialEq, Eq, Debug)] |
30 | pub struct AnchoredPathBuf { | 33 | pub struct AnchoredPathBuf { |
34 | /// File that this path is relative to. | ||
31 | pub anchor: FileId, | 35 | pub anchor: FileId, |
36 | /// Path relative to `anchor`'s containing directory. | ||
32 | pub path: String, | 37 | pub path: String, |
33 | } | 38 | } |
34 | 39 | ||
40 | /// Path relative to a file. | ||
41 | /// | ||
42 | /// Borrowed version of [`AnchoredPathBuf`]. | ||
35 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] | 43 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] |
36 | pub struct AnchoredPath<'a> { | 44 | pub struct AnchoredPath<'a> { |
45 | /// File that this path is relative to. | ||
37 | pub anchor: FileId, | 46 | pub anchor: FileId, |
47 | /// Path relative to `anchor`'s containing directory. | ||
38 | pub path: &'a str, | 48 | pub path: &'a str, |
39 | } | 49 | } |
diff --git a/crates/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs index 49ca593ac..0a4590c8d 100644 --- a/crates/vfs/src/file_set.rs +++ b/crates/vfs/src/file_set.rs | |||
@@ -9,6 +9,7 @@ use rustc_hash::FxHashMap; | |||
9 | 9 | ||
10 | use crate::{AnchoredPath, FileId, Vfs, VfsPath}; | 10 | use crate::{AnchoredPath, FileId, Vfs, VfsPath}; |
11 | 11 | ||
12 | /// A set of [`VfsPath`]s identified by [`FileId`]s. | ||
12 | #[derive(Default, Clone, Eq, PartialEq)] | 13 | #[derive(Default, Clone, Eq, PartialEq)] |
13 | pub struct FileSet { | 14 | pub struct FileSet { |
14 | files: FxHashMap<VfsPath, FileId>, | 15 | files: FxHashMap<VfsPath, FileId>, |
@@ -16,9 +17,15 @@ pub struct FileSet { | |||
16 | } | 17 | } |
17 | 18 | ||
18 | impl FileSet { | 19 | impl FileSet { |
20 | /// Returns the number of stored paths. | ||
19 | pub fn len(&self) -> usize { | 21 | pub fn len(&self) -> usize { |
20 | self.files.len() | 22 | self.files.len() |
21 | } | 23 | } |
24 | |||
25 | /// Get the id of the file corresponding to `path`. | ||
26 | /// | ||
27 | /// If either `path`'s [`anchor`](AnchoredPath::anchor) or the resolved path is not in | ||
28 | /// the set, returns [`None`]. | ||
22 | pub fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> { | 29 | pub fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> { |
23 | let mut base = self.paths[&path.anchor].clone(); | 30 | let mut base = self.paths[&path.anchor].clone(); |
24 | base.pop(); | 31 | base.pop(); |
@@ -26,19 +33,26 @@ impl FileSet { | |||
26 | self.files.get(&path).copied() | 33 | self.files.get(&path).copied() |
27 | } | 34 | } |
28 | 35 | ||
36 | /// Get the id corresponding to `path` if it exists in the set. | ||
29 | pub fn file_for_path(&self, path: &VfsPath) -> Option<&FileId> { | 37 | pub fn file_for_path(&self, path: &VfsPath) -> Option<&FileId> { |
30 | self.files.get(path) | 38 | self.files.get(path) |
31 | } | 39 | } |
32 | 40 | ||
41 | /// Get the path corresponding to `file` if it exists in the set. | ||
33 | pub fn path_for_file(&self, file: &FileId) -> Option<&VfsPath> { | 42 | pub fn path_for_file(&self, file: &FileId) -> Option<&VfsPath> { |
34 | self.paths.get(file) | 43 | self.paths.get(file) |
35 | } | 44 | } |
36 | 45 | ||
46 | /// Insert the `file_id, path` pair into the set. | ||
47 | /// | ||
48 | /// # Note | ||
49 | /// Multiple [`FileId`] can be mapped to the same [`VfsPath`], and vice-versa. | ||
37 | pub fn insert(&mut self, file_id: FileId, path: VfsPath) { | 50 | pub fn insert(&mut self, file_id: FileId, path: VfsPath) { |
38 | self.files.insert(path.clone(), file_id); | 51 | self.files.insert(path.clone(), file_id); |
39 | self.paths.insert(file_id, path); | 52 | self.paths.insert(file_id, path); |
40 | } | 53 | } |
41 | 54 | ||
55 | /// Iterate over this set's ids. | ||
42 | pub fn iter(&self) -> impl Iterator<Item = FileId> + '_ { | 56 | pub fn iter(&self) -> impl Iterator<Item = FileId> + '_ { |
43 | self.paths.keys().copied() | 57 | self.paths.keys().copied() |
44 | } | 58 | } |
@@ -50,9 +64,31 @@ impl fmt::Debug for FileSet { | |||
50 | } | 64 | } |
51 | } | 65 | } |
52 | 66 | ||
67 | /// This contains path prefixes to partition a [`Vfs`] into [`FileSet`]s. | ||
68 | /// | ||
69 | /// # Example | ||
70 | /// ```rust | ||
71 | /// # use vfs::{file_set::FileSetConfigBuilder, VfsPath, Vfs}; | ||
72 | /// let mut builder = FileSetConfigBuilder::default(); | ||
73 | /// builder.add_file_set(vec![VfsPath::new_virtual_path("/src".to_string())]); | ||
74 | /// let config = builder.build(); | ||
75 | /// let mut file_system = Vfs::default(); | ||
76 | /// file_system.set_file_contents(VfsPath::new_virtual_path("/src/main.rs".to_string()), Some(vec![])); | ||
77 | /// file_system.set_file_contents(VfsPath::new_virtual_path("/src/lib.rs".to_string()), Some(vec![])); | ||
78 | /// file_system.set_file_contents(VfsPath::new_virtual_path("/build.rs".to_string()), Some(vec![])); | ||
79 | /// // contains the sets : | ||
80 | /// // { "/src/main.rs", "/src/lib.rs" } | ||
81 | /// // { "build.rs" } | ||
82 | /// let sets = config.partition(&file_system); | ||
83 | /// ``` | ||
53 | #[derive(Debug)] | 84 | #[derive(Debug)] |
54 | pub struct FileSetConfig { | 85 | pub struct FileSetConfig { |
86 | /// Number of sets that `self` can partition a [`Vfs`] into. | ||
87 | /// | ||
88 | /// This should be the number of sets in `self.map` + 1 for files that don't fit in any | ||
89 | /// defined set. | ||
55 | n_file_sets: usize, | 90 | n_file_sets: usize, |
91 | /// Map from encoded paths to the set they belong to. | ||
56 | map: fst::Map<Vec<u8>>, | 92 | map: fst::Map<Vec<u8>>, |
57 | } | 93 | } |
58 | 94 | ||
@@ -63,9 +99,14 @@ impl Default for FileSetConfig { | |||
63 | } | 99 | } |
64 | 100 | ||
65 | impl FileSetConfig { | 101 | impl FileSetConfig { |
102 | /// Returns a builder for `FileSetConfig`. | ||
66 | pub fn builder() -> FileSetConfigBuilder { | 103 | pub fn builder() -> FileSetConfigBuilder { |
67 | FileSetConfigBuilder::default() | 104 | FileSetConfigBuilder::default() |
68 | } | 105 | } |
106 | |||
107 | /// Partition `vfs` into `FileSet`s. | ||
108 | /// | ||
109 | /// Creates a new [`FileSet`] for every set of prefixes in `self`. | ||
69 | pub fn partition(&self, vfs: &Vfs) -> Vec<FileSet> { | 110 | pub fn partition(&self, vfs: &Vfs) -> Vec<FileSet> { |
70 | let mut scratch_space = Vec::new(); | 111 | let mut scratch_space = Vec::new(); |
71 | let mut res = vec![FileSet::default(); self.len()]; | 112 | let mut res = vec![FileSet::default(); self.len()]; |
@@ -75,9 +116,15 @@ impl FileSetConfig { | |||
75 | } | 116 | } |
76 | res | 117 | res |
77 | } | 118 | } |
119 | |||
120 | /// Number of sets that `self` can partition a [`Vfs`] into. | ||
78 | fn len(&self) -> usize { | 121 | fn len(&self) -> usize { |
79 | self.n_file_sets | 122 | self.n_file_sets |
80 | } | 123 | } |
124 | |||
125 | /// Returns the set index for the given `path`. | ||
126 | /// | ||
127 | /// `scratch_space` is used as a buffer and will be entirely replaced. | ||
81 | fn classify(&self, path: &VfsPath, scratch_space: &mut Vec<u8>) -> usize { | 128 | fn classify(&self, path: &VfsPath, scratch_space: &mut Vec<u8>) -> usize { |
82 | scratch_space.clear(); | 129 | scratch_space.clear(); |
83 | path.encode(scratch_space); | 130 | path.encode(scratch_space); |
@@ -91,6 +138,7 @@ impl FileSetConfig { | |||
91 | } | 138 | } |
92 | } | 139 | } |
93 | 140 | ||
141 | /// Builder for [`FileSetConfig`]. | ||
94 | pub struct FileSetConfigBuilder { | 142 | pub struct FileSetConfigBuilder { |
95 | roots: Vec<Vec<VfsPath>>, | 143 | roots: Vec<Vec<VfsPath>>, |
96 | } | 144 | } |
@@ -102,12 +150,17 @@ impl Default for FileSetConfigBuilder { | |||
102 | } | 150 | } |
103 | 151 | ||
104 | impl FileSetConfigBuilder { | 152 | impl FileSetConfigBuilder { |
153 | /// Returns the number of sets currently held. | ||
105 | pub fn len(&self) -> usize { | 154 | pub fn len(&self) -> usize { |
106 | self.roots.len() | 155 | self.roots.len() |
107 | } | 156 | } |
157 | |||
158 | /// Add a new set of paths prefixes. | ||
108 | pub fn add_file_set(&mut self, roots: Vec<VfsPath>) { | 159 | pub fn add_file_set(&mut self, roots: Vec<VfsPath>) { |
109 | self.roots.push(roots) | 160 | self.roots.push(roots) |
110 | } | 161 | } |
162 | |||
163 | /// Build the `FileSetConfig`. | ||
111 | pub fn build(self) -> FileSetConfig { | 164 | pub fn build(self) -> FileSetConfig { |
112 | let n_file_sets = self.roots.len() + 1; | 165 | let n_file_sets = self.roots.len() + 1; |
113 | let map = { | 166 | let map = { |
@@ -127,11 +180,15 @@ impl FileSetConfigBuilder { | |||
127 | } | 180 | } |
128 | } | 181 | } |
129 | 182 | ||
183 | /// Implements [`fst::Automaton`] | ||
184 | /// | ||
185 | /// It will match if `prefix_of` is a prefix of the given data. | ||
130 | struct PrefixOf<'a> { | 186 | struct PrefixOf<'a> { |
131 | prefix_of: &'a [u8], | 187 | prefix_of: &'a [u8], |
132 | } | 188 | } |
133 | 189 | ||
134 | impl<'a> PrefixOf<'a> { | 190 | impl<'a> PrefixOf<'a> { |
191 | /// Creates a new `PrefixOf` from the given slice. | ||
135 | fn new(prefix_of: &'a [u8]) -> Self { | 192 | fn new(prefix_of: &'a [u8]) -> Self { |
136 | Self { prefix_of } | 193 | Self { prefix_of } |
137 | } | 194 | } |
diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs index 2b7b14524..e075d752b 100644 --- a/crates/vfs/src/lib.rs +++ b/crates/vfs/src/lib.rs | |||
@@ -53,9 +53,15 @@ pub use crate::{ | |||
53 | }; | 53 | }; |
54 | pub use paths::{AbsPath, AbsPathBuf}; | 54 | pub use paths::{AbsPath, AbsPathBuf}; |
55 | 55 | ||
56 | /// Handle to a file in [`Vfs`] | ||
57 | /// | ||
58 | /// Most functions in rust-analyzer use this when they need to refer to a file. | ||
56 | #[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] | 59 | #[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] |
57 | pub struct FileId(pub u32); | 60 | pub struct FileId(pub u32); |
58 | 61 | ||
62 | /// Storage for all files read by rust-analyzer. | ||
63 | /// | ||
64 | /// For more informations see the [crate-level](crate) documentation. | ||
59 | #[derive(Default)] | 65 | #[derive(Default)] |
60 | pub struct Vfs { | 66 | pub struct Vfs { |
61 | interner: PathInterner, | 67 | interner: PathInterner, |
@@ -63,40 +69,73 @@ pub struct Vfs { | |||
63 | changes: Vec<ChangedFile>, | 69 | changes: Vec<ChangedFile>, |
64 | } | 70 | } |
65 | 71 | ||
72 | /// Changed file in the [`Vfs`]. | ||
66 | pub struct ChangedFile { | 73 | pub struct ChangedFile { |
74 | /// Id of the changed file | ||
67 | pub file_id: FileId, | 75 | pub file_id: FileId, |
76 | /// Kind of change | ||
68 | pub change_kind: ChangeKind, | 77 | pub change_kind: ChangeKind, |
69 | } | 78 | } |
70 | 79 | ||
71 | impl ChangedFile { | 80 | impl ChangedFile { |
81 | /// Returns `true` if the change is not [`Delete`](ChangeKind::Delete). | ||
72 | pub fn exists(&self) -> bool { | 82 | pub fn exists(&self) -> bool { |
73 | self.change_kind != ChangeKind::Delete | 83 | self.change_kind != ChangeKind::Delete |
74 | } | 84 | } |
85 | |||
86 | /// Returns `true` if the change is [`Create`](ChangeKind::Create) or | ||
87 | /// [`Delete`](ChangeKind::Delete). | ||
75 | pub fn is_created_or_deleted(&self) -> bool { | 88 | pub fn is_created_or_deleted(&self) -> bool { |
76 | matches!(self.change_kind, ChangeKind::Create | ChangeKind::Delete) | 89 | matches!(self.change_kind, ChangeKind::Create | ChangeKind::Delete) |
77 | } | 90 | } |
78 | } | 91 | } |
79 | 92 | ||
93 | /// Kind of [file change](ChangedFile). | ||
80 | #[derive(Eq, PartialEq, Copy, Clone, Debug)] | 94 | #[derive(Eq, PartialEq, Copy, Clone, Debug)] |
81 | pub enum ChangeKind { | 95 | pub enum ChangeKind { |
96 | /// The file was (re-)created | ||
82 | Create, | 97 | Create, |
98 | /// The file was modified | ||
83 | Modify, | 99 | Modify, |
100 | /// The file was deleted | ||
84 | Delete, | 101 | Delete, |
85 | } | 102 | } |
86 | 103 | ||
87 | impl Vfs { | 104 | impl Vfs { |
105 | /// Amount of files currently stored. | ||
106 | /// | ||
107 | /// Note that this includes deleted files. | ||
88 | pub fn len(&self) -> usize { | 108 | pub fn len(&self) -> usize { |
89 | self.data.len() | 109 | self.data.len() |
90 | } | 110 | } |
111 | |||
112 | /// Id of the given path if it exists in the `Vfs` and is not deleted. | ||
91 | pub fn file_id(&self, path: &VfsPath) -> Option<FileId> { | 113 | pub fn file_id(&self, path: &VfsPath) -> Option<FileId> { |
92 | self.interner.get(path).filter(|&it| self.get(it).is_some()) | 114 | self.interner.get(path).filter(|&it| self.get(it).is_some()) |
93 | } | 115 | } |
116 | |||
117 | /// File path corresponding to the given `file_id`. | ||
118 | /// | ||
119 | /// # Panics | ||
120 | /// | ||
121 | /// Panics if the id is not present in the `Vfs`. | ||
94 | pub fn file_path(&self, file_id: FileId) -> VfsPath { | 122 | pub fn file_path(&self, file_id: FileId) -> VfsPath { |
95 | self.interner.lookup(file_id).clone() | 123 | self.interner.lookup(file_id).clone() |
96 | } | 124 | } |
125 | |||
126 | /// File content corresponding to the given `file_id`. | ||
127 | /// | ||
128 | /// # Panics | ||
129 | /// | ||
130 | /// Panics if the id is not present in the `Vfs`, or if the corresponding file is | ||
131 | /// deleted. | ||
97 | pub fn file_contents(&self, file_id: FileId) -> &[u8] { | 132 | pub fn file_contents(&self, file_id: FileId) -> &[u8] { |
98 | self.get(file_id).as_deref().unwrap() | 133 | self.get(file_id).as_deref().unwrap() |
99 | } | 134 | } |
135 | |||
136 | /// Returns an iterator over the stored ids and their corresponding paths. | ||
137 | /// | ||
138 | /// This will skip deleted files. | ||
100 | pub fn iter(&self) -> impl Iterator<Item = (FileId, &VfsPath)> + '_ { | 139 | pub fn iter(&self) -> impl Iterator<Item = (FileId, &VfsPath)> + '_ { |
101 | (0..self.data.len()) | 140 | (0..self.data.len()) |
102 | .map(|it| FileId(it as u32)) | 141 | .map(|it| FileId(it as u32)) |
@@ -106,6 +145,13 @@ impl Vfs { | |||
106 | (file_id, path) | 145 | (file_id, path) |
107 | }) | 146 | }) |
108 | } | 147 | } |
148 | |||
149 | /// Update the `path` with the given `contents`. `None` means the file was deleted. | ||
150 | /// | ||
151 | /// Returns `true` if the file was modified, and saves the [change](ChangedFile). | ||
152 | /// | ||
153 | /// If the path does not currently exists in the `Vfs`, allocates a new | ||
154 | /// [`FileId`] for it. | ||
109 | pub fn set_file_contents(&mut self, path: VfsPath, contents: Option<Vec<u8>>) -> bool { | 155 | pub fn set_file_contents(&mut self, path: VfsPath, contents: Option<Vec<u8>>) -> bool { |
110 | let file_id = self.alloc_file_id(path); | 156 | let file_id = self.alloc_file_id(path); |
111 | let change_kind = match (&self.get(file_id), &contents) { | 157 | let change_kind = match (&self.get(file_id), &contents) { |
@@ -120,12 +166,24 @@ impl Vfs { | |||
120 | self.changes.push(ChangedFile { file_id, change_kind }); | 166 | self.changes.push(ChangedFile { file_id, change_kind }); |
121 | true | 167 | true |
122 | } | 168 | } |
169 | |||
170 | /// Returns `true` if the `Vfs` contains [changes](ChangedFile). | ||
123 | pub fn has_changes(&self) -> bool { | 171 | pub fn has_changes(&self) -> bool { |
124 | !self.changes.is_empty() | 172 | !self.changes.is_empty() |
125 | } | 173 | } |
174 | |||
175 | /// Drain and returns all the changes in the `Vfs`. | ||
126 | pub fn take_changes(&mut self) -> Vec<ChangedFile> { | 176 | pub fn take_changes(&mut self) -> Vec<ChangedFile> { |
127 | mem::take(&mut self.changes) | 177 | mem::take(&mut self.changes) |
128 | } | 178 | } |
179 | |||
180 | /// Returns the id associated with `path` | ||
181 | /// | ||
182 | /// - If `path` does not exists in the `Vfs`, allocate a new id for it, associated with a | ||
183 | /// deleted file; | ||
184 | /// - Else, returns `path`'s id. | ||
185 | /// | ||
186 | /// Does not record a change. | ||
129 | fn alloc_file_id(&mut self, path: VfsPath) -> FileId { | 187 | fn alloc_file_id(&mut self, path: VfsPath) -> FileId { |
130 | let file_id = self.interner.intern(path); | 188 | let file_id = self.interner.intern(path); |
131 | let idx = file_id.0 as usize; | 189 | let idx = file_id.0 as usize; |
@@ -133,9 +191,21 @@ impl Vfs { | |||
133 | self.data.resize_with(len, || None); | 191 | self.data.resize_with(len, || None); |
134 | file_id | 192 | file_id |
135 | } | 193 | } |
194 | |||
195 | /// Returns the content associated with the given `file_id`. | ||
196 | /// | ||
197 | /// # Panics | ||
198 | /// | ||
199 | /// Panics if no file is associated to that id. | ||
136 | fn get(&self, file_id: FileId) -> &Option<Vec<u8>> { | 200 | fn get(&self, file_id: FileId) -> &Option<Vec<u8>> { |
137 | &self.data[file_id.0 as usize] | 201 | &self.data[file_id.0 as usize] |
138 | } | 202 | } |
203 | |||
204 | /// Mutably returns the content associated with the given `file_id`. | ||
205 | /// | ||
206 | /// # Panics | ||
207 | /// | ||
208 | /// Panics if no file is associated to that id. | ||
139 | fn get_mut(&mut self, file_id: FileId) -> &mut Option<Vec<u8>> { | 209 | fn get_mut(&mut self, file_id: FileId) -> &mut Option<Vec<u8>> { |
140 | &mut self.data[file_id.0 as usize] | 210 | &mut self.data[file_id.0 as usize] |
141 | } | 211 | } |
diff --git a/crates/vfs/src/loader.rs b/crates/vfs/src/loader.rs index 40cf96020..d3bdae562 100644 --- a/crates/vfs/src/loader.rs +++ b/crates/vfs/src/loader.rs | |||
@@ -3,9 +3,12 @@ use std::fmt; | |||
3 | 3 | ||
4 | use paths::{AbsPath, AbsPathBuf}; | 4 | use paths::{AbsPath, AbsPathBuf}; |
5 | 5 | ||
6 | /// A set of files on the file system. | ||
6 | #[derive(Debug, Clone)] | 7 | #[derive(Debug, Clone)] |
7 | pub enum Entry { | 8 | pub enum Entry { |
9 | /// The `Entry` is represented by a raw set of files. | ||
8 | Files(Vec<AbsPathBuf>), | 10 | Files(Vec<AbsPathBuf>), |
11 | /// The `Entry` is represented by `Directories`. | ||
9 | Directories(Directories), | 12 | Directories(Directories), |
10 | } | 13 | } |
11 | 14 | ||
@@ -17,6 +20,8 @@ pub enum Entry { | |||
17 | /// * it is not under `exclude` path | 20 | /// * it is not under `exclude` path |
18 | /// | 21 | /// |
19 | /// If many include/exclude paths match, the longest one wins. | 22 | /// If many include/exclude paths match, the longest one wins. |
23 | /// | ||
24 | /// If a path is in both `include` and `exclude`, the `exclude` one wins. | ||
20 | #[derive(Debug, Clone, Default)] | 25 | #[derive(Debug, Clone, Default)] |
21 | pub struct Directories { | 26 | pub struct Directories { |
22 | pub extensions: Vec<String>, | 27 | pub extensions: Vec<String>, |
@@ -24,45 +29,99 @@ pub struct Directories { | |||
24 | pub exclude: Vec<AbsPathBuf>, | 29 | pub exclude: Vec<AbsPathBuf>, |
25 | } | 30 | } |
26 | 31 | ||
32 | /// [`Handle`]'s configuration. | ||
27 | #[derive(Debug)] | 33 | #[derive(Debug)] |
28 | pub struct Config { | 34 | pub struct Config { |
35 | /// Set of initially loaded files. | ||
29 | pub load: Vec<Entry>, | 36 | pub load: Vec<Entry>, |
37 | /// Index of watched entries in `load`. | ||
38 | /// | ||
39 | /// If a path in a watched entry is modified,the [`Handle`] should notify it. | ||
30 | pub watch: Vec<usize>, | 40 | pub watch: Vec<usize>, |
31 | } | 41 | } |
32 | 42 | ||
43 | /// Message about an action taken by a [`Handle`]. | ||
33 | pub enum Message { | 44 | pub enum Message { |
45 | /// Indicate a gradual progress. | ||
46 | /// | ||
47 | /// This is supposed to be the number of loaded files. | ||
34 | Progress { n_total: usize, n_done: usize }, | 48 | Progress { n_total: usize, n_done: usize }, |
49 | /// The handle loaded the following files' content. | ||
35 | Loaded { files: Vec<(AbsPathBuf, Option<Vec<u8>>)> }, | 50 | Loaded { files: Vec<(AbsPathBuf, Option<Vec<u8>>)> }, |
36 | } | 51 | } |
37 | 52 | ||
53 | /// Type that will receive [`Messages`](Message) from a [`Handle`]. | ||
38 | pub type Sender = Box<dyn Fn(Message) + Send>; | 54 | pub type Sender = Box<dyn Fn(Message) + Send>; |
39 | 55 | ||
56 | /// Interface for reading and watching files. | ||
40 | pub trait Handle: fmt::Debug { | 57 | pub trait Handle: fmt::Debug { |
58 | /// Spawn a new handle with the given `sender`. | ||
41 | fn spawn(sender: Sender) -> Self | 59 | fn spawn(sender: Sender) -> Self |
42 | where | 60 | where |
43 | Self: Sized; | 61 | Self: Sized; |
62 | |||
63 | /// Set this handle's configuration. | ||
44 | fn set_config(&mut self, config: Config); | 64 | fn set_config(&mut self, config: Config); |
65 | |||
66 | /// The file's content at `path` has been modified, and should be reloaded. | ||
45 | fn invalidate(&mut self, path: AbsPathBuf); | 67 | fn invalidate(&mut self, path: AbsPathBuf); |
68 | |||
69 | /// Load the content of the given file, returning [`None`] if it does not | ||
70 | /// exists. | ||
46 | fn load_sync(&mut self, path: &AbsPath) -> Option<Vec<u8>>; | 71 | fn load_sync(&mut self, path: &AbsPath) -> Option<Vec<u8>>; |
47 | } | 72 | } |
48 | 73 | ||
49 | impl Entry { | 74 | impl Entry { |
75 | /// Returns: | ||
76 | /// ```text | ||
77 | /// Entry::Directories(Directories { | ||
78 | /// extensions: ["rs"], | ||
79 | /// include: [base], | ||
80 | /// exclude: [base/.git], | ||
81 | /// }) | ||
82 | /// ``` | ||
50 | pub fn rs_files_recursively(base: AbsPathBuf) -> Entry { | 83 | pub fn rs_files_recursively(base: AbsPathBuf) -> Entry { |
51 | Entry::Directories(dirs(base, &[".git"])) | 84 | Entry::Directories(dirs(base, &[".git"])) |
52 | } | 85 | } |
86 | |||
87 | /// Returns: | ||
88 | /// ```text | ||
89 | /// Entry::Directories(Directories { | ||
90 | /// extensions: ["rs"], | ||
91 | /// include: [base], | ||
92 | /// exclude: [base/.git, base/target], | ||
93 | /// }) | ||
94 | /// ``` | ||
53 | pub fn local_cargo_package(base: AbsPathBuf) -> Entry { | 95 | pub fn local_cargo_package(base: AbsPathBuf) -> Entry { |
54 | Entry::Directories(dirs(base, &[".git", "target"])) | 96 | Entry::Directories(dirs(base, &[".git", "target"])) |
55 | } | 97 | } |
98 | |||
99 | /// Returns: | ||
100 | /// ```text | ||
101 | /// Entry::Directories(Directories { | ||
102 | /// extensions: ["rs"], | ||
103 | /// include: [base], | ||
104 | /// exclude: [base/.git, /tests, /examples, /benches], | ||
105 | /// }) | ||
106 | /// ``` | ||
56 | pub fn cargo_package_dependency(base: AbsPathBuf) -> Entry { | 107 | pub fn cargo_package_dependency(base: AbsPathBuf) -> Entry { |
57 | Entry::Directories(dirs(base, &[".git", "/tests", "/examples", "/benches"])) | 108 | Entry::Directories(dirs(base, &[".git", "/tests", "/examples", "/benches"])) |
58 | } | 109 | } |
59 | 110 | ||
111 | /// Returns `true` if `path` is included in `self`. | ||
112 | /// | ||
113 | /// See [`Directories::contains_file`]. | ||
60 | pub fn contains_file(&self, path: &AbsPath) -> bool { | 114 | pub fn contains_file(&self, path: &AbsPath) -> bool { |
61 | match self { | 115 | match self { |
62 | Entry::Files(files) => files.iter().any(|it| it == path), | 116 | Entry::Files(files) => files.iter().any(|it| it == path), |
63 | Entry::Directories(dirs) => dirs.contains_file(path), | 117 | Entry::Directories(dirs) => dirs.contains_file(path), |
64 | } | 118 | } |
65 | } | 119 | } |
120 | |||
121 | /// Returns `true` if `path` is included in `self`. | ||
122 | /// | ||
123 | /// - If `self` is `Entry::Files`, returns `false` | ||
124 | /// - Else, see [`Directories::contains_dir`]. | ||
66 | pub fn contains_dir(&self, path: &AbsPath) -> bool { | 125 | pub fn contains_dir(&self, path: &AbsPath) -> bool { |
67 | match self { | 126 | match self { |
68 | Entry::Files(_) => false, | 127 | Entry::Files(_) => false, |
@@ -72,6 +131,7 @@ impl Entry { | |||
72 | } | 131 | } |
73 | 132 | ||
74 | impl Directories { | 133 | impl Directories { |
134 | /// Returns `true` if `path` is included in `self`. | ||
75 | pub fn contains_file(&self, path: &AbsPath) -> bool { | 135 | pub fn contains_file(&self, path: &AbsPath) -> bool { |
76 | let ext = path.extension().unwrap_or_default(); | 136 | let ext = path.extension().unwrap_or_default(); |
77 | if self.extensions.iter().all(|it| it.as_str() != ext) { | 137 | if self.extensions.iter().all(|it| it.as_str() != ext) { |
@@ -79,9 +139,21 @@ impl Directories { | |||
79 | } | 139 | } |
80 | self.includes_path(path) | 140 | self.includes_path(path) |
81 | } | 141 | } |
142 | |||
143 | /// Returns `true` if `path` is included in `self`. | ||
144 | /// | ||
145 | /// Since `path` is supposed to be a directory, this will not take extension | ||
146 | /// into account. | ||
82 | pub fn contains_dir(&self, path: &AbsPath) -> bool { | 147 | pub fn contains_dir(&self, path: &AbsPath) -> bool { |
83 | self.includes_path(path) | 148 | self.includes_path(path) |
84 | } | 149 | } |
150 | |||
151 | /// Returns `true` if `path` is included in `self`. | ||
152 | /// | ||
153 | /// It is included if | ||
154 | /// - An element in `self.include` is a prefix of `path`. | ||
155 | /// - This path is longer than any element in `self.exclude` that is a prefix | ||
156 | /// of `path`. In case of equality, exclusion wins. | ||
85 | fn includes_path(&self, path: &AbsPath) -> bool { | 157 | fn includes_path(&self, path: &AbsPath) -> bool { |
86 | let mut include: Option<&AbsPathBuf> = None; | 158 | let mut include: Option<&AbsPathBuf> = None; |
87 | for incl in &self.include { | 159 | for incl in &self.include { |
@@ -105,6 +177,14 @@ impl Directories { | |||
105 | } | 177 | } |
106 | } | 178 | } |
107 | 179 | ||
180 | /// Returns : | ||
181 | /// ```text | ||
182 | /// Directories { | ||
183 | /// extensions: ["rs"], | ||
184 | /// include: [base], | ||
185 | /// exclude: [base/<exclude>], | ||
186 | /// } | ||
187 | /// ``` | ||
108 | fn dirs(base: AbsPathBuf, exclude: &[&str]) -> Directories { | 188 | fn dirs(base: AbsPathBuf, exclude: &[&str]) -> Directories { |
109 | let exclude = exclude.iter().map(|it| base.join(it)).collect::<Vec<_>>(); | 189 | let exclude = exclude.iter().map(|it| base.join(it)).collect::<Vec<_>>(); |
110 | Directories { extensions: vec!["rs".to_string()], include: vec![base], exclude } | 190 | Directories { extensions: vec!["rs".to_string()], include: vec![base], exclude } |
diff --git a/crates/vfs/src/path_interner.rs b/crates/vfs/src/path_interner.rs index 4f70d61e8..2189e5e25 100644 --- a/crates/vfs/src/path_interner.rs +++ b/crates/vfs/src/path_interner.rs | |||
@@ -5,6 +5,7 @@ use rustc_hash::FxHashMap; | |||
5 | 5 | ||
6 | use crate::{FileId, VfsPath}; | 6 | use crate::{FileId, VfsPath}; |
7 | 7 | ||
8 | /// Structure to map between [`VfsPath`] and [`FileId`]. | ||
8 | #[derive(Default)] | 9 | #[derive(Default)] |
9 | pub(crate) struct PathInterner { | 10 | pub(crate) struct PathInterner { |
10 | map: FxHashMap<VfsPath, FileId>, | 11 | map: FxHashMap<VfsPath, FileId>, |
@@ -12,9 +13,17 @@ pub(crate) struct PathInterner { | |||
12 | } | 13 | } |
13 | 14 | ||
14 | impl PathInterner { | 15 | impl PathInterner { |
16 | /// Get the id corresponding to `path`. | ||
17 | /// | ||
18 | /// If `path` does not exists in `self`, returns [`None`]. | ||
15 | pub(crate) fn get(&self, path: &VfsPath) -> Option<FileId> { | 19 | pub(crate) fn get(&self, path: &VfsPath) -> Option<FileId> { |
16 | self.map.get(path).copied() | 20 | self.map.get(path).copied() |
17 | } | 21 | } |
22 | |||
23 | /// Insert `path` in `self`. | ||
24 | /// | ||
25 | /// - If `path` already exists in `self`, returns its associated id; | ||
26 | /// - Else, returns a newly allocated id. | ||
18 | pub(crate) fn intern(&mut self, path: VfsPath) -> FileId { | 27 | pub(crate) fn intern(&mut self, path: VfsPath) -> FileId { |
19 | if let Some(id) = self.get(&path) { | 28 | if let Some(id) = self.get(&path) { |
20 | return id; | 29 | return id; |
@@ -25,6 +34,11 @@ impl PathInterner { | |||
25 | id | 34 | id |
26 | } | 35 | } |
27 | 36 | ||
37 | /// Returns the path corresponding to `id`. | ||
38 | /// | ||
39 | /// # Panics | ||
40 | /// | ||
41 | /// Panics if `id` does not exists in `self`. | ||
28 | pub(crate) fn lookup(&self, id: FileId) -> &VfsPath { | 42 | pub(crate) fn lookup(&self, id: FileId) -> &VfsPath { |
29 | &self.vec[id.0 as usize] | 43 | &self.vec[id.0 as usize] |
30 | } | 44 | } |
diff --git a/crates/vfs/src/vfs_path.rs b/crates/vfs/src/vfs_path.rs index bd14911c9..2b3d7fd84 100644 --- a/crates/vfs/src/vfs_path.rs +++ b/crates/vfs/src/vfs_path.rs | |||
@@ -3,25 +3,37 @@ use std::fmt; | |||
3 | 3 | ||
4 | use paths::{AbsPath, AbsPathBuf}; | 4 | use paths::{AbsPath, AbsPathBuf}; |
5 | 5 | ||
6 | /// Path in [`Vfs`]. | ||
7 | /// | ||
6 | /// Long-term, we want to support files which do not reside in the file-system, | 8 | /// Long-term, we want to support files which do not reside in the file-system, |
7 | /// so we treat VfsPaths as opaque identifiers. | 9 | /// so we treat `VfsPath`s as opaque identifiers. |
10 | /// | ||
11 | /// [`Vfs`]: crate::Vfs | ||
8 | #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] | 12 | #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] |
9 | pub struct VfsPath(VfsPathRepr); | 13 | pub struct VfsPath(VfsPathRepr); |
10 | 14 | ||
11 | impl VfsPath { | 15 | impl VfsPath { |
12 | /// Creates an "in-memory" path from `/`-separates string. | 16 | /// Creates an "in-memory" path from `/`-separated string. |
17 | /// | ||
13 | /// This is most useful for testing, to avoid windows/linux differences | 18 | /// This is most useful for testing, to avoid windows/linux differences |
19 | /// | ||
20 | /// # Panics | ||
21 | /// | ||
22 | /// Panics if `path` does not start with `'/'`. | ||
14 | pub fn new_virtual_path(path: String) -> VfsPath { | 23 | pub fn new_virtual_path(path: String) -> VfsPath { |
15 | assert!(path.starts_with('/')); | 24 | assert!(path.starts_with('/')); |
16 | VfsPath(VfsPathRepr::VirtualPath(VirtualPath(path))) | 25 | VfsPath(VfsPathRepr::VirtualPath(VirtualPath(path))) |
17 | } | 26 | } |
18 | 27 | ||
28 | /// Returns the `AbsPath` representation of `self` if `self` is on the file system. | ||
19 | pub fn as_path(&self) -> Option<&AbsPath> { | 29 | pub fn as_path(&self) -> Option<&AbsPath> { |
20 | match &self.0 { | 30 | match &self.0 { |
21 | VfsPathRepr::PathBuf(it) => Some(it.as_path()), | 31 | VfsPathRepr::PathBuf(it) => Some(it.as_path()), |
22 | VfsPathRepr::VirtualPath(_) => None, | 32 | VfsPathRepr::VirtualPath(_) => None, |
23 | } | 33 | } |
24 | } | 34 | } |
35 | |||
36 | /// Creates a new `VfsPath` with `path` adjoined to `self`. | ||
25 | pub fn join(&self, path: &str) -> Option<VfsPath> { | 37 | pub fn join(&self, path: &str) -> Option<VfsPath> { |
26 | match &self.0 { | 38 | match &self.0 { |
27 | VfsPathRepr::PathBuf(it) => { | 39 | VfsPathRepr::PathBuf(it) => { |
@@ -34,12 +46,30 @@ impl VfsPath { | |||
34 | } | 46 | } |
35 | } | 47 | } |
36 | } | 48 | } |
49 | |||
50 | /// Remove the last component of `self` if there is one. | ||
51 | /// | ||
52 | /// If `self` has no component, returns `false`; else returns `true`. | ||
53 | /// | ||
54 | /// # Example | ||
55 | /// | ||
56 | /// ``` | ||
57 | /// # use vfs::{AbsPathBuf, VfsPath}; | ||
58 | /// let mut path = VfsPath::from(AbsPathBuf::assert("/foo/bar".into())); | ||
59 | /// assert!(path.pop()); | ||
60 | /// assert_eq!(path, VfsPath::from(AbsPathBuf::assert("/foo".into()))); | ||
61 | /// assert!(path.pop()); | ||
62 | /// assert_eq!(path, VfsPath::from(AbsPathBuf::assert("/".into()))); | ||
63 | /// assert!(!path.pop()); | ||
64 | /// ``` | ||
37 | pub fn pop(&mut self) -> bool { | 65 | pub fn pop(&mut self) -> bool { |
38 | match &mut self.0 { | 66 | match &mut self.0 { |
39 | VfsPathRepr::PathBuf(it) => it.pop(), | 67 | VfsPathRepr::PathBuf(it) => it.pop(), |
40 | VfsPathRepr::VirtualPath(it) => it.pop(), | 68 | VfsPathRepr::VirtualPath(it) => it.pop(), |
41 | } | 69 | } |
42 | } | 70 | } |
71 | |||
72 | /// Returns `true` if `other` is a prefix of `self`. | ||
43 | pub fn starts_with(&self, other: &VfsPath) -> bool { | 73 | pub fn starts_with(&self, other: &VfsPath) -> bool { |
44 | match (&self.0, &other.0) { | 74 | match (&self.0, &other.0) { |
45 | (VfsPathRepr::PathBuf(lhs), VfsPathRepr::PathBuf(rhs)) => lhs.starts_with(rhs), | 75 | (VfsPathRepr::PathBuf(lhs), VfsPathRepr::PathBuf(rhs)) => lhs.starts_with(rhs), |
@@ -48,6 +78,10 @@ impl VfsPath { | |||
48 | (VfsPathRepr::VirtualPath(_), _) => false, | 78 | (VfsPathRepr::VirtualPath(_), _) => false, |
49 | } | 79 | } |
50 | } | 80 | } |
81 | |||
82 | /// Returns the `VfsPath` without its final component, if there is one. | ||
83 | /// | ||
84 | /// Returns [`None`] if the path is a root or prefix. | ||
51 | pub fn parent(&self) -> Option<VfsPath> { | 85 | pub fn parent(&self) -> Option<VfsPath> { |
52 | let mut parent = self.clone(); | 86 | let mut parent = self.clone(); |
53 | if parent.pop() { | 87 | if parent.pop() { |
@@ -57,6 +91,7 @@ impl VfsPath { | |||
57 | } | 91 | } |
58 | } | 92 | } |
59 | 93 | ||
94 | /// Returns `self`'s base name and file extension. | ||
60 | pub fn name_and_extension(&self) -> Option<(&str, Option<&str>)> { | 95 | pub fn name_and_extension(&self) -> Option<(&str, Option<&str>)> { |
61 | match &self.0 { | 96 | match &self.0 { |
62 | VfsPathRepr::PathBuf(p) => Some(( | 97 | VfsPathRepr::PathBuf(p) => Some(( |
@@ -67,7 +102,14 @@ impl VfsPath { | |||
67 | } | 102 | } |
68 | } | 103 | } |
69 | 104 | ||
70 | // Don't make this `pub` | 105 | /// **Don't make this `pub`** |
106 | /// | ||
107 | /// Encode the path in the given buffer. | ||
108 | /// | ||
109 | /// The encoding will be `0` if [`AbsPathBuf`], `1` if [`VirtualPath`], followed | ||
110 | /// by `self`'s representation. | ||
111 | /// | ||
112 | /// Note that this encoding is dependent on the operating system. | ||
71 | pub(crate) fn encode(&self, buf: &mut Vec<u8>) { | 113 | pub(crate) fn encode(&self, buf: &mut Vec<u8>) { |
72 | let tag = match &self.0 { | 114 | let tag = match &self.0 { |
73 | VfsPathRepr::PathBuf(_) => 0, | 115 | VfsPathRepr::PathBuf(_) => 0, |
@@ -224,6 +266,7 @@ mod windows_paths { | |||
224 | } | 266 | } |
225 | } | 267 | } |
226 | 268 | ||
269 | /// Internal, private representation of [`VfsPath`]. | ||
227 | #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] | 270 | #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] |
228 | enum VfsPathRepr { | 271 | enum VfsPathRepr { |
229 | PathBuf(AbsPathBuf), | 272 | PathBuf(AbsPathBuf), |
@@ -260,13 +303,34 @@ impl fmt::Debug for VfsPathRepr { | |||
260 | } | 303 | } |
261 | } | 304 | } |
262 | 305 | ||
306 | /// `/`-separated virtual path. | ||
307 | /// | ||
308 | /// This is used to describe files that do not reside on the file system. | ||
263 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] | 309 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] |
264 | struct VirtualPath(String); | 310 | struct VirtualPath(String); |
265 | 311 | ||
266 | impl VirtualPath { | 312 | impl VirtualPath { |
313 | /// Returns `true` if `other` is a prefix of `self` (as strings). | ||
267 | fn starts_with(&self, other: &VirtualPath) -> bool { | 314 | fn starts_with(&self, other: &VirtualPath) -> bool { |
268 | self.0.starts_with(&other.0) | 315 | self.0.starts_with(&other.0) |
269 | } | 316 | } |
317 | |||
318 | /// Remove the last component of `self`. | ||
319 | /// | ||
320 | /// This will find the last `'/'` in `self`, and remove everything after it, | ||
321 | /// including the `'/'`. | ||
322 | /// | ||
323 | /// If `self` contains no `'/'`, returns `false`; else returns `true`. | ||
324 | /// | ||
325 | /// # Example | ||
326 | /// | ||
327 | /// ```rust,ignore | ||
328 | /// let mut path = VirtualPath("/foo/bar".to_string()); | ||
329 | /// path.pop(); | ||
330 | /// assert_eq!(path.0, "/foo"); | ||
331 | /// path.pop(); | ||
332 | /// assert_eq!(path.0, ""); | ||
333 | /// ``` | ||
270 | fn pop(&mut self) -> bool { | 334 | fn pop(&mut self) -> bool { |
271 | let pos = match self.0.rfind('/') { | 335 | let pos = match self.0.rfind('/') { |
272 | Some(pos) => pos, | 336 | Some(pos) => pos, |
@@ -275,6 +339,17 @@ impl VirtualPath { | |||
275 | self.0 = self.0[..pos].to_string(); | 339 | self.0 = self.0[..pos].to_string(); |
276 | true | 340 | true |
277 | } | 341 | } |
342 | |||
343 | /// Append the given *relative* path `path` to `self`. | ||
344 | /// | ||
345 | /// This will resolve any leading `"../"` in `path` before appending it. | ||
346 | /// | ||
347 | /// Returns [`None`] if `path` has more leading `"../"` than the number of | ||
348 | /// components in `self`. | ||
349 | /// | ||
350 | /// # Notes | ||
351 | /// | ||
352 | /// In practice, appending here means `self/path` as strings. | ||
278 | fn join(&self, mut path: &str) -> Option<VirtualPath> { | 353 | fn join(&self, mut path: &str) -> Option<VirtualPath> { |
279 | let mut res = self.clone(); | 354 | let mut res = self.clone(); |
280 | while path.starts_with("../") { | 355 | while path.starts_with("../") { |
@@ -287,7 +362,18 @@ impl VirtualPath { | |||
287 | Some(res) | 362 | Some(res) |
288 | } | 363 | } |
289 | 364 | ||
290 | pub(crate) fn name_and_extension(&self) -> Option<(&str, Option<&str>)> { | 365 | /// Returns `self`'s base name and file extension. |
366 | /// | ||
367 | /// # Returns | ||
368 | /// - `None` if `self` ends with `"//"`. | ||
369 | /// - `Some((name, None))` if `self`'s base contains no `.`, or only one `.` at | ||
370 | /// the start. | ||
371 | /// - `Some((name, Some(extension))` else. | ||
372 | /// | ||
373 | /// # Note | ||
374 | /// The extension will not contains `.`. This means `"/foo/bar.baz.rs"` will | ||
375 | /// return `Some(("bar.baz", Some("rs"))`. | ||
376 | fn name_and_extension(&self) -> Option<(&str, Option<&str>)> { | ||
291 | let file_path = if self.0.ends_with('/') { &self.0[..&self.0.len() - 1] } else { &self.0 }; | 377 | let file_path = if self.0.ends_with('/') { &self.0[..&self.0.len() - 1] } else { &self.0 }; |
292 | let file_name = match file_path.rfind('/') { | 378 | let file_name = match file_path.rfind('/') { |
293 | Some(position) => &file_path[position + 1..], | 379 | Some(position) => &file_path[position + 1..], |
diff --git a/docs/dev/style.md b/docs/dev/style.md index 67cbc6744..9859f6148 100644 --- a/docs/dev/style.md +++ b/docs/dev/style.md | |||
@@ -78,6 +78,8 @@ Use original span for FileId | |||
78 | 78 | ||
79 | This makes it easier to prepare a changelog. | 79 | This makes it easier to prepare a changelog. |
80 | 80 | ||
81 | If the change adds a new user-visible functionality, consider recording a GIF with [peek](https://github.com/phw/peek) and pasting it into the PR description. | ||
82 | |||
81 | **Rationale:** clean history is potentially useful, but rarely used. | 83 | **Rationale:** clean history is potentially useful, but rarely used. |
82 | But many users read changelogs. | 84 | But many users read changelogs. |
83 | 85 | ||
@@ -643,6 +645,27 @@ assert!(x >= lo && x <= hi>); | |||
643 | **Rationale:** Less-then comparisons are more intuitive, they correspond spatially to [real line](https://en.wikipedia.org/wiki/Real_line). | 645 | **Rationale:** Less-then comparisons are more intuitive, they correspond spatially to [real line](https://en.wikipedia.org/wiki/Real_line). |
644 | 646 | ||
645 | 647 | ||
648 | ## Token names | ||
649 | |||
650 | Use `T![foo]` instead of `SyntaxKind::FOO_KW`. | ||
651 | |||
652 | ```rust | ||
653 | // GOOD | ||
654 | match p.current() { | ||
655 | T![true] | T![false] => true, | ||
656 | _ => false, | ||
657 | } | ||
658 | |||
659 | // BAD | ||
660 | |||
661 | match p.current() { | ||
662 | SyntaxKind::TRUE_KW | SyntaxKind::FALSE_KW => true, | ||
663 | _ => false, | ||
664 | } | ||
665 | ``` | ||
666 | |||
667 | **Rationale:** The macro uses the familiar Rust syntax, avoiding ambiguities like "is this a brace or bracket?". | ||
668 | |||
646 | ## Documentation | 669 | ## Documentation |
647 | 670 | ||
648 | For `.md` and `.adoc` files, prefer a sentence-per-line format, don't wrap lines. | 671 | For `.md` and `.adoc` files, prefer a sentence-per-line format, don't wrap lines. |
diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 3e07daae9..dec48629c 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs | |||
@@ -12,7 +12,7 @@ use std::env; | |||
12 | 12 | ||
13 | use codegen::CodegenCmd; | 13 | use codegen::CodegenCmd; |
14 | use pico_args::Arguments; | 14 | use pico_args::Arguments; |
15 | use xshell::pushd; | 15 | use xshell::{cmd, cp, pushd}; |
16 | use xtask::{ | 16 | use xtask::{ |
17 | codegen::{self, Mode}, | 17 | codegen::{self, Mode}, |
18 | dist::DistCmd, | 18 | dist::DistCmd, |
@@ -124,6 +124,13 @@ FLAGS: | |||
124 | args.finish()?; | 124 | args.finish()?; |
125 | MetricsCmd { dry_run }.run() | 125 | MetricsCmd { dry_run }.run() |
126 | } | 126 | } |
127 | "bb" => { | ||
128 | let suffix: String = args.free_from_str()?.unwrap(); | ||
129 | args.finish()?; | ||
130 | cmd!("cargo build --release").run()?; | ||
131 | cp("./target/release/rust-analyzer", format!("./target/rust-analyzer-{}", suffix))?; | ||
132 | Ok(()) | ||
133 | } | ||
127 | _ => { | 134 | _ => { |
128 | eprintln!( | 135 | eprintln!( |
129 | "\ | 136 | "\ |
@@ -141,7 +148,8 @@ SUBCOMMANDS: | |||
141 | install | 148 | install |
142 | lint | 149 | lint |
143 | dist | 150 | dist |
144 | promote" | 151 | promote |
152 | bb" | ||
145 | ); | 153 | ); |
146 | Ok(()) | 154 | Ok(()) |
147 | } | 155 | } |
diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs index 7e5b1f7b5..6abad189a 100644 --- a/xtask/tests/tidy.rs +++ b/xtask/tests/tidy.rs | |||
@@ -85,6 +85,7 @@ fn rust_files_are_tidy() { | |||
85 | for path in rust_files(&project_root().join("crates")) { | 85 | for path in rust_files(&project_root().join("crates")) { |
86 | let text = read_file(&path).unwrap(); | 86 | let text = read_file(&path).unwrap(); |
87 | check_todo(&path, &text); | 87 | check_todo(&path, &text); |
88 | check_dbg(&path, &text); | ||
88 | check_trailing_ws(&path, &text); | 89 | check_trailing_ws(&path, &text); |
89 | deny_clippy(&path, &text); | 90 | deny_clippy(&path, &text); |
90 | tidy_docs.visit(&path, &text); | 91 | tidy_docs.visit(&path, &text); |
@@ -94,10 +95,9 @@ fn rust_files_are_tidy() { | |||
94 | 95 | ||
95 | #[test] | 96 | #[test] |
96 | fn check_merge_commits() { | 97 | fn check_merge_commits() { |
97 | let stdout = | 98 | let stdout = cmd!("git rev-list --merges --invert-grep --author 'bors\\[bot\\]' HEAD~19..") |
98 | dbg!(cmd!("git rev-list --merges --invert-grep --author 'bors\\[bot\\]' HEAD~19..")) | 99 | .read() |
99 | .read() | 100 | .unwrap(); |
100 | .unwrap(); | ||
101 | if !stdout.is_empty() { | 101 | if !stdout.is_empty() { |
102 | panic!( | 102 | panic!( |
103 | " | 103 | " |
@@ -224,7 +224,7 @@ Zlib OR Apache-2.0 OR MIT | |||
224 | fn check_todo(path: &Path, text: &str) { | 224 | fn check_todo(path: &Path, text: &str) { |
225 | let need_todo = &[ | 225 | let need_todo = &[ |
226 | // This file itself obviously needs to use todo (<- like this!). | 226 | // This file itself obviously needs to use todo (<- like this!). |
227 | "tests/cli.rs", | 227 | "tests/tidy.rs", |
228 | // Some of our assists generate `todo!()`. | 228 | // Some of our assists generate `todo!()`. |
229 | "handlers/add_turbo_fish.rs", | 229 | "handlers/add_turbo_fish.rs", |
230 | "handlers/generate_function.rs", | 230 | "handlers/generate_function.rs", |
@@ -252,6 +252,32 @@ fn check_todo(path: &Path, text: &str) { | |||
252 | } | 252 | } |
253 | } | 253 | } |
254 | 254 | ||
255 | fn check_dbg(path: &Path, text: &str) { | ||
256 | let need_dbg = &[ | ||
257 | // This file itself obviously needs to use dbg. | ||
258 | "tests/tidy.rs", | ||
259 | // Assists to remove `dbg!()` | ||
260 | "handlers/remove_dbg.rs", | ||
261 | // We have .dbg postfix | ||
262 | "completion/src/completions/postfix.rs", | ||
263 | // The documentation in string literals may contain anything for its own purposes | ||
264 | "completion/src/lib.rs", | ||
265 | "completion/src/generated_lint_completions.rs", | ||
266 | // test for doc test for remove_dbg | ||
267 | "src/tests/generated.rs", | ||
268 | ]; | ||
269 | if need_dbg.iter().any(|p| path.ends_with(p)) { | ||
270 | return; | ||
271 | } | ||
272 | if text.contains("dbg!") { | ||
273 | panic!( | ||
274 | "\ndbg! macros should not be committed to the master branch,\n\ | ||
275 | {}\n", | ||
276 | path.display(), | ||
277 | ) | ||
278 | } | ||
279 | } | ||
280 | |||
255 | fn check_trailing_ws(path: &Path, text: &str) { | 281 | fn check_trailing_ws(path: &Path, text: &str) { |
256 | if is_exclude_dir(path, &["test_data"]) { | 282 | if is_exclude_dir(path, &["test_data"]) { |
257 | return; | 283 | return; |
@@ -298,7 +324,7 @@ impl TidyDocs { | |||
298 | } | 324 | } |
299 | 325 | ||
300 | fn is_exclude_file(d: &Path) -> bool { | 326 | fn is_exclude_file(d: &Path) -> bool { |
301 | let file_names = ["tests.rs"]; | 327 | let file_names = ["tests.rs", "famous_defs_fixture.rs"]; |
302 | 328 | ||
303 | d.file_name() | 329 | d.file_name() |
304 | .unwrap_or_default() | 330 | .unwrap_or_default() |