|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2002 - 2019 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a Python lexer with some additional methods. |
|
8 """ |
|
9 |
|
10 from __future__ import unicode_literals |
|
11 |
|
12 import re |
|
13 |
|
14 from PyQt5.Qsci import QsciLexerPython, QsciScintilla |
|
15 |
|
16 from .SubstyledLexer import SubstyledLexer |
|
17 import Preferences |
|
18 |
|
19 |
|
20 class LexerPython(SubstyledLexer, QsciLexerPython): |
|
21 """ |
|
22 Subclass to implement some additional lexer dependant methods. |
|
23 """ |
|
24 def __init__(self, variant="", parent=None): |
|
25 """ |
|
26 Constructor |
|
27 |
|
28 @param variant name of the language variant (string) |
|
29 @param parent parent widget of this lexer |
|
30 """ |
|
31 QsciLexerPython.__init__(self, parent) |
|
32 SubstyledLexer.__init__(self) |
|
33 |
|
34 self.variant = variant |
|
35 self.commentString = "#" |
|
36 |
|
37 self.keywordSetDescriptions = [ |
|
38 self.tr("Keywords"), |
|
39 self.tr("Highlighted identifiers"), |
|
40 ] |
|
41 |
|
42 ############################################################## |
|
43 ## default sub-style definitions |
|
44 ############################################################## |
|
45 |
|
46 # list of style numbers, that support sub-styling |
|
47 self.baseStyles = [11] |
|
48 |
|
49 self.defaultSubStyles = { |
|
50 11: { |
|
51 0: { |
|
52 "Description": self.tr("Standard Library Modules"), |
|
53 "Words": """ |
|
54 __main__ _dummy_thread _thread abc aifc argparse array ast asynchat asyncio |
|
55 asyncore atexit audioop base64 bdb binascii binhex bisect builtins bz2 |
|
56 calendar cgi cgitb chunk cmath cmd code codecs codeop collections colorsys |
|
57 compileall concurrent configparser contextlib copy copyreg crypt csv ctypes |
|
58 curses datetime dbm decimal difflib dis distutils dummy_threading email |
|
59 ensurepip enum errno faulthandler fcntl filecmp fileinput fnmatch formatter |
|
60 fpectl fractions ftplib functools gc getopt getpass gettext glob grp gzip |
|
61 hashlib heapq hmac html http http imaplib imghdr importlib inspect io |
|
62 ipaddress itertools json keyword linecache locale logging lzma macpath |
|
63 mailbox mailcap marshal math mimetypes mmap modulefinder msilib msvcrt |
|
64 multiprocessing netrc nis nntplib numbers operator os ossaudiodev parser path |
|
65 pathlib pdb pickle pickletools pipes pkgutil platform plistlib poplib posix |
|
66 pprint pty pwd py_compile pyclbr queue quopri random re readline reprlib |
|
67 resource rlcompleter runpy sched select selectors shelve shlex shutil signal |
|
68 site smtpd smtplib sndhdr socket socketserver spwd sqlite3 ssl stat statistics |
|
69 string stringprep struct subprocess sunau symbol symtable sys sysconfig syslog |
|
70 tabnanny tarfile telnetlib tempfile termios textwrap threading time timeit |
|
71 tkinter token tokenize trace traceback tracemalloc tty turtle types |
|
72 unicodedata unittest urllib uu uuid venv warnings wave weakref webbrowser |
|
73 winreg winsound wsgiref xdrlib xml xmlrpc zipfile zipimport zlib""", |
|
74 "Style": { |
|
75 "fore": 0xDD9900, |
|
76 "font_bold": True, |
|
77 } |
|
78 }, |
|
79 1: { |
|
80 "Description": self.tr("__future__ Imports"), |
|
81 "Words": """ |
|
82 __future__ with_statement unicode_literals print_function division |
|
83 absolute_import generator_stop annotations""", |
|
84 "Style": { |
|
85 "fore": 0xEE00AA, |
|
86 "font_italic": True, |
|
87 } |
|
88 }, |
|
89 2: { |
|
90 "Description": self.tr("PyQt5 Modules"), |
|
91 "Words": """ |
|
92 PyQt5 Qsci Qt QtCore QtDBus QtDesigner QtGui QtHelp QtLocation QtMacExtras |
|
93 QtMultimedia QtMultimediaWidgets QtNetwork QtNetworkAuth QtNfc QtOpenGL |
|
94 QtPositioning QtPrintSupport QtQml QtQuick QtQuickWidgets QtRemoteObjects |
|
95 QtSensors QtSerialPort QtSql QtSvg QtTest QtWebChannel QtWebEngine |
|
96 QtWebEngineCore QtWebEngineWidgets QtWebSockets QtWidgets QtWinExtras |
|
97 QtX11Extras QtXml QtXmlPatterns sip QtWebKit QtWebKitWidgets""", |
|
98 "Style": { |
|
99 "fore": 0x44AADD, |
|
100 "font_bold": True, |
|
101 } |
|
102 }, |
|
103 3: { |
|
104 "Description": self.tr("Cython Specifics"), |
|
105 "Words": "cython pyximport Cython __cinit__ __dealloc__", |
|
106 "Style": { |
|
107 "fore": 0xdd0000, |
|
108 "font_bold": True, |
|
109 } |
|
110 }, |
|
111 }, |
|
112 } |
|
113 |
|
114 def language(self): |
|
115 """ |
|
116 Public method to get the lexer language. |
|
117 |
|
118 @return lexer language (string) |
|
119 """ |
|
120 if not self.variant: |
|
121 return QsciLexerPython.language(self) |
|
122 else: |
|
123 return self.variant |
|
124 |
|
125 def initProperties(self): |
|
126 """ |
|
127 Public slot to initialize the properties. |
|
128 """ |
|
129 self.setIndentationWarning( |
|
130 Preferences.getEditor("PythonBadIndentation")) |
|
131 self.setFoldComments(Preferences.getEditor("PythonFoldComment")) |
|
132 self.setFoldQuotes(Preferences.getEditor("PythonFoldString")) |
|
133 if not Preferences.getEditor("PythonAutoIndent"): |
|
134 self.setAutoIndentStyle(QsciScintilla.AiMaintain) |
|
135 try: |
|
136 self.setV2UnicodeAllowed( |
|
137 Preferences.getEditor("PythonAllowV2Unicode")) |
|
138 self.setV3BinaryOctalAllowed( |
|
139 Preferences.getEditor("PythonAllowV3Binary")) |
|
140 self.setV3BytesAllowed(Preferences.getEditor("PythonAllowV3Bytes")) |
|
141 except AttributeError: |
|
142 pass |
|
143 try: |
|
144 self.setFoldQuotes(Preferences.getEditor("PythonFoldQuotes")) |
|
145 self.setStringsOverNewlineAllowed( |
|
146 Preferences.getEditor("PythonStringsOverNewLineAllowed")) |
|
147 except AttributeError: |
|
148 pass |
|
149 try: |
|
150 self.setHighlightSubidentifiers( |
|
151 Preferences.getEditor("PythonHighlightSubidentifier")) |
|
152 except AttributeError: |
|
153 pass |
|
154 |
|
155 def getIndentationDifference(self, line, editor): |
|
156 """ |
|
157 Public method to determine the difference for the new indentation. |
|
158 |
|
159 @param line line to perform the calculation for (integer) |
|
160 @param editor QScintilla editor |
|
161 @return amount of difference in indentation (integer) |
|
162 """ |
|
163 indent_width = editor.getEditorConfig('IndentWidth') |
|
164 |
|
165 lead_spaces = editor.indentation(line) |
|
166 |
|
167 pline = line - 1 |
|
168 while pline >= 0 and re.match(r'^\s*(#.*)?$', editor.text(pline)): |
|
169 pline -= 1 |
|
170 |
|
171 if pline < 0: |
|
172 last = 0 |
|
173 else: |
|
174 previous_lead_spaces = editor.indentation(pline) |
|
175 # trailing spaces |
|
176 m = re.search(r':\s*(#.*)?$', editor.text(pline)) |
|
177 last = previous_lead_spaces |
|
178 if m: |
|
179 last += indent_width |
|
180 else: |
|
181 # special cases, like pass (unindent) or return (also unindent) |
|
182 m = re.search(r'(pass\s*(#.*)?$)|(^[^#]return)', |
|
183 editor.text(pline)) |
|
184 if m: |
|
185 last -= indent_width |
|
186 |
|
187 if lead_spaces % indent_width != 0 or lead_spaces == 0 \ |
|
188 or self.lastIndented != line: |
|
189 indentDifference = last - lead_spaces |
|
190 else: |
|
191 indentDifference = -indent_width |
|
192 |
|
193 return indentDifference |
|
194 |
|
195 def autoCompletionWordSeparators(self): |
|
196 """ |
|
197 Public method to return the list of separators for autocompletion. |
|
198 |
|
199 @return list of separators (list of strings) |
|
200 """ |
|
201 return ['.'] |
|
202 |
|
203 def isCommentStyle(self, style): |
|
204 """ |
|
205 Public method to check, if a style is a comment style. |
|
206 |
|
207 @param style style to check (integer) |
|
208 @return flag indicating a comment style (boolean) |
|
209 """ |
|
210 return style in [QsciLexerPython.Comment, |
|
211 QsciLexerPython.CommentBlock] |
|
212 |
|
213 def isStringStyle(self, style): |
|
214 """ |
|
215 Public method to check, if a style is a string style. |
|
216 |
|
217 @param style style to check (integer) |
|
218 @return flag indicating a string style (boolean) |
|
219 """ |
|
220 return style in [QsciLexerPython.DoubleQuotedString, |
|
221 QsciLexerPython.SingleQuotedString, |
|
222 QsciLexerPython.TripleDoubleQuotedString, |
|
223 QsciLexerPython.TripleSingleQuotedString, |
|
224 QsciLexerPython.UnclosedString] |
|
225 |
|
226 def defaultKeywords(self, kwSet): |
|
227 """ |
|
228 Public method to get the default keywords. |
|
229 |
|
230 @param kwSet number of the keyword set (integer) |
|
231 @return string giving the keywords (string) or None |
|
232 """ |
|
233 if kwSet == 1: |
|
234 if self.language() in ["Python", "Python2"]: |
|
235 keywords = ("and as assert break class continue def del elif " |
|
236 "else except exec finally for from global if " |
|
237 "import in is lambda not or pass print raise " |
|
238 "return try while with yield") |
|
239 elif self.language() == "Python3": |
|
240 keywords = ("False None True and as assert break class " |
|
241 "continue def del elif else except finally for " |
|
242 "from global if import in is lambda nonlocal not " |
|
243 "or pass raise return try while with yield") |
|
244 elif self.language() == "Cython": |
|
245 keywords = ("False None True and as assert break class " |
|
246 "continue def del elif else except finally for " |
|
247 "from global if import in is lambda nonlocal not " |
|
248 "or pass raise return try while with yield " |
|
249 "cdef cimport cpdef ctypedef") |
|
250 else: |
|
251 keywords = QsciLexerPython.keywords(self, kwSet) |
|
252 else: |
|
253 keywords = QsciLexerPython.keywords(self, kwSet) |
|
254 |
|
255 return keywords |
|
256 |
|
257 def maximumKeywordSet(self): |
|
258 """ |
|
259 Public method to get the maximum keyword set. |
|
260 |
|
261 @return maximum keyword set (integer) |
|
262 """ |
|
263 return 2 |