aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml57
-rw-r--r--crates/ra_assists/src/add_missing_impl_members.rs123
-rw-r--r--crates/ra_assists/src/flip_eq_operands.rs86
-rw-r--r--crates/ra_assists/src/lib.rs3
-rw-r--r--crates/ra_hir/src/expr.rs4
-rw-r--r--crates/ra_syntax/src/ast.rs78
-rw-r--r--docs/dev/README.md6
7 files changed, 272 insertions, 85 deletions
diff --git a/.travis.yml b/.travis.yml
index 8d10a43f0..4dffdea34 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,42 +4,49 @@ before_cache:
4 - rm -fr ./target/debug/{deps,.fingerprint}/{*ra_*,*test*,*tools*,*gen_lsp*,*thread_worker*} 4 - rm -fr ./target/debug/{deps,.fingerprint}/{*ra_*,*test*,*tools*,*gen_lsp*,*thread_worker*}
5 - rm -f ./target/.rustc_info.json 5 - rm -f ./target/.rustc_info.json
6 6
7build: &rust_build
8 language: rust
9 rust: stable
10 script:
11 - rustup component add rustfmt
12 - rustup component add rust-src
13 - cargo test --no-run # let's measure compile time separately
14 - cargo test
15 env:
16 - RUSTFLAGS="-D warnings", CARGO_INCREMENTAL=0
17
18matrix: 7matrix:
19 include: 8 include:
20 - os: linux 9 - os: linux
21 <<: *rust_build 10 language: rust
11 rust: stable
12 script:
13 - rustup component add rustfmt
14 - rustup component add rust-src
15 - cargo test --no-run # let's measure compile time separately
16 - cargo test
17 env:
18 - RUSTFLAGS="-D warnings", CARGO_INCREMENTAL=0
19
20 - os: linux
21 if: branch = master AND type = push
22 before_stript:
23 - DEPLOY_DOCS=1
24 language: rust
25 rust: stable
26 script:
27 - cargo doc --all --no-deps
28 env:
29 - RUSTFLAGS="-D warnings", CARGO_INCREMENTAL=0
30
22 - language: node_js 31 - language: node_js
23 node_js: node 32 node_js: node
24 before_script: false 33 before_script: false
25 script: 34 script:
26 - cd editors/code && npm ci && npm run travis 35 - cd editors/code && npm ci && npm run travis
27 36
28 - os: windows
29 if: branch = master AND type = push
30 before_script:
31 - dos2unix ./crates/ra_syntax/tests/data/parser/**/*.txt
32 - dos2unix ./crates/ra_syntax/tests/data/parser/**/*.rs
33 <<: *rust_build
34
35 allow_failures:
36 # Because Travis-Windows-Rust can be flaky
37 # We still support Windows and want the tests to be succeeding,
38 # but there are too many spurious failures
39 - os: windows
40
41branches: 37branches:
42 only: 38 only:
43 - staging 39 - staging
44 - master 40 - master
45 - trying 41 - trying
42
43deploy:
44 provider: pages
45 skip-cleanup: true
46 github-token: $DOCS_TOKEN # Set in the settings page of your repository, as a secure variable
47 keep-history: true
48 local-dir: target/doc
49 branch: gh-pages
50 on:
51 branch: master
52 condition: $DEPLOY_DOCS = 1
diff --git a/crates/ra_assists/src/add_missing_impl_members.rs b/crates/ra_assists/src/add_missing_impl_members.rs
index 4435c4b5d..e13f54c4f 100644
--- a/crates/ra_assists/src/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/add_missing_impl_members.rs
@@ -9,7 +9,35 @@ use ra_fmt::{leading_indent, reindent};
9 9
10use itertools::Itertools; 10use itertools::Itertools;
11 11
12pub(crate) fn add_missing_impl_members(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 12enum AddMissingImplMembersMode {
13 DefaultMethodsOnly,
14 NoDefaultMethods,
15}
16
17pub(crate) fn add_missing_impl_members(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
18 add_missing_impl_members_inner(
19 ctx,
20 AddMissingImplMembersMode::NoDefaultMethods,
21 "add_impl_missing_members",
22 "add missing impl members",
23 )
24}
25
26pub(crate) fn add_missing_default_members(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
27 add_missing_impl_members_inner(
28 ctx,
29 AddMissingImplMembersMode::DefaultMethodsOnly,
30 "add_impl_default_members",
31 "add impl default members",
32 )
33}
34
35fn add_missing_impl_members_inner(
36 mut ctx: AssistCtx<impl HirDatabase>,
37 mode: AddMissingImplMembersMode,
38 assist_id: &'static str,
39 label: &'static str,
40) -> Option<Assist> {
13 let impl_node = ctx.node_at_offset::<ast::ImplBlock>()?; 41 let impl_node = ctx.node_at_offset::<ast::ImplBlock>()?;
14 let impl_item_list = impl_node.item_list()?; 42 let impl_item_list = impl_node.item_list()?;
15 43
@@ -35,6 +63,10 @@ pub(crate) fn add_missing_impl_members(mut ctx: AssistCtx<impl HirDatabase>) ->
35 trait_fns 63 trait_fns
36 .into_iter() 64 .into_iter()
37 .filter(|t| def_name(t).is_some()) 65 .filter(|t| def_name(t).is_some())
66 .filter(|t| match mode {
67 AddMissingImplMembersMode::DefaultMethodsOnly => t.body().is_some(),
68 AddMissingImplMembersMode::NoDefaultMethods => t.body().is_none(),
69 })
38 .filter(|t| impl_fns.iter().all(|i| def_name(i) != def_name(t))) 70 .filter(|t| impl_fns.iter().all(|i| def_name(i) != def_name(t)))
39 .collect() 71 .collect()
40 }; 72 };
@@ -42,7 +74,7 @@ pub(crate) fn add_missing_impl_members(mut ctx: AssistCtx<impl HirDatabase>) ->
42 return None; 74 return None;
43 } 75 }
44 76
45 ctx.add_action(AssistId("add_impl_missing_members"), "add missing impl members", |edit| { 77 ctx.add_action(AssistId(assist_id), label, |edit| {
46 let (parent_indent, indent) = { 78 let (parent_indent, indent) = {
47 // FIXME: Find a way to get the indent already used in the file. 79 // FIXME: Find a way to get the indent already used in the file.
48 // Now, we copy the indent of first item or indent with 4 spaces relative to impl block 80 // Now, we copy the indent of first item or indent with 4 spaces relative to impl block
@@ -103,11 +135,13 @@ fn build_func_body(def: &ast::FnDef) -> String {
103 let mut buf = String::new(); 135 let mut buf = String::new();
104 136
105 for child in def.syntax().children() { 137 for child in def.syntax().children() {
106 if child.kind() == SyntaxKind::SEMI { 138 match (child.prev_sibling().map(|c| c.kind()), child.kind()) {
107 buf.push_str(" { unimplemented!() }") 139 (_, SyntaxKind::SEMI) => buf.push_str(" { unimplemented!() }"),
108 } else { 140 (_, SyntaxKind::ATTR) | (_, SyntaxKind::COMMENT) => {}
109 child.text().push_to(&mut buf); 141 (Some(SyntaxKind::ATTR), SyntaxKind::WHITESPACE)
110 } 142 | (Some(SyntaxKind::COMMENT), SyntaxKind::WHITESPACE) => {}
143 _ => child.text().push_to(&mut buf),
144 };
111 } 145 }
112 146
113 buf.trim_end().to_string() 147 buf.trim_end().to_string()
@@ -180,8 +214,7 @@ struct S;
180 214
181impl Foo for S { 215impl Foo for S {
182 fn bar(&self) {} 216 fn bar(&self) {}
183 fn foo(&self) { unimplemented!() } 217 fn foo(&self) { unimplemented!() }<|>
184 fn baz(&self) -> u32 { 42 }<|>
185}", 218}",
186 ); 219 );
187 } 220 }
@@ -193,7 +226,7 @@ impl Foo for S {
193 " 226 "
194trait Foo { fn foo(&self); } 227trait Foo { fn foo(&self); }
195struct S; 228struct S;
196impl Foo for S {<|>}", 229impl Foo for S { <|> }",
197 " 230 "
198trait Foo { fn foo(&self); } 231trait Foo { fn foo(&self); }
199struct S; 232struct S;
@@ -232,8 +265,8 @@ impl Foo for S { <|> }",
232 } 265 }
233 266
234 #[test] 267 #[test]
235 fn test_ignore_unnamed_trait_members() { 268 fn test_ignore_unnamed_trait_members_and_default_methods() {
236 check_assist( 269 check_assist_not_applicable(
237 add_missing_impl_members, 270 add_missing_impl_members,
238 " 271 "
239trait Foo { 272trait Foo {
@@ -242,15 +275,6 @@ trait Foo {
242} 275}
243struct S; 276struct S;
244impl Foo for S { <|> }", 277impl Foo for S { <|> }",
245 "
246trait Foo {
247 fn (arg: u32);
248 fn valid(some: u32) -> bool { false }
249}
250struct S;
251impl Foo for S {
252 fn valid(some: u32) -> bool { false }<|>
253}",
254 ) 278 )
255 } 279 }
256 280
@@ -260,7 +284,7 @@ impl Foo for S {
260 add_missing_impl_members, 284 add_missing_impl_members,
261 " 285 "
262trait Foo { 286trait Foo {
263 fn valid(some: u32) -> bool { false } 287 fn valid(some: u32) -> bool;
264} 288}
265struct S; 289struct S;
266 290
@@ -269,15 +293,66 @@ mod my_mod {
269}", 293}",
270 " 294 "
271trait Foo { 295trait Foo {
272 fn valid(some: u32) -> bool { false } 296 fn valid(some: u32) -> bool;
273} 297}
274struct S; 298struct S;
275 299
276mod my_mod { 300mod my_mod {
277 impl crate::Foo for S { 301 impl crate::Foo for S {
278 fn valid(some: u32) -> bool { false }<|> 302 fn valid(some: u32) -> bool { unimplemented!() }<|>
303 }
304}",
305 )
306 }
307
308 #[test]
309 fn test_with_docstring_and_attrs() {
310 check_assist(
311 add_missing_impl_members,
312 r#"
313#[doc(alias = "test alias")]
314trait Foo {
315 /// doc string
316 #[must_use]
317 fn foo(&self);
318}
319struct S;
320impl Foo for S {}<|>"#,
321 r#"
322#[doc(alias = "test alias")]
323trait Foo {
324 /// doc string
325 #[must_use]
326 fn foo(&self);
327}
328struct S;
329impl Foo for S {
330 fn foo(&self) { unimplemented!() }<|>
331}"#,
332 )
279 } 333 }
334
335 #[test]
336 fn test_default_methods() {
337 check_assist(
338 add_missing_default_members,
339 "
340trait Foo {
341 fn valid(some: u32) -> bool { false }
342 fn foo(some: u32) -> bool;
343}
344struct S;
345impl Foo for S { <|> }",
346 "
347trait Foo {
348 fn valid(some: u32) -> bool { false }
349 fn foo(some: u32) -> bool;
350}
351struct S;
352impl Foo for S {
353 fn valid(some: u32) -> bool { false }<|>
280}", 354}",
281 ) 355 )
282 } 356 }
357
283} 358}
diff --git a/crates/ra_assists/src/flip_eq_operands.rs b/crates/ra_assists/src/flip_eq_operands.rs
new file mode 100644
index 000000000..df0bb689d
--- /dev/null
+++ b/crates/ra_assists/src/flip_eq_operands.rs
@@ -0,0 +1,86 @@
1use hir::db::HirDatabase;
2use ra_syntax::ast::{AstNode, BinExpr, BinOp};
3
4use crate::{AssistCtx, Assist, AssistId};
5
6pub(crate) fn flip_eq_operands(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
7 let expr = ctx.node_at_offset::<BinExpr>()?;
8 let lhs = expr.lhs()?.syntax();
9 let rhs = expr.rhs()?.syntax();
10 let op_range = expr.op()?.range();
11 let cursor_in_range = ctx.frange.range.is_subrange(&op_range);
12 let allowed_ops = [BinOp::EqualityTest, BinOp::NegatedEqualityTest];
13 let expr_op = expr.op_kind()?;
14 if !cursor_in_range || !allowed_ops.iter().any(|o| *o == expr_op) {
15 return None;
16 }
17 ctx.add_action(AssistId("flip_eq_operands"), "flip equality operands", |edit| {
18 edit.target(op_range);
19 edit.replace(lhs.range(), rhs.text());
20 edit.replace(rhs.range(), lhs.text());
21 });
22
23 ctx.build()
24}
25
26#[cfg(test)]
27mod tests {
28 use super::*;
29
30 use crate::helpers::{check_assist, check_assist_target};
31
32 #[test]
33 fn flip_eq_operands_for_simple_stmt() {
34 check_assist(
35 flip_eq_operands,
36 "fn f() { let res = 1 ==<|> 2; }",
37 "fn f() { let res = 2 ==<|> 1; }",
38 )
39 }
40
41 #[test]
42 fn flip_neq_operands_for_simple_stmt() {
43 check_assist(
44 flip_eq_operands,
45 "fn f() { let res = 1 !=<|> 2; }",
46 "fn f() { let res = 2 !=<|> 1; }",
47 )
48 }
49
50 #[test]
51 fn flip_eq_operands_for_complex_stmt() {
52 check_assist(
53 flip_eq_operands,
54 "fn f() { let res = (1 + 1) ==<|> (2 + 2); }",
55 "fn f() { let res = (2 + 2) ==<|> (1 + 1); }",
56 )
57 }
58
59 #[test]
60 fn flip_eq_operands_in_match_expr() {
61 check_assist(
62 flip_eq_operands,
63 r#"
64 fn dyn_eq(&self, other: &dyn Diagnostic) -> bool {
65 match other.downcast_ref::<Self>() {
66 None => false,
67 Some(it) => it ==<|> self,
68 }
69 }
70 "#,
71 r#"
72 fn dyn_eq(&self, other: &dyn Diagnostic) -> bool {
73 match other.downcast_ref::<Self>() {
74 None => false,
75 Some(it) => self ==<|> it,
76 }
77 }
78 "#,
79 )
80 }
81
82 #[test]
83 fn flip_eq_operands_target() {
84 check_assist_target(flip_eq_operands, "fn f() { let res = 1 ==<|> 2; }", "==")
85 }
86}
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 871b37f58..8900fbc4b 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -88,6 +88,7 @@ where
88mod add_derive; 88mod add_derive;
89mod add_impl; 89mod add_impl;
90mod flip_comma; 90mod flip_comma;
91mod flip_eq_operands;
91mod change_visibility; 92mod change_visibility;
92mod fill_match_arms; 93mod fill_match_arms;
93mod fill_struct_fields; 94mod fill_struct_fields;
@@ -106,12 +107,14 @@ fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assis
106 fill_match_arms::fill_match_arms, 107 fill_match_arms::fill_match_arms,
107 fill_struct_fields::fill_struct_fields, 108 fill_struct_fields::fill_struct_fields,
108 flip_comma::flip_comma, 109 flip_comma::flip_comma,
110 flip_eq_operands::flip_eq_operands,
109 introduce_variable::introduce_variable, 111 introduce_variable::introduce_variable,
110 replace_if_let_with_match::replace_if_let_with_match, 112 replace_if_let_with_match::replace_if_let_with_match,
111 split_import::split_import, 113 split_import::split_import,
112 remove_dbg::remove_dbg, 114 remove_dbg::remove_dbg,
113 auto_import::auto_import, 115 auto_import::auto_import,
114 add_missing_impl_members::add_missing_impl_members, 116 add_missing_impl_members::add_missing_impl_members,
117 add_missing_impl_members::add_missing_default_members,
115 ] 118 ]
116} 119}
117 120
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs
index 703d99d9b..c37fd0454 100644
--- a/crates/ra_hir/src/expr.rs
+++ b/crates/ra_hir/src/expr.rs
@@ -680,7 +680,7 @@ impl ExprCollector {
680 } 680 }
681 ast::ExprKind::PrefixExpr(e) => { 681 ast::ExprKind::PrefixExpr(e) => {
682 let expr = self.collect_expr_opt(e.expr()); 682 let expr = self.collect_expr_opt(e.expr());
683 if let Some(op) = e.op() { 683 if let Some(op) = e.op_kind() {
684 self.alloc_expr(Expr::UnaryOp { expr, op }, syntax_ptr) 684 self.alloc_expr(Expr::UnaryOp { expr, op }, syntax_ptr)
685 } else { 685 } else {
686 self.alloc_expr(Expr::Missing, syntax_ptr) 686 self.alloc_expr(Expr::Missing, syntax_ptr)
@@ -703,7 +703,7 @@ impl ExprCollector {
703 ast::ExprKind::BinExpr(e) => { 703 ast::ExprKind::BinExpr(e) => {
704 let lhs = self.collect_expr_opt(e.lhs()); 704 let lhs = self.collect_expr_opt(e.lhs());
705 let rhs = self.collect_expr_opt(e.rhs()); 705 let rhs = self.collect_expr_opt(e.rhs());
706 let op = e.op(); 706 let op = e.op_kind();
707 self.alloc_expr(Expr::BinaryOp { lhs, rhs, op }, syntax_ptr) 707 self.alloc_expr(Expr::BinaryOp { lhs, rhs, op }, syntax_ptr)
708 } 708 }
709 ast::ExprKind::TupleExpr(e) => { 709 ast::ExprKind::TupleExpr(e) => {
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs
index d8c2cb063..226208700 100644
--- a/crates/ra_syntax/src/ast.rs
+++ b/crates/ra_syntax/src/ast.rs
@@ -521,7 +521,7 @@ pub enum PrefixOp {
521} 521}
522 522
523impl PrefixExpr { 523impl PrefixExpr {
524 pub fn op(&self) -> Option<PrefixOp> { 524 pub fn op_kind(&self) -> Option<PrefixOp> {
525 match self.syntax().first_child()?.kind() { 525 match self.syntax().first_child()?.kind() {
526 STAR => Some(PrefixOp::Deref), 526 STAR => Some(PrefixOp::Deref),
527 EXCL => Some(PrefixOp::Not), 527 EXCL => Some(PrefixOp::Not),
@@ -529,6 +529,10 @@ impl PrefixExpr {
529 _ => None, 529 _ => None,
530 } 530 }
531 } 531 }
532
533 pub fn op(&self) -> Option<&SyntaxNode> {
534 self.syntax().first_child()
535 }
532} 536}
533 537
534#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 538#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
@@ -598,46 +602,54 @@ pub enum BinOp {
598} 602}
599 603
600impl BinExpr { 604impl BinExpr {
601 pub fn op(&self) -> Option<BinOp> { 605 fn op_details(&self) -> Option<(&SyntaxNode, BinOp)> {
602 self.syntax() 606 self.syntax()
603 .children() 607 .children()
604 .filter_map(|c| match c.kind() { 608 .filter_map(|c| match c.kind() {
605 PIPEPIPE => Some(BinOp::BooleanOr), 609 PIPEPIPE => Some((c, BinOp::BooleanOr)),
606 AMPAMP => Some(BinOp::BooleanAnd), 610 AMPAMP => Some((c, BinOp::BooleanAnd)),
607 EQEQ => Some(BinOp::EqualityTest), 611 EQEQ => Some((c, BinOp::EqualityTest)),
608 NEQ => Some(BinOp::NegatedEqualityTest), 612 NEQ => Some((c, BinOp::NegatedEqualityTest)),
609 LTEQ => Some(BinOp::LesserEqualTest), 613 LTEQ => Some((c, BinOp::LesserEqualTest)),
610 GTEQ => Some(BinOp::GreaterEqualTest), 614 GTEQ => Some((c, BinOp::GreaterEqualTest)),
611 L_ANGLE => Some(BinOp::LesserTest), 615 L_ANGLE => Some((c, BinOp::LesserTest)),
612 R_ANGLE => Some(BinOp::GreaterTest), 616 R_ANGLE => Some((c, BinOp::GreaterTest)),
613 PLUS => Some(BinOp::Addition), 617 PLUS => Some((c, BinOp::Addition)),
614 STAR => Some(BinOp::Multiplication), 618 STAR => Some((c, BinOp::Multiplication)),
615 MINUS => Some(BinOp::Subtraction), 619 MINUS => Some((c, BinOp::Subtraction)),
616 SLASH => Some(BinOp::Division), 620 SLASH => Some((c, BinOp::Division)),
617 PERCENT => Some(BinOp::Remainder), 621 PERCENT => Some((c, BinOp::Remainder)),
618 SHL => Some(BinOp::LeftShift), 622 SHL => Some((c, BinOp::LeftShift)),
619 SHR => Some(BinOp::RightShift), 623 SHR => Some((c, BinOp::RightShift)),
620 CARET => Some(BinOp::BitwiseXor), 624 CARET => Some((c, BinOp::BitwiseXor)),
621 PIPE => Some(BinOp::BitwiseOr), 625 PIPE => Some((c, BinOp::BitwiseOr)),
622 AMP => Some(BinOp::BitwiseAnd), 626 AMP => Some((c, BinOp::BitwiseAnd)),
623 DOTDOT => Some(BinOp::RangeRightOpen), 627 DOTDOT => Some((c, BinOp::RangeRightOpen)),
624 DOTDOTEQ => Some(BinOp::RangeRightClosed), 628 DOTDOTEQ => Some((c, BinOp::RangeRightClosed)),
625 EQ => Some(BinOp::Assignment), 629 EQ => Some((c, BinOp::Assignment)),
626 PLUSEQ => Some(BinOp::AddAssign), 630 PLUSEQ => Some((c, BinOp::AddAssign)),
627 SLASHEQ => Some(BinOp::DivAssign), 631 SLASHEQ => Some((c, BinOp::DivAssign)),
628 STAREQ => Some(BinOp::MulAssign), 632 STAREQ => Some((c, BinOp::MulAssign)),
629 PERCENTEQ => Some(BinOp::RemAssign), 633 PERCENTEQ => Some((c, BinOp::RemAssign)),
630 SHREQ => Some(BinOp::ShrAssign), 634 SHREQ => Some((c, BinOp::ShrAssign)),
631 SHLEQ => Some(BinOp::ShlAssign), 635 SHLEQ => Some((c, BinOp::ShlAssign)),
632 MINUSEQ => Some(BinOp::SubAssign), 636 MINUSEQ => Some((c, BinOp::SubAssign)),
633 PIPEEQ => Some(BinOp::BitOrAssign), 637 PIPEEQ => Some((c, BinOp::BitOrAssign)),
634 AMPEQ => Some(BinOp::BitAndAssign), 638 AMPEQ => Some((c, BinOp::BitAndAssign)),
635 CARETEQ => Some(BinOp::BitXorAssign), 639 CARETEQ => Some((c, BinOp::BitXorAssign)),
636 _ => None, 640 _ => None,
637 }) 641 })
638 .next() 642 .next()
639 } 643 }
640 644
645 pub fn op_kind(&self) -> Option<BinOp> {
646 self.op_details().map(|t| t.1)
647 }
648
649 pub fn op(&self) -> Option<&SyntaxNode> {
650 self.op_details().map(|t| t.0)
651 }
652
641 pub fn lhs(&self) -> Option<&Expr> { 653 pub fn lhs(&self) -> Option<&Expr> {
642 children(self).nth(0) 654 children(self).nth(0)
643 } 655 }
diff --git a/docs/dev/README.md b/docs/dev/README.md
index 104dd703d..d81c2f70d 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -12,7 +12,11 @@ should be enough to get you started!
12To learn more about how rust-analyzer works, see 12To learn more about how rust-analyzer works, see
13[./architecture.md](./architecture.md) document. 13[./architecture.md](./architecture.md) document.
14 14
15Various organizational and process issues are discussed here. 15We also publish rustdoc docs to pages:
16
17https://rust-analyzer.github.io/rust-analyzer/ra_ide_api/index.html
18
19Various organizational and process issues are discussed in this document.
16 20
17# Getting in Touch 21# Getting in Touch
18 22