eric6/QScintilla/Lexers/LexerPygments.py

changeset 6942
2602857055c5
parent 6891
93f82da09f22
child 7229
53054eb5b15a
equal deleted inserted replaced
6941:f99d60d6b59b 6942:2602857055c5
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2008 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a custom lexer using pygments.
8 """
9
10 from __future__ import unicode_literals
11
12 from pygments.token import Token
13 from pygments.lexers import guess_lexer_for_filename, guess_lexer, \
14 find_lexer_class
15 from pygments.util import ClassNotFound
16
17 from PyQt5.QtGui import QColor, QFont
18
19 from QScintilla.Lexers.LexerContainer import LexerContainer
20
21 import Utilities
22
23 PYGMENTS_DEFAULT = 0
24 PYGMENTS_COMMENT = 1
25 PYGMENTS_PREPROCESSOR = 2
26 PYGMENTS_KEYWORD = 3
27 PYGMENTS_PSEUDOKEYWORD = 4
28 PYGMENTS_TYPEKEYWORD = 5
29 PYGMENTS_OPERATOR = 6
30 PYGMENTS_WORD = 7
31 PYGMENTS_BUILTIN = 8
32 PYGMENTS_FUNCTION = 9
33 PYGMENTS_CLASS = 10
34 PYGMENTS_NAMESPACE = 11
35 PYGMENTS_EXCEPTION = 12
36 PYGMENTS_VARIABLE = 13
37 PYGMENTS_CONSTANT = 14
38 PYGMENTS_LABEL = 15
39 PYGMENTS_ENTITY = 16
40 PYGMENTS_ATTRIBUTE = 17
41 PYGMENTS_TAG = 18
42 PYGMENTS_DECORATOR = 19
43 PYGMENTS_STRING = 20
44 PYGMENTS_DOCSTRING = 21
45 PYGMENTS_SCALAR = 22
46 PYGMENTS_ESCAPE = 23
47 PYGMENTS_REGEX = 24
48 PYGMENTS_SYMBOL = 25
49 PYGMENTS_OTHER = 26
50 PYGMENTS_NUMBER = 27
51 PYGMENTS_HEADING = 28
52 PYGMENTS_SUBHEADING = 29
53 PYGMENTS_DELETED = 30
54 PYGMENTS_INSERTED = 31
55 # 32 to 39 are reserved for QScintilla internal styles
56 PYGMENTS_GENERIC_ERROR = 40
57 PYGMENTS_EMPHASIZE = 41
58 PYGMENTS_STRONG = 42
59 PYGMENTS_PROMPT = 43
60 PYGMENTS_OUTPUT = 44
61 PYGMENTS_TRACEBACK = 45
62 PYGMENTS_ERROR = 46
63 PYGMENTS_MULTILINECOMMENT = 47
64 PYGMENTS_PROPERTY = 48
65 PYGMENTS_CHAR = 49
66 PYGMENTS_HEREDOC = 50
67 PYGMENTS_PUNCTUATION = 51
68 # added with Pygments 2.1
69 PYGMENTS_HASHBANG = 52
70 PYGMENTS_RESERVEDKEYWORD = 53
71 PYGMENTS_LITERAL = 54
72 PYGMENTS_DOUBLESTRING = 55
73 PYGMENTS_SINGLESTRING = 56
74 PYGMENTS_BACKTICKSTRING = 57
75 PYGMENTS_WHITESPACE = 58
76
77 #-----------------------------------------------------------------------------#
78
79 TOKEN_MAP = {
80 Token.Comment: PYGMENTS_COMMENT,
81 Token.Comment.Hashbang: PYGMENTS_HASHBANG,
82 Token.Comment.Multiline: PYGMENTS_MULTILINECOMMENT,
83 Token.Comment.Preproc: PYGMENTS_PREPROCESSOR,
84 Token.Comment.PreprocFile: PYGMENTS_PREPROCESSOR,
85 Token.Comment.Single: PYGMENTS_COMMENT,
86 Token.Comment.Special: PYGMENTS_COMMENT,
87
88 Token.Error: PYGMENTS_ERROR,
89
90 Token.Generic.Deleted: PYGMENTS_DELETED,
91 Token.Generic.Emph: PYGMENTS_EMPHASIZE,
92 Token.Generic.Error: PYGMENTS_GENERIC_ERROR,
93 Token.Generic.Heading: PYGMENTS_HEADING,
94 Token.Generic.Inserted: PYGMENTS_INSERTED,
95 Token.Generic.Output: PYGMENTS_OUTPUT,
96 Token.Generic.Prompt: PYGMENTS_PROMPT,
97 Token.Generic.Strong: PYGMENTS_STRONG,
98 Token.Generic.Subheading: PYGMENTS_SUBHEADING,
99 Token.Generic.Traceback: PYGMENTS_TRACEBACK,
100
101 Token.Keyword: PYGMENTS_KEYWORD,
102 Token.Keyword.Constant: PYGMENTS_KEYWORD,
103 Token.Keyword.Declaration: PYGMENTS_KEYWORD,
104 Token.Keyword.Namespace: PYGMENTS_KEYWORD,
105 Token.Keyword.Pseudo: PYGMENTS_PSEUDOKEYWORD,
106 Token.Keyword.Reserved: PYGMENTS_RESERVEDKEYWORD,
107 Token.Keyword.Type: PYGMENTS_TYPEKEYWORD,
108
109 Token.Literal: PYGMENTS_LITERAL,
110 Token.Literal.Date: PYGMENTS_LITERAL,
111
112 Token.Name: PYGMENTS_DEFAULT,
113 Token.Name.Attribute: PYGMENTS_ATTRIBUTE,
114 Token.Name.Builtin: PYGMENTS_BUILTIN,
115 Token.Name.Builtin.Pseudo: PYGMENTS_BUILTIN,
116 Token.Name.Class: PYGMENTS_CLASS,
117 Token.Name.Constant: PYGMENTS_CONSTANT,
118 Token.Name.Decorator: PYGMENTS_DECORATOR,
119 Token.Name.Entity: PYGMENTS_ENTITY,
120 Token.Name.Exception: PYGMENTS_EXCEPTION,
121 Token.Name.Function: PYGMENTS_FUNCTION,
122 Token.Name.Label: PYGMENTS_LABEL,
123 Token.Name.Namespace: PYGMENTS_NAMESPACE,
124 Token.Name.Other: PYGMENTS_VARIABLE,
125 Token.Name.Property: PYGMENTS_PROPERTY,
126 Token.Name.Tag: PYGMENTS_TAG,
127 Token.Name.Variable: PYGMENTS_VARIABLE,
128 Token.Name.Variable.Class: PYGMENTS_VARIABLE,
129 Token.Name.Variable.Global: PYGMENTS_VARIABLE,
130 Token.Name.Variable.Instance: PYGMENTS_VARIABLE,
131
132 Token.Number: PYGMENTS_NUMBER,
133 Token.Number.Bin: PYGMENTS_NUMBER,
134 Token.Number.Float: PYGMENTS_NUMBER,
135 Token.Number.Hex: PYGMENTS_NUMBER,
136 Token.Number.Integer: PYGMENTS_NUMBER,
137 Token.Number.Integer.Long: PYGMENTS_NUMBER,
138 Token.Number.Oct: PYGMENTS_NUMBER,
139
140 Token.Operator: PYGMENTS_OPERATOR,
141 Token.Operator.Word: PYGMENTS_WORD,
142
143 Token.Punctuation: PYGMENTS_PUNCTUATION,
144
145 Token.String: PYGMENTS_STRING,
146 Token.String.Backtick: PYGMENTS_BACKTICKSTRING,
147 Token.String.Char: PYGMENTS_CHAR,
148 Token.String.Doc: PYGMENTS_DOCSTRING,
149 Token.String.Double: PYGMENTS_DOUBLESTRING,
150 Token.String.Escape: PYGMENTS_ESCAPE,
151 Token.String.Heredoc: PYGMENTS_HEREDOC,
152 Token.String.Interpol: PYGMENTS_SCALAR,
153 Token.String.Other: PYGMENTS_OTHER,
154 Token.String.Regex: PYGMENTS_REGEX,
155 Token.String.Single: PYGMENTS_SINGLESTRING,
156 Token.String.Symbol: PYGMENTS_SYMBOL,
157
158 Token.Whitespace: PYGMENTS_WHITESPACE,
159 }
160
161 #-----------------------------------------------------------------------------#
162
163
164 class LexerPygments(LexerContainer):
165 """
166 Class implementing a custom lexer using pygments.
167 """
168 def __init__(self, parent=None, name=""):
169 """
170 Constructor
171
172 @param parent parent widget of this lexer
173 @keyparam name name of the pygments lexer to use (string)
174 """
175 LexerContainer.__init__(self, parent)
176
177 self.__pygmentsName = name
178
179 self.descriptions = {
180 PYGMENTS_DEFAULT: self.tr("Default"),
181 PYGMENTS_COMMENT: self.tr("Comment"),
182 PYGMENTS_PREPROCESSOR: self.tr("Preprocessor"),
183 PYGMENTS_KEYWORD: self.tr("Keyword"),
184 PYGMENTS_PSEUDOKEYWORD: self.tr("Pseudo Keyword"),
185 PYGMENTS_TYPEKEYWORD: self.tr("Type Keyword"),
186 PYGMENTS_OPERATOR: self.tr("Operator"),
187 PYGMENTS_WORD: self.tr("Word"),
188 PYGMENTS_BUILTIN: self.tr("Builtin"),
189 PYGMENTS_FUNCTION: self.tr("Function or method name"),
190 PYGMENTS_CLASS: self.tr("Class name"),
191 PYGMENTS_NAMESPACE: self.tr("Namespace"),
192 PYGMENTS_EXCEPTION: self.tr("Exception"),
193 PYGMENTS_VARIABLE: self.tr("Identifier"),
194 PYGMENTS_CONSTANT: self.tr("Constant"),
195 PYGMENTS_LABEL: self.tr("Label"),
196 PYGMENTS_ENTITY: self.tr("Entity"),
197 PYGMENTS_ATTRIBUTE: self.tr("Attribute"),
198 PYGMENTS_TAG: self.tr("Tag"),
199 PYGMENTS_DECORATOR: self.tr("Decorator"),
200 PYGMENTS_STRING: self.tr("String"),
201 PYGMENTS_DOCSTRING: self.tr("Documentation string"),
202 PYGMENTS_SCALAR: self.tr("Scalar"),
203 PYGMENTS_ESCAPE: self.tr("Escape"),
204 PYGMENTS_REGEX: self.tr("Regular expression"),
205 PYGMENTS_SYMBOL: self.tr("Symbol"),
206 PYGMENTS_OTHER: self.tr("Other string"),
207 PYGMENTS_NUMBER: self.tr("Number"),
208 PYGMENTS_HEADING: self.tr("Heading"),
209 PYGMENTS_SUBHEADING: self.tr("Subheading"),
210 PYGMENTS_DELETED: self.tr("Deleted"),
211 PYGMENTS_INSERTED: self.tr("Inserted"),
212 PYGMENTS_GENERIC_ERROR: self.tr("Generic error"),
213 PYGMENTS_EMPHASIZE: self.tr("Emphasized text"),
214 PYGMENTS_STRONG: self.tr("Strong text"),
215 PYGMENTS_PROMPT: self.tr("Prompt"),
216 PYGMENTS_OUTPUT: self.tr("Output"),
217 PYGMENTS_TRACEBACK: self.tr("Traceback"),
218 PYGMENTS_ERROR: self.tr("Error"),
219 PYGMENTS_MULTILINECOMMENT: self.tr("Comment block"),
220 PYGMENTS_PROPERTY: self.tr("Property"),
221 PYGMENTS_CHAR: self.tr("Character"),
222 PYGMENTS_HEREDOC: self.tr("Here document"),
223 PYGMENTS_PUNCTUATION: self.tr("Punctuation"),
224 PYGMENTS_HASHBANG: self.tr("Hashbang"),
225 PYGMENTS_RESERVEDKEYWORD: self.tr("Reserved Keyword"),
226 PYGMENTS_LITERAL: self.tr("Literal"),
227 PYGMENTS_DOUBLESTRING: self.tr("Double quoted string"),
228 PYGMENTS_SINGLESTRING: self.tr("Single quoted string"),
229 PYGMENTS_BACKTICKSTRING: self.tr("Backtick string"),
230 PYGMENTS_WHITESPACE: self.tr("Whitespace"),
231 }
232
233 self.defaultColors = {
234 PYGMENTS_DEFAULT: QColor("#000000"),
235 PYGMENTS_COMMENT: QColor("#408080"),
236 PYGMENTS_PREPROCESSOR: QColor("#BC7A00"),
237 PYGMENTS_KEYWORD: QColor("#008000"),
238 PYGMENTS_PSEUDOKEYWORD: QColor("#008000"),
239 PYGMENTS_TYPEKEYWORD: QColor("#B00040"),
240 PYGMENTS_OPERATOR: QColor("#666666"),
241 PYGMENTS_WORD: QColor("#AA22FF"),
242 PYGMENTS_BUILTIN: QColor("#008000"),
243 PYGMENTS_FUNCTION: QColor("#0000FF"),
244 PYGMENTS_CLASS: QColor("#0000FF"),
245 PYGMENTS_NAMESPACE: QColor("#0000FF"),
246 PYGMENTS_EXCEPTION: QColor("#D2413A"),
247 PYGMENTS_VARIABLE: QColor("#19177C"),
248 PYGMENTS_CONSTANT: QColor("#880000"),
249 PYGMENTS_LABEL: QColor("#A0A000"),
250 PYGMENTS_ENTITY: QColor("#999999"),
251 PYGMENTS_ATTRIBUTE: QColor("#7D9029"),
252 PYGMENTS_TAG: QColor("#008000"),
253 PYGMENTS_DECORATOR: QColor("#AA22FF"),
254 PYGMENTS_STRING: QColor("#BA2121"),
255 PYGMENTS_DOCSTRING: QColor("#BA2121"),
256 PYGMENTS_SCALAR: QColor("#BB6688"),
257 PYGMENTS_ESCAPE: QColor("#BB6622"),
258 PYGMENTS_REGEX: QColor("#BB6688"),
259 PYGMENTS_SYMBOL: QColor("#19177C"),
260 PYGMENTS_OTHER: QColor("#008000"),
261 PYGMENTS_NUMBER: QColor("#666666"),
262 PYGMENTS_HEADING: QColor("#000080"),
263 PYGMENTS_SUBHEADING: QColor("#800080"),
264 PYGMENTS_DELETED: QColor("#A00000"),
265 PYGMENTS_INSERTED: QColor("#00A000"),
266 PYGMENTS_GENERIC_ERROR: QColor("#FF0000"),
267 PYGMENTS_PROMPT: QColor("#000080"),
268 PYGMENTS_OUTPUT: QColor("#808080"),
269 PYGMENTS_TRACEBACK: QColor("#0040D0"),
270 PYGMENTS_MULTILINECOMMENT: QColor("#007F00"),
271 PYGMENTS_PROPERTY: QColor("#00A0E0"),
272 PYGMENTS_CHAR: QColor("#7F007F"),
273 PYGMENTS_HEREDOC: QColor("#7F007F"),
274 PYGMENTS_PUNCTUATION: QColor("#000000"),
275 PYGMENTS_HASHBANG: QColor("#00C000"),
276 PYGMENTS_RESERVEDKEYWORD: QColor("#A90D91"),
277 PYGMENTS_LITERAL: QColor("#1C01CE"),
278 PYGMENTS_DOUBLESTRING: QColor("#7F007F"),
279 PYGMENTS_SINGLESTRING: QColor("#7F007F"),
280 PYGMENTS_BACKTICKSTRING: QColor("#FFFF00"),
281 PYGMENTS_WHITESPACE: QColor("#BBBBBB"),
282 }
283
284 self.defaultPapers = {
285 PYGMENTS_ERROR: QColor("#FF0000"),
286 PYGMENTS_MULTILINECOMMENT: QColor("#A8FFA8"),
287 PYGMENTS_HEREDOC: QColor("#DDD0DD"),
288 PYGMENTS_BACKTICKSTRING: QColor("#a08080"),
289 }
290
291 self.defaultEolFills = {
292 PYGMENTS_ERROR: True,
293 PYGMENTS_MULTILINECOMMENT: True,
294 PYGMENTS_HEREDOC: True,
295 PYGMENTS_BACKTICKSTRING: True,
296 }
297
298 def language(self):
299 """
300 Public method returning the language of the lexer.
301
302 @return language of the lexer (string)
303 """
304 if self.__pygmentsName:
305 return self.__pygmentsName
306 else:
307 return "Guessed"
308
309 def description(self, style):
310 """
311 Public method returning the descriptions of the styles supported
312 by the lexer.
313
314 @param style style number (integer)
315 @return description for the style (string)
316 """
317 try:
318 return self.descriptions[style]
319 except KeyError:
320 return ""
321
322 def defaultColor(self, style):
323 """
324 Public method to get the default foreground color for a style.
325
326 @param style style number (integer)
327 @return foreground color (QColor)
328 """
329 try:
330 return self.defaultColors[style]
331 except KeyError:
332 return LexerContainer.defaultColor(self, style)
333
334 def defaultPaper(self, style):
335 """
336 Public method to get the default background color for a style.
337
338 @param style style number (integer)
339 @return background color (QColor)
340 """
341 try:
342 return self.defaultPapers[style]
343 except KeyError:
344 return LexerContainer.defaultPaper(self, style)
345
346 def defaultFont(self, style):
347 """
348 Public method to get the default font for a style.
349
350 @param style style number (integer)
351 @return font (QFont)
352 """
353 if style in [PYGMENTS_COMMENT, PYGMENTS_PREPROCESSOR,
354 PYGMENTS_MULTILINECOMMENT]:
355 if Utilities.isWindowsPlatform():
356 f = QFont("Comic Sans MS", 9)
357 elif Utilities.isMacPlatform():
358 f = QFont("Courier", 11)
359 else:
360 f = QFont("Bitstream Vera Serif", 9)
361 if style == PYGMENTS_PREPROCESSOR:
362 f.setItalic(True)
363 return f
364
365 if style in [PYGMENTS_STRING, PYGMENTS_CHAR]:
366 if Utilities.isWindowsPlatform():
367 return QFont("Comic Sans MS", 10)
368 elif Utilities.isMacPlatform():
369 f = QFont("Courier", 11)
370 else:
371 return QFont("Bitstream Vera Serif", 10)
372
373 if style in [PYGMENTS_KEYWORD, PYGMENTS_OPERATOR, PYGMENTS_WORD,
374 PYGMENTS_BUILTIN, PYGMENTS_ATTRIBUTE, PYGMENTS_FUNCTION,
375 PYGMENTS_CLASS, PYGMENTS_NAMESPACE, PYGMENTS_EXCEPTION,
376 PYGMENTS_ENTITY, PYGMENTS_TAG, PYGMENTS_SCALAR,
377 PYGMENTS_ESCAPE, PYGMENTS_HEADING, PYGMENTS_SUBHEADING,
378 PYGMENTS_STRONG, PYGMENTS_PROMPT]:
379 f = LexerContainer.defaultFont(self, style)
380 f.setBold(True)
381 return f
382
383 if style in [PYGMENTS_DOCSTRING, PYGMENTS_EMPHASIZE]:
384 f = LexerContainer.defaultFont(self, style)
385 f.setItalic(True)
386 return f
387
388 return LexerContainer.defaultFont(self, style)
389
390 def defaultEolFill(self, style):
391 """
392 Public method to get the default fill to eol flag.
393
394 @param style style number (integer)
395 @return fill to eol flag (boolean)
396 """
397 try:
398 return self.defaultEolFills[style]
399 except KeyError:
400 return LexerContainer.defaultEolFill(self, style)
401
402 def __guessLexer(self, text):
403 """
404 Private method to guess a pygments lexer.
405
406 @param text text to base guessing on (string)
407 @return reference to the guessed lexer (pygments.lexer)
408 """
409 lexer = None
410
411 if self.__pygmentsName:
412 lexerClass = find_lexer_class(self.__pygmentsName)
413 if lexerClass is not None:
414 lexer = lexerClass()
415
416 elif text:
417 # step 1: guess based on filename and text
418 if self.editor is not None:
419 fn = self.editor.getFileName()
420 if fn:
421 try:
422 lexer = guess_lexer_for_filename(fn, text)
423 except ClassNotFound:
424 pass
425
426 # step 2: guess on text only
427 if lexer is None:
428 try:
429 lexer = guess_lexer(text)
430 except ClassNotFound:
431 pass
432
433 return lexer
434
435 def canStyle(self):
436 """
437 Public method to check, if the lexer is able to style the text.
438
439 @return flag indicating the lexer capability (boolean)
440 """
441 if self.editor is None:
442 return True
443
444 text = self.editor.text()
445 self.__lexer = self.__guessLexer(text)
446
447 return self.__lexer is not None
448
449 def name(self):
450 """
451 Public method to get the name of the pygments lexer.
452
453 @return name of the pygments lexer (string)
454 """
455 if self.__lexer is None:
456 return ""
457 else:
458 return self.__lexer.name
459
460 def styleText(self, start, end):
461 """
462 Public method to perform the styling.
463
464 @param start position of first character to be styled (integer)
465 @param end position of last character to be styled (integer)
466 """
467 text = self.editor.text()[:end + 1]
468 textLen = len(text.encode("utf-8"))
469 self.__lexer = self.__guessLexer(text)
470
471 cpos = 0
472 # adjust start position because pygments ignores empty line at
473 # start of text
474 for c in text:
475 if c == "\n":
476 cpos += 1
477 else:
478 break
479
480 self.editor.startStyling(cpos, 0x3f)
481 if self.__lexer is None:
482 self.editor.setStyling(len(text), PYGMENTS_DEFAULT)
483 else:
484 eolLen = len(self.editor.getLineSeparator())
485 for token, txt in self.__lexer.get_tokens(text):
486 style = TOKEN_MAP.get(token, PYGMENTS_DEFAULT)
487
488 tlen = len(txt.encode('utf-8'))
489 if eolLen > 1:
490 tlen += txt.count('\n')
491 cpos += tlen
492 if tlen and cpos < textLen:
493 self.editor.setStyling(tlen, style)
494 if cpos >= textLen:
495 break
496 self.editor.startStyling(cpos, 0x3f)
497
498 def isCommentStyle(self, style):
499 """
500 Public method to check, if a style is a comment style.
501
502 @param style style to check (integer)
503 @return flag indicating a comment style (boolean)
504 """
505 return style in [PYGMENTS_COMMENT]
506
507 def isStringStyle(self, style):
508 """
509 Public method to check, if a style is a string style.
510
511 @param style style to check (integer)
512 @return flag indicating a string style (boolean)
513 """
514 return style in [PYGMENTS_STRING, PYGMENTS_DOCSTRING, PYGMENTS_OTHER,
515 PYGMENTS_HEADING, PYGMENTS_SUBHEADING,
516 PYGMENTS_EMPHASIZE, PYGMENTS_STRONG]
517
518 def defaultKeywords(self, kwSet):
519 """
520 Public method to get the default keywords.
521
522 @param kwSet number of the keyword set (integer)
523 @return string giving the keywords (string) or None
524 """
525 return None # __IGNORE_WARNING_M831__

eric ide

mercurial