aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock16
-rw-r--r--crates/ra_assists/src/ast_transform.rs25
-rw-r--r--crates/ra_fmt/src/lib.rs36
-rw-r--r--crates/ra_hir_ty/Cargo.toml6
-rw-r--r--crates/ra_hir_ty/src/traits.rs14
-rw-r--r--crates/ra_hir_ty/src/traits/chalk.rs107
-rw-r--r--crates/ra_ide/src/call_hierarchy.rs20
-rw-r--r--crates/ra_ide/src/display/navigation_target.rs2
-rw-r--r--crates/ra_ide/src/extend_selection.rs24
-rw-r--r--crates/ra_ide/src/join_lines.rs25
-rw-r--r--crates/ra_ide/src/lib.rs2
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs52
-rw-r--r--crates/ra_syntax/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/caps.rs24
-rw-r--r--crates/rust-analyzer/src/conv.rs79
-rw-r--r--crates/rust-analyzer/src/lib.rs1
-rw-r--r--crates/rust-analyzer/src/main_loop.rs1
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs28
-rw-r--r--crates/rust-analyzer/src/req.rs6
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs94
-rw-r--r--editors/code/.eslintrc.js3
-rw-r--r--editors/code/package-lock.json6
-rw-r--r--editors/code/package.json2
-rw-r--r--editors/code/src/commands/analyzer_status.ts6
-rw-r--r--editors/code/src/commands/expand_macro.ts18
-rw-r--r--editors/code/src/commands/index.ts9
-rw-r--r--editors/code/src/commands/join_lines.ts17
-rw-r--r--editors/code/src/commands/matching_brace.ts15
-rw-r--r--editors/code/src/commands/on_enter.ts21
-rw-r--r--editors/code/src/commands/parent_module.ts10
-rw-r--r--editors/code/src/commands/runnables.ts29
-rw-r--r--editors/code/src/commands/ssr.ts16
-rw-r--r--editors/code/src/commands/syntax_tree.ts27
-rw-r--r--editors/code/src/highlighting.ts60
-rw-r--r--editors/code/src/inlay_hints.ts31
-rw-r--r--editors/code/src/installation/fetch_artifact_release_info.ts58
-rw-r--r--editors/code/src/installation/server.ts2
-rw-r--r--editors/code/src/rust-analyzer-api.ts117
-rw-r--r--editors/code/src/source_change.ts9
-rw-r--r--editors/code/src/util.ts16
40 files changed, 659 insertions, 377 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 10564d451..e5400f5eb 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -129,7 +129,7 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
129[[package]] 129[[package]]
130name = "chalk-derive" 130name = "chalk-derive"
131version = "0.1.0" 131version = "0.1.0"
132source = "git+https://github.com/rust-lang/chalk.git?rev=af48f302a1f571b3ca418f7c5aa639a144a34f75#af48f302a1f571b3ca418f7c5aa639a144a34f75" 132source = "git+https://github.com/rust-lang/chalk.git?rev=2939913fb7bb94ac2a6721087dc086be11410702#2939913fb7bb94ac2a6721087dc086be11410702"
133dependencies = [ 133dependencies = [
134 "proc-macro2", 134 "proc-macro2",
135 "quote", 135 "quote",
@@ -139,7 +139,7 @@ dependencies = [
139[[package]] 139[[package]]
140name = "chalk-engine" 140name = "chalk-engine"
141version = "0.9.0" 141version = "0.9.0"
142source = "git+https://github.com/rust-lang/chalk.git?rev=af48f302a1f571b3ca418f7c5aa639a144a34f75#af48f302a1f571b3ca418f7c5aa639a144a34f75" 142source = "git+https://github.com/rust-lang/chalk.git?rev=2939913fb7bb94ac2a6721087dc086be11410702#2939913fb7bb94ac2a6721087dc086be11410702"
143dependencies = [ 143dependencies = [
144 "chalk-macros", 144 "chalk-macros",
145 "rustc-hash", 145 "rustc-hash",
@@ -148,7 +148,7 @@ dependencies = [
148[[package]] 148[[package]]
149name = "chalk-ir" 149name = "chalk-ir"
150version = "0.1.0" 150version = "0.1.0"
151source = "git+https://github.com/rust-lang/chalk.git?rev=af48f302a1f571b3ca418f7c5aa639a144a34f75#af48f302a1f571b3ca418f7c5aa639a144a34f75" 151source = "git+https://github.com/rust-lang/chalk.git?rev=2939913fb7bb94ac2a6721087dc086be11410702#2939913fb7bb94ac2a6721087dc086be11410702"
152dependencies = [ 152dependencies = [
153 "chalk-derive", 153 "chalk-derive",
154 "chalk-engine", 154 "chalk-engine",
@@ -159,7 +159,7 @@ dependencies = [
159[[package]] 159[[package]]
160name = "chalk-macros" 160name = "chalk-macros"
161version = "0.1.1" 161version = "0.1.1"
162source = "git+https://github.com/rust-lang/chalk.git?rev=af48f302a1f571b3ca418f7c5aa639a144a34f75#af48f302a1f571b3ca418f7c5aa639a144a34f75" 162source = "git+https://github.com/rust-lang/chalk.git?rev=2939913fb7bb94ac2a6721087dc086be11410702#2939913fb7bb94ac2a6721087dc086be11410702"
163dependencies = [ 163dependencies = [
164 "lazy_static", 164 "lazy_static",
165] 165]
@@ -167,7 +167,7 @@ dependencies = [
167[[package]] 167[[package]]
168name = "chalk-rust-ir" 168name = "chalk-rust-ir"
169version = "0.1.0" 169version = "0.1.0"
170source = "git+https://github.com/rust-lang/chalk.git?rev=af48f302a1f571b3ca418f7c5aa639a144a34f75#af48f302a1f571b3ca418f7c5aa639a144a34f75" 170source = "git+https://github.com/rust-lang/chalk.git?rev=2939913fb7bb94ac2a6721087dc086be11410702#2939913fb7bb94ac2a6721087dc086be11410702"
171dependencies = [ 171dependencies = [
172 "chalk-derive", 172 "chalk-derive",
173 "chalk-engine", 173 "chalk-engine",
@@ -178,7 +178,7 @@ dependencies = [
178[[package]] 178[[package]]
179name = "chalk-solve" 179name = "chalk-solve"
180version = "0.1.0" 180version = "0.1.0"
181source = "git+https://github.com/rust-lang/chalk.git?rev=af48f302a1f571b3ca418f7c5aa639a144a34f75#af48f302a1f571b3ca418f7c5aa639a144a34f75" 181source = "git+https://github.com/rust-lang/chalk.git?rev=2939913fb7bb94ac2a6721087dc086be11410702#2939913fb7bb94ac2a6721087dc086be11410702"
182dependencies = [ 182dependencies = [
183 "chalk-derive", 183 "chalk-derive",
184 "chalk-engine", 184 "chalk-engine",
@@ -1308,9 +1308,9 @@ dependencies = [
1308 1308
1309[[package]] 1309[[package]]
1310name = "rowan" 1310name = "rowan"
1311version = "0.9.0" 1311version = "0.9.1"
1312source = "registry+https://github.com/rust-lang/crates.io-index" 1312source = "registry+https://github.com/rust-lang/crates.io-index"
1313checksum = "6d03d4eff7a4e8dcc362e4c06bb2b1b33af4bcd64336c7f40a31a05850336b6c" 1313checksum = "1ea7cadf87a9d8432e85cb4eb86bd2e765ace60c24ef86e79084dcae5d1c5a19"
1314dependencies = [ 1314dependencies = [
1315 "rustc-hash", 1315 "rustc-hash",
1316 "smol_str", 1316 "smol_str",
diff --git a/crates/ra_assists/src/ast_transform.rs b/crates/ra_assists/src/ast_transform.rs
index 56b7588ef..c6d15af5f 100644
--- a/crates/ra_assists/src/ast_transform.rs
+++ b/crates/ra_assists/src/ast_transform.rs
@@ -1,7 +1,8 @@
1//! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined. 1//! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined.
2use rustc_hash::FxHashMap; 2use rustc_hash::FxHashMap;
3 3
4use hir::{db::HirDatabase, InFile, PathResolution}; 4use hir::{InFile, PathResolution};
5use ra_ide_db::RootDatabase;
5use ra_syntax::ast::{self, AstNode}; 6use ra_syntax::ast::{self, AstNode};
6 7
7pub trait AstTransform<'a> { 8pub trait AstTransform<'a> {
@@ -33,18 +34,18 @@ impl<'a> AstTransform<'a> for NullTransformer {
33 } 34 }
34} 35}
35 36
36pub struct SubstituteTypeParams<'a, DB: HirDatabase> { 37pub struct SubstituteTypeParams<'a> {
37 db: &'a DB, 38 db: &'a RootDatabase,
38 substs: FxHashMap<hir::TypeParam, ast::TypeRef>, 39 substs: FxHashMap<hir::TypeParam, ast::TypeRef>,
39 previous: Box<dyn AstTransform<'a> + 'a>, 40 previous: Box<dyn AstTransform<'a> + 'a>,
40} 41}
41 42
42impl<'a, DB: HirDatabase> SubstituteTypeParams<'a, DB> { 43impl<'a> SubstituteTypeParams<'a> {
43 pub fn for_trait_impl( 44 pub fn for_trait_impl(
44 db: &'a DB, 45 db: &'a RootDatabase,
45 trait_: hir::Trait, 46 trait_: hir::Trait,
46 impl_block: ast::ImplBlock, 47 impl_block: ast::ImplBlock,
47 ) -> SubstituteTypeParams<'a, DB> { 48 ) -> SubstituteTypeParams<'a> {
48 let substs = get_syntactic_substs(impl_block).unwrap_or_default(); 49 let substs = get_syntactic_substs(impl_block).unwrap_or_default();
49 let generic_def: hir::GenericDef = trait_.into(); 50 let generic_def: hir::GenericDef = trait_.into();
50 let substs_by_param: FxHashMap<_, _> = generic_def 51 let substs_by_param: FxHashMap<_, _> = generic_def
@@ -95,7 +96,7 @@ impl<'a, DB: HirDatabase> SubstituteTypeParams<'a, DB> {
95 } 96 }
96} 97}
97 98
98impl<'a, DB: HirDatabase> AstTransform<'a> for SubstituteTypeParams<'a, DB> { 99impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> {
99 fn get_substitution( 100 fn get_substitution(
100 &self, 101 &self,
101 node: InFile<&ra_syntax::SyntaxNode>, 102 node: InFile<&ra_syntax::SyntaxNode>,
@@ -107,14 +108,14 @@ impl<'a, DB: HirDatabase> AstTransform<'a> for SubstituteTypeParams<'a, DB> {
107 } 108 }
108} 109}
109 110
110pub struct QualifyPaths<'a, DB: HirDatabase> { 111pub struct QualifyPaths<'a> {
111 db: &'a DB, 112 db: &'a RootDatabase,
112 from: Option<hir::Module>, 113 from: Option<hir::Module>,
113 previous: Box<dyn AstTransform<'a> + 'a>, 114 previous: Box<dyn AstTransform<'a> + 'a>,
114} 115}
115 116
116impl<'a, DB: HirDatabase> QualifyPaths<'a, DB> { 117impl<'a> QualifyPaths<'a> {
117 pub fn new(db: &'a DB, from: Option<hir::Module>) -> Self { 118 pub fn new(db: &'a RootDatabase, from: Option<hir::Module>) -> Self {
118 Self { db, from, previous: Box::new(NullTransformer) } 119 Self { db, from, previous: Box::new(NullTransformer) }
119 } 120 }
120 121
@@ -168,7 +169,7 @@ pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: InFile<N>
168 N::cast(result).unwrap() 169 N::cast(result).unwrap()
169} 170}
170 171
171impl<'a, DB: HirDatabase> AstTransform<'a> for QualifyPaths<'a, DB> { 172impl<'a> AstTransform<'a> for QualifyPaths<'a> {
172 fn get_substitution( 173 fn get_substitution(
173 &self, 174 &self,
174 node: InFile<&ra_syntax::SyntaxNode>, 175 node: InFile<&ra_syntax::SyntaxNode>,
diff --git a/crates/ra_fmt/src/lib.rs b/crates/ra_fmt/src/lib.rs
index 4bca27b5c..db27f9b83 100644
--- a/crates/ra_fmt/src/lib.rs
+++ b/crates/ra_fmt/src/lib.rs
@@ -43,15 +43,35 @@ pub fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr {
43 43
44pub fn extract_trivial_expression(block: &ast::BlockExpr) -> Option<ast::Expr> { 44pub fn extract_trivial_expression(block: &ast::BlockExpr) -> Option<ast::Expr> {
45 let block = block.block()?; 45 let block = block.block()?;
46 let expr = block.expr()?; 46 let has_anything_else = |thing: &SyntaxNode| -> bool {
47 let non_trivial_children = block.syntax().children().filter(|it| match it.kind() { 47 let mut non_trivial_children =
48 WHITESPACE | T!['{'] | T!['}'] => false, 48 block.syntax().children_with_tokens().filter(|it| match it.kind() {
49 _ => it != expr.syntax(), 49 WHITESPACE | T!['{'] | T!['}'] => false,
50 }); 50 _ => it.as_node() != Some(thing),
51 if non_trivial_children.count() > 0 { 51 });
52 return None; 52 non_trivial_children.next().is_some()
53 };
54
55 if let Some(expr) = block.expr() {
56 if has_anything_else(expr.syntax()) {
57 return None;
58 }
59 return Some(expr);
60 } else {
61 // Unwrap `{ continue; }`
62 let (stmt,) = block.statements().next_tuple()?;
63 if has_anything_else(stmt.syntax()) {
64 return None;
65 }
66 if let ast::Stmt::ExprStmt(expr_stmt) = stmt {
67 let expr = expr_stmt.expr()?;
68 match expr.syntax().kind() {
69 CONTINUE_EXPR | BREAK_EXPR | RETURN_EXPR => return Some(expr),
70 _ => (),
71 }
72 }
53 } 73 }
54 Some(expr) 74 None
55} 75}
56 76
57pub fn compute_ws(left: SyntaxKind, right: SyntaxKind) -> &'static str { 77pub fn compute_ws(left: SyntaxKind, right: SyntaxKind) -> &'static str {
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml
index 49cafc539..99e2fe1bf 100644
--- a/crates/ra_hir_ty/Cargo.toml
+++ b/crates/ra_hir_ty/Cargo.toml
@@ -21,9 +21,9 @@ ra_prof = { path = "../ra_prof" }
21ra_syntax = { path = "../ra_syntax" } 21ra_syntax = { path = "../ra_syntax" }
22test_utils = { path = "../test_utils" } 22test_utils = { path = "../test_utils" }
23 23
24chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "af48f302a1f571b3ca418f7c5aa639a144a34f75" } 24chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "2939913fb7bb94ac2a6721087dc086be11410702" }
25chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "af48f302a1f571b3ca418f7c5aa639a144a34f75" } 25chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "2939913fb7bb94ac2a6721087dc086be11410702" }
26chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "af48f302a1f571b3ca418f7c5aa639a144a34f75" } 26chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "2939913fb7bb94ac2a6721087dc086be11410702" }
27 27
28lalrpop-intern = "0.15.1" 28lalrpop-intern = "0.15.1"
29 29
diff --git a/crates/ra_hir_ty/src/traits.rs b/crates/ra_hir_ty/src/traits.rs
index 2317fcac3..17aef9490 100644
--- a/crates/ra_hir_ty/src/traits.rs
+++ b/crates/ra_hir_ty/src/traits.rs
@@ -14,7 +14,7 @@ use crate::db::HirDatabase;
14 14
15use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk}; 15use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk};
16 16
17use self::chalk::{from_chalk, ToChalk, TypeFamily}; 17use self::chalk::{from_chalk, Interner, ToChalk};
18 18
19pub(crate) mod chalk; 19pub(crate) mod chalk;
20mod builtin; 20mod builtin;
@@ -22,7 +22,7 @@ mod builtin;
22#[derive(Debug, Clone)] 22#[derive(Debug, Clone)]
23pub struct TraitSolver { 23pub struct TraitSolver {
24 krate: CrateId, 24 krate: CrateId,
25 inner: Arc<Mutex<chalk_solve::Solver<TypeFamily>>>, 25 inner: Arc<Mutex<chalk_solve::Solver<Interner>>>,
26} 26}
27 27
28/// We need eq for salsa 28/// We need eq for salsa
@@ -38,8 +38,8 @@ impl TraitSolver {
38 fn solve( 38 fn solve(
39 &self, 39 &self,
40 db: &impl HirDatabase, 40 db: &impl HirDatabase,
41 goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal<TypeFamily>>>, 41 goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal<Interner>>>,
42 ) -> Option<chalk_solve::Solution<TypeFamily>> { 42 ) -> Option<chalk_solve::Solution<Interner>> {
43 let context = ChalkContext { db, krate: self.krate }; 43 let context = ChalkContext { db, krate: self.krate };
44 log::debug!("solve goal: {:?}", goal); 44 log::debug!("solve goal: {:?}", goal);
45 let mut solver = match self.inner.lock() { 45 let mut solver = match self.inner.lock() {
@@ -110,7 +110,7 @@ pub(crate) fn trait_solver_query(
110 TraitSolver { krate, inner: Arc::new(Mutex::new(create_chalk_solver())) } 110 TraitSolver { krate, inner: Arc::new(Mutex::new(create_chalk_solver())) }
111} 111}
112 112
113fn create_chalk_solver() -> chalk_solve::Solver<TypeFamily> { 113fn create_chalk_solver() -> chalk_solve::Solver<Interner> {
114 let solver_choice = 114 let solver_choice =
115 chalk_solve::SolverChoice::SLG { max_size: CHALK_SOLVER_MAX_SIZE, expected_answers: None }; 115 chalk_solve::SolverChoice::SLG { max_size: CHALK_SOLVER_MAX_SIZE, expected_answers: None };
116 solver_choice.into_solver() 116 solver_choice.into_solver()
@@ -242,9 +242,9 @@ pub(crate) fn trait_solve_query(
242 242
243fn solution_from_chalk( 243fn solution_from_chalk(
244 db: &impl HirDatabase, 244 db: &impl HirDatabase,
245 solution: chalk_solve::Solution<TypeFamily>, 245 solution: chalk_solve::Solution<Interner>,
246) -> Solution { 246) -> Solution {
247 let convert_subst = |subst: chalk_ir::Canonical<chalk_ir::Substitution<TypeFamily>>| { 247 let convert_subst = |subst: chalk_ir::Canonical<chalk_ir::Substitution<Interner>>| {
248 let value = subst 248 let value = subst
249 .value 249 .value
250 .into_iter() 250 .into_iter()
diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs
index e1e430aeb..5b6c1a62e 100644
--- a/crates/ra_hir_ty/src/traits/chalk.rs
+++ b/crates/ra_hir_ty/src/traits/chalk.rs
@@ -3,7 +3,7 @@ use std::{fmt, sync::Arc};
3 3
4use log::debug; 4use log::debug;
5 5
6use chalk_ir::{cast::Cast, GoalData, Parameter, PlaceholderIndex, TypeName, UniverseIndex}; 6use chalk_ir::{cast::Cast, Goal, GoalData, Parameter, PlaceholderIndex, TypeName, UniverseIndex};
7 7
8use hir_def::{AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, TypeAliasId}; 8use hir_def::{AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, TypeAliasId};
9use ra_db::{ 9use ra_db::{
@@ -18,13 +18,14 @@ use crate::{
18}; 18};
19 19
20#[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] 20#[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
21pub struct TypeFamily {} 21pub struct Interner {}
22 22
23impl chalk_ir::family::TypeFamily for TypeFamily { 23impl chalk_ir::interner::Interner for Interner {
24 type InternedType = Box<chalk_ir::TyData<Self>>; 24 type InternedType = Box<chalk_ir::TyData<Self>>;
25 type InternedLifetime = chalk_ir::LifetimeData<Self>; 25 type InternedLifetime = chalk_ir::LifetimeData<Self>;
26 type InternedParameter = chalk_ir::ParameterData<Self>; 26 type InternedParameter = chalk_ir::ParameterData<Self>;
27 type InternedGoal = Arc<GoalData<Self>>; 27 type InternedGoal = Arc<GoalData<Self>>;
28 type InternedGoals = Vec<Goal<Self>>;
28 type InternedSubstitution = Vec<Parameter<Self>>; 29 type InternedSubstitution = Vec<Parameter<Self>>;
29 type DefId = InternId; 30 type DefId = InternId;
30 31
@@ -85,10 +86,18 @@ impl chalk_ir::family::TypeFamily for TypeFamily {
85 Arc::new(goal) 86 Arc::new(goal)
86 } 87 }
87 88
89 fn intern_goals(data: impl IntoIterator<Item = Goal<Self>>) -> Self::InternedGoals {
90 data.into_iter().collect()
91 }
92
88 fn goal_data(goal: &Arc<GoalData<Self>>) -> &GoalData<Self> { 93 fn goal_data(goal: &Arc<GoalData<Self>>) -> &GoalData<Self> {
89 goal 94 goal
90 } 95 }
91 96
97 fn goals_data(goals: &Vec<Goal<Interner>>) -> &[Goal<Interner>] {
98 goals
99 }
100
92 fn intern_substitution<E>( 101 fn intern_substitution<E>(
93 data: impl IntoIterator<Item = Result<Parameter<Self>, E>>, 102 data: impl IntoIterator<Item = Result<Parameter<Self>, E>>,
94 ) -> Result<Vec<Parameter<Self>>, E> { 103 ) -> Result<Vec<Parameter<Self>>, E> {
@@ -100,20 +109,20 @@ impl chalk_ir::family::TypeFamily for TypeFamily {
100 } 109 }
101} 110}
102 111
103impl chalk_ir::family::HasTypeFamily for TypeFamily { 112impl chalk_ir::interner::HasInterner for Interner {
104 type TypeFamily = Self; 113 type Interner = Self;
105} 114}
106 115
107pub type AssocTypeId = chalk_ir::AssocTypeId<TypeFamily>; 116pub type AssocTypeId = chalk_ir::AssocTypeId<Interner>;
108pub type AssociatedTyDatum = chalk_rust_ir::AssociatedTyDatum<TypeFamily>; 117pub type AssociatedTyDatum = chalk_rust_ir::AssociatedTyDatum<Interner>;
109pub type TraitId = chalk_ir::TraitId<TypeFamily>; 118pub type TraitId = chalk_ir::TraitId<Interner>;
110pub type TraitDatum = chalk_rust_ir::TraitDatum<TypeFamily>; 119pub type TraitDatum = chalk_rust_ir::TraitDatum<Interner>;
111pub type StructId = chalk_ir::StructId<TypeFamily>; 120pub type StructId = chalk_ir::StructId<Interner>;
112pub type StructDatum = chalk_rust_ir::StructDatum<TypeFamily>; 121pub type StructDatum = chalk_rust_ir::StructDatum<Interner>;
113pub type ImplId = chalk_ir::ImplId<TypeFamily>; 122pub type ImplId = chalk_ir::ImplId<Interner>;
114pub type ImplDatum = chalk_rust_ir::ImplDatum<TypeFamily>; 123pub type ImplDatum = chalk_rust_ir::ImplDatum<Interner>;
115pub type AssociatedTyValueId = chalk_rust_ir::AssociatedTyValueId; 124pub type AssociatedTyValueId = chalk_rust_ir::AssociatedTyValueId;
116pub type AssociatedTyValue = chalk_rust_ir::AssociatedTyValue<TypeFamily>; 125pub type AssociatedTyValue = chalk_rust_ir::AssociatedTyValue<Interner>;
117 126
118pub(super) trait ToChalk { 127pub(super) trait ToChalk {
119 type Chalk; 128 type Chalk;
@@ -129,8 +138,8 @@ where
129} 138}
130 139
131impl ToChalk for Ty { 140impl ToChalk for Ty {
132 type Chalk = chalk_ir::Ty<TypeFamily>; 141 type Chalk = chalk_ir::Ty<Interner>;
133 fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Ty<TypeFamily> { 142 fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Ty<Interner> {
134 match self { 143 match self {
135 Ty::Apply(apply_ty) => { 144 Ty::Apply(apply_ty) => {
136 let name = apply_ty.ctor.to_chalk(db); 145 let name = apply_ty.ctor.to_chalk(db);
@@ -148,7 +157,7 @@ impl ToChalk for Ty {
148 ui: UniverseIndex::ROOT, 157 ui: UniverseIndex::ROOT,
149 idx: interned_id.as_intern_id().as_usize(), 158 idx: interned_id.as_intern_id().as_usize(),
150 } 159 }
151 .to_ty::<TypeFamily>() 160 .to_ty::<Interner>()
152 } 161 }
153 Ty::Bound(idx) => chalk_ir::TyData::BoundVar(idx as usize).intern(), 162 Ty::Bound(idx) => chalk_ir::TyData::BoundVar(idx as usize).intern(),
154 Ty::Infer(_infer_ty) => panic!("uncanonicalized infer ty"), 163 Ty::Infer(_infer_ty) => panic!("uncanonicalized infer ty"),
@@ -169,7 +178,7 @@ impl ToChalk for Ty {
169 } 178 }
170 } 179 }
171 } 180 }
172 fn from_chalk(db: &impl HirDatabase, chalk: chalk_ir::Ty<TypeFamily>) -> Self { 181 fn from_chalk(db: &impl HirDatabase, chalk: chalk_ir::Ty<Interner>) -> Self {
173 match chalk.data().clone() { 182 match chalk.data().clone() {
174 chalk_ir::TyData::Apply(apply_ty) => match apply_ty.name { 183 chalk_ir::TyData::Apply(apply_ty) => match apply_ty.name {
175 TypeName::Error => Ty::Unknown, 184 TypeName::Error => Ty::Unknown,
@@ -205,13 +214,13 @@ impl ToChalk for Ty {
205} 214}
206 215
207impl ToChalk for Substs { 216impl ToChalk for Substs {
208 type Chalk = chalk_ir::Substitution<TypeFamily>; 217 type Chalk = chalk_ir::Substitution<Interner>;
209 218
210 fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Substitution<TypeFamily> { 219 fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Substitution<Interner> {
211 chalk_ir::Substitution::from(self.iter().map(|ty| ty.clone().to_chalk(db))) 220 chalk_ir::Substitution::from(self.iter().map(|ty| ty.clone().to_chalk(db)))
212 } 221 }
213 222
214 fn from_chalk(db: &impl HirDatabase, parameters: chalk_ir::Substitution<TypeFamily>) -> Substs { 223 fn from_chalk(db: &impl HirDatabase, parameters: chalk_ir::Substitution<Interner>) -> Substs {
215 let tys = parameters 224 let tys = parameters
216 .into_iter() 225 .into_iter()
217 .map(|p| match p.ty() { 226 .map(|p| match p.ty() {
@@ -224,15 +233,15 @@ impl ToChalk for Substs {
224} 233}
225 234
226impl ToChalk for TraitRef { 235impl ToChalk for TraitRef {
227 type Chalk = chalk_ir::TraitRef<TypeFamily>; 236 type Chalk = chalk_ir::TraitRef<Interner>;
228 237
229 fn to_chalk(self: TraitRef, db: &impl HirDatabase) -> chalk_ir::TraitRef<TypeFamily> { 238 fn to_chalk(self: TraitRef, db: &impl HirDatabase) -> chalk_ir::TraitRef<Interner> {
230 let trait_id = self.trait_.to_chalk(db); 239 let trait_id = self.trait_.to_chalk(db);
231 let substitution = self.substs.to_chalk(db); 240 let substitution = self.substs.to_chalk(db);
232 chalk_ir::TraitRef { trait_id, substitution } 241 chalk_ir::TraitRef { trait_id, substitution }
233 } 242 }
234 243
235 fn from_chalk(db: &impl HirDatabase, trait_ref: chalk_ir::TraitRef<TypeFamily>) -> Self { 244 fn from_chalk(db: &impl HirDatabase, trait_ref: chalk_ir::TraitRef<Interner>) -> Self {
236 let trait_ = from_chalk(db, trait_ref.trait_id); 245 let trait_ = from_chalk(db, trait_ref.trait_id);
237 let substs = from_chalk(db, trait_ref.substitution); 246 let substs = from_chalk(db, trait_ref.substitution);
238 TraitRef { trait_, substs } 247 TraitRef { trait_, substs }
@@ -252,9 +261,9 @@ impl ToChalk for hir_def::TraitId {
252} 261}
253 262
254impl ToChalk for TypeCtor { 263impl ToChalk for TypeCtor {
255 type Chalk = TypeName<TypeFamily>; 264 type Chalk = TypeName<Interner>;
256 265
257 fn to_chalk(self, db: &impl HirDatabase) -> TypeName<TypeFamily> { 266 fn to_chalk(self, db: &impl HirDatabase) -> TypeName<Interner> {
258 match self { 267 match self {
259 TypeCtor::AssociatedType(type_alias) => { 268 TypeCtor::AssociatedType(type_alias) => {
260 let type_id = type_alias.to_chalk(db); 269 let type_id = type_alias.to_chalk(db);
@@ -268,7 +277,7 @@ impl ToChalk for TypeCtor {
268 } 277 }
269 } 278 }
270 279
271 fn from_chalk(db: &impl HirDatabase, type_name: TypeName<TypeFamily>) -> TypeCtor { 280 fn from_chalk(db: &impl HirDatabase, type_name: TypeName<Interner>) -> TypeCtor {
272 match type_name { 281 match type_name {
273 TypeName::Struct(struct_id) => db.lookup_intern_type_ctor(struct_id.into()), 282 TypeName::Struct(struct_id) => db.lookup_intern_type_ctor(struct_id.into()),
274 TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)), 283 TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)),
@@ -317,9 +326,9 @@ impl ToChalk for AssocTyValue {
317} 326}
318 327
319impl ToChalk for GenericPredicate { 328impl ToChalk for GenericPredicate {
320 type Chalk = chalk_ir::QuantifiedWhereClause<TypeFamily>; 329 type Chalk = chalk_ir::QuantifiedWhereClause<Interner>;
321 330
322 fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::QuantifiedWhereClause<TypeFamily> { 331 fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::QuantifiedWhereClause<Interner> {
323 match self { 332 match self {
324 GenericPredicate::Implemented(trait_ref) => { 333 GenericPredicate::Implemented(trait_ref) => {
325 make_binders(chalk_ir::WhereClause::Implemented(trait_ref.to_chalk(db)), 0) 334 make_binders(chalk_ir::WhereClause::Implemented(trait_ref.to_chalk(db)), 0)
@@ -337,7 +346,7 @@ impl ToChalk for GenericPredicate {
337 346
338 fn from_chalk( 347 fn from_chalk(
339 db: &impl HirDatabase, 348 db: &impl HirDatabase,
340 where_clause: chalk_ir::QuantifiedWhereClause<TypeFamily>, 349 where_clause: chalk_ir::QuantifiedWhereClause<Interner>,
341 ) -> GenericPredicate { 350 ) -> GenericPredicate {
342 match where_clause.value { 351 match where_clause.value {
343 chalk_ir::WhereClause::Implemented(tr) => { 352 chalk_ir::WhereClause::Implemented(tr) => {
@@ -353,9 +362,9 @@ impl ToChalk for GenericPredicate {
353} 362}
354 363
355impl ToChalk for ProjectionTy { 364impl ToChalk for ProjectionTy {
356 type Chalk = chalk_ir::AliasTy<TypeFamily>; 365 type Chalk = chalk_ir::AliasTy<Interner>;
357 366
358 fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::AliasTy<TypeFamily> { 367 fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::AliasTy<Interner> {
359 chalk_ir::AliasTy { 368 chalk_ir::AliasTy {
360 associated_ty_id: self.associated_ty.to_chalk(db), 369 associated_ty_id: self.associated_ty.to_chalk(db),
361 substitution: self.parameters.to_chalk(db), 370 substitution: self.parameters.to_chalk(db),
@@ -364,7 +373,7 @@ impl ToChalk for ProjectionTy {
364 373
365 fn from_chalk( 374 fn from_chalk(
366 db: &impl HirDatabase, 375 db: &impl HirDatabase,
367 projection_ty: chalk_ir::AliasTy<TypeFamily>, 376 projection_ty: chalk_ir::AliasTy<Interner>,
368 ) -> ProjectionTy { 377 ) -> ProjectionTy {
369 ProjectionTy { 378 ProjectionTy {
370 associated_ty: from_chalk(db, projection_ty.associated_ty_id), 379 associated_ty: from_chalk(db, projection_ty.associated_ty_id),
@@ -374,28 +383,28 @@ impl ToChalk for ProjectionTy {
374} 383}
375 384
376impl ToChalk for super::ProjectionPredicate { 385impl ToChalk for super::ProjectionPredicate {
377 type Chalk = chalk_ir::Normalize<TypeFamily>; 386 type Chalk = chalk_ir::Normalize<Interner>;
378 387
379 fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Normalize<TypeFamily> { 388 fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Normalize<Interner> {
380 chalk_ir::Normalize { alias: self.projection_ty.to_chalk(db), ty: self.ty.to_chalk(db) } 389 chalk_ir::Normalize { alias: self.projection_ty.to_chalk(db), ty: self.ty.to_chalk(db) }
381 } 390 }
382 391
383 fn from_chalk(_db: &impl HirDatabase, _normalize: chalk_ir::Normalize<TypeFamily>) -> Self { 392 fn from_chalk(_db: &impl HirDatabase, _normalize: chalk_ir::Normalize<Interner>) -> Self {
384 unimplemented!() 393 unimplemented!()
385 } 394 }
386} 395}
387 396
388impl ToChalk for Obligation { 397impl ToChalk for Obligation {
389 type Chalk = chalk_ir::DomainGoal<TypeFamily>; 398 type Chalk = chalk_ir::DomainGoal<Interner>;
390 399
391 fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::DomainGoal<TypeFamily> { 400 fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::DomainGoal<Interner> {
392 match self { 401 match self {
393 Obligation::Trait(tr) => tr.to_chalk(db).cast(), 402 Obligation::Trait(tr) => tr.to_chalk(db).cast(),
394 Obligation::Projection(pr) => pr.to_chalk(db).cast(), 403 Obligation::Projection(pr) => pr.to_chalk(db).cast(),
395 } 404 }
396 } 405 }
397 406
398 fn from_chalk(_db: &impl HirDatabase, _goal: chalk_ir::DomainGoal<TypeFamily>) -> Self { 407 fn from_chalk(_db: &impl HirDatabase, _goal: chalk_ir::DomainGoal<Interner>) -> Self {
399 unimplemented!() 408 unimplemented!()
400 } 409 }
401} 410}
@@ -418,16 +427,16 @@ where
418} 427}
419 428
420impl ToChalk for Arc<super::TraitEnvironment> { 429impl ToChalk for Arc<super::TraitEnvironment> {
421 type Chalk = chalk_ir::Environment<TypeFamily>; 430 type Chalk = chalk_ir::Environment<Interner>;
422 431
423 fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Environment<TypeFamily> { 432 fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Environment<Interner> {
424 let mut clauses = Vec::new(); 433 let mut clauses = Vec::new();
425 for pred in &self.predicates { 434 for pred in &self.predicates {
426 if pred.is_error() { 435 if pred.is_error() {
427 // for env, we just ignore errors 436 // for env, we just ignore errors
428 continue; 437 continue;
429 } 438 }
430 let program_clause: chalk_ir::ProgramClause<TypeFamily> = 439 let program_clause: chalk_ir::ProgramClause<Interner> =
431 pred.clone().to_chalk(db).cast(); 440 pred.clone().to_chalk(db).cast();
432 clauses.push(program_clause.into_from_env_clause()); 441 clauses.push(program_clause.into_from_env_clause());
433 } 442 }
@@ -436,7 +445,7 @@ impl ToChalk for Arc<super::TraitEnvironment> {
436 445
437 fn from_chalk( 446 fn from_chalk(
438 _db: &impl HirDatabase, 447 _db: &impl HirDatabase,
439 _env: chalk_ir::Environment<TypeFamily>, 448 _env: chalk_ir::Environment<Interner>,
440 ) -> Arc<super::TraitEnvironment> { 449 ) -> Arc<super::TraitEnvironment> {
441 unimplemented!() 450 unimplemented!()
442 } 451 }
@@ -444,7 +453,7 @@ impl ToChalk for Arc<super::TraitEnvironment> {
444 453
445impl<T: ToChalk> ToChalk for super::InEnvironment<T> 454impl<T: ToChalk> ToChalk for super::InEnvironment<T>
446where 455where
447 T::Chalk: chalk_ir::family::HasTypeFamily<TypeFamily = TypeFamily>, 456 T::Chalk: chalk_ir::interner::HasInterner<Interner = Interner>,
448{ 457{
449 type Chalk = chalk_ir::InEnvironment<T::Chalk>; 458 type Chalk = chalk_ir::InEnvironment<T::Chalk>;
450 459
@@ -522,7 +531,7 @@ fn convert_where_clauses(
522 db: &impl HirDatabase, 531 db: &impl HirDatabase,
523 def: GenericDefId, 532 def: GenericDefId,
524 substs: &Substs, 533 substs: &Substs,
525) -> Vec<chalk_ir::QuantifiedWhereClause<TypeFamily>> { 534) -> Vec<chalk_ir::QuantifiedWhereClause<Interner>> {
526 let generic_predicates = db.generic_predicates(def); 535 let generic_predicates = db.generic_predicates(def);
527 let mut result = Vec::with_capacity(generic_predicates.len()); 536 let mut result = Vec::with_capacity(generic_predicates.len());
528 for pred in generic_predicates.iter() { 537 for pred in generic_predicates.iter() {
@@ -535,7 +544,7 @@ fn convert_where_clauses(
535 result 544 result
536} 545}
537 546
538impl<'a, DB> chalk_solve::RustIrDatabase<TypeFamily> for ChalkContext<'a, DB> 547impl<'a, DB> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a, DB>
539where 548where
540 DB: HirDatabase, 549 DB: HirDatabase,
541{ 550{
@@ -554,7 +563,7 @@ where
554 fn impls_for_trait( 563 fn impls_for_trait(
555 &self, 564 &self,
556 trait_id: TraitId, 565 trait_id: TraitId,
557 parameters: &[Parameter<TypeFamily>], 566 parameters: &[Parameter<Interner>],
558 ) -> Vec<ImplId> { 567 ) -> Vec<ImplId> {
559 debug!("impls_for_trait {:?}", trait_id); 568 debug!("impls_for_trait {:?}", trait_id);
560 let trait_: hir_def::TraitId = from_chalk(self.db, trait_id); 569 let trait_: hir_def::TraitId = from_chalk(self.db, trait_id);
@@ -589,14 +598,14 @@ where
589 fn associated_ty_value(&self, id: AssociatedTyValueId) -> Arc<AssociatedTyValue> { 598 fn associated_ty_value(&self, id: AssociatedTyValueId) -> Arc<AssociatedTyValue> {
590 self.db.associated_ty_value(self.krate, id) 599 self.db.associated_ty_value(self.krate, id)
591 } 600 }
592 fn custom_clauses(&self) -> Vec<chalk_ir::ProgramClause<TypeFamily>> { 601 fn custom_clauses(&self) -> Vec<chalk_ir::ProgramClause<Interner>> {
593 vec![] 602 vec![]
594 } 603 }
595 fn local_impls_to_coherence_check(&self, _trait_id: TraitId) -> Vec<ImplId> { 604 fn local_impls_to_coherence_check(&self, _trait_id: TraitId) -> Vec<ImplId> {
596 // We don't do coherence checking (yet) 605 // We don't do coherence checking (yet)
597 unimplemented!() 606 unimplemented!()
598 } 607 }
599 fn as_struct_id(&self, id: &TypeName<TypeFamily>) -> Option<StructId> { 608 fn as_struct_id(&self, id: &TypeName<Interner>) -> Option<StructId> {
600 match id { 609 match id {
601 TypeName::Struct(struct_id) => Some(*struct_id), 610 TypeName::Struct(struct_id) => Some(*struct_id),
602 _ => None, 611 _ => None,
diff --git a/crates/ra_ide/src/call_hierarchy.rs b/crates/ra_ide/src/call_hierarchy.rs
index f984f40ad..51ac59a71 100644
--- a/crates/ra_ide/src/call_hierarchy.rs
+++ b/crates/ra_ide/src/call_hierarchy.rs
@@ -4,16 +4,11 @@ use indexmap::IndexMap;
4 4
5use hir::db::AstDatabase; 5use hir::db::AstDatabase;
6use ra_ide_db::RootDatabase; 6use ra_ide_db::RootDatabase;
7use ra_syntax::{ 7use ra_syntax::{ast, match_ast, AstNode, TextRange};
8 ast::{self, DocCommentsOwner},
9 match_ast, AstNode, TextRange,
10};
11 8
12use crate::{ 9use crate::{
13 call_info::FnCallNode, 10 call_info::FnCallNode, display::ToNav, expand::descend_into_macros, goto_definition,
14 display::{ShortLabel, ToNav}, 11 references, FilePosition, NavigationTarget, RangeInfo,
15 expand::descend_into_macros,
16 goto_definition, references, FilePosition, NavigationTarget, RangeInfo,
17}; 12};
18 13
19#[derive(Debug, Clone)] 14#[derive(Debug, Clone)]
@@ -49,6 +44,7 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio
49 let refs = references::find_all_refs(db, position, None)?; 44 let refs = references::find_all_refs(db, position, None)?;
50 45
51 let mut calls = CallLocations::default(); 46 let mut calls = CallLocations::default();
47 let mut sb = hir::SourceBinder::new(db);
52 48
53 for reference in refs.info.references() { 49 for reference in refs.info.references() {
54 let file_id = reference.file_range.file_id; 50 let file_id = reference.file_range.file_id;
@@ -62,12 +58,8 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio
62 match_ast! { 58 match_ast! {
63 match node { 59 match node {
64 ast::FnDef(it) => { 60 ast::FnDef(it) => {
65 Some(NavigationTarget::from_named( 61 let def = sb.to_def(token.with_value(it))?;
66 db, 62 Some(def.to_nav(sb.db))
67 token.with_value(&it),
68 it.doc_comment_text(),
69 it.short_label(),
70 ))
71 }, 63 },
72 _ => { None }, 64 _ => { None },
73 } 65 }
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs
index b42cb477e..c9d0058a6 100644
--- a/crates/ra_ide/src/display/navigation_target.rs
+++ b/crates/ra_ide/src/display/navigation_target.rs
@@ -125,7 +125,7 @@ impl NavigationTarget {
125 } 125 }
126 126
127 /// Allows `NavigationTarget` to be created from a `NameOwner` 127 /// Allows `NavigationTarget` to be created from a `NameOwner`
128 pub(crate) fn from_named( 128 fn from_named(
129 db: &RootDatabase, 129 db: &RootDatabase,
130 node: InFile<&dyn ast::NameOwner>, 130 node: InFile<&dyn ast::NameOwner>,
131 docs: Option<String>, 131 docs: Option<String>,
diff --git a/crates/ra_ide/src/extend_selection.rs b/crates/ra_ide/src/extend_selection.rs
index 4757d8e22..1e7d0621a 100644
--- a/crates/ra_ide/src/extend_selection.rs
+++ b/crates/ra_ide/src/extend_selection.rs
@@ -145,25 +145,25 @@ fn extend_tokens_from_range(
145 let src = db.parse_or_expand(expanded.file_id)?; 145 let src = db.parse_or_expand(expanded.file_id)?;
146 let parent = shallowest_node(&find_covering_element(&src, expanded.value))?.parent()?; 146 let parent = shallowest_node(&find_covering_element(&src, expanded.value))?.parent()?;
147 147
148 let validate = |token: SyntaxToken| { 148 let validate = |token: &SyntaxToken| {
149 let node = descend_into_macros(db, file_id, token.clone()); 149 let node = descend_into_macros(db, file_id, token.clone());
150 if node.file_id == expanded.file_id 150 node.file_id == expanded.file_id
151 && node.value.text_range().is_subrange(&parent.text_range()) 151 && node.value.text_range().is_subrange(&parent.text_range())
152 {
153 Some(token)
154 } else {
155 None
156 }
157 }; 152 };
158 153
159 // Find the first and last text range under expanded parent 154 // Find the first and last text range under expanded parent
160 let first = successors(Some(first_token), |token| { 155 let first = successors(Some(first_token), |token| {
161 validate(skip_whitespace(token.prev_token()?, Direction::Prev)?) 156 let token = token.prev_token()?;
157 skip_whitespace(token, Direction::Prev)
162 }) 158 })
159 .take_while(validate)
163 .last()?; 160 .last()?;
161
164 let last = successors(Some(last_token), |token| { 162 let last = successors(Some(last_token), |token| {
165 validate(skip_whitespace(token.next_token()?, Direction::Next)?) 163 let token = token.next_token()?;
164 skip_whitespace(token, Direction::Next)
166 }) 165 })
166 .take_while(validate)
167 .last()?; 167 .last()?;
168 168
169 let range = union_range(first.text_range(), last.text_range()); 169 let range = union_range(first.text_range(), last.text_range());
@@ -334,10 +334,12 @@ fn adj_comments(comment: &ast::Comment, dir: Direction) -> ast::Comment {
334 334
335#[cfg(test)] 335#[cfg(test)]
336mod tests { 336mod tests {
337 use super::*;
338 use crate::mock_analysis::single_file;
339 use test_utils::extract_offset; 337 use test_utils::extract_offset;
340 338
339 use crate::mock_analysis::single_file;
340
341 use super::*;
342
341 fn do_check(before: &str, afters: &[&str]) { 343 fn do_check(before: &str, afters: &[&str]) {
342 let (cursor, before) = extract_offset(before); 344 let (cursor, before) = extract_offset(before);
343 let (analysis, file_id) = single_file(&before); 345 let (analysis, file_id) = single_file(&before);
diff --git a/crates/ra_ide/src/join_lines.rs b/crates/ra_ide/src/join_lines.rs
index 01fb32b3d..7d70dab9c 100644
--- a/crates/ra_ide/src/join_lines.rs
+++ b/crates/ra_ide/src/join_lines.rs
@@ -228,6 +228,31 @@ fn foo() {
228 } 228 }
229 229
230 #[test] 230 #[test]
231 fn test_join_lines_diverging_block() {
232 let before = r"
233 fn foo() {
234 loop {
235 match x {
236 92 => <|>{
237 continue;
238 }
239 }
240 }
241 }
242 ";
243 let after = r"
244 fn foo() {
245 loop {
246 match x {
247 92 => <|>continue,
248 }
249 }
250 }
251 ";
252 check_join_lines(before, after);
253 }
254
255 #[test]
231 fn join_lines_adds_comma_for_block_in_match_arm() { 256 fn join_lines_adds_comma_for_block_in_match_arm() {
232 check_join_lines( 257 check_join_lines(
233 r" 258 r"
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index f86f98be7..82e10bc7e 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -75,7 +75,7 @@ pub use crate::{
75 runnables::{Runnable, RunnableKind, TestId}, 75 runnables::{Runnable, RunnableKind, TestId},
76 source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, 76 source_change::{FileSystemEdit, SourceChange, SourceFileEdit},
77 ssr::SsrError, 77 ssr::SsrError,
78 syntax_highlighting::HighlightedRange, 78 syntax_highlighting::{tags, HighlightedRange},
79}; 79};
80 80
81pub use hir::Documentation; 81pub use hir::Documentation;
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index d873f153e..812229b4e 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -17,32 +17,32 @@ use crate::{
17}; 17};
18 18
19pub mod tags { 19pub mod tags {
20 pub(crate) const FIELD: &str = "field"; 20 pub const FIELD: &str = "field";
21 pub(crate) const FUNCTION: &str = "function"; 21 pub const FUNCTION: &str = "function";
22 pub(crate) const MODULE: &str = "module"; 22 pub const MODULE: &str = "module";
23 pub(crate) const CONSTANT: &str = "constant"; 23 pub const CONSTANT: &str = "constant";
24 pub(crate) const MACRO: &str = "macro"; 24 pub const MACRO: &str = "macro";
25 25
26 pub(crate) const VARIABLE: &str = "variable"; 26 pub const VARIABLE: &str = "variable";
27 pub(crate) const VARIABLE_MUT: &str = "variable.mut"; 27 pub const VARIABLE_MUT: &str = "variable.mut";
28 28
29 pub(crate) const TYPE: &str = "type"; 29 pub const TYPE: &str = "type";
30 pub(crate) const TYPE_BUILTIN: &str = "type.builtin"; 30 pub const TYPE_BUILTIN: &str = "type.builtin";
31 pub(crate) const TYPE_SELF: &str = "type.self"; 31 pub const TYPE_SELF: &str = "type.self";
32 pub(crate) const TYPE_PARAM: &str = "type.param"; 32 pub const TYPE_PARAM: &str = "type.param";
33 pub(crate) const TYPE_LIFETIME: &str = "type.lifetime"; 33 pub const TYPE_LIFETIME: &str = "type.lifetime";
34 34
35 pub(crate) const LITERAL_BYTE: &str = "literal.byte"; 35 pub const LITERAL_BYTE: &str = "literal.byte";
36 pub(crate) const LITERAL_NUMERIC: &str = "literal.numeric"; 36 pub const LITERAL_NUMERIC: &str = "literal.numeric";
37 pub(crate) const LITERAL_CHAR: &str = "literal.char"; 37 pub const LITERAL_CHAR: &str = "literal.char";
38 38
39 pub(crate) const LITERAL_COMMENT: &str = "comment"; 39 pub const LITERAL_COMMENT: &str = "comment";
40 pub(crate) const LITERAL_STRING: &str = "string"; 40 pub const LITERAL_STRING: &str = "string";
41 pub(crate) const LITERAL_ATTRIBUTE: &str = "attribute"; 41 pub const LITERAL_ATTRIBUTE: &str = "attribute";
42 42
43 pub(crate) const KEYWORD: &str = "keyword"; 43 pub const KEYWORD: &str = "keyword";
44 pub(crate) const KEYWORD_UNSAFE: &str = "keyword.unsafe"; 44 pub const KEYWORD_UNSAFE: &str = "keyword.unsafe";
45 pub(crate) const KEYWORD_CONTROL: &str = "keyword.control"; 45 pub const KEYWORD_CONTROL: &str = "keyword.control";
46} 46}
47 47
48#[derive(Debug)] 48#[derive(Debug)]
diff --git a/crates/ra_syntax/Cargo.toml b/crates/ra_syntax/Cargo.toml
index 7891628dc..8efc6b368 100644
--- a/crates/ra_syntax/Cargo.toml
+++ b/crates/ra_syntax/Cargo.toml
@@ -12,7 +12,7 @@ doctest = false
12 12
13[dependencies] 13[dependencies]
14itertools = "0.8.2" 14itertools = "0.8.2"
15rowan = "0.9.0" 15rowan = "0.9.1"
16rustc_lexer = "0.1.0" 16rustc_lexer = "0.1.0"
17rustc-hash = "1.1.0" 17rustc-hash = "1.1.0"
18arrayvec = "0.5.1" 18arrayvec = "0.5.1"
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index c9fd645f1..638987ee8 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -1,12 +1,15 @@
1//! Advertizes the capabilities of the LSP Server. 1//! Advertizes the capabilities of the LSP Server.
2 2
3use crate::semantic_tokens;
4
3use lsp_types::{ 5use lsp_types::{
4 CallHierarchyServerCapability, CodeActionProviderCapability, CodeLensOptions, 6 CallHierarchyServerCapability, CodeActionProviderCapability, CodeLensOptions,
5 CompletionOptions, DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, 7 CompletionOptions, DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability,
6 ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions, 8 ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions,
7 SelectionRangeProviderCapability, ServerCapabilities, SignatureHelpOptions, 9 SelectionRangeProviderCapability, SemanticTokensDocumentProvider, SemanticTokensLegend,
8 TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions, 10 SemanticTokensOptions, SemanticTokensServerCapabilities, ServerCapabilities,
9 TypeDefinitionProviderCapability, WorkDoneProgressOptions, 11 SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind,
12 TextDocumentSyncOptions, TypeDefinitionProviderCapability, WorkDoneProgressOptions,
10}; 13};
11 14
12pub fn server_capabilities() -> ServerCapabilities { 15pub fn server_capabilities() -> ServerCapabilities {
@@ -57,7 +60,20 @@ pub fn server_capabilities() -> ServerCapabilities {
57 execute_command_provider: None, 60 execute_command_provider: None,
58 workspace: None, 61 workspace: None,
59 call_hierarchy_provider: Some(CallHierarchyServerCapability::Simple(true)), 62 call_hierarchy_provider: Some(CallHierarchyServerCapability::Simple(true)),
60 semantic_tokens_provider: None, 63 semantic_tokens_provider: Some(SemanticTokensServerCapabilities::SemanticTokensOptions(
64 SemanticTokensOptions {
65 legend: SemanticTokensLegend {
66 token_types: semantic_tokens::supported_token_types().iter().cloned().collect(),
67 token_modifiers: semantic_tokens::supported_token_modifiers()
68 .iter()
69 .cloned()
70 .collect(),
71 },
72
73 document_provider: Some(SemanticTokensDocumentProvider::Bool(true)),
74 ..SemanticTokensOptions::default()
75 },
76 )),
61 experimental: Default::default(), 77 experimental: Default::default(),
62 } 78 }
63} 79}
diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs
index 90ef74056..5fcb46b61 100644
--- a/crates/rust-analyzer/src/conv.rs
+++ b/crates/rust-analyzer/src/conv.rs
@@ -4,11 +4,12 @@
4use lsp_types::{ 4use lsp_types::{
5 self, CreateFile, DiagnosticSeverity, DocumentChangeOperation, DocumentChanges, Documentation, 5 self, CreateFile, DiagnosticSeverity, DocumentChangeOperation, DocumentChanges, Documentation,
6 Location, LocationLink, MarkupContent, MarkupKind, Position, Range, RenameFile, ResourceOp, 6 Location, LocationLink, MarkupContent, MarkupKind, Position, Range, RenameFile, ResourceOp,
7 SymbolKind, TextDocumentEdit, TextDocumentIdentifier, TextDocumentItem, 7 SemanticTokenModifier, SemanticTokenType, SymbolKind, TextDocumentEdit, TextDocumentIdentifier,
8 TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, WorkspaceEdit, 8 TextDocumentItem, TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier,
9 WorkspaceEdit,
9}; 10};
10use ra_ide::{ 11use ra_ide::{
11 translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition, 12 tags, translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition,
12 FileRange, FileSystemEdit, Fold, FoldKind, InsertTextFormat, LineCol, LineIndex, 13 FileRange, FileSystemEdit, Fold, FoldKind, InsertTextFormat, LineCol, LineIndex,
13 NavigationTarget, RangeInfo, ReferenceAccess, Severity, SourceChange, SourceFileEdit, 14 NavigationTarget, RangeInfo, ReferenceAccess, Severity, SourceChange, SourceFileEdit,
14}; 15};
@@ -16,7 +17,7 @@ use ra_syntax::{SyntaxKind, TextRange, TextUnit};
16use ra_text_edit::{AtomTextEdit, TextEdit}; 17use ra_text_edit::{AtomTextEdit, TextEdit};
17use ra_vfs::LineEndings; 18use ra_vfs::LineEndings;
18 19
19use crate::{req, world::WorldSnapshot, Result}; 20use crate::{req, semantic_tokens, world::WorldSnapshot, Result};
20 21
21pub trait Conv { 22pub trait Conv {
22 type Output; 23 type Output;
@@ -302,6 +303,76 @@ impl ConvWith<&FoldConvCtx<'_>> for Fold {
302 } 303 }
303} 304}
304 305
306impl Conv for &'static str {
307 type Output = (SemanticTokenType, Vec<SemanticTokenModifier>);
308
309 fn conv(self) -> (SemanticTokenType, Vec<SemanticTokenModifier>) {
310 let token_type: SemanticTokenType = match self {
311 tags::FIELD => SemanticTokenType::MEMBER,
312 tags::FUNCTION => SemanticTokenType::FUNCTION,
313 tags::MODULE => SemanticTokenType::NAMESPACE,
314 tags::CONSTANT => {
315 return (
316 SemanticTokenType::VARIABLE,
317 vec![SemanticTokenModifier::STATIC, SemanticTokenModifier::READONLY],
318 )
319 }
320 tags::MACRO => SemanticTokenType::MACRO,
321
322 tags::VARIABLE => {
323 return (SemanticTokenType::VARIABLE, vec![SemanticTokenModifier::READONLY])
324 }
325 tags::VARIABLE_MUT => SemanticTokenType::VARIABLE,
326
327 tags::TYPE => SemanticTokenType::TYPE,
328 tags::TYPE_BUILTIN => SemanticTokenType::TYPE,
329 tags::TYPE_SELF => {
330 return (SemanticTokenType::TYPE, vec![SemanticTokenModifier::REFERENCE])
331 }
332 tags::TYPE_PARAM => SemanticTokenType::TYPE_PARAMETER,
333 tags::TYPE_LIFETIME => {
334 return (SemanticTokenType::LABEL, vec![SemanticTokenModifier::REFERENCE])
335 }
336
337 tags::LITERAL_BYTE => SemanticTokenType::NUMBER,
338 tags::LITERAL_NUMERIC => SemanticTokenType::NUMBER,
339 tags::LITERAL_CHAR => SemanticTokenType::NUMBER,
340
341 tags::LITERAL_COMMENT => {
342 return (SemanticTokenType::COMMENT, vec![SemanticTokenModifier::DOCUMENTATION])
343 }
344
345 tags::LITERAL_STRING => SemanticTokenType::STRING,
346 tags::LITERAL_ATTRIBUTE => SemanticTokenType::KEYWORD,
347
348 tags::KEYWORD => SemanticTokenType::KEYWORD,
349 tags::KEYWORD_UNSAFE => SemanticTokenType::KEYWORD,
350 tags::KEYWORD_CONTROL => SemanticTokenType::KEYWORD,
351 unknown => panic!("Unknown semantic token: {}", unknown),
352 };
353
354 (token_type, vec![])
355 }
356}
357
358impl Conv for (SemanticTokenType, Vec<SemanticTokenModifier>) {
359 type Output = (u32, u32);
360
361 fn conv(self) -> Self::Output {
362 let token_index =
363 semantic_tokens::supported_token_types().iter().position(|it| *it == self.0).unwrap();
364 let mut token_modifier_bitset = 0;
365 for modifier in self.1.iter() {
366 token_modifier_bitset |= semantic_tokens::supported_token_modifiers()
367 .iter()
368 .position(|it| it == modifier)
369 .unwrap();
370 }
371
372 (token_index as u32, token_modifier_bitset as u32)
373 }
374}
375
305impl<T: ConvWith<CTX>, CTX> ConvWith<CTX> for Option<T> { 376impl<T: ConvWith<CTX>, CTX> ConvWith<CTX> for Option<T> {
306 type Output = Option<T::Output>; 377 type Output = Option<T::Output>;
307 378
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index 0dae30e46..a0f968823 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -36,6 +36,7 @@ pub mod req;
36mod config; 36mod config;
37mod world; 37mod world;
38mod diagnostics; 38mod diagnostics;
39mod semantic_tokens;
39 40
40use serde::de::DeserializeOwned; 41use serde::de::DeserializeOwned;
41 42
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 98306986b..6e9e604a6 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -528,6 +528,7 @@ fn on_request(
528 .on::<req::CallHierarchyIncomingCalls>(handlers::handle_call_hierarchy_incoming)? 528 .on::<req::CallHierarchyIncomingCalls>(handlers::handle_call_hierarchy_incoming)?
529 .on::<req::CallHierarchyOutgoingCalls>(handlers::handle_call_hierarchy_outgoing)? 529 .on::<req::CallHierarchyOutgoingCalls>(handlers::handle_call_hierarchy_outgoing)?
530 .on::<req::Ssr>(handlers::handle_ssr)? 530 .on::<req::Ssr>(handlers::handle_ssr)?
531 .on::<req::SemanticTokensRequest>(handlers::handle_semantic_tokens)?
531 .finish(); 532 .finish();
532 Ok(()) 533 Ok(())
533} 534}
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index bb7bab372..e13e7c95a 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -16,8 +16,9 @@ use lsp_types::{
16 CodeAction, CodeActionOrCommand, CodeActionResponse, CodeLens, Command, CompletionItem, 16 CodeAction, CodeActionOrCommand, CodeActionResponse, CodeLens, Command, CompletionItem,
17 Diagnostic, DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, 17 Diagnostic, DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange,
18 FoldingRangeParams, Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, 18 FoldingRangeParams, Hover, HoverContents, Location, MarkupContent, MarkupKind, Position,
19 PrepareRenameResponse, Range, RenameParams, SymbolInformation, TextDocumentIdentifier, 19 PrepareRenameResponse, Range, RenameParams, SemanticTokenModifier, SemanticTokenType,
20 TextEdit, WorkspaceEdit, 20 SemanticTokens, SemanticTokensParams, SemanticTokensResult, SymbolInformation,
21 TextDocumentIdentifier, TextEdit, WorkspaceEdit,
21}; 22};
22use ra_ide::{ 23use ra_ide::{
23 AssistId, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, 24 AssistId, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind,
@@ -38,6 +39,7 @@ use crate::{
38 diagnostics::DiagnosticTask, 39 diagnostics::DiagnosticTask,
39 from_json, 40 from_json,
40 req::{self, Decoration, InlayHint, InlayHintsParams, InlayKind}, 41 req::{self, Decoration, InlayHint, InlayHintsParams, InlayKind},
42 semantic_tokens::SemanticTokensBuilder,
41 world::WorldSnapshot, 43 world::WorldSnapshot,
42 LspError, Result, 44 LspError, Result,
43}; 45};
@@ -1068,3 +1070,25 @@ pub fn handle_call_hierarchy_outgoing(
1068 1070
1069 Ok(Some(res)) 1071 Ok(Some(res))
1070} 1072}
1073
1074pub fn handle_semantic_tokens(
1075 world: WorldSnapshot,
1076 params: SemanticTokensParams,
1077) -> Result<Option<SemanticTokensResult>> {
1078 let _p = profile("handle_semantic_tokens");
1079
1080 let file_id = params.text_document.try_conv_with(&world)?;
1081 let line_index = world.analysis().file_line_index(file_id)?;
1082
1083 let mut builder = SemanticTokensBuilder::default();
1084
1085 for h in world.analysis().highlight(file_id)?.into_iter() {
1086 let type_and_modifiers: (SemanticTokenType, Vec<SemanticTokenModifier>) = h.tag.conv();
1087 let (token_type, token_modifiers) = type_and_modifiers.conv();
1088 builder.push(h.range.conv_with(&line_index), token_type, token_modifiers);
1089 }
1090
1091 let tokens = SemanticTokens { data: builder.build(), ..Default::default() };
1092
1093 Ok(Some(tokens.into()))
1094}
diff --git a/crates/rust-analyzer/src/req.rs b/crates/rust-analyzer/src/req.rs
index 7ff7f60b3..3734899bc 100644
--- a/crates/rust-analyzer/src/req.rs
+++ b/crates/rust-analyzer/src/req.rs
@@ -12,9 +12,9 @@ pub use lsp_types::{
12 DocumentSymbolResponse, FileSystemWatcher, Hover, InitializeResult, MessageType, 12 DocumentSymbolResponse, FileSystemWatcher, Hover, InitializeResult, MessageType,
13 PartialResultParams, ProgressParams, ProgressParamsValue, ProgressToken, 13 PartialResultParams, ProgressParams, ProgressParamsValue, ProgressToken,
14 PublishDiagnosticsParams, ReferenceParams, Registration, RegistrationParams, SelectionRange, 14 PublishDiagnosticsParams, ReferenceParams, Registration, RegistrationParams, SelectionRange,
15 SelectionRangeParams, ServerCapabilities, ShowMessageParams, SignatureHelp, SymbolKind, 15 SelectionRangeParams, SemanticTokensParams, SemanticTokensResult, ServerCapabilities,
16 TextDocumentEdit, TextDocumentPositionParams, TextEdit, WorkDoneProgressParams, WorkspaceEdit, 16 ShowMessageParams, SignatureHelp, SymbolKind, TextDocumentEdit, TextDocumentPositionParams,
17 WorkspaceSymbolParams, 17 TextEdit, WorkDoneProgressParams, WorkspaceEdit, WorkspaceSymbolParams,
18}; 18};
19 19
20pub enum AnalyzerStatus {} 20pub enum AnalyzerStatus {}
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
new file mode 100644
index 000000000..e6a8eb146
--- /dev/null
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -0,0 +1,94 @@
1//! Semantic Tokens helpers
2
3use lsp_types::{Range, SemanticToken, SemanticTokenModifier, SemanticTokenType};
4
5const SUPPORTED_TYPES: &[SemanticTokenType] = &[
6 SemanticTokenType::COMMENT,
7 SemanticTokenType::KEYWORD,
8 SemanticTokenType::STRING,
9 SemanticTokenType::NUMBER,
10 SemanticTokenType::REGEXP,
11 SemanticTokenType::OPERATOR,
12 SemanticTokenType::NAMESPACE,
13 SemanticTokenType::TYPE,
14 SemanticTokenType::STRUCT,
15 SemanticTokenType::CLASS,
16 SemanticTokenType::INTERFACE,
17 SemanticTokenType::ENUM,
18 SemanticTokenType::TYPE_PARAMETER,
19 SemanticTokenType::FUNCTION,
20 SemanticTokenType::MEMBER,
21 SemanticTokenType::PROPERTY,
22 SemanticTokenType::MACRO,
23 SemanticTokenType::VARIABLE,
24 SemanticTokenType::PARAMETER,
25 SemanticTokenType::LABEL,
26];
27
28const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[
29 SemanticTokenModifier::DOCUMENTATION,
30 SemanticTokenModifier::DECLARATION,
31 SemanticTokenModifier::DEFINITION,
32 SemanticTokenModifier::REFERENCE,
33 SemanticTokenModifier::STATIC,
34 SemanticTokenModifier::ABSTRACT,
35 SemanticTokenModifier::DEPRECATED,
36 SemanticTokenModifier::ASYNC,
37 SemanticTokenModifier::VOLATILE,
38 SemanticTokenModifier::READONLY,
39];
40
41/// Token types that the server supports
42pub(crate) fn supported_token_types() -> &'static [SemanticTokenType] {
43 SUPPORTED_TYPES
44}
45
46/// Token modifiers that the server supports
47pub(crate) fn supported_token_modifiers() -> &'static [SemanticTokenModifier] {
48 SUPPORTED_MODIFIERS
49}
50
51/// Tokens are encoded relative to each other.
52///
53/// This is a direct port of https://github.com/microsoft/vscode-languageserver-node/blob/f425af9de46a0187adb78ec8a46b9b2ce80c5412/server/src/sematicTokens.proposed.ts#L45
54#[derive(Default)]
55pub(crate) struct SemanticTokensBuilder {
56 prev_line: u32,
57 prev_char: u32,
58 data: Vec<SemanticToken>,
59}
60
61impl SemanticTokensBuilder {
62 /// Push a new token onto the builder
63 pub fn push(&mut self, range: Range, token_index: u32, modifier_bitset: u32) {
64 let mut push_line = range.start.line as u32;
65 let mut push_char = range.start.character as u32;
66
67 if !self.data.is_empty() {
68 push_line -= self.prev_line;
69 if push_line == 0 {
70 push_char -= self.prev_char;
71 }
72 }
73
74 // A token cannot be multiline
75 let token_len = range.end.character - range.start.character;
76
77 let token = SemanticToken {
78 delta_line: push_line,
79 delta_start: push_char,
80 length: token_len as u32,
81 token_type: token_index,
82 token_modifiers_bitset: modifier_bitset,
83 };
84
85 self.data.push(token);
86
87 self.prev_line = range.start.line as u32;
88 self.prev_char = range.start.character as u32;
89 }
90
91 pub fn build(self) -> Vec<SemanticToken> {
92 self.data
93 }
94}
diff --git a/editors/code/.eslintrc.js b/editors/code/.eslintrc.js
index 16f18ab2c..c6bf410f4 100644
--- a/editors/code/.eslintrc.js
+++ b/editors/code/.eslintrc.js
@@ -32,6 +32,7 @@ module.exports = {
32 "@typescript-eslint/semi": [ 32 "@typescript-eslint/semi": [
33 "error", 33 "error",
34 "always" 34 "always"
35 ] 35 ],
36 "@typescript-eslint/no-unnecessary-type-assertion": "error"
36 } 37 }
37}; 38};
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 76ef2aae4..0288a468e 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -1575,9 +1575,9 @@
1575 } 1575 }
1576 }, 1576 },
1577 "typescript": { 1577 "typescript": {
1578 "version": "3.7.5", 1578 "version": "3.8.2",
1579 "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz", 1579 "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.2.tgz",
1580 "integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==", 1580 "integrity": "sha512-EgOVgL/4xfVrCMbhYKUQTdF37SQn4Iw73H5BgCrF1Abdun7Kwy/QZsE/ssAy0y4LxBbvua3PIbFsbRczWWnDdQ==",
1581 "dev": true 1581 "dev": true
1582 }, 1582 },
1583 "typescript-formatter": { 1583 "typescript-formatter": {
diff --git a/editors/code/package.json b/editors/code/package.json
index 9ef6c6983..dff535fcd 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -42,7 +42,7 @@
42 "eslint": "^6.8.0", 42 "eslint": "^6.8.0",
43 "rollup": "^1.31.1", 43 "rollup": "^1.31.1",
44 "tslib": "^1.10.0", 44 "tslib": "^1.10.0",
45 "typescript": "^3.7.5", 45 "typescript": "^3.8.2",
46 "typescript-formatter": "^7.2.2", 46 "typescript-formatter": "^7.2.2",
47 "vsce": "^1.73.0" 47 "vsce": "^1.73.0"
48 }, 48 },
diff --git a/editors/code/src/commands/analyzer_status.ts b/editors/code/src/commands/analyzer_status.ts
index 6631e8db7..1c6ea399b 100644
--- a/editors/code/src/commands/analyzer_status.ts
+++ b/editors/code/src/commands/analyzer_status.ts
@@ -1,5 +1,6 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2 2
3import * as ra from '../rust-analyzer-api';
3import { Ctx, Cmd } from '../ctx'; 4import { Ctx, Cmd } from '../ctx';
4 5
5// Shows status of rust-analyzer (for debugging) 6// Shows status of rust-analyzer (for debugging)
@@ -50,10 +51,7 @@ class TextDocumentContentProvider
50 const client = this.ctx.client; 51 const client = this.ctx.client;
51 if (!editor || !client) return ''; 52 if (!editor || !client) return '';
52 53
53 return client.sendRequest<string>( 54 return client.sendRequest(ra.analyzerStatus, null);
54 'rust-analyzer/analyzerStatus',
55 null,
56 );
57 } 55 }
58 56
59 get onDidChange(): vscode.Event<vscode.Uri> { 57 get onDidChange(): vscode.Event<vscode.Uri> {
diff --git a/editors/code/src/commands/expand_macro.ts b/editors/code/src/commands/expand_macro.ts
index edec9bbc1..23f2ef1d5 100644
--- a/editors/code/src/commands/expand_macro.ts
+++ b/editors/code/src/commands/expand_macro.ts
@@ -1,5 +1,5 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as ra from '../rust-analyzer-api';
3 3
4import { Ctx, Cmd } from '../ctx'; 4import { Ctx, Cmd } from '../ctx';
5 5
@@ -26,12 +26,7 @@ export function expandMacro(ctx: Ctx): Cmd {
26 }; 26 };
27} 27}
28 28
29interface ExpandedMacro { 29function codeFormat(expanded: ra.ExpandedMacro): string {
30 name: string;
31 expansion: string;
32}
33
34function codeFormat(expanded: ExpandedMacro): string {
35 let result = `// Recursive expansion of ${expanded.name}! macro\n`; 30 let result = `// Recursive expansion of ${expanded.name}! macro\n`;
36 result += '// ' + '='.repeat(result.length - 3); 31 result += '// ' + '='.repeat(result.length - 3);
37 result += '\n\n'; 32 result += '\n\n';
@@ -54,14 +49,11 @@ class TextDocumentContentProvider
54 if (!editor || !client) return ''; 49 if (!editor || !client) return '';
55 50
56 const position = editor.selection.active; 51 const position = editor.selection.active;
57 const request: lc.TextDocumentPositionParams = { 52
53 const expanded = await client.sendRequest(ra.expandMacro, {
58 textDocument: { uri: editor.document.uri.toString() }, 54 textDocument: { uri: editor.document.uri.toString() },
59 position, 55 position,
60 }; 56 });
61 const expanded = await client.sendRequest<ExpandedMacro>(
62 'rust-analyzer/expandMacro',
63 request,
64 );
65 57
66 if (expanded == null) return 'Not available'; 58 if (expanded == null) return 'Not available';
67 59
diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts
index 839245f48..bdb7fc3b0 100644
--- a/editors/code/src/commands/index.ts
+++ b/editors/code/src/commands/index.ts
@@ -1,5 +1,6 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as lc from 'vscode-languageclient';
3import * as ra from '../rust-analyzer-api';
3 4
4import { Ctx, Cmd } from '../ctx'; 5import { Ctx, Cmd } from '../ctx';
5import * as sourceChange from '../source_change'; 6import * as sourceChange from '../source_change';
@@ -16,9 +17,7 @@ export * from './ssr';
16export * from './server_version'; 17export * from './server_version';
17 18
18export function collectGarbage(ctx: Ctx): Cmd { 19export function collectGarbage(ctx: Ctx): Cmd {
19 return async () => { 20 return async () => ctx.client.sendRequest(ra.collectGarbage, null);
20 await ctx.client?.sendRequest<null>('rust-analyzer/collectGarbage', null);
21 };
22} 21}
23 22
24export function showReferences(ctx: Ctx): Cmd { 23export function showReferences(ctx: Ctx): Cmd {
@@ -36,13 +35,13 @@ export function showReferences(ctx: Ctx): Cmd {
36} 35}
37 36
38export function applySourceChange(ctx: Ctx): Cmd { 37export function applySourceChange(ctx: Ctx): Cmd {
39 return async (change: sourceChange.SourceChange) => { 38 return async (change: ra.SourceChange) => {
40 await sourceChange.applySourceChange(ctx, change); 39 await sourceChange.applySourceChange(ctx, change);
41 }; 40 };
42} 41}
43 42
44export function selectAndApplySourceChange(ctx: Ctx): Cmd { 43export function selectAndApplySourceChange(ctx: Ctx): Cmd {
45 return async (changes: sourceChange.SourceChange[]) => { 44 return async (changes: ra.SourceChange[]) => {
46 if (changes.length === 1) { 45 if (changes.length === 1) {
47 await sourceChange.applySourceChange(ctx, changes[0]); 46 await sourceChange.applySourceChange(ctx, changes[0]);
48 } else if (changes.length > 0) { 47 } else if (changes.length > 0) {
diff --git a/editors/code/src/commands/join_lines.ts b/editors/code/src/commands/join_lines.ts
index 7b08c3255..de0614653 100644
--- a/editors/code/src/commands/join_lines.ts
+++ b/editors/code/src/commands/join_lines.ts
@@ -1,7 +1,7 @@
1import * as lc from 'vscode-languageclient'; 1import * as ra from '../rust-analyzer-api';
2 2
3import { Ctx, Cmd } from '../ctx'; 3import { Ctx, Cmd } from '../ctx';
4import { applySourceChange, SourceChange } from '../source_change'; 4import { applySourceChange } from '../source_change';
5 5
6export function joinLines(ctx: Ctx): Cmd { 6export function joinLines(ctx: Ctx): Cmd {
7 return async () => { 7 return async () => {
@@ -9,19 +9,10 @@ export function joinLines(ctx: Ctx): Cmd {
9 const client = ctx.client; 9 const client = ctx.client;
10 if (!editor || !client) return; 10 if (!editor || !client) return;
11 11
12 const request: JoinLinesParams = { 12 const change = await client.sendRequest(ra.joinLines, {
13 range: client.code2ProtocolConverter.asRange(editor.selection), 13 range: client.code2ProtocolConverter.asRange(editor.selection),
14 textDocument: { uri: editor.document.uri.toString() }, 14 textDocument: { uri: editor.document.uri.toString() },
15 }; 15 });
16 const change = await client.sendRequest<SourceChange>(
17 'rust-analyzer/joinLines',
18 request,
19 );
20 await applySourceChange(ctx, change); 16 await applySourceChange(ctx, change);
21 }; 17 };
22} 18}
23
24interface JoinLinesParams {
25 textDocument: lc.TextDocumentIdentifier;
26 range: lc.Range;
27}
diff --git a/editors/code/src/commands/matching_brace.ts b/editors/code/src/commands/matching_brace.ts
index 7c58bb7e7..a60776e2d 100644
--- a/editors/code/src/commands/matching_brace.ts
+++ b/editors/code/src/commands/matching_brace.ts
@@ -1,5 +1,5 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as ra from '../rust-analyzer-api';
3 3
4import { Ctx, Cmd } from '../ctx'; 4import { Ctx, Cmd } from '../ctx';
5 5
@@ -9,16 +9,12 @@ export function matchingBrace(ctx: Ctx): Cmd {
9 const client = ctx.client; 9 const client = ctx.client;
10 if (!editor || !client) return; 10 if (!editor || !client) return;
11 11
12 const request: FindMatchingBraceParams = { 12 const response = await client.sendRequest(ra.findMatchingBrace, {
13 textDocument: { uri: editor.document.uri.toString() }, 13 textDocument: { uri: editor.document.uri.toString() },
14 offsets: editor.selections.map(s => 14 offsets: editor.selections.map(s =>
15 client.code2ProtocolConverter.asPosition(s.active), 15 client.code2ProtocolConverter.asPosition(s.active),
16 ), 16 ),
17 }; 17 });
18 const response = await client.sendRequest<lc.Position[]>(
19 'rust-analyzer/findMatchingBrace',
20 request,
21 );
22 editor.selections = editor.selections.map((sel, idx) => { 18 editor.selections = editor.selections.map((sel, idx) => {
23 const active = client.protocol2CodeConverter.asPosition( 19 const active = client.protocol2CodeConverter.asPosition(
24 response[idx], 20 response[idx],
@@ -29,8 +25,3 @@ export function matchingBrace(ctx: Ctx): Cmd {
29 editor.revealRange(editor.selection); 25 editor.revealRange(editor.selection);
30 }; 26 };
31} 27}
32
33interface FindMatchingBraceParams {
34 textDocument: lc.TextDocumentIdentifier;
35 offsets: lc.Position[];
36}
diff --git a/editors/code/src/commands/on_enter.ts b/editors/code/src/commands/on_enter.ts
index 27ae8ec23..285849db7 100644
--- a/editors/code/src/commands/on_enter.ts
+++ b/editors/code/src/commands/on_enter.ts
@@ -1,7 +1,7 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as ra from '../rust-analyzer-api';
3 3
4import { applySourceChange, SourceChange } from '../source_change'; 4import { applySourceChange } from '../source_change';
5import { Cmd, Ctx } from '../ctx'; 5import { Cmd, Ctx } from '../ctx';
6 6
7async function handleKeypress(ctx: Ctx) { 7async function handleKeypress(ctx: Ctx) {
@@ -10,22 +10,15 @@ async function handleKeypress(ctx: Ctx) {
10 10
11 if (!editor || !client) return false; 11 if (!editor || !client) return false;
12 12
13 const request: lc.TextDocumentPositionParams = { 13 const change = await client.sendRequest(ra.onEnter, {
14 textDocument: { uri: editor.document.uri.toString() }, 14 textDocument: { uri: editor.document.uri.toString() },
15 position: client.code2ProtocolConverter.asPosition( 15 position: client.code2ProtocolConverter.asPosition(
16 editor.selection.active, 16 editor.selection.active,
17 ), 17 ),
18 }; 18 }).catch(_error => {
19 const change = await client.sendRequest<undefined | SourceChange>( 19 // client.logFailedRequest(OnEnterRequest.type, error);
20 'rust-analyzer/onEnter', 20 return null;
21 request, 21 });
22 ).catch(
23 (_error: any) => {
24 // FIXME: switch to the more modern (?) typed request infrastructure
25 // client.logFailedRequest(OnEnterRequest.type, error);
26 return Promise.resolve(null);
27 }
28 );
29 if (!change) return false; 22 if (!change) return false;
30 23
31 await applySourceChange(ctx, change); 24 await applySourceChange(ctx, change);
diff --git a/editors/code/src/commands/parent_module.ts b/editors/code/src/commands/parent_module.ts
index bf40b4021..8f78ddd71 100644
--- a/editors/code/src/commands/parent_module.ts
+++ b/editors/code/src/commands/parent_module.ts
@@ -1,5 +1,5 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as ra from '../rust-analyzer-api';
3 3
4import { Ctx, Cmd } from '../ctx'; 4import { Ctx, Cmd } from '../ctx';
5 5
@@ -9,16 +9,12 @@ export function parentModule(ctx: Ctx): Cmd {
9 const client = ctx.client; 9 const client = ctx.client;
10 if (!editor || !client) return; 10 if (!editor || !client) return;
11 11
12 const request: lc.TextDocumentPositionParams = { 12 const response = await client.sendRequest(ra.parentModule, {
13 textDocument: { uri: editor.document.uri.toString() }, 13 textDocument: { uri: editor.document.uri.toString() },
14 position: client.code2ProtocolConverter.asPosition( 14 position: client.code2ProtocolConverter.asPosition(
15 editor.selection.active, 15 editor.selection.active,
16 ), 16 ),
17 }; 17 });
18 const response = await client.sendRequest<lc.Location[]>(
19 'rust-analyzer/parentModule',
20 request,
21 );
22 const loc = response[0]; 18 const loc = response[0];
23 if (loc == null) return; 19 if (loc == null) return;
24 20
diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts
index 7919997ce..06b513466 100644
--- a/editors/code/src/commands/runnables.ts
+++ b/editors/code/src/commands/runnables.ts
@@ -1,5 +1,6 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as lc from 'vscode-languageclient';
3import * as ra from '../rust-analyzer-api';
3 4
4import { Ctx, Cmd } from '../ctx'; 5import { Ctx, Cmd } from '../ctx';
5 6
@@ -14,16 +15,13 @@ export function run(ctx: Ctx): Cmd {
14 const textDocument: lc.TextDocumentIdentifier = { 15 const textDocument: lc.TextDocumentIdentifier = {
15 uri: editor.document.uri.toString(), 16 uri: editor.document.uri.toString(),
16 }; 17 };
17 const params: RunnablesParams = { 18
19 const runnables = await client.sendRequest(ra.runnables, {
18 textDocument, 20 textDocument,
19 position: client.code2ProtocolConverter.asPosition( 21 position: client.code2ProtocolConverter.asPosition(
20 editor.selection.active, 22 editor.selection.active,
21 ), 23 ),
22 }; 24 });
23 const runnables = await client.sendRequest<Runnable[]>(
24 'rust-analyzer/runnables',
25 params,
26 );
27 const items: RunnableQuickPick[] = []; 25 const items: RunnableQuickPick[] = [];
28 if (prevRunnable) { 26 if (prevRunnable) {
29 items.push(prevRunnable); 27 items.push(prevRunnable);
@@ -48,7 +46,7 @@ export function run(ctx: Ctx): Cmd {
48} 46}
49 47
50export function runSingle(ctx: Ctx): Cmd { 48export function runSingle(ctx: Ctx): Cmd {
51 return async (runnable: Runnable) => { 49 return async (runnable: ra.Runnable) => {
52 const editor = ctx.activeRustEditor; 50 const editor = ctx.activeRustEditor;
53 if (!editor) return; 51 if (!editor) return;
54 52
@@ -64,26 +62,13 @@ export function runSingle(ctx: Ctx): Cmd {
64 }; 62 };
65} 63}
66 64
67interface RunnablesParams {
68 textDocument: lc.TextDocumentIdentifier;
69 position?: lc.Position;
70}
71
72interface Runnable {
73 label: string;
74 bin: string;
75 args: string[];
76 env: { [index: string]: string };
77 cwd?: string;
78}
79
80class RunnableQuickPick implements vscode.QuickPickItem { 65class RunnableQuickPick implements vscode.QuickPickItem {
81 public label: string; 66 public label: string;
82 public description?: string | undefined; 67 public description?: string | undefined;
83 public detail?: string | undefined; 68 public detail?: string | undefined;
84 public picked?: boolean | undefined; 69 public picked?: boolean | undefined;
85 70
86 constructor(public runnable: Runnable) { 71 constructor(public runnable: ra.Runnable) {
87 this.label = runnable.label; 72 this.label = runnable.label;
88 } 73 }
89} 74}
@@ -96,7 +81,7 @@ interface CargoTaskDefinition extends vscode.TaskDefinition {
96 env?: { [key: string]: string }; 81 env?: { [key: string]: string };
97} 82}
98 83
99function createTask(spec: Runnable): vscode.Task { 84function createTask(spec: ra.Runnable): vscode.Task {
100 const TASK_SOURCE = 'Rust'; 85 const TASK_SOURCE = 'Rust';
101 const definition: CargoTaskDefinition = { 86 const definition: CargoTaskDefinition = {
102 type: 'cargo', 87 type: 'cargo',
diff --git a/editors/code/src/commands/ssr.ts b/editors/code/src/commands/ssr.ts
index 9b814612a..eee48c693 100644
--- a/editors/code/src/commands/ssr.ts
+++ b/editors/code/src/commands/ssr.ts
@@ -1,6 +1,8 @@
1import { Ctx, Cmd } from '../ctx';
2import { applySourceChange, SourceChange } from '../source_change';
3import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as ra from "../rust-analyzer-api";
3
4import { Ctx, Cmd } from '../ctx';
5import { applySourceChange } from '../source_change';
4 6
5export function ssr(ctx: Ctx): Cmd { 7export function ssr(ctx: Ctx): Cmd {
6 return async () => { 8 return async () => {
@@ -21,16 +23,8 @@ export function ssr(ctx: Ctx): Cmd {
21 23
22 if (!request) return; 24 if (!request) return;
23 25
24 const ssrRequest: SsrRequest = { arg: request }; 26 const change = await client.sendRequest(ra.ssr, { arg: request });
25 const change = await client.sendRequest<SourceChange>(
26 'rust-analyzer/ssr',
27 ssrRequest,
28 );
29 27
30 await applySourceChange(ctx, change); 28 await applySourceChange(ctx, change);
31 }; 29 };
32} 30}
33
34interface SsrRequest {
35 arg: string;
36}
diff --git a/editors/code/src/commands/syntax_tree.ts b/editors/code/src/commands/syntax_tree.ts
index 2887c96c8..7218bfb90 100644
--- a/editors/code/src/commands/syntax_tree.ts
+++ b/editors/code/src/commands/syntax_tree.ts
@@ -1,5 +1,5 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as ra from '../rust-analyzer-api';
3 3
4import { Ctx, Cmd } from '../ctx'; 4import { Ctx, Cmd } from '../ctx';
5 5
@@ -61,13 +61,8 @@ function afterLs(f: () => void) {
61 setTimeout(f, 10); 61 setTimeout(f, 10);
62} 62}
63 63
64interface SyntaxTreeParams {
65 textDocument: lc.TextDocumentIdentifier;
66 range?: lc.Range;
67}
68 64
69class TextDocumentContentProvider 65class TextDocumentContentProvider implements vscode.TextDocumentContentProvider {
70 implements vscode.TextDocumentContentProvider {
71 uri = vscode.Uri.parse('rust-analyzer://syntaxtree'); 66 uri = vscode.Uri.parse('rust-analyzer://syntaxtree');
72 eventEmitter = new vscode.EventEmitter<vscode.Uri>(); 67 eventEmitter = new vscode.EventEmitter<vscode.Uri>();
73 68
@@ -79,23 +74,15 @@ class TextDocumentContentProvider
79 const client = this.ctx.client; 74 const client = this.ctx.client;
80 if (!editor || !client) return ''; 75 if (!editor || !client) return '';
81 76
82 let range: lc.Range | undefined;
83
84 // When the range based query is enabled we take the range of the selection 77 // When the range based query is enabled we take the range of the selection
85 if (uri.query === 'range=true') { 78 const range = uri.query === 'range=true' && !editor.selection.isEmpty
86 range = editor.selection.isEmpty 79 ? client.code2ProtocolConverter.asRange(editor.selection)
87 ? undefined 80 : null;
88 : client.code2ProtocolConverter.asRange(editor.selection);
89 }
90 81
91 const request: SyntaxTreeParams = { 82 return client.sendRequest(ra.syntaxTree, {
92 textDocument: { uri: editor.document.uri.toString() }, 83 textDocument: { uri: editor.document.uri.toString() },
93 range, 84 range,
94 }; 85 });
95 return client.sendRequest<string>(
96 'rust-analyzer/syntaxTree',
97 request,
98 );
99 } 86 }
100 87
101 get onDidChange(): vscode.Event<vscode.Uri> { 88 get onDidChange(): vscode.Event<vscode.Uri> {
diff --git a/editors/code/src/highlighting.ts b/editors/code/src/highlighting.ts
index 77b4a1a68..3e0cbdc56 100644
--- a/editors/code/src/highlighting.ts
+++ b/editors/code/src/highlighting.ts
@@ -1,5 +1,5 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as ra from './rust-analyzer-api';
3 3
4import { ColorTheme, TextMateRuleSettings } from './color_theme'; 4import { ColorTheme, TextMateRuleSettings } from './color_theme';
5 5
@@ -8,29 +8,25 @@ import { sendRequestWithRetry } from './util';
8 8
9export function activateHighlighting(ctx: Ctx) { 9export function activateHighlighting(ctx: Ctx) {
10 const highlighter = new Highlighter(ctx); 10 const highlighter = new Highlighter(ctx);
11 const client = ctx.client;
12 if (client != null) {
13 client.onNotification(
14 'rust-analyzer/publishDecorations',
15 (params: PublishDecorationsParams) => {
16 if (!ctx.config.highlightingOn) return;
17
18 const targetEditor = vscode.window.visibleTextEditors.find(
19 editor => {
20 const unescapedUri = unescape(
21 editor.document.uri.toString(),
22 );
23 // Unescaped URI looks like:
24 // file:///c:/Workspace/ra-test/src/main.rs
25 return unescapedUri === params.uri;
26 },
27 );
28 if (!targetEditor) return;
29 11
30 highlighter.setHighlights(targetEditor, params.decorations); 12 ctx.client.onNotification(ra.publishDecorations, params => {
13 if (!ctx.config.highlightingOn) return;
14
15 const targetEditor = vscode.window.visibleTextEditors.find(
16 editor => {
17 const unescapedUri = unescape(
18 editor.document.uri.toString(),
19 );
20 // Unescaped URI looks like:
21 // file:///c:/Workspace/ra-test/src/main.rs
22 return unescapedUri === params.uri;
31 }, 23 },
32 ); 24 );
33 } 25 if (!targetEditor) return;
26
27 highlighter.setHighlights(targetEditor, params.decorations);
28 });
29
34 30
35 vscode.workspace.onDidChangeConfiguration( 31 vscode.workspace.onDidChangeConfiguration(
36 _ => highlighter.removeHighlights(), 32 _ => highlighter.removeHighlights(),
@@ -45,13 +41,10 @@ export function activateHighlighting(ctx: Ctx) {
45 const client = ctx.client; 41 const client = ctx.client;
46 if (!client) return; 42 if (!client) return;
47 43
48 const params: lc.TextDocumentIdentifier = { 44 const decorations = await sendRequestWithRetry(
49 uri: editor.document.uri.toString(),
50 };
51 const decorations = await sendRequestWithRetry<Decoration[]>(
52 client, 45 client,
53 'rust-analyzer/decorationsRequest', 46 ra.decorationsRequest,
54 params, 47 { uri: editor.document.uri.toString() },
55 ); 48 );
56 highlighter.setHighlights(editor, decorations); 49 highlighter.setHighlights(editor, decorations);
57 }, 50 },
@@ -60,17 +53,6 @@ export function activateHighlighting(ctx: Ctx) {
60 ); 53 );
61} 54}
62 55
63interface PublishDecorationsParams {
64 uri: string;
65 decorations: Decoration[];
66}
67
68interface Decoration {
69 range: lc.Range;
70 tag: string;
71 bindingHash?: string;
72}
73
74// Based on this HSL-based color generator: https://gist.github.com/bendc/76c48ce53299e6078a76 56// Based on this HSL-based color generator: https://gist.github.com/bendc/76c48ce53299e6078a76
75function fancify(seed: string, shade: 'light' | 'dark') { 57function fancify(seed: string, shade: 'light' | 'dark') {
76 const random = randomU32Numbers(hashString(seed)); 58 const random = randomU32Numbers(hashString(seed));
@@ -108,7 +90,7 @@ class Highlighter {
108 this.decorations = null; 90 this.decorations = null;
109 } 91 }
110 92
111 public setHighlights(editor: vscode.TextEditor, highlights: Decoration[]) { 93 public setHighlights(editor: vscode.TextEditor, highlights: ra.Decoration[]) {
112 const client = this.ctx.client; 94 const client = this.ctx.client;
113 if (!client) return; 95 if (!client) return;
114 // Initialize decorations if necessary 96 // Initialize decorations if necessary
diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts
index 5f9229efb..5951cf1b4 100644
--- a/editors/code/src/inlay_hints.ts
+++ b/editors/code/src/inlay_hints.ts
@@ -1,5 +1,5 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as ra from './rust-analyzer-api';
3 3
4import { Ctx } from './ctx'; 4import { Ctx } from './ctx';
5import { log, sendRequestWithRetry } from './util'; 5import { log, sendRequestWithRetry } from './util';
@@ -39,16 +39,6 @@ export function activateInlayHints(ctx: Ctx) {
39 void hintsUpdater.setEnabled(ctx.config.displayInlayHints); 39 void hintsUpdater.setEnabled(ctx.config.displayInlayHints);
40} 40}
41 41
42interface InlayHintsParams {
43 textDocument: lc.TextDocumentIdentifier;
44}
45
46interface InlayHint {
47 range: vscode.Range;
48 kind: "TypeHint" | "ParameterHint";
49 label: string;
50}
51
52const typeHintDecorationType = vscode.window.createTextEditorDecorationType({ 42const typeHintDecorationType = vscode.window.createTextEditorDecorationType({
53 after: { 43 after: {
54 color: new vscode.ThemeColor('rust_analyzer.inlayHint'), 44 color: new vscode.ThemeColor('rust_analyzer.inlayHint'),
@@ -107,9 +97,9 @@ class HintsUpdater {
107 if (newHints == null) return; 97 if (newHints == null) return;
108 98
109 const newTypeDecorations = newHints 99 const newTypeDecorations = newHints
110 .filter(hint => hint.kind === 'TypeHint') 100 .filter(hint => hint.kind === ra.InlayKind.TypeHint)
111 .map(hint => ({ 101 .map(hint => ({
112 range: hint.range, 102 range: this.ctx.client.protocol2CodeConverter.asRange(hint.range),
113 renderOptions: { 103 renderOptions: {
114 after: { 104 after: {
115 contentText: `: ${hint.label}`, 105 contentText: `: ${hint.label}`,
@@ -119,9 +109,9 @@ class HintsUpdater {
119 this.setTypeDecorations(editor, newTypeDecorations); 109 this.setTypeDecorations(editor, newTypeDecorations);
120 110
121 const newParameterDecorations = newHints 111 const newParameterDecorations = newHints
122 .filter(hint => hint.kind === 'ParameterHint') 112 .filter(hint => hint.kind === ra.InlayKind.ParameterHint)
123 .map(hint => ({ 113 .map(hint => ({
124 range: hint.range, 114 range: this.ctx.client.protocol2CodeConverter.asRange(hint.range),
125 renderOptions: { 115 renderOptions: {
126 before: { 116 before: {
127 contentText: `${hint.label}: `, 117 contentText: `${hint.label}: `,
@@ -151,20 +141,15 @@ class HintsUpdater {
151 ); 141 );
152 } 142 }
153 143
154 private async queryHints(documentUri: string): Promise<InlayHint[] | null> { 144 private async queryHints(documentUri: string): Promise<ra.InlayHint[] | null> {
155 this.pending.get(documentUri)?.cancel(); 145 this.pending.get(documentUri)?.cancel();
156 146
157 const tokenSource = new vscode.CancellationTokenSource(); 147 const tokenSource = new vscode.CancellationTokenSource();
158 this.pending.set(documentUri, tokenSource); 148 this.pending.set(documentUri, tokenSource);
159 149
160 const request: InlayHintsParams = { textDocument: { uri: documentUri } }; 150 const request = { textDocument: { uri: documentUri } };
161 151
162 return sendRequestWithRetry<InlayHint[]>( 152 return sendRequestWithRetry(this.ctx.client, ra.inlayHints, request, tokenSource.token)
163 this.ctx.client,
164 'rust-analyzer/inlayHints',
165 request,
166 tokenSource.token
167 )
168 .catch(_ => null) 153 .catch(_ => null)
169 .finally(() => { 154 .finally(() => {
170 if (!tokenSource.token.isCancellationRequested) { 155 if (!tokenSource.token.isCancellationRequested) {
diff --git a/editors/code/src/installation/fetch_artifact_release_info.ts b/editors/code/src/installation/fetch_artifact_release_info.ts
index 1b6fc8d48..b1b5a3485 100644
--- a/editors/code/src/installation/fetch_artifact_release_info.ts
+++ b/editors/code/src/installation/fetch_artifact_release_info.ts
@@ -4,41 +4,61 @@ import { log } from "../util";
4 4
5const GITHUB_API_ENDPOINT_URL = "https://api.github.com"; 5const GITHUB_API_ENDPOINT_URL = "https://api.github.com";
6 6
7
8/** 7/**
9 * Fetches the release with `releaseTag` (or just latest release when not specified) 8 * Fetches the release with `releaseTag` from GitHub `repo` and
10 * from GitHub `repo` and returns metadata about `artifactFileName` shipped with 9 * returns metadata about `artifactFileName` shipped with
11 * this release or `null` if no such artifact was published. 10 * this release.
11 *
12 * @throws Error upon network failure or if no such repository, release, or artifact exists.
12 */ 13 */
13export async function fetchArtifactReleaseInfo( 14export async function fetchArtifactReleaseInfo(
14 repo: GithubRepo, artifactFileName: string, releaseTag?: string 15 repo: GithubRepo,
15): Promise<null | ArtifactReleaseInfo> { 16 artifactFileName: string,
17 releaseTag: string
18): Promise<ArtifactReleaseInfo> {
16 19
17 const repoOwner = encodeURIComponent(repo.owner); 20 const repoOwner = encodeURIComponent(repo.owner);
18 const repoName = encodeURIComponent(repo.name); 21 const repoName = encodeURIComponent(repo.name);
19 22
20 const apiEndpointPath = releaseTag 23 const apiEndpointPath = `/repos/${repoOwner}/${repoName}/releases/tags/${releaseTag}`;
21 ? `/repos/${repoOwner}/${repoName}/releases/tags/${releaseTag}`
22 : `/repos/${repoOwner}/${repoName}/releases/latest`;
23 24
24 const requestUrl = GITHUB_API_ENDPOINT_URL + apiEndpointPath; 25 const requestUrl = GITHUB_API_ENDPOINT_URL + apiEndpointPath;
25 26
26 // We skip runtime type checks for simplicity (here we cast from `any` to `GithubRelease`)
27
28 log.debug("Issuing request for released artifacts metadata to", requestUrl); 27 log.debug("Issuing request for released artifacts metadata to", requestUrl);
29 28
30 // FIXME: handle non-ok response 29 const response = await fetch(requestUrl, { headers: { Accept: "application/vnd.github.v3+json" } });
31 const response: GithubRelease = await fetch(requestUrl, {
32 headers: { Accept: "application/vnd.github.v3+json" }
33 })
34 .then(res => res.json());
35 30
36 const artifact = response.assets.find(artifact => artifact.name === artifactFileName); 31 if (!response.ok) {
32 log.error("Error fetching artifact release info", {
33 requestUrl,
34 releaseTag,
35 artifactFileName,
36 response: {
37 headers: response.headers,
38 status: response.status,
39 body: await response.text(),
40 }
41 });
42
43 throw new Error(
44 `Got response ${response.status} when trying to fetch ` +
45 `"${artifactFileName}" artifact release info for ${releaseTag} release`
46 );
47 }
37 48
38 if (!artifact) return null; 49 // We skip runtime type checks for simplicity (here we cast from `any` to `GithubRelease`)
50 const release: GithubRelease = await response.json();
51
52 const artifact = release.assets.find(artifact => artifact.name === artifactFileName);
53
54 if (!artifact) {
55 throw new Error(
56 `Artifact ${artifactFileName} was not found in ${release.name} release!`
57 );
58 }
39 59
40 return { 60 return {
41 releaseName: response.name, 61 releaseName: release.name,
42 downloadUrl: artifact.browser_download_url 62 downloadUrl: artifact.browser_download_url
43 }; 63 };
44 64
diff --git a/editors/code/src/installation/server.ts b/editors/code/src/installation/server.ts
index 9de257dd5..cb5e56844 100644
--- a/editors/code/src/installation/server.ts
+++ b/editors/code/src/installation/server.ts
@@ -63,7 +63,7 @@ export async function ensureServerBinary(source: null | BinarySource): Promise<n
63 63
64async function downloadServer(source: BinarySource.GithubRelease): Promise<boolean> { 64async function downloadServer(source: BinarySource.GithubRelease): Promise<boolean> {
65 try { 65 try {
66 const releaseInfo = (await fetchArtifactReleaseInfo(source.repo, source.file, source.version))!; 66 const releaseInfo = await fetchArtifactReleaseInfo(source.repo, source.file, source.version);
67 67
68 await downloadArtifact(releaseInfo, source.file, source.dir, "language server"); 68 await downloadArtifact(releaseInfo, source.file, source.dir, "language server");
69 await setServerVersion(source.storage, releaseInfo.releaseName); 69 await setServerVersion(source.storage, releaseInfo.releaseName);
diff --git a/editors/code/src/rust-analyzer-api.ts b/editors/code/src/rust-analyzer-api.ts
new file mode 100644
index 000000000..c5a010e94
--- /dev/null
+++ b/editors/code/src/rust-analyzer-api.ts
@@ -0,0 +1,117 @@
1/**
2 * This file mirrors `crates/rust-analyzer/src/req.rs` declarations.
3 */
4
5import * as lc from "vscode-languageclient";
6
7type Option<T> = null | T;
8type Vec<T> = T[];
9type FxHashMap<K extends PropertyKey, V> = Record<K, V>;
10
11function request<TParams, TResult>(method: string) {
12 return new lc.RequestType<TParams, TResult, unknown>(`rust-analyzer/${method}`);
13}
14function notification<TParam>(method: string) {
15 return new lc.NotificationType<TParam>(method);
16}
17
18
19export const analyzerStatus = request<null, string>("analyzerStatus");
20
21
22export const collectGarbage = request<null, null>("collectGarbage");
23
24
25export interface SyntaxTreeParams {
26 textDocument: lc.TextDocumentIdentifier;
27 range: Option<lc.Range>;
28}
29export const syntaxTree = request<SyntaxTreeParams, string>("syntaxTree");
30
31
32export interface ExpandMacroParams {
33 textDocument: lc.TextDocumentIdentifier;
34 position: Option<lc.Position>;
35}
36export interface ExpandedMacro {
37 name: string;
38 expansion: string;
39}
40export const expandMacro = request<ExpandMacroParams, Option<ExpandedMacro>>("expandMacro");
41
42
43export interface FindMatchingBraceParams {
44 textDocument: lc.TextDocumentIdentifier;
45 offsets: Vec<lc.Position>;
46}
47export const findMatchingBrace = request<FindMatchingBraceParams, Vec<lc.Position>>("findMatchingBrace");
48
49
50export interface PublishDecorationsParams {
51 uri: string;
52 decorations: Vec<Decoration>;
53}
54export interface Decoration {
55 range: lc.Range;
56 tag: string;
57 bindingHash: Option<string>;
58}
59export const decorationsRequest = request<lc.TextDocumentIdentifier, Vec<Decoration>>("decorationsRequest");
60
61
62export const parentModule = request<lc.TextDocumentPositionParams, Vec<lc.Location>>("parentModule");
63
64
65export interface JoinLinesParams {
66 textDocument: lc.TextDocumentIdentifier;
67 range: lc.Range;
68}
69export const joinLines = request<JoinLinesParams, SourceChange>("joinLines");
70
71
72export const onEnter = request<lc.TextDocumentPositionParams, Option<SourceChange>>("onEnter");
73
74export interface RunnablesParams {
75 textDocument: lc.TextDocumentIdentifier;
76 position: Option<lc.Position>;
77}
78export interface Runnable {
79 range: lc.Range;
80 label: string;
81 bin: string;
82 args: Vec<string>;
83 env: FxHashMap<string, string>;
84 cwd: Option<string>;
85}
86export const runnables = request<RunnablesParams, Vec<Runnable>>("runnables");
87
88
89export const enum InlayKind {
90 TypeHint = "TypeHint",
91 ParameterHint = "ParameterHint",
92}
93export interface InlayHint {
94 range: lc.Range;
95 kind: InlayKind;
96 label: string;
97}
98export interface InlayHintsParams {
99 textDocument: lc.TextDocumentIdentifier;
100}
101export const inlayHints = request<InlayHintsParams, Vec<InlayHint>>("inlayHints");
102
103
104export interface SsrParams {
105 arg: string;
106}
107export const ssr = request<SsrParams, SourceChange>("ssr");
108
109
110export const publishDecorations = notification<PublishDecorationsParams>("publishDecorations");
111
112
113export interface SourceChange {
114 label: string;
115 workspaceEdit: lc.WorkspaceEdit;
116 cursorPosition: Option<lc.TextDocumentPositionParams>;
117}
diff --git a/editors/code/src/source_change.ts b/editors/code/src/source_change.ts
index a336269ba..399a150c6 100644
--- a/editors/code/src/source_change.ts
+++ b/editors/code/src/source_change.ts
@@ -1,15 +1,10 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as lc from 'vscode-languageclient';
3import * as ra from './rust-analyzer-api';
3 4
4import { Ctx } from './ctx'; 5import { Ctx } from './ctx';
5 6
6export interface SourceChange { 7export async function applySourceChange(ctx: Ctx, change: ra.SourceChange) {
7 label: string;
8 workspaceEdit: lc.WorkspaceEdit;
9 cursorPosition?: lc.TextDocumentPositionParams;
10}
11
12export async function applySourceChange(ctx: Ctx, change: SourceChange) {
13 const client = ctx.client; 8 const client = ctx.client;
14 if (!client) return; 9 if (!client) return;
15 10
diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts
index 2f18f85a3..68c2a94d0 100644
--- a/editors/code/src/util.ts
+++ b/editors/code/src/util.ts
@@ -20,21 +20,21 @@ export const log = {
20 } 20 }
21}; 21};
22 22
23export async function sendRequestWithRetry<R>( 23export async function sendRequestWithRetry<TParam, TRet>(
24 client: lc.LanguageClient, 24 client: lc.LanguageClient,
25 method: string, 25 reqType: lc.RequestType<TParam, TRet, unknown>,
26 param: unknown, 26 param: TParam,
27 token?: vscode.CancellationToken, 27 token?: vscode.CancellationToken,
28): Promise<R> { 28): Promise<TRet> {
29 for (const delay of [2, 4, 6, 8, 10, null]) { 29 for (const delay of [2, 4, 6, 8, 10, null]) {
30 try { 30 try {
31 return await (token 31 return await (token
32 ? client.sendRequest(method, param, token) 32 ? client.sendRequest(reqType, param, token)
33 : client.sendRequest(method, param) 33 : client.sendRequest(reqType, param)
34 ); 34 );
35 } catch (error) { 35 } catch (error) {
36 if (delay === null) { 36 if (delay === null) {
37 log.error("LSP request timed out", { method, param, error }); 37 log.error("LSP request timed out", { method: reqType.method, param, error });
38 throw error; 38 throw error;
39 } 39 }
40 40
@@ -43,7 +43,7 @@ export async function sendRequestWithRetry<R>(
43 } 43 }
44 44
45 if (error.code !== lc.ErrorCodes.ContentModified) { 45 if (error.code !== lc.ErrorCodes.ContentModified) {
46 log.error("LSP request failed", { method, param, error }); 46 log.error("LSP request failed", { method: reqType.method, param, error });
47 throw error; 47 throw error;
48 } 48 }
49 49