mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Highlight CSS inside <style> tags
This commit is contained in:
parent
a5cd87de72
commit
4d405d7b90
@ -19,13 +19,12 @@ class SimpleState(object):
|
|||||||
def value(self):
|
def value(self):
|
||||||
return self.parse
|
return self.parse
|
||||||
|
|
||||||
def run_loop(state, state_map, set_format, formats, text):
|
def run_loop(state, state_map, formats, text):
|
||||||
i = 0
|
i = 0
|
||||||
while i < len(text):
|
while i < len(text):
|
||||||
fmt = state_map[state.parse](state, text, i, formats)
|
fmt = state_map[state.parse](state, text, i, formats)
|
||||||
for num, f in fmt:
|
for num, f in fmt:
|
||||||
if f is not None:
|
yield i, num, f
|
||||||
set_format(i, num, f)
|
|
||||||
i += num
|
i += num
|
||||||
|
|
||||||
class SyntaxHighlighter(QSyntaxHighlighter):
|
class SyntaxHighlighter(QSyntaxHighlighter):
|
||||||
@ -57,7 +56,9 @@ class SyntaxHighlighter(QSyntaxHighlighter):
|
|||||||
if state == -1:
|
if state == -1:
|
||||||
state = 0
|
state = 0
|
||||||
state = self.state_class(state)
|
state = self.state_class(state)
|
||||||
run_loop(state, self.state_map, self.setFormat, self.formats, unicode(text))
|
for i, num, fmt in run_loop(state, self.state_map, self.formats, unicode(text)):
|
||||||
|
if fmt is not None:
|
||||||
|
self.setFormat(i, num, fmt)
|
||||||
self.setCurrentBlockState(state.value)
|
self.setCurrentBlockState(state.value)
|
||||||
except:
|
except:
|
||||||
import traceback
|
import traceback
|
||||||
|
@ -10,7 +10,9 @@ import re
|
|||||||
|
|
||||||
from PyQt4.Qt import (QTextCharFormat, QFont)
|
from PyQt4.Qt import (QTextCharFormat, QFont)
|
||||||
|
|
||||||
from calibre.gui2.tweak_book.editor.syntax.base import SyntaxHighlighter
|
from calibre.gui2.tweak_book.editor.syntax.base import SyntaxHighlighter, run_loop
|
||||||
|
from calibre.gui2.tweak_book.editor.syntax.css import create_formats as create_css_formats, state_map as css_state_map, State as CSSState
|
||||||
|
|
||||||
from html5lib.constants import cdataElements, rcdataElements
|
from html5lib.constants import cdataElements, rcdataElements
|
||||||
|
|
||||||
cdata_tags = cdataElements | rcdataElements
|
cdata_tags = cdataElements | rcdataElements
|
||||||
@ -43,6 +45,7 @@ class State(object):
|
|||||||
SQ_VAL = 8
|
SQ_VAL = 8
|
||||||
DQ_VAL = 9
|
DQ_VAL = 9
|
||||||
CDATA = 10
|
CDATA = 10
|
||||||
|
CSS = 11
|
||||||
|
|
||||||
TAGS = {x:i+1 for i, x in enumerate(cdata_tags | bold_tags | italic_tags)}
|
TAGS = {x:i+1 for i, x in enumerate(cdata_tags | bold_tags | italic_tags)}
|
||||||
TAGS_RMAP = {v:k for k, v in TAGS.iteritems()}
|
TAGS_RMAP = {v:k for k, v in TAGS.iteritems()}
|
||||||
@ -53,15 +56,43 @@ class State(object):
|
|||||||
self.bold = (num >> 4) & 0b11111111
|
self.bold = (num >> 4) & 0b11111111
|
||||||
self.italic = (num >> 12) & 0b11111111
|
self.italic = (num >> 12) & 0b11111111
|
||||||
self.tag = self.TAGS_RMAP.get(num >> 20, self.UNKNOWN_TAG)
|
self.tag = self.TAGS_RMAP.get(num >> 20, self.UNKNOWN_TAG)
|
||||||
|
self.css = 0
|
||||||
|
if self.parse == State.CSS:
|
||||||
|
self.css = num >> 4
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def value(self):
|
def value(self):
|
||||||
|
if self.parse == State.CSS:
|
||||||
|
return ((self.parse & 0b1111) | (self.css << 4))
|
||||||
tag = self.TAGS.get(self.tag.lower(), 0)
|
tag = self.TAGS.get(self.tag.lower(), 0)
|
||||||
return ((self.parse & 0b1111) |
|
return ((self.parse & 0b1111) |
|
||||||
((max(0, self.bold) & 0b11111111) << 4) |
|
((max(0, self.bold) & 0b11111111) << 4) |
|
||||||
((max(0, self.italic) & 0b11111111) << 12) |
|
((max(0, self.italic) & 0b11111111) << 12) |
|
||||||
(tag << 20))
|
(tag << 20))
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self.parse = self.bold = self.italic = self.css = 0
|
||||||
|
self.tag = self.UNKNOWN_TAG
|
||||||
|
|
||||||
|
def css(state, text, i, formats):
|
||||||
|
' Inside a <style> tag '
|
||||||
|
pat = cdata_close_pats['style']
|
||||||
|
m = pat.search(text, i)
|
||||||
|
if m is None:
|
||||||
|
css_text = text[i:]
|
||||||
|
else:
|
||||||
|
css_text = text[i:m.start()]
|
||||||
|
ans = []
|
||||||
|
css_state = CSSState(state.css)
|
||||||
|
for j, num, fmt in run_loop(css_state, css_state_map, state.css_formats, css_text):
|
||||||
|
ans.append((num, fmt))
|
||||||
|
state.css = css_state.value
|
||||||
|
if m is not None:
|
||||||
|
state.clear()
|
||||||
|
state.parse = State.IN_CLOSING_TAG
|
||||||
|
ans.extend([(2, formats['end_tag']), (len(m.group()) - 2, formats['tag_name'])])
|
||||||
|
return ans
|
||||||
|
|
||||||
def cdata(state, text, i, formats):
|
def cdata(state, text, i, formats):
|
||||||
'CDATA inside tags like <title> or <style>'
|
'CDATA inside tags like <title> or <style>'
|
||||||
pat = cdata_close_pats[state.tag]
|
pat = cdata_close_pats[state.tag]
|
||||||
@ -152,6 +183,9 @@ def opening_tag(state, text, i, formats):
|
|||||||
tag = state.tag.lower()
|
tag = state.tag.lower()
|
||||||
if tag in cdata_tags:
|
if tag in cdata_tags:
|
||||||
state.parse = state.CDATA
|
state.parse = state.CDATA
|
||||||
|
if tag == 'style':
|
||||||
|
state.clear()
|
||||||
|
state.parse = state.CSS
|
||||||
state.bold += int(tag in bold_tags)
|
state.bold += int(tag in bold_tags)
|
||||||
state.italic += int(tag in italic_tags)
|
state.italic += int(tag in italic_tags)
|
||||||
return [(1, formats['tag'])]
|
return [(1, formats['tag'])]
|
||||||
@ -241,6 +275,7 @@ state_map = {
|
|||||||
State.ATTRIBUTE_NAME: attribute_name,
|
State.ATTRIBUTE_NAME: attribute_name,
|
||||||
State.ATTRIBUTE_VALUE: attribute_value,
|
State.ATTRIBUTE_VALUE: attribute_value,
|
||||||
State.CDATA: cdata,
|
State.CDATA: cdata,
|
||||||
|
State.CSS: css,
|
||||||
}
|
}
|
||||||
|
|
||||||
for x in (State.IN_COMMENT, State.IN_PI, State.IN_DOCTYPE):
|
for x in (State.IN_COMMENT, State.IN_PI, State.IN_DOCTYPE):
|
||||||
@ -287,6 +322,16 @@ class HTMLHighlighter(SyntaxHighlighter):
|
|||||||
state_class = State
|
state_class = State
|
||||||
create_formats_func = create_formats
|
create_formats_func = create_formats
|
||||||
|
|
||||||
|
def create_formats(self):
|
||||||
|
super(HTMLHighlighter, self).create_formats()
|
||||||
|
self.css_formats = create_css_formats(self)
|
||||||
|
self.state_class = self.create_state
|
||||||
|
|
||||||
|
def create_state(self, val):
|
||||||
|
ans = State(val)
|
||||||
|
ans.css_formats = self.css_formats
|
||||||
|
return ans
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
from calibre.gui2.tweak_book.editor.text import launch_editor
|
from calibre.gui2.tweak_book.editor.text import launch_editor
|
||||||
launch_editor('''\
|
launch_editor('''\
|
||||||
@ -301,6 +346,7 @@ if __name__ == '__main__':
|
|||||||
font-size: 12pt;
|
font-size: 12pt;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<style type="text/css">p.small { font-size: x-small; color:gray }</style>
|
||||||
</head id="invalid attribute on closing tag">
|
</head id="invalid attribute on closing tag">
|
||||||
<body>
|
<body>
|
||||||
<!-- The start of the actual body text -->
|
<!-- The start of the actual body text -->
|
||||||
|
Loading…
x
Reference in New Issue
Block a user