forked from tweecode/twine
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtweelexer.py
169 lines (131 loc) · 6.02 KB
/
tweelexer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#!/usr/bin/env python
#
# TweeLexer
# This lexes (or syntax highlights) Twee syntax in a wx.StyledTextCtrl.
# This needs to be passed the control it will be lexing, so it can
# look up the body text as needed.
#
import re, wx, wx.stc
class TweeLexer:
def __init__ (self, control, frame):
self.ctrl = control
self.frame = frame
self.app = frame.app
self.ctrl.Bind(wx.stc.EVT_STC_STYLENEEDED, self.lex)
self.initStyles()
def initStyles (self):
"""
Initialize style definitions. This is automatically invoked when
the constructor is called, but should be called any time font
preferences are changed.
"""
bodyFont = wx.Font(self.app.config.ReadInt('windowedFontSize'), wx.MODERN, wx.NORMAL, \
wx.NORMAL, False, self.app.config.Read('windowedFontFace'))
self.ctrl.StyleSetFont(TweeLexer.GOOD_LINK, bodyFont)
self.ctrl.StyleSetBold(TweeLexer.GOOD_LINK, True)
self.ctrl.StyleSetForeground(TweeLexer.GOOD_LINK, TweeLexer.GOOD_LINK_COLOR)
self.ctrl.StyleSetFont(TweeLexer.BAD_LINK, bodyFont)
self.ctrl.StyleSetBold(TweeLexer.BAD_LINK, True)
self.ctrl.StyleSetForeground(TweeLexer.BAD_LINK, TweeLexer.BAD_LINK_COLOR)
self.ctrl.StyleSetFont(TweeLexer.MARKUP, bodyFont)
self.ctrl.StyleSetForeground(TweeLexer.MARKUP, TweeLexer.MARKUP_COLOR)
self.ctrl.StyleSetFont(TweeLexer.MACRO, bodyFont)
self.ctrl.StyleSetBold(TweeLexer.MACRO, True)
self.ctrl.StyleSetForeground(TweeLexer.MACRO, TweeLexer.MACRO_COLOR)
def lex (self, event):
"""
Lexes, or applies syntax highlighting, to text based on a
wx.stc.EVT_STC_STYLENEEDED event.
"""
pos = 0 # should be self.ctrl.GetEndStyled(), but doesn't work
end = self.ctrl.GetLength()
text = self.ctrl.GetTextUTF8()
style = TweeLexer.DEFAULT
styleStart = pos
inMarkup = False
# we have to apply DEFAULT styles as necessary here, otherwise
# old style ranges stick around, and things look very strange
while pos < end:
styleChanged = False
# important: all style ends must be handled before beginnings
# otherwise we start treading on each other in certain circumstances
# end of markup
if inMarkup and ((text[pos - 2:pos] in TweeLexer.MARKUPS \
and pos > styleStart + 2) \
or text[pos - 7:pos] == '</html>'):
if not styleChanged:
self.applyStyle(styleStart, pos, style)
styleChanged = True
inMarkup = False
style = TweeLexer.DEFAULT
# end of macro
if text[pos - 2:pos] == '>>':
if not styleChanged:
self.applyStyle(styleStart, pos, style)
styleChanged = True
style = TweeLexer.DEFAULT
# end of link
if text[pos - 2:pos] == ']]':
if not styleChanged:
# check for prettylinks
link = text[styleStart + 2:pos - 2]
link = re.sub('.*\|', '', link)
ulink = link.decode('utf-8', 'replace')
if not self.passageExists(ulink):
style = TweeLexer.BAD_LINK
self.applyStyle(styleStart, pos, style)
styleChanged = True
style = TweeLexer.DEFAULT
# start of markup
if not inMarkup and (text[pos:pos + 2] in TweeLexer.MARKUPS \
or text[pos:pos + 6] == '<html>'):
if not styleChanged:
self.applyStyle(styleStart, pos, style)
styleChanged = True
inMarkup = True
style = TweeLexer.MARKUP
# start of macro
if text[pos:pos + 2] == '<<':
if not styleChanged:
self.applyStyle(styleStart, pos, style)
styleChanged = True
style = TweeLexer.MACRO
# start of link
if text[pos:pos + 2] == '[[':
if not styleChanged:
self.applyStyle(styleStart, pos, style)
styleChanged = True
style = TweeLexer.GOOD_LINK
if styleChanged: styleStart = pos
pos = pos + 1
if styleStart != pos:
# one last link check
if style == TweeLexer.GOOD_LINK and \
not self.passageExists(text[styleStart + 2:pos - 2]):
style = TweeLexer.BAD_LINK
self.applyStyle(styleStart, pos, style)
def passageExists (self, title):
"""
Returns whether a given passage exists in the story.
"""
return (self.frame.widget.parent.findWidget(title) != None)
def applyStyle (self, start, end, style):
"""
Applies a style to a certain range.
"""
self.ctrl.StartStyling(start, TweeLexer.TEXT_STYLES)
self.ctrl.SetStyling(end, style)
# markup constants
MARKUPS = ["''", "//", "__", "^^", "~~", "=="]
# style constants
DEFAULT = 0
GOOD_LINK = 1
BAD_LINK = 2
MARKUP = 3
MACRO = 4
# style colors
GOOD_LINK_COLOR = '#0000ff'
BAD_LINK_COLOR = '#ff0000'
MARKUP_COLOR = '#008200'
MACRO_COLOR = '#a94286'
TEXT_STYLES = 31 # mask for StartStyling() to indicate we're only changing text styles