Wire up the completion worker into the editor widget

This commit is contained in:
Kovid Goyal 2014-12-24 07:55:43 +05:30
parent dedd6c84aa
commit 8943e2883c
6 changed files with 70 additions and 4 deletions

View File

@ -83,7 +83,8 @@ class Boss(QObject):
setup_cssutils_serialization()
_boss = self
self.gui = parent
completion_worker()
completion_worker().result_callback = self.handle_completion_result
self.completion_request_count = 0
def __call__(self, gui):
self.gui = gui
@ -671,6 +672,17 @@ class Boss(QObject):
' Mark the book as having been modified '
self.gui.action_save.setEnabled(True)
def request_completion(self, name, completion_type, completion_data, query=None):
request_id = (self.completion_request_count, name)
self.completion_request_count += 1
completion_worker().queue_completion(request_id, completion_type, completion_data, query)
def handle_completion_result(self, result):
name = result.request_id[1]
editor = editors.get(name)
if editor is not None:
editor.handle_completion_result(result)
def fix_html(self, current):
if current:
ed = self.gui.central.current_editor
@ -1164,6 +1176,8 @@ class Boss(QObject):
editor.link_clicked.connect(self.editor_link_clicked)
if getattr(editor, 'syntax', None) == 'html':
editor.smart_highlighting_updated.connect(self.gui.live_css.sync_to_editor)
if hasattr(editor, 'set_request_completion'):
editor.set_request_completion(partial(self.request_completion, name), name)
if data is not None:
if use_template:
editor.init_from_template(data)

View File

@ -79,7 +79,7 @@ class HandleDataRequest(QObject):
# Ensure data is obtained in the GUI thread
call = pyqtSignal(object, object, object)
call = pyqtSignal(object, object)
def __init__(self):
QObject.__init__(self)

View File

@ -33,3 +33,6 @@ class NullSmarts(object):
def handle_key_press(self, ev, editor):
return False
def get_completion_data(self, editor, ev=None):
return None

View File

@ -16,7 +16,7 @@ from calibre import prepare_string_for_xml, xml_entity_to_unicode
from calibre.gui2 import error_dialog
from calibre.gui2.tweak_book.editor.syntax.html import ATTR_NAME, ATTR_END, ATTR_START, ATTR_VALUE
from calibre.utils.icu import utf16_length
from calibre.gui2.tweak_book import tprefs
from calibre.gui2.tweak_book import tprefs, current_container
from calibre.gui2.tweak_book.editor.smarts import NullSmarts
from calibre.gui2.tweak_book.editor.smarts.utils import (
no_modifiers, get_leading_whitespace_on_block, get_text_before_cursor,
@ -288,6 +288,7 @@ class Smarts(NullSmarts):
Smarts.closing_tag_pat = re.compile(r'<\s*/[^>]+>')
Smarts.closing_pat = re.compile(r'<\s*/')
Smarts.self_closing_pat = re.compile(r'/\s*>')
Smarts.complete_attr_pat = re.compile(r'''([a-zA-Z0-9_-]+)\s*=\s*(?:'([^']+)|"([^"]+))$''')
NullSmarts.__init__(self, *args, **kwargs)
self.last_matched_tag = None
@ -625,7 +626,29 @@ class Smarts(NullSmarts):
editor.setTextCursor(c)
return True
if __name__ == '__main__':
def get_completion_data(self, editor, ev=None):
c = editor.textCursor()
block, offset = c.block(), c.positionInBlock()
oblock, boundary = next_tag_boundary(block, offset, forward=False)
if boundary is None or not boundary.is_start or boundary.closing:
# Not inside a opening tag definition
return
tagname = boundary.name.lower()
startpos = oblock.position() + boundary.offset
c.setPosition(c.position()), c.setPosition(startpos, c.KeepAnchor)
text = c.selectedText()
m = self.complete_attr_pat.search(text)
if m is None:
return
attr = m.group(1).lower().split(':')[-1]
doc_name = editor.highlighter.doc_name
if doc_name and attr in {'href', 'src'}:
# A link
query = m.group(2) or m.group(3)
names_type = {'a':'text_link', 'img':'image', 'image':'image', 'link':'stylesheet'}.get(tagname)
return 'complete_names', (names_type, doc_name, current_container().root), query
if __name__ == '__main__': # {{{
from calibre.gui2.tweak_book.editor.widget import launch_editor
launch_editor('''\
<!DOCTYPE html>
@ -657,3 +680,4 @@ if __name__ == '__main__':
</body>
</html>
''', path_is_raw=True, syntax='xml')
# }}}

View File

@ -19,6 +19,7 @@ from PyQt5.Qt import (
from calibre import prepare_string_for_xml
from calibre.constants import isosx
from calibre.gui2.tweak_book import tprefs, TOP
from calibre.gui2.tweak_book.completion.popup import CompletionPopup
from calibre.gui2.tweak_book.editor import (
SYNTAX_PROPERTY, SPELL_PROPERTY, SPELL_LOCALE_PROPERTY, store_locale, LINK_PROPERTY)
from calibre.gui2.tweak_book.editor.themes import get_theme, theme_color, theme_format
@ -138,6 +139,8 @@ class TextEdit(PlainTextEdit):
def __init__(self, parent=None, expected_geometry=(100, 50)):
PlainTextEdit.__init__(self, parent)
self.completion_popup = CompletionPopup(self)
self.request_completion = self.completion_doc_name = None
self.gutter_width = 0
self.tw = 2
self.expected_geometry = expected_geometry
@ -769,8 +772,21 @@ class TextEdit(PlainTextEdit):
ev.setAccepted(False)
return
if self.smarts.handle_key_press(ev, self):
self.handle_keypress_completion(ev)
return
QPlainTextEdit.keyPressEvent(self, ev)
self.handle_keypress_completion(ev)
def handle_keypress_completion(self, ev):
if self.request_completion is None:
return
result = self.smarts.get_completion_data(self, ev)
if result is None:
return
self.request_completion(*result)
def handle_completion_result(self, result):
print (result)
def replace_possible_unicode_sequence(self):
c = self.textCursor()

View File

@ -173,6 +173,7 @@ class Editor(QMainWindow):
def change_document_name(self, newname):
self.editor.change_document_name(newname)
self.editor.completion_doc_name = newname
def get_raw_data(self):
# The EPUB spec requires NFC normalization, see section 1.3.6 of
@ -220,6 +221,13 @@ class Editor(QMainWindow):
tprefs['insert_tag_mru'] = mru
self._build_insert_tag_button_menu()
def set_request_completion(self, callback=None, doc_name=None):
self.editor.request_completion = callback
self.editor.completion_doc_name = doc_name
def handle_completion_result(self, result):
return self.editor.handle_completion_result(result)
def undo(self):
self.editor.undo()
@ -367,6 +375,7 @@ class Editor(QMainWindow):
self.editor.smart_highlighting_updated.disconnect()
self.editor.setPlainText('')
self.editor.smarts = None
self.editor.request_completion = None
def _modification_state_changed(self):
self.is_synced_to_container = self.is_modified