-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
codeeditor.py
4633 lines (4039 loc) · 177 KB
/
codeeditor.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
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
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
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
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
590
591
592
593
594
595
596
597
598
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
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
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
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
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
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
945
946
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
# -*- coding: utf-8 -*-
#
# Copyright © Spyder Project Contributors
# Licensed under the terms of the MIT License
# (see spyder/__init__.py for details)
"""
Editor widget based on QtGui.QPlainTextEdit
"""
# TODO: Try to separate this module from spyder to create a self
# consistent editor module (Qt source code and shell widgets library)
# pylint: disable=C0103
# pylint: disable=R0903
# pylint: disable=R0911
# pylint: disable=R0201
# Standard library imports
from unicodedata import category
import logging
import os
import os.path as osp
import re
import sre_constants
import sys
import textwrap
# Third party imports
from IPython.core.inputtransformer2 import TransformerManager
from packaging.version import parse
from qtpy import QT_VERSION
from qtpy.compat import to_qvariant
from qtpy.QtCore import (
QEvent, QRegularExpression, Qt, QTimer, QUrl, Signal, Slot)
from qtpy.QtGui import (QColor, QCursor, QFont, QPaintEvent, QPainter,
QMouseEvent, QTextCursor, QDesktopServices, QKeyEvent,
QTextDocument, QTextFormat, QTextOption,
QTextCharFormat, QTextLayout)
from qtpy.QtWidgets import QApplication, QMessageBox, QSplitter, QScrollBar
from spyder_kernels.utils.dochelpers import getobj
# Local imports
from spyder.config.base import _, running_under_pytest
from spyder.plugins.editor.api.decoration import TextDecoration
from spyder.plugins.editor.api.panel import Panel
from spyder.plugins.editor.extensions import (CloseBracketsExtension,
CloseQuotesExtension,
DocstringWriterExtension,
QMenuOnlyForEnter,
EditorExtensionsManager,
SnippetsExtension)
from spyder.plugins.editor.panels import (
ClassFunctionDropdown, EdgeLine, FoldingPanel, IndentationGuide,
LineNumberArea, PanelsManager, ScrollFlagArea)
from spyder.plugins.editor.utils.editor import (TextHelper, BlockUserData,
get_file_language)
from spyder.plugins.editor.utils.kill_ring import QtKillRing
from spyder.plugins.editor.utils.languages import ALL_LANGUAGES, CELL_LANGUAGES
from spyder.plugins.editor.widgets.gotoline import GoToLineDialog
from spyder.plugins.editor.widgets.base import TextEditBaseWidget
from spyder.plugins.editor.widgets.codeeditor.lsp_mixin import LSPMixin
from spyder.plugins.outlineexplorer.api import (OutlineExplorerData as OED,
is_cell_header)
from spyder.py3compat import to_text_string, is_string
from spyder.utils import encoding, sourcecode
from spyder.utils.clipboard_helper import CLIPBOARD_HELPER
from spyder.utils.icon_manager import ima
from spyder.utils import syntaxhighlighters as sh
from spyder.utils.palette import SpyderPalette
from spyder.utils.qthelpers import file_uri, mimedata2url, start_file
from spyder.utils.vcs import get_git_remotes, remote_to_url
from spyder.utils.qstringhelpers import qstring_length
from spyder.widgets.mixins import HINT_MAX_WIDTH
try:
import nbformat as nbformat
from nbconvert import PythonExporter as nbexporter
except Exception:
nbformat = None # analysis:ignore
logger = logging.getLogger(__name__)
class CodeEditorActions:
Undo = 'undo'
Redo = 'redo'
Cut = 'cut'
Copy = 'copy'
Paste = 'paste'
SelectAll = 'select all'
ToggleComment = 'toggle comment'
ClearAllOutput = 'clear_all_output_action'
ConvertToPython = 'convert_to_python_action'
GoToDefinition = 'go to definition'
InspectCurrentObject = 'inspect current object'
ZoomIn = 'zoom in 1'
ZoomOut = 'zoom out'
ZoomReset = 'zoom reset'
Docstring = 'docstring'
Autoformat = 'autoformatting'
class CodeEditorMenus:
ContextMenu = 'context_menu'
ReadOnlyMenu = 'read_only_menu'
class CodeEditorContextMenuSections:
InspectSection = "inspect_section"
UndoRedoSection = "undo_redo_section"
EditSection = "edit_section"
NbformatSections = "nbformat_section"
ZoomSection = "zoom_section"
RefactorCodeSection = "refactor_code_section"
CopySection = "copy_section"
OthersSection = "others_section"
class CodeEditor(LSPMixin, TextEditBaseWidget):
"""Source Code Editor Widget based exclusively on Qt"""
CONF_SECTION = 'editor'
LANGUAGES = {
'Python': (sh.PythonSH, '#'),
'IPython': (sh.IPythonSH, '#'),
'Cython': (sh.CythonSH, '#'),
'Fortran77': (sh.Fortran77SH, 'c'),
'Fortran': (sh.FortranSH, '!'),
'Idl': (sh.IdlSH, ';'),
'Diff': (sh.DiffSH, ''),
'GetText': (sh.GetTextSH, '#'),
'Nsis': (sh.NsisSH, '#'),
'Html': (sh.HtmlSH, ''),
'Yaml': (sh.YamlSH, '#'),
'Cpp': (sh.CppSH, '//'),
'OpenCL': (sh.OpenCLSH, '//'),
'Enaml': (sh.EnamlSH, '#'),
'Markdown': (sh.MarkdownSH, '#'),
# Every other language
'None': (sh.TextSH, ''),
}
TAB_ALWAYS_INDENTS = (
'py', 'pyw', 'python', 'ipy', 'c', 'cpp', 'cl', 'h', 'pyt', 'pyi'
)
# Timeout to update decorations (through a QTimer) when a position
# changed is detected in the vertical scrollbar or when releasing
# the up/down arrow keys.
UPDATE_DECORATIONS_TIMEOUT = 500 # milliseconds
# Custom signal to be emitted upon completion of the editor's paintEvent
painted = Signal(QPaintEvent)
# To have these attrs when early viewportEvent's are triggered
edge_line = None
indent_guides = None
sig_filename_changed = Signal(str)
sig_bookmarks_changed = Signal()
go_to_definition = Signal(str, int, int)
sig_show_object_info = Signal(bool)
sig_cursor_position_changed = Signal(int, int)
sig_new_file = Signal(str)
sig_refresh_formatting = Signal(bool)
#: Signal emitted when the editor loses focus
sig_focus_changed = Signal()
#: Signal emitted when a key is pressed
sig_key_pressed = Signal(QKeyEvent)
#: Signal emitted when a key is released
sig_key_released = Signal(QKeyEvent)
#: Signal emitted when the alt key is pressed and the left button of the
# mouse is clicked
sig_alt_left_mouse_pressed = Signal(QMouseEvent)
#: Signal emitted when the alt key is pressed and the cursor moves over
# the editor
sig_alt_mouse_moved = Signal(QMouseEvent)
#: Signal emitted when the cursor leaves the editor
sig_leave_out = Signal()
#: Signal emitted when the flags need to be updated in the scrollflagarea
sig_flags_changed = Signal()
#: Signal emitted when the syntax color theme of the editor.
sig_theme_colors_changed = Signal(dict)
#: Signal emitted when a new text is set on the widget
new_text_set = Signal()
# Used for testing. When the mouse moves with Ctrl/Cmd pressed and
# a URI is found, this signal is emitted
sig_uri_found = Signal(str)
sig_file_uri_preprocessed = Signal(str)
"""
This signal is emitted when the go to uri for a file has been
preprocessed.
Parameters
----------
fpath: str
The preprocessed file path.
"""
# Signal with the info about the current completion item documentation
# str: object name
# str: object signature/documentation
# bool: force showing the info
sig_show_completion_object_info = Signal(str, str, bool)
# Used to indicate if text was inserted into the editor
sig_text_was_inserted = Signal()
# Used to indicate that text will be inserted into the editor
sig_will_insert_text = Signal(str)
# Used to indicate that a text selection will be removed
sig_will_remove_selection = Signal(tuple, tuple)
# Used to indicate that text will be pasted
sig_will_paste_text = Signal(str)
# Used to indicate that an undo operation will take place
sig_undo = Signal()
# Used to indicate that an undo operation will take place
sig_redo = Signal()
# Used to signal font change
sig_font_changed = Signal()
# Used to request saving a file
sig_save_requested = Signal()
# Used to signal that a text deletion was triggered
sig_delete_requested = Signal()
def __init__(self, parent=None):
super().__init__(parent, class_parent=parent)
self.setFocusPolicy(Qt.StrongFocus)
# Projects
self.current_project_path = None
# Caret (text cursor)
self.setCursorWidth(self.get_conf('cursor/width', section='main'))
self.text_helper = TextHelper(self)
self._panels = PanelsManager(self)
# Mouse moving timer / Hover hints handling
# See: mouseMoveEvent
self.tooltip_widget.sig_help_requested.connect(
self.show_object_info)
self.tooltip_widget.sig_completion_help_requested.connect(
self.show_completion_object_info)
self._last_point = None
self._last_hover_word = None
self._last_hover_cursor = None
self._timer_mouse_moving = QTimer(self)
self._timer_mouse_moving.setInterval(350)
self._timer_mouse_moving.setSingleShot(True)
self._timer_mouse_moving.timeout.connect(self._handle_hover)
# Typing keys / handling for on the fly completions
self._last_key_pressed_text = ''
self._last_pressed_key = None
# Handle completions hints
self._completions_hint_idle = False
self._timer_completions_hint = QTimer(self)
self._timer_completions_hint.setSingleShot(True)
self._timer_completions_hint.timeout.connect(
self._set_completions_hint_idle)
self.completion_widget.sig_completion_hint.connect(
self.show_hint_for_completion)
# Goto uri
self._last_hover_pattern_key = None
self._last_hover_pattern_text = None
# 79-col edge line
self.edge_line = self.panels.register(
EdgeLine(),
Panel.Position.FLOATING
)
# indent guides
self.indent_guides = self.panels.register(
IndentationGuide(),
Panel.Position.FLOATING
)
# Blanks enabled
self.blanks_enabled = False
# Underline errors and warnings
self.underline_errors_enabled = False
# Scrolling past the end of the document
self.scrollpastend_enabled = False
self.background = QColor('white')
# Folding
self.folding_panel = self.panels.register(FoldingPanel())
# Line number area management
self.linenumberarea = self.panels.register(LineNumberArea())
# Set order for the left panels
self.linenumberarea.order_in_zone = 2
self.folding_panel.order_in_zone = 0 # Debugger panel is 1
# Class and Method/Function Dropdowns
self.classfuncdropdown = self.panels.register(
ClassFunctionDropdown(),
Panel.Position.TOP,
)
# Colors to be defined in _apply_highlighter_color_scheme()
# Currentcell color and current line color are defined in base.py
self.occurrence_color = None
self.ctrl_click_color = None
self.sideareas_color = None
self.matched_p_color = None
self.unmatched_p_color = None
self.normal_color = None
self.comment_color = None
# --- Syntax highlight entrypoint ---
#
# - if set, self.highlighter is responsible for
# - coloring raw text data inside editor on load
# - coloring text data when editor is cloned
# - updating document highlight on line edits
# - providing color palette (scheme) for the editor
# - providing data for Outliner
# - self.highlighter is not responsible for
# - background highlight for current line
# - background highlight for search / current line occurrences
self.highlighter_class = sh.TextSH
self.highlighter = None
ccs = 'Spyder'
if ccs not in sh.COLOR_SCHEME_NAMES:
ccs = sh.COLOR_SCHEME_NAMES[0]
self.color_scheme = ccs
self.highlight_current_line_enabled = False
# Vertical scrollbar
# This is required to avoid a "RuntimeError: no access to protected
# functions or signals for objects not created from Python" in
# Linux Ubuntu. See spyder-ide/spyder#5215.
self.setVerticalScrollBar(QScrollBar())
# Highlights and flag colors
self.warning_color = SpyderPalette.COLOR_WARN_2
self.error_color = SpyderPalette.COLOR_ERROR_1
self.todo_color = SpyderPalette.GROUP_9
self.breakpoint_color = SpyderPalette.ICON_3
self.occurrence_color = QColor(SpyderPalette.GROUP_2).lighter(160)
self.found_results_color = QColor(SpyderPalette.COLOR_OCCURRENCE_4)
# Scrollbar flag area
self.scrollflagarea = self.panels.register(
ScrollFlagArea(),
Panel.Position.RIGHT
)
self.panels.refresh()
self.document_id = id(self)
# Indicate occurrences of the selected word
self.cursorPositionChanged.connect(self.__cursor_position_changed)
self.__find_first_pos = None
self.__find_args = {}
self.language = None
self.supported_language = False
self.supported_cell_language = False
self.comment_string = None
self._kill_ring = QtKillRing(self)
# Block user data
self.blockCountChanged.connect(self.update_bookmarks)
# Highlight using Pygments highlighter timer
# ---------------------------------------------------------------------
# For files that use the PygmentsSH we parse the full file inside
# the highlighter in order to generate the correct coloring.
self.timer_syntax_highlight = QTimer(self)
self.timer_syntax_highlight.setSingleShot(True)
self.timer_syntax_highlight.timeout.connect(
self.run_pygments_highlighter)
# Mark occurrences timer
self.occurrence_highlighting = None
self.occurrence_timer = QTimer(self)
self.occurrence_timer.setSingleShot(True)
self.occurrence_timer.setInterval(1500)
self.occurrence_timer.timeout.connect(self.mark_occurrences)
self.occurrences = []
# Update decorations
self.update_decorations_timer = QTimer(self)
self.update_decorations_timer.setSingleShot(True)
self.update_decorations_timer.setInterval(
self.UPDATE_DECORATIONS_TIMEOUT)
self.update_decorations_timer.timeout.connect(
self.update_decorations)
self.verticalScrollBar().valueChanged.connect(
lambda value: self.update_decorations_timer.start()
)
# Hide calltip when scrolling
self.verticalScrollBar().valueChanged.connect(
lambda value: self.hide_calltip()
)
# QTextEdit + LSPMixin
self.textChanged.connect(self._schedule_document_did_change)
# Mark found results
self.textChanged.connect(self.__text_has_changed)
self.found_results = []
# Docstring
self.writer_docstring = DocstringWriterExtension(self)
# Context menu
self.gotodef_action = None
self.setup_context_menu()
# Tab key behavior
self.tab_indents = None
self.tab_mode = True # see CodeEditor.set_tab_mode
# Intelligent backspace mode
self.intelligent_backspace = True
# Automatic (on the fly) completions
self.automatic_completions = True
self.automatic_completions_after_chars = 3
# Completions hint
self.completions_hint = True
self.completions_hint_after_ms = 500
self.close_parentheses_enabled = True
self.close_quotes_enabled = False
self.add_colons_enabled = True
self.auto_unindent_enabled = True
# Mouse tracking
self.setMouseTracking(True)
self.__cursor_changed = False
self._mouse_left_button_pressed = False
self.ctrl_click_color = QColor(Qt.blue)
self._bookmarks_blocks = {}
self.bookmarks = []
# Keyboard shortcuts
self.register_shortcuts()
# Paint event
self.__visible_blocks = [] # Visible blocks, update with repaint
self.painted.connect(self._draw_editor_cell_divider)
# Line stripping
self.last_change_position = None
self.last_position = None
self.last_auto_indent = None
self.skip_rstrip = False
self.strip_trailing_spaces_on_modify = True
# Hover hints
self.hover_hints_enabled = None
# Editor Extensions
self.editor_extensions = EditorExtensionsManager(self)
self.editor_extensions.add(CloseQuotesExtension())
self.editor_extensions.add(SnippetsExtension())
self.editor_extensions.add(CloseBracketsExtension())
# Some events should not be triggered during undo/redo
# such as line stripping
self.is_undoing = False
self.is_redoing = False
# Timer to Avoid too many calls to rehighlight.
self._rehighlight_timer = QTimer(self)
self._rehighlight_timer.setSingleShot(True)
self._rehighlight_timer.setInterval(150)
# ---- Hover/Hints
# -------------------------------------------------------------------------
def _should_display_hover(self, point):
"""Check if a hover hint should be displayed:"""
if not self._mouse_left_button_pressed:
return (self.hover_hints_enabled and point
and self.get_word_at(point))
def _handle_hover(self):
"""Handle hover hint trigger after delay."""
self._timer_mouse_moving.stop()
pos = self._last_point
# These are textual characters but should not trigger a completion
# FIXME: update per language
ignore_chars = ['(', ')', '.']
if self._should_display_hover(pos):
# --- Show Ctrl/Cmd click tooltips
key, pattern_text, __ = self.get_pattern_at(pos)
if pattern_text:
ctrl_text = 'Cmd' if sys.platform == "darwin" else 'Ctrl'
if key in ['file']:
hint_text = ctrl_text + ' + ' + _('click to open file')
elif key in ['mail']:
hint_text = ctrl_text + ' + ' + _('click to send email')
elif key in ['url']:
hint_text = ctrl_text + ' + ' + _('click to open url')
else:
hint_text = ctrl_text + ' + ' + _('click to open')
hint_text = '<span> {} </span>'.format(hint_text)
self.show_tooltip(text=hint_text, at_point=pos)
return
# --- Show LSP hovers
# This prevents requesting a new hover while trying to reach the
# current one to click on it.
if (
self.tooltip_widget.is_hover()
and self.tooltip_widget.is_hovered()
):
return
text = self.get_word_at(pos)
cursor = self.cursorForPosition(pos)
cursor_offset = cursor.position()
line, col = cursor.blockNumber(), cursor.columnNumber()
self._last_point = pos
if text and self._last_hover_word != text:
if all(char not in text for char in ignore_chars):
self._last_hover_word = text
self.request_hover(line, col, cursor_offset)
else:
self.hide_tooltip()
elif not self.is_completion_widget_visible():
self.hide_tooltip()
def blockuserdata_list(self):
"""Get the list of all user data in document."""
block = self.document().firstBlock()
while block.isValid():
data = block.userData()
if data:
yield data
block = block.next()
def outlineexplorer_data_list(self):
"""Get the list of all user data in document."""
for data in self.blockuserdata_list():
if data.oedata:
yield data.oedata
# ---- Keyboard Shortcuts
# -------------------------------------------------------------------------
def create_cursor_callback(self, attr):
"""Make a callback for cursor move event type, (e.g. "Start")"""
def cursor_move_event():
cursor = self.textCursor()
move_type = getattr(QTextCursor, attr)
cursor.movePosition(move_type)
self.setTextCursor(cursor)
return cursor_move_event
def register_shortcuts(self):
"""Register shortcuts for this widget."""
shortcuts = (
('code completion', self.do_completion),
('duplicate line down', self.duplicate_line_down),
('duplicate line up', self.duplicate_line_up),
('delete line', self.delete_line),
('move line up', self.move_line_up),
('move line down', self.move_line_down),
('go to new line', self.go_to_new_line),
('go to definition', self.go_to_definition_from_cursor),
('toggle comment', self.toggle_comment),
('blockcomment', self.blockcomment),
('create_new_cell', self.create_new_cell),
('unblockcomment', self.unblockcomment),
('transform to uppercase', self.transform_to_uppercase),
('transform to lowercase', self.transform_to_lowercase),
('indent', lambda: self.indent(force=True)),
('unindent', lambda: self.unindent(force=True)),
('start of line', self.create_cursor_callback('StartOfLine')),
('end of line', self.create_cursor_callback('EndOfLine')),
('previous line', self.create_cursor_callback('Up')),
('next line', self.create_cursor_callback('Down')),
('previous char', self.create_cursor_callback('Left')),
('next char', self.create_cursor_callback('Right')),
('previous word', self.create_cursor_callback('PreviousWord')),
('next word', self.create_cursor_callback('NextWord')),
('kill to line end', self.kill_line_end),
('kill to line start', self.kill_line_start),
('yank', self._kill_ring.yank),
('rotate kill ring', self._kill_ring.rotate),
('kill previous word', self.kill_prev_word),
('kill next word', self.kill_next_word),
('start of document', self.create_cursor_callback('Start')),
('end of document', self.create_cursor_callback('End')),
('undo', self.undo),
('redo', self.redo),
('cut', self.cut),
('copy', self.copy),
('paste', self.paste),
('delete', self.delete),
('select all', self.selectAll),
('docstring', self.writer_docstring.write_docstring_for_shortcut),
('autoformatting', self.format_document_or_range),
('scroll line down', self.scroll_line_down),
('scroll line up', self.scroll_line_up),
('enter array inline', self.enter_array_inline),
('enter array table', self.enter_array_table),
)
for name, callback in shortcuts:
self.register_shortcut_for_widget(name=name, triggered=callback)
def closeEvent(self, event):
if isinstance(self.highlighter, sh.PygmentsSH):
self.highlighter.stop()
self.update_folding_thread.quit()
self.update_folding_thread.wait()
self.update_diagnostics_thread.quit()
self.update_diagnostics_thread.wait()
TextEditBaseWidget.closeEvent(self, event)
def get_document_id(self):
return self.document_id
def set_as_clone(self, editor):
"""Set as clone editor"""
self.setDocument(editor.document())
self.document_id = editor.get_document_id()
self.highlighter = editor.highlighter
self._rehighlight_timer.timeout.connect(
self.highlighter.rehighlight)
self.eol_chars = editor.eol_chars
self._apply_highlighter_color_scheme()
self.highlighter.sig_font_changed.connect(self.sync_font)
# ---- Widget setup and options
# -------------------------------------------------------------------------
def toggle_wrap_mode(self, enable):
"""Enable/disable wrap mode"""
self.set_wrap_mode('word' if enable else None)
def toggle_line_numbers(self, linenumbers=True, markers=False):
"""Enable/disable line numbers."""
self.linenumberarea.setup_margins(linenumbers, markers)
@property
def panels(self):
"""
Returns a reference to the
:class:`spyder.widgets.panels.managers.PanelsManager`
used to manage the collection of installed panels
"""
return self._panels
def setup_editor(self,
linenumbers=True,
language=None,
markers=False,
font=None,
color_scheme=None,
wrap=False,
tab_mode=True,
strip_mode=False,
intelligent_backspace=True,
automatic_completions=True,
automatic_completions_after_chars=3,
completions_hint=True,
completions_hint_after_ms=500,
hover_hints=True,
code_snippets=True,
highlight_current_line=True,
highlight_current_cell=True,
occurrence_highlighting=True,
scrollflagarea=True,
edge_line=True,
edge_line_columns=(79,),
show_blanks=False,
underline_errors=False,
close_parentheses=True,
close_quotes=False,
add_colons=True,
auto_unindent=True,
indent_chars=" "*4,
tab_stop_width_spaces=4,
cloned_from=None,
filename=None,
occurrence_timeout=1500,
show_class_func_dropdown=False,
indent_guides=False,
scroll_past_end=False,
folding=True,
remove_trailing_spaces=False,
remove_trailing_newlines=False,
add_newline=False,
format_on_save=False):
"""
Set-up configuration for the CodeEditor instance.
Usually the parameters here are related with a configurable preference
in the Preference Dialog and Editor configurations:
linenumbers: Enable/Disable line number panel. Default True.
language: Set editor language for example python. Default None.
markers: Enable/Disable markers panel. Used to show elements like
Code Analysis. Default False.
font: Base font for the Editor to use. Default None.
color_scheme: Initial color scheme for the Editor to use. Default None.
wrap: Enable/Disable line wrap. Default False.
tab_mode: Enable/Disable using Tab as delimiter between word,
Default True.
strip_mode: strip_mode: Enable/Disable striping trailing spaces when
modifying the file. Default False.
intelligent_backspace: Enable/Disable automatically unindenting
inserted text (unindenting happens if the leading text length of
the line isn't module of the length of indentation chars being use)
Default True.
automatic_completions: Enable/Disable automatic completions.
The behavior of the trigger of this the completions can be
established with the two following kwargs. Default True.
automatic_completions_after_chars: Number of charts to type to trigger
an automatic completion. Default 3.
completions_hint: Enable/Disable documentation hints for completions.
Default True.
completions_hint_after_ms: Number of milliseconds over a completion
item to show the documentation hint. Default 500.
hover_hints: Enable/Disable documentation hover hints. Default True.
code_snippets: Enable/Disable code snippets completions. Default True.
highlight_current_line: Enable/Disable current line highlighting.
Default True.
highlight_current_cell: Enable/Disable current cell highlighting.
Default True.
occurrence_highlighting: Enable/Disable highlighting of current word
occurrence in the file. Default True.
scrollflagarea : Enable/Disable flag area that shows at the left of
the scroll bar. Default True.
edge_line: Enable/Disable vertical line to show max number of
characters per line. Customizable number of columns in the
following kwarg. Default True.
edge_line_columns: Number of columns/characters where the editor
horizontal edge line will show. Default (79,).
show_blanks: Enable/Disable blanks highlighting. Default False.
underline_errors: Enable/Disable showing and underline to highlight
errors. Default False.
close_parentheses: Enable/Disable automatic parentheses closing
insertion. Default True.
close_quotes: Enable/Disable automatic closing of quotes.
Default False.
add_colons: Enable/Disable automatic addition of colons. Default True.
auto_unindent: Enable/Disable automatically unindentation before else,
elif, finally or except statements. Default True.
indent_chars: Characters to use for indentation. Default " "*4.
tab_stop_width_spaces: Enable/Disable using tabs for indentation.
Default 4.
cloned_from: Editor instance used as template to instantiate this
CodeEditor instance. Default None.
filename: Initial filename to show. Default None.
occurrence_timeout : Timeout in milliseconds to start highlighting
matches/occurrences for the current word under the cursor.
Default 1500 ms.
show_class_func_dropdown: Enable/Disable a Matlab like widget to show
classes and functions available in the current file. Default False.
indent_guides: Enable/Disable highlighting of code indentation.
Default False.
scroll_past_end: Enable/Disable possibility to scroll file passed
its end. Default False.
folding: Enable/Disable code folding. Default True.
remove_trailing_spaces: Remove trailing whitespaces on lines.
Default False.
remove_trailing_newlines: Remove extra lines at the end of the file.
Default False.
add_newline: Add a newline at the end of the file if there is not one.
Default False.
format_on_save: Autoformat file automatically when saving.
Default False.
"""
self.set_close_parentheses_enabled(close_parentheses)
self.set_close_quotes_enabled(close_quotes)
self.set_add_colons_enabled(add_colons)
self.set_auto_unindent_enabled(auto_unindent)
self.set_indent_chars(indent_chars)
# Show/hide folding panel depending on parameter
self.toggle_code_folding(folding)
# Scrollbar flag area
self.scrollflagarea.set_enabled(scrollflagarea)
# Edge line
self.edge_line.set_enabled(edge_line)
self.edge_line.set_columns(edge_line_columns)
# Indent guides
self.toggle_identation_guides(indent_guides)
if self.indent_chars == '\t':
self.indent_guides.set_indentation_width(
tab_stop_width_spaces)
else:
self.indent_guides.set_indentation_width(len(self.indent_chars))
# Blanks
self.set_blanks_enabled(show_blanks)
# Remove trailing whitespaces
self.set_remove_trailing_spaces(remove_trailing_spaces)
# Remove trailing newlines
self.set_remove_trailing_newlines(remove_trailing_newlines)
# Add newline at the end
self.set_add_newline(add_newline)
# Scrolling past the end
self.set_scrollpastend_enabled(scroll_past_end)
# Line number area and indent guides
self.toggle_line_numbers(linenumbers, markers)
# Lexer
self.filename = filename
self.set_language(language, filename)
# Underline errors and warnings
self.set_underline_errors_enabled(underline_errors)
# Highlight current cell
self.set_highlight_current_cell(highlight_current_cell)
# Highlight current line
self.set_highlight_current_line(highlight_current_line)
# Occurrence highlighting
self.set_occurrence_highlighting(occurrence_highlighting)
self.set_occurrence_timeout(occurrence_timeout)
# Tab always indents (even when cursor is not at the begin of line)
self.set_tab_mode(tab_mode)
# Intelligent backspace
self.toggle_intelligent_backspace(intelligent_backspace)
# Automatic completions
self.toggle_automatic_completions(automatic_completions)
self.set_automatic_completions_after_chars(
automatic_completions_after_chars)
# Completions hint
self.toggle_completions_hint(completions_hint)
self.set_completions_hint_after_ms(completions_hint_after_ms)
# Hover hints
self.toggle_hover_hints(hover_hints)
# Code snippets
self.toggle_code_snippets(code_snippets)
# Autoformat on save
self.toggle_format_on_save(format_on_save)
if cloned_from is not None:
self.is_cloned = True
# This is required for the line number area
self.setFont(font)
# Needed to show indent guides for splited editor panels
# See spyder-ide/spyder#10900
self.patch = cloned_from.patch
# Clone text and other properties
self.set_as_clone(cloned_from)
# Refresh panels
self.panels.refresh()
elif font is not None:
self.set_font(font, color_scheme)
elif color_scheme is not None:
self.set_color_scheme(color_scheme)
# Set tab spacing after font is set
self.set_tab_stop_width_spaces(tab_stop_width_spaces)
self.toggle_wrap_mode(wrap)
# Class/Function dropdown will be disabled if we're not in a Python
# file.
self.classfuncdropdown.setVisible(show_class_func_dropdown
and self.is_python_like())
self.set_strip_mode(strip_mode)
# ---- Set different attributes
# -------------------------------------------------------------------------
def set_folding_panel(self, folding):
"""Enable/disable folding panel."""
self.folding_panel.setVisible(folding)
def set_tab_mode(self, enable):
"""
enabled = tab always indent
(otherwise tab indents only when cursor is at the beginning of a line)
"""
self.tab_mode = enable
def set_strip_mode(self, enable):
"""
Strip all trailing spaces if enabled.
"""
self.strip_trailing_spaces_on_modify = enable
def toggle_intelligent_backspace(self, state):
self.intelligent_backspace = state
def toggle_automatic_completions(self, state):
self.automatic_completions = state
def toggle_hover_hints(self, state):
self.hover_hints_enabled = state
def toggle_code_snippets(self, state):
self.code_snippets = state
def toggle_format_on_save(self, state):
self.format_on_save = state
def toggle_code_folding(self, state):
self.code_folding = state
self.set_folding_panel(state)
if not state and self.indent_guides._enabled:
self.code_folding = True
def toggle_identation_guides(self, state):
if state and not self.code_folding:
self.code_folding = True
self.indent_guides.set_enabled(state)
def toggle_completions_hint(self, state):
"""Enable/disable completion hint."""
self.completions_hint = state
def set_automatic_completions_after_chars(self, number):
"""
Set the number of characters after which auto completion is fired.
"""
self.automatic_completions_after_chars = number
def set_completions_hint_after_ms(self, ms):
"""
Set the amount of time in ms after which the completions hint is shown.
"""
self.completions_hint_after_ms = ms
def set_close_parentheses_enabled(self, enable):
"""Enable/disable automatic parentheses insertion feature"""
self.close_parentheses_enabled = enable
bracket_extension = self.editor_extensions.get(CloseBracketsExtension)
if bracket_extension is not None:
bracket_extension.enabled = enable
def set_close_quotes_enabled(self, enable):
"""Enable/disable automatic quote insertion feature"""
self.close_quotes_enabled = enable
quote_extension = self.editor_extensions.get(CloseQuotesExtension)
if quote_extension is not None: