42
42
# Types of parso nodes for which snippet is not included in the completion
43
43
_IMPORTS = ('import_name' , 'import_from' )
44
44
45
+ # Types of parso node for errors
46
+ _ERRORS = ('error_node' , )
47
+
45
48
46
49
@hookimpl
47
50
def pyls_completions (config , document , position ):
@@ -63,11 +66,25 @@ def pyls_completions(config, document, position):
63
66
64
67
settings = config .plugin_settings ('jedi_completion' , document_path = document .path )
65
68
should_include_params = settings .get ('include_params' )
66
- include_params = (snippet_support and should_include_params and
67
- use_snippets (document , position ))
69
+ include_params = snippet_support and should_include_params and use_snippets (document , position )
68
70
return [_format_completion (d , include_params ) for d in definitions ] or None
69
71
70
72
73
+ def is_exception_class (name ):
74
+ """
75
+ Determine if a class name is an instance of an Exception.
76
+
77
+ This returns `False` if the name given corresponds with a instance of
78
+ the 'Exception' class, `True` otherwise
79
+ """
80
+ try :
81
+ return name in [cls .__name__ for cls in Exception .__subclasses__ ()]
82
+ except AttributeError :
83
+ # Needed in case a class don't uses new-style
84
+ # class definition in Python 2
85
+ return False
86
+
87
+
71
88
def use_snippets (document , position ):
72
89
"""
73
90
Determine if it's necessary to return snippets in code completions.
@@ -79,15 +96,28 @@ def use_snippets(document, position):
79
96
lines = document .source .split ('\n ' , line )
80
97
act_lines = [lines [line ][:position ['character' ]]]
81
98
line -= 1
99
+ last_character = ''
82
100
while line > - 1 :
83
101
act_line = lines [line ]
84
- if act_line .rstrip ().endswith ('\\ ' ):
102
+ if (act_line .rstrip ().endswith ('\\ ' ) or
103
+ act_line .rstrip ().endswith ('(' ) or
104
+ act_line .rstrip ().endswith (',' )):
85
105
act_lines .insert (0 , act_line )
86
106
line -= 1
107
+ if act_line .rstrip ().endswith ('(' ):
108
+ # Needs to be added to the end of the code before parsing
109
+ # to make it valid, otherwise the node type could end
110
+ # being an 'error_node' for multi-line imports that use '('
111
+ last_character = ')'
87
112
else :
88
113
break
89
- tokens = parso .parse ('\n ' .join (act_lines ).split (';' )[- 1 ].strip ())
90
- return tokens .children [0 ].type not in _IMPORTS
114
+ if '(' in act_lines [- 1 ].strip ():
115
+ last_character = ')'
116
+ code = '\n ' .join (act_lines ).split (';' )[- 1 ].strip () + last_character
117
+ tokens = parso .parse (code )
118
+ expr_type = tokens .children [0 ].type
119
+ return (expr_type not in _IMPORTS and
120
+ not (expr_type in _ERRORS and 'import' in code ))
91
121
92
122
93
123
def _format_completion (d , include_params = True ):
@@ -100,19 +130,25 @@ def _format_completion(d, include_params=True):
100
130
'insertText' : d .name
101
131
}
102
132
103
- if include_params and hasattr (d , 'params' ) and d .params :
133
+ if (include_params and hasattr (d , 'params' ) and d .params and
134
+ not is_exception_class (d .name )):
104
135
positional_args = [param for param in d .params if '=' not in param .description ]
105
136
106
- # For completions with params, we can generate a snippet instead
107
- completion ['insertTextFormat' ] = lsp .InsertTextFormat .Snippet
108
- snippet = d .name + '('
109
- for i , param in enumerate (positional_args ):
110
- name = param .name if param .name != '/' else '\\ /'
111
- snippet += '${%s:%s}' % (i + 1 , name )
112
- if i < len (positional_args ) - 1 :
113
- snippet += ', '
114
- snippet += ')$0'
115
- completion ['insertText' ] = snippet
137
+ if len (positional_args ) > 1 :
138
+ # For completions with params, we can generate a snippet instead
139
+ completion ['insertTextFormat' ] = lsp .InsertTextFormat .Snippet
140
+ snippet = d .name + '('
141
+ for i , param in enumerate (positional_args ):
142
+ name = param .name if param .name != '/' else '\\ /'
143
+ snippet += '${%s:%s}' % (i + 1 , name )
144
+ if i < len (positional_args ) - 1 :
145
+ snippet += ', '
146
+ snippet += ')$0'
147
+ completion ['insertText' ] = snippet
148
+ elif len (positional_args ) == 1 :
149
+ completion ['insertText' ] = d .name + '($0)'
150
+ else :
151
+ completion ['insertText' ] = d .name + '()'
116
152
117
153
return completion
118
154
0 commit comments