5 |
5 |
6 """ |
6 """ |
7 Module implementing a typing completer for Python. |
7 Module implementing a typing completer for Python. |
8 """ |
8 """ |
9 |
9 |
10 |
|
11 import re |
10 import re |
12 |
11 |
13 from PyQt5.QtCore import QRegExp |
|
14 from PyQt5.Qsci import QsciLexerPython, QsciScintilla |
12 from PyQt5.Qsci import QsciLexerPython, QsciScintilla |
15 |
13 |
16 from .CompleterBase import CompleterBase |
14 from .CompleterBase import CompleterBase |
17 |
15 |
18 import Preferences |
16 import Preferences |
|
17 from Utilities import rxIndex |
19 |
18 |
20 |
19 |
21 class CompleterPython(CompleterBase): |
20 class CompleterPython(CompleterBase): |
22 """ |
21 """ |
23 Class implementing typing completer for Python. |
22 Class implementing typing completer for Python. |
29 @param editor reference to the editor object (QScintilla.Editor) |
28 @param editor reference to the editor object (QScintilla.Editor) |
30 @param parent reference to the parent object (QObject) |
29 @param parent reference to the parent object (QObject) |
31 """ |
30 """ |
32 super(CompleterPython, self).__init__(editor, parent) |
31 super(CompleterPython, self).__init__(editor, parent) |
33 |
32 |
34 self.__defRX = QRegExp(r"""^[ \t]*def \w+\(""") |
33 self.__defRX = re.compile(r"^[ \t]*def \w+\(") |
35 self.__defSelfRX = QRegExp(r"""^[ \t]*def \w+\([ \t]*self[ \t]*[,)]""") |
34 self.__defSelfRX = re.compile(r"^[ \t]*def \w+\([ \t]*self[ \t]*[,)]") |
36 self.__defClsRX = QRegExp(r"""^[ \t]*def \w+\([ \t]*cls[ \t]*[,)]""") |
35 self.__defClsRX = re.compile(r"^[ \t]*def \w+\([ \t]*cls[ \t]*[,)]") |
37 self.__classRX = QRegExp(r"""^[ \t]*class \w+\(""") |
36 self.__classRX = re.compile(r"^[ \t]*class \w+\(") |
38 self.__importRX = QRegExp(r"""^[ \t]*from [\w.]+ """) |
37 self.__importRX = re.compile(r"^[ \t]*from [\w.]+ ") |
39 self.__classmethodRX = QRegExp(r"""^[ \t]*@classmethod""") |
38 self.__classmethodRX = re.compile(r"^[ \t]*@classmethod") |
40 self.__staticmethodRX = QRegExp(r"""^[ \t]*@staticmethod""") |
39 self.__staticmethodRX = re.compile(r"^[ \t]*@staticmethod") |
41 |
40 |
42 self.__defOnlyRX = QRegExp(r"""^[ \t]*def """) |
41 self.__defOnlyRX = re.compile(r"^[ \t]*def ") |
43 |
42 |
44 self.__ifRX = QRegExp(r"""^[ \t]*if """) |
43 self.__ifRX = re.compile(r"^[ \t]*if ") |
45 self.__elifRX = QRegExp(r"""^[ \t]*elif """) |
44 self.__elifRX = re.compile(r"^[ \t]*elif ") |
46 self.__elseRX = QRegExp(r"""^[ \t]*else:""") |
45 self.__elseRX = re.compile(r"^[ \t]*else:") |
47 |
46 |
48 self.__tryRX = QRegExp(r"""^[ \t]*try:""") |
47 self.__tryRX = re.compile(r"^[ \t]*try:") |
49 self.__finallyRX = QRegExp(r"""^[ \t]*finally:""") |
48 self.__finallyRX = re.compile(r"^[ \t]*finally:") |
50 self.__exceptRX = QRegExp(r"""^[ \t]*except """) |
49 self.__exceptRX = re.compile(r"^[ \t]*except ") |
51 self.__exceptcRX = QRegExp(r"""^[ \t]*except:""") |
50 self.__exceptcRX = re.compile(r"^[ \t]*except:") |
52 |
51 |
53 self.__whileRX = QRegExp(r"""^[ \t]*while """) |
52 self.__whileRX = re.compile(r"^[ \t]*while ") |
54 self.__forRX = QRegExp(r"""^[ \t]*for """) |
53 self.__forRX = re.compile(r"^[ \t]*for ") |
55 |
54 |
56 self.readSettings() |
55 self.readSettings() |
57 |
56 |
58 def readSettings(self): |
57 def readSettings(self): |
59 """ |
58 """ |
112 # insert closing parenthesis and self |
111 # insert closing parenthesis and self |
113 if char == '(': |
112 if char == '(': |
114 txt = self.editor.text(line)[:col] |
113 txt = self.editor.text(line)[:col] |
115 if ( |
114 if ( |
116 self.__insertSelf and |
115 self.__insertSelf and |
117 self.__defRX.exactMatch(txt) |
116 self.__defRX.fullmatch(txt) is not None |
118 ): |
117 ): |
119 if self.__isClassMethodDef(): |
118 if self.__isClassMethodDef(): |
120 self.editor.insert('cls') |
119 self.editor.insert('cls') |
121 self.editor.setCursorPosition(line, col + 3) |
120 self.editor.setCursorPosition(line, col + 3) |
122 elif self.__isStaticMethodDef(): |
121 elif self.__isStaticMethodDef(): |
125 elif self.__isClassMethod(): |
124 elif self.__isClassMethod(): |
126 self.editor.insert('self') |
125 self.editor.insert('self') |
127 self.editor.setCursorPosition(line, col + 4) |
126 self.editor.setCursorPosition(line, col + 4) |
128 if self.__insertClosingBrace: |
127 if self.__insertClosingBrace: |
129 if ( |
128 if ( |
130 self.__defRX.exactMatch(txt) or |
129 self.__defRX.fullmatch(txt) is not None or |
131 self.__classRX.exactMatch(txt) |
130 self.__classRX.fullmatch(txt) is not None |
132 ): |
131 ): |
133 self.editor.insert('):') |
132 self.editor.insert('):') |
134 else: |
133 else: |
135 self.editor.insert(')') |
134 self.editor.insert(')') |
136 |
135 |
146 # space |
145 # space |
147 # insert import, dedent to if for elif, dedent to try for except, |
146 # insert import, dedent to if for elif, dedent to try for except, |
148 # dedent def |
147 # dedent def |
149 elif char == ' ': |
148 elif char == ' ': |
150 txt = self.editor.text(line)[:col] |
149 txt = self.editor.text(line)[:col] |
151 if self.__insertImport and self.__importRX.exactMatch(txt): |
150 if self.__insertImport and self.__importRX.fullmatch(txt): |
152 if self.__importBraceType: |
151 if self.__importBraceType: |
153 self.editor.insert('import ()') |
152 self.editor.insert('import ()') |
154 self.editor.setCursorPosition(line, col + 8) |
153 self.editor.setCursorPosition(line, col + 8) |
155 else: |
154 else: |
156 self.editor.insert('import ') |
155 self.editor.insert('import ') |
157 self.editor.setCursorPosition(line, col + 7) |
156 self.editor.setCursorPosition(line, col + 7) |
158 elif self.__dedentElse and self.__elifRX.exactMatch(txt): |
157 elif self.__dedentElse and self.__elifRX.fullmatch(txt): |
159 self.__dedentToIf() |
158 self.__dedentToIf() |
160 elif self.__dedentExcept and self.__exceptRX.exactMatch(txt): |
159 elif self.__dedentExcept and self.__exceptRX.fullmatch(txt): |
161 self.__dedentExceptToTry(False) |
160 self.__dedentExceptToTry(False) |
162 elif self.__dedentDef and self.__defOnlyRX.exactMatch(txt): |
161 elif self.__dedentDef and self.__defOnlyRX.fullmatch(txt): |
163 self.__dedentDefStatement() |
162 self.__dedentDefStatement() |
164 |
163 |
165 # comma |
164 # comma |
166 # insert blank |
165 # insert blank |
167 elif char == ',': |
166 elif char == ',': |
201 if self.__colonDetection: |
200 if self.__colonDetection: |
202 self.editor.setSelection(line, col, line, col + 1) |
201 self.editor.setSelection(line, col, line, col + 1) |
203 self.editor.removeSelectedText() |
202 self.editor.removeSelectedText() |
204 else: |
203 else: |
205 txt = text[:col] |
204 txt = text[:col] |
206 if self.__dedentElse and self.__elseRX.exactMatch(txt): |
205 if self.__dedentElse and self.__elseRX.fullmatch(txt): |
207 self.__dedentElseToIfWhileForTry() |
206 self.__dedentElseToIfWhileForTry() |
208 elif self.__dedentExcept and self.__exceptcRX.exactMatch(txt): |
207 elif self.__dedentExcept and self.__exceptcRX.fullmatch(txt): |
209 self.__dedentExceptToTry(True) |
208 self.__dedentExceptToTry(True) |
210 elif self.__dedentExcept and self.__finallyRX.exactMatch(txt): |
209 elif self.__dedentExcept and self.__finallyRX.fullmatch(txt): |
211 self.__dedentFinallyToTry() |
210 self.__dedentFinallyToTry() |
212 |
211 |
213 # new line |
212 # new line |
214 # indent to opening brace |
213 # indent to opening brace |
215 elif char == '\n': |
214 elif char == '\n': |
251 indentation = self.editor.indentation(line) |
250 indentation = self.editor.indentation(line) |
252 ifLine = line - 1 |
251 ifLine = line - 1 |
253 while ifLine >= 0: |
252 while ifLine >= 0: |
254 txt = self.editor.text(ifLine) |
253 txt = self.editor.text(ifLine) |
255 edInd = self.editor.indentation(ifLine) |
254 edInd = self.editor.indentation(ifLine) |
256 if self.__elseRX.indexIn(txt) == 0 and edInd <= indentation: |
255 if rxIndex(self.__elseRX, txt) == 0 and edInd <= indentation: |
257 indentation = edInd - 1 |
256 indentation = edInd - 1 |
258 elif (self.__ifRX.indexIn(txt) == 0 or |
257 elif (rxIndex(self.__ifRX, txt) == 0 or |
259 self.__elifRX.indexIn(txt) == 0) and edInd <= indentation: |
258 rxIndex(self.__elifRX, txt) == 0) and edInd <= indentation: |
260 self.editor.cancelList() |
259 self.editor.cancelList() |
261 self.editor.setIndentation(line, edInd) |
260 self.editor.setIndentation(line, edInd) |
262 break |
261 break |
263 ifLine -= 1 |
262 ifLine -= 1 |
264 |
263 |
273 prevInd = self.editor.indentation(line - 1) |
272 prevInd = self.editor.indentation(line - 1) |
274 ifLine = line - 1 |
273 ifLine = line - 1 |
275 while ifLine >= 0: |
274 while ifLine >= 0: |
276 txt = self.editor.text(ifLine) |
275 txt = self.editor.text(ifLine) |
277 edInd = self.editor.indentation(ifLine) |
276 edInd = self.editor.indentation(ifLine) |
278 if self.__elseRX.indexIn(txt) == 0 and edInd <= indentation: |
277 if rxIndex(self.__elseRX, txt) == 0 and edInd <= indentation: |
279 indentation = edInd - 1 |
278 indentation = edInd - 1 |
280 elif ( |
279 elif ( |
281 self.__elifRX.indexIn(txt) == 0 and |
280 rxIndex(self.__elifRX, txt) == 0 and |
282 edInd == indentation and |
281 edInd == indentation and |
283 edInd == prevInd |
282 edInd == prevInd |
284 ): |
283 ): |
285 indentation = edInd - 1 |
284 indentation = edInd - 1 |
286 elif ( |
285 elif ( |
287 (self.__ifRX.indexIn(txt) == 0 or |
286 (rxIndex(self.__ifRX, txt) == 0 or |
288 self.__whileRX.indexIn(txt) == 0 or |
287 rxIndex(self.__whileRX, txt) == 0 or |
289 self.__forRX.indexIn(txt) == 0 or |
288 rxIndex(self.__forRX, txt) == 0 or |
290 self.__tryRX.indexIn(txt) == 0) and |
289 rxIndex(self.__tryRX, txt) == 0) and |
291 edInd <= indentation |
290 edInd <= indentation |
292 ): |
291 ): |
293 self.editor.cancelList() |
292 self.editor.cancelList() |
294 self.editor.setIndentation(line, edInd) |
293 self.editor.setIndentation(line, edInd) |
295 break |
294 break |
307 tryLine = line - 1 |
306 tryLine = line - 1 |
308 while tryLine >= 0: |
307 while tryLine >= 0: |
309 txt = self.editor.text(tryLine) |
308 txt = self.editor.text(tryLine) |
310 edInd = self.editor.indentation(tryLine) |
309 edInd = self.editor.indentation(tryLine) |
311 if ( |
310 if ( |
312 (self.__exceptcRX.indexIn(txt) == 0 or |
311 (rxIndex(self.__exceptcRX, txt) == 0 or |
313 self.__finallyRX.indexIn(txt) == 0) and |
312 rxIndex(self.__finallyRX, txt) == 0) and |
314 edInd <= indentation |
313 edInd <= indentation |
315 ): |
314 ): |
316 indentation = edInd - 1 |
315 indentation = edInd - 1 |
317 elif (self.__exceptRX.indexIn(txt) == 0 or |
316 elif (rxIndex(self.__exceptRX, txt) == 0 or |
318 self.__tryRX.indexIn(txt) == 0) and edInd <= indentation: |
317 rxIndex(self.__tryRX, txt) == 0) and edInd <= indentation: |
319 self.editor.cancelList() |
318 self.editor.cancelList() |
320 self.editor.setIndentation(line, edInd) |
319 self.editor.setIndentation(line, edInd) |
321 break |
320 break |
322 tryLine -= 1 |
321 tryLine -= 1 |
323 |
322 |
330 indentation = self.editor.indentation(line) |
329 indentation = self.editor.indentation(line) |
331 tryLine = line - 1 |
330 tryLine = line - 1 |
332 while tryLine >= 0: |
331 while tryLine >= 0: |
333 txt = self.editor.text(tryLine) |
332 txt = self.editor.text(tryLine) |
334 edInd = self.editor.indentation(tryLine) |
333 edInd = self.editor.indentation(tryLine) |
335 if self.__finallyRX.indexIn(txt) == 0 and edInd <= indentation: |
334 if rxIndex(self.__finallyRX, txt) == 0 and edInd <= indentation: |
336 indentation = edInd - 1 |
335 indentation = edInd - 1 |
337 elif ( |
336 elif ( |
338 (self.__tryRX.indexIn(txt) == 0 or |
337 (rxIndex(self.__tryRX, txt) == 0 or |
339 self.__exceptcRX.indexIn(txt) == 0 or |
338 rxIndex(self.__exceptcRX, txt) == 0 or |
340 self.__exceptRX.indexIn(txt) == 0) and |
339 rxIndex(self.__exceptRX, txt) == 0) and |
341 edInd <= indentation |
340 edInd <= indentation |
342 ): |
341 ): |
343 self.editor.cancelList() |
342 self.editor.cancelList() |
344 self.editor.setIndentation(line, edInd) |
343 self.editor.setIndentation(line, edInd) |
345 break |
344 break |
355 tryLine = line - 1 |
354 tryLine = line - 1 |
356 while tryLine >= 0: |
355 while tryLine >= 0: |
357 txt = self.editor.text(tryLine) |
356 txt = self.editor.text(tryLine) |
358 edInd = self.editor.indentation(tryLine) |
357 edInd = self.editor.indentation(tryLine) |
359 newInd = -1 |
358 newInd = -1 |
360 if self.__defRX.indexIn(txt) == 0 and edInd < indentation: |
359 if rxIndex(self.__defRX, txt) == 0 and edInd < indentation: |
361 newInd = edInd |
360 newInd = edInd |
362 elif self.__classRX.indexIn(txt) == 0 and edInd < indentation: |
361 elif rxIndex(self.__classRX, txt) == 0 and edInd < indentation: |
363 newInd = edInd + ( |
362 newInd = edInd + ( |
364 self.editor.indentationWidth() or self.editor.tabWidth() |
363 self.editor.indentationWidth() or self.editor.tabWidth() |
365 ) |
364 ) |
366 if newInd >= 0: |
365 if newInd >= 0: |
367 self.editor.cancelList() |
366 self.editor.cancelList() |
379 indentation = self.editor.indentation(line) |
378 indentation = self.editor.indentation(line) |
380 curLine = line - 1 |
379 curLine = line - 1 |
381 while curLine >= 0: |
380 while curLine >= 0: |
382 txt = self.editor.text(curLine) |
381 txt = self.editor.text(curLine) |
383 if ( |
382 if ( |
384 (self.__defSelfRX.indexIn(txt) == 0 or |
383 (rxIndex(self.__defSelfRX, txt) == 0 or |
385 self.__defClsRX.indexIn(txt) == 0) and |
384 rxIndex(self.__defClsRX, txt) == 0) and |
386 self.editor.indentation(curLine) == indentation |
385 self.editor.indentation(curLine) == indentation |
387 ): |
386 ): |
388 return True |
387 return True |
389 elif ( |
388 elif ( |
390 self.__classRX.indexIn(txt) == 0 and |
389 rxIndex(self.__classRX, txt) == 0 and |
391 self.editor.indentation(curLine) < indentation |
390 self.editor.indentation(curLine) < indentation |
392 ): |
391 ): |
393 return True |
392 return True |
394 elif ( |
393 elif ( |
395 self.__defRX.indexIn(txt) == 0 and |
394 rxIndex(self.__defRX, txt) == 0 and |
396 self.editor.indentation(curLine) <= indentation |
395 self.editor.indentation(curLine) <= indentation |
397 ): |
396 ): |
398 return False |
397 return False |
399 curLine -= 1 |
398 curLine -= 1 |
400 return False |
399 return False |