]> git.lizzy.rs Git - rust.git/commitdiff
Implement yield point highlighting
authorLukas Wirth <lukastw97@gmail.com>
Wed, 23 Jun 2021 14:16:32 +0000 (16:16 +0200)
committerLukas Wirth <lukastw97@gmail.com>
Wed, 23 Jun 2021 14:16:32 +0000 (16:16 +0200)
crates/ide/src/highlight_references.rs [deleted file]
crates/ide/src/highlight_related.rs [new file with mode: 0644]
crates/ide/src/lib.rs

diff --git a/crates/ide/src/highlight_references.rs b/crates/ide/src/highlight_references.rs
deleted file mode 100644 (file)
index 78e5a9f..0000000
+++ /dev/null
@@ -1,170 +0,0 @@
-use hir::Semantics;
-use ide_db::{
-    base_db::FilePosition,
-    defs::Definition,
-    helpers::pick_best_token,
-    search::{FileReference, ReferenceAccess, SearchScope},
-    RootDatabase,
-};
-use syntax::{
-    AstNode,
-    SyntaxKind::{ASYNC_KW, AWAIT_KW, QUESTION, RETURN_KW, THIN_ARROW},
-    SyntaxNode, TextRange,
-};
-
-use crate::{display::TryToNav, references, NavigationTarget};
-
-pub struct DocumentHighlight {
-    pub range: TextRange,
-    pub access: Option<ReferenceAccess>,
-}
-
-// Feature: Highlight related
-//
-// Highlights exit points, yield points or the definition and all references of the item at the cursor location in the current file.
-pub(crate) fn highlight_related(
-    sema: &Semantics<RootDatabase>,
-    position: FilePosition,
-) -> Option<Vec<DocumentHighlight>> {
-    let _p = profile::span("document_highlight");
-    let syntax = sema.parse(position.file_id).syntax().clone();
-
-    let token = pick_best_token(syntax.token_at_offset(position.offset), |kind| match kind {
-        QUESTION => 2, // prefer `?` when the cursor is sandwiched like `await$0?`
-        AWAIT_KW | ASYNC_KW | THIN_ARROW | RETURN_KW => 1,
-        _ => 0,
-    })?;
-
-    match token.kind() {
-        QUESTION | RETURN_KW | THIN_ARROW => highlight_exit_points(),
-        AWAIT_KW | ASYNC_KW => highlight_yield_points(),
-        _ => highlight_references(sema, &syntax, position),
-    }
-}
-
-fn highlight_exit_points() -> Option<Vec<DocumentHighlight>> {
-    None
-}
-
-fn highlight_yield_points() -> Option<Vec<DocumentHighlight>> {
-    None
-}
-
-fn highlight_references(
-    sema: &Semantics<RootDatabase>,
-    syntax: &SyntaxNode,
-    FilePosition { offset, file_id }: FilePosition,
-) -> Option<Vec<DocumentHighlight>> {
-    let def = references::find_def(sema, syntax, offset)?;
-    let usages = def.usages(sema).set_scope(Some(SearchScope::single_file(file_id))).all();
-
-    let declaration = match def {
-        Definition::ModuleDef(hir::ModuleDef::Module(module)) => {
-            Some(NavigationTarget::from_module_to_decl(sema.db, module))
-        }
-        def => def.try_to_nav(sema.db),
-    }
-    .filter(|decl| decl.file_id == file_id)
-    .and_then(|decl| {
-        let range = decl.focus_range?;
-        let access = references::decl_access(&def, syntax, range);
-        Some(DocumentHighlight { range, access })
-    });
-
-    let file_refs = usages.references.get(&file_id).map_or(&[][..], Vec::as_slice);
-    let mut res = Vec::with_capacity(file_refs.len() + 1);
-    res.extend(declaration);
-    res.extend(
-        file_refs
-            .iter()
-            .map(|&FileReference { access, range, .. }| DocumentHighlight { range, access }),
-    );
-    Some(res)
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::fixture;
-
-    use super::*;
-
-    fn check(ra_fixture: &str) {
-        let (analysis, pos, annotations) = fixture::annotations(ra_fixture);
-        let hls = analysis.highlight_related(pos).unwrap().unwrap();
-
-        let mut expected = annotations
-            .into_iter()
-            .map(|(r, access)| (r.range, (!access.is_empty()).then(|| access)))
-            .collect::<Vec<_>>();
-
-        let mut actual = hls
-            .into_iter()
-            .map(|hl| {
-                (
-                    hl.range,
-                    hl.access.map(|it| {
-                        match it {
-                            ReferenceAccess::Read => "read",
-                            ReferenceAccess::Write => "write",
-                        }
-                        .to_string()
-                    }),
-                )
-            })
-            .collect::<Vec<_>>();
-        actual.sort_by_key(|(range, _)| range.start());
-        expected.sort_by_key(|(range, _)| range.start());
-
-        assert_eq!(expected, actual);
-    }
-
-    #[test]
-    fn test_hl_module() {
-        check(
-            r#"
-//- /lib.rs
-mod foo$0;
- // ^^^
-//- /foo.rs
-struct Foo;
-"#,
-        );
-    }
-
-    #[test]
-    fn test_hl_self_in_crate_root() {
-        check(
-            r#"
-//- /lib.rs
-use self$0;
-"#,
-        );
-    }
-
-    #[test]
-    fn test_hl_self_in_module() {
-        check(
-            r#"
-//- /lib.rs
-mod foo;
-//- /foo.rs
-use self$0;
-"#,
-        );
-    }
-
-    #[test]
-    fn test_hl_local() {
-        check(
-            r#"
-//- /lib.rs
-fn foo() {
-    let mut bar = 3;
-         // ^^^ write
-    bar$0;
- // ^^^ read
-}
-"#,
-        );
-    }
-}
diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs
new file mode 100644 (file)
index 0000000..1daaeb4
--- /dev/null
@@ -0,0 +1,284 @@
+use hir::Semantics;
+use ide_db::{
+    base_db::FilePosition,
+    defs::Definition,
+    helpers::pick_best_token,
+    search::{FileReference, ReferenceAccess, SearchScope},
+    RootDatabase,
+};
+use syntax::{
+    ast, match_ast, AstNode,
+    SyntaxKind::{ASYNC_KW, AWAIT_KW, QUESTION, RETURN_KW, THIN_ARROW},
+    SyntaxNode, SyntaxToken, TextRange, WalkEvent,
+};
+
+use crate::{display::TryToNav, references, NavigationTarget};
+
+pub struct DocumentHighlight {
+    pub range: TextRange,
+    pub access: Option<ReferenceAccess>,
+}
+
+// Feature: Highlight related
+//
+// Highlights exit points, yield points or the definition and all references of the item at the cursor location in the current file.
+pub(crate) fn highlight_related(
+    sema: &Semantics<RootDatabase>,
+    position: FilePosition,
+) -> Option<Vec<DocumentHighlight>> {
+    let _p = profile::span("document_highlight");
+    let syntax = sema.parse(position.file_id).syntax().clone();
+
+    let token = pick_best_token(syntax.token_at_offset(position.offset), |kind| match kind {
+        QUESTION => 2, // prefer `?` when the cursor is sandwiched like `await$0?`
+        AWAIT_KW | ASYNC_KW | THIN_ARROW | RETURN_KW => 1,
+        _ => 0,
+    })?;
+
+    match token.kind() {
+        QUESTION | RETURN_KW | THIN_ARROW => highlight_exit_points(token),
+        AWAIT_KW | ASYNC_KW => highlight_yield_points(token),
+        _ => highlight_references(sema, &syntax, position),
+    }
+}
+
+fn highlight_exit_points(_token: SyntaxToken) -> Option<Vec<DocumentHighlight>> {
+    None
+}
+
+fn highlight_yield_points(token: SyntaxToken) -> Option<Vec<DocumentHighlight>> {
+    fn hl(
+        async_token: Option<SyntaxToken>,
+        body: Option<ast::BlockExpr>,
+    ) -> Option<Vec<DocumentHighlight>> {
+        let mut highlights = Vec::new();
+        highlights.push(DocumentHighlight { access: None, range: async_token?.text_range() });
+        if let Some(body) = body {
+            let mut preorder = body.syntax().preorder();
+            while let Some(event) = preorder.next() {
+                let node = match event {
+                    WalkEvent::Enter(node) => node,
+                    WalkEvent::Leave(_) => continue,
+                };
+                match_ast! {
+                    match node {
+                        ast::AwaitExpr(expr) => if let Some(token) = expr.await_token() {
+                            highlights.push(DocumentHighlight {
+                                access: None,
+                                range: token.text_range(),
+                            });
+                        },
+                        ast::EffectExpr(__) => preorder.skip_subtree(),
+                        ast::ClosureExpr(__) => preorder.skip_subtree(),
+                        ast::Item(__) => preorder.skip_subtree(),
+                        ast::Path(__) => preorder.skip_subtree(),
+                        _ => (),
+                    }
+                }
+            }
+        }
+        Some(highlights)
+    }
+    for anc in token.ancestors() {
+        return match_ast! {
+            match anc {
+                ast::Fn(fn_) => hl(fn_.async_token(), fn_.body()),
+                ast::EffectExpr(effect) => hl(effect.async_token(), effect.block_expr()),
+                ast::ClosureExpr(__) => None,
+                _ => continue,
+            }
+        };
+    }
+    None
+}
+
+fn highlight_references(
+    sema: &Semantics<RootDatabase>,
+    syntax: &SyntaxNode,
+    FilePosition { offset, file_id }: FilePosition,
+) -> Option<Vec<DocumentHighlight>> {
+    let def = references::find_def(sema, syntax, offset)?;
+    let usages = def.usages(sema).set_scope(Some(SearchScope::single_file(file_id))).all();
+
+    let declaration = match def {
+        Definition::ModuleDef(hir::ModuleDef::Module(module)) => {
+            Some(NavigationTarget::from_module_to_decl(sema.db, module))
+        }
+        def => def.try_to_nav(sema.db),
+    }
+    .filter(|decl| decl.file_id == file_id)
+    .and_then(|decl| {
+        let range = decl.focus_range?;
+        let access = references::decl_access(&def, syntax, range);
+        Some(DocumentHighlight { range, access })
+    });
+
+    let file_refs = usages.references.get(&file_id).map_or(&[][..], Vec::as_slice);
+    let mut res = Vec::with_capacity(file_refs.len() + 1);
+    res.extend(declaration);
+    res.extend(
+        file_refs
+            .iter()
+            .map(|&FileReference { access, range, .. }| DocumentHighlight { range, access }),
+    );
+    Some(res)
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::fixture;
+
+    use super::*;
+
+    fn check(ra_fixture: &str) {
+        let (analysis, pos, annotations) = fixture::annotations(ra_fixture);
+        let hls = analysis.highlight_related(pos).unwrap().unwrap();
+
+        let mut expected = annotations
+            .into_iter()
+            .map(|(r, access)| (r.range, (!access.is_empty()).then(|| access)))
+            .collect::<Vec<_>>();
+
+        let mut actual = hls
+            .into_iter()
+            .map(|hl| {
+                (
+                    hl.range,
+                    hl.access.map(|it| {
+                        match it {
+                            ReferenceAccess::Read => "read",
+                            ReferenceAccess::Write => "write",
+                        }
+                        .to_string()
+                    }),
+                )
+            })
+            .collect::<Vec<_>>();
+        actual.sort_by_key(|(range, _)| range.start());
+        expected.sort_by_key(|(range, _)| range.start());
+
+        assert_eq!(expected, actual);
+    }
+
+    #[test]
+    fn test_hl_module() {
+        check(
+            r#"
+//- /lib.rs
+mod foo$0;
+ // ^^^
+//- /foo.rs
+struct Foo;
+"#,
+        );
+    }
+
+    #[test]
+    fn test_hl_self_in_crate_root() {
+        check(
+            r#"
+use self$0;
+"#,
+        );
+    }
+
+    #[test]
+    fn test_hl_self_in_module() {
+        check(
+            r#"
+//- /lib.rs
+mod foo;
+//- /foo.rs
+use self$0;
+"#,
+        );
+    }
+
+    #[test]
+    fn test_hl_local() {
+        check(
+            r#"
+fn foo() {
+    let mut bar = 3;
+         // ^^^ write
+    bar$0;
+ // ^^^ read
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_hl_yield_points() {
+        check(
+            r#"
+pub async fn foo() {
+ // ^^^^^
+    let x = foo()
+        .await$0
+      // ^^^^^
+        .await;
+      // ^^^^^
+    || { 0.await };
+    (async { 0.await }).await
+                     // ^^^^^
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_hl_yield_points2() {
+        check(
+            r#"
+pub async$0 fn foo() {
+ // ^^^^^
+    let x = foo()
+        .await
+      // ^^^^^
+        .await;
+      // ^^^^^
+    || { 0.await };
+    (async { 0.await }).await
+                     // ^^^^^
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_hl_yield_nested_fn() {
+        check(
+            r#"
+async fn foo() {
+    async fn foo2() {
+ // ^^^^^
+        async fn foo3() {
+            0.await
+        }
+        0.await$0
+       // ^^^^^
+    }
+    0.await
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_hl_yield_nested_async_blocks() {
+        check(
+            r#"
+async fn foo() {
+    (async {
+  // ^^^^^
+        (async {
+           0.await
+        }).await$0 }
+        // ^^^^^
+    ).await;
+}
+"#,
+        );
+    }
+}
index 5e3420c0461b3ad08e3cba35a2c5f02072d6442c..bb10ce5e8ae70b7bf561e1b3f41e93aa2987a830 100644 (file)
@@ -25,7 +25,7 @@ macro_rules! eprintln {
 mod annotations;
 mod call_hierarchy;
 mod doc_links;
-mod highlight_references;
+mod highlight_related;
 mod expand_macro;
 mod extend_selection;
 mod file_structure;
@@ -76,7 +76,7 @@ macro_rules! eprintln {
     expand_macro::ExpandedMacro,
     file_structure::{StructureNode, StructureNodeKind},
     folding_ranges::{Fold, FoldKind},
-    highlight_references::DocumentHighlight,
+    highlight_related::DocumentHighlight,
     hover::{HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult},
     inlay_hints::{InlayHint, InlayHintsConfig, InlayKind},
     markup::Markup,
@@ -490,7 +490,7 @@ pub fn highlight_related(
         &self,
         position: FilePosition,
     ) -> Cancellable<Option<Vec<DocumentHighlight>>> {
-        self.with_db(|db| highlight_references::highlight_related(&Semantics::new(db), position))
+        self.with_db(|db| highlight_related::highlight_related(&Semantics::new(db), position))
     }
 
     /// Computes syntax highlighting for the given file range.