PrEditor 1.1.0__py3-none-any.whl → 1.3.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.
- preditor/__init__.py +4 -1
- preditor/about_module.py +6 -2
- preditor/dccs/.hab.json +10 -0
- preditor/dccs/maya/PrEditor_maya.mod +0 -1
- preditor/dccs/maya/README.md +22 -0
- preditor/dccs/maya/plug-ins/PrEditor_maya.py +32 -1
- 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 +7 -3
- preditor/excepthooks.py +1 -1
- preditor/gui/app.py +2 -2
- preditor/gui/codehighlighter.py +10 -24
- preditor/gui/completer.py +17 -6
- preditor/gui/console.py +94 -47
- preditor/gui/dialog.py +10 -7
- preditor/gui/drag_tab_bar.py +7 -7
- preditor/gui/errordialog.py +2 -2
- preditor/gui/find_files.py +7 -5
- preditor/gui/fuzzy_search/fuzzy_search.py +8 -4
- preditor/gui/group_tab_widget/__init__.py +32 -4
- preditor/gui/group_tab_widget/grouped_tab_models.py +4 -4
- preditor/gui/group_tab_widget/grouped_tab_widget.py +6 -4
- preditor/gui/level_buttons.py +16 -1
- preditor/gui/loggerwindow.py +48 -27
- preditor/gui/set_text_editor_path_dialog.py +3 -1
- preditor/gui/ui/loggerwindow.ui +11 -1
- preditor/gui/window.py +4 -4
- preditor/gui/workbox_mixin.py +43 -14
- preditor/gui/workbox_text_edit.py +7 -5
- preditor/gui/workboxwidget.py +25 -16
- preditor/logging_config.py +5 -2
- preditor/scintilla/__init__.py +19 -1
- preditor/scintilla/delayables/smart_highlight.py +7 -4
- preditor/scintilla/delayables/spell_check.py +5 -4
- preditor/scintilla/documenteditor.py +228 -135
- preditor/scintilla/finddialog.py +3 -3
- preditor/scintilla/lang/language.py +1 -1
- preditor/scintilla/lexers/cpplexer.py +3 -2
- preditor/scintilla/lexers/javascriptlexer.py +6 -4
- preditor/scintilla/lexers/maxscriptlexer.py +8 -7
- preditor/scintilla/lexers/mellexer.py +3 -2
- preditor/scintilla/lexers/mulexer.py +3 -2
- preditor/scintilla/lexers/pythonlexer.py +7 -6
- preditor/utils/cute.py +9 -8
- preditor/version.py +16 -3
- {preditor-1.1.0.dist-info → preditor-1.3.0.dist-info}/METADATA +69 -32
- {preditor-1.1.0.dist-info → preditor-1.3.0.dist-info}/RECORD +56 -47
- preditor-1.3.0.dist-info/top_level.txt +3 -0
- tests/find_files/test_find_files.py +74 -0
- tests/ide/test_delayable_engine.py +171 -0
- preditor-1.1.0.dist-info/top_level.txt +0 -1
- {preditor-1.1.0.dist-info → preditor-1.3.0.dist-info}/WHEEL +0 -0
- {preditor-1.1.0.dist-info → preditor-1.3.0.dist-info}/entry_points.txt +0 -0
- {preditor-1.1.0.dist-info → preditor-1.3.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from preditor.utils.text_search import RegexTextSearch, SimpleTextSearch
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def text_for_test(filename):
|
|
9
|
+
dirname = os.path.dirname(__file__)
|
|
10
|
+
filename = os.path.join(dirname, filename)
|
|
11
|
+
with open(filename) as fle:
|
|
12
|
+
return fle.read()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@pytest.mark.parametrize(
|
|
16
|
+
"check_type,search_text,is_cs,context,is_re",
|
|
17
|
+
(
|
|
18
|
+
# Simple text search testing context and case
|
|
19
|
+
("simple", "search term", False, 0, False),
|
|
20
|
+
("simple", "search term", False, 1, False),
|
|
21
|
+
("simple", "search term", False, 2, False),
|
|
22
|
+
("simple", "search term", False, 3, False),
|
|
23
|
+
("simple", "search term", True, 2, False),
|
|
24
|
+
# Regex search testing context and case
|
|
25
|
+
("re_simple", "search term", False, 0, True),
|
|
26
|
+
("re_simple", "search term", False, 2, True),
|
|
27
|
+
("re_simple", "search term", False, 3, True),
|
|
28
|
+
("re_simple", "search term", True, 2, True),
|
|
29
|
+
# Complex regex with a greedy search term
|
|
30
|
+
("re_greedy", "search.+term", False, 0, True),
|
|
31
|
+
("re_greedy", "search.+term", False, 2, True),
|
|
32
|
+
("re_greedy", "search.+term", True, 2, True),
|
|
33
|
+
("re_greedy_upper", "Search.+term", True, 2, True),
|
|
34
|
+
),
|
|
35
|
+
)
|
|
36
|
+
def test_find_files(capsys, check_type, search_text, is_cs, context, is_re):
|
|
37
|
+
workbox_id = "1,2"
|
|
38
|
+
path = 'First Group/First Tab'
|
|
39
|
+
text = text_for_test("tab_text.txt")
|
|
40
|
+
|
|
41
|
+
if is_re:
|
|
42
|
+
TextSearch = RegexTextSearch
|
|
43
|
+
else:
|
|
44
|
+
TextSearch = SimpleTextSearch
|
|
45
|
+
|
|
46
|
+
search = TextSearch(search_text, case_sensitive=is_cs, context=context)
|
|
47
|
+
# Add the title to the printed output so title is tested when checking
|
|
48
|
+
# `captured.out` later.
|
|
49
|
+
print(search.title())
|
|
50
|
+
|
|
51
|
+
# Generate the search text and print it to `captured.out` so we can check
|
|
52
|
+
search.search_text(text, path, workbox_id)
|
|
53
|
+
|
|
54
|
+
captured = capsys.readouterr()
|
|
55
|
+
check_filename = "{}_{}_{}_{}.md".format(check_type, is_cs, context, is_re)
|
|
56
|
+
check = text_for_test(check_filename)
|
|
57
|
+
|
|
58
|
+
# To update tests, print text and save over top of the md. Then verify
|
|
59
|
+
# that it is actually rendered properly. You will need to add one trailing
|
|
60
|
+
# space after dot lines, two spaces after blank lines, and ensue the end of
|
|
61
|
+
# file newline is present. The default print callbacks use markdown links,
|
|
62
|
+
# but don't really render valid markdown. If you want to render to html,
|
|
63
|
+
# use regular markdown not github flavored.
|
|
64
|
+
# print(check_filename)
|
|
65
|
+
# print(captured.out)
|
|
66
|
+
|
|
67
|
+
# print('*' * 50)
|
|
68
|
+
# for line in check.rstrip().splitlines(keepends=True):
|
|
69
|
+
# print([line])
|
|
70
|
+
# print('*' * 50)
|
|
71
|
+
# for line in captured.out.splitlines(keepends=True):
|
|
72
|
+
# print([line])
|
|
73
|
+
|
|
74
|
+
assert captured.out == check
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
from __future__ import absolute_import, print_function
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from Qt.QtWidgets import QApplication
|
|
7
|
+
|
|
8
|
+
from preditor.delayable_engine import DelayableEngine
|
|
9
|
+
from preditor.delayable_engine.delayables import Delayable, RangeDelayable
|
|
10
|
+
from preditor.scintilla.documenteditor import DocumentEditor
|
|
11
|
+
|
|
12
|
+
# TODO: Re-enable these tests once they work on the github runners
|
|
13
|
+
pytestmark = pytest.mark.skipif(
|
|
14
|
+
sys.platform != "win32", reason="Test fails on gitrunner"
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class RangeTestDelayable(RangeDelayable):
|
|
19
|
+
key = 'range_test'
|
|
20
|
+
|
|
21
|
+
def add_document(self, document):
|
|
22
|
+
try:
|
|
23
|
+
document.init_count += 1
|
|
24
|
+
except AttributeError:
|
|
25
|
+
document.init_count = 1
|
|
26
|
+
|
|
27
|
+
def loop(self, document, line_num, line_end, value):
|
|
28
|
+
return
|
|
29
|
+
|
|
30
|
+
def remove_document(self, document):
|
|
31
|
+
document.init_count -= 1
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ADelayable(Delayable):
|
|
35
|
+
key = 'a_delayable'
|
|
36
|
+
|
|
37
|
+
def add_document(self, document):
|
|
38
|
+
try:
|
|
39
|
+
document.init_count += 1
|
|
40
|
+
except AttributeError:
|
|
41
|
+
document.init_count = 1
|
|
42
|
+
# enqueue a command so we can verify that remove_delayable removes it
|
|
43
|
+
self.engine.enqueue(document, self.key)
|
|
44
|
+
|
|
45
|
+
def remove_document(self, document):
|
|
46
|
+
document.init_count -= 1
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@pytest.fixture()
|
|
50
|
+
def engine():
|
|
51
|
+
"""Creates a test DelayableEngine and DocumentEditor.
|
|
52
|
+
The DocumentEditor can be accessed by using `engine.test_doc`.
|
|
53
|
+
"""
|
|
54
|
+
global app
|
|
55
|
+
if not QApplication.instance():
|
|
56
|
+
# These test require an initialized QApplication create one and ensure
|
|
57
|
+
# it doesn't get garbage collected. Creating the app here prevents
|
|
58
|
+
# segfaults when skipping this file on linux. We need to figure out
|
|
59
|
+
# how to run these tests in github actions.
|
|
60
|
+
app = QApplication([])
|
|
61
|
+
|
|
62
|
+
engine = DelayableEngine('test_engine')
|
|
63
|
+
engine.test_doc = DocumentEditor(None)
|
|
64
|
+
engine.add_document(engine.test_doc)
|
|
65
|
+
|
|
66
|
+
return engine
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_add_delayable(engine):
|
|
70
|
+
assert len(engine.delayables) == 0
|
|
71
|
+
|
|
72
|
+
# Check that we can add a instance of Delayable
|
|
73
|
+
delayable = RangeTestDelayable(engine)
|
|
74
|
+
engine.add_delayable(delayable)
|
|
75
|
+
assert len(engine.delayables) == 1
|
|
76
|
+
|
|
77
|
+
# Adding a second delayable with the same key replaces the previous one
|
|
78
|
+
delayable1 = RangeTestDelayable(engine)
|
|
79
|
+
engine.add_delayable(delayable1)
|
|
80
|
+
assert len(engine.delayables) == 1
|
|
81
|
+
assert engine.delayables['range_test'] == delayable1
|
|
82
|
+
|
|
83
|
+
# Check that if a invalid delayable key is passed a exception is raised
|
|
84
|
+
with pytest.raises(KeyError):
|
|
85
|
+
engine.add_delayable('undefined_key')
|
|
86
|
+
assert len(engine.delayables) == 1
|
|
87
|
+
|
|
88
|
+
# We can add a new Delayable instance if a valid key is passed
|
|
89
|
+
engine.add_delayable('a_delayable')
|
|
90
|
+
assert len(engine.delayables) == 2
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@pytest.mark.parametrize(
|
|
94
|
+
'args1,args2,merged',
|
|
95
|
+
(
|
|
96
|
+
# Smallest start should be used
|
|
97
|
+
((10, 100, 'a'), (25, 75, 'b'), (10, 100, 'b')),
|
|
98
|
+
((50, 100, 'a'), (25, 75, 'b'), (25, 100, 'b')),
|
|
99
|
+
# Largest end should be used
|
|
100
|
+
((50, 60, 'a'), (25, 75, 'b'), (25, 75, 'b')),
|
|
101
|
+
((50, 100, 'a'), (60, 75, 'b'), (50, 100, 'b')),
|
|
102
|
+
# None should always be used for end if passed
|
|
103
|
+
((50, 60, 'a'), (25, None, 'b'), (25, None, 'b')),
|
|
104
|
+
((50, None, 'a'), (25, 75, 'b'), (25, None, 'b')),
|
|
105
|
+
((50, None, 'a'), (25, None, 'b'), (25, None, 'b')),
|
|
106
|
+
),
|
|
107
|
+
)
|
|
108
|
+
def test_merge_args(engine, args1, args2, merged):
|
|
109
|
+
delayable = RangeTestDelayable(engine)
|
|
110
|
+
engine.add_delayable(delayable)
|
|
111
|
+
|
|
112
|
+
engine.enqueue(engine.test_doc, 'range_test', *args1)
|
|
113
|
+
args = engine.test_doc.delayable_info['range_test']
|
|
114
|
+
# merge_args should not have been called yet, so args should be unchanged
|
|
115
|
+
assert args == args1
|
|
116
|
+
|
|
117
|
+
# Encueue the same key twice without looping will force a call of merge_args
|
|
118
|
+
engine.enqueue(engine.test_doc, 'range_test', *args2)
|
|
119
|
+
args = engine.test_doc.delayable_info['range_test']
|
|
120
|
+
assert args == merged
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def test_remove_documents(engine):
|
|
124
|
+
"""Check that a document can be removed and added correctly."""
|
|
125
|
+
delayable = RangeTestDelayable(engine)
|
|
126
|
+
engine.add_delayable(delayable)
|
|
127
|
+
|
|
128
|
+
# Check that add_document was called originally
|
|
129
|
+
assert len(engine.documents) == 1
|
|
130
|
+
assert engine.test_doc.init_count == 1
|
|
131
|
+
# Check that the delayable_engine was updated correctly
|
|
132
|
+
assert engine.test_doc.delayable_engine.name == 'test_engine'
|
|
133
|
+
|
|
134
|
+
# Check that remove_document was called
|
|
135
|
+
engine.remove_document(engine.test_doc)
|
|
136
|
+
assert len(engine.documents) == 0
|
|
137
|
+
assert engine.test_doc.init_count == 0
|
|
138
|
+
assert engine.test_doc.delayable_engine.name == 'default'
|
|
139
|
+
|
|
140
|
+
# Check that add_document is called again
|
|
141
|
+
engine.add_document(engine.test_doc)
|
|
142
|
+
assert len(engine.documents) == 1
|
|
143
|
+
assert engine.test_doc.init_count == 1
|
|
144
|
+
assert engine.test_doc.delayable_engine.name == 'test_engine'
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def test_remove_delayables(engine):
|
|
148
|
+
"""Check that a delayable can be added and removed correctly."""
|
|
149
|
+
delayable = ADelayable(engine)
|
|
150
|
+
engine.add_delayable(delayable)
|
|
151
|
+
|
|
152
|
+
# Check that add_document was called originally
|
|
153
|
+
assert len(engine.delayables) == 1
|
|
154
|
+
assert engine.test_doc.init_count == 1
|
|
155
|
+
assert 'a_delayable' in engine.test_doc.delayable_info
|
|
156
|
+
# Removing a delayable doesn't remove the delay engine
|
|
157
|
+
assert engine.test_doc.delayable_engine.name == 'test_engine'
|
|
158
|
+
|
|
159
|
+
# Check that remove_document was called
|
|
160
|
+
engine.remove_delayable(delayable)
|
|
161
|
+
assert len(engine.delayables) == 0
|
|
162
|
+
assert engine.test_doc.init_count == 0
|
|
163
|
+
assert 'a_delayable' not in engine.test_doc.delayable_info
|
|
164
|
+
assert engine.test_doc.delayable_engine.name == 'test_engine'
|
|
165
|
+
|
|
166
|
+
# Check that add_document is called again
|
|
167
|
+
engine.add_delayable(delayable)
|
|
168
|
+
assert len(engine.delayables) == 1
|
|
169
|
+
assert engine.test_doc.init_count == 1
|
|
170
|
+
assert 'a_delayable' in engine.test_doc.delayable_info
|
|
171
|
+
assert engine.test_doc.delayable_engine.name == 'test_engine'
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
preditor
|
|
File without changes
|
|
File without changes
|
|
File without changes
|