@@ -5,8 +5,10 @@ use ra_syntax::{
55 ATTR , COMMENT , CONST_DEF , ENUM_DEF , FN_DEF , MODULE , STRUCT_DEF , TRAIT_DEF , VISIBILITY ,
66 WHITESPACE ,
77 } ,
8- SyntaxNode , TextSize , T ,
8+ SyntaxNode , TextRange , TextSize , T ,
99} ;
10+
11+ use hir:: { db:: HirDatabase , HasSource , PathResolution } ;
1012use test_utils:: tested_by;
1113
1214use crate :: { AssistContext , AssistId , Assists } ;
@@ -72,6 +74,88 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
7274 } )
7375}
7476
77+ fn add_missing_vis ( ctx : AssistCtx ) -> Option < Assist > {
78+ let path: ast:: Path = ctx. find_node_at_offset ( ) ?;
79+ let path_res = dbg ! ( ctx. sema. resolve_path( & path) ) ?;
80+ let def = match path_res {
81+ PathResolution :: Def ( def) => def,
82+ _ => return None ,
83+ } ;
84+ dbg ! ( & def) ;
85+
86+ let current_module = dbg ! ( ctx. sema. scope( & path. syntax( ) ) . module( ) ) ?;
87+ let target_module = dbg ! ( def. module( ctx. db) ) ?;
88+
89+ let vis = dbg ! ( target_module. visibility_of( ctx. db, & def) ) ?;
90+ if vis. is_visible_from ( ctx. db , current_module. into ( ) ) {
91+ return None ;
92+ } ;
93+ let target_name;
94+
95+ let ( offset, target) = match def {
96+ hir:: ModuleDef :: Function ( f) => {
97+ target_name = Some ( f. name ( ctx. db ) ) ;
98+ offset_and_target ( ctx. db , f)
99+ }
100+ hir:: ModuleDef :: Adt ( adt) => {
101+ target_name = Some ( adt. name ( ctx. db ) ) ;
102+ match adt {
103+ hir:: Adt :: Struct ( s) => offset_and_target ( ctx. db , s) ,
104+ hir:: Adt :: Union ( u) => offset_and_target ( ctx. db , u) ,
105+ hir:: Adt :: Enum ( e) => offset_and_target ( ctx. db , e) ,
106+ }
107+ }
108+ hir:: ModuleDef :: Const ( c) => {
109+ target_name = c. name ( ctx. db ) ;
110+ offset_and_target ( ctx. db , c)
111+ }
112+ hir:: ModuleDef :: Static ( s) => {
113+ target_name = s. name ( ctx. db ) ;
114+ offset_and_target ( ctx. db , s)
115+ }
116+ hir:: ModuleDef :: Trait ( t) => {
117+ target_name = Some ( t. name ( ctx. db ) ) ;
118+ offset_and_target ( ctx. db , t)
119+ }
120+ hir:: ModuleDef :: TypeAlias ( t) => {
121+ target_name = Some ( t. name ( ctx. db ) ) ;
122+ offset_and_target ( ctx. db , t)
123+ }
124+ hir:: ModuleDef :: Module ( m) => {
125+ target_name = m. name ( ctx. db ) ;
126+ let source = dbg ! ( m. declaration_source( ctx. db) ) ?. value ;
127+ let syntax = source. syntax ( ) ;
128+ ( vis_offset ( syntax) , syntax. text_range ( ) )
129+ }
130+ // Enum variants can't be private, we can't modify builtin types
131+ hir:: ModuleDef :: EnumVariant ( _) | hir:: ModuleDef :: BuiltinType ( _) => return None ,
132+ } ;
133+
134+ // FIXME if target is in another crate, add `pub` instead of `pub(crate)`
135+
136+ let assist_label = match target_name {
137+ None => "Change visibility to pub(crate)" . to_string ( ) ,
138+ Some ( name) => format ! ( "Change visibility of {} to pub(crate)" , name) ,
139+ } ;
140+ let target_file = target_module. definition_source ( ctx. db ) . file_id . original_file ( ctx. db ) ;
141+
142+ ctx. add_assist ( AssistId ( "change_visibility" ) , assist_label, target, |edit| {
143+ edit. set_file ( target_file) ;
144+ edit. insert ( offset, "pub(crate) " ) ;
145+ edit. set_cursor ( offset) ;
146+ } )
147+ }
148+
149+ fn offset_and_target < S , Ast > ( db : & dyn HirDatabase , x : S ) -> ( TextSize , TextRange )
150+ where
151+ S : HasSource < Ast = Ast > ,
152+ Ast : AstNode ,
153+ {
154+ let source = x. source ( db) ;
155+ let syntax = source. syntax ( ) . value ;
156+ ( vis_offset ( syntax) , syntax. text_range ( ) )
157+ }
158+
75159fn vis_offset ( node : & SyntaxNode ) -> TextSize {
76160 node. children_with_tokens ( )
77161 . skip_while ( |it| match it. kind ( ) {
@@ -191,6 +275,249 @@ mod tests {
191275 )
192276 }
193277
278+ #[ test]
279+ fn change_visibility_of_fn_via_path ( ) {
280+ check_assist (
281+ change_visibility,
282+ r"mod foo { fn foo() {} }
283+ fn main() { foo::foo<|>() } " ,
284+ r"mod foo { <|>pub(crate) fn foo() {} }
285+ fn main() { foo::foo() } " ,
286+ ) ;
287+ check_assist_not_applicable (
288+ change_visibility,
289+ r"mod foo { pub fn foo() {} }
290+ fn main() { foo::foo<|>() } " ,
291+ )
292+ }
293+
294+ #[ test]
295+ fn change_visibility_of_adt_in_submodule_via_path ( ) {
296+ check_assist (
297+ change_visibility,
298+ r"mod foo { struct Foo; }
299+ fn main() { foo::Foo<|> } " ,
300+ r"mod foo { <|>pub(crate) struct Foo; }
301+ fn main() { foo::Foo } " ,
302+ ) ;
303+ check_assist_not_applicable (
304+ change_visibility,
305+ r"mod foo { pub struct Foo; }
306+ fn main() { foo::Foo<|> } " ,
307+ ) ;
308+ check_assist (
309+ change_visibility,
310+ r"mod foo { enum Foo; }
311+ fn main() { foo::Foo<|> } " ,
312+ r"mod foo { <|>pub(crate) enum Foo; }
313+ fn main() { foo::Foo } " ,
314+ ) ;
315+ check_assist_not_applicable (
316+ change_visibility,
317+ r"mod foo { pub enum Foo; }
318+ fn main() { foo::Foo<|> } " ,
319+ ) ;
320+ check_assist (
321+ change_visibility,
322+ r"mod foo { union Foo; }
323+ fn main() { foo::Foo<|> } " ,
324+ r"mod foo { <|>pub(crate) union Foo; }
325+ fn main() { foo::Foo } " ,
326+ ) ;
327+ check_assist_not_applicable (
328+ change_visibility,
329+ r"mod foo { pub union Foo; }
330+ fn main() { foo::Foo<|> } " ,
331+ ) ;
332+ }
333+
334+ #[ test]
335+ fn change_visibility_of_adt_in_other_file_via_path ( ) {
336+ check_assist (
337+ change_visibility,
338+ r"
339+ //- /main.rs
340+ mod foo;
341+ fn main() { foo::Foo<|> }
342+
343+ //- /foo.rs
344+ struct Foo;
345+ " ,
346+ r"<|>pub(crate) struct Foo;
347+
348+ " ,
349+ ) ;
350+ }
351+
352+ #[ test]
353+ // FIXME this requires a separate implementation, struct fields are not a ast::Path
354+ fn change_visibility_of_struct_field_via_path ( ) {
355+ check_assist (
356+ change_visibility,
357+ r"mod foo { pub struct Foo { bar: (), } }
358+ fn main() { foo::Foo { <|>bar: () }; } " ,
359+ r"mod foo { pub struct Foo { <|>pub(crate) bar: (), } }
360+ fn main() { foo::Foo { bar: () }; } " ,
361+ ) ;
362+ check_assist_not_applicable (
363+ change_visibility,
364+ r"mod foo { pub struct Foo { pub bar: (), } }
365+ fn main() { foo::Foo { <|>bar: () }; } " ,
366+ ) ;
367+ }
368+
369+ #[ test]
370+ fn not_applicable_for_enum_variants ( ) {
371+ check_assist_not_applicable (
372+ change_visibility,
373+ r"mod foo { pub enum Foo {Foo1} }
374+ fn main() { foo::Foo::Foo1<|> } " ,
375+ ) ;
376+ }
377+
378+ #[ test]
379+ fn change_visibility_of_const_via_path ( ) {
380+ check_assist (
381+ change_visibility,
382+ r"mod foo { const FOO: () = (); }
383+ fn main() { foo::FOO<|> } " ,
384+ r"mod foo { <|>pub(crate) const FOO: () = (); }
385+ fn main() { foo::FOO } " ,
386+ ) ;
387+ check_assist_not_applicable (
388+ change_visibility,
389+ r"mod foo { pub const FOO: () = (); }
390+ fn main() { foo::FOO<|> } " ,
391+ ) ;
392+ }
393+
394+ #[ test]
395+ fn change_visibility_of_static_via_path ( ) {
396+ check_assist (
397+ change_visibility,
398+ r"mod foo { static FOO: () = (); }
399+ fn main() { foo::FOO<|> } " ,
400+ r"mod foo { <|>pub(crate) static FOO: () = (); }
401+ fn main() { foo::FOO } " ,
402+ ) ;
403+ check_assist_not_applicable (
404+ change_visibility,
405+ r"mod foo { pub static FOO: () = (); }
406+ fn main() { foo::FOO<|> } " ,
407+ ) ;
408+ }
409+
410+ #[ test]
411+ fn change_visibility_of_trait_via_path ( ) {
412+ check_assist (
413+ change_visibility,
414+ r"mod foo { trait Foo { fn foo(&self) {} } }
415+ fn main() { let x: &dyn foo::<|>Foo; } " ,
416+ r"mod foo { <|>pub(crate) trait Foo { fn foo(&self) {} } }
417+ fn main() { let x: &dyn foo::Foo; } " ,
418+ ) ;
419+ check_assist_not_applicable (
420+ change_visibility,
421+ r"mod foo { pub trait Foo { fn foo(&self) {} } }
422+ fn main() { let x: &dyn foo::Foo<|>; } " ,
423+ ) ;
424+ }
425+
426+ #[ test]
427+ fn change_visibility_of_type_alias_via_path ( ) {
428+ check_assist (
429+ change_visibility,
430+ r"mod foo { type Foo = (); }
431+ fn main() { let x: foo::Foo<|>; } " ,
432+ r"mod foo { <|>pub(crate) type Foo = (); }
433+ fn main() { let x: foo::Foo; } " ,
434+ ) ;
435+ check_assist_not_applicable (
436+ change_visibility,
437+ r"mod foo { pub type Foo = (); }
438+ fn main() { let x: foo::Foo<|>; } " ,
439+ ) ;
440+ }
441+
442+ #[ test]
443+ fn change_visibility_of_module_via_path ( ) {
444+ check_assist (
445+ change_visibility,
446+ r"mod foo { mod bar { fn bar() {} } }
447+ fn main() { foo::bar<|>::bar(); } " ,
448+ r"mod foo { <|>pub(crate) mod bar { fn bar() {} } }
449+ fn main() { foo::bar::bar(); } " ,
450+ ) ;
451+
452+ check_assist (
453+ change_visibility,
454+ r"
455+ //- /main.rs
456+ mod foo;
457+ fn main() { foo::bar<|>::baz(); }
458+
459+ //- /foo.rs
460+ mod bar {
461+ pub fn baz() {}
462+ }
463+ " ,
464+ r"<|>pub(crate) mod bar {
465+ pub fn baz() {}
466+ }
467+
468+ " ,
469+ ) ;
470+
471+ check_assist_not_applicable (
472+ change_visibility,
473+ r"mod foo { pub mod bar { pub fn bar() {} } }
474+ fn main() { foo::bar<|>::bar(); } " ,
475+ ) ;
476+ }
477+
478+ #[ test]
479+ fn change_visibility_of_inline_module_in_other_file_via_path ( ) {
480+ check_assist (
481+ change_visibility,
482+ r"
483+ //- /main.rs
484+ mod foo;
485+ fn main() { foo::bar<|>::baz(); }
486+
487+ //- /foo.rs
488+ mod bar;
489+
490+ //- /foo/bar.rs
491+ pub fn baz() {}
492+ }
493+ " ,
494+ r"<|>pub(crate) mod bar;
495+ " ,
496+ ) ;
497+ }
498+
499+ #[ test]
500+ fn change_visibility_of_module_declaration_in_other_file_via_path ( ) {
501+ check_assist (
502+ change_visibility,
503+ r"
504+ //- /main.rs
505+ mod foo;
506+ fn main() { foo::bar<|>>::baz(); }
507+
508+ //- /foo.rs
509+ mod bar {
510+ pub fn baz() {}
511+ }
512+ " ,
513+ r"<|>pub(crate) mod bar {
514+ pub fn baz() {}
515+ }
516+
517+ " ,
518+ ) ;
519+ }
520+
194521 #[ test]
195522 fn change_visibility_target ( ) {
196523 check_assist_target ( change_visibility, "<|>fn foo() {}" , "fn" ) ;
0 commit comments