QScintilla/Lexers/LexerPygments.py

changeset 0
de9c2efb9d02
child 12
1d8dd9706f46
equal deleted inserted replaced
-1:000000000000 0:de9c2efb9d02
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2008 - 2009 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a custom lexer using pygments.
8 """
9
10 import sys
11
12 from pygments.token import Token
13 from pygments.lexers import guess_lexer_for_filename, guess_lexer, find_lexer_class
14 from pygments.util import ClassNotFound
15
16 from PyQt4.QtGui import QColor, QFont
17
18 from QScintilla.Lexers.LexerContainer import LexerContainer
19
20 import Utilities
21
22 PYGMENTS_DEFAULT, \
23 PYGMENTS_COMMENT, \
24 PYGMENTS_PREPROCESSOR, \
25 PYGMENTS_KEYWORD, \
26 PYGMENTS_PSEUDOKEYWORD, \
27 PYGMENTS_TYPEKEYWORD, \
28 PYGMENTS_OPERATOR, \
29 PYGMENTS_WORD, \
30 PYGMENTS_BUILTIN, \
31 PYGMENTS_FUNCTION, \
32 PYGMENTS_CLASS, \
33 PYGMENTS_NAMESPACE, \
34 PYGMENTS_EXCEPTION, \
35 PYGMENTS_VARIABLE, \
36 PYGMENTS_CONSTANT, \
37 PYGMENTS_LABEL, \
38 PYGMENTS_ENTITY, \
39 PYGMENTS_ATTRIBUTE, \
40 PYGMENTS_TAG, \
41 PYGMENTS_DECORATOR, \
42 PYGMENTS_STRING, \
43 PYGMENTS_DOCSTRING, \
44 PYGMENTS_SCALAR, \
45 PYGMENTS_ESCAPE, \
46 PYGMENTS_REGEX, \
47 PYGMENTS_SYMBOL, \
48 PYGMENTS_OTHER, \
49 PYGMENTS_NUMBER, \
50 PYGMENTS_HEADING, \
51 PYGMENTS_SUBHEADING, \
52 PYGMENTS_DELETED, \
53 PYGMENTS_INSERTED = range(32)
54 # 32 to 39 are reserved for QScintilla internal styles
55 PYGMENTS_GENERIC_ERROR, \
56 PYGMENTS_EMPHASIZE, \
57 PYGMENTS_STRONG, \
58 PYGMENTS_PROMPT, \
59 PYGMENTS_OUTPUT, \
60 PYGMENTS_TRACEBACK, \
61 PYGMENTS_ERROR = range(40, 47)
62
63 #-----------------------------------------------------------------------------#
64
65 TOKEN_MAP = {
66 Token.Comment: PYGMENTS_COMMENT,
67 Token.Comment.Preproc: PYGMENTS_PREPROCESSOR,
68
69 Token.Keyword: PYGMENTS_KEYWORD,
70 Token.Keyword.Pseudo: PYGMENTS_PSEUDOKEYWORD,
71 Token.Keyword.Type: PYGMENTS_TYPEKEYWORD,
72
73 Token.Operator: PYGMENTS_OPERATOR,
74 Token.Operator.Word: PYGMENTS_WORD,
75
76 Token.Name.Builtin: PYGMENTS_BUILTIN,
77 Token.Name.Function: PYGMENTS_FUNCTION,
78 Token.Name.Class: PYGMENTS_CLASS,
79 Token.Name.Namespace: PYGMENTS_NAMESPACE,
80 Token.Name.Exception: PYGMENTS_EXCEPTION,
81 Token.Name.Variable: PYGMENTS_VARIABLE,
82 Token.Name.Constant: PYGMENTS_CONSTANT,
83 Token.Name.Label: PYGMENTS_LABEL,
84 Token.Name.Entity: PYGMENTS_ENTITY,
85 Token.Name.Attribute: PYGMENTS_ATTRIBUTE,
86 Token.Name.Tag: PYGMENTS_TAG,
87 Token.Name.Decorator: PYGMENTS_DECORATOR,
88
89 Token.String: PYGMENTS_STRING,
90 Token.String.Doc: PYGMENTS_DOCSTRING,
91 Token.String.Interpol: PYGMENTS_SCALAR,
92 Token.String.Escape: PYGMENTS_ESCAPE,
93 Token.String.Regex: PYGMENTS_REGEX,
94 Token.String.Symbol: PYGMENTS_SYMBOL,
95 Token.String.Other: PYGMENTS_OTHER,
96 Token.Number: PYGMENTS_NUMBER,
97
98 Token.Generic.Heading: PYGMENTS_HEADING,
99 Token.Generic.Subheading: PYGMENTS_SUBHEADING,
100 Token.Generic.Deleted: PYGMENTS_DELETED,
101 Token.Generic.Inserted: PYGMENTS_INSERTED,
102 Token.Generic.Error: PYGMENTS_GENERIC_ERROR,
103 Token.Generic.Emph: PYGMENTS_EMPHASIZE,
104 Token.Generic.Strong: PYGMENTS_STRONG,
105 Token.Generic.Prompt: PYGMENTS_PROMPT,
106 Token.Generic.Output: PYGMENTS_OUTPUT,
107 Token.Generic.Traceback: PYGMENTS_TRACEBACK,
108
109 Token.Error: PYGMENTS_ERROR,
110 }
111
112 #-----------------------------------------------------------------------------#
113
114 class LexerPygments(LexerContainer):
115 """
116 Class implementing a custom lexer using pygments.
117 """
118 def __init__(self, parent = None, name = ""):
119 """
120 Constructor
121
122 @param parent parent widget of this lexer
123 @keyparam name name of the pygments lexer to use (string)
124 """
125 LexerContainer.__init__(self, parent)
126
127 self.__pygmentsName = name
128
129 self.descriptions = {
130 PYGMENTS_DEFAULT : self.trUtf8("Default"),
131 PYGMENTS_COMMENT : self.trUtf8("Comment"),
132 PYGMENTS_PREPROCESSOR : self.trUtf8("Preprocessor"),
133 PYGMENTS_KEYWORD : self.trUtf8("Keyword"),
134 PYGMENTS_PSEUDOKEYWORD : self.trUtf8("Pseudo Keyword"),
135 PYGMENTS_TYPEKEYWORD : self.trUtf8("Type Keyword"),
136 PYGMENTS_OPERATOR : self.trUtf8("Operator"),
137 PYGMENTS_WORD : self.trUtf8("Word"),
138 PYGMENTS_BUILTIN : self.trUtf8("Builtin"),
139 PYGMENTS_FUNCTION : self.trUtf8("Function or method name"),
140 PYGMENTS_CLASS : self.trUtf8("Class name"),
141 PYGMENTS_NAMESPACE : self.trUtf8("Namespace"),
142 PYGMENTS_EXCEPTION : self.trUtf8("Exception"),
143 PYGMENTS_VARIABLE : self.trUtf8("Identifier"),
144 PYGMENTS_CONSTANT : self.trUtf8("Constant"),
145 PYGMENTS_LABEL : self.trUtf8("Label"),
146 PYGMENTS_ENTITY : self.trUtf8("Entity"),
147 PYGMENTS_ATTRIBUTE : self.trUtf8("Attribute"),
148 PYGMENTS_TAG : self.trUtf8("Tag"),
149 PYGMENTS_DECORATOR : self.trUtf8("Decorator"),
150 PYGMENTS_STRING : self.trUtf8("String"),
151 PYGMENTS_DOCSTRING : self.trUtf8("Documentation string"),
152 PYGMENTS_SCALAR : self.trUtf8("Scalar"),
153 PYGMENTS_ESCAPE : self.trUtf8("Escape"),
154 PYGMENTS_REGEX : self.trUtf8("Regular expression"),
155 PYGMENTS_SYMBOL : self.trUtf8("Symbol"),
156 PYGMENTS_OTHER : self.trUtf8("Other string"),
157 PYGMENTS_NUMBER : self.trUtf8("Number"),
158 PYGMENTS_HEADING : self.trUtf8("Heading"),
159 PYGMENTS_SUBHEADING : self.trUtf8("Subheading"),
160 PYGMENTS_DELETED : self.trUtf8("Deleted"),
161 PYGMENTS_INSERTED : self.trUtf8("Inserted"),
162 PYGMENTS_GENERIC_ERROR : self.trUtf8("Generic error"),
163 PYGMENTS_EMPHASIZE : self.trUtf8("Emphasized text"),
164 PYGMENTS_STRONG : self.trUtf8("Strong text"),
165 PYGMENTS_PROMPT : self.trUtf8("Prompt"),
166 PYGMENTS_OUTPUT : self.trUtf8("Output"),
167 PYGMENTS_TRACEBACK : self.trUtf8("Traceback"),
168 PYGMENTS_ERROR : self.trUtf8("Error"),
169 }
170
171 self.defaultColors = {
172 PYGMENTS_DEFAULT : QColor("#000000"),
173 PYGMENTS_COMMENT : QColor("#408080"),
174 PYGMENTS_PREPROCESSOR : QColor("#BC7A00"),
175 PYGMENTS_KEYWORD : QColor("#008000"),
176 PYGMENTS_PSEUDOKEYWORD : QColor("#008000"),
177 PYGMENTS_TYPEKEYWORD : QColor("#B00040"),
178 PYGMENTS_OPERATOR : QColor("#666666"),
179 PYGMENTS_WORD : QColor("#AA22FF"),
180 PYGMENTS_BUILTIN : QColor("#008000"),
181 PYGMENTS_FUNCTION : QColor("#0000FF"),
182 PYGMENTS_CLASS : QColor("#0000FF"),
183 PYGMENTS_NAMESPACE : QColor("#0000FF"),
184 PYGMENTS_EXCEPTION : QColor("#D2413A"),
185 PYGMENTS_VARIABLE : QColor("#19177C"),
186 PYGMENTS_CONSTANT : QColor("#880000"),
187 PYGMENTS_LABEL : QColor("#A0A000"),
188 PYGMENTS_ENTITY : QColor("#999999"),
189 PYGMENTS_ATTRIBUTE : QColor("#7D9029"),
190 PYGMENTS_TAG : QColor("#008000"),
191 PYGMENTS_DECORATOR : QColor("#AA22FF"),
192 PYGMENTS_STRING : QColor("#BA2121"),
193 PYGMENTS_DOCSTRING : QColor("#BA2121"),
194 PYGMENTS_SCALAR : QColor("#BB6688"),
195 PYGMENTS_ESCAPE : QColor("#BB6622"),
196 PYGMENTS_REGEX : QColor("#BB6688"),
197 PYGMENTS_SYMBOL : QColor("#19177C"),
198 PYGMENTS_OTHER : QColor("#008000"),
199 PYGMENTS_NUMBER : QColor("#666666"),
200 PYGMENTS_HEADING : QColor("#000080"),
201 PYGMENTS_SUBHEADING : QColor("#800080"),
202 PYGMENTS_DELETED : QColor("#A00000"),
203 PYGMENTS_INSERTED : QColor("#00A000"),
204 PYGMENTS_GENERIC_ERROR : QColor("#FF0000"),
205 PYGMENTS_PROMPT : QColor("#000080"),
206 PYGMENTS_OUTPUT : QColor("#808080"),
207 PYGMENTS_TRACEBACK : QColor("#0040D0"),
208 }
209
210 self.defaultPapers = {
211 PYGMENTS_ERROR : QColor("#FF0000"),
212 }
213
214 def language(self):
215 """
216 Public method returning the language of the lexer.
217
218 @return language of the lexer (string)
219 """
220 return "Guessed"
221
222 def description(self, style):
223 """
224 Public method returning the descriptions of the styles supported
225 by the lexer.
226
227 @param style style number (integer)
228 @return description for the style (string)
229 """
230 try:
231 return self.descriptions[style]
232 except KeyError:
233 return ""
234
235 def defaultColor(self, style):
236 """
237 Public method to get the default foreground color for a style.
238
239 @param style style number (integer)
240 @return foreground color (QColor)
241 """
242 try:
243 return self.defaultColors[style]
244 except KeyError:
245 return LexerContainer.defaultColor(self, style)
246
247 def defaultPaper(self, style):
248 """
249 Public method to get the default background color for a style.
250
251 @param style style number (integer)
252 @return background color (QColor)
253 """
254 try:
255 return self.defaultPapers[style]
256 except KeyError:
257 return LexerContainer.defaultPaper(self, style)
258
259 def defaultFont(self, style):
260 """
261 Public method to get the default font for a style.
262
263 @param style style number (integer)
264 @return font (QFont)
265 """
266 if style in [PYGMENTS_COMMENT, PYGMENTS_PREPROCESSOR]:
267 if Utilities.isWindowsPlatform():
268 f = QFont("Comic Sans MS", 9)
269 else:
270 f = QFont("Bitstream Vera Serif", 9)
271 if style == PYGMENTS_PREPROCESSOR:
272 f.setItalic(True)
273 return f
274
275 if style in [PYGMENTS_STRING]:
276 if Utilities.isWindowsPlatform():
277 return QFont("Comic Sans MS", 10)
278 else:
279 return QFont("Bitstream Vera Serif", 10)
280
281 if style in [PYGMENTS_KEYWORD, PYGMENTS_OPERATOR, PYGMENTS_WORD, PYGMENTS_BUILTIN,
282 PYGMENTS_ATTRIBUTE, PYGMENTS_FUNCTION, PYGMENTS_CLASS,
283 PYGMENTS_NAMESPACE, PYGMENTS_EXCEPTION, PYGMENTS_ENTITY,
284 PYGMENTS_TAG, PYGMENTS_SCALAR, PYGMENTS_ESCAPE, PYGMENTS_HEADING,
285 PYGMENTS_SUBHEADING, PYGMENTS_STRONG, PYGMENTS_PROMPT]:
286 f = LexerContainer.defaultFont(self, style)
287 f.setBold(True)
288 return f
289
290 if style in [PYGMENTS_DOCSTRING, PYGMENTS_EMPHASIZE]:
291 f = LexerContainer.defaultFont(self, style)
292 f.setItalic(True)
293 return f
294
295 return LexerContainer.defaultFont(self, style)
296
297 def styleBitsNeeded(self):
298 """
299 Public method to get the number of style bits needed by the lexer.
300
301 @return number of style bits needed (integer)
302 """
303 return 6
304
305 def __guessLexer(self, text):
306 """
307 Private method to guess a pygments lexer.
308
309 @param text text to base guessing on (string)
310 @return reference to the guessed lexer (pygments.lexer)
311 """
312 lexer = None
313
314 if self.__pygmentsName:
315 lexerClass = find_lexer_class(self.__pygmentsName)
316 if lexerClass is not None:
317 lexer = lexerClass()
318
319 elif text:
320 # step 1: guess based on filename and text
321 if self.editor is not None:
322 fn = self.editor.getFileName()
323 try:
324 lexer = guess_lexer_for_filename(fn, text)
325 except ClassNotFound:
326 pass
327
328 # step 2: guess on text only
329 if lexer is None:
330 try:
331 lexer = guess_lexer(text)
332 except ClassNotFound:
333 pass
334
335 return lexer
336
337 def canStyle(self):
338 """
339 Public method to check, if the lexer is able to style the text.
340
341 @return flag indicating the lexer capability (boolean)
342 """
343 if self.editor is None:
344 return True
345
346 text = self.editor.text().encode('utf-8')
347 self.__lexer = self.__guessLexer(text)
348
349 return self.__lexer is not None
350
351 def name(self):
352 """
353 Public method to get the name of the pygments lexer.
354
355 @return name of the pygments lexer (string)
356 """
357 if self.__lexer is None:
358 return ""
359 else:
360 return self.__lexer.name
361
362 def styleText(self, start, end):
363 """
364 Public method to perform the styling.
365
366 @param start position of first character to be styled (integer)
367 @param end position of last character to be styled (integer)
368 """
369 text = self.editor.text()[:end + 1].encode('utf-8')
370 self.__lexer = self.__guessLexer(text)
371
372 cpos = 0
373 self.editor.startStyling(cpos, 0x3f)
374 if self.__lexer is None:
375 self.editor.setStyling(len(text), PYGMENTS_DEFAULT)
376 else:
377 eolLen = len(self.editor.getLineSeparator())
378 for token, txt in self.__lexer.get_tokens(text):
379 style = TOKEN_MAP.get(token, PYGMENTS_DEFAULT)
380
381 tlen = len(txt)
382 if eolLen > 1:
383 tlen += txt.count('\n')
384 if tlen:
385 self.editor.setStyling(tlen, style)
386 cpos += tlen
387 self.editor.startStyling(cpos, 0x3f)
388
389 def isCommentStyle(self, style):
390 """
391 Public method to check, if a style is a comment style.
392
393 @return flag indicating a comment style (boolean)
394 """
395 return style in [PYGMENTS_COMMENT]
396
397 def isStringStyle(self, style):
398 """
399 Public method to check, if a style is a string style.
400
401 @return flag indicating a string style (boolean)
402 """
403 return style in [PYGMENTS_STRING, PYGMENTS_DOCSTRING, PYGMENTS_OTHER,
404 PYGMENTS_HEADING, PYGMENTS_SUBHEADING, PYGMENTS_EMPHASIZE,
405 PYGMENTS_STRONG]

eric ide

mercurial