mirror of
				https://github.com/kovidgoyal/calibre.git
				synced 2025-11-04 03:27:00 -05:00 
			
		
		
		
	Implement navigation to rules defined in stylesheets
This commit is contained in:
		
							parent
							
								
									d993534dfc
								
							
						
					
					
						commit
						4febe3c455
					
				@ -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
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										31
									
								
								src/calibre/gui2/tweak_book/editor/smart/css.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/calibre/gui2/tweak_book/editor/smart/css.py
									
									
									
									
									
										Normal 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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -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)
 | 
				
			||||||
 | 
				
			|||||||
@ -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)
 | 
				
			||||||
 | 
				
			|||||||
@ -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'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user