Skip to content

Commit 0bd70ef

Browse files
committed
<<< tweak autocompletion
1 parent f6bc403 commit 0bd70ef

File tree

1 file changed

+58
-27
lines changed

1 file changed

+58
-27
lines changed

larray_editor/editor.py

Lines changed: 58 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,9 @@ def insert_completion(self, completion):
516516
cursor = self.textCursor()
517517
cursor.select(QTextCursor.WordUnderCursor)
518518
cursor.removeSelectedText()
519-
cursor.insertText(completion)
519+
# Insert a space if the cursor is at the end of the text
520+
at_end = cursor.position() == len(self.toPlainText())
521+
cursor.insertText(completion + (' ' if at_end else ''))
520522
self.setTextCursor(cursor)
521523

522524
def keyPressEvent(self, event):
@@ -535,44 +537,35 @@ def keyPressEvent(self, event):
535537
elif event.key() == Qt.Key.Key_Escape:
536538
completer_popup.hide()
537539
return
538-
if event.key() == Qt.Key.Key_Tab:
539-
cursor = self.textCursor()
540-
cursor.select(QTextCursor.WordUnderCursor)
541-
prefix = cursor.selectedText()
542-
self.completer.setCompletionPrefix(prefix.upper())
543-
if self.completer.completionCount() == 1:
544-
completion = self.completer.currentCompletion()
545-
self.insert_completion(completion)
546-
return
547-
else:
548-
# create a new cursor and move it to the start of the word
549-
word_start_cursor = self.textCursor()
550-
word_start_cursor.movePosition(QTextCursor.StartOfWord)
551-
rect = self.cursorRect(word_start_cursor)
552-
popup_scrollbar = completer_popup.verticalScrollBar()
553-
popup_scrollbar_width = popup_scrollbar.sizeHint().width() + 10
554-
rect.setWidth(completer_popup.sizeHintForColumn(0)
555-
+ popup_scrollbar_width)
556-
self.completer.complete(rect)
557-
return
558-
elif (event.key() in (Qt.Key.Key_Enter, Qt.Key.Key_Return) and
540+
541+
if (event.key() in (Qt.Key.Key_Enter, Qt.Key.Key_Return) and
559542
event.modifiers() & Qt.KeyboardModifier.ShiftModifier):
560543
query_text = self.toPlainText().strip()
561544
if query_text:
562545
self.append_to_history(query_text)
563546
self.execute_sql(query_text)
564547
return
548+
elif event.key() == Qt.Key.Key_Tab:
549+
prefix = self.get_word_prefix()
550+
self.completer.setCompletionPrefix(prefix)
551+
if self.completer.completionCount() == 1:
552+
completion = self.completer.currentCompletion()
553+
self.insert_completion(completion)
554+
return
555+
else:
556+
self.show_autocomplete_popup()
557+
return
565558

566559
cursor = self.textCursor()
567-
block_number = cursor.blockNumber()
568-
total_blocks = self.document().blockCount()
569-
560+
# for plaintext QTextEdit, blockNumber gives the line number
561+
line_num = cursor.blockNumber()
570562
if event.key() == Qt.Key.Key_Up:
571-
if block_number == 0: # Cursor is on the first line
563+
if line_num == 0:
572564
if self.search_and_recall_history(direction=-1):
573565
return
574566
elif event.key() == Qt.Key.Key_Down:
575-
if block_number == total_blocks - 1: # Cursor is on the last line
567+
total_lines = self.document().blockCount()
568+
if line_num == total_lines - 1:
576569
if self.history_index < len(self.history) - 1:
577570
if self.search_and_recall_history(direction=1):
578571
return
@@ -581,6 +574,44 @@ def keyPressEvent(self, event):
581574
self.clear()
582575
return
583576
super().keyPressEvent(event)
577+
# we need to compute the prefix *after* the keypress event has been
578+
# handled so that the prefix contains the last key stroke
579+
prefix = self.get_word_prefix()
580+
if prefix:
581+
self.completer.setCompletionPrefix(prefix)
582+
num_completion = self.completer.completionCount()
583+
if (0 < num_completion <= self.completer.maxVisibleItems() and
584+
not completer_popup.isVisible()):
585+
self.show_autocomplete_popup()
586+
elif num_completion == 0 and completer_popup.isVisible():
587+
completer_popup.hide()
588+
589+
def show_autocomplete_popup(self):
590+
# create a new cursor and move it to the start of the word, so that
591+
# we can position the popup correctly
592+
word_start_cursor = self.textCursor()
593+
word_start_cursor.movePosition(QTextCursor.StartOfWord)
594+
rect = self.cursorRect(word_start_cursor)
595+
completer_popup = self.completer.popup()
596+
popup_scrollbar = completer_popup.verticalScrollBar()
597+
popup_scrollbar_width = popup_scrollbar.sizeHint().width() + 10
598+
rect.setWidth(completer_popup.sizeHintForColumn(0)
599+
+ popup_scrollbar_width)
600+
self.completer.complete(rect)
601+
602+
def get_word_prefix(self):
603+
text = self.toPlainText()
604+
if not text:
605+
return ''
606+
cursor = self.textCursor()
607+
cursor_pos = cursor.position()
608+
# <= len(text) (instead of <) because cursor can be at the end
609+
assert 0 <= cursor_pos <= len(text), f"{cursor_pos=} {len(text)=}"
610+
word_start = cursor_pos
611+
while (word_start > 0 and
612+
text[word_start - 1].isalnum() or text[word_start - 1] == '_'):
613+
word_start -= 1
614+
return text[word_start:cursor_pos]
584615

585616
def search_and_recall_history(self, direction: int):
586617
if not self.history:

0 commit comments

Comments
 (0)