259
269
self.document().isModified())
272
#------Highlight current line
273
def highlight_current_line(self):
274
"""Highlight current line"""
275
selection = QTextEdit.ExtraSelection()
276
selection.format.setProperty(QTextFormat.FullWidthSelection,
278
selection.format.setBackground(self.currentline_color)
279
selection.cursor = self.textCursor()
280
selection.cursor.clearSelection()
281
self.set_extra_selections('current_line', [selection])
282
self.update_extra_selections()
284
def unhighlight_current_line(self):
285
"""Unhighlight current line"""
286
self.clear_extra_selections('current_line')
288
#------Highlight current cell
289
def highlight_current_cell(self):
290
"""Highlight current cell"""
291
selection = QTextEdit.ExtraSelection()
292
selection.format.setProperty(QTextFormat.FullWidthSelection,
294
selection.format.setBackground(self.currentcell_color)
295
selection.cursor, whole_file_selected, whole_screen_selected =\
296
self.select_current_cell_in_visible_portion()
297
if whole_file_selected:
298
self.clear_extra_selections('current_cell')
299
elif whole_screen_selected:
300
if self.has_cell_separators:
301
self.set_extra_selections('current_cell', [selection])
302
self.update_extra_selections()
304
self.clear_extra_selections('current_cell')
306
self.set_extra_selections('current_cell', [selection])
307
self.update_extra_selections()
309
def unhighlight_current_cell(self):
310
"""Unhighlight current cell"""
311
self.clear_extra_selections('current_cell')
262
313
#------Brace matching
263
314
def find_brace_match(self, position, brace, forward):
264
315
start_pos, end_pos = self.BRACE_MATCHING_SCOPE
395
441
#------Text: get, set, ...
396
def get_executable_text(self):
397
"""Return selected text or current line as an processed text,
442
def get_selection_as_executable_code(self):
443
"""Return selected text as a processed text,
398
444
to be executable in a Python/IPython interpreter"""
399
no_selection = not self.has_selected_text()
401
self.select_current_block()
403
445
ls = self.get_line_separator()
405
447
_indent = lambda line: len(line)-len(line.lstrip())
407
449
line_from, line_to = self.get_selection_bounds()
408
450
text = self.get_selected_text()
410
454
lines = text.split(ls)
411
455
if len(lines) > 1:
412
456
# Multiline selection -> eventually fixing indentation
413
457
original_indent = _indent(self.get_text_line(line_from))
414
458
text = (" "*(original_indent-_indent(lines[0])))+text
416
# If there is a common indent to all lines, remove it
460
# If there is a common indent to all lines, find it.
461
# Moving from bottom line to top line ensures that blank
462
# lines inherit the indent of the line *below* it,
463
# which is the desired behavior.
418
for line in text.split(ls):
466
lines = text.split(ls)
467
for i in range(len(lines)-1, -1, -1):
420
min_indent = min(_indent(line), min_indent)
470
current_indent = _indent(line)
471
min_indent = min(current_indent, min_indent)
473
lines[i] = ' ' * current_indent
422
text = ls.join([line[min_indent:] for line in text.split(ls)])
475
lines = [line[min_indent:] for line in lines]
477
# Remove any leading whitespace or comment lines
478
# since they confuse the reserved word detector that follows below
480
first_line = lines[0].lstrip()
481
if first_line == '' or first_line[0] == '#':
424
# Add an EOL character if a block stars with various Python
425
# reserved words, so that it gets evaluated automatically
486
# Add an EOL character after indentation blocks that start with some
487
# Python reserved words, so that it gets evaluated automatically
427
first_line = lines[0].lstrip()
428
last_line = self.get_text_line(line_to).strip()
429
words = ['def', 'for', 'if', 'while', 'try', 'with', 'class']
430
if any([first_line.startswith(w) for w in words]):
489
varname = re.compile('[a-zA-Z0-9_]*') # matches valid variable names
492
for n, line in enumerate(lines):
493
if not _indent(line):
494
word = varname.match(line).group()
495
if maybe and word not in nextexcept:
499
if word in ('def', 'for', 'while', 'with', 'class'):
504
nextexcept = ('elif', 'else')
507
nextexcept = ('except', 'finally')
509
if lines[-1].strip() == '':
514
return ls.join(lines)
437
self.clear_selection()
516
def get_cell_as_executable_code(self):
517
"""Return cell contents as executable code"""
518
start_pos, end_pos = self.__save_selection()
519
cursor, whole_file_selected = self.select_current_cell()
520
if not whole_file_selected:
521
self.setTextCursor(cursor)
522
text = self.get_selection_as_executable_code()
523
self.__restore_selection(start_pos, end_pos)
526
def is_cell_separator(self, cursor=None, block=None):
527
"""Return True if cursor (or text block) is on a block separator"""
528
assert cursor is not None or block is not None
529
if cursor is not None:
530
cursor0 = QTextCursor(cursor)
531
cursor0.select(QTextCursor.BlockUnderCursor)
532
text = to_text_string(cursor0.selectedText())
534
text = to_text_string(block.text())
535
return text.lstrip().startswith(CELL_SEPARATORS)
537
def select_current_cell(self):
538
"""Select cell under cursor
539
cell = group of lines separated by CELL_SEPARATORS
540
returns the textCursor and a boolean indicating if the
541
entire file is selected"""
542
cursor = self.textCursor()
543
cursor.movePosition(QTextCursor.StartOfBlock)
544
cur_pos = prev_pos = cursor.position()
546
# Moving to the next line that is not a separator, if we are
547
# exactly at one of them
548
while self.is_cell_separator(cursor):
549
cursor.movePosition(QTextCursor.NextBlock)
551
cur_pos = cursor.position()
552
if cur_pos == prev_pos:
555
# If not, move backwards to find the previous separator
556
while not self.is_cell_separator(cursor):
557
cursor.movePosition(QTextCursor.PreviousBlock)
559
cur_pos = cursor.position()
560
if cur_pos == prev_pos:
561
if self.is_cell_separator(cursor):
565
cursor.setPosition(prev_pos)
566
cell_at_file_start = cursor.atStart()
567
# Once we find it (or reach the beginning of the file)
568
# move to the next separator (or the end of the file)
569
# so we can grab the cell contents
570
while not self.is_cell_separator(cursor):
571
cursor.movePosition(QTextCursor.NextBlock,
572
QTextCursor.KeepAnchor)
573
cur_pos = cursor.position()
574
if cur_pos == prev_pos:
575
cursor.movePosition(QTextCursor.EndOfBlock,
576
QTextCursor.KeepAnchor)
579
cell_at_file_end = cursor.atEnd()
580
return cursor, cell_at_file_start and cell_at_file_end
582
def select_current_cell_in_visible_portion(self):
583
"""Select cell under cursor in the visible portion of the file
584
cell = group of lines separated by CELL_SEPARATORS
587
-a boolean indicating if the entire file is selected
588
-a boolean indicating if the entire visible portion of the file is selected"""
589
cursor = self.textCursor()
590
cursor.movePosition(QTextCursor.StartOfBlock)
591
cur_pos = prev_pos = cursor.position()
593
beg_pos = self.cursorForPosition(QPoint(0, 0)).position()
594
bottom_right = QPoint(self.viewport().width() - 1,
595
self.viewport().height() - 1)
596
end_pos = self.cursorForPosition(bottom_right).position()
598
# Moving to the next line that is not a separator, if we are
599
# exactly at one of them
600
while self.is_cell_separator(cursor):
601
cursor.movePosition(QTextCursor.NextBlock)
603
cur_pos = cursor.position()
604
if cur_pos == prev_pos:
605
return cursor, False, False
607
# If not, move backwards to find the previous separator
608
while not self.is_cell_separator(cursor)\
609
and cursor.position() >= beg_pos:
610
cursor.movePosition(QTextCursor.PreviousBlock)
612
cur_pos = cursor.position()
613
if cur_pos == prev_pos:
614
if self.is_cell_separator(cursor):
615
return cursor, False, False
618
cell_at_screen_start = cursor.position() <= beg_pos
619
cursor.setPosition(prev_pos)
620
cell_at_file_start = cursor.atStart()
621
# Selecting cell header
622
if not cell_at_file_start:
623
cursor.movePosition(QTextCursor.PreviousBlock)
624
cursor.movePosition(QTextCursor.NextBlock,
625
QTextCursor.KeepAnchor)
626
# Once we find it (or reach the beginning of the file)
627
# move to the next separator (or the end of the file)
628
# so we can grab the cell contents
629
while not self.is_cell_separator(cursor)\
630
and cursor.position() <= end_pos:
631
cursor.movePosition(QTextCursor.NextBlock,
632
QTextCursor.KeepAnchor)
633
cur_pos = cursor.position()
634
if cur_pos == prev_pos:
635
cursor.movePosition(QTextCursor.EndOfBlock,
636
QTextCursor.KeepAnchor)
639
cell_at_file_end = cursor.atEnd()
640
cell_at_screen_end = cursor.position() >= end_pos
642
cell_at_file_start and cell_at_file_end,\
643
cell_at_screen_start and cell_at_screen_end
645
def go_to_next_cell(self):
646
"""Go to the next cell of lines"""
647
cursor = self.textCursor()
648
cursor.movePosition(QTextCursor.NextBlock)
649
cur_pos = prev_pos = cursor.position()
650
while not self.is_cell_separator(cursor):
651
# Moving to the next code cell
652
cursor.movePosition(QTextCursor.NextBlock)
654
cur_pos = cursor.position()
655
if cur_pos == prev_pos:
657
self.setTextCursor(cursor)
441
659
def get_line_count(self):
442
660
"""Return document total line number"""
443
661
return self.blockCount()
663
def __save_selection(self):
664
"""Save current cursor selection and return position bounds"""
665
cursor = self.textCursor()
666
return cursor.selectionStart(), cursor.selectionEnd()
668
def __restore_selection(self, start_pos, end_pos):
669
"""Restore cursor selection from position bounds"""
670
cursor = self.textCursor()
671
cursor.setPosition(start_pos)
672
cursor.setPosition(end_pos, QTextCursor.KeepAnchor)
673
self.setTextCursor(cursor)
445
675
def __duplicate_line_or_selection(self, after_current_line=True):
446
676
"""Duplicate current line or selected text"""
447
677
cursor = self.textCursor()
448
678
cursor.beginEditBlock()
449
orig_sel = start_pos, end_pos = (cursor.selectionStart(),
450
cursor.selectionEnd())
451
if unicode(cursor.selectedText()):
679
start_pos, end_pos = self.__save_selection()
680
if to_text_string(cursor.selectedText()):
452
681
cursor.setPosition(end_pos)
453
682
# Check if end_pos is at the start of a block: if so, starting
454
683
# changes from the previous block
455
684
cursor.movePosition(QTextCursor.StartOfBlock,
456
685
QTextCursor.KeepAnchor)
457
if not unicode(cursor.selectedText()):
686
if not to_text_string(cursor.selectedText()):
458
687
cursor.movePosition(QTextCursor.PreviousBlock)
459
688
end_pos = cursor.position()