PrEditor 2.1.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.
- preditor/__init__.py +315 -0
- preditor/__main__.py +13 -0
- preditor/about_module.py +165 -0
- preditor/cli.py +192 -0
- preditor/config.py +318 -0
- preditor/constants.py +13 -0
- preditor/contexts.py +210 -0
- preditor/cores/__init__.py +0 -0
- preditor/cores/core.py +20 -0
- preditor/dccs/.hab.json +10 -0
- preditor/dccs/maya/PrEditor_maya.mod +1 -0
- preditor/dccs/maya/README.md +22 -0
- preditor/dccs/maya/plug-ins/PrEditor_maya.py +141 -0
- preditor/dccs/studiomax/PackageContents.xml +32 -0
- preditor/dccs/studiomax/PrEditor-PrEditor_Show.mcr +8 -0
- preditor/dccs/studiomax/README.md +17 -0
- preditor/dccs/studiomax/preditor.ms +16 -0
- preditor/dccs/studiomax/preditor_menu.mnx +7 -0
- preditor/debug.py +149 -0
- preditor/delayable_engine/__init__.py +302 -0
- preditor/delayable_engine/delayables.py +85 -0
- preditor/enum.py +728 -0
- preditor/excepthooks.py +165 -0
- preditor/gui/__init__.py +56 -0
- preditor/gui/app.py +163 -0
- preditor/gui/codehighlighter.py +289 -0
- preditor/gui/completer.py +237 -0
- preditor/gui/console.py +605 -0
- preditor/gui/console_base.py +911 -0
- preditor/gui/dialog.py +181 -0
- preditor/gui/drag_tab_bar.py +625 -0
- preditor/gui/editor_chooser.py +57 -0
- preditor/gui/errordialog.py +69 -0
- preditor/gui/find_files.py +137 -0
- preditor/gui/fuzzy_search/__init__.py +0 -0
- preditor/gui/fuzzy_search/fuzzy_search.py +97 -0
- preditor/gui/group_tab_widget/__init__.py +0 -0
- preditor/gui/group_tab_widget/group_tab_widget.py +528 -0
- preditor/gui/group_tab_widget/grouped_tab_menu.py +35 -0
- preditor/gui/group_tab_widget/grouped_tab_models.py +107 -0
- preditor/gui/group_tab_widget/grouped_tab_widget.py +223 -0
- preditor/gui/group_tab_widget/one_tab_widget.py +96 -0
- preditor/gui/level_buttons.py +358 -0
- preditor/gui/logger_window_handler.py +77 -0
- preditor/gui/logger_window_plugin.py +35 -0
- preditor/gui/loggerwindow.py +2405 -0
- preditor/gui/newtabwidget.py +69 -0
- preditor/gui/output_console.py +11 -0
- preditor/gui/qtdesigner/__init__.py +21 -0
- preditor/gui/qtdesigner/_log_plugin.py +29 -0
- preditor/gui/qtdesigner/console_base_plugin.py +48 -0
- preditor/gui/qtdesigner/console_predit_plugin.py +48 -0
- preditor/gui/set_text_editor_path_dialog.py +61 -0
- preditor/gui/status_label.py +99 -0
- preditor/gui/suggest_path_quotes_dialog.py +50 -0
- preditor/gui/ui/editor_chooser.ui +93 -0
- preditor/gui/ui/errordialog.ui +74 -0
- preditor/gui/ui/find_files.ui +140 -0
- preditor/gui/ui/loggerwindow.ui +1909 -0
- preditor/gui/ui/set_text_editor_path_dialog.ui +189 -0
- preditor/gui/ui/suggest_path_quotes_dialog.ui +225 -0
- preditor/gui/window.py +161 -0
- preditor/gui/workbox_mixin.py +1139 -0
- preditor/gui/workbox_text_edit.py +136 -0
- preditor/gui/workboxwidget.py +315 -0
- preditor/logging_config.py +55 -0
- preditor/osystem.py +401 -0
- preditor/plugins.py +118 -0
- preditor/prefs.py +381 -0
- preditor/resource/environment_variables.html +26 -0
- preditor/resource/error_mail.html +85 -0
- preditor/resource/error_mail_inline.html +41 -0
- preditor/resource/img/README.md +17 -0
- preditor/resource/img/arrow_forward.png +0 -0
- preditor/resource/img/check-bold.png +0 -0
- preditor/resource/img/chevron-down.png +0 -0
- preditor/resource/img/chevron-up.png +0 -0
- preditor/resource/img/close-thick.png +0 -0
- preditor/resource/img/comment-edit.png +0 -0
- preditor/resource/img/content-copy.png +0 -0
- preditor/resource/img/content-cut.png +0 -0
- preditor/resource/img/content-duplicate.png +0 -0
- preditor/resource/img/content-paste.png +0 -0
- preditor/resource/img/content-save.png +0 -0
- preditor/resource/img/debug_disabled.png +0 -0
- preditor/resource/img/eye-check.png +0 -0
- preditor/resource/img/file-plus.png +0 -0
- preditor/resource/img/file-remove.png +0 -0
- preditor/resource/img/format-align-left.png +0 -0
- preditor/resource/img/format-letter-case-lower.png +0 -0
- preditor/resource/img/format-letter-case-upper.png +0 -0
- preditor/resource/img/format-letter-case.svg +1 -0
- preditor/resource/img/information.png +0 -0
- preditor/resource/img/logging_critical.png +0 -0
- preditor/resource/img/logging_custom.png +0 -0
- preditor/resource/img/logging_debug.png +0 -0
- preditor/resource/img/logging_error.png +0 -0
- preditor/resource/img/logging_info.png +0 -0
- preditor/resource/img/logging_not_set.png +0 -0
- preditor/resource/img/logging_warning.png +0 -0
- preditor/resource/img/marker.png +0 -0
- preditor/resource/img/play.png +0 -0
- preditor/resource/img/playlist-play.png +0 -0
- preditor/resource/img/plus-minus-variant.png +0 -0
- preditor/resource/img/preditor.ico +0 -0
- preditor/resource/img/preditor.png +0 -0
- preditor/resource/img/preditor.psd +0 -0
- preditor/resource/img/preditor.svg +44 -0
- preditor/resource/img/regex.svg +1 -0
- preditor/resource/img/restart.svg +1 -0
- preditor/resource/img/skip-forward-outline.png +0 -0
- preditor/resource/img/skip-next-outline.png +0 -0
- preditor/resource/img/skip-next.png +0 -0
- preditor/resource/img/skip-previous.png +0 -0
- preditor/resource/img/subdirectory-arrow-right.png +0 -0
- preditor/resource/img/text-search-variant.png +0 -0
- preditor/resource/img/warning-big.png +0 -0
- preditor/resource/lang/python.json +30 -0
- preditor/resource/pref_updates/pref_updates.json +17 -0
- preditor/resource/settings.ini +25 -0
- preditor/resource/stylesheet/Bright.css +76 -0
- preditor/resource/stylesheet/Dark.css +210 -0
- preditor/scintilla/__init__.py +40 -0
- preditor/scintilla/delayables/__init__.py +11 -0
- preditor/scintilla/delayables/smart_highlight.py +97 -0
- preditor/scintilla/delayables/spell_check.py +174 -0
- preditor/scintilla/documenteditor.py +1924 -0
- preditor/scintilla/finddialog.py +68 -0
- preditor/scintilla/lang/__init__.py +80 -0
- preditor/scintilla/lang/config/bash.ini +15 -0
- preditor/scintilla/lang/config/batch.ini +14 -0
- preditor/scintilla/lang/config/cpp.ini +19 -0
- preditor/scintilla/lang/config/css.ini +19 -0
- preditor/scintilla/lang/config/eyeonscript.ini +17 -0
- preditor/scintilla/lang/config/html.ini +21 -0
- preditor/scintilla/lang/config/javascript.ini +24 -0
- preditor/scintilla/lang/config/lua.ini +16 -0
- preditor/scintilla/lang/config/maxscript.ini +20 -0
- preditor/scintilla/lang/config/mel.ini +18 -0
- preditor/scintilla/lang/config/mu.ini +22 -0
- preditor/scintilla/lang/config/nsi.ini +19 -0
- preditor/scintilla/lang/config/perl.ini +19 -0
- preditor/scintilla/lang/config/puppet.ini +19 -0
- preditor/scintilla/lang/config/python.ini +28 -0
- preditor/scintilla/lang/config/ruby.ini +19 -0
- preditor/scintilla/lang/config/sql.ini +7 -0
- preditor/scintilla/lang/config/xml.ini +21 -0
- preditor/scintilla/lang/config/yaml.ini +18 -0
- preditor/scintilla/lang/language.py +240 -0
- preditor/scintilla/lexers/__init__.py +0 -0
- preditor/scintilla/lexers/cpplexer.py +22 -0
- preditor/scintilla/lexers/javascriptlexer.py +27 -0
- preditor/scintilla/lexers/maxscriptlexer.py +235 -0
- preditor/scintilla/lexers/mellexer.py +369 -0
- preditor/scintilla/lexers/mulexer.py +33 -0
- preditor/scintilla/lexers/pythonlexer.py +42 -0
- preditor/scintilla/ui/finddialog.ui +160 -0
- preditor/settings.py +71 -0
- preditor/stream/__init__.py +72 -0
- preditor/stream/console_handler.py +169 -0
- preditor/stream/director.py +144 -0
- preditor/stream/manager.py +97 -0
- preditor/streamhandler_helper.py +46 -0
- preditor/utils/__init__.py +191 -0
- preditor/utils/call_stack.py +86 -0
- preditor/utils/cute.py +106 -0
- preditor/utils/stylesheets.py +54 -0
- preditor/utils/text_search.py +338 -0
- preditor/version.py +34 -0
- preditor/weakref.py +363 -0
- preditor-2.1.0.dist-info/METADATA +308 -0
- preditor-2.1.0.dist-info/RECORD +179 -0
- preditor-2.1.0.dist-info/WHEEL +5 -0
- preditor-2.1.0.dist-info/entry_points.txt +19 -0
- preditor-2.1.0.dist-info/licenses/LICENSE +165 -0
- preditor-2.1.0.dist-info/top_level.txt +3 -0
- tests/encodings/test_ecoding.py +33 -0
- tests/find_files/test_find_files.py +74 -0
- tests/ide/test_delayable_engine.py +171 -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 .. 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,22 @@
|
|
|
1
|
+
from __future__ import absolute_import
|
|
2
|
+
|
|
3
|
+
from Qt.QtGui import QColor
|
|
4
|
+
|
|
5
|
+
from .. import Qsci
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CppLexer(Qsci.QsciLexerCPP):
|
|
9
|
+
# Items in this list will be highlighted using the color for self.KeywordSet2
|
|
10
|
+
highlightedKeywords = ''
|
|
11
|
+
|
|
12
|
+
def defaultPaper(self, style):
|
|
13
|
+
if style == self.CommentLine:
|
|
14
|
+
# Set the highlight color for this lexer
|
|
15
|
+
return QColor(155, 255, 155)
|
|
16
|
+
return super(CppLexer, self).defaultPaper(style)
|
|
17
|
+
|
|
18
|
+
def keywords(self, style):
|
|
19
|
+
# Words to be highlighted
|
|
20
|
+
if style == self.CommentLine and self.highlightedKeywords:
|
|
21
|
+
return self.highlightedKeywords
|
|
22
|
+
return super(CppLexer, self).keywords(style)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from __future__ import absolute_import
|
|
2
|
+
|
|
3
|
+
from Qt.QtGui import QColor
|
|
4
|
+
|
|
5
|
+
from .. import Qsci
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class JavaScriptLexer(Qsci.QsciLexerJavaScript):
|
|
9
|
+
# Items in this list will be highlighted using the color for
|
|
10
|
+
# `Qsci.QsciLexerJavaScript.KeywordSet2`
|
|
11
|
+
highlightedKeywords = ''
|
|
12
|
+
|
|
13
|
+
def defaultFont(self, index):
|
|
14
|
+
# HACK: TODO: I should probably preserve the existing fonts
|
|
15
|
+
return self.font(0)
|
|
16
|
+
|
|
17
|
+
def defaultPaper(self, style):
|
|
18
|
+
if style == Qsci.QsciLexerJavaScript.KeywordSet2:
|
|
19
|
+
# Set the highlight color for this lexer
|
|
20
|
+
return QColor(155, 255, 155)
|
|
21
|
+
return super(JavaScriptLexer, self).defaultPaper(style)
|
|
22
|
+
|
|
23
|
+
def keywords(self, style):
|
|
24
|
+
# Words to be highlighted
|
|
25
|
+
if style == 2 and self.highlightedKeywords:
|
|
26
|
+
return self.highlightedKeywords
|
|
27
|
+
return super(JavaScriptLexer, self).keywords(style)
|
|
@@ -0,0 +1,235 @@
|
|
|
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
|
+
|
|
8
|
+
from .. import Qsci, QsciScintilla
|
|
9
|
+
|
|
10
|
+
MS_KEYWORDS = """
|
|
11
|
+
if then else not and or key collect
|
|
12
|
+
do while for in with where
|
|
13
|
+
function fn rollout struct parameters attributes exit continue
|
|
14
|
+
local global
|
|
15
|
+
true false
|
|
16
|
+
ok undefined unsupplied return
|
|
17
|
+
filein open close flush include print
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class MaxscriptLexer(Qsci.QsciLexerCustom):
|
|
22
|
+
# Items in this list will be highligheded using the color for self.SmartHighlight
|
|
23
|
+
highlightedKeywords = ''
|
|
24
|
+
|
|
25
|
+
def __init__(self, parent=None):
|
|
26
|
+
super(MaxscriptLexer, self).__init__(parent)
|
|
27
|
+
self._styles = {
|
|
28
|
+
0: 'Default',
|
|
29
|
+
1: 'Comment',
|
|
30
|
+
2: 'CommentLine',
|
|
31
|
+
3: 'Keyword',
|
|
32
|
+
4: 'Operator',
|
|
33
|
+
5: 'Number',
|
|
34
|
+
6: 'String',
|
|
35
|
+
7: 'SmartHighlight',
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
for key, value in iteritems(self._styles):
|
|
39
|
+
setattr(self, value, key)
|
|
40
|
+
|
|
41
|
+
def description(self, style):
|
|
42
|
+
return self._styles.get(style, '')
|
|
43
|
+
|
|
44
|
+
def defaultColor(self, style):
|
|
45
|
+
from Qt.QtCore import Qt
|
|
46
|
+
from Qt.QtGui import QColor
|
|
47
|
+
|
|
48
|
+
if style in (self.Comment, self.CommentLine):
|
|
49
|
+
return QColor(40, 160, 40)
|
|
50
|
+
|
|
51
|
+
elif style in (self.Keyword, self.Operator):
|
|
52
|
+
return QColor(Qt.GlobalColor.blue)
|
|
53
|
+
|
|
54
|
+
elif style == self.Number:
|
|
55
|
+
return QColor(Qt.GlobalColor.red)
|
|
56
|
+
|
|
57
|
+
elif style == self.String:
|
|
58
|
+
return QColor(180, 140, 30)
|
|
59
|
+
|
|
60
|
+
return super(MaxscriptLexer, self).defaultColor(style)
|
|
61
|
+
|
|
62
|
+
def defaultPaper(self, style):
|
|
63
|
+
if style == self.SmartHighlight:
|
|
64
|
+
from Qt.QtGui import QColor
|
|
65
|
+
|
|
66
|
+
# Set the highlight color for this lexer
|
|
67
|
+
return QColor(155, 255, 155)
|
|
68
|
+
return super(MaxscriptLexer, self).defaultPaper(style)
|
|
69
|
+
|
|
70
|
+
def font(self, style):
|
|
71
|
+
font = super(MaxscriptLexer, self).font(style)
|
|
72
|
+
if style in (self.Comment, self.CommentLine):
|
|
73
|
+
font.setFamily('Arial Bold')
|
|
74
|
+
return font
|
|
75
|
+
|
|
76
|
+
def keywords(self, style):
|
|
77
|
+
if style == self.Keyword:
|
|
78
|
+
return MS_KEYWORDS
|
|
79
|
+
if style == self.SmartHighlight:
|
|
80
|
+
return self.highlightedKeywords
|
|
81
|
+
return super(MaxscriptLexer, self).keywords(style)
|
|
82
|
+
|
|
83
|
+
def processChunk(self, chunk, lastState, keywords):
|
|
84
|
+
# process the length of the chunk
|
|
85
|
+
if isinstance(chunk, bytearray):
|
|
86
|
+
chunk = chunk.decode('utf8')
|
|
87
|
+
length = len(chunk)
|
|
88
|
+
|
|
89
|
+
# check to see if our last state was a block comment
|
|
90
|
+
if lastState == self.Comment:
|
|
91
|
+
pos = chunk.find('*/')
|
|
92
|
+
if pos != -1:
|
|
93
|
+
self.setStyling(pos + 2, self.Comment)
|
|
94
|
+
return self.processChunk(chunk[pos + 2 :], self.Default, keywords)
|
|
95
|
+
else:
|
|
96
|
+
self.setStyling(length, self.Comment)
|
|
97
|
+
return (self.Comment, 0)
|
|
98
|
+
|
|
99
|
+
# check to see if our last state was a string
|
|
100
|
+
elif lastState == self.String:
|
|
101
|
+
# remove special case backslashes
|
|
102
|
+
while r'\\' in chunk:
|
|
103
|
+
chunk = chunk.replace(r'\\', '||')
|
|
104
|
+
|
|
105
|
+
# remove special case strings
|
|
106
|
+
while r'\"' in chunk:
|
|
107
|
+
chunk = chunk.replace(r'\"', r"\'")
|
|
108
|
+
|
|
109
|
+
pos = chunk.find('"')
|
|
110
|
+
if pos != -1:
|
|
111
|
+
self.setStyling(pos + 1, self.String)
|
|
112
|
+
return self.processChunk(chunk[pos + 1 :], self.Default, keywords)
|
|
113
|
+
else:
|
|
114
|
+
self.setStyling(length, self.String)
|
|
115
|
+
return (self.String, 0)
|
|
116
|
+
|
|
117
|
+
# otherwise, process a default chunk
|
|
118
|
+
else:
|
|
119
|
+
blockpos = chunk.find('/*')
|
|
120
|
+
linepos = chunk.find('--')
|
|
121
|
+
strpos = chunk.find('"')
|
|
122
|
+
order = [blockpos, linepos, strpos]
|
|
123
|
+
order.sort()
|
|
124
|
+
|
|
125
|
+
# any of the above symbols will affect how a symbol following it is treated,
|
|
126
|
+
# so make sure we process in the proper order
|
|
127
|
+
for i in order:
|
|
128
|
+
if i == -1:
|
|
129
|
+
continue
|
|
130
|
+
|
|
131
|
+
# process a string
|
|
132
|
+
if i == strpos:
|
|
133
|
+
state, folding = self.processChunk(chunk[:i], lastState, keywords)
|
|
134
|
+
self.setStyling(1, self.String)
|
|
135
|
+
newstate, newfolding = self.processChunk(
|
|
136
|
+
chunk[i + 1 :], self.String, keywords
|
|
137
|
+
)
|
|
138
|
+
return (newstate, newfolding + folding)
|
|
139
|
+
|
|
140
|
+
# process a line comment
|
|
141
|
+
elif i == linepos:
|
|
142
|
+
state, folding = self.processChunk(chunk[:i], lastState, keywords)
|
|
143
|
+
self.setStyling(length - i, self.CommentLine)
|
|
144
|
+
return (self.Default, folding)
|
|
145
|
+
|
|
146
|
+
# process a block comment
|
|
147
|
+
elif i == blockpos:
|
|
148
|
+
state, folding = self.processChunk(chunk[:i], lastState, keywords)
|
|
149
|
+
self.setStyling(2, self.Comment)
|
|
150
|
+
newstate, newfolding = self.processChunk(
|
|
151
|
+
chunk[i + 2 :], self.Comment, keywords
|
|
152
|
+
)
|
|
153
|
+
return (newstate, newfolding + folding)
|
|
154
|
+
|
|
155
|
+
# otherwise, we are processing a default set of text whose syntaxing is
|
|
156
|
+
# irrelavent from the previous one TODO: this needs to handle QStrings.
|
|
157
|
+
# However I do not thing QStrings are the problem, its more likely a
|
|
158
|
+
# bytearray problem. the conversion at the start of this function may have
|
|
159
|
+
# resolved it.
|
|
160
|
+
results = self.chunkRegex.findall(chunk)
|
|
161
|
+
for space, kwd in results:
|
|
162
|
+
if not (space or kwd):
|
|
163
|
+
break
|
|
164
|
+
|
|
165
|
+
self.setStyling(len(space), self.Default)
|
|
166
|
+
|
|
167
|
+
if kwd.lower() in self.hlkwords:
|
|
168
|
+
self.setStyling(len(kwd), self.SmartHighlight)
|
|
169
|
+
elif kwd.lower() in keywords:
|
|
170
|
+
self.setStyling(len(kwd), self.Keyword)
|
|
171
|
+
else:
|
|
172
|
+
self.setStyling(len(kwd), self.Default)
|
|
173
|
+
|
|
174
|
+
# in this context, look for opening and closing parenthesis which will
|
|
175
|
+
# determine folding scope
|
|
176
|
+
return (self.Default, chunk.count('(') - chunk.count(')'))
|
|
177
|
+
|
|
178
|
+
def styleText(self, start, end):
|
|
179
|
+
editor = self.editor()
|
|
180
|
+
if not editor:
|
|
181
|
+
return
|
|
182
|
+
|
|
183
|
+
# scintilla works with encoded bytes, not decoded characters
|
|
184
|
+
# this matters if the source contains non-ascii characters and
|
|
185
|
+
# a multi-byte encoding is used (e.g. utf-8)
|
|
186
|
+
source = ''
|
|
187
|
+
if end > editor.length():
|
|
188
|
+
end = editor.length()
|
|
189
|
+
|
|
190
|
+
# define commonly used methods
|
|
191
|
+
SCI = editor.SendScintilla
|
|
192
|
+
SETFOLDLEVEL = QsciScintilla.SCI_SETFOLDLEVEL
|
|
193
|
+
HEADERFLAG = QsciScintilla.SC_FOLDLEVELHEADERFLAG
|
|
194
|
+
CURRFOLDLEVEL = QsciScintilla.SC_FOLDLEVELBASE
|
|
195
|
+
|
|
196
|
+
if end > start:
|
|
197
|
+
source = bytearray(end - start)
|
|
198
|
+
editor.SendScintilla(editor.SCI_GETTEXTRANGE, start, end, source)
|
|
199
|
+
|
|
200
|
+
if not source:
|
|
201
|
+
return
|
|
202
|
+
self.parent().blockSignals(True)
|
|
203
|
+
|
|
204
|
+
# the line index will also need to implement folding
|
|
205
|
+
index = editor.SendScintilla(editor.SCI_LINEFROMPOSITION, start)
|
|
206
|
+
if index > 0:
|
|
207
|
+
# the previous state may be needed for multi-line styling
|
|
208
|
+
pos = editor.SendScintilla(editor.SCI_GETLINEENDPOSITION, index - 1)
|
|
209
|
+
lastState = editor.SendScintilla(editor.SCI_GETSTYLEAT, pos)
|
|
210
|
+
else:
|
|
211
|
+
lastState = self.Default
|
|
212
|
+
|
|
213
|
+
self.startStyling(start, 0x1F)
|
|
214
|
+
|
|
215
|
+
# cache objects used by processChunk that do not need updated every time it is
|
|
216
|
+
# called
|
|
217
|
+
self.hlkwords = set(text(self.keywords(self.SmartHighlight)).lower().split())
|
|
218
|
+
self.chunkRegex = re.compile('([^A-Za-z0-9]*)([A-Za-z0-9]*)')
|
|
219
|
+
kwrds = set(MS_KEYWORDS.split())
|
|
220
|
+
|
|
221
|
+
# scintilla always asks to style whole lines
|
|
222
|
+
for line in source.splitlines(True):
|
|
223
|
+
lastState, folding = self.processChunk(line, lastState, kwrds)
|
|
224
|
+
|
|
225
|
+
# open folding levels
|
|
226
|
+
if folding > 0:
|
|
227
|
+
SCI(SETFOLDLEVEL, index, CURRFOLDLEVEL | HEADERFLAG)
|
|
228
|
+
CURRFOLDLEVEL += folding
|
|
229
|
+
else:
|
|
230
|
+
SCI(SETFOLDLEVEL, index, CURRFOLDLEVEL)
|
|
231
|
+
CURRFOLDLEVEL += folding
|
|
232
|
+
|
|
233
|
+
# folding implementation goes here
|
|
234
|
+
index += 1
|
|
235
|
+
self.parent().blockSignals(False)
|