1 use hir::db::HirDatabase;
3 AstNode, SyntaxNode, TextUnit,
4 ast::{self, VisibilityOwner, NameOwner},
5 SyntaxKind::{VISIBILITY, FN_KW, MOD_KW, STRUCT_KW, ENUM_KW, TRAIT_KW, FN_DEF, MODULE, STRUCT_DEF, ENUM_DEF, TRAIT_DEF, IDENT, WHITESPACE, COMMENT, ATTR},
8 use crate::{AssistCtx, Assist};
10 pub(crate) fn change_visibility(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
11 if let Some(vis) = ctx.node_at_offset::<ast::Visibility>() {
12 return change_vis(ctx, vis);
17 fn add_vis(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
18 let item_keyword = ctx.leaf_at_offset().find(|leaf| match leaf.kind() {
19 FN_KW | MOD_KW | STRUCT_KW | ENUM_KW | TRAIT_KW => true,
23 let (offset, target) = if let Some(keyword) = item_keyword {
24 let parent = keyword.parent()?;
25 let def_kws = vec![FN_DEF, MODULE, STRUCT_DEF, ENUM_DEF, TRAIT_DEF];
26 // Parent is not a definition, can't add visibility
27 if !def_kws.iter().any(|&def_kw| def_kw == parent.kind()) {
30 // Already have visibility, do nothing
31 if parent.children().any(|child| child.kind() == VISIBILITY) {
34 (vis_offset(parent), keyword.range())
36 let ident = ctx.leaf_at_offset().find(|leaf| leaf.kind() == IDENT)?;
37 let field = ident.ancestors().find_map(ast::NamedFieldDef::cast)?;
38 if field.name()?.syntax().range() != ident.range() && field.visibility().is_some() {
41 (vis_offset(field.syntax()), ident.range())
44 ctx.build("make pub(crate)", |edit| {
46 edit.insert(offset, "pub(crate) ");
47 edit.set_cursor(offset);
51 fn vis_offset(node: &SyntaxNode) -> TextUnit {
53 .skip_while(|it| match it.kind() {
54 WHITESPACE | COMMENT | ATTR => true,
58 .map(|it| it.range().start())
59 .unwrap_or(node.range().start())
62 fn change_vis(ctx: AssistCtx<impl HirDatabase>, vis: &ast::Visibility) -> Option<Assist> {
63 if vis.syntax().text() == "pub" {
64 return ctx.build("change to pub(crate)", |edit| {
65 edit.target(vis.syntax().range());
66 edit.replace(vis.syntax().range(), "pub(crate)");
67 edit.set_cursor(vis.syntax().range().start());
70 if vis.syntax().text() == "pub(crate)" {
71 return ctx.build("change to pub", |edit| {
72 edit.target(vis.syntax().range());
73 edit.replace(vis.syntax().range(), "pub");
74 edit.set_cursor(vis.syntax().range().start());
83 use crate::helpers::{check_assist, check_assist_target};
86 fn change_visibility_adds_pub_crate_to_items() {
87 check_assist(change_visibility, "<|>fn foo() {}", "<|>pub(crate) fn foo() {}");
88 check_assist(change_visibility, "f<|>n foo() {}", "<|>pub(crate) fn foo() {}");
89 check_assist(change_visibility, "<|>struct Foo {}", "<|>pub(crate) struct Foo {}");
90 check_assist(change_visibility, "<|>mod foo {}", "<|>pub(crate) mod foo {}");
91 check_assist(change_visibility, "<|>trait Foo {}", "<|>pub(crate) trait Foo {}");
92 check_assist(change_visibility, "m<|>od {}", "<|>pub(crate) mod {}");
95 "unsafe f<|>n foo() {}",
96 "<|>pub(crate) unsafe fn foo() {}",
101 fn change_visibility_works_with_struct_fields() {
104 "struct S { <|>field: u32 }",
105 "struct S { <|>pub(crate) field: u32 }",
110 fn change_visibility_pub_to_pub_crate() {
111 check_assist(change_visibility, "<|>pub fn foo() {}", "<|>pub(crate) fn foo() {}")
115 fn change_visibility_pub_crate_to_pub() {
116 check_assist(change_visibility, "<|>pub(crate) fn foo() {}", "<|>pub fn foo() {}")
120 fn change_visibility_handles_comment_attrs() {
137 <|>pub(crate) struct Foo;
143 fn change_visibility_target() {
144 check_assist_target(change_visibility, "<|>fn foo() {}", "fn");
145 check_assist_target(change_visibility, "pub(crate)<|> fn foo() {}", "pub(crate)");
146 check_assist_target(change_visibility, "struct S { <|>field: u32 }", "field");