Implement navigation to rules defined in stylesheets

This commit is contained in:
Kovid Goyal 2014-05-21 11:15:15 +05:30
parent d993534dfc
commit 4febe3c455
6 changed files with 98 additions and 1 deletions

View File

@ -25,3 +25,6 @@ class NullSmarts(object):
def goto_sourceline(self, editor, sourceline, tags, attribute=None): def goto_sourceline(self, editor, sourceline, tags, attribute=None):
return False return False
def get_inner_HTML(self, editor):
return None

View File

@ -0,0 +1,31 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
from __future__ import (unicode_literals, division, absolute_import,
print_function)
__license__ = 'GPL v3'
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
from calibre.gui2.tweak_book.editor.smart import NullSmarts
def find_rule(raw, rule_address):
import tinycss
parser = tinycss.make_parser()
sheet = parser.parse_stylesheet(raw)
rules = sheet.rules
ans = None, None
while rule_address:
try:
r = rules[rule_address[0]]
except IndexError:
return None, None
else:
ans = r.line, r.column
rule_address = rule_address[1:]
if rule_address:
rules = getattr(r, 'rules', ())
return ans
class CSSSmarts(NullSmarts):
pass

View File

@ -383,3 +383,27 @@ class HTMLSmarts(NullSmarts):
editor.setTextCursor(c) editor.setTextCursor(c)
return found_tag return found_tag
def get_inner_HTML(self, editor):
''' Select the inner HTML of the current tag. Return a cursor with the
inner HTML selected or None. '''
c = editor.textCursor()
block = c.block()
offset = c.position() - block.position()
nblock, boundary = next_tag_boundary(block, offset)
if boundary.is_start:
# We are within the contents of a tag already
tag = find_closest_containing_tag(block, offset)
else:
# We are inside a tag definition < | >
if boundary.self_closing:
return None # self closing tags have no inner html
tag = find_closest_containing_tag(nblock, boundary.offset + 1)
if tag is None:
return None
ctag = find_closing_tag(tag)
if ctag is None:
return None
c.setPosition(tag.end_block.position() + tag.end_offset + 1)
c.setPosition(ctag.start_block.position() + ctag.start_offset, c.KeepAnchor)
return c

View File

@ -25,6 +25,7 @@ from calibre.gui2.tweak_book.editor.syntax.html import HTMLHighlighter, XMLHighl
from calibre.gui2.tweak_book.editor.syntax.css import CSSHighlighter from calibre.gui2.tweak_book.editor.syntax.css import CSSHighlighter
from calibre.gui2.tweak_book.editor.smart import NullSmarts from calibre.gui2.tweak_book.editor.smart import NullSmarts
from calibre.gui2.tweak_book.editor.smart.html import HTMLSmarts from calibre.gui2.tweak_book.editor.smart.html import HTMLSmarts
from calibre.gui2.tweak_book.editor.smart.css import CSSSmarts
from calibre.spell.break_iterator import index_of from calibre.spell.break_iterator import index_of
from calibre.utils.icu import safe_chr, string_length from calibre.utils.icu import safe_chr, string_length
@ -211,7 +212,7 @@ class TextEdit(PlainTextEdit):
self.highlighter = get_highlighter(syntax)() self.highlighter = get_highlighter(syntax)()
self.highlighter.apply_theme(self.theme) self.highlighter.apply_theme(self.theme)
self.highlighter.set_document(self.document()) self.highlighter.set_document(self.document())
sclass = {'html':HTMLSmarts, 'xml':HTMLSmarts}.get(syntax, None) sclass = {'html':HTMLSmarts, 'xml':HTMLSmarts, 'css':CSSSmarts}.get(syntax, None)
if sclass is not None: if sclass is not None:
self.smarts = sclass(self) self.smarts = sclass(self)
self.setPlainText(unicodedata.normalize('NFC', text)) self.setPlainText(unicodedata.normalize('NFC', text))
@ -755,3 +756,31 @@ class TextEdit(PlainTextEdit):
def goto_sourceline(self, sourceline, tags, attribute=None): def goto_sourceline(self, sourceline, tags, attribute=None):
return self.smarts.goto_sourceline(self, sourceline, tags, attribute=attribute) return self.smarts.goto_sourceline(self, sourceline, tags, attribute=attribute)
def get_tag_contents(self):
c = self.smarts.get_inner_HTML(self)
if c is not None:
return self.selected_text_from_cursor(c)
def goto_css_rule(self, rule_address, sourceline_address=None):
from calibre.gui2.tweak_book.editor.smart.css import find_rule
block = None
if self.syntax == 'css':
raw = unicode(self.toPlainText())
line, col = find_rule(raw, rule_address)
if line is not None:
block = self.document().findBlockByNumber(line - 1)
elif sourceline_address is not None:
sourceline, tags = sourceline_address
if self.goto_sourceline(sourceline, tags):
c = self.textCursor()
c.setPosition(c.position() + 1)
self.setTextCursor(c)
raw = self.get_tag_contents()
line, col = find_rule(raw, rule_address)
if line is not None:
block = self.document().findBlockByNumber(c.blockNumber() + line - 1)
if block is not None and block.isValid():
c = self.textCursor()
c.setPosition(block.position() + col)
self.setTextCursor(c)

View File

@ -406,6 +406,12 @@ class Editor(QMainWindow):
def goto_sourceline(self, *args, **kwargs): def goto_sourceline(self, *args, **kwargs):
return self.editor.goto_sourceline(*args, **kwargs) return self.editor.goto_sourceline(*args, **kwargs)
def goto_css_rule(self, *args, **kwargs):
return self.editor.goto_css_rule(*args, **kwargs)
def get_tag_contents(self, *args, **kwargs):
return self.editor.get_tag_contents(*args, **kwargs)
def _nuke_word(self, dic, word, locale): def _nuke_word(self, dic, word, locale):
if dic is None: if dic is None:
dictionaries.ignore_word(word, locale) dictionaries.ignore_word(word, locale)

View File

@ -412,4 +412,8 @@ class LiveCSS(QWidget):
if data['type'] == 'inline': if data['type'] == 'inline':
sourceline, tags = data['sourceline_address'] sourceline, tags = data['sourceline_address']
editor.goto_sourceline(sourceline, tags, attribute='style') editor.goto_sourceline(sourceline, tags, attribute='style')
elif data['type'] == 'sheet':
editor.goto_css_rule(data['rule_address'])
elif data['type'] == 'elem':
editor.goto_css_rule(data['rule_address'], sourceline_address=data['sourceline_address'])