Completion for anchors

This commit is contained in:
Kovid Goyal 2014-12-29 11:35:30 +05:30
parent 709e04bd16
commit 5bcd936470
2 changed files with 72 additions and 3 deletions

View File

@ -12,9 +12,11 @@ from collections import namedtuple, OrderedDict
from PyQt5.Qt import QObject, pyqtSignal, Qt from PyQt5.Qt import QObject, pyqtSignal, Qt
from calibre import prepare_string_for_xml from calibre import prepare_string_for_xml
from calibre.ebooks.oeb.base import xml2text
from calibre.ebooks.oeb.polish.container import OEB_STYLES, OEB_FONTS, name_to_href from calibre.ebooks.oeb.polish.container import OEB_STYLES, OEB_FONTS, name_to_href
from calibre.ebooks.oeb.polish.parsing import parse
from calibre.gui2 import is_gui_thread from calibre.gui2 import is_gui_thread
from calibre.gui2.tweak_book import current_container from calibre.gui2.tweak_book import current_container, editors
from calibre.gui2.tweak_book.completion.utils import control, data, DataError from calibre.gui2.tweak_book.completion.utils import control, data, DataError
from calibre.utils.ipc import eintr_retry_call from calibre.utils.ipc import eintr_retry_call
from calibre.utils.matcher import Matcher from calibre.utils.matcher import Matcher
@ -44,6 +46,13 @@ def names_data(request_data):
c = current_container() c = current_container()
return c.mime_map, {n for n, is_linear in c.spine_names} return c.mime_map, {n for n, is_linear in c.spine_names}
@data
def file_data(name):
'Get the data for name. Returns a unicode string if name is a text document/stylesheet'
if name in editors:
return editors[name].get_raw_data()
return current_container().raw_data(name)
def get_data(data_conn, data_type, data=None): def get_data(data_conn, data_type, data=None):
eintr_retry_call(data_conn.send, Request(None, data_type, data, None)) eintr_retry_call(data_conn.send, Request(None, data_type, data, None))
result, tb = eintr_retry_call(data_conn.recv) result, tb = eintr_retry_call(data_conn.recv)
@ -81,6 +90,57 @@ def complete_names(names_data, data_conn):
descriptions = {href:d(name) for name, href in nmap.iteritems()} descriptions = {href:d(name) for name, href in nmap.iteritems()}
return items, descriptions, {} return items, descriptions, {}
def description_for_anchor(elem):
def check(x, min_len=4):
if x:
x = x.strip()
if len(x) >= min_len:
return x[:30]
desc = check(elem.get('title'))
if desc is not None:
return desc
desc = check(elem.text)
if desc is not None:
return desc
if len(elem) > 0:
desc = check(elem[0].text)
if desc is not None:
return desc
# Get full text for tags that have only a few descendants
for i, x in enumerate(elem.iterdescendants('*')):
if i > 5:
break
else:
desc = check(xml2text(elem), min_len=1)
if desc is not None:
return desc
def create_anchor_map(root):
ans = {}
for elem in root.xpath('//*[@id or @name]'):
anchor = elem.get('id') or elem.get('name')
if anchor and anchor not in ans:
ans[anchor] = description_for_anchor(elem)
return ans
@control
def complete_anchor(name, data_conn):
if name not in file_cache:
data = raw = get_data(data_conn, 'file_data', name)
if isinstance(raw, type('')):
try:
root = parse(raw, decoder=lambda x:x.decode('utf-8'))
except Exception:
pass
else:
data = (root, create_anchor_map(root))
file_cache[name] = data
data = file_cache[name]
if isinstance(data, tuple) and len(data) > 1 and isinstance(data[1], dict):
return frozenset(data[1]), data[1], {}
_current_matcher = (None, None, None) _current_matcher = (None, None, None)
def handle_control_request(request, data_conn): def handle_control_request(request, data_conn):

View File

@ -13,14 +13,15 @@ from cssutils import parseStyle
from PyQt5.Qt import QTextEdit, Qt from PyQt5.Qt import QTextEdit, Qt
from calibre import prepare_string_for_xml, xml_entity_to_unicode from calibre import prepare_string_for_xml, xml_entity_to_unicode
from calibre.ebooks.oeb.polish.container import OEB_DOCS
from calibre.gui2 import error_dialog 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.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, current_container 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 import NullSmarts
from calibre.gui2.tweak_book.editor.smarts.utils import ( from calibre.gui2.tweak_book.editor.smarts.utils import (
no_modifiers, get_leading_whitespace_on_block, get_text_before_cursor, no_modifiers, get_leading_whitespace_on_block, get_text_before_cursor,
get_text_after_cursor, smart_home, smart_backspace, smart_tab, expand_tabs) get_text_after_cursor, smart_home, smart_backspace, smart_tab, expand_tabs)
from calibre.utils.icu import utf16_length
get_offset = itemgetter(0) get_offset = itemgetter(0)
PARAGRAPH_SEPARATOR = '\u2029' PARAGRAPH_SEPARATOR = '\u2029'
@ -646,8 +647,16 @@ class Smarts(NullSmarts):
if doc_name and attr in {'href', 'src'}: if doc_name and attr in {'href', 'src'}:
# A link # A link
query = m.group(2) or m.group(3) or '' query = m.group(2) or m.group(3) or ''
c = current_container()
names_type = {'a':'text_link', 'img':'image', 'image':'image', 'link':'stylesheet'}.get(tagname) names_type = {'a':'text_link', 'img':'image', 'image':'image', 'link':'stylesheet'}.get(tagname)
return 'complete_names', (names_type, doc_name, current_container().root), query idx = query.find('#')
if idx > -1 and names_type in (None, 'text_link'):
href, query = query[:idx], query[idx+1:]
name = c.href_to_name(href) if href else doc_name
if c.mime_map.get(name) in OEB_DOCS:
return 'complete_anchor', name, query
return 'complete_names', (names_type, doc_name, c.root), query
if __name__ == '__main__': # {{{ if __name__ == '__main__': # {{{
from calibre.gui2.tweak_book.editor.widget import launch_editor from calibre.gui2.tweak_book.editor.widget import launch_editor