PrEditor 0.0.0.dev1__py2.py3-none-any.whl → 0.1.0__py2.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-0.1.0.dist-info/LICENSE +165 -0
- PrEditor-0.1.0.dist-info/METADATA +212 -0
- PrEditor-0.1.0.dist-info/RECORD +149 -0
- {PrEditor-0.0.0.dev1.dist-info → PrEditor-0.1.0.dist-info}/WHEEL +1 -1
- PrEditor-0.1.0.dist-info/entry_points.txt +18 -0
- PrEditor-0.1.0.dist-info/top_level.txt +1 -0
- preditor/__init__.py +301 -0
- {blurdev → preditor}/__main__.py +13 -13
- preditor/about_module.py +166 -0
- preditor/cli.py +192 -0
- {blurdev → preditor}/contexts.py +119 -119
- preditor/cores/core.py +65 -0
- preditor/dccs/maya/PrEditor_maya.mod +2 -0
- preditor/dccs/maya/plug-ins/PrEditor_maya.py +108 -0
- preditor/debug.py +294 -0
- blurdev/scintilla/delayable_engine.py → preditor/delayable_engine/__init__.py +310 -299
- blurdev/scintilla/delayables/base.py → preditor/delayable_engine/delayables.py +85 -85
- {blurdev → preditor}/enum.py +728 -1003
- {blurdev → preditor}/gui/__init__.py +84 -125
- preditor/gui/app.py +159 -0
- {blurdev → preditor}/gui/codehighlighter.py +209 -219
- {blurdev → preditor}/gui/completer.py +226 -236
- {blurdev → preditor}/gui/console.py +801 -858
- {blurdev → preditor}/gui/dialog.py +200 -216
- preditor/gui/drag_tab_bar.py +190 -0
- preditor/gui/editor_chooser.py +57 -0
- {blurdev → preditor}/gui/errordialog.py +100 -97
- preditor/gui/fuzzy_search/fuzzy_search.py +93 -0
- preditor/gui/group_tab_widget/__init__.py +319 -0
- preditor/gui/group_tab_widget/grouped_tab_menu.py +35 -0
- preditor/gui/group_tab_widget/grouped_tab_models.py +108 -0
- preditor/gui/group_tab_widget/grouped_tab_widget.py +75 -0
- preditor/gui/group_tab_widget/one_tab_widget.py +54 -0
- preditor/gui/level_buttons.py +349 -0
- {blurdev → preditor}/gui/logger_window_handler.py +46 -45
- {blurdev → preditor}/gui/loggerwindow.py +1205 -1417
- {blurdev → preditor}/gui/newtabwidget.py +69 -68
- {blurdev → preditor}/gui/redmine_login_dialog.py +63 -61
- {blurdev → preditor}/gui/set_text_editor_path_dialog.py +59 -57
- preditor/gui/ui/editor_chooser.ui +93 -0
- {blurdev → preditor}/gui/ui/errordialog.ui +81 -81
- {blurdev → preditor}/gui/ui/loggerwindow.ui +1030 -864
- {blurdev → preditor}/gui/ui/redmine_login_dialog.ui +124 -124
- {blurdev → preditor}/gui/ui/set_text_editor_path_dialog.ui +149 -149
- {blurdev → preditor}/gui/window.py +183 -199
- preditor/gui/workbox_mixin.py +357 -0
- preditor/gui/workbox_text_edit.py +117 -0
- preditor/gui/workboxwidget.py +276 -0
- preditor/logging_config.py +52 -0
- preditor/osystem.py +401 -0
- preditor/plugins.py +65 -0
- preditor/prefs.py +74 -0
- {blurdev → preditor}/resource/environment_variables.html +26 -38
- {blurdev → preditor}/resource/error_mail.html +85 -85
- {blurdev → preditor}/resource/error_mail_inline.html +41 -41
- preditor/resource/img/README.md +7 -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/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/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
- {blurdev → preditor}/resource/lang/python.json +30 -30
- preditor/resource/settings.ini +25 -0
- {blurdev/resource/stylesheet/logger → preditor/resource/stylesheet}/Bright.css +56 -61
- {blurdev → preditor}/resource/stylesheet/Dark.css +190 -132
- {blurdev → preditor}/scintilla/__init__.py +22 -28
- preditor/scintilla/delayables/__init__.py +11 -0
- {blurdev → preditor}/scintilla/delayables/smart_highlight.py +94 -93
- {blurdev → preditor}/scintilla/delayables/spell_check.py +173 -172
- {blurdev → preditor}/scintilla/documenteditor.py +2039 -2115
- {blurdev → preditor}/scintilla/finddialog.py +68 -81
- {blurdev → preditor}/scintilla/lang/__init__.py +80 -93
- {blurdev → preditor}/scintilla/lang/config/bash.ini +15 -15
- {blurdev → preditor}/scintilla/lang/config/batch.ini +14 -14
- {blurdev → preditor}/scintilla/lang/config/cpp.ini +19 -19
- {blurdev → preditor}/scintilla/lang/config/css.ini +19 -19
- {blurdev → preditor}/scintilla/lang/config/eyeonscript.ini +17 -17
- {blurdev → preditor}/scintilla/lang/config/html.ini +21 -21
- {blurdev → preditor}/scintilla/lang/config/javascript.ini +24 -24
- {blurdev → preditor}/scintilla/lang/config/lua.ini +16 -16
- {blurdev → preditor}/scintilla/lang/config/maxscript.ini +20 -20
- {blurdev → preditor}/scintilla/lang/config/mel.ini +18 -18
- {blurdev → preditor}/scintilla/lang/config/mu.ini +22 -22
- {blurdev → preditor}/scintilla/lang/config/nsi.ini +5 -5
- {blurdev → preditor}/scintilla/lang/config/perl.ini +19 -19
- {blurdev → preditor}/scintilla/lang/config/puppet.ini +19 -19
- {blurdev → preditor}/scintilla/lang/config/python.ini +28 -28
- {blurdev → preditor}/scintilla/lang/config/ruby.ini +19 -19
- {blurdev → preditor}/scintilla/lang/config/sql.ini +7 -7
- {blurdev → preditor}/scintilla/lang/config/xml.ini +21 -21
- {blurdev → preditor}/scintilla/lang/config/yaml.ini +18 -18
- {blurdev → preditor}/scintilla/lang/language.py +240 -250
- preditor/scintilla/lexers/__init__.py +0 -0
- {blurdev → preditor}/scintilla/lexers/cpplexer.py +21 -30
- {blurdev → preditor}/scintilla/lexers/javascriptlexer.py +25 -34
- {blurdev → preditor}/scintilla/lexers/maxscriptlexer.py +234 -253
- {blurdev → preditor}/scintilla/lexers/mellexer.py +368 -376
- {blurdev → preditor}/scintilla/lexers/mulexer.py +32 -41
- {blurdev → preditor}/scintilla/lexers/pythonlexer.py +41 -50
- {blurdev → preditor}/scintilla/ui/finddialog.ui +160 -160
- preditor/settings.py +71 -0
- preditor/stream/__init__.py +80 -0
- preditor/stream/director.py +56 -0
- preditor/stream/manager.py +74 -0
- preditor/streamhandler_helper.py +46 -0
- preditor/utils/__init__.py +0 -0
- preditor/utils/cute.py +30 -0
- preditor/utils/stylesheets.py +54 -0
- {blurdev → preditor}/version.py +5 -5
- preditor/weakref.py +363 -0
- PrEditor-0.0.0.dev1.dist-info/METADATA +0 -51
- PrEditor-0.0.0.dev1.dist-info/RECORD +0 -279
- PrEditor-0.0.0.dev1.dist-info/top_level.txt +0 -1
- blurdev/__init__.py +0 -356
- blurdev/cores/__init__.py +0 -98
- blurdev/cores/application.py +0 -26
- blurdev/cores/core.py +0 -634
- blurdev/debug.py +0 -593
- blurdev/external.py +0 -391
- blurdev/gui/level_buttons.py +0 -585
- blurdev/gui/workboxwidget.py +0 -205
- blurdev/logger.py +0 -238
- blurdev/osystem.py +0 -813
- blurdev/prefs.py +0 -33
- blurdev/protocols/__init__.py +0 -71
- blurdev/protocols/write_std_output_handler.py +0 -83
- blurdev/resource/designer_plugins.xml +0 -9
- blurdev/resource/error_email_old.html +0 -41
- blurdev/resource/img/add.png +0 -0
- blurdev/resource/img/ajax-loader.gif +0 -0
- blurdev/resource/img/application.png +0 -0
- blurdev/resource/img/applications.png +0 -0
- blurdev/resource/img/assburner.png +0 -0
- blurdev/resource/img/assfreezer.png +0 -0
- blurdev/resource/img/bar.gif +0 -0
- blurdev/resource/img/blank.png +0 -0
- blurdev/resource/img/blurdev.png +0 -0
- blurdev/resource/img/calendar_disabled.png +0 -0
- blurdev/resource/img/calendar_enabled.png +0 -0
- blurdev/resource/img/cancel.png +0 -0
- blurdev/resource/img/custom.png +0 -0
- blurdev/resource/img/debug_high.png +0 -0
- blurdev/resource/img/debug_low.png +0 -0
- blurdev/resource/img/debug_mid.png +0 -0
- blurdev/resource/img/debug_off.png +0 -0
- blurdev/resource/img/django.png +0 -0
- blurdev/resource/img/doc.png +0 -0
- blurdev/resource/img/edit.png +0 -0
- blurdev/resource/img/elemental.png +0 -0
- blurdev/resource/img/explore.png +0 -0
- blurdev/resource/img/favorite.png +0 -0
- blurdev/resource/img/file.png +0 -0
- blurdev/resource/img/folder.png +0 -0
- blurdev/resource/img/ide/add.png +0 -0
- blurdev/resource/img/ide/add_note.png +0 -0
- blurdev/resource/img/ide/arrow_down.png +0 -0
- blurdev/resource/img/ide/arrow_up.png +0 -0
- blurdev/resource/img/ide/check.png +0 -0
- blurdev/resource/img/ide/class.png +0 -0
- blurdev/resource/img/ide/clean.png +0 -0
- blurdev/resource/img/ide/clearlog.png +0 -0
- blurdev/resource/img/ide/close.png +0 -0
- blurdev/resource/img/ide/comment_add.png +0 -0
- blurdev/resource/img/ide/comment_remove.png +0 -0
- blurdev/resource/img/ide/comment_toggle.png +0 -0
- blurdev/resource/img/ide/console.png +0 -0
- blurdev/resource/img/ide/copy.png +0 -0
- blurdev/resource/img/ide/copylstrip.png +0 -0
- blurdev/resource/img/ide/cut.png +0 -0
- blurdev/resource/img/ide/edit.png +0 -0
- blurdev/resource/img/ide/find.png +0 -0
- blurdev/resource/img/ide/find_replace.png +0 -0
- blurdev/resource/img/ide/findnext.png +0 -0
- blurdev/resource/img/ide/findprev.png +0 -0
- blurdev/resource/img/ide/folder_find.png +0 -0
- blurdev/resource/img/ide/function.png +0 -0
- blurdev/resource/img/ide/git-bash.png +0 -0
- blurdev/resource/img/ide/git-gui.png +0 -0
- blurdev/resource/img/ide/gitk.png +0 -0
- blurdev/resource/img/ide/goto.png +0 -0
- blurdev/resource/img/ide/goto_def.png +0 -0
- blurdev/resource/img/ide/help.png +0 -0
- blurdev/resource/img/ide/highlighter.png +0 -0
- blurdev/resource/img/ide/lowercase.png +0 -0
- blurdev/resource/img/ide/newfile.png +0 -0
- blurdev/resource/img/ide/newfolder.png +0 -0
- blurdev/resource/img/ide/newproject.png +0 -0
- blurdev/resource/img/ide/newwizard.png +0 -0
- blurdev/resource/img/ide/open.png +0 -0
- blurdev/resource/img/ide/paste.png +0 -0
- blurdev/resource/img/ide/pdb_continue.png +0 -0
- blurdev/resource/img/ide/pdb_down.png +0 -0
- blurdev/resource/img/ide/pdb_next.png +0 -0
- blurdev/resource/img/ide/pdb_step.png +0 -0
- blurdev/resource/img/ide/pdb_up.png +0 -0
- blurdev/resource/img/ide/plus_minus.png +0 -0
- blurdev/resource/img/ide/preferences.png +0 -0
- blurdev/resource/img/ide/project_find.png +0 -0
- blurdev/resource/img/ide/python.png +0 -0
- blurdev/resource/img/ide/pyular.png +0 -0
- blurdev/resource/img/ide/qt.png +0 -0
- blurdev/resource/img/ide/quit.png +0 -0
- blurdev/resource/img/ide/redo.png +0 -0
- blurdev/resource/img/ide/refresh.png +0 -0
- blurdev/resource/img/ide/remove.png +0 -0
- blurdev/resource/img/ide/ruler.png +0 -0
- blurdev/resource/img/ide/run.png +0 -0
- blurdev/resource/img/ide/runall.png +0 -0
- blurdev/resource/img/ide/runallclear.png +0 -0
- blurdev/resource/img/ide/runselected.png +0 -0
- blurdev/resource/img/ide/runselectedclear.png +0 -0
- blurdev/resource/img/ide/save.png +0 -0
- blurdev/resource/img/ide/saveas.png +0 -0
- blurdev/resource/img/ide/sdk.png +0 -0
- blurdev/resource/img/ide/separator.png +0 -0
- blurdev/resource/img/ide/tabbed.png +0 -0
- blurdev/resource/img/ide/tile.png +0 -0
- blurdev/resource/img/ide/toolbar.png +0 -0
- blurdev/resource/img/ide/undo.png +0 -0
- blurdev/resource/img/ide/uppercase.png +0 -0
- blurdev/resource/img/ide/view_as.png +0 -0
- blurdev/resource/img/ide/windowed.png +0 -0
- blurdev/resource/img/ide.ico +0 -0
- blurdev/resource/img/ide.png +0 -0
- blurdev/resource/img/ide48.png +0 -0
- blurdev/resource/img/info.png +0 -0
- blurdev/resource/img/legacy tool.png +0 -0
- blurdev/resource/img/library.png +0 -0
- blurdev/resource/img/logger/about.png +0 -0
- blurdev/resource/img/logger/arrow_forward.png +0 -0
- blurdev/resource/img/logger/clear.png +0 -0
- blurdev/resource/img/logger/close.png +0 -0
- blurdev/resource/img/logger/debug_disabled.png +0 -0
- blurdev/resource/img/logger/debug_high.png +0 -0
- blurdev/resource/img/logger/debug_low.png +0 -0
- blurdev/resource/img/logger/debug_mid.png +0 -0
- blurdev/resource/img/logger/down.png +0 -0
- blurdev/resource/img/logger/find.png +0 -0
- blurdev/resource/img/logger/logging_critical.png +0 -0
- blurdev/resource/img/logger/logging_debug.png +0 -0
- blurdev/resource/img/logger/logging_error.png +0 -0
- blurdev/resource/img/logger/logging_info.png +0 -0
- blurdev/resource/img/logger/logging_not_set.png +0 -0
- blurdev/resource/img/logger/logging_warning.png +0 -0
- blurdev/resource/img/logger/next.png +0 -0
- blurdev/resource/img/logger/play.png +0 -0
- blurdev/resource/img/logger/playlist_play.png +0 -0
- blurdev/resource/img/logger/previous.png +0 -0
- blurdev/resource/img/logger/return.png +0 -0
- blurdev/resource/img/logger/save.png +0 -0
- blurdev/resource/img/logger/subdirectory_arrow_right.png +0 -0
- blurdev/resource/img/logger/up.png +0 -0
- blurdev/resource/img/lovebar.png +0 -0
- blurdev/resource/img/new.png +0 -0
- blurdev/resource/img/new_selected.png +0 -0
- blurdev/resource/img/node.png +0 -0
- blurdev/resource/img/ok.png +0 -0
- blurdev/resource/img/options.png +0 -0
- blurdev/resource/img/packages.png +0 -0
- blurdev/resource/img/preview/add.png +0 -0
- blurdev/resource/img/preview/brush.png +0 -0
- blurdev/resource/img/preview/delete.png +0 -0
- blurdev/resource/img/preview/delte.png +0 -0
- blurdev/resource/img/preview/fill.png +0 -0
- blurdev/resource/img/preview/layers.png +0 -0
- blurdev/resource/img/preview/media.png +0 -0
- blurdev/resource/img/preview/navigate.png +0 -0
- blurdev/resource/img/preview/pencil.png +0 -0
- blurdev/resource/img/preview/select.png +0 -0
- blurdev/resource/img/preview/type.png +0 -0
- blurdev/resource/img/preview/visible.png +0 -0
- blurdev/resource/img/project.png +0 -0
- blurdev/resource/img/python_logger.ico +0 -0
- blurdev/resource/img/python_logger.png +0 -0
- blurdev/resource/img/refresh.png +0 -0
- blurdev/resource/img/remove.png +0 -0
- blurdev/resource/img/reset.png +0 -0
- blurdev/resource/img/richtext/font_bold.png +0 -0
- blurdev/resource/img/richtext/font_italic.png +0 -0
- blurdev/resource/img/richtext/link_image.png +0 -0
- blurdev/resource/img/richtext/spell_check.png +0 -0
- blurdev/resource/img/richtext/unordered_list.png +0 -0
- blurdev/resource/img/save.png +0 -0
- blurdev/resource/img/savesettings.png +0 -0
- blurdev/resource/img/settings.png +0 -0
- blurdev/resource/img/tool.png +0 -0
- blurdev/resource/img/toolbarHandleHorizontal.png +0 -0
- blurdev/resource/img/toolbarHandleVertical.png +0 -0
- blurdev/resource/img/trash.png +0 -0
- blurdev/resource/img/trax.png +0 -0
- blurdev/resource/img/tree.png +0 -0
- blurdev/resource/img/treegrunt.ico +0 -0
- blurdev/resource/img/treegrunt.png +0 -0
- blurdev/resource/img/treegruntedit.png +0 -0
- blurdev/resource/img/user interface.png +0 -0
- blurdev/resource/img/warning.png +0 -0
- blurdev/resource/img/watermark.png +0 -0
- blurdev/resource/sdk/blurdev.sdk +0 -3
- blurdev/resource/settings.ini +0 -82
- blurdev/resource/softimage/BlurApplication.dll +0 -0
- blurdev/resource/softimage/BlurApplication64.dll +0 -0
- blurdev/resource/stylesheet/Carbon.css +0 -35
- blurdev/resource/stylesheet/logger/Dark.css +0 -62
- blurdev/resource/templ/py_comment.templ +0 -1
- blurdev/resource/templ/py_debug_raise_error.templ +0 -7
- blurdev/resource/templ/py_doc_string.templ +0 -10
- blurdev/resource/templ/py_header.templ +0 -9
- blurdev/resource/templ/py_line_comment.templ +0 -1
- blurdev/resource/templ/py_log_to_file.templ +0 -22
- blurdev/resource/templ/py_module_path.templ +0 -1
- blurdev/resource/templ/py_pyqt_core.templ +0 -1
- blurdev/resource/templ/py_pyqt_gui.templ +0 -1
- blurdev/resource/templ/py_splashscreen.templ +0 -6
- blurdev/resource/templ/py_testing_note.templ +0 -1
- blurdev/resource/templ/py_testing_note_end.templ +0 -1
- blurdev/resource/tools_environments.json +0 -72
- blurdev/resource/tools_environments.xml +0 -11
- blurdev/resource/tools_environments_linux.xml +0 -11
- blurdev/resource/tools_environments_offline.xml +0 -7
- blurdev/runtimes/__init__.py +0 -2
- blurdev/runtimes/logger.py +0 -44
- blurdev/scintilla/delayables/__init__.py +0 -9
- blurdev/settings.py +0 -312
- blurdev/utils/error.py +0 -389
- {blurdev/scintilla/lexers → preditor/cores}/__init__.py +0 -0
- {blurdev/utils → preditor/gui/fuzzy_search}/__init__.py +0 -0
- {blurdev → preditor}/resource/img/warning-big.png +0 -0
|
@@ -1,2115 +1,2039 @@
|
|
|
1
|
-
##
|
|
2
|
-
#
|
|
3
|
-
# \remarks This dialog allows the user to create new python classes and packages
|
|
4
|
-
# based on plugin templates
|
|
5
|
-
#
|
|
6
|
-
# \author beta@blur.com
|
|
7
|
-
# \author Blur Studio
|
|
8
|
-
# \date 08/19/10
|
|
9
|
-
#
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
import
|
|
13
|
-
import os
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
from
|
|
19
|
-
from
|
|
20
|
-
from
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
from
|
|
37
|
-
import
|
|
38
|
-
import
|
|
39
|
-
import
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
self.
|
|
82
|
-
self.
|
|
83
|
-
|
|
84
|
-
self.
|
|
85
|
-
self.
|
|
86
|
-
self.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
self.
|
|
90
|
-
self.
|
|
91
|
-
self.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
self.
|
|
95
|
-
self.
|
|
96
|
-
self.
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
self.
|
|
100
|
-
#
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
self.
|
|
104
|
-
|
|
105
|
-
self.
|
|
106
|
-
self.
|
|
107
|
-
self.
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
self.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
self.
|
|
114
|
-
self.
|
|
115
|
-
self.
|
|
116
|
-
self.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
#
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
#
|
|
130
|
-
self.
|
|
131
|
-
self.
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
self.
|
|
138
|
-
self.
|
|
139
|
-
self.
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
self.
|
|
147
|
-
self.
|
|
148
|
-
self.
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
#
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
self.
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
#
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
)
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
self.
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
self.
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
super(DocumentEditor, self).
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
)
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
def
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
def
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
return
|
|
597
|
-
|
|
598
|
-
def
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
self.
|
|
749
|
-
|
|
750
|
-
#
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
find_state.
|
|
804
|
-
|
|
805
|
-
find_state.
|
|
806
|
-
|
|
807
|
-
return
|
|
808
|
-
|
|
809
|
-
def
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
if
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
)
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
)
|
|
853
|
-
|
|
854
|
-
def
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
self.
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
self.
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
self.
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
"""
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
return self.
|
|
945
|
-
|
|
946
|
-
def
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
self.
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
)
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
)
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
return
|
|
1058
|
-
|
|
1059
|
-
def
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
self.
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
#
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
if
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
#
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
else:
|
|
1226
|
-
lexer
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
self.
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
):
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
self.
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
self.
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
self.
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
self
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
act
|
|
1478
|
-
act.
|
|
1479
|
-
act
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
act.
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
act
|
|
1491
|
-
act.
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
act
|
|
1497
|
-
act.
|
|
1498
|
-
act.
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
act.
|
|
1503
|
-
act.
|
|
1504
|
-
act.
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
#
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
act
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
#
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
self.
|
|
1630
|
-
self.
|
|
1631
|
-
self.
|
|
1632
|
-
self.
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
#
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
self.
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
if
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
)
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
return
|
|
1933
|
-
|
|
1934
|
-
def
|
|
1935
|
-
|
|
1936
|
-
super(DocumentEditor, self).
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
def
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
def
|
|
1946
|
-
self.
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
)
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
)
|
|
2036
|
-
|
|
2037
|
-
QColor,
|
|
2038
|
-
)
|
|
2039
|
-
|
|
2040
|
-
QColor, matchedBraceForegroundColor, setMatchedBraceForegroundColor
|
|
2041
|
-
)
|
|
2042
|
-
pyCaretBackgroundColor = Property(
|
|
2043
|
-
QColor, caretBackgroundColor, setCaretLineBackgroundColor
|
|
2044
|
-
)
|
|
2045
|
-
pyCaretForegroundColor = Property(
|
|
2046
|
-
QColor, caretForegroundColor, setCaretForegroundColor
|
|
2047
|
-
)
|
|
2048
|
-
pySelectionBackgroundColor = Property(
|
|
2049
|
-
QColor, selectionBackgroundColor, setSelectionBackgroundColor
|
|
2050
|
-
)
|
|
2051
|
-
pySelectionForegroundColor = Property(
|
|
2052
|
-
QColor, selectionForegroundColor, setSelectionForegroundColor
|
|
2053
|
-
)
|
|
2054
|
-
pyIndentationGuidesBackgroundColor = Property(
|
|
2055
|
-
QColor, indentationGuidesBackgroundColor, setIndentationGuidesBackgroundColor
|
|
2056
|
-
)
|
|
2057
|
-
pyIndentationGuidesForegroundColor = Property(
|
|
2058
|
-
QColor, indentationGuidesForegroundColor, setIndentationGuidesForegroundColor
|
|
2059
|
-
)
|
|
2060
|
-
pyMarkerBackgroundColor = Property(
|
|
2061
|
-
QColor, markerBackgroundColor, setMarkerBackgroundColor
|
|
2062
|
-
)
|
|
2063
|
-
pyMarkerForegroundColor = Property(
|
|
2064
|
-
QColor, markerForegroundColor, setMarkerForegroundColor
|
|
2065
|
-
)
|
|
2066
|
-
pyUnmatchedBraceBackgroundColor = Property(
|
|
2067
|
-
QColor, unmatchedBraceBackgroundColor, setUnmatchedBraceBackgroundColor
|
|
2068
|
-
)
|
|
2069
|
-
pyUnmatchedBraceForegroundColor = Property(
|
|
2070
|
-
QColor, unmatchedBraceForegroundColor, setUnmatchedBraceForegroundColor
|
|
2071
|
-
)
|
|
2072
|
-
pyEdgeColor = Property(QColor, edgeColor, setEdgeColor)
|
|
2073
|
-
documentFont = QtPropertyInit('_documentFont', _defaultFont)
|
|
2074
|
-
pyMarginsFont = Property(QFont, marginsFont, setMarginsFont)
|
|
2075
|
-
|
|
2076
|
-
copyIndentsAsSpaces = QtPropertyInit('_copyIndentsAsSpaces', False)
|
|
2077
|
-
|
|
2078
|
-
# These colors are purely defined in DocumentEditor so we can use QtPropertyInit
|
|
2079
|
-
braceBadForeground = QtPropertyInit('_braceBadForeground', QColor(255, 255, 255))
|
|
2080
|
-
braceBadBackground = QtPropertyInit('_braceBadBackground', QColor(100, 60, 60))
|
|
2081
|
-
|
|
2082
|
-
colorDefault = QtPropertyInit('_colorDefault', QColor())
|
|
2083
|
-
colorComment = QtPropertyInit('_colorComment', QColor(0, 127, 0))
|
|
2084
|
-
colorNumber = QtPropertyInit('_colorNumber', QColor(0, 127, 127))
|
|
2085
|
-
colorString = QtPropertyInit('_colorString', QColor(127, 0, 127))
|
|
2086
|
-
colorKeyword = QtPropertyInit('_colorKeyword', QColor(0, 0, 127))
|
|
2087
|
-
colorTripleQuotedString = QtPropertyInit(
|
|
2088
|
-
'_colorTripleQuotedString', QColor(127, 0, 0)
|
|
2089
|
-
)
|
|
2090
|
-
colorMethod = QtPropertyInit('_colorMethod', QColor(0, 0, 255))
|
|
2091
|
-
colorFunction = QtPropertyInit('_colorFunction', QColor(0, 127, 127))
|
|
2092
|
-
colorOperator = QtPropertyInit('_colorOperator', QColor(0, 0, 0))
|
|
2093
|
-
colorIdentifier = QtPropertyInit('_colorIdentifier', QColor(0, 0, 0))
|
|
2094
|
-
colorCommentBlock = QtPropertyInit('_colorCommentBlock', QColor(127, 127, 127))
|
|
2095
|
-
colorUnclosedString = QtPropertyInit('_colorUnclosedString', QColor(0, 0, 0))
|
|
2096
|
-
colorSmartHighlight = QtPropertyInit('_colorSmartHighlight', QColor(64, 112, 144))
|
|
2097
|
-
colorDecorator = QtPropertyInit('_colorDecorator', QColor(128, 80, 0))
|
|
2098
|
-
|
|
2099
|
-
_defaultPaper = QColor(255, 255, 255)
|
|
2100
|
-
paperDefault = QtPropertyInit('_paperDefault', _defaultPaper)
|
|
2101
|
-
paperComment = QtPropertyInit('_paperComment', _defaultPaper)
|
|
2102
|
-
paperNumber = QtPropertyInit('_paperNumber', _defaultPaper)
|
|
2103
|
-
paperString = QtPropertyInit('_paperString', _defaultPaper)
|
|
2104
|
-
paperKeyword = QtPropertyInit('_paperKeyword', _defaultPaper)
|
|
2105
|
-
paperTripleQuotedString = QtPropertyInit('_paperTripleQuotedString', _defaultPaper)
|
|
2106
|
-
paperMethod = QtPropertyInit('_paperMethod', _defaultPaper)
|
|
2107
|
-
paperFunction = QtPropertyInit('_paperFunction', _defaultPaper)
|
|
2108
|
-
paperOperator = QtPropertyInit('_paperOperator', _defaultPaper)
|
|
2109
|
-
paperIdentifier = QtPropertyInit('_paperIdentifier', _defaultPaper)
|
|
2110
|
-
paperCommentBlock = QtPropertyInit('_paperCommentBlock', _defaultPaper)
|
|
2111
|
-
paperUnclosedString = QtPropertyInit('_paperUnclosedString', QColor(224, 192, 224))
|
|
2112
|
-
paperSmartHighlight = QtPropertyInit(
|
|
2113
|
-
'_paperSmartHighlight', QColor(155, 255, 155, 75)
|
|
2114
|
-
)
|
|
2115
|
-
paperDecorator = QtPropertyInit('_paperDecorator', _defaultPaper)
|
|
1
|
+
##
|
|
2
|
+
#
|
|
3
|
+
# \remarks This dialog allows the user to create new python classes and packages
|
|
4
|
+
# based on plugin templates
|
|
5
|
+
#
|
|
6
|
+
# \author beta@blur.com
|
|
7
|
+
# \author Blur Studio
|
|
8
|
+
# \date 08/19/10
|
|
9
|
+
#
|
|
10
|
+
from __future__ import absolute_import
|
|
11
|
+
|
|
12
|
+
import logging
|
|
13
|
+
import os.path
|
|
14
|
+
import re
|
|
15
|
+
import string
|
|
16
|
+
import sys
|
|
17
|
+
import time
|
|
18
|
+
from collections import OrderedDict
|
|
19
|
+
from contextlib import contextmanager
|
|
20
|
+
from functools import partial
|
|
21
|
+
|
|
22
|
+
import six
|
|
23
|
+
from PyQt5.Qsci import QsciScintilla
|
|
24
|
+
from Qt import QtCompat
|
|
25
|
+
from Qt.QtCore import Property, QFile, QPoint, Qt, QTextCodec, Signal
|
|
26
|
+
from Qt.QtGui import QColor, QFont, QFontMetrics, QIcon
|
|
27
|
+
from Qt.QtWidgets import (
|
|
28
|
+
QAction,
|
|
29
|
+
QApplication,
|
|
30
|
+
QInputDialog,
|
|
31
|
+
QMenu,
|
|
32
|
+
QMessageBox,
|
|
33
|
+
QShortcut,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
from .. import osystem, resourcePath
|
|
37
|
+
from ..delayable_engine import DelayableEngine
|
|
38
|
+
from ..enum import Enum, EnumGroup
|
|
39
|
+
from ..gui import QtPropertyInit
|
|
40
|
+
from . import lang
|
|
41
|
+
|
|
42
|
+
logger = logging.getLogger(__name__)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class SearchDirection(EnumGroup):
|
|
46
|
+
First = Enum()
|
|
47
|
+
Forward = Enum()
|
|
48
|
+
Backward = Enum()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class SearchOptions(EnumGroup):
|
|
52
|
+
Backward = Enum()
|
|
53
|
+
CaseSensitive = Enum()
|
|
54
|
+
WholeWords = Enum()
|
|
55
|
+
QRegExp = Enum()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@contextmanager
|
|
59
|
+
def undo_step(editor):
|
|
60
|
+
"""Context manager that combines all changes performed inside it as a
|
|
61
|
+
single undo action for the document editor."""
|
|
62
|
+
editor.beginUndoAction()
|
|
63
|
+
try:
|
|
64
|
+
yield
|
|
65
|
+
finally:
|
|
66
|
+
editor.endUndoAction()
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class DocumentEditor(QsciScintilla):
|
|
70
|
+
_defaultFont = QFont()
|
|
71
|
+
_defaultFont.fromString('Courier New,9,-1,5,50,0,0,0,1,0')
|
|
72
|
+
|
|
73
|
+
fontsChanged = Signal(
|
|
74
|
+
QFont, QFont
|
|
75
|
+
) # emits the font size change (font size, margin font size)
|
|
76
|
+
documentSaved = Signal(
|
|
77
|
+
QsciScintilla, object
|
|
78
|
+
) # (DocumentEditor, filename) emitted when ever the document is saved.
|
|
79
|
+
|
|
80
|
+
def __init__(self, parent, filename='', lineno=0, delayable_engine='default'):
|
|
81
|
+
super(DocumentEditor, self).__init__(parent)
|
|
82
|
+
self.setObjectName('DocumentEditor')
|
|
83
|
+
# Spell check variables
|
|
84
|
+
self.__speller__ = None
|
|
85
|
+
self.pos = None
|
|
86
|
+
self.anchor = None
|
|
87
|
+
|
|
88
|
+
# create custom properties
|
|
89
|
+
self._filename = ''
|
|
90
|
+
self.additionalFilenames = []
|
|
91
|
+
self._language = ''
|
|
92
|
+
self._lastSearch = ''
|
|
93
|
+
self._textCodec = None
|
|
94
|
+
self._fileMonitoringActive = False
|
|
95
|
+
self._marginsFont = self._defaultFont
|
|
96
|
+
self._lastSearchDirection = SearchDirection.First
|
|
97
|
+
self._saveTimer = 0.0
|
|
98
|
+
self._autoReloadOnChange = False
|
|
99
|
+
self._enableFontResizing = True
|
|
100
|
+
# QSci doesnt provide accessors to these values, so store them internally
|
|
101
|
+
self._foldMarginBackgroundColor = QColor(224, 224, 224)
|
|
102
|
+
self._foldMarginForegroundColor = QColor(Qt.white)
|
|
103
|
+
self._marginsBackgroundColor = QColor(224, 224, 224)
|
|
104
|
+
self._marginsForegroundColor = QColor()
|
|
105
|
+
self._matchedBraceBackgroundColor = QColor(224, 224, 224)
|
|
106
|
+
self._matchedBraceForegroundColor = QColor()
|
|
107
|
+
self._unmatchedBraceBackgroundColor = QColor(Qt.white)
|
|
108
|
+
self._unmatchedBraceForegroundColor = QColor(Qt.blue)
|
|
109
|
+
self._caretForegroundColor = QColor()
|
|
110
|
+
self._caretBackgroundColor = QColor(255, 255, 255, 255)
|
|
111
|
+
self._selectionBackgroundColor = QColor(192, 192, 192)
|
|
112
|
+
self._selectionForegroundColor = QColor(Qt.black)
|
|
113
|
+
self._indentationGuidesBackgroundColor = QColor(Qt.white)
|
|
114
|
+
self._indentationGuidesForegroundColor = QColor(Qt.black)
|
|
115
|
+
self._markerBackgroundColor = QColor(Qt.white)
|
|
116
|
+
self._markerForegroundColor = QColor(Qt.black)
|
|
117
|
+
|
|
118
|
+
# Setup the DelayableEngine and add the document to it
|
|
119
|
+
self.delayable_info = OrderedDict()
|
|
120
|
+
self.delayable_engine = DelayableEngine.instance(delayable_engine)
|
|
121
|
+
self.delayable_engine.add_document(self)
|
|
122
|
+
# ------------------------------------------------------------------------------
|
|
123
|
+
# used to store the right click location
|
|
124
|
+
self._clickPos = None
|
|
125
|
+
# dialog shown is used to prevent showing multiple versions of the of the
|
|
126
|
+
# confirmation dialog. this is caused because multiple signals are emitted and
|
|
127
|
+
# processed.
|
|
128
|
+
self._dialogShown = False
|
|
129
|
+
# used to store perminately highlighted keywords
|
|
130
|
+
self._permaHighlight = []
|
|
131
|
+
self.setSmartHighlightingRegEx()
|
|
132
|
+
|
|
133
|
+
# intialize settings
|
|
134
|
+
self.initSettings(first_time=True)
|
|
135
|
+
|
|
136
|
+
# set one time properties
|
|
137
|
+
self.setFolding(QsciScintilla.BoxedTreeFoldStyle)
|
|
138
|
+
self.setBraceMatching(QsciScintilla.SloppyBraceMatch)
|
|
139
|
+
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
|
140
|
+
self.setAcceptDrops(False)
|
|
141
|
+
# Not supported by older builds of QsciScintilla
|
|
142
|
+
if hasattr(self, 'setTabDrawMode'):
|
|
143
|
+
self.setTabDrawMode(QsciScintilla.TabStrikeOut)
|
|
144
|
+
|
|
145
|
+
# create connections
|
|
146
|
+
self.customContextMenuRequested.connect(self.showMenu)
|
|
147
|
+
self.selectionChanged.connect(self.updateSelectionInfo)
|
|
148
|
+
window = self.window()
|
|
149
|
+
if hasattr(window, 'openFileMonitor'):
|
|
150
|
+
window.styleSheetChanged.connect(self.updateColorScheme)
|
|
151
|
+
|
|
152
|
+
# Create shortcuts
|
|
153
|
+
icon = QIcon(resourcePath('img/content-copy.png'))
|
|
154
|
+
|
|
155
|
+
# We have to re-create the copy shortcut so we can use our implementation
|
|
156
|
+
self.uiCopyACT = QAction(icon, 'Copy', self)
|
|
157
|
+
self.uiCopyACT.setShortcut('Ctrl+C')
|
|
158
|
+
self.uiCopyACT.triggered.connect(self.copy)
|
|
159
|
+
self.addAction(self.uiCopyACT)
|
|
160
|
+
|
|
161
|
+
iconlstrip = QIcon(resourcePath('img/content-duplicate.png'))
|
|
162
|
+
self.uiCopyLstripACT = QAction(iconlstrip, 'Copy lstrip', self)
|
|
163
|
+
self.uiCopyLstripACT.setShortcut('Ctrl+Shift+C')
|
|
164
|
+
self.uiCopyLstripACT.triggered.connect(self.copyLstrip)
|
|
165
|
+
self.addAction(self.uiCopyLstripACT)
|
|
166
|
+
|
|
167
|
+
self.uiCopySpaceIndentationACT = QAction(icon, 'Copy Tabs to Spaces', self)
|
|
168
|
+
self.uiCopySpaceIndentationACT.setShortcut('Ctrl+Shift+Space')
|
|
169
|
+
self.uiCopySpaceIndentationACT.triggered.connect(self.copySpaceIndentation)
|
|
170
|
+
self.addAction(self.uiCopySpaceIndentationACT)
|
|
171
|
+
|
|
172
|
+
# Update keyboard shortcuts that come with QsciScintilla
|
|
173
|
+
commands = self.standardCommands()
|
|
174
|
+
# Remove the Ctrl+/ "Move left one word part" shortcut so it can be used to
|
|
175
|
+
# comment
|
|
176
|
+
command = commands.boundTo(Qt.ControlModifier | Qt.Key_Slash)
|
|
177
|
+
if command is not None:
|
|
178
|
+
command.setKey(0)
|
|
179
|
+
|
|
180
|
+
for command in commands.commands():
|
|
181
|
+
if command.description() == 'Move selected lines up one line':
|
|
182
|
+
command.setKey(Qt.ControlModifier | Qt.ShiftModifier | Qt.Key_Up)
|
|
183
|
+
if command.description() == 'Move selected lines down one line':
|
|
184
|
+
command.setKey(Qt.ControlModifier | Qt.ShiftModifier | Qt.Key_Down)
|
|
185
|
+
if command.description() == 'Duplicate selection':
|
|
186
|
+
command.setKey(Qt.ControlModifier | Qt.ShiftModifier | Qt.Key_D)
|
|
187
|
+
if command.description() == 'Cut current line':
|
|
188
|
+
command.setKey(0)
|
|
189
|
+
|
|
190
|
+
# Add QShortcuts
|
|
191
|
+
self.uiShowAutoCompleteSCT = QShortcut(
|
|
192
|
+
Qt.CTRL | Qt.Key_Space, self, context=Qt.WidgetShortcut
|
|
193
|
+
)
|
|
194
|
+
self.uiShowAutoCompleteSCT.activated.connect(lambda: self.showAutoComplete())
|
|
195
|
+
|
|
196
|
+
# load the file
|
|
197
|
+
if filename:
|
|
198
|
+
self.load(filename)
|
|
199
|
+
else:
|
|
200
|
+
self.refreshTitle()
|
|
201
|
+
self.setLanguage('Plain Text')
|
|
202
|
+
|
|
203
|
+
# goto the line
|
|
204
|
+
if lineno:
|
|
205
|
+
self.setCursorPosition(lineno, 0)
|
|
206
|
+
|
|
207
|
+
def autoReloadOnChange(self):
|
|
208
|
+
return self._autoReloadOnChange
|
|
209
|
+
|
|
210
|
+
def caretBackgroundColor(self):
|
|
211
|
+
return self._caretBackgroundColor
|
|
212
|
+
|
|
213
|
+
def caretForegroundColor(self):
|
|
214
|
+
return self._caretForegroundColor
|
|
215
|
+
|
|
216
|
+
def setCaretLineBackgroundColor(self, color):
|
|
217
|
+
self._caretBackgroundColor = color
|
|
218
|
+
super(DocumentEditor, self).setCaretLineBackgroundColor(color)
|
|
219
|
+
|
|
220
|
+
def setCaretForegroundColor(self, color):
|
|
221
|
+
self._caretForegroundColor = color
|
|
222
|
+
super(DocumentEditor, self).setCaretForegroundColor(color)
|
|
223
|
+
|
|
224
|
+
def checkForSave(self):
|
|
225
|
+
if self.isModified():
|
|
226
|
+
result = QMessageBox.question(
|
|
227
|
+
self.window(),
|
|
228
|
+
'Save changes to...',
|
|
229
|
+
'Do you want to save your changes?',
|
|
230
|
+
QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel,
|
|
231
|
+
)
|
|
232
|
+
if result == QMessageBox.Yes:
|
|
233
|
+
return self.save()
|
|
234
|
+
elif result == QMessageBox.Cancel:
|
|
235
|
+
return False
|
|
236
|
+
return True
|
|
237
|
+
|
|
238
|
+
def clear(self):
|
|
239
|
+
super(DocumentEditor, self).clear()
|
|
240
|
+
self._filename = ''
|
|
241
|
+
|
|
242
|
+
def closeEvent(self, event):
|
|
243
|
+
self.disableTitleUpdate()
|
|
244
|
+
# unsubcribe the file from the open file monitor
|
|
245
|
+
self.enableFileWatching(False)
|
|
246
|
+
super(DocumentEditor, self).closeEvent(event)
|
|
247
|
+
|
|
248
|
+
def closeEditor(self):
|
|
249
|
+
parent = self.parent()
|
|
250
|
+
if parent and parent.inherits('QMdiSubWindow'):
|
|
251
|
+
parent.close()
|
|
252
|
+
|
|
253
|
+
def commentCheck(self):
|
|
254
|
+
# collect the language
|
|
255
|
+
language = lang.byName(self._language)
|
|
256
|
+
if not language:
|
|
257
|
+
QMessageBox.critical(
|
|
258
|
+
self,
|
|
259
|
+
'No Language Defined',
|
|
260
|
+
'There is no language defined for this editor.',
|
|
261
|
+
)
|
|
262
|
+
return '', False
|
|
263
|
+
|
|
264
|
+
# grab the line comment
|
|
265
|
+
comment = language.lineComment()
|
|
266
|
+
if not comment:
|
|
267
|
+
QMessageBox.critical(
|
|
268
|
+
self,
|
|
269
|
+
'No Line Comment Defined',
|
|
270
|
+
'There is no line comment symbol defined for the "%s" language.'
|
|
271
|
+
% self._language,
|
|
272
|
+
)
|
|
273
|
+
return '', False
|
|
274
|
+
return comment, True
|
|
275
|
+
|
|
276
|
+
def commentToggle(self, doWhich=None):
|
|
277
|
+
"""Toggle comments, mimicing SublimeText functionality.
|
|
278
|
+
|
|
279
|
+
- Comments will be indented to match the outermost line being commented.
|
|
280
|
+
- Commenting / uncommenting is determined by whether all non-empty lines are
|
|
281
|
+
currently commented or not. If they ALL are, then uncomment, otherwise
|
|
282
|
+
comment.
|
|
283
|
+
"""
|
|
284
|
+
|
|
285
|
+
# If called by 'triggered' signal, clear out passed argument.
|
|
286
|
+
if not isinstance(doWhich, six.string_types):
|
|
287
|
+
doWhich = None
|
|
288
|
+
|
|
289
|
+
comment, result = self.commentCheck()
|
|
290
|
+
if not result:
|
|
291
|
+
return False
|
|
292
|
+
commentSpace = comment + " "
|
|
293
|
+
|
|
294
|
+
with undo_step(self):
|
|
295
|
+
# lookup the selected text positions
|
|
296
|
+
cursorLine, cursorIndex = self.expandCursorToLineSelection()
|
|
297
|
+
startLine, startCol, endLine, endCol = self.getSelection()
|
|
298
|
+
|
|
299
|
+
# Collect comments and indents, to determine indentation to use, and whether
|
|
300
|
+
# to comment or uncomment.
|
|
301
|
+
comments = []
|
|
302
|
+
indents = []
|
|
303
|
+
for line in range(startLine, endLine + 1):
|
|
304
|
+
lineText = self.getSelectionCurrentLineText(line)
|
|
305
|
+
|
|
306
|
+
# Skip if line is empty, or line is last line without selection
|
|
307
|
+
if not lineText.strip() or (line == endLine and not endCol):
|
|
308
|
+
continue
|
|
309
|
+
|
|
310
|
+
comments.append(lineText.lstrip()[0] == comment)
|
|
311
|
+
|
|
312
|
+
curIndent = self.determineIndent(lineText, comment)
|
|
313
|
+
indents.append(curIndent)
|
|
314
|
+
|
|
315
|
+
if not indents:
|
|
316
|
+
return
|
|
317
|
+
indent = min(indents)
|
|
318
|
+
|
|
319
|
+
# If all lines are comments, we un-comment. If any aren't
|
|
320
|
+
# comments, we comment.
|
|
321
|
+
if doWhich is None:
|
|
322
|
+
if all(comments):
|
|
323
|
+
doWhich = "Uncomment"
|
|
324
|
+
else:
|
|
325
|
+
doWhich = "Comment"
|
|
326
|
+
|
|
327
|
+
for line in range(startLine, endLine + 1):
|
|
328
|
+
lineText = self.getSelectionCurrentLineText(line)
|
|
329
|
+
if not lineText.strip():
|
|
330
|
+
continue
|
|
331
|
+
|
|
332
|
+
# Do not toggle comments on the last line if it contains no selection
|
|
333
|
+
if line != endLine or endCol:
|
|
334
|
+
|
|
335
|
+
if doWhich == "Comment":
|
|
336
|
+
self.setCursorPosition(line, indent)
|
|
337
|
+
self.insert(commentSpace)
|
|
338
|
+
if cursorIndex is not None and cursorIndex >= indent:
|
|
339
|
+
cursorIndex += len(commentSpace)
|
|
340
|
+
if line == startLine:
|
|
341
|
+
startCol -= len(commentSpace)
|
|
342
|
+
if line == endLine:
|
|
343
|
+
endCol += len(commentSpace)
|
|
344
|
+
|
|
345
|
+
elif doWhich == "Uncomment":
|
|
346
|
+
for curComment in [commentSpace, comment]:
|
|
347
|
+
foundText = self.getSelectedCommentText(
|
|
348
|
+
line, indent, len(curComment)
|
|
349
|
+
)
|
|
350
|
+
startCol, endCol, cursorIndex, removed = self.removeComment(
|
|
351
|
+
foundText,
|
|
352
|
+
curComment,
|
|
353
|
+
line,
|
|
354
|
+
indent,
|
|
355
|
+
startLine,
|
|
356
|
+
startCol,
|
|
357
|
+
endLine,
|
|
358
|
+
endCol,
|
|
359
|
+
cursorIndex,
|
|
360
|
+
)
|
|
361
|
+
if removed:
|
|
362
|
+
break
|
|
363
|
+
|
|
364
|
+
# restore the currently selected text, or cursor position
|
|
365
|
+
if cursorLine is not None:
|
|
366
|
+
startLine, endLine = cursorLine, cursorLine
|
|
367
|
+
startCol, endCol = cursorIndex, cursorIndex
|
|
368
|
+
self.setSelection(startLine, startCol, endLine, endCol)
|
|
369
|
+
|
|
370
|
+
def removeComment(
|
|
371
|
+
self,
|
|
372
|
+
text,
|
|
373
|
+
comment,
|
|
374
|
+
line,
|
|
375
|
+
indent,
|
|
376
|
+
startLine,
|
|
377
|
+
startCol,
|
|
378
|
+
endLine,
|
|
379
|
+
endCol,
|
|
380
|
+
cursorIndex,
|
|
381
|
+
):
|
|
382
|
+
removed = False
|
|
383
|
+
if text == comment:
|
|
384
|
+
commentLen = len(comment)
|
|
385
|
+
self.setSelection(line, indent, line, indent + commentLen)
|
|
386
|
+
self.removeSelectedText()
|
|
387
|
+
|
|
388
|
+
# py3 will throw an error if comparing None, so only compare if cursorIndex
|
|
389
|
+
# is not None
|
|
390
|
+
if cursorIndex is not None and cursorIndex > indent:
|
|
391
|
+
adjustment = None
|
|
392
|
+
for checkIndex in range(commentLen - 1):
|
|
393
|
+
newIndex = indent + checkIndex + 1
|
|
394
|
+
if cursorIndex == newIndex:
|
|
395
|
+
adjustment = checkIndex + 1
|
|
396
|
+
break
|
|
397
|
+
if adjustment is None:
|
|
398
|
+
adjustment = commentLen
|
|
399
|
+
cursorIndex -= adjustment
|
|
400
|
+
|
|
401
|
+
if line == startLine:
|
|
402
|
+
startCol -= commentLen
|
|
403
|
+
if line == endLine:
|
|
404
|
+
endCol -= commentLen
|
|
405
|
+
|
|
406
|
+
removed = True
|
|
407
|
+
return startCol, endCol, cursorIndex, removed
|
|
408
|
+
|
|
409
|
+
def determineIndent(self, lineText, comment=None):
|
|
410
|
+
indent = len(lineText) - len(lineText.lstrip())
|
|
411
|
+
return indent
|
|
412
|
+
|
|
413
|
+
def getSelectedCommentText(self, line, indent, commentLen):
|
|
414
|
+
"""Because QScintilla.setSelection automatically strips trailing
|
|
415
|
+
whitespace, we grab the whole rest of the line, then reset it
|
|
416
|
+
to just the length of the currentComment
|
|
417
|
+
"""
|
|
418
|
+
self.setSelection(line, indent, line, self.lineLength(line))
|
|
419
|
+
text = self.selectedText()
|
|
420
|
+
if len(text) >= commentLen:
|
|
421
|
+
text = text[:commentLen]
|
|
422
|
+
return text
|
|
423
|
+
|
|
424
|
+
def getSelectionCurrentLineText(self, line):
|
|
425
|
+
lineLength = len(self.text(line).rstrip())
|
|
426
|
+
self.setSelection(line, 0, line, lineLength)
|
|
427
|
+
lineText = self.selectedText()
|
|
428
|
+
return lineText
|
|
429
|
+
|
|
430
|
+
def expandCursorToLineSelection(self):
|
|
431
|
+
line, index = None, None
|
|
432
|
+
if not self.hasSelectedText():
|
|
433
|
+
line, index = self.getCursorPosition()
|
|
434
|
+
self.setSelection(line, 0, line, self.lineLength(line) - 2)
|
|
435
|
+
return line, index
|
|
436
|
+
|
|
437
|
+
def copy(self):
|
|
438
|
+
"""Copies the selected text.
|
|
439
|
+
|
|
440
|
+
If copyIndentsAsSpaces and self.indentationsUseTabs() is True it will convert
|
|
441
|
+
any indents to spaces before copying the text.
|
|
442
|
+
"""
|
|
443
|
+
if self.copyIndentsAsSpaces and self.indentationsUseTabs():
|
|
444
|
+
self.copySpaceIndentation()
|
|
445
|
+
else:
|
|
446
|
+
super(DocumentEditor, self).copy()
|
|
447
|
+
|
|
448
|
+
def copyFilenameToClipboard(self):
|
|
449
|
+
QApplication.clipboard().setText(self._filename)
|
|
450
|
+
|
|
451
|
+
def copyLineReference(self):
|
|
452
|
+
sel = self.getSelection()
|
|
453
|
+
# Note: getSelection is 0 based like all good code
|
|
454
|
+
if sel[0] == -1 and self._clickPos:
|
|
455
|
+
lines = (self.lineAt(self.mapFromGlobal(self._clickPos)) + 1, -1)
|
|
456
|
+
else:
|
|
457
|
+
end = sel[2]
|
|
458
|
+
if sel[3] == 0:
|
|
459
|
+
# if nothing is selected on the last line, exclude it
|
|
460
|
+
end -= 1
|
|
461
|
+
lines = (sel[0] + 1, end + 1)
|
|
462
|
+
args = {'filename': self.filename(), 'plural': ''}
|
|
463
|
+
if lines[1] == -1 or lines[0] == lines[1]:
|
|
464
|
+
args['line'] = lines[0]
|
|
465
|
+
else:
|
|
466
|
+
args['line'] = '{}-{}'.format(*lines)
|
|
467
|
+
args['plural'] = 's'
|
|
468
|
+
QApplication.clipboard().setText(
|
|
469
|
+
'{filename}: Line{plural} {line}'.format(**args)
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
def copyLstrip(self):
|
|
473
|
+
"""Copy's the selected text, but strips off any leading whitespace shared by the
|
|
474
|
+
entire selection.
|
|
475
|
+
"""
|
|
476
|
+
start, s, end, e = self.getSelection()
|
|
477
|
+
count = end - start + 1
|
|
478
|
+
self.setSelection(start, 0, end, e)
|
|
479
|
+
txt = self.selectedText()
|
|
480
|
+
|
|
481
|
+
def replacement(match):
|
|
482
|
+
return re.sub('[ \t]', '', match.group(), count=1)
|
|
483
|
+
|
|
484
|
+
# NOTE: Don't use re.M, it does not support mac line endings.
|
|
485
|
+
regex = re.compile('(?:^|\r\n?|\n)[ \t]')
|
|
486
|
+
while len(regex.findall(txt)) == count:
|
|
487
|
+
# We found the same number of leading whitespace as lines of text.
|
|
488
|
+
# This means that it all has leading whitespace that needs removed.
|
|
489
|
+
txt = regex.sub(replacement, txt)
|
|
490
|
+
QApplication.clipboard().setText(txt)
|
|
491
|
+
|
|
492
|
+
def copySpaceIndentation(self):
|
|
493
|
+
"""Copy the selected text with any tab indents converted to space indents.
|
|
494
|
+
|
|
495
|
+
If indentationsUseTabs is False it will just copy the text
|
|
496
|
+
"""
|
|
497
|
+
txt = self.selectedText()
|
|
498
|
+
|
|
499
|
+
def replacement(match):
|
|
500
|
+
return match.group().replace('\t', ' ' * self.tabWidth())
|
|
501
|
+
|
|
502
|
+
# NOTE: Don't use re.M, it does not support mac line endings.
|
|
503
|
+
ret = re.sub('(?:^|\r\n?|\n)\t+', replacement, txt)
|
|
504
|
+
QApplication.clipboard().setText(ret)
|
|
505
|
+
|
|
506
|
+
def detectEndLine(self, text):
|
|
507
|
+
newlineN = text.find('\n')
|
|
508
|
+
newlineR = text.find('\r')
|
|
509
|
+
if newlineN != -1 and newlineR != -1:
|
|
510
|
+
if newlineN == newlineR + 1:
|
|
511
|
+
# CR LF Windows
|
|
512
|
+
return self.EolWindows
|
|
513
|
+
elif newlineR == newlineN + 1:
|
|
514
|
+
# LF CR ACorn and RISC unsuported
|
|
515
|
+
return self.eolMode()
|
|
516
|
+
if newlineN != -1 and newlineR != -1:
|
|
517
|
+
if newlineN < newlineR:
|
|
518
|
+
# First return is a LF
|
|
519
|
+
return self.EolUnix
|
|
520
|
+
else:
|
|
521
|
+
# first return is a CR
|
|
522
|
+
return self.EolMac
|
|
523
|
+
if newlineN != -1:
|
|
524
|
+
return self.EolUnix
|
|
525
|
+
if sys.platform == 'win32':
|
|
526
|
+
return self.EolWindows
|
|
527
|
+
return self.EolUnix
|
|
528
|
+
|
|
529
|
+
def editPermaHighlight(self):
|
|
530
|
+
text, success = QInputDialog.getText(
|
|
531
|
+
self,
|
|
532
|
+
'Edit PermaHighlight keywords',
|
|
533
|
+
'Add keywords separated by a space',
|
|
534
|
+
text=' '.join(self.permaHighlight()),
|
|
535
|
+
)
|
|
536
|
+
if success:
|
|
537
|
+
self.setPermaHighlight(text.split(' '))
|
|
538
|
+
|
|
539
|
+
def enableFileWatching(self, state):
|
|
540
|
+
"""Enables/Disables open file change monitoring. If enabled, A dialog will pop
|
|
541
|
+
up when ever the open file is changed externally. If file monitoring is
|
|
542
|
+
disabled in the IDE settings it will be ignored.
|
|
543
|
+
|
|
544
|
+
Returns:
|
|
545
|
+
bool:
|
|
546
|
+
"""
|
|
547
|
+
# if file monitoring is enabled and we have a file name then set up the file
|
|
548
|
+
# monitoring
|
|
549
|
+
window = self.window()
|
|
550
|
+
self._fileMonitoringActive = False
|
|
551
|
+
if hasattr(window, 'openFileMonitor'):
|
|
552
|
+
fm = window.openFileMonitor()
|
|
553
|
+
if fm:
|
|
554
|
+
if state:
|
|
555
|
+
fm.addPath(self._filename)
|
|
556
|
+
self._fileMonitoringActive = True
|
|
557
|
+
else:
|
|
558
|
+
fm.removePath(self._filename)
|
|
559
|
+
return self._fileMonitoringActive
|
|
560
|
+
|
|
561
|
+
def disableTitleUpdate(self):
|
|
562
|
+
self.modificationChanged.connect(self.refreshTitle)
|
|
563
|
+
|
|
564
|
+
def enableTitleUpdate(self):
|
|
565
|
+
self.modificationChanged.connect(self.refreshTitle)
|
|
566
|
+
|
|
567
|
+
def eventFilter(self, object, event):
|
|
568
|
+
if event.type() == event.Close and not self.checkForSave():
|
|
569
|
+
event.ignore()
|
|
570
|
+
return True
|
|
571
|
+
return False
|
|
572
|
+
|
|
573
|
+
def exploreDocument(self):
|
|
574
|
+
path = self._filename
|
|
575
|
+
if os.path.isfile(path):
|
|
576
|
+
path = os.path.split(path)[0]
|
|
577
|
+
|
|
578
|
+
if os.path.exists(path):
|
|
579
|
+
osystem.explore(path)
|
|
580
|
+
else:
|
|
581
|
+
QMessageBox.critical(
|
|
582
|
+
self, 'Missing Path', 'Could not find %s' % path.replace('/', '\\')
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
def execStandalone(self):
|
|
586
|
+
if self.save():
|
|
587
|
+
os.startfile(str(self.filename()))
|
|
588
|
+
|
|
589
|
+
def foldMarginColors(self):
|
|
590
|
+
"""Returns the fold margin's foreground and background QColor
|
|
591
|
+
|
|
592
|
+
Returns:
|
|
593
|
+
foreground(QColor): The foreground color
|
|
594
|
+
background(QColor): The background color
|
|
595
|
+
"""
|
|
596
|
+
return self._foldMarginForegroundColor, self._foldMarginBackgroundColor
|
|
597
|
+
|
|
598
|
+
def setFoldMarginColors(self, foreground, background):
|
|
599
|
+
"""Sets the fold margins foreground and background QColor
|
|
600
|
+
|
|
601
|
+
Args:
|
|
602
|
+
foreground(QColor): The forground color of the checkerboard
|
|
603
|
+
background(QColor): The background color of the checkerboard
|
|
604
|
+
"""
|
|
605
|
+
self._foldMarginForegroundColor = foreground
|
|
606
|
+
self._foldMarginBackgroundColor = background
|
|
607
|
+
super(DocumentEditor, self).setFoldMarginColors(foreground, background)
|
|
608
|
+
|
|
609
|
+
def goToLine(self, line=None):
|
|
610
|
+
if type(line) != int:
|
|
611
|
+
line, accepted = QInputDialog.getInt(self, 'Line Number', 'Line:')
|
|
612
|
+
else:
|
|
613
|
+
accepted = True
|
|
614
|
+
|
|
615
|
+
if accepted:
|
|
616
|
+
# MH 04/12/11 changed from line + 1 to line - 1 to make the gotoLine dialog
|
|
617
|
+
# go to the correct line.
|
|
618
|
+
self.setCursorPosition(line - 1, 0)
|
|
619
|
+
self.ensureLineVisible(line)
|
|
620
|
+
|
|
621
|
+
def goToDefinition(self, text=None):
|
|
622
|
+
if not text:
|
|
623
|
+
text = self.selectedText()
|
|
624
|
+
if not text:
|
|
625
|
+
text, accepted = QInputDialog.getText(self, 'def Name', 'Name:')
|
|
626
|
+
else:
|
|
627
|
+
accepted = True
|
|
628
|
+
else:
|
|
629
|
+
accepted = True
|
|
630
|
+
if accepted:
|
|
631
|
+
descriptors = lang.byName(self.language()).descriptors()
|
|
632
|
+
docText = self.text()
|
|
633
|
+
for descriptor in descriptors:
|
|
634
|
+
result = descriptor.search(docText)
|
|
635
|
+
while result:
|
|
636
|
+
name = result.group('name')
|
|
637
|
+
if name.startswith(text):
|
|
638
|
+
self.findNext(name, 0)
|
|
639
|
+
return
|
|
640
|
+
result = descriptor.search(docText, result.end())
|
|
641
|
+
|
|
642
|
+
def language(self):
|
|
643
|
+
return self._language
|
|
644
|
+
|
|
645
|
+
def languageChosen(self, action):
|
|
646
|
+
self.setLanguage(action.text())
|
|
647
|
+
self.updateColorScheme()
|
|
648
|
+
self._fileMonitoringActive = False
|
|
649
|
+
window = self.window()
|
|
650
|
+
if hasattr(window, 'uiLanguageDDL'):
|
|
651
|
+
window.uiLanguageDDL.blockSignals(True)
|
|
652
|
+
window.uiLanguageDDL.setCurrentLanguage(action.text())
|
|
653
|
+
window.uiLanguageDDL.blockSignals(False)
|
|
654
|
+
|
|
655
|
+
def lineMarginWidth(self):
|
|
656
|
+
return self.marginWidth(self.SymbolMargin)
|
|
657
|
+
|
|
658
|
+
def load(self, filename):
|
|
659
|
+
filename = str(filename)
|
|
660
|
+
if filename and os.path.exists(filename):
|
|
661
|
+
f = QFile(filename)
|
|
662
|
+
f.open(QFile.ReadOnly)
|
|
663
|
+
text = f.readAll()
|
|
664
|
+
self._textCodec = QTextCodec.codecForUtfText(
|
|
665
|
+
text, QTextCodec.codecForName('UTF-8')
|
|
666
|
+
)
|
|
667
|
+
self.setText(self._textCodec.toUnicode(text))
|
|
668
|
+
f.close()
|
|
669
|
+
self.updateFilename(filename)
|
|
670
|
+
self.enableFileWatching(True)
|
|
671
|
+
self.setEolMode(self.detectEndLine(self.text()))
|
|
672
|
+
return True
|
|
673
|
+
return False
|
|
674
|
+
|
|
675
|
+
def filename(self):
|
|
676
|
+
return self._filename
|
|
677
|
+
|
|
678
|
+
def findNext(self, text, flags):
|
|
679
|
+
re = (flags & SearchOptions.QRegExp) != 0
|
|
680
|
+
cs = (flags & SearchOptions.CaseSensitive) != 0
|
|
681
|
+
wo = (flags & SearchOptions.WholeWords) != 0
|
|
682
|
+
wrap = True
|
|
683
|
+
forward = True
|
|
684
|
+
|
|
685
|
+
result = self.findFirst(text, re, cs, wo, wrap, forward)
|
|
686
|
+
|
|
687
|
+
if not result:
|
|
688
|
+
self.findTextNotFound(text)
|
|
689
|
+
|
|
690
|
+
return result
|
|
691
|
+
|
|
692
|
+
def findPrev(self, text, flags):
|
|
693
|
+
re = (flags & SearchOptions.QRegExp) != 0
|
|
694
|
+
cs = (flags & SearchOptions.CaseSensitive) != 0
|
|
695
|
+
wo = (flags & SearchOptions.WholeWords) != 0
|
|
696
|
+
wrap = True
|
|
697
|
+
forward = False
|
|
698
|
+
|
|
699
|
+
isSelected = self.hasSelectedText()
|
|
700
|
+
result = self.findFirst(text, re, cs, wo, wrap, forward)
|
|
701
|
+
if result and isSelected:
|
|
702
|
+
# If text is selected when finding previous, it will find the currently
|
|
703
|
+
# selected text so do another find.
|
|
704
|
+
result = QsciScintilla.findNext(self)
|
|
705
|
+
|
|
706
|
+
if not result:
|
|
707
|
+
self.findTextNotFound(text)
|
|
708
|
+
|
|
709
|
+
return result
|
|
710
|
+
|
|
711
|
+
def find_simple(self, find_state):
|
|
712
|
+
"""Python implementation of QsciScintilla.simpleFind.
|
|
713
|
+
|
|
714
|
+
Args:
|
|
715
|
+
find_state (preditor.scintilla.FindState): A find state used to
|
|
716
|
+
manage the find.
|
|
717
|
+
|
|
718
|
+
https://github.com/josephwilk/qscintilla/blob/master/Qt4Qt5/qsciscintilla.cpp
|
|
719
|
+
"""
|
|
720
|
+
if find_state.start_pos == find_state.end_pos:
|
|
721
|
+
return -1
|
|
722
|
+
|
|
723
|
+
self.SendScintilla(self.SCI_SETTARGETSTART, find_state.start_pos)
|
|
724
|
+
self.SendScintilla(self.SCI_SETTARGETEND, find_state.end_pos)
|
|
725
|
+
|
|
726
|
+
# scintilla can't match unicode strings, even in python 3
|
|
727
|
+
# In python 3 you have to cast it to a bytes object
|
|
728
|
+
expr = bytes(str(find_state.expr).encode("utf-8"))
|
|
729
|
+
|
|
730
|
+
return self.SendScintilla(self.SCI_SEARCHINTARGET, len(expr), expr)
|
|
731
|
+
|
|
732
|
+
def find_text(self, find_state):
|
|
733
|
+
"""Finds text in the document without changing the selection.
|
|
734
|
+
|
|
735
|
+
Args:
|
|
736
|
+
find_state (preditor.scintilla.FindState): A find state used to
|
|
737
|
+
manage the find.
|
|
738
|
+
|
|
739
|
+
Based on QsciScintilla.doFind.
|
|
740
|
+
https://github.com/josephwilk/qscintilla/blob/master/Qt4Qt5/qsciscintilla.cpp
|
|
741
|
+
"""
|
|
742
|
+
# Set the search flags
|
|
743
|
+
self.SendScintilla(self.SCI_SETSEARCHFLAGS, find_state.flags)
|
|
744
|
+
# If no end was specified, use the end of the document
|
|
745
|
+
if find_state.end_pos is None:
|
|
746
|
+
find_state.end_pos = self.SendScintilla(self.SCI_GETLENGTH)
|
|
747
|
+
|
|
748
|
+
pos = self.find_simple(find_state)
|
|
749
|
+
|
|
750
|
+
# See if it was found. If not and wraparound is wanted, try again.
|
|
751
|
+
if pos == -1 and find_state.wrap:
|
|
752
|
+
if find_state.forward:
|
|
753
|
+
find_state.start_pos = 0
|
|
754
|
+
if find_state.start_pos_original is None:
|
|
755
|
+
find_state.end_pos = self.SendScintilla(self.SCI_GETLENGTH)
|
|
756
|
+
else:
|
|
757
|
+
find_state.end_pos = find_state.start_pos_original
|
|
758
|
+
else:
|
|
759
|
+
if find_state.start_pos_original is None:
|
|
760
|
+
find_state.start_pos = self.SendScintilla(self.SCI_GETLENGTH)
|
|
761
|
+
else:
|
|
762
|
+
find_state.start_pos = find_state.start_pos_original
|
|
763
|
+
find_state.end_pos = 0
|
|
764
|
+
# Give a indication that we have wrapped
|
|
765
|
+
find_state.wrapped = True
|
|
766
|
+
|
|
767
|
+
pos = self.find_simple(find_state)
|
|
768
|
+
|
|
769
|
+
if pos == -1:
|
|
770
|
+
return -1, 0
|
|
771
|
+
|
|
772
|
+
# It was found.
|
|
773
|
+
target_start = self.SendScintilla(self.SCI_GETTARGETSTART)
|
|
774
|
+
target_end = self.SendScintilla(self.SCI_GETTARGETEND)
|
|
775
|
+
|
|
776
|
+
# Finally adjust the start position so that we don't find the same one again.
|
|
777
|
+
if find_state.forward:
|
|
778
|
+
find_state.start_pos = target_end
|
|
779
|
+
else:
|
|
780
|
+
find_state.start_pos = target_start - 1
|
|
781
|
+
if find_state.start_pos < 0:
|
|
782
|
+
find_state.start_pos = 0
|
|
783
|
+
|
|
784
|
+
return target_start, target_end
|
|
785
|
+
|
|
786
|
+
def find_text_from_cursor(self, find_state):
|
|
787
|
+
"""Starting from the current cursor position wrapping around, return all
|
|
788
|
+
matches to the provided find_state.
|
|
789
|
+
|
|
790
|
+
Args:
|
|
791
|
+
find_state (preditor.scintilla.FindState): A find state used to
|
|
792
|
+
manage the find.
|
|
793
|
+
"""
|
|
794
|
+
# Start searching from the cursor, wrap past the end and stop where we started
|
|
795
|
+
current_position = self.positionFromLineIndex(*self.getCursorPosition())
|
|
796
|
+
find_state.start_pos = current_position
|
|
797
|
+
find_state.start_pos_original = current_position
|
|
798
|
+
|
|
799
|
+
positions = []
|
|
800
|
+
start, end = self.find_text(find_state)
|
|
801
|
+
while start != -1:
|
|
802
|
+
positions.append((start, end))
|
|
803
|
+
if find_state.wrapped:
|
|
804
|
+
# once we have wrapped, disable wrap
|
|
805
|
+
find_state.wrap = False
|
|
806
|
+
start, end = self.find_text(find_state)
|
|
807
|
+
return positions
|
|
808
|
+
|
|
809
|
+
def findTextNotFound(self, text):
|
|
810
|
+
try:
|
|
811
|
+
# If a number was typed in, ask the user if they wanted to goto that line
|
|
812
|
+
# number.
|
|
813
|
+
line = int(text)
|
|
814
|
+
msg = (
|
|
815
|
+
'Search string "%s" was not found. \nIt looks like a line number, '
|
|
816
|
+
'would you like to goto line %i?'
|
|
817
|
+
)
|
|
818
|
+
result = QMessageBox.critical(
|
|
819
|
+
self,
|
|
820
|
+
'No Text Found',
|
|
821
|
+
msg % (text, line),
|
|
822
|
+
buttons=(QMessageBox.Yes | QMessageBox.No),
|
|
823
|
+
defaultButton=QMessageBox.Yes,
|
|
824
|
+
)
|
|
825
|
+
if result == QMessageBox.Yes:
|
|
826
|
+
self.goToLine(line)
|
|
827
|
+
except ValueError:
|
|
828
|
+
QMessageBox.critical(
|
|
829
|
+
self, 'No Text Found', 'Search string "%s" was not found.' % text
|
|
830
|
+
)
|
|
831
|
+
|
|
832
|
+
def keyPressEvent(self, event):
|
|
833
|
+
key = event.key()
|
|
834
|
+
if key == Qt.Key_Backtab:
|
|
835
|
+
self.unindentSelection()
|
|
836
|
+
elif key == Qt.Key_Escape:
|
|
837
|
+
# Using QShortcut for Escape did not seem to work.
|
|
838
|
+
self.showAutoComplete(True)
|
|
839
|
+
else:
|
|
840
|
+
return QsciScintilla.keyPressEvent(self, event)
|
|
841
|
+
|
|
842
|
+
def keyReleaseEvent(self, event):
|
|
843
|
+
if event.key() == Qt.Key_Menu:
|
|
844
|
+
# Calculate the screen coordinates of the text cursor.
|
|
845
|
+
position = self.positionFromLineIndex(*self.getCursorPosition())
|
|
846
|
+
x = self.SendScintilla(self.SCI_POINTXFROMPOSITION, 0, position)
|
|
847
|
+
y = self.SendScintilla(self.SCI_POINTYFROMPOSITION, 0, position)
|
|
848
|
+
# When using the menu key, show the right click menu at the text
|
|
849
|
+
# cursor, not the mouse cursor, it is not in the correct place.
|
|
850
|
+
self.showMenu(QPoint(x, y))
|
|
851
|
+
else:
|
|
852
|
+
return super(DocumentEditor, self).keyReleaseEvent(event)
|
|
853
|
+
|
|
854
|
+
def initSettings(self, first_time=False):
|
|
855
|
+
"""Set/reset settings using the IDE section settings."""
|
|
856
|
+
|
|
857
|
+
# set visibility settings
|
|
858
|
+
self.setAutoIndent(True)
|
|
859
|
+
if first_time:
|
|
860
|
+
self.setIndentationsUseTabs(False)
|
|
861
|
+
self.setTabIndents(True)
|
|
862
|
+
self.setTabWidth(4)
|
|
863
|
+
self.setCaretLineVisible(False)
|
|
864
|
+
self.setShowWhitespaces(False)
|
|
865
|
+
self.setMarginLineNumbers(0, True)
|
|
866
|
+
self.setIndentationGuides(False)
|
|
867
|
+
self.setEolVisibility(False)
|
|
868
|
+
self.setShowSmartHighlighting(True)
|
|
869
|
+
self.setBackspaceUnindents(True)
|
|
870
|
+
|
|
871
|
+
self.setEdgeMode(self.EdgeNone)
|
|
872
|
+
|
|
873
|
+
# set autocompletion settings
|
|
874
|
+
self.setAutoCompletionSource(QsciScintilla.AcsAll)
|
|
875
|
+
self.setAutoCompletionThreshold(3)
|
|
876
|
+
|
|
877
|
+
self.setFont(self.documentFont)
|
|
878
|
+
self.setMarginsFont(self.marginsFont())
|
|
879
|
+
self.setMarginWidth(0, QFontMetrics(self.marginsFont()).width('0000000') + 5)
|
|
880
|
+
|
|
881
|
+
def markerNext(self):
|
|
882
|
+
line, index = self.getCursorPosition()
|
|
883
|
+
newline = self.markerFindNext(line + 1, self.marginMarkerMask(1))
|
|
884
|
+
|
|
885
|
+
# wrap around the document if necessary
|
|
886
|
+
if newline == -1:
|
|
887
|
+
newline = self.markerFindNext(0, self.marginMarkerMask(1))
|
|
888
|
+
|
|
889
|
+
self.setCursorPosition(newline, index)
|
|
890
|
+
|
|
891
|
+
def markerLoad(self, input):
|
|
892
|
+
r"""
|
|
893
|
+
\remarks Takes a list of line numbers and adds a marker to each of them
|
|
894
|
+
in the file.
|
|
895
|
+
"""
|
|
896
|
+
for line in input:
|
|
897
|
+
marker = self.markerDefine(self.Circle)
|
|
898
|
+
self.markerAdd(line, marker)
|
|
899
|
+
|
|
900
|
+
def markerToggle(self):
|
|
901
|
+
line, index = self.getCursorPosition()
|
|
902
|
+
markers = self.markersAtLine(line)
|
|
903
|
+
if not markers:
|
|
904
|
+
marker = self.markerDefine(self.Circle)
|
|
905
|
+
self.markerAdd(line, marker)
|
|
906
|
+
else:
|
|
907
|
+
self.markerDelete(line)
|
|
908
|
+
|
|
909
|
+
def marginsFont(self):
|
|
910
|
+
return self._marginsFont
|
|
911
|
+
|
|
912
|
+
def multipleSelection(self):
|
|
913
|
+
"""Returns if multiple selection is enabled."""
|
|
914
|
+
return self.SendScintilla(self.SCI_GETMULTIPLESELECTION)
|
|
915
|
+
|
|
916
|
+
def multipleSelectionAdditionalSelectionTyping(self):
|
|
917
|
+
"""Returns if multiple selection allows additional typing."""
|
|
918
|
+
return self.SendScintilla(self.SCI_GETMULTIPLESELECTION)
|
|
919
|
+
|
|
920
|
+
def multipleSelectionMultiPaste(self):
|
|
921
|
+
"""Paste into all multiple selections."""
|
|
922
|
+
return self.SendScintilla(self.SCI_GETMULTIPASTE)
|
|
923
|
+
|
|
924
|
+
def paste(self):
|
|
925
|
+
text = QApplication.clipboard().text()
|
|
926
|
+
if text.find('\n') == -1 and text.find('\r') == -1:
|
|
927
|
+
return super(DocumentEditor, self).paste()
|
|
928
|
+
|
|
929
|
+
def repForMode(mode):
|
|
930
|
+
if mode == self.EolWindows:
|
|
931
|
+
return '\r\n'
|
|
932
|
+
elif mode == self.EolUnix:
|
|
933
|
+
return '\n'
|
|
934
|
+
else:
|
|
935
|
+
return '\r'
|
|
936
|
+
|
|
937
|
+
text = text.replace(
|
|
938
|
+
repForMode(self.detectEndLine(text)), repForMode(self.eolMode())
|
|
939
|
+
)
|
|
940
|
+
QApplication.clipboard().setText(text)
|
|
941
|
+
return super(DocumentEditor, self).paste()
|
|
942
|
+
|
|
943
|
+
def permaHighlight(self):
|
|
944
|
+
return self._permaHighlight
|
|
945
|
+
|
|
946
|
+
def setPermaHighlight(self, value):
|
|
947
|
+
if not isinstance(value, list):
|
|
948
|
+
raise TypeError('PermaHighlight must be a list')
|
|
949
|
+
|
|
950
|
+
def refreshToolTip(self):
|
|
951
|
+
# TODO: This will proably be removed once I add a user interface to
|
|
952
|
+
# additionalFilenames.
|
|
953
|
+
toolTip = []
|
|
954
|
+
if self.additionalFilenames:
|
|
955
|
+
toolTip.append('<u><b>Additional Filenames:</b></u>')
|
|
956
|
+
for filename in self.additionalFilenames:
|
|
957
|
+
toolTip.append(filename)
|
|
958
|
+
self.setToolTip('\n<br>'.join(toolTip))
|
|
959
|
+
|
|
960
|
+
def reloadFile(self):
|
|
961
|
+
return self.reloadDialog(
|
|
962
|
+
'Are you sure you want to reload %s? You will lose all changes'
|
|
963
|
+
% os.path.basename(self.filename())
|
|
964
|
+
)
|
|
965
|
+
|
|
966
|
+
def reloadChange(self):
|
|
967
|
+
"""Callback for file monitoring. If a file was modified or deleted this method
|
|
968
|
+
is called when Open File Monitoring is enabled. Returns if the file was updated
|
|
969
|
+
or left open
|
|
970
|
+
|
|
971
|
+
Returns:
|
|
972
|
+
bool:
|
|
973
|
+
"""
|
|
974
|
+
logger.debug(
|
|
975
|
+
'Reload Change called: %0.3f Dialog Shown: %r'
|
|
976
|
+
% (self._saveTimer, self._dialogShown),
|
|
977
|
+
)
|
|
978
|
+
if time.time() - self._saveTimer < 0.5:
|
|
979
|
+
# If we are saving no need to reload the file
|
|
980
|
+
logger.debug('timer has not expired')
|
|
981
|
+
return False
|
|
982
|
+
if not os.path.isfile(self.filename()) and not self._dialogShown:
|
|
983
|
+
logger.debug('The file was deleted')
|
|
984
|
+
# the file was deleted, ask the user if they still want to keep the file in
|
|
985
|
+
# the editor.
|
|
986
|
+
self._dialogShown = True
|
|
987
|
+
result = QMessageBox.question(
|
|
988
|
+
self.window(),
|
|
989
|
+
'File Removed...',
|
|
990
|
+
'File: %s has been deleted.\nKeep file in editor?' % self.filename(),
|
|
991
|
+
QMessageBox.Yes,
|
|
992
|
+
QMessageBox.No,
|
|
993
|
+
)
|
|
994
|
+
self._dialogShown = False
|
|
995
|
+
if result == QMessageBox.No:
|
|
996
|
+
logger.debug(
|
|
997
|
+
'The file was deleted, removing document from editor',
|
|
998
|
+
)
|
|
999
|
+
self.parent().close()
|
|
1000
|
+
return False
|
|
1001
|
+
# TODO: The file no longer exists, and the document should be marked as
|
|
1002
|
+
# changed.
|
|
1003
|
+
logger.debug(
|
|
1004
|
+
'The file was deleted, But the user left it in the editor',
|
|
1005
|
+
)
|
|
1006
|
+
self.enableFileWatching(False)
|
|
1007
|
+
return True
|
|
1008
|
+
logger.debug('Defaulting to reload message')
|
|
1009
|
+
return self.reloadDialog(
|
|
1010
|
+
'File: %s has been changed.\nReload from disk?' % self.filename()
|
|
1011
|
+
)
|
|
1012
|
+
|
|
1013
|
+
def reloadDialog(self, message, title='Reload File...'):
|
|
1014
|
+
if not self._dialogShown:
|
|
1015
|
+
self._dialogShown = True
|
|
1016
|
+
if self._autoReloadOnChange or not self.isModified():
|
|
1017
|
+
result = QMessageBox.Yes
|
|
1018
|
+
else:
|
|
1019
|
+
result = QMessageBox.question(
|
|
1020
|
+
self.window(), title, message, QMessageBox.Yes | QMessageBox.No
|
|
1021
|
+
)
|
|
1022
|
+
self._dialogShown = False
|
|
1023
|
+
if result == QMessageBox.Yes:
|
|
1024
|
+
return self.load(self.filename())
|
|
1025
|
+
return False
|
|
1026
|
+
|
|
1027
|
+
def replace(self, text, searchtext=None, all=False):
|
|
1028
|
+
# replace the current text with the inputed text
|
|
1029
|
+
if not searchtext:
|
|
1030
|
+
searchtext = self.selectedText()
|
|
1031
|
+
|
|
1032
|
+
# make sure something is selected
|
|
1033
|
+
if not searchtext:
|
|
1034
|
+
return 0
|
|
1035
|
+
|
|
1036
|
+
with undo_step(self):
|
|
1037
|
+
sel = self.getSelection()
|
|
1038
|
+
|
|
1039
|
+
# replace all of the instances of the text
|
|
1040
|
+
if all:
|
|
1041
|
+
count = self.text().count(searchtext, Qt.CaseInsensitive)
|
|
1042
|
+
found = 0
|
|
1043
|
+
while self.findFirst(searchtext, False, False, False, True, True):
|
|
1044
|
+
if found == count:
|
|
1045
|
+
# replaced all items, exit so we don't get a infinite loop
|
|
1046
|
+
break
|
|
1047
|
+
found += 1
|
|
1048
|
+
super(DocumentEditor, self).replace(text)
|
|
1049
|
+
|
|
1050
|
+
# replace a single instance of the text
|
|
1051
|
+
else:
|
|
1052
|
+
count = 1
|
|
1053
|
+
super(DocumentEditor, self).replace(text)
|
|
1054
|
+
|
|
1055
|
+
self.setSelection(*sel)
|
|
1056
|
+
|
|
1057
|
+
return count
|
|
1058
|
+
|
|
1059
|
+
def setText(self, text):
|
|
1060
|
+
self.blockSignals(True)
|
|
1061
|
+
super(DocumentEditor, self).setText(text)
|
|
1062
|
+
self.blockSignals(False)
|
|
1063
|
+
self.spellCheck(0, None)
|
|
1064
|
+
|
|
1065
|
+
def refreshTitle(self):
|
|
1066
|
+
try:
|
|
1067
|
+
parent = self.parent()
|
|
1068
|
+
if parent and parent.inherits('QMdiSubWindow'):
|
|
1069
|
+
parent.setWindowTitle(self.windowTitle())
|
|
1070
|
+
except RuntimeError:
|
|
1071
|
+
pass
|
|
1072
|
+
|
|
1073
|
+
def save(self):
|
|
1074
|
+
logger.debug(' Saved Called'.center(60, '-'))
|
|
1075
|
+
ret = self.saveAs(self.filename())
|
|
1076
|
+
# If the user has provided additionalFilenames to save, process each of them
|
|
1077
|
+
# without switching the current filename.
|
|
1078
|
+
for filename in self.additionalFilenames:
|
|
1079
|
+
self.saveAs(filename, setFilename=False)
|
|
1080
|
+
return ret
|
|
1081
|
+
|
|
1082
|
+
def saveAs(self, filename='', setFilename=True):
|
|
1083
|
+
logger.debug(' Save As Called '.center(60, '-'))
|
|
1084
|
+
newFile = False
|
|
1085
|
+
if not filename:
|
|
1086
|
+
newFile = True
|
|
1087
|
+
filename = self.filename()
|
|
1088
|
+
filename, extFilter = QtCompat.QFileDialog.getSaveFileName(
|
|
1089
|
+
self.window(), 'Save File as...', filename
|
|
1090
|
+
)
|
|
1091
|
+
|
|
1092
|
+
if filename:
|
|
1093
|
+
self._saveTimer = time.time()
|
|
1094
|
+
# save the file to disk
|
|
1095
|
+
f = QFile(filename)
|
|
1096
|
+
f.open(QFile.WriteOnly)
|
|
1097
|
+
# make sure the file is writeable
|
|
1098
|
+
if f.error() != QFile.NoError:
|
|
1099
|
+
logger.debug('An error occured while saving')
|
|
1100
|
+
QMessageBox.question(
|
|
1101
|
+
self.window(),
|
|
1102
|
+
'Error saving file...',
|
|
1103
|
+
'There was a error saving the file. Error Code: %i' % f.error(),
|
|
1104
|
+
QMessageBox.Ok,
|
|
1105
|
+
)
|
|
1106
|
+
f.close()
|
|
1107
|
+
return False
|
|
1108
|
+
# Attempt to save the file using the same codec that it used to display it
|
|
1109
|
+
if self._textCodec:
|
|
1110
|
+
f.write(self._textCodec.fromUnicode(self.text()))
|
|
1111
|
+
else:
|
|
1112
|
+
self.write(f)
|
|
1113
|
+
f.close()
|
|
1114
|
+
# notify that the document was saved
|
|
1115
|
+
self.documentSaved.emit(self, filename)
|
|
1116
|
+
|
|
1117
|
+
# update the file
|
|
1118
|
+
if setFilename:
|
|
1119
|
+
self.updateFilename(filename)
|
|
1120
|
+
if newFile:
|
|
1121
|
+
self.enableFileWatching(True)
|
|
1122
|
+
return True
|
|
1123
|
+
return False
|
|
1124
|
+
|
|
1125
|
+
def selectProjectItem(self):
|
|
1126
|
+
window = self.window()
|
|
1127
|
+
if window:
|
|
1128
|
+
window.selectProjectItem(self.filename())
|
|
1129
|
+
|
|
1130
|
+
def selectionBackgroundColor(self):
|
|
1131
|
+
return self._selectionBackgroundColor
|
|
1132
|
+
|
|
1133
|
+
def setSelectionBackgroundColor(self, color):
|
|
1134
|
+
self._selectionBackgroundColor = color
|
|
1135
|
+
super(DocumentEditor, self).setSelectionBackgroundColor(color)
|
|
1136
|
+
|
|
1137
|
+
def selectionForegroundColor(self):
|
|
1138
|
+
return self._selectionForegroundColor
|
|
1139
|
+
|
|
1140
|
+
def setSelectionForegroundColor(self, color):
|
|
1141
|
+
self._selectionForegroundColor = color
|
|
1142
|
+
super(DocumentEditor, self).setSelectionForegroundColor(color)
|
|
1143
|
+
|
|
1144
|
+
def selection_is_word(self):
|
|
1145
|
+
"""Checks if the current selection is a single word.
|
|
1146
|
+
|
|
1147
|
+
Returns:
|
|
1148
|
+
bool: The selected text is a single word.
|
|
1149
|
+
"""
|
|
1150
|
+
sel = self.getSelection()
|
|
1151
|
+
start = self.positionFromLineIndex(*sel[:2])
|
|
1152
|
+
end = self.positionFromLineIndex(*sel[2:])
|
|
1153
|
+
return self.is_word(start, end)
|
|
1154
|
+
|
|
1155
|
+
def is_word(self, start, end):
|
|
1156
|
+
"""Checks if the text between start and end position is a word
|
|
1157
|
+
|
|
1158
|
+
Args:
|
|
1159
|
+
start (int): Start of text offset index position.
|
|
1160
|
+
end (int): End of text offset index position.
|
|
1161
|
+
|
|
1162
|
+
Returns:
|
|
1163
|
+
bool: The text between the start and end position is a single word.
|
|
1164
|
+
"""
|
|
1165
|
+
if start == end:
|
|
1166
|
+
return False
|
|
1167
|
+
# Get the word at the start of selection, if the selection doesn't match
|
|
1168
|
+
# its not a word.
|
|
1169
|
+
start_pos = self.SendScintilla(self.SCI_WORDSTARTPOSITION, start, True)
|
|
1170
|
+
end_pos = self.SendScintilla(self.SCI_WORDENDPOSITION, start, True)
|
|
1171
|
+
|
|
1172
|
+
return start == start_pos and end == end_pos
|
|
1173
|
+
|
|
1174
|
+
def setLanguage(self, language):
|
|
1175
|
+
if language == 'Plain Text':
|
|
1176
|
+
language = ''
|
|
1177
|
+
# grab the language from the lang module if it is a string
|
|
1178
|
+
if type(language) != lang.Language:
|
|
1179
|
+
language = str(language)
|
|
1180
|
+
language = lang.byName(language)
|
|
1181
|
+
|
|
1182
|
+
# collect the language's lexer
|
|
1183
|
+
if language:
|
|
1184
|
+
lexer = language.createLexer(self)
|
|
1185
|
+
self._language = language.name()
|
|
1186
|
+
else:
|
|
1187
|
+
lexer = None
|
|
1188
|
+
self._language = ''
|
|
1189
|
+
|
|
1190
|
+
# set the lexer & init the settings
|
|
1191
|
+
self.setLexer(lexer)
|
|
1192
|
+
self.initSettings()
|
|
1193
|
+
|
|
1194
|
+
# Add language keywords to aspell session dictionary
|
|
1195
|
+
if self.spellCheckEnabled():
|
|
1196
|
+
self.delayable_engine.delayables['spell_check'].reset_session(self)
|
|
1197
|
+
|
|
1198
|
+
def setLexer(self, lexer):
|
|
1199
|
+
font = self.documentFont
|
|
1200
|
+
if lexer:
|
|
1201
|
+
font = lexer.font(0)
|
|
1202
|
+
# Backup values destroyed when we set the lexer
|
|
1203
|
+
marginFont = self.marginsFont()
|
|
1204
|
+
folds = self.contractedFolds()
|
|
1205
|
+
super(DocumentEditor, self).setLexer(lexer)
|
|
1206
|
+
# Restore values destroyed when we set the lexer
|
|
1207
|
+
self.setContractedFolds(folds)
|
|
1208
|
+
self.setMarginsFont(marginFont)
|
|
1209
|
+
self.setMarginsBackgroundColor(self.marginsBackgroundColor())
|
|
1210
|
+
self.setMarginsForegroundColor(self.marginsForegroundColor())
|
|
1211
|
+
self.setFoldMarginColors(*self.foldMarginColors())
|
|
1212
|
+
self.setMatchedBraceBackgroundColor(self.matchedBraceBackgroundColor())
|
|
1213
|
+
self.setMatchedBraceForegroundColor(self.matchedBraceForegroundColor())
|
|
1214
|
+
if lexer:
|
|
1215
|
+
lexer.setColor(
|
|
1216
|
+
self.pyIndentationGuidesForegroundColor, self.STYLE_INDENTGUIDE
|
|
1217
|
+
)
|
|
1218
|
+
lexer.setPaper(
|
|
1219
|
+
self.pyIndentationGuidesBackgroundColor, self.STYLE_INDENTGUIDE
|
|
1220
|
+
)
|
|
1221
|
+
# QSciLexer.wordCharacters is not virtual, or even exposed. This hack allows
|
|
1222
|
+
# custom lexers to define their own wordCharacters
|
|
1223
|
+
if hasattr(lexer, 'wordCharactersOverride'):
|
|
1224
|
+
wordCharacters = lexer.wordCharactersOverride
|
|
1225
|
+
else:
|
|
1226
|
+
# We can't query the lexer for its word characters, but we can query the
|
|
1227
|
+
# document. This ensures the lexer's wordCharacters are used if switching
|
|
1228
|
+
# from a wordCharactersOverride lexer to a lexer that doesn't define custom
|
|
1229
|
+
# wordCharacters.
|
|
1230
|
+
wordCharacters = self.wordCharacters()
|
|
1231
|
+
self.SendScintilla(self.SCI_SETWORDCHARS, wordCharacters.encode('utf8'))
|
|
1232
|
+
|
|
1233
|
+
if lexer:
|
|
1234
|
+
lexer.setFont(font)
|
|
1235
|
+
else:
|
|
1236
|
+
self.setFont(font)
|
|
1237
|
+
|
|
1238
|
+
def setLineMarginWidth(self, width):
|
|
1239
|
+
self.setMarginWidth(self.SymbolMargin, width)
|
|
1240
|
+
|
|
1241
|
+
def setMarginsFont(self, font):
|
|
1242
|
+
super(DocumentEditor, self).setMarginsFont(font)
|
|
1243
|
+
self._marginsFont = font
|
|
1244
|
+
|
|
1245
|
+
def setMultipleSelection(self, state):
|
|
1246
|
+
"""Enables or disables multiple selection
|
|
1247
|
+
|
|
1248
|
+
Args:
|
|
1249
|
+
state (bool): Enable or disable multiple selection. When multiple
|
|
1250
|
+
selection is disabled, it is not possible to select multiple
|
|
1251
|
+
ranges by holding down the Ctrl key while dragging with the
|
|
1252
|
+
mouse.
|
|
1253
|
+
"""
|
|
1254
|
+
self.SendScintilla(self.SCI_SETMULTIPLESELECTION, state)
|
|
1255
|
+
|
|
1256
|
+
def setMultipleSelectionAdditionalSelectionTyping(self, state):
|
|
1257
|
+
"""Enables or disables multiple selection allows additional typing.
|
|
1258
|
+
|
|
1259
|
+
Args:
|
|
1260
|
+
state (bool): Whether typing, new line, cursor left/right/up/down,
|
|
1261
|
+
backspace, delete, home, and end work with multiple selections
|
|
1262
|
+
simultaneously. Also allows selection and word and line
|
|
1263
|
+
deletion commands.
|
|
1264
|
+
"""
|
|
1265
|
+
self.SendScintilla(self.SCI_SETADDITIONALSELECTIONTYPING, state)
|
|
1266
|
+
|
|
1267
|
+
def setMultipleSelectionMultiPaste(self, state):
|
|
1268
|
+
"""Enables or disables multiple selection allows additional typing.
|
|
1269
|
+
|
|
1270
|
+
Args:
|
|
1271
|
+
state (int): When pasting into multiple selections, the pasted text
|
|
1272
|
+
can go into just the main selection with self.SC_MULTIPASTE_ONCE or
|
|
1273
|
+
into each selection with self.SC_MULTIPASTE_EACH.
|
|
1274
|
+
self.SC_MULTIPASTE_ONCE is the default.
|
|
1275
|
+
"""
|
|
1276
|
+
self.SendScintilla(self.SCI_SETMULTIPASTE, state)
|
|
1277
|
+
|
|
1278
|
+
def setSmartHighlightingRegEx(
|
|
1279
|
+
self, exp=r'[ \t\n\r\.,?;:!()\[\]+\-\*\/#@^%$"\\~&{}|=<>\']'
|
|
1280
|
+
):
|
|
1281
|
+
"""Set the regular expression used to control if a selection is considered
|
|
1282
|
+
valid for smart highlighting.
|
|
1283
|
+
|
|
1284
|
+
Args:
|
|
1285
|
+
exp (str):
|
|
1286
|
+
"""
|
|
1287
|
+
self._smartHighlightingRegEx = exp
|
|
1288
|
+
self.selectionValidator = re.compile(exp)
|
|
1289
|
+
|
|
1290
|
+
def setShowFolding(self, state):
|
|
1291
|
+
if state:
|
|
1292
|
+
self.setFolding(self.BoxedTreeFoldStyle)
|
|
1293
|
+
else:
|
|
1294
|
+
self.setFolding(self.NoFoldStyle)
|
|
1295
|
+
|
|
1296
|
+
def setShowLineNumbers(self, state):
|
|
1297
|
+
self.setMarginLineNumbers(self.SymbolMargin, state)
|
|
1298
|
+
|
|
1299
|
+
def setShowSmartHighlighting(self, state):
|
|
1300
|
+
self.delayable_engine.set_delayable_enabled('smart_highlight', state)
|
|
1301
|
+
|
|
1302
|
+
def setShowWhitespaces(self, state):
|
|
1303
|
+
if state:
|
|
1304
|
+
self.setWhitespaceVisibility(QsciScintilla.WsVisible)
|
|
1305
|
+
else:
|
|
1306
|
+
self.setWhitespaceVisibility(QsciScintilla.WsInvisible)
|
|
1307
|
+
|
|
1308
|
+
def spellCheckEnabled(self):
|
|
1309
|
+
"""Is spellcheck is enabled for this document."""
|
|
1310
|
+
return self.delayable_engine.delayable_enabled('spell_check')
|
|
1311
|
+
|
|
1312
|
+
def setSpellCheckEnabled(self, state):
|
|
1313
|
+
"""Enable/disable spellcheck if spellcheck can be enabled.
|
|
1314
|
+
This changes spellcheck for all documents attached to this
|
|
1315
|
+
documents delayable_engine.
|
|
1316
|
+
"""
|
|
1317
|
+
self.delayable_engine.set_delayable_enabled('spell_check', state)
|
|
1318
|
+
|
|
1319
|
+
def addWordToDict(self, word):
|
|
1320
|
+
self.__speller__.addtoPersonal(word)
|
|
1321
|
+
self.__speller__.saveAllwords()
|
|
1322
|
+
self.spellCheck(0, None)
|
|
1323
|
+
self.pos += len(word)
|
|
1324
|
+
self.SendScintilla(self.SCI_GOTOPOS, self.pos)
|
|
1325
|
+
|
|
1326
|
+
def correctSpelling(self, action):
|
|
1327
|
+
self.SendScintilla(self.SCI_GOTOPOS, self.pos)
|
|
1328
|
+
self.SendScintilla(self.SCI_SETANCHOR, self.anchor)
|
|
1329
|
+
with undo_step(self):
|
|
1330
|
+
self.SendScintilla(self.SCI_REPLACESEL, action.text())
|
|
1331
|
+
|
|
1332
|
+
def spellCheck(self, start_pos, end_pos):
|
|
1333
|
+
"""Check spelling for some text in the document.
|
|
1334
|
+
|
|
1335
|
+
Args:
|
|
1336
|
+
start_pos (int): The document position to start spell checking.
|
|
1337
|
+
end_pos (int): The document position to stop spell checking.
|
|
1338
|
+
|
|
1339
|
+
Returns:
|
|
1340
|
+
int: Returns 0 if spell check is finished. 1 if additional
|
|
1341
|
+
processing is scheduled. 2 if the spell check was canceled
|
|
1342
|
+
because the widget is not visible.
|
|
1343
|
+
"""
|
|
1344
|
+
self.delayable_engine.enqueue(self, 'spell_check', start_pos, end_pos)
|
|
1345
|
+
|
|
1346
|
+
def onTextModified(
|
|
1347
|
+
self,
|
|
1348
|
+
pos,
|
|
1349
|
+
mtype,
|
|
1350
|
+
text,
|
|
1351
|
+
length,
|
|
1352
|
+
linesAdded,
|
|
1353
|
+
line,
|
|
1354
|
+
foldNow,
|
|
1355
|
+
foldPrev,
|
|
1356
|
+
token,
|
|
1357
|
+
annotationLinesAdded,
|
|
1358
|
+
):
|
|
1359
|
+
if self.spellCheckEnabled() and (
|
|
1360
|
+
(mtype & self.SC_MOD_INSERTTEXT) == self.SC_MOD_INSERTTEXT
|
|
1361
|
+
or (mtype & self.SC_MOD_DELETETEXT) == self.SC_MOD_DELETETEXT
|
|
1362
|
+
):
|
|
1363
|
+
# Only spell-check if text was inserted/deleted
|
|
1364
|
+
line = self.SendScintilla(self.SCI_LINEFROMPOSITION, pos)
|
|
1365
|
+
# More than one line could have been inserted.
|
|
1366
|
+
# If this number is negative it will cause Qt to crash.
|
|
1367
|
+
lines_to_check = line + max(0, linesAdded)
|
|
1368
|
+
self.spellCheck(
|
|
1369
|
+
self.SendScintilla(self.SCI_POSITIONFROMLINE, line),
|
|
1370
|
+
self.SendScintilla(self.SCI_GETLINEENDPOSITION, lines_to_check),
|
|
1371
|
+
)
|
|
1372
|
+
|
|
1373
|
+
def showAutoComplete(self, toggle=False):
|
|
1374
|
+
# if using autoComplete toggle the autoComplete list
|
|
1375
|
+
if self.autoCompletionSource() == QsciScintilla.AcsAll:
|
|
1376
|
+
if self.isListActive(): # is the autoComplete list visible
|
|
1377
|
+
if toggle:
|
|
1378
|
+
self.cancelList() # Close the autoComplete list
|
|
1379
|
+
else:
|
|
1380
|
+
self.autoCompleteFromAll() # Show the autoComplete list
|
|
1381
|
+
|
|
1382
|
+
def showMenu(self, pos, popup=True):
|
|
1383
|
+
menu = QMenu(self)
|
|
1384
|
+
pos = self.mapToGlobal(pos)
|
|
1385
|
+
self._clickPos = pos
|
|
1386
|
+
|
|
1387
|
+
if self.spellCheckEnabled():
|
|
1388
|
+
# Get the word under the mouse and split the word if camelCase
|
|
1389
|
+
point = self.mapFromGlobal(self._clickPos)
|
|
1390
|
+
x = point.x()
|
|
1391
|
+
y = point.y()
|
|
1392
|
+
wordUnderMouse = self.wordAtPoint(point)
|
|
1393
|
+
positionMouse = self.SendScintilla(self.SCI_POSITIONFROMPOINT, x, y)
|
|
1394
|
+
wordStartPosition = self.SendScintilla(
|
|
1395
|
+
self.SCI_WORDSTARTPOSITION, positionMouse, True
|
|
1396
|
+
)
|
|
1397
|
+
spell_check = self.delayable_engine.delayables['spell_check']
|
|
1398
|
+
results = spell_check.chunk_re.findall(
|
|
1399
|
+
self.text(wordStartPosition, wordStartPosition + len(wordUnderMouse))
|
|
1400
|
+
)
|
|
1401
|
+
|
|
1402
|
+
for space, wordChunk in results:
|
|
1403
|
+
camel_case_words = spell_check.camel_case_split(wordChunk)
|
|
1404
|
+
lengthSpace = len(space)
|
|
1405
|
+
for word in camel_case_words:
|
|
1406
|
+
lengthWord = len(word)
|
|
1407
|
+
# Calcualate the actual word start position accounting for any
|
|
1408
|
+
# non-alpha chars word_new_start_position = wordStartPosition +
|
|
1409
|
+
# lengthSpace
|
|
1410
|
+
if (
|
|
1411
|
+
wordStartPosition + lengthSpace <= positionMouse
|
|
1412
|
+
and wordStartPosition + lengthSpace + lengthWord > positionMouse
|
|
1413
|
+
and not any(letter in string.digits for letter in word)
|
|
1414
|
+
and not self.__speller__.check(word)
|
|
1415
|
+
):
|
|
1416
|
+
# For camelCase words, get the exact word under the mouse
|
|
1417
|
+
self.pos = wordStartPosition + lengthSpace
|
|
1418
|
+
self.anchor = wordStartPosition + lengthSpace + lengthWord
|
|
1419
|
+
# Add spelling suggestions to menu
|
|
1420
|
+
submenu = menu.addMenu(word)
|
|
1421
|
+
submenu.setObjectName('uiSpellCheckMENU')
|
|
1422
|
+
wordSuggestionList = self.__speller__.suggest(word)
|
|
1423
|
+
for wordSuggestion in wordSuggestionList:
|
|
1424
|
+
act = submenu.addAction(wordSuggestion)
|
|
1425
|
+
submenu.triggered.connect(self.correctSpelling)
|
|
1426
|
+
addmenu = menu.addAction('Add %s to dictionary' % word)
|
|
1427
|
+
addmenu.triggered.connect(partial(self.addWordToDict, word))
|
|
1428
|
+
addmenu.setObjectName('uiSpellCheckAddWordACT')
|
|
1429
|
+
menu.addSeparator()
|
|
1430
|
+
break
|
|
1431
|
+
else:
|
|
1432
|
+
wordStartPosition += lengthWord
|
|
1433
|
+
wordStartPosition += lengthSpace
|
|
1434
|
+
|
|
1435
|
+
act = menu.addAction('Goto')
|
|
1436
|
+
# act.setShortcut('Ctrl+G')
|
|
1437
|
+
act.triggered.connect(self.goToLine)
|
|
1438
|
+
act.setIcon(QIcon(resourcePath('img/skip-next-outline.png')))
|
|
1439
|
+
act = menu.addAction('Go to Definition')
|
|
1440
|
+
# act.setShortcut('Ctrl+Shift+G')
|
|
1441
|
+
act.triggered.connect(self.goToDefinition)
|
|
1442
|
+
act.setIcon(QIcon(resourcePath('img/skip-forward-outline.png')))
|
|
1443
|
+
if self.showSmartHighlighting():
|
|
1444
|
+
act = menu.addAction('Edit PermaHighlight')
|
|
1445
|
+
act.setIcon(QIcon(resourcePath('img/marker.png')))
|
|
1446
|
+
act.triggered.connect(self.editPermaHighlight)
|
|
1447
|
+
|
|
1448
|
+
menu.addSeparator()
|
|
1449
|
+
|
|
1450
|
+
act = menu.addAction('Collapse/Expand All')
|
|
1451
|
+
act.triggered.connect(self.toggleFolding)
|
|
1452
|
+
act.setIcon(QIcon(resourcePath('img/plus-minus-variant.png')))
|
|
1453
|
+
|
|
1454
|
+
menu.addSeparator()
|
|
1455
|
+
|
|
1456
|
+
act = menu.addAction('Cut')
|
|
1457
|
+
act.triggered.connect(self.cut)
|
|
1458
|
+
act.setShortcut('Ctrl+X')
|
|
1459
|
+
act.setIcon(QIcon(resourcePath('img/content-cut.png')))
|
|
1460
|
+
|
|
1461
|
+
act = menu.addAction('Copy')
|
|
1462
|
+
act.triggered.connect(self.copy)
|
|
1463
|
+
act.setShortcut('Ctrl+C')
|
|
1464
|
+
act.setIcon(QIcon(resourcePath('img/content-copy.png')))
|
|
1465
|
+
|
|
1466
|
+
copyMenu = menu.addMenu('Advanced Copy')
|
|
1467
|
+
|
|
1468
|
+
# Note: I cant use the actions defined above because they end up getting garbage
|
|
1469
|
+
# collected
|
|
1470
|
+
iconlstrip = QIcon(resourcePath('img/content-duplicate.png'))
|
|
1471
|
+
act = QAction(iconlstrip, 'Copy lstrip', copyMenu)
|
|
1472
|
+
act.setShortcut('Ctrl+Shift+C')
|
|
1473
|
+
act.triggered.connect(self.copyLstrip)
|
|
1474
|
+
copyMenu.addAction(act)
|
|
1475
|
+
|
|
1476
|
+
icon = QIcon(resourcePath('img/content-copy.png'))
|
|
1477
|
+
act = QAction(icon, 'Copy Tabs to Spaces', copyMenu)
|
|
1478
|
+
act.setShortcut('Ctrl+Shift+Space')
|
|
1479
|
+
act.triggered.connect(self.copySpaceIndentation)
|
|
1480
|
+
copyMenu.addAction(act)
|
|
1481
|
+
|
|
1482
|
+
act = menu.addAction('Paste')
|
|
1483
|
+
act.triggered.connect(self.paste)
|
|
1484
|
+
act.setShortcut('Ctrl+V')
|
|
1485
|
+
act.setIcon(QIcon(resourcePath('img/content-paste.png')))
|
|
1486
|
+
|
|
1487
|
+
menu.addSeparator()
|
|
1488
|
+
|
|
1489
|
+
act = menu.addAction('Copy Line Reference')
|
|
1490
|
+
act.triggered.connect(self.copyLineReference)
|
|
1491
|
+
act.setIcon(QIcon(resourcePath('img/content-copy.png')))
|
|
1492
|
+
|
|
1493
|
+
menu.addSeparator()
|
|
1494
|
+
|
|
1495
|
+
act = menu.addAction('Comment Toggle')
|
|
1496
|
+
act.triggered.connect(self.commentToggle)
|
|
1497
|
+
act.setShortcut("Ctrl+/")
|
|
1498
|
+
act.setIcon(QIcon(resourcePath('img/comment-edit.png')))
|
|
1499
|
+
|
|
1500
|
+
menu.addSeparator()
|
|
1501
|
+
|
|
1502
|
+
act = menu.addAction('To Lowercase')
|
|
1503
|
+
act.triggered.connect(self.toLower)
|
|
1504
|
+
# act.setShortcut('Ctrl+L')
|
|
1505
|
+
act.setIcon(QIcon(resourcePath('img/format-letter-case-lower.png')))
|
|
1506
|
+
act = menu.addAction('To Uppercase')
|
|
1507
|
+
act.triggered.connect(self.toUpper)
|
|
1508
|
+
# act.setShortcut('Ctrl+U')
|
|
1509
|
+
act.setIcon(QIcon(resourcePath('img/format-letter-case-upper.png')))
|
|
1510
|
+
|
|
1511
|
+
menu.addSeparator()
|
|
1512
|
+
|
|
1513
|
+
submenu = menu.addMenu('View as...')
|
|
1514
|
+
submenu.setIcon(QIcon(resourcePath('img/eye-check.png')))
|
|
1515
|
+
lg = self.language()
|
|
1516
|
+
act = submenu.addAction('Plain Text')
|
|
1517
|
+
if lg == "":
|
|
1518
|
+
act.setIcon(QIcon(resourcePath('img/check-bold.png')))
|
|
1519
|
+
submenu.addSeparator()
|
|
1520
|
+
|
|
1521
|
+
for language in lang.languages():
|
|
1522
|
+
act = submenu.addAction(language)
|
|
1523
|
+
if language == lg:
|
|
1524
|
+
act.setIcon(QIcon(resourcePath('img/check-bold.png')))
|
|
1525
|
+
|
|
1526
|
+
submenu.triggered.connect(self.languageChosen)
|
|
1527
|
+
|
|
1528
|
+
menu.addSeparator()
|
|
1529
|
+
|
|
1530
|
+
act = menu.addAction('Indent using tabs')
|
|
1531
|
+
act.triggered.connect(self.setIndentationsUseTabs)
|
|
1532
|
+
act.setCheckable(True)
|
|
1533
|
+
act.setChecked(self.indentationsUseTabs())
|
|
1534
|
+
|
|
1535
|
+
if self._fileMonitoringActive:
|
|
1536
|
+
act = menu.addAction('Auto Reload file')
|
|
1537
|
+
act.triggered.connect(self.setAutoReloadOnChange)
|
|
1538
|
+
act.setCheckable(True)
|
|
1539
|
+
act.setChecked(self._autoReloadOnChange)
|
|
1540
|
+
|
|
1541
|
+
if popup:
|
|
1542
|
+
menu.popup(self._clickPos)
|
|
1543
|
+
return menu
|
|
1544
|
+
|
|
1545
|
+
def showEvent(self, event):
|
|
1546
|
+
super(DocumentEditor, self).showEvent(event)
|
|
1547
|
+
# Update the colorScheme after the stylesheet has been fully loaded.
|
|
1548
|
+
self.updateColorScheme()
|
|
1549
|
+
|
|
1550
|
+
def showFolding(self):
|
|
1551
|
+
return self.folding() != self.NoFoldStyle
|
|
1552
|
+
|
|
1553
|
+
def showLineNumbers(self):
|
|
1554
|
+
return self.marginLineNumbers(self.SymbolMargin)
|
|
1555
|
+
|
|
1556
|
+
def showSmartHighlighting(self):
|
|
1557
|
+
return self.delayable_engine.delayable_enabled('smart_highlight')
|
|
1558
|
+
|
|
1559
|
+
def showWhitespaces(self):
|
|
1560
|
+
return self.whitespaceVisibility() == QsciScintilla.WsVisible
|
|
1561
|
+
|
|
1562
|
+
def smartHighlightingRegEx(self):
|
|
1563
|
+
return self._smartHighlightingRegEx
|
|
1564
|
+
|
|
1565
|
+
def toLower(self):
|
|
1566
|
+
with undo_step(self):
|
|
1567
|
+
lineFrom, indexFrom, lineTo, indexTo = self.getSelection()
|
|
1568
|
+
text = self.selectedText().lower()
|
|
1569
|
+
self.removeSelectedText()
|
|
1570
|
+
self.insert(text)
|
|
1571
|
+
self.setSelection(lineFrom, indexFrom, lineTo, indexTo)
|
|
1572
|
+
|
|
1573
|
+
def toggleFolding(self):
|
|
1574
|
+
self.foldAll(QApplication.instance().keyboardModifiers() == Qt.ShiftModifier)
|
|
1575
|
+
|
|
1576
|
+
def toUpper(self):
|
|
1577
|
+
with undo_step(self):
|
|
1578
|
+
lineFrom, indexFrom, lineTo, indexTo = self.getSelection()
|
|
1579
|
+
text = self.selectedText().upper()
|
|
1580
|
+
self.removeSelectedText()
|
|
1581
|
+
self.insert(text)
|
|
1582
|
+
self.setSelection(lineFrom, indexFrom, lineTo, indexTo)
|
|
1583
|
+
|
|
1584
|
+
def updateColorScheme(self):
|
|
1585
|
+
"""Sets the DocumentEditor's lexer colors, see colorScheme for a compatible
|
|
1586
|
+
dict
|
|
1587
|
+
"""
|
|
1588
|
+
# lookup the language
|
|
1589
|
+
language = lang.byName(self.language())
|
|
1590
|
+
lex = self.lexer()
|
|
1591
|
+
if not lex:
|
|
1592
|
+
self.setPaper(self.paperDefault)
|
|
1593
|
+
self.setColor(self.colorDefault)
|
|
1594
|
+
return
|
|
1595
|
+
# Backup the lexer font. The calls to setPaper/setColor cause it to be reset.
|
|
1596
|
+
font = lex.font(0)
|
|
1597
|
+
# Set Default lexer colors
|
|
1598
|
+
for i in range(128):
|
|
1599
|
+
lex.setPaper(self.paperDefault, i)
|
|
1600
|
+
lex.setColor(self.colorDefault, i)
|
|
1601
|
+
lex.setDefaultPaper(self.paperDefault)
|
|
1602
|
+
lex.setDefaultColor(self.colorDefault)
|
|
1603
|
+
# Override lexer color/paper values
|
|
1604
|
+
if language:
|
|
1605
|
+
_lexerColorNames = set(
|
|
1606
|
+
[
|
|
1607
|
+
x.replace('color', '')
|
|
1608
|
+
for x in dir(self)
|
|
1609
|
+
if x.startswith('color') and x.replace('color', '')
|
|
1610
|
+
]
|
|
1611
|
+
)
|
|
1612
|
+
for colorName, keys in language.lexerColorTypes().items():
|
|
1613
|
+
color = None
|
|
1614
|
+
paper = None
|
|
1615
|
+
if colorName == 'misc':
|
|
1616
|
+
color = self.colorDefault
|
|
1617
|
+
paper = self.paperDefault
|
|
1618
|
+
else:
|
|
1619
|
+
for name in _lexerColorNames:
|
|
1620
|
+
if name.lower() == colorName:
|
|
1621
|
+
color = getattr(self, 'color{}'.format(name))
|
|
1622
|
+
paper = getattr(self, 'paper{}'.format(name))
|
|
1623
|
+
break
|
|
1624
|
+
for key in keys:
|
|
1625
|
+
if paper:
|
|
1626
|
+
lex.setPaper(paper, key)
|
|
1627
|
+
if color:
|
|
1628
|
+
lex.setColor(color, key)
|
|
1629
|
+
lex.setColor(self.braceBadForeground, self.STYLE_BRACEBAD)
|
|
1630
|
+
lex.setPaper(self.braceBadBackground, self.STYLE_BRACEBAD)
|
|
1631
|
+
lex.setColor(self.pyIndentationGuidesForegroundColor, self.STYLE_INDENTGUIDE)
|
|
1632
|
+
lex.setPaper(self.pyIndentationGuidesBackgroundColor, self.STYLE_INDENTGUIDE)
|
|
1633
|
+
# Update other values stored in the lexer
|
|
1634
|
+
self.setFoldMarginColors(
|
|
1635
|
+
self.foldMarginsForegroundColor, self.foldMarginsBackgroundColor
|
|
1636
|
+
)
|
|
1637
|
+
self.setMarginsBackgroundColor(self.marginsBackgroundColor())
|
|
1638
|
+
self.setMarginsForegroundColor(self.marginsForegroundColor())
|
|
1639
|
+
self.setFoldMarginColors(*self.foldMarginColors())
|
|
1640
|
+
self.setMatchedBraceBackgroundColor(self.matchedBraceBackgroundColor())
|
|
1641
|
+
self.setMatchedBraceForegroundColor(self.matchedBraceForegroundColor())
|
|
1642
|
+
# Restore the existing font
|
|
1643
|
+
lex.setFont(font, 0)
|
|
1644
|
+
|
|
1645
|
+
def updateFilename(self, filename):
|
|
1646
|
+
filename = str(filename)
|
|
1647
|
+
extension = os.path.splitext(filename)[1]
|
|
1648
|
+
|
|
1649
|
+
# determine if we need to modify the language
|
|
1650
|
+
if not self._filename or extension != os.path.splitext(self._filename)[1]:
|
|
1651
|
+
self.setLanguage(lang.byExtension(extension))
|
|
1652
|
+
|
|
1653
|
+
# update the filename information
|
|
1654
|
+
self._filename = os.path.abspath(filename)
|
|
1655
|
+
self.setModified(False)
|
|
1656
|
+
|
|
1657
|
+
try:
|
|
1658
|
+
self.window().emitDocumentTitleChanged()
|
|
1659
|
+
except Exception:
|
|
1660
|
+
pass
|
|
1661
|
+
|
|
1662
|
+
self.refreshTitle()
|
|
1663
|
+
|
|
1664
|
+
def updateHighlighter(self):
|
|
1665
|
+
# Get selection
|
|
1666
|
+
selectedText = self.selectedText()
|
|
1667
|
+
# if text is selected make sure it is a word
|
|
1668
|
+
lexer = self.lexer()
|
|
1669
|
+
if selectedText != lexer.highlightedKeywords:
|
|
1670
|
+
if selectedText:
|
|
1671
|
+
validator = self.selectionValidator
|
|
1672
|
+
if hasattr(lexer, 'selectionValidator'):
|
|
1673
|
+
# If a lexer has defined its own selectionValidator use that instead
|
|
1674
|
+
validator = lexer.selectionValidator
|
|
1675
|
+
# Does the text contain a non allowed word?
|
|
1676
|
+
if not validator.findall(selectedText) == []:
|
|
1677
|
+
return
|
|
1678
|
+
else:
|
|
1679
|
+
selection = self.getSelection()
|
|
1680
|
+
# the character before and after the selection must not be a word.
|
|
1681
|
+
text = self.text(selection[2]) # Character after
|
|
1682
|
+
if selection[3] < len(text):
|
|
1683
|
+
if validator.findall(text[selection[3]]) == []:
|
|
1684
|
+
return
|
|
1685
|
+
text = self.text(selection[0]) # Character Before
|
|
1686
|
+
if selection[1] and selection[1] != -1:
|
|
1687
|
+
if validator.findall(text[selection[1] - 1]) == []:
|
|
1688
|
+
return
|
|
1689
|
+
|
|
1690
|
+
def updateSelectionInfo(self):
|
|
1691
|
+
window = self.window()
|
|
1692
|
+
if window and hasattr(window, 'uiCursorInfoLBL'):
|
|
1693
|
+
sline, spos, eline, epos = self.getSelection()
|
|
1694
|
+
# Add 1 to line numbers because document line numbers are 1 based
|
|
1695
|
+
text = ''
|
|
1696
|
+
if sline == -1:
|
|
1697
|
+
line, pos = self.getCursorPosition()
|
|
1698
|
+
line += 1
|
|
1699
|
+
text = 'Line: {} Pos: {}'.format(line, pos)
|
|
1700
|
+
else:
|
|
1701
|
+
sline += 1
|
|
1702
|
+
eline += 1
|
|
1703
|
+
text = (
|
|
1704
|
+
'Line: {sline} Pos: {spos} To Line: {eline} '
|
|
1705
|
+
'Pos: {epos} Line Count: {lineCount}'
|
|
1706
|
+
)
|
|
1707
|
+
text = text.format(
|
|
1708
|
+
sline=sline,
|
|
1709
|
+
spos=spos,
|
|
1710
|
+
eline=eline,
|
|
1711
|
+
epos=epos,
|
|
1712
|
+
lineCount=eline - sline + 1,
|
|
1713
|
+
)
|
|
1714
|
+
if self._textCodec and self._textCodec.name() != 'System':
|
|
1715
|
+
text = 'Encoding: {enc} {text}'.format(
|
|
1716
|
+
enc=self._textCodec.name(), text=text
|
|
1717
|
+
)
|
|
1718
|
+
window.uiCursorInfoLBL.setText(text)
|
|
1719
|
+
|
|
1720
|
+
def setAutoReloadOnChange(self, state):
|
|
1721
|
+
self._autoReloadOnChange = state
|
|
1722
|
+
|
|
1723
|
+
def indentSelection(self, all=False):
|
|
1724
|
+
if all:
|
|
1725
|
+
lineFrom = 0
|
|
1726
|
+
lineTo = self.lines()
|
|
1727
|
+
else:
|
|
1728
|
+
lineFrom, indexFrom, lineTo, indextTo = self.getSelection()
|
|
1729
|
+
with undo_step(self):
|
|
1730
|
+
for line in range(lineFrom, lineTo + 1):
|
|
1731
|
+
self.indent(line)
|
|
1732
|
+
|
|
1733
|
+
def unindentSelection(self, all=False):
|
|
1734
|
+
if all:
|
|
1735
|
+
lineFrom = 0
|
|
1736
|
+
lineTo = self.lines()
|
|
1737
|
+
else:
|
|
1738
|
+
lineFrom, indexFrom, lineTo, indextTo = self.getSelection()
|
|
1739
|
+
with undo_step(self):
|
|
1740
|
+
for line in range(lineFrom, lineTo + 1):
|
|
1741
|
+
self.unindent(line)
|
|
1742
|
+
|
|
1743
|
+
def windowTitle(self):
|
|
1744
|
+
if self._filename:
|
|
1745
|
+
title = os.path.basename(self._filename)
|
|
1746
|
+
else:
|
|
1747
|
+
title = 'New Document'
|
|
1748
|
+
|
|
1749
|
+
if self.isModified():
|
|
1750
|
+
title += '*'
|
|
1751
|
+
|
|
1752
|
+
if self.additionalFilenames:
|
|
1753
|
+
title = '[{}]'.format(title)
|
|
1754
|
+
|
|
1755
|
+
return title
|
|
1756
|
+
|
|
1757
|
+
def wheelEvent(self, event):
|
|
1758
|
+
if self._enableFontResizing and event.modifiers() == Qt.ControlModifier:
|
|
1759
|
+
# If used in LoggerWindow, use that wheel event
|
|
1760
|
+
# May not want to import LoggerWindow, so perhaps
|
|
1761
|
+
# check by str(type())
|
|
1762
|
+
# if isinstance(self.window(), "LoggerWindow"):
|
|
1763
|
+
if "LoggerWindow" in str(type(self.window())):
|
|
1764
|
+
self.window().wheelEvent(event)
|
|
1765
|
+
return
|
|
1766
|
+
|
|
1767
|
+
font = self.documentFont
|
|
1768
|
+
marginsFont = self.marginsFont()
|
|
1769
|
+
lexer = self.lexer()
|
|
1770
|
+
if lexer:
|
|
1771
|
+
font = lexer.font(0)
|
|
1772
|
+
try:
|
|
1773
|
+
# Qt5 support
|
|
1774
|
+
delta = event.angleDelta().y()
|
|
1775
|
+
except Exception:
|
|
1776
|
+
# Qt4 support
|
|
1777
|
+
delta = event.delta()
|
|
1778
|
+
if delta > 0:
|
|
1779
|
+
font.setPointSize(font.pointSize() + 1)
|
|
1780
|
+
marginsFont.setPointSize(marginsFont.pointSize() + 1)
|
|
1781
|
+
else:
|
|
1782
|
+
if font.pointSize() - 1 > 0:
|
|
1783
|
+
font.setPointSize(font.pointSize() - 1)
|
|
1784
|
+
if marginsFont.pointSize() - 1 > 0:
|
|
1785
|
+
marginsFont.setPointSize(marginsFont.pointSize() - 1)
|
|
1786
|
+
|
|
1787
|
+
self.setMarginsFont(marginsFont)
|
|
1788
|
+
if lexer:
|
|
1789
|
+
lexer.setFont(font)
|
|
1790
|
+
else:
|
|
1791
|
+
self.setFont(font)
|
|
1792
|
+
|
|
1793
|
+
self.fontsChanged.emit(font, marginsFont)
|
|
1794
|
+
event.accept()
|
|
1795
|
+
else:
|
|
1796
|
+
super(DocumentEditor, self).wheelEvent(event)
|
|
1797
|
+
|
|
1798
|
+
# expose properties for the designer
|
|
1799
|
+
pyLanguage = Property("QString", language, setLanguage)
|
|
1800
|
+
pyLineMarginWidth = Property("int", lineMarginWidth, setLineMarginWidth)
|
|
1801
|
+
pyShowLineNumbers = Property("bool", showLineNumbers, setShowLineNumbers)
|
|
1802
|
+
pyShowFolding = Property("bool", showFolding, setShowFolding)
|
|
1803
|
+
pyShowSmartHighlighting = Property(
|
|
1804
|
+
"bool", showSmartHighlighting, setShowSmartHighlighting
|
|
1805
|
+
)
|
|
1806
|
+
pySmartHighlightingRegEx = Property(
|
|
1807
|
+
"QString", smartHighlightingRegEx, setSmartHighlightingRegEx
|
|
1808
|
+
)
|
|
1809
|
+
|
|
1810
|
+
pyAutoCompletionCaseSensitivity = Property(
|
|
1811
|
+
"bool",
|
|
1812
|
+
QsciScintilla.autoCompletionCaseSensitivity,
|
|
1813
|
+
QsciScintilla.setAutoCompletionCaseSensitivity,
|
|
1814
|
+
)
|
|
1815
|
+
pyAutoCompletionReplaceWord = Property(
|
|
1816
|
+
"bool",
|
|
1817
|
+
QsciScintilla.autoCompletionReplaceWord,
|
|
1818
|
+
QsciScintilla.setAutoCompletionReplaceWord,
|
|
1819
|
+
)
|
|
1820
|
+
pyAutoCompletionShowSingle = Property(
|
|
1821
|
+
"bool",
|
|
1822
|
+
QsciScintilla.autoCompletionShowSingle,
|
|
1823
|
+
QsciScintilla.setAutoCompletionShowSingle,
|
|
1824
|
+
)
|
|
1825
|
+
pyAutoCompletionThreshold = Property(
|
|
1826
|
+
"int",
|
|
1827
|
+
QsciScintilla.autoCompletionThreshold,
|
|
1828
|
+
QsciScintilla.setAutoCompletionThreshold,
|
|
1829
|
+
)
|
|
1830
|
+
pyAutoIndent = Property(
|
|
1831
|
+
"bool", QsciScintilla.autoIndent, QsciScintilla.setAutoIndent
|
|
1832
|
+
)
|
|
1833
|
+
pyBackspaceUnindents = Property(
|
|
1834
|
+
"bool", QsciScintilla.backspaceUnindents, QsciScintilla.setBackspaceUnindents
|
|
1835
|
+
)
|
|
1836
|
+
pyIndentationGuides = Property(
|
|
1837
|
+
"bool", QsciScintilla.indentationGuides, QsciScintilla.setIndentationGuides
|
|
1838
|
+
)
|
|
1839
|
+
pyIndentationsUseTabs = Property(
|
|
1840
|
+
"bool", QsciScintilla.indentationsUseTabs, QsciScintilla.setIndentationsUseTabs
|
|
1841
|
+
)
|
|
1842
|
+
pyTabIndents = Property(
|
|
1843
|
+
"bool", QsciScintilla.tabIndents, QsciScintilla.setTabIndents
|
|
1844
|
+
)
|
|
1845
|
+
pyUtf8 = Property("bool", QsciScintilla.isUtf8, QsciScintilla.setUtf8)
|
|
1846
|
+
pyWhitespaceVisibility = Property(
|
|
1847
|
+
"bool",
|
|
1848
|
+
QsciScintilla.whitespaceVisibility,
|
|
1849
|
+
QsciScintilla.setWhitespaceVisibility,
|
|
1850
|
+
)
|
|
1851
|
+
|
|
1852
|
+
# Color Setters required because QSci doesn't expose getters.
|
|
1853
|
+
# --------------------------------------------------------------------------------
|
|
1854
|
+
def edgeColor(self):
|
|
1855
|
+
"""This is subclassed so we can create a Property of it"""
|
|
1856
|
+
return super(DocumentEditor, self).edgeColor()
|
|
1857
|
+
|
|
1858
|
+
def setEdgeColor(self, color):
|
|
1859
|
+
"""This is subclassed so we can create a Property of it"""
|
|
1860
|
+
super(DocumentEditor, self).setEdgeColor(color)
|
|
1861
|
+
|
|
1862
|
+
# Because foreground and background must be set together, this cant use
|
|
1863
|
+
# QtPropertyInit
|
|
1864
|
+
@Property(QColor)
|
|
1865
|
+
def foldMarginsBackgroundColor(self):
|
|
1866
|
+
return self._foldMarginBackgroundColor
|
|
1867
|
+
|
|
1868
|
+
@foldMarginsBackgroundColor.setter
|
|
1869
|
+
def foldMarginsBackgroundColor(self, color):
|
|
1870
|
+
self._foldMarginBackgroundColor = color
|
|
1871
|
+
self.setFoldMarginColors(self._foldMarginForegroundColor, color)
|
|
1872
|
+
|
|
1873
|
+
@Property(QColor)
|
|
1874
|
+
def foldMarginsForegroundColor(self):
|
|
1875
|
+
return self._foldMarginForegroundColor
|
|
1876
|
+
|
|
1877
|
+
@foldMarginsForegroundColor.setter
|
|
1878
|
+
def foldMarginsForegroundColor(self, color):
|
|
1879
|
+
self._foldMarginForegroundColor = color
|
|
1880
|
+
self.setFoldMarginColors(color, self._foldMarginBackgroundColor)
|
|
1881
|
+
|
|
1882
|
+
def indentationGuidesBackgroundColor(self):
|
|
1883
|
+
return self._indentationGuidesBackgroundColor
|
|
1884
|
+
|
|
1885
|
+
def setIndentationGuidesBackgroundColor(self, color):
|
|
1886
|
+
self._indentationGuidesBackgroundColor = color
|
|
1887
|
+
super(DocumentEditor, self).setIndentationGuidesBackgroundColor(color)
|
|
1888
|
+
|
|
1889
|
+
def indentationGuidesForegroundColor(self):
|
|
1890
|
+
return self._indentationGuidesForegroundColor
|
|
1891
|
+
|
|
1892
|
+
def setIndentationGuidesForegroundColor(self, color):
|
|
1893
|
+
self._indentationGuidesForegroundColor = color
|
|
1894
|
+
super(DocumentEditor, self).setIndentationGuidesForegroundColor(color)
|
|
1895
|
+
|
|
1896
|
+
def marginsBackgroundColor(self):
|
|
1897
|
+
return self._marginsBackgroundColor
|
|
1898
|
+
|
|
1899
|
+
def setMarginsBackgroundColor(self, color):
|
|
1900
|
+
self._marginsBackgroundColor = color
|
|
1901
|
+
super(DocumentEditor, self).setMarginsBackgroundColor(color)
|
|
1902
|
+
|
|
1903
|
+
def marginsForegroundColor(self):
|
|
1904
|
+
return self._marginsForegroundColor
|
|
1905
|
+
|
|
1906
|
+
def setMarginsForegroundColor(self, color):
|
|
1907
|
+
self._marginsForegroundColor = color
|
|
1908
|
+
super(DocumentEditor, self).setMarginsForegroundColor(color)
|
|
1909
|
+
|
|
1910
|
+
def matchedBraceBackgroundColor(self):
|
|
1911
|
+
return self._matchedBraceBackgroundColor
|
|
1912
|
+
|
|
1913
|
+
def matchedBraceForegroundColor(self):
|
|
1914
|
+
return self._matchedBraceForegroundColor
|
|
1915
|
+
|
|
1916
|
+
def setMatchedBraceBackgroundColor(self, color):
|
|
1917
|
+
self._matchedBraceBackgroundColor = color
|
|
1918
|
+
super(DocumentEditor, self).setMatchedBraceBackgroundColor(color)
|
|
1919
|
+
|
|
1920
|
+
def setMatchedBraceForegroundColor(self, color):
|
|
1921
|
+
self._matchedBraceForegroundColor = color
|
|
1922
|
+
super(DocumentEditor, self).setMatchedBraceForegroundColor(color)
|
|
1923
|
+
|
|
1924
|
+
def markerBackgroundColor(self):
|
|
1925
|
+
return self._markerBackgroundColor
|
|
1926
|
+
|
|
1927
|
+
def setMarkerBackgroundColor(self, color):
|
|
1928
|
+
self._markerBackgroundColor = color
|
|
1929
|
+
super(DocumentEditor, self).setMarkerBackgroundColor(color)
|
|
1930
|
+
|
|
1931
|
+
def markerForegroundColor(self):
|
|
1932
|
+
return self._markerForegroundColor
|
|
1933
|
+
|
|
1934
|
+
def setMarkerForegroundColor(self, color):
|
|
1935
|
+
self._markerForegroundColor = color
|
|
1936
|
+
super(DocumentEditor, self).setMarkerForegroundColor(color)
|
|
1937
|
+
|
|
1938
|
+
def unmatchedBraceBackgroundColor(self):
|
|
1939
|
+
return self._unmatchedBraceBackgroundColor
|
|
1940
|
+
|
|
1941
|
+
def setUnmatchedBraceBackgroundColor(self, color):
|
|
1942
|
+
self._unmatchedBraceBackgroundColor = color
|
|
1943
|
+
super(DocumentEditor, self).setUnmatchedBraceBackgroundColor(color)
|
|
1944
|
+
|
|
1945
|
+
def unmatchedBraceForegroundColor(self):
|
|
1946
|
+
return self._unmatchedBraceForegroundColor
|
|
1947
|
+
|
|
1948
|
+
def setUnmatchedBraceForegroundColor(self, color):
|
|
1949
|
+
self._unmatchedBraceForegroundColor = color
|
|
1950
|
+
super(DocumentEditor, self).setUnmatchedBraceForegroundColor(color)
|
|
1951
|
+
|
|
1952
|
+
# Handle Stylesheet colors for properties that are built into QsciScintilla but dont
|
|
1953
|
+
# have getters.
|
|
1954
|
+
pyMarginsBackgroundColor = Property(
|
|
1955
|
+
QColor, marginsBackgroundColor, setMarginsBackgroundColor
|
|
1956
|
+
)
|
|
1957
|
+
pyMarginsForegroundColor = Property(
|
|
1958
|
+
QColor, marginsForegroundColor, setMarginsForegroundColor
|
|
1959
|
+
)
|
|
1960
|
+
pyMatchedBraceBackgroundColor = Property(
|
|
1961
|
+
QColor, matchedBraceBackgroundColor, setMatchedBraceBackgroundColor
|
|
1962
|
+
)
|
|
1963
|
+
pyMatchedBraceForegroundColor = Property(
|
|
1964
|
+
QColor, matchedBraceForegroundColor, setMatchedBraceForegroundColor
|
|
1965
|
+
)
|
|
1966
|
+
pyCaretBackgroundColor = Property(
|
|
1967
|
+
QColor, caretBackgroundColor, setCaretLineBackgroundColor
|
|
1968
|
+
)
|
|
1969
|
+
pyCaretForegroundColor = Property(
|
|
1970
|
+
QColor, caretForegroundColor, setCaretForegroundColor
|
|
1971
|
+
)
|
|
1972
|
+
pySelectionBackgroundColor = Property(
|
|
1973
|
+
QColor, selectionBackgroundColor, setSelectionBackgroundColor
|
|
1974
|
+
)
|
|
1975
|
+
pySelectionForegroundColor = Property(
|
|
1976
|
+
QColor, selectionForegroundColor, setSelectionForegroundColor
|
|
1977
|
+
)
|
|
1978
|
+
pyIndentationGuidesBackgroundColor = Property(
|
|
1979
|
+
QColor, indentationGuidesBackgroundColor, setIndentationGuidesBackgroundColor
|
|
1980
|
+
)
|
|
1981
|
+
pyIndentationGuidesForegroundColor = Property(
|
|
1982
|
+
QColor, indentationGuidesForegroundColor, setIndentationGuidesForegroundColor
|
|
1983
|
+
)
|
|
1984
|
+
pyMarkerBackgroundColor = Property(
|
|
1985
|
+
QColor, markerBackgroundColor, setMarkerBackgroundColor
|
|
1986
|
+
)
|
|
1987
|
+
pyMarkerForegroundColor = Property(
|
|
1988
|
+
QColor, markerForegroundColor, setMarkerForegroundColor
|
|
1989
|
+
)
|
|
1990
|
+
pyUnmatchedBraceBackgroundColor = Property(
|
|
1991
|
+
QColor, unmatchedBraceBackgroundColor, setUnmatchedBraceBackgroundColor
|
|
1992
|
+
)
|
|
1993
|
+
pyUnmatchedBraceForegroundColor = Property(
|
|
1994
|
+
QColor, unmatchedBraceForegroundColor, setUnmatchedBraceForegroundColor
|
|
1995
|
+
)
|
|
1996
|
+
pyEdgeColor = Property(QColor, edgeColor, setEdgeColor)
|
|
1997
|
+
documentFont = QtPropertyInit('_documentFont', _defaultFont)
|
|
1998
|
+
pyMarginsFont = Property(QFont, marginsFont, setMarginsFont)
|
|
1999
|
+
|
|
2000
|
+
copyIndentsAsSpaces = QtPropertyInit('_copyIndentsAsSpaces', False)
|
|
2001
|
+
|
|
2002
|
+
# These colors are purely defined in DocumentEditor so we can use QtPropertyInit
|
|
2003
|
+
braceBadForeground = QtPropertyInit('_braceBadForeground', QColor(255, 255, 255))
|
|
2004
|
+
braceBadBackground = QtPropertyInit('_braceBadBackground', QColor(100, 60, 60))
|
|
2005
|
+
|
|
2006
|
+
colorDefault = QtPropertyInit('_colorDefault', QColor())
|
|
2007
|
+
colorComment = QtPropertyInit('_colorComment', QColor(0, 127, 0))
|
|
2008
|
+
colorNumber = QtPropertyInit('_colorNumber', QColor(0, 127, 127))
|
|
2009
|
+
colorString = QtPropertyInit('_colorString', QColor(127, 0, 127))
|
|
2010
|
+
colorKeyword = QtPropertyInit('_colorKeyword', QColor(0, 0, 127))
|
|
2011
|
+
colorTripleQuotedString = QtPropertyInit(
|
|
2012
|
+
'_colorTripleQuotedString', QColor(127, 0, 0)
|
|
2013
|
+
)
|
|
2014
|
+
colorMethod = QtPropertyInit('_colorMethod', QColor(0, 0, 255))
|
|
2015
|
+
colorFunction = QtPropertyInit('_colorFunction', QColor(0, 127, 127))
|
|
2016
|
+
colorOperator = QtPropertyInit('_colorOperator', QColor(0, 0, 0))
|
|
2017
|
+
colorIdentifier = QtPropertyInit('_colorIdentifier', QColor(0, 0, 0))
|
|
2018
|
+
colorCommentBlock = QtPropertyInit('_colorCommentBlock', QColor(127, 127, 127))
|
|
2019
|
+
colorUnclosedString = QtPropertyInit('_colorUnclosedString', QColor(0, 0, 0))
|
|
2020
|
+
colorSmartHighlight = QtPropertyInit('_colorSmartHighlight', QColor(64, 112, 144))
|
|
2021
|
+
colorDecorator = QtPropertyInit('_colorDecorator', QColor(128, 80, 0))
|
|
2022
|
+
|
|
2023
|
+
_defaultPaper = QColor(255, 255, 255)
|
|
2024
|
+
paperDefault = QtPropertyInit('_paperDefault', _defaultPaper)
|
|
2025
|
+
paperComment = QtPropertyInit('_paperComment', _defaultPaper)
|
|
2026
|
+
paperNumber = QtPropertyInit('_paperNumber', _defaultPaper)
|
|
2027
|
+
paperString = QtPropertyInit('_paperString', _defaultPaper)
|
|
2028
|
+
paperKeyword = QtPropertyInit('_paperKeyword', _defaultPaper)
|
|
2029
|
+
paperTripleQuotedString = QtPropertyInit('_paperTripleQuotedString', _defaultPaper)
|
|
2030
|
+
paperMethod = QtPropertyInit('_paperMethod', _defaultPaper)
|
|
2031
|
+
paperFunction = QtPropertyInit('_paperFunction', _defaultPaper)
|
|
2032
|
+
paperOperator = QtPropertyInit('_paperOperator', _defaultPaper)
|
|
2033
|
+
paperIdentifier = QtPropertyInit('_paperIdentifier', _defaultPaper)
|
|
2034
|
+
paperCommentBlock = QtPropertyInit('_paperCommentBlock', _defaultPaper)
|
|
2035
|
+
paperUnclosedString = QtPropertyInit('_paperUnclosedString', QColor(224, 192, 224))
|
|
2036
|
+
paperSmartHighlight = QtPropertyInit(
|
|
2037
|
+
'_paperSmartHighlight', QColor(155, 255, 155, 75)
|
|
2038
|
+
)
|
|
2039
|
+
paperDecorator = QtPropertyInit('_paperDecorator', _defaultPaper)
|