PrEditor 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of PrEditor might be problematic. Click here for more details.

Files changed (158) hide show
  1. preditor/__init__.py +322 -0
  2. preditor/__main__.py +13 -0
  3. preditor/about_module.py +161 -0
  4. preditor/cli.py +192 -0
  5. preditor/config.py +302 -0
  6. preditor/contexts.py +119 -0
  7. preditor/cores/__init__.py +0 -0
  8. preditor/cores/core.py +20 -0
  9. preditor/dccs/maya/PrEditor_maya.mod +2 -0
  10. preditor/dccs/maya/plug-ins/PrEditor_maya.py +110 -0
  11. preditor/debug.py +144 -0
  12. preditor/delayable_engine/__init__.py +302 -0
  13. preditor/delayable_engine/delayables.py +85 -0
  14. preditor/enum.py +728 -0
  15. preditor/excepthooks.py +131 -0
  16. preditor/gui/__init__.py +93 -0
  17. preditor/gui/app.py +160 -0
  18. preditor/gui/codehighlighter.py +209 -0
  19. preditor/gui/completer.py +226 -0
  20. preditor/gui/console.py +867 -0
  21. preditor/gui/dialog.py +178 -0
  22. preditor/gui/drag_tab_bar.py +190 -0
  23. preditor/gui/editor_chooser.py +57 -0
  24. preditor/gui/errordialog.py +68 -0
  25. preditor/gui/find_files.py +125 -0
  26. preditor/gui/fuzzy_search/__init__.py +0 -0
  27. preditor/gui/fuzzy_search/fuzzy_search.py +93 -0
  28. preditor/gui/group_tab_widget/__init__.py +325 -0
  29. preditor/gui/group_tab_widget/grouped_tab_menu.py +35 -0
  30. preditor/gui/group_tab_widget/grouped_tab_models.py +108 -0
  31. preditor/gui/group_tab_widget/grouped_tab_widget.py +78 -0
  32. preditor/gui/group_tab_widget/one_tab_widget.py +54 -0
  33. preditor/gui/level_buttons.py +343 -0
  34. preditor/gui/logger_window_handler.py +48 -0
  35. preditor/gui/logger_window_plugin.py +32 -0
  36. preditor/gui/loggerwindow.py +1385 -0
  37. preditor/gui/newtabwidget.py +69 -0
  38. preditor/gui/set_text_editor_path_dialog.py +59 -0
  39. preditor/gui/status_label.py +99 -0
  40. preditor/gui/suggest_path_quotes_dialog.py +50 -0
  41. preditor/gui/ui/editor_chooser.ui +93 -0
  42. preditor/gui/ui/errordialog.ui +74 -0
  43. preditor/gui/ui/find_files.ui +140 -0
  44. preditor/gui/ui/loggerwindow.ui +1105 -0
  45. preditor/gui/ui/set_text_editor_path_dialog.ui +189 -0
  46. preditor/gui/ui/suggest_path_quotes_dialog.ui +225 -0
  47. preditor/gui/window.py +161 -0
  48. preditor/gui/workbox_mixin.py +389 -0
  49. preditor/gui/workbox_text_edit.py +137 -0
  50. preditor/gui/workboxwidget.py +298 -0
  51. preditor/logging_config.py +52 -0
  52. preditor/osystem.py +401 -0
  53. preditor/plugins.py +118 -0
  54. preditor/prefs.py +74 -0
  55. preditor/resource/environment_variables.html +26 -0
  56. preditor/resource/error_mail.html +85 -0
  57. preditor/resource/error_mail_inline.html +41 -0
  58. preditor/resource/img/README.md +17 -0
  59. preditor/resource/img/arrow_forward.png +0 -0
  60. preditor/resource/img/check-bold.png +0 -0
  61. preditor/resource/img/chevron-down.png +0 -0
  62. preditor/resource/img/chevron-up.png +0 -0
  63. preditor/resource/img/close-thick.png +0 -0
  64. preditor/resource/img/comment-edit.png +0 -0
  65. preditor/resource/img/content-copy.png +0 -0
  66. preditor/resource/img/content-cut.png +0 -0
  67. preditor/resource/img/content-duplicate.png +0 -0
  68. preditor/resource/img/content-paste.png +0 -0
  69. preditor/resource/img/content-save.png +0 -0
  70. preditor/resource/img/debug_disabled.png +0 -0
  71. preditor/resource/img/eye-check.png +0 -0
  72. preditor/resource/img/file-plus.png +0 -0
  73. preditor/resource/img/file-remove.png +0 -0
  74. preditor/resource/img/format-align-left.png +0 -0
  75. preditor/resource/img/format-letter-case-lower.png +0 -0
  76. preditor/resource/img/format-letter-case-upper.png +0 -0
  77. preditor/resource/img/format-letter-case.svg +1 -0
  78. preditor/resource/img/information.png +0 -0
  79. preditor/resource/img/logging_critical.png +0 -0
  80. preditor/resource/img/logging_custom.png +0 -0
  81. preditor/resource/img/logging_debug.png +0 -0
  82. preditor/resource/img/logging_error.png +0 -0
  83. preditor/resource/img/logging_info.png +0 -0
  84. preditor/resource/img/logging_not_set.png +0 -0
  85. preditor/resource/img/logging_warning.png +0 -0
  86. preditor/resource/img/marker.png +0 -0
  87. preditor/resource/img/play.png +0 -0
  88. preditor/resource/img/playlist-play.png +0 -0
  89. preditor/resource/img/plus-minus-variant.png +0 -0
  90. preditor/resource/img/preditor.ico +0 -0
  91. preditor/resource/img/preditor.png +0 -0
  92. preditor/resource/img/preditor.psd +0 -0
  93. preditor/resource/img/preditor.svg +44 -0
  94. preditor/resource/img/regex.svg +1 -0
  95. preditor/resource/img/restart.svg +1 -0
  96. preditor/resource/img/skip-forward-outline.png +0 -0
  97. preditor/resource/img/skip-next-outline.png +0 -0
  98. preditor/resource/img/skip-next.png +0 -0
  99. preditor/resource/img/skip-previous.png +0 -0
  100. preditor/resource/img/subdirectory-arrow-right.png +0 -0
  101. preditor/resource/img/text-search-variant.png +0 -0
  102. preditor/resource/img/warning-big.png +0 -0
  103. preditor/resource/lang/python.json +30 -0
  104. preditor/resource/settings.ini +25 -0
  105. preditor/resource/stylesheet/Bright.css +65 -0
  106. preditor/resource/stylesheet/Dark.css +199 -0
  107. preditor/scintilla/__init__.py +22 -0
  108. preditor/scintilla/delayables/__init__.py +11 -0
  109. preditor/scintilla/delayables/smart_highlight.py +94 -0
  110. preditor/scintilla/delayables/spell_check.py +173 -0
  111. preditor/scintilla/documenteditor.py +2038 -0
  112. preditor/scintilla/finddialog.py +68 -0
  113. preditor/scintilla/lang/__init__.py +80 -0
  114. preditor/scintilla/lang/config/bash.ini +15 -0
  115. preditor/scintilla/lang/config/batch.ini +14 -0
  116. preditor/scintilla/lang/config/cpp.ini +19 -0
  117. preditor/scintilla/lang/config/css.ini +19 -0
  118. preditor/scintilla/lang/config/eyeonscript.ini +17 -0
  119. preditor/scintilla/lang/config/html.ini +21 -0
  120. preditor/scintilla/lang/config/javascript.ini +24 -0
  121. preditor/scintilla/lang/config/lua.ini +16 -0
  122. preditor/scintilla/lang/config/maxscript.ini +20 -0
  123. preditor/scintilla/lang/config/mel.ini +18 -0
  124. preditor/scintilla/lang/config/mu.ini +22 -0
  125. preditor/scintilla/lang/config/nsi.ini +19 -0
  126. preditor/scintilla/lang/config/perl.ini +19 -0
  127. preditor/scintilla/lang/config/puppet.ini +19 -0
  128. preditor/scintilla/lang/config/python.ini +28 -0
  129. preditor/scintilla/lang/config/ruby.ini +19 -0
  130. preditor/scintilla/lang/config/sql.ini +7 -0
  131. preditor/scintilla/lang/config/xml.ini +21 -0
  132. preditor/scintilla/lang/config/yaml.ini +18 -0
  133. preditor/scintilla/lang/language.py +240 -0
  134. preditor/scintilla/lexers/__init__.py +0 -0
  135. preditor/scintilla/lexers/cpplexer.py +21 -0
  136. preditor/scintilla/lexers/javascriptlexer.py +25 -0
  137. preditor/scintilla/lexers/maxscriptlexer.py +234 -0
  138. preditor/scintilla/lexers/mellexer.py +368 -0
  139. preditor/scintilla/lexers/mulexer.py +32 -0
  140. preditor/scintilla/lexers/pythonlexer.py +41 -0
  141. preditor/scintilla/ui/finddialog.ui +160 -0
  142. preditor/settings.py +71 -0
  143. preditor/stream/__init__.py +80 -0
  144. preditor/stream/director.py +73 -0
  145. preditor/stream/manager.py +74 -0
  146. preditor/streamhandler_helper.py +46 -0
  147. preditor/utils/__init__.py +0 -0
  148. preditor/utils/cute.py +30 -0
  149. preditor/utils/stylesheets.py +54 -0
  150. preditor/utils/text_search.py +342 -0
  151. preditor/version.py +21 -0
  152. preditor/weakref.py +363 -0
  153. preditor-1.0.0.dist-info/METADATA +224 -0
  154. preditor-1.0.0.dist-info/RECORD +158 -0
  155. preditor-1.0.0.dist-info/WHEEL +5 -0
  156. preditor-1.0.0.dist-info/entry_points.txt +18 -0
  157. preditor-1.0.0.dist-info/licenses/LICENSE +165 -0
  158. preditor-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,240 @@
1
+ from __future__ import absolute_import, print_function
2
+
3
+ import re
4
+ import sys
5
+
6
+ try:
7
+ from configparser import ConfigParser
8
+ except ImportError:
9
+ from ConfigParser import ConfigParser
10
+
11
+ from PyQt5 import Qsci
12
+
13
+
14
+ class MethodDescriptor(object):
15
+ def __init__(self, dtype, expr):
16
+ self.dtype = dtype
17
+ self.exprText = expr
18
+ try:
19
+ self.expr = re.compile(expr, re.DOTALL | re.MULTILINE)
20
+ except Exception:
21
+ print('error generating expression', expr)
22
+ self.expr = None
23
+
24
+ def search(self, text, startpos=-1):
25
+ if not self.expr:
26
+ return None
27
+
28
+ if startpos != -1:
29
+ return self.expr.search(text, startpos)
30
+ else:
31
+ return self.expr.search(text)
32
+
33
+
34
+ class Language(object):
35
+ def __init__(self):
36
+ self._name = ''
37
+ self._fileTypes = []
38
+
39
+ # lexer class information
40
+ self._lexerClass = -1
41
+ self._lexerClassName = ''
42
+ self._lexerModule = ''
43
+ self._lexerColorTypes = {}
44
+ self._custom = False
45
+ self._sourcefile = ''
46
+
47
+ # comment information
48
+ self._lineComment = ''
49
+
50
+ # method descriptors
51
+ self._descriptors = []
52
+
53
+ def addDescriptor(self, type, expr):
54
+ self._descriptors.append(MethodDescriptor(type, expr))
55
+
56
+ def createLexer(self, parent=None):
57
+ # create an instance of the lexer
58
+ cls = self.lexerClass()
59
+ if not cls:
60
+ return None
61
+
62
+ output = cls(parent)
63
+ if output and parent:
64
+ output.setFont(parent.font())
65
+ return output
66
+
67
+ def descriptors(self):
68
+ return self._descriptors
69
+
70
+ def isCustom(self):
71
+ return self._custom
72
+
73
+ def name(self):
74
+ return self._name
75
+
76
+ def lexerColorTypes(self):
77
+ return self._lexerColorTypes
78
+
79
+ def lineComment(self):
80
+ return self._lineComment
81
+
82
+ def fileTypes(self):
83
+ return self._fileTypes
84
+
85
+ def lexerClass(self):
86
+ if self._lexerClass == -1:
87
+ # use a custom lexer module
88
+ if self._lexerModule:
89
+ # retrieve the lexer module
90
+ module = sys.modules.get(self._lexerModule)
91
+
92
+ # try to import the module
93
+ if not module:
94
+ try:
95
+ __import__(self._lexerModule)
96
+ module = sys.modules.get(self._lexerModule)
97
+ except Exception:
98
+ print(
99
+ (
100
+ '[preditor.scintilla.lexers.Language.createLexer() '
101
+ 'Error] Could not import %s module'
102
+ )
103
+ % self._lexerModule
104
+ )
105
+ self._lexerClass = None
106
+ return None
107
+
108
+ # otherwise, its in the Qsci module
109
+ else:
110
+ module = Qsci
111
+
112
+ # retrieve the lexer class
113
+ self._lexerClass = module.__dict__.get(self._lexerClassName)
114
+ if not self._lexerClass:
115
+ print(
116
+ (
117
+ '[preditor.scintilla.lexers.Language.createLexer() Error] '
118
+ 'No %s class in %s'
119
+ )
120
+ % (self._lexerClassName, module.__name__)
121
+ )
122
+
123
+ return self._lexerClass
124
+
125
+ def lexerClassName(self):
126
+ return self._lexerClassName
127
+
128
+ def lexerModule(self):
129
+ return self._lexerModule
130
+
131
+ def save(self, filename=''):
132
+ if not filename:
133
+ filename = self.filename()
134
+ if not filename:
135
+ return False
136
+
137
+ parser = ConfigParser()
138
+ parser.add_section('GLOBALS')
139
+ parser.set('GLOBALS', 'name', self.name())
140
+ parser.set('GLOBALS', 'filetypes', ';'.join(self.fileTypes()))
141
+ # quotes are to preserve spaces which configparser strips out
142
+ parser.set('GLOBALS', 'linecomment', '"{}"'.format(self.lineComment()))
143
+
144
+ parser.add_section('LEXER')
145
+ parser.set('LEXER', 'class', self.lexerClassName())
146
+ parser.set('LEXER', 'module', self.lexerModule())
147
+
148
+ parser.add_section('DESCRIPTORS')
149
+ for i, desc in enumerate(self._descriptors):
150
+ parser.set('DESCRIPTORS', '%s%i' % (desc.dtype, i), desc.exprText)
151
+
152
+ parser.add_section('COLOR_TYPES')
153
+ for key, value in self.lexerColorTypes().items():
154
+ parser.set('COLOR_TYPES', key, ','.join([str(val) for val in value]))
155
+
156
+ # save the language
157
+ f = open(filename, 'w')
158
+ parser.write(f)
159
+ f.close()
160
+
161
+ self._sourcefile = filename
162
+ return True
163
+
164
+ def setCustom(self, state):
165
+ self._custom = state
166
+
167
+ def setFileTypes(self, fileTypes):
168
+ self._fileTypes = fileTypes
169
+
170
+ def setLexerClassName(self, className):
171
+ self._lexerClassName = className
172
+
173
+ def setLexerModule(self, module):
174
+ self._lexerModule = module
175
+
176
+ def setLineComment(self, lineComment):
177
+ self._lineComment = lineComment
178
+
179
+ def setLexerColorTypes(self, lexerColorTypes):
180
+ self._lexerColorTypes = lexerColorTypes
181
+
182
+ def setName(self, name):
183
+ self._name = name
184
+
185
+ def sourcefile(self):
186
+ return self._sourcefile
187
+
188
+ @staticmethod
189
+ def fromConfig(filename):
190
+ parser = ConfigParser()
191
+
192
+ if not parser.read(filename):
193
+ return False
194
+
195
+ plugin = Language()
196
+ plugin._name = parser.get('GLOBALS', 'name')
197
+ plugin._fileTypes = parser.get('GLOBALS', 'filetypes').split(';')
198
+
199
+ # try to load the line comment information
200
+ try:
201
+ plugin._lineComment = parser.get('GLOBALS', 'linecomment').strip('"')
202
+ except Exception:
203
+ pass
204
+
205
+ # try to load the lexer information
206
+ try:
207
+ plugin._lexerClassName = parser.get('LEXER', 'class')
208
+ plugin._lexerModule = parser.get('LEXER', 'module')
209
+ except Exception:
210
+ pass
211
+
212
+ # load the different descriptor options
213
+ try:
214
+ options = parser.options('DESCRIPTORS')
215
+ except Exception:
216
+ options = []
217
+
218
+ for option in options:
219
+ expr = parser.get('DESCRIPTORS', option)
220
+ option = re.match(r'([^\d]*)\d*', option).groups()[0]
221
+ plugin._descriptors.append(MethodDescriptor(option, expr))
222
+
223
+ # load the different color map options
224
+ try:
225
+ options = parser.options('COLOR_TYPES')
226
+ except Exception:
227
+ options = []
228
+
229
+ for option in options:
230
+ vals = []
231
+ for val in parser.get('COLOR_TYPES', option).split(','):
232
+ try:
233
+ vals.append(int(val))
234
+ except Exception:
235
+ pass
236
+ plugin._lexerColorTypes[option] = vals
237
+
238
+ plugin._sourcefile = filename
239
+
240
+ return plugin
File without changes
@@ -0,0 +1,21 @@
1
+ from __future__ import absolute_import
2
+
3
+ from PyQt5.Qsci import QsciLexerCPP
4
+ from Qt.QtGui import QColor
5
+
6
+
7
+ class CppLexer(QsciLexerCPP):
8
+ # Items in this list will be highlighted using the color for self.KeywordSet2
9
+ highlightedKeywords = ''
10
+
11
+ def defaultPaper(self, style):
12
+ if style == self.CommentLine:
13
+ # Set the highlight color for this lexer
14
+ return QColor(155, 255, 155)
15
+ return super(CppLexer, self).defaultPaper(style)
16
+
17
+ def keywords(self, style):
18
+ # Words to be highlighted
19
+ if style == self.CommentLine and self.highlightedKeywords:
20
+ return self.highlightedKeywords
21
+ return super(CppLexer, self).keywords(style)
@@ -0,0 +1,25 @@
1
+ from __future__ import absolute_import
2
+
3
+ from PyQt5.Qsci import QsciLexerJavaScript
4
+ from Qt.QtGui import QColor
5
+
6
+
7
+ class JavaScriptLexer(QsciLexerJavaScript):
8
+ # Items in this list will be highlighted using the color for self.KeywordSet2
9
+ highlightedKeywords = ''
10
+
11
+ def defaultFont(self, index):
12
+ # HACK: TODO: I should probably preserve the existing fonts
13
+ return self.font(0)
14
+
15
+ def defaultPaper(self, style):
16
+ if style == self.KeywordSet2:
17
+ # Set the highlight color for this lexer
18
+ return QColor(155, 255, 155)
19
+ return super(JavaScriptLexer, self).defaultPaper(style)
20
+
21
+ def keywords(self, style):
22
+ # Words to be highlighted
23
+ if style == 2 and self.highlightedKeywords:
24
+ return self.highlightedKeywords
25
+ return super(JavaScriptLexer, self).keywords(style)
@@ -0,0 +1,234 @@
1
+ from __future__ import absolute_import
2
+
3
+ import re
4
+ from builtins import str as text
5
+
6
+ from future.utils import iteritems
7
+ from PyQt5.Qsci import QsciLexerCustom, QsciScintilla
8
+
9
+ MS_KEYWORDS = """
10
+ if then else not and or key collect
11
+ do while for in with where
12
+ function fn rollout struct parameters attributes exit continue
13
+ local global
14
+ true false
15
+ ok undefined unsupplied return
16
+ filein open close flush include print
17
+ """
18
+
19
+
20
+ class MaxscriptLexer(QsciLexerCustom):
21
+ # Items in this list will be highligheded using the color for self.SmartHighlight
22
+ highlightedKeywords = ''
23
+
24
+ def __init__(self, parent=None):
25
+ QsciLexerCustom.__init__(self, parent)
26
+ self._styles = {
27
+ 0: 'Default',
28
+ 1: 'Comment',
29
+ 2: 'CommentLine',
30
+ 3: 'Keyword',
31
+ 4: 'Operator',
32
+ 5: 'Number',
33
+ 6: 'String',
34
+ 7: 'SmartHighlight',
35
+ }
36
+
37
+ for key, value in iteritems(self._styles):
38
+ setattr(self, value, key)
39
+
40
+ def description(self, style):
41
+ return self._styles.get(style, '')
42
+
43
+ def defaultColor(self, style):
44
+ from Qt.QtCore import Qt
45
+ from Qt.QtGui import QColor
46
+
47
+ if style in (self.Comment, self.CommentLine):
48
+ return QColor(40, 160, 40)
49
+
50
+ elif style in (self.Keyword, self.Operator):
51
+ return QColor(Qt.blue)
52
+
53
+ elif style == self.Number:
54
+ return QColor(Qt.red)
55
+
56
+ elif style == self.String:
57
+ return QColor(180, 140, 30)
58
+
59
+ return QsciLexerCustom.defaultColor(self, style)
60
+
61
+ def defaultPaper(self, style):
62
+ if style == self.SmartHighlight:
63
+ from Qt.QtGui import QColor
64
+
65
+ # Set the highlight color for this lexer
66
+ return QColor(155, 255, 155)
67
+ return super(MaxscriptLexer, self).defaultPaper(style)
68
+
69
+ def font(self, style):
70
+ font = super(MaxscriptLexer, self).font(style)
71
+ if style in (self.Comment, self.CommentLine):
72
+ font.setFamily('Arial Bold')
73
+ return font
74
+
75
+ def keywords(self, style):
76
+ if style == self.Keyword:
77
+ return MS_KEYWORDS
78
+ if style == self.SmartHighlight:
79
+ return self.highlightedKeywords
80
+ return QsciLexerCustom.keywords(self, style)
81
+
82
+ def processChunk(self, chunk, lastState, keywords):
83
+ # process the length of the chunk
84
+ if isinstance(chunk, bytearray):
85
+ chunk = chunk.decode('utf8')
86
+ length = len(chunk)
87
+
88
+ # check to see if our last state was a block comment
89
+ if lastState == self.Comment:
90
+ pos = chunk.find('*/')
91
+ if pos != -1:
92
+ self.setStyling(pos + 2, self.Comment)
93
+ return self.processChunk(chunk[pos + 2 :], self.Default, keywords)
94
+ else:
95
+ self.setStyling(length, self.Comment)
96
+ return (self.Comment, 0)
97
+
98
+ # check to see if our last state was a string
99
+ elif lastState == self.String:
100
+ # remove special case backslashes
101
+ while r'\\' in chunk:
102
+ chunk = chunk.replace(r'\\', '||')
103
+
104
+ # remove special case strings
105
+ while r'\"' in chunk:
106
+ chunk = chunk.replace(r'\"', r"\'")
107
+
108
+ pos = chunk.find('"')
109
+ if pos != -1:
110
+ self.setStyling(pos + 1, self.String)
111
+ return self.processChunk(chunk[pos + 1 :], self.Default, keywords)
112
+ else:
113
+ self.setStyling(length, self.String)
114
+ return (self.String, 0)
115
+
116
+ # otherwise, process a default chunk
117
+ else:
118
+ blockpos = chunk.find('/*')
119
+ linepos = chunk.find('--')
120
+ strpos = chunk.find('"')
121
+ order = [blockpos, linepos, strpos]
122
+ order.sort()
123
+
124
+ # any of the above symbols will affect how a symbol following it is treated,
125
+ # so make sure we process in the proper order
126
+ for i in order:
127
+ if i == -1:
128
+ continue
129
+
130
+ # process a string
131
+ if i == strpos:
132
+ state, folding = self.processChunk(chunk[:i], lastState, keywords)
133
+ self.setStyling(1, self.String)
134
+ newstate, newfolding = self.processChunk(
135
+ chunk[i + 1 :], self.String, keywords
136
+ )
137
+ return (newstate, newfolding + folding)
138
+
139
+ # process a line comment
140
+ elif i == linepos:
141
+ state, folding = self.processChunk(chunk[:i], lastState, keywords)
142
+ self.setStyling(length - i, self.CommentLine)
143
+ return (self.Default, folding)
144
+
145
+ # process a block comment
146
+ elif i == blockpos:
147
+ state, folding = self.processChunk(chunk[:i], lastState, keywords)
148
+ self.setStyling(2, self.Comment)
149
+ newstate, newfolding = self.processChunk(
150
+ chunk[i + 2 :], self.Comment, keywords
151
+ )
152
+ return (newstate, newfolding + folding)
153
+
154
+ # otherwise, we are processing a default set of text whose syntaxing is
155
+ # irrelavent from the previous one TODO: this needs to handle QStrings.
156
+ # However I do not thing QStrings are the problem, its more likely a
157
+ # bytearray problem. the conversion at the start of this function may have
158
+ # resolved it.
159
+ results = self.chunkRegex.findall(chunk)
160
+ for space, kwd in results:
161
+ if not (space or kwd):
162
+ break
163
+
164
+ self.setStyling(len(space), self.Default)
165
+
166
+ if kwd.lower() in self.hlkwords:
167
+ self.setStyling(len(kwd), self.SmartHighlight)
168
+ elif kwd.lower() in keywords:
169
+ self.setStyling(len(kwd), self.Keyword)
170
+ else:
171
+ self.setStyling(len(kwd), self.Default)
172
+
173
+ # in this context, look for opening and closing parenthesis which will
174
+ # determine folding scope
175
+ return (self.Default, chunk.count('(') - chunk.count(')'))
176
+
177
+ def styleText(self, start, end):
178
+ editor = self.editor()
179
+ if not editor:
180
+ return
181
+
182
+ # scintilla works with encoded bytes, not decoded characters
183
+ # this matters if the source contains non-ascii characters and
184
+ # a multi-byte encoding is used (e.g. utf-8)
185
+ source = ''
186
+ if end > editor.length():
187
+ end = editor.length()
188
+
189
+ # define commonly used methods
190
+ SCI = editor.SendScintilla
191
+ SETFOLDLEVEL = QsciScintilla.SCI_SETFOLDLEVEL
192
+ HEADERFLAG = QsciScintilla.SC_FOLDLEVELHEADERFLAG
193
+ CURRFOLDLEVEL = QsciScintilla.SC_FOLDLEVELBASE
194
+
195
+ if end > start:
196
+ source = bytearray(end - start)
197
+ editor.SendScintilla(editor.SCI_GETTEXTRANGE, start, end, source)
198
+
199
+ if not source:
200
+ return
201
+ self.parent().blockSignals(True)
202
+
203
+ # the line index will also need to implement folding
204
+ index = editor.SendScintilla(editor.SCI_LINEFROMPOSITION, start)
205
+ if index > 0:
206
+ # the previous state may be needed for multi-line styling
207
+ pos = editor.SendScintilla(editor.SCI_GETLINEENDPOSITION, index - 1)
208
+ lastState = editor.SendScintilla(editor.SCI_GETSTYLEAT, pos)
209
+ else:
210
+ lastState = self.Default
211
+
212
+ self.startStyling(start, 0x1F)
213
+
214
+ # cache objects used by processChunk that do not need updated every time it is
215
+ # called
216
+ self.hlkwords = set(text(self.keywords(self.SmartHighlight)).lower().split())
217
+ self.chunkRegex = re.compile('([^A-Za-z0-9]*)([A-Za-z0-9]*)')
218
+ kwrds = set(MS_KEYWORDS.split())
219
+
220
+ # scintilla always asks to style whole lines
221
+ for line in source.splitlines(True):
222
+ lastState, folding = self.processChunk(line, lastState, kwrds)
223
+
224
+ # open folding levels
225
+ if folding > 0:
226
+ SCI(SETFOLDLEVEL, index, CURRFOLDLEVEL | HEADERFLAG)
227
+ CURRFOLDLEVEL += folding
228
+ else:
229
+ SCI(SETFOLDLEVEL, index, CURRFOLDLEVEL)
230
+ CURRFOLDLEVEL += folding
231
+
232
+ # folding implementation goes here
233
+ index += 1
234
+ self.parent().blockSignals(False)