@@ -5,7 +5,7 @@ use ruff_db::parsed::{ParsedModuleRef, parsed_module};
55use ruff_python_ast as ast;
66use ruff_python_parser:: { Token , TokenAt , TokenKind } ;
77use ruff_text_size:: { Ranged , TextRange , TextSize } ;
8- use ty_python_semantic:: { Completion , SemanticModel } ;
8+ use ty_python_semantic:: { Completion , NameKind , SemanticModel } ;
99
1010use crate :: Db ;
1111use crate :: find_node:: covering_node;
@@ -325,38 +325,7 @@ fn import_from_tokens(tokens: &[Token]) -> Option<&Token> {
325325/// This has the effect of putting all dunder attributes after "normal"
326326/// attributes, and all single-underscore attributes after dunder attributes.
327327fn compare_suggestions ( c1 : & Completion , c2 : & Completion ) -> Ordering {
328- /// A helper type for sorting completions based only on name.
329- ///
330- /// This sorts "normal" names first, then dunder names and finally
331- /// single-underscore names. This matches the order of the variants defined for
332- /// this enum, which is in turn picked up by the derived trait implementation
333- /// for `Ord`.
334- #[ derive( Clone , Copy , Eq , PartialEq , PartialOrd , Ord ) ]
335- enum Kind {
336- Normal ,
337- Dunder ,
338- Sunder ,
339- }
340-
341- impl Kind {
342- fn classify ( c : & Completion ) -> Kind {
343- // Dunder needs a prefix and suffix double underscore.
344- // When there's only a prefix double underscore, this
345- // results in explicit name mangling. We let that be
346- // classified as-if they were single underscore names.
347- //
348- // Ref: <https://docs.python.org/3/reference/lexical_analysis.html#reserved-classes-of-identifiers>
349- if c. name . starts_with ( "__" ) && c. name . ends_with ( "__" ) {
350- Kind :: Dunder
351- } else if c. name . starts_with ( '_' ) {
352- Kind :: Sunder
353- } else {
354- Kind :: Normal
355- }
356- }
357- }
358-
359- let ( kind1, kind2) = ( Kind :: classify ( c1) , Kind :: classify ( c2) ) ;
328+ let ( kind1, kind2) = ( NameKind :: classify ( & c1. name ) , NameKind :: classify ( & c2. name ) ) ;
360329 kind1. cmp ( & kind2) . then_with ( || c1. name . cmp ( & c2. name ) )
361330}
362331
@@ -472,6 +441,11 @@ mod tests {
472441" ,
473442 ) ;
474443 test. assert_completions_include ( "filter" ) ;
444+ // Sunder items should be filtered out
445+ test. assert_completions_do_not_include ( "_T" ) ;
446+ // Dunder attributes should not be stripped
447+ test. assert_completions_include ( "__annotations__" ) ;
448+ // See `private_symbols_in_stub` for more comprehensive testing private of symbol filtering.
475449 }
476450
477451 #[ test]
@@ -536,6 +510,112 @@ re.<CURSOR>
536510 test. assert_completions_include ( "findall" ) ;
537511 }
538512
513+ #[ test]
514+ fn private_symbols_in_stub ( ) {
515+ let test = CursorTest :: builder ( )
516+ . source (
517+ "package/__init__.pyi" ,
518+ r#"\
519+ from typing import TypeAlias, Literal, TypeVar, ParamSpec, TypeVarTuple, Protocol
520+
521+ public_name = 1
522+ _private_name = 1
523+ __mangled_name = 1
524+ __dunder_name__ = 1
525+
526+ public_type_var = TypeVar("public_type_var")
527+ _private_type_var = TypeVar("_private_type_var")
528+ __mangled_type_var = TypeVar("__mangled_type_var")
529+
530+ public_param_spec = ParamSpec("public_param_spec")
531+ _private_param_spec = ParamSpec("_private_param_spec")
532+
533+ public_type_var_tuple = TypeVarTuple("public_type_var_tuple")
534+ _private_type_var_tuple = TypeVarTuple("_private_type_var_tuple")
535+
536+ public_explicit_type_alias: TypeAlias = Literal[1]
537+ _private_explicit_type_alias: TypeAlias = Literal[1]
538+
539+ class PublicProtocol(Protocol):
540+ def method(self) -> None: ...
541+
542+ class _PrivateProtocol(Protocol):
543+ def method(self) -> None: ...
544+ "# ,
545+ )
546+ . source ( "main.py" , "import package; package.<CURSOR>" )
547+ . build ( ) ;
548+ test. assert_completions_include ( "public_name" ) ;
549+ test. assert_completions_include ( "_private_name" ) ;
550+ test. assert_completions_include ( "__mangled_name" ) ;
551+ test. assert_completions_include ( "__dunder_name__" ) ;
552+ test. assert_completions_include ( "public_type_var" ) ;
553+ test. assert_completions_do_not_include ( "_private_type_var" ) ;
554+ test. assert_completions_do_not_include ( "__mangled_type_var" ) ;
555+ test. assert_completions_include ( "public_param_spec" ) ;
556+ test. assert_completions_do_not_include ( "_private_param_spec" ) ;
557+ test. assert_completions_include ( "public_type_var_tuple" ) ;
558+ test. assert_completions_do_not_include ( "_private_type_var_tuple" ) ;
559+ test. assert_completions_include ( "public_explicit_type_alias" ) ;
560+ test. assert_completions_include ( "_private_explicit_type_alias" ) ;
561+ test. assert_completions_include ( "PublicProtocol" ) ;
562+ test. assert_completions_do_not_include ( "_PrivateProtocol" ) ;
563+ }
564+
565+ /// Unlike [`private_symbols_in_stub`], this test doesn't use a `.pyi` file so all of the names
566+ /// are visible.
567+ #[ test]
568+ fn private_symbols_in_module ( ) {
569+ let test = CursorTest :: builder ( )
570+ . source (
571+ "package/__init__.py" ,
572+ r#"\
573+ from typing import TypeAlias, Literal, TypeVar, ParamSpec, TypeVarTuple, Protocol
574+
575+ public_name = 1
576+ _private_name = 1
577+ __mangled_name = 1
578+ __dunder_name__ = 1
579+
580+ public_type_var = TypeVar("public_type_var")
581+ _private_type_var = TypeVar("_private_type_var")
582+ __mangled_type_var = TypeVar("__mangled_type_var")
583+
584+ public_param_spec = ParamSpec("public_param_spec")
585+ _private_param_spec = ParamSpec("_private_param_spec")
586+
587+ public_type_var_tuple = TypeVarTuple("public_type_var_tuple")
588+ _private_type_var_tuple = TypeVarTuple("_private_type_var_tuple")
589+
590+ public_explicit_type_alias: TypeAlias = Literal[1]
591+ _private_explicit_type_alias: TypeAlias = Literal[1]
592+
593+ class PublicProtocol(Protocol):
594+ def method(self) -> None: ...
595+
596+ class _PrivateProtocol(Protocol):
597+ def method(self) -> None: ...
598+ "# ,
599+ )
600+ . source ( "main.py" , "import package; package.<CURSOR>" )
601+ . build ( ) ;
602+ test. assert_completions_include ( "public_name" ) ;
603+ test. assert_completions_include ( "_private_name" ) ;
604+ test. assert_completions_include ( "__mangled_name" ) ;
605+ test. assert_completions_include ( "__dunder_name__" ) ;
606+ test. assert_completions_include ( "public_type_var" ) ;
607+ test. assert_completions_include ( "_private_type_var" ) ;
608+ test. assert_completions_include ( "__mangled_type_var" ) ;
609+ test. assert_completions_include ( "public_param_spec" ) ;
610+ test. assert_completions_include ( "_private_param_spec" ) ;
611+ test. assert_completions_include ( "public_type_var_tuple" ) ;
612+ test. assert_completions_include ( "_private_type_var_tuple" ) ;
613+ test. assert_completions_include ( "public_explicit_type_alias" ) ;
614+ test. assert_completions_include ( "_private_explicit_type_alias" ) ;
615+ test. assert_completions_include ( "PublicProtocol" ) ;
616+ test. assert_completions_include ( "_PrivateProtocol" ) ;
617+ }
618+
539619 #[ test]
540620 fn one_function_prefix ( ) {
541621 let test = cursor_test (
0 commit comments