aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock8
-rw-r--r--crates/hir/src/code_model.rs36
-rw-r--r--crates/hir_expand/src/name.rs10
-rw-r--r--crates/hir_ty/src/infer.rs2
-rw-r--r--crates/ide/src/completion/complete_postfix.rs12
-rw-r--r--crates/ide/src/completion/completion_context.rs10
-rw-r--r--crates/ide/src/completion/presentation.rs123
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html6
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs6
-rw-r--r--crates/mbe/src/syntax_bridge.rs5
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/caps.rs14
-rw-r--r--docs/user/manual.adoc1
-rw-r--r--editors/code/package.json9
-rw-r--r--editors/code/src/main.ts98
-rw-r--r--editors/code/src/net.ts18
-rw-r--r--editors/code/src/persistent_state.ts11
17 files changed, 319 insertions, 52 deletions
diff --git a/Cargo.lock b/Cargo.lock
index cf88c4e17..85839bf47 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -775,9 +775,9 @@ dependencies = [
775 775
776[[package]] 776[[package]]
777name = "lsp-types" 777name = "lsp-types"
778version = "0.81.0" 778version = "0.82.0"
779source = "registry+https://github.com/rust-lang/crates.io-index" 779source = "registry+https://github.com/rust-lang/crates.io-index"
780checksum = "5e02724627e9ef8ba91f461ebc01d48aebbd13a4b7c9dc547a0a2890f53e2171" 780checksum = "db895abb8527cf59e3de893ab2acf52cf904faeb65e60ea6f373e11fe86464e8"
781dependencies = [ 781dependencies = [
782 "base64", 782 "base64",
783 "bitflags", 783 "bitflags",
@@ -1465,9 +1465,9 @@ checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252"
1465 1465
1466[[package]] 1466[[package]]
1467name = "smol_str" 1467name = "smol_str"
1468version = "0.1.16" 1468version = "0.1.17"
1469source = "registry+https://github.com/rust-lang/crates.io-index" 1469source = "registry+https://github.com/rust-lang/crates.io-index"
1470checksum = "2f7909a1d8bc166a862124d84fdc11bda0ea4ed3157ccca662296919c2972db1" 1470checksum = "6ca0f7ce3a29234210f0f4f0b56f8be2e722488b95cb522077943212da3b32eb"
1471dependencies = [ 1471dependencies = [
1472 "serde", 1472 "serde",
1473] 1473]
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index 849c8f6d0..a2a166e0a 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -709,11 +709,23 @@ impl Function {
709 } 709 }
710 710
711 pub fn params(self, db: &dyn HirDatabase) -> Vec<Param> { 711 pub fn params(self, db: &dyn HirDatabase) -> Vec<Param> {
712 let resolver = self.id.resolver(db.upcast());
713 let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
714 let environment = TraitEnvironment::lower(db, &resolver);
712 db.function_data(self.id) 715 db.function_data(self.id)
713 .params 716 .params
714 .iter() 717 .iter()
715 .skip(if self.self_param(db).is_some() { 1 } else { 0 }) 718 .skip(if self.self_param(db).is_some() { 1 } else { 0 })
716 .map(|_| Param { _ty: () }) 719 .map(|type_ref| {
720 let ty = Type {
721 krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate,
722 ty: InEnvironment {
723 value: Ty::from_hir_ext(&ctx, type_ref).0,
724 environment: environment.clone(),
725 },
726 };
727 Param { ty }
728 })
717 .collect() 729 .collect()
718 } 730 }
719 731
@@ -742,15 +754,21 @@ impl From<Mutability> for Access {
742 } 754 }
743} 755}
744 756
757pub struct Param {
758 ty: Type,
759}
760
761impl Param {
762 pub fn ty(&self) -> &Type {
763 &self.ty
764 }
765}
766
745#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 767#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
746pub struct SelfParam { 768pub struct SelfParam {
747 func: FunctionId, 769 func: FunctionId,
748} 770}
749 771
750pub struct Param {
751 _ty: (),
752}
753
754impl SelfParam { 772impl SelfParam {
755 pub fn access(self, db: &dyn HirDatabase) -> Access { 773 pub fn access(self, db: &dyn HirDatabase) -> Access {
756 let func_data = db.function_data(self.func); 774 let func_data = db.function_data(self.func);
@@ -1276,6 +1294,14 @@ impl Type {
1276 ) 1294 )
1277 } 1295 }
1278 1296
1297 pub fn remove_ref(&self) -> Option<Type> {
1298 if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Ref(_), .. }) = self.ty.value {
1299 self.ty.value.substs().map(|substs| self.derived(substs[0].clone()))
1300 } else {
1301 None
1302 }
1303 }
1304
1279 pub fn is_unknown(&self) -> bool { 1305 pub fn is_unknown(&self) -> bool {
1280 matches!(self.ty.value, Ty::Unknown) 1306 matches!(self.ty.value, Ty::Unknown)
1281 } 1307 }
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs
index 49841c7a1..a5750d829 100644
--- a/crates/hir_expand/src/name.rs
+++ b/crates/hir_expand/src/name.rs
@@ -43,8 +43,8 @@ impl Name {
43 } 43 }
44 44
45 /// Shortcut to create inline plain text name 45 /// Shortcut to create inline plain text name
46 const fn new_inline_ascii(text: &[u8]) -> Name { 46 const fn new_inline(text: &str) -> Name {
47 Name::new_text(SmolStr::new_inline_from_ascii(text.len(), text)) 47 Name::new_text(SmolStr::new_inline(text))
48 } 48 }
49 49
50 /// Resolve a name from the text of token. 50 /// Resolve a name from the text of token.
@@ -127,7 +127,7 @@ pub mod known {
127 $( 127 $(
128 #[allow(bad_style)] 128 #[allow(bad_style)]
129 pub const $ident: super::Name = 129 pub const $ident: super::Name =
130 super::Name::new_inline_ascii(stringify!($ident).as_bytes()); 130 super::Name::new_inline(stringify!($ident));
131 )* 131 )*
132 }; 132 };
133 } 133 }
@@ -210,8 +210,8 @@ pub mod known {
210 ); 210 );
211 211
212 // self/Self cannot be used as an identifier 212 // self/Self cannot be used as an identifier
213 pub const SELF_PARAM: super::Name = super::Name::new_inline_ascii(b"self"); 213 pub const SELF_PARAM: super::Name = super::Name::new_inline("self");
214 pub const SELF_TYPE: super::Name = super::Name::new_inline_ascii(b"Self"); 214 pub const SELF_TYPE: super::Name = super::Name::new_inline("Self");
215 215
216 #[macro_export] 216 #[macro_export]
217 macro_rules! name { 217 macro_rules! name {
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index 2b53b8297..9a7785c76 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -555,7 +555,7 @@ impl<'a> InferenceContext<'a> {
555 555
556 fn resolve_lang_item(&self, name: &str) -> Option<LangItemTarget> { 556 fn resolve_lang_item(&self, name: &str) -> Option<LangItemTarget> {
557 let krate = self.resolver.krate()?; 557 let krate = self.resolver.krate()?;
558 let name = SmolStr::new_inline_from_ascii(name.len(), name.as_bytes()); 558 let name = SmolStr::new_inline(name);
559 self.db.lang_item(krate, name) 559 self.db.lang_item(krate, name)
560 } 560 }
561 561
diff --git a/crates/ide/src/completion/complete_postfix.rs b/crates/ide/src/completion/complete_postfix.rs
index 00f546563..26a5af5b9 100644
--- a/crates/ide/src/completion/complete_postfix.rs
+++ b/crates/ide/src/completion/complete_postfix.rs
@@ -192,6 +192,16 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
192 ctx, 192 ctx,
193 cap, 193 cap,
194 &dot_receiver, 194 &dot_receiver,
195 "dbgr",
196 "dbg!(&expr)",
197 &format!("dbg!(&{})", receiver_text),
198 )
199 .add_to(acc);
200
201 postfix_snippet(
202 ctx,
203 cap,
204 &dot_receiver,
195 "call", 205 "call",
196 "function(expr)", 206 "function(expr)",
197 &format!("${{1}}({})", receiver_text), 207 &format!("${{1}}({})", receiver_text),
@@ -266,6 +276,7 @@ fn main() {
266 sn box Box::new(expr) 276 sn box Box::new(expr)
267 sn call function(expr) 277 sn call function(expr)
268 sn dbg dbg!(expr) 278 sn dbg dbg!(expr)
279 sn dbgr dbg!(&expr)
269 sn if if expr {} 280 sn if if expr {}
270 sn match match expr {} 281 sn match match expr {}
271 sn not !expr 282 sn not !expr
@@ -290,6 +301,7 @@ fn main() {
290 sn box Box::new(expr) 301 sn box Box::new(expr)
291 sn call function(expr) 302 sn call function(expr)
292 sn dbg dbg!(expr) 303 sn dbg dbg!(expr)
304 sn dbgr dbg!(&expr)
293 sn match match expr {} 305 sn match match expr {}
294 sn ok Ok(expr) 306 sn ok Ok(expr)
295 sn ref &expr 307 sn ref &expr
diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs
index 161f59c1e..671b13328 100644
--- a/crates/ide/src/completion/completion_context.rs
+++ b/crates/ide/src/completion/completion_context.rs
@@ -1,7 +1,7 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use base_db::SourceDatabase; 3use base_db::SourceDatabase;
4use hir::{Semantics, SemanticsScope, Type}; 4use hir::{Local, ScopeDef, Semantics, SemanticsScope, Type};
5use ide_db::RootDatabase; 5use ide_db::RootDatabase;
6use syntax::{ 6use syntax::{
7 algo::{find_covering_element, find_node_at_offset}, 7 algo::{find_covering_element, find_node_at_offset},
@@ -91,6 +91,7 @@ pub(crate) struct CompletionContext<'a> {
91 pub(super) impl_as_prev_sibling: bool, 91 pub(super) impl_as_prev_sibling: bool,
92 pub(super) is_match_arm: bool, 92 pub(super) is_match_arm: bool,
93 pub(super) has_item_list_or_source_file_parent: bool, 93 pub(super) has_item_list_or_source_file_parent: bool,
94 pub(super) locals: Vec<(String, Local)>,
94} 95}
95 96
96impl<'a> CompletionContext<'a> { 97impl<'a> CompletionContext<'a> {
@@ -119,6 +120,12 @@ impl<'a> CompletionContext<'a> {
119 original_file.syntax().token_at_offset(position.offset).left_biased()?; 120 original_file.syntax().token_at_offset(position.offset).left_biased()?;
120 let token = sema.descend_into_macros(original_token.clone()); 121 let token = sema.descend_into_macros(original_token.clone());
121 let scope = sema.scope_at_offset(&token.parent(), position.offset); 122 let scope = sema.scope_at_offset(&token.parent(), position.offset);
123 let mut locals = vec![];
124 scope.process_all_names(&mut |name, scope| {
125 if let ScopeDef::Local(local) = scope {
126 locals.push((name.to_string(), local));
127 }
128 });
122 let mut ctx = CompletionContext { 129 let mut ctx = CompletionContext {
123 sema, 130 sema,
124 scope, 131 scope,
@@ -167,6 +174,7 @@ impl<'a> CompletionContext<'a> {
167 if_is_prev: false, 174 if_is_prev: false,
168 is_match_arm: false, 175 is_match_arm: false,
169 has_item_list_or_source_file_parent: false, 176 has_item_list_or_source_file_parent: false,
177 locals,
170 }; 178 };
171 179
172 let mut original_file = original_file.syntax().clone(); 180 let mut original_file = original_file.syntax().clone();
diff --git a/crates/ide/src/completion/presentation.rs b/crates/ide/src/completion/presentation.rs
index 24c507f9b..987cbfa7a 100644
--- a/crates/ide/src/completion/presentation.rs
+++ b/crates/ide/src/completion/presentation.rs
@@ -191,6 +191,17 @@ impl Completions {
191 func: hir::Function, 191 func: hir::Function,
192 local_name: Option<String>, 192 local_name: Option<String>,
193 ) { 193 ) {
194 fn add_arg(arg: &str, ty: &Type, ctx: &CompletionContext) -> String {
195 if let Some(derefed_ty) = ty.remove_ref() {
196 for (name, local) in ctx.locals.iter() {
197 if name == arg && local.ty(ctx.db) == derefed_ty {
198 return (if ty.is_mutable_reference() { "&mut " } else { "&" }).to_string()
199 + &arg.to_string();
200 }
201 }
202 }
203 arg.to_string()
204 };
194 let name = local_name.unwrap_or_else(|| func.name(ctx.db).to_string()); 205 let name = local_name.unwrap_or_else(|| func.name(ctx.db).to_string());
195 let ast_node = func.source(ctx.db).value; 206 let ast_node = func.source(ctx.db).value;
196 207
@@ -205,12 +216,20 @@ impl Completions {
205 .set_deprecated(is_deprecated(func, ctx.db)) 216 .set_deprecated(is_deprecated(func, ctx.db))
206 .detail(function_declaration(&ast_node)); 217 .detail(function_declaration(&ast_node));
207 218
219 let params_ty = func.params(ctx.db);
208 let params = ast_node 220 let params = ast_node
209 .param_list() 221 .param_list()
210 .into_iter() 222 .into_iter()
211 .flat_map(|it| it.params()) 223 .flat_map(|it| it.params())
212 .flat_map(|it| it.pat()) 224 .zip(params_ty)
213 .map(|pat| pat.to_string().trim_start_matches('_').into()) 225 .flat_map(|(it, param_ty)| {
226 if let Some(pat) = it.pat() {
227 let name = pat.to_string();
228 let arg = name.trim_start_matches("mut ").trim_start_matches('_');
229 return Some(add_arg(arg, param_ty.ty(), ctx));
230 }
231 None
232 })
214 .collect(); 233 .collect();
215 234
216 builder = builder.add_call_parens(ctx, name, Params::Named(params)); 235 builder = builder.add_call_parens(ctx, name, Params::Named(params));
@@ -864,6 +883,106 @@ fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_})$0 }
864 } 883 }
865 884
866 #[test] 885 #[test]
886 fn insert_ref_when_matching_local_in_scope() {
887 check_edit(
888 "ref_arg",
889 r#"
890struct Foo {}
891fn ref_arg(x: &Foo) {}
892fn main() {
893 let x = Foo {};
894 ref_ar<|>
895}
896"#,
897 r#"
898struct Foo {}
899fn ref_arg(x: &Foo) {}
900fn main() {
901 let x = Foo {};
902 ref_arg(${1:&x})$0
903}
904"#,
905 );
906 }
907
908 #[test]
909 fn insert_mut_ref_when_matching_local_in_scope() {
910 check_edit(
911 "ref_arg",
912 r#"
913struct Foo {}
914fn ref_arg(x: &mut Foo) {}
915fn main() {
916 let x = Foo {};
917 ref_ar<|>
918}
919"#,
920 r#"
921struct Foo {}
922fn ref_arg(x: &mut Foo) {}
923fn main() {
924 let x = Foo {};
925 ref_arg(${1:&mut x})$0
926}
927"#,
928 );
929 }
930
931 #[test]
932 fn insert_ref_when_matching_local_in_scope_for_method() {
933 check_edit(
934 "apply_foo",
935 r#"
936struct Foo {}
937struct Bar {}
938impl Bar {
939 fn apply_foo(&self, x: &Foo) {}
940}
941
942fn main() {
943 let x = Foo {};
944 let y = Bar {};
945 y.<|>
946}
947"#,
948 r#"
949struct Foo {}
950struct Bar {}
951impl Bar {
952 fn apply_foo(&self, x: &Foo) {}
953}
954
955fn main() {
956 let x = Foo {};
957 let y = Bar {};
958 y.apply_foo(${1:&x})$0
959}
960"#,
961 );
962 }
963
964 #[test]
965 fn trim_mut_keyword_in_func_completion() {
966 check_edit(
967 "take_mutably",
968 r#"
969fn take_mutably(mut x: &i32) {}
970
971fn main() {
972 take_m<|>
973}
974"#,
975 r#"
976fn take_mutably(mut x: &i32) {}
977
978fn main() {
979 take_mutably(${1:x})$0
980}
981"#,
982 );
983 }
984
985 #[test]
867 fn inserts_parens_for_tuple_enums() { 986 fn inserts_parens_for_tuple_enums() {
868 mark::check!(inserts_parens_for_tuple_enums); 987 mark::check!(inserts_parens_for_tuple_enums);
869 check_edit( 988 check_edit(
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index cde42024c..1d8a3c404 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -44,7 +44,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
44 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span> 44 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span>
45<span class="punctuation">}</span> 45<span class="punctuation">}</span>
46 46
47<span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">derive</span><span class="punctuation">(</span><span class="attribute">Clone</span><span class="punctuation">,</span><span class="attribute"> Debug</span><span class="punctuation">)</span><span class="attribute">]</span> 47
48<span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="punctuation">{</span> 48<span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="punctuation">{</span>
49 <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> 49 <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>
50 <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> 50 <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>
@@ -74,7 +74,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
74 <span class="punctuation">}</span> 74 <span class="punctuation">}</span>
75<span class="punctuation">}</span> 75<span class="punctuation">}</span>
76 76
77<span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">derive</span><span class="punctuation">(</span><span class="attribute">Copy</span><span class="punctuation">,</span><span class="attribute"> Clone</span><span class="punctuation">)</span><span class="attribute">]</span> 77<span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">derive</span><span class="punctuation">(</span><span class="attribute">Copy</span><span class="punctuation">)</span><span class="attribute">]</span>
78<span class="keyword">struct</span> <span class="struct declaration">FooCopy</span> <span class="punctuation">{</span> 78<span class="keyword">struct</span> <span class="struct declaration">FooCopy</span> <span class="punctuation">{</span>
79 <span class="field declaration">x</span><span class="punctuation">:</span> <span class="builtin_type">u32</span><span class="punctuation">,</span> 79 <span class="field declaration">x</span><span class="punctuation">:</span> <span class="builtin_type">u32</span><span class="punctuation">,</span>
80<span class="punctuation">}</span> 80<span class="punctuation">}</span>
@@ -144,7 +144,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
144 <span class="variable">y</span><span class="punctuation">;</span> 144 <span class="variable">y</span><span class="punctuation">;</span>
145 145
146 <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> 146 <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>
147 <span class="keyword">let</span> <span class="variable declaration">foo2</span> <span class="operator">=</span> <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="unresolved_reference">clone</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 147 <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>
148 <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 148 <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
149 <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 149 <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
150 <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function consuming">baz</span><span class="punctuation">(</span><span class="variable consuming">foo2</span><span class="punctuation">)</span><span class="punctuation">;</span> 150 <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function consuming">baz</span><span class="punctuation">(</span><span class="variable consuming">foo2</span><span class="punctuation">)</span><span class="punctuation">;</span>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 57d4e1252..211e62ea1 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -18,7 +18,7 @@ pub mod marker {
18 pub trait Copy {} 18 pub trait Copy {}
19} 19}
20 20
21#[derive(Clone, Debug)] 21
22struct Foo { 22struct Foo {
23 pub x: i32, 23 pub x: i32,
24 pub y: i32, 24 pub y: i32,
@@ -48,7 +48,7 @@ impl Foo {
48 } 48 }
49} 49}
50 50
51#[derive(Copy, Clone)] 51#[derive(Copy)]
52struct FooCopy { 52struct FooCopy {
53 x: u32, 53 x: u32,
54} 54}
@@ -118,7 +118,7 @@ fn main() {
118 y; 118 y;
119 119
120 let mut foo = Foo { x, y: x }; 120 let mut foo = Foo { x, y: x };
121 let foo2 = foo.clone(); 121 let foo2 = Foo { x, y: x };
122 foo.quop(); 122 foo.quop();
123 foo.qux(); 123 foo.qux();
124 foo.baz(foo2); 124 foo.baz(foo2);
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index a8ad917fb..d987b2500 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -636,7 +636,10 @@ impl<'a> TreeSink for TtTreeSink<'a> {
636 let (text, id) = match leaf { 636 let (text, id) = match leaf {
637 tt::Leaf::Ident(ident) => (ident.text.clone(), ident.id), 637 tt::Leaf::Ident(ident) => (ident.text.clone(), ident.id),
638 tt::Leaf::Punct(punct) => { 638 tt::Leaf::Punct(punct) => {
639 (SmolStr::new_inline_from_ascii(1, &[punct.char as u8]), punct.id) 639 assert!(punct.char.is_ascii());
640 let char = &(punct.char as u8);
641 let text = std::str::from_utf8(std::slice::from_ref(char)).unwrap();
642 (SmolStr::new_inline(text), punct.id)
640 } 643 }
641 tt::Leaf::Literal(lit) => (lit.text.clone(), lit.id), 644 tt::Leaf::Literal(lit) => (lit.text.clone(), lit.id),
642 }; 645 };
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 0fbb9cb0d..631ffc4a7 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -21,7 +21,7 @@ env_logger = { version = "0.7.1", default-features = false }
21itertools = "0.9.0" 21itertools = "0.9.0"
22jod-thread = "0.1.0" 22jod-thread = "0.1.0"
23log = "0.4.8" 23log = "0.4.8"
24lsp-types = { version = "0.81.0", features = ["proposed"] } 24lsp-types = { version = "0.82.0", features = ["proposed"] }
25parking_lot = "0.11.0" 25parking_lot = "0.11.0"
26pico-args = "0.3.1" 26pico-args = "0.3.1"
27oorandom = "11.1.2" 27oorandom = "11.1.2"
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index de4bc2813..c589afeaf 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -5,7 +5,7 @@ use lsp_types::{
5 CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions, 5 CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions,
6 CodeActionProviderCapability, CodeLensOptions, CompletionOptions, 6 CodeActionProviderCapability, CodeLensOptions, CompletionOptions,
7 DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, HoverProviderCapability, 7 DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, HoverProviderCapability,
8 ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions, 8 ImplementationProviderCapability, OneOf, RenameOptions, SaveOptions,
9 SelectionRangeProviderCapability, SemanticTokensFullOptions, SemanticTokensLegend, 9 SelectionRangeProviderCapability, SemanticTokensFullOptions, SemanticTokensLegend,
10 SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability, 10 SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability,
11 TextDocumentSyncKind, TextDocumentSyncOptions, TypeDefinitionProviderCapability, 11 TextDocumentSyncKind, TextDocumentSyncOptions, TypeDefinitionProviderCapability,
@@ -42,16 +42,16 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
42 work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None }, 42 work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
43 }), 43 }),
44 declaration_provider: None, 44 declaration_provider: None,
45 definition_provider: Some(true), 45 definition_provider: Some(OneOf::Left(true)),
46 type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)), 46 type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)),
47 implementation_provider: Some(ImplementationProviderCapability::Simple(true)), 47 implementation_provider: Some(ImplementationProviderCapability::Simple(true)),
48 references_provider: Some(true), 48 references_provider: Some(OneOf::Left(true)),
49 document_highlight_provider: Some(true), 49 document_highlight_provider: Some(OneOf::Left(true)),
50 document_symbol_provider: Some(true), 50 document_symbol_provider: Some(OneOf::Left(true)),
51 workspace_symbol_provider: Some(true), 51 workspace_symbol_provider: Some(true),
52 code_action_provider: Some(code_action_provider), 52 code_action_provider: Some(code_action_provider),
53 code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }), 53 code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }),
54 document_formatting_provider: Some(true), 54 document_formatting_provider: Some(OneOf::Left(true)),
55 document_range_formatting_provider: None, 55 document_range_formatting_provider: None,
56 document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions { 56 document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
57 first_trigger_character: "=".to_string(), 57 first_trigger_character: "=".to_string(),
@@ -60,7 +60,7 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
60 selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)), 60 selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)),
61 semantic_highlighting: None, 61 semantic_highlighting: None,
62 folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)), 62 folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
63 rename_provider: Some(RenameProviderCapability::Options(RenameOptions { 63 rename_provider: Some(OneOf::Right(RenameOptions {
64 prepare_provider: Some(true), 64 prepare_provider: Some(true),
65 work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None }, 65 work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
66 })), 66 })),
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index d3e6b23ae..7d85b36cb 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -66,6 +66,7 @@ To disable this notification put the following to `settings.json`
66The server binary is stored in: 66The server binary is stored in:
67 67
68* Linux: `~/.config/Code/User/globalStorage/matklad.rust-analyzer` 68* Linux: `~/.config/Code/User/globalStorage/matklad.rust-analyzer`
69* Linux (Remote, such as WSL): `~/.vscode-server/data/User/globalStorage/matklad.rust-analyzer`
69* macOS: `~/Library/Application\ Support/Code/User/globalStorage/matklad.rust-analyzer` 70* macOS: `~/Library/Application\ Support/Code/User/globalStorage/matklad.rust-analyzer`
70* Windows: `%APPDATA%\Code\User\globalStorage\matklad.rust-analyzer` 71* Windows: `%APPDATA%\Code\User\globalStorage\matklad.rust-analyzer`
71 72
diff --git a/editors/code/package.json b/editors/code/package.json
index c57fbdda2..132664926 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -159,6 +159,11 @@
159 "category": "Rust Analyzer" 159 "category": "Rust Analyzer"
160 }, 160 },
161 { 161 {
162 "command": "rust-analyzer.updateGithubToken",
163 "title": "Update Github API token",
164 "category": "Rust Analyzer"
165 },
166 {
162 "command": "rust-analyzer.onEnter", 167 "command": "rust-analyzer.onEnter",
163 "title": "Enhanced enter key", 168 "title": "Enhanced enter key",
164 "category": "Rust Analyzer" 169 "category": "Rust Analyzer"
@@ -985,6 +990,10 @@
985 "when": "inRustProject" 990 "when": "inRustProject"
986 }, 991 },
987 { 992 {
993 "command": "rust-analyzer.updateGithubToken",
994 "when": "inRustProject"
995 },
996 {
988 "command": "rust-analyzer.onEnter", 997 "command": "rust-analyzer.onEnter",
989 "when": "inRustProject" 998 "when": "inRustProject"
990 }, 999 },
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index bd99d696a..2896d90ac 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -95,6 +95,10 @@ async function tryActivate(context: vscode.ExtensionContext) {
95 await activate(context).catch(log.error); 95 await activate(context).catch(log.error);
96 }); 96 });
97 97
98 ctx.registerCommand('updateGithubToken', ctx => async () => {
99 await queryForGithubToken(new PersistentState(ctx.globalState));
100 });
101
98 ctx.registerCommand('analyzerStatus', commands.analyzerStatus); 102 ctx.registerCommand('analyzerStatus', commands.analyzerStatus);
99 ctx.registerCommand('memoryUsage', commands.memoryUsage); 103 ctx.registerCommand('memoryUsage', commands.memoryUsage);
100 ctx.registerCommand('reloadWorkspace', commands.reloadWorkspace); 104 ctx.registerCommand('reloadWorkspace', commands.reloadWorkspace);
@@ -173,7 +177,9 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi
173 if (!shouldCheckForNewNightly) return; 177 if (!shouldCheckForNewNightly) return;
174 } 178 }
175 179
176 const release = await fetchRelease("nightly").catch((e) => { 180 const release = await downloadWithRetryDialog(state, async () => {
181 return await fetchRelease("nightly", state.githubToken);
182 }).catch((e) => {
177 log.error(e); 183 log.error(e);
178 if (state.releaseId === undefined) { // Show error only for the initial download 184 if (state.releaseId === undefined) { // Show error only for the initial download
179 vscode.window.showErrorMessage(`Failed to download rust-analyzer nightly ${e}`); 185 vscode.window.showErrorMessage(`Failed to download rust-analyzer nightly ${e}`);
@@ -192,10 +198,14 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi
192 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); 198 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`);
193 199
194 const dest = path.join(config.globalStoragePath, "rust-analyzer.vsix"); 200 const dest = path.join(config.globalStoragePath, "rust-analyzer.vsix");
195 await download({ 201
196 url: artifact.browser_download_url, 202 await downloadWithRetryDialog(state, async () => {
197 dest, 203 await download({
198 progressTitle: "Downloading rust-analyzer extension", 204 url: artifact.browser_download_url,
205 dest,
206 progressTitle: "Downloading rust-analyzer extension",
207 overwrite: true,
208 });
199 }); 209 });
200 210
201 await vscode.commands.executeCommand("workbench.extensions.installExtension", vscode.Uri.file(dest)); 211 await vscode.commands.executeCommand("workbench.extensions.installExtension", vscode.Uri.file(dest));
@@ -308,21 +318,22 @@ async function getServer(config: Config, state: PersistentState): Promise<string
308 if (userResponse !== "Download now") return dest; 318 if (userResponse !== "Download now") return dest;
309 } 319 }
310 320
311 const release = await fetchRelease(config.package.releaseTag); 321 const releaseTag = config.package.releaseTag;
322 const release = await downloadWithRetryDialog(state, async () => {
323 return await fetchRelease(releaseTag, state.githubToken);
324 });
312 const artifact = release.assets.find(artifact => artifact.name === `rust-analyzer-${platform}.gz`); 325 const artifact = release.assets.find(artifact => artifact.name === `rust-analyzer-${platform}.gz`);
313 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); 326 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`);
314 327
315 // Unlinking the exe file before moving new one on its place should prevent ETXTBSY error. 328 await downloadWithRetryDialog(state, async () => {
316 await fs.unlink(dest).catch(err => { 329 await download({
317 if (err.code !== "ENOENT") throw err; 330 url: artifact.browser_download_url,
318 }); 331 dest,
319 332 progressTitle: "Downloading rust-analyzer server",
320 await download({ 333 gunzip: true,
321 url: artifact.browser_download_url, 334 mode: 0o755,
322 dest, 335 overwrite: true,
323 progressTitle: "Downloading rust-analyzer server", 336 });
324 gunzip: true,
325 mode: 0o755
326 }); 337 });
327 338
328 // Patching executable if that's NixOS. 339 // Patching executable if that's NixOS.
@@ -333,3 +344,56 @@ async function getServer(config: Config, state: PersistentState): Promise<string
333 await state.updateServerVersion(config.package.version); 344 await state.updateServerVersion(config.package.version);
334 return dest; 345 return dest;
335} 346}
347
348async function downloadWithRetryDialog<T>(state: PersistentState, downloadFunc: () => Promise<T>): Promise<T> {
349 while (true) {
350 try {
351 return await downloadFunc();
352 } catch (e) {
353 const selected = await vscode.window.showErrorMessage("Failed to download: " + e.message, {}, {
354 title: "Update Github Auth Token",
355 updateToken: true,
356 }, {
357 title: "Retry download",
358 retry: true,
359 }, {
360 title: "Dismiss",
361 });
362
363 if (selected?.updateToken) {
364 await queryForGithubToken(state);
365 continue;
366 } else if (selected?.retry) {
367 continue;
368 }
369 throw e;
370 };
371 }
372}
373
374async function queryForGithubToken(state: PersistentState): Promise<void> {
375 const githubTokenOptions: vscode.InputBoxOptions = {
376 value: state.githubToken,
377 password: true,
378 prompt: `
379 This dialog allows to store a Github authorization token.
380 The usage of an authorization token will increase the rate
381 limit on the use of Github APIs and can thereby prevent getting
382 throttled.
383 Auth tokens can be created at https://github.com/settings/tokens`,
384 };
385
386 const newToken = await vscode.window.showInputBox(githubTokenOptions);
387 if (newToken === undefined) {
388 // The user aborted the dialog => Do not update the stored token
389 return;
390 }
391
392 if (newToken === "") {
393 log.info("Clearing github token");
394 await state.updateGithubToken(undefined);
395 } else {
396 log.info("Storing new github token");
397 await state.updateGithubToken(newToken);
398 }
399}
diff --git a/editors/code/src/net.ts b/editors/code/src/net.ts
index 5eba2728d..9ba17b7b5 100644
--- a/editors/code/src/net.ts
+++ b/editors/code/src/net.ts
@@ -18,7 +18,8 @@ const OWNER = "rust-analyzer";
18const REPO = "rust-analyzer"; 18const REPO = "rust-analyzer";
19 19
20export async function fetchRelease( 20export async function fetchRelease(
21 releaseTag: string 21 releaseTag: string,
22 githubToken: string | null | undefined,
22): Promise<GithubRelease> { 23): Promise<GithubRelease> {
23 24
24 const apiEndpointPath = `/repos/${OWNER}/${REPO}/releases/tags/${releaseTag}`; 25 const apiEndpointPath = `/repos/${OWNER}/${REPO}/releases/tags/${releaseTag}`;
@@ -27,7 +28,12 @@ export async function fetchRelease(
27 28
28 log.debug("Issuing request for released artifacts metadata to", requestUrl); 29 log.debug("Issuing request for released artifacts metadata to", requestUrl);
29 30
30 const response = await fetch(requestUrl, { headers: { Accept: "application/vnd.github.v3+json" } }); 31 const headers: Record<string, string> = { Accept: "application/vnd.github.v3+json" };
32 if (githubToken != null) {
33 headers.Authorization = "token " + githubToken;
34 }
35
36 const response = await fetch(requestUrl, { headers: headers });
31 37
32 if (!response.ok) { 38 if (!response.ok) {
33 log.error("Error fetching artifact release info", { 39 log.error("Error fetching artifact release info", {
@@ -70,6 +76,7 @@ interface DownloadOpts {
70 dest: string; 76 dest: string;
71 mode?: number; 77 mode?: number;
72 gunzip?: boolean; 78 gunzip?: boolean;
79 overwrite?: boolean;
73} 80}
74 81
75export async function download(opts: DownloadOpts) { 82export async function download(opts: DownloadOpts) {
@@ -79,6 +86,13 @@ export async function download(opts: DownloadOpts) {
79 const randomHex = crypto.randomBytes(5).toString("hex"); 86 const randomHex = crypto.randomBytes(5).toString("hex");
80 const tempFile = path.join(dest.dir, `${dest.name}${randomHex}`); 87 const tempFile = path.join(dest.dir, `${dest.name}${randomHex}`);
81 88
89 if (opts.overwrite) {
90 // Unlinking the exe file before moving new one on its place should prevent ETXTBSY error.
91 await fs.promises.unlink(opts.dest).catch(err => {
92 if (err.code !== "ENOENT") throw err;
93 });
94 }
95
82 await vscode.window.withProgress( 96 await vscode.window.withProgress(
83 { 97 {
84 location: vscode.ProgressLocation.Notification, 98 location: vscode.ProgressLocation.Notification,
diff --git a/editors/code/src/persistent_state.ts b/editors/code/src/persistent_state.ts
index 5705eed81..afb652589 100644
--- a/editors/code/src/persistent_state.ts
+++ b/editors/code/src/persistent_state.ts
@@ -38,4 +38,15 @@ export class PersistentState {
38 async updateServerVersion(value: string | undefined) { 38 async updateServerVersion(value: string | undefined) {
39 await this.globalState.update("serverVersion", value); 39 await this.globalState.update("serverVersion", value);
40 } 40 }
41
42 /**
43 * Github authorization token.
44 * This is used for API requests against the Github API.
45 */
46 get githubToken(): string | undefined {
47 return this.globalState.get("githubToken");
48 }
49 async updateGithubToken(value: string | undefined) {
50 await this.globalState.update("githubToken", value);
51 }
41} 52}