おけ。
あっちゅうまに出来た。
1 # -*- coding: utf-8 -*-
2 """
3 pygments.lexers.markup
4 ~~~~~~~~~~~~~~~~~~~~~~
5
6 Lexers for non-HTML markup languages.
7
8 :copyright: Copyright 2006-2015 by the Pygments team, see AUTHORS.
9 :license: BSD, see LICENSE for details.
10 """
11
12 import re
13
14 from pygments.lexers.html import HtmlLexer, XmlLexer
15 from pygments.lexers.javascript import JavascriptLexer
16 from pygments.lexers.css import CssLexer
17
18 from pygments.lexer import RegexLexer, DelegatingLexer, include, bygroups, \
19 using, this, do_insertions, default, words
20 from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
21 Number, Punctuation, Generic, Other
22 from pygments.util import get_bool_opt, ClassNotFound
23
24 __all__ = ['BBCodeLexer', 'MoinWikiLexer', 'RstLexer', 'TexLexer', 'GroffLexer',
25 'MozPreprocHashLexer', 'MozPreprocPercentLexer',
26 'MozPreprocXulLexer', 'MozPreprocJavascriptLexer',
27 'MozPreprocCssLexer', 'Creole10Lexer']
28
29 # ...
30
31
32 # internal tokens for Creole10Lexer
33 from pygments.token import _TokenType
34 _InnerToken = _TokenType()
35 _StrongInHold = _InnerToken.StrongInHold
36 _ItalicInHold = _InnerToken.ItalicInHold
37 _ParaEnd = _InnerToken.ParaEnd
38
39
40 class Creole10Lexer(RegexLexer):
41 """
42 For `Creole 1.0 <http://www.wikicreole.org/wiki/Creole1.0>`_ markup.
43
44 .. versionadded:: 2.1
45 """
46
47 name = 'Creole 1.0 markup'
48 aliases = ['creole10']
49 filenames = []
50 mimetypes = []
51 flags = re.MULTILINE | re.DOTALL
52
53 tokens = {
54 'root': [
55 # Escape
56 (r'~.', Text),
57
58 # Headings
59 # Whitespace **is** allowed before the left-side equal signs.
60 # Only whitespace characters are permitted after the closing
61 # equal signs. Markup parsing is optional within headings.
62 (r'^s*={1,6}[^\n]*$', Generic.Heading),
63
64 # Placeholder
65 (r'(<<<)([^\n]*?)(>>>)',
66 bygroups(Generic.Subheading, Keyword, Generic.Subheading)),
67
68 # Nowiki (Preformatted)
69 #
70 # In inline nowiki, any trailing closing brace is included
71 # in the nowiki span (i.e. in a sequence of more than three
72 # closing braces, the end marker is made of the //last three braces//).
73 (r'({{{)([^\n]*?)(}}})(?!})',
74 bygroups(Generic.Subheading, Comment.Preproc, Generic.Subheading)),
75
76 # As a block, the three curly braces should be on one line by
77 # itself to open and another line of three curly braces should
78 # be on a line by itself to close, without leading spaces.
79 (r'^{{{', Generic.Subheading, 'nowikiblock'),
80
81 # Links
82 (r'(\[\[)([^[\]|]+)(\|)([^[\]|]+)(\]\])',
83 bygroups(
84 Punctuation,
85 Keyword, Punctuation, String,
86 Punctuation)),
87 (r'(\[\[)([^[\]|]+)(\]\])',
88 bygroups(
89 Punctuation, Keyword, Punctuation)),
90
91 # Image (inline)
92 (r'({{)([^[\]|]+)(\|)([^[\]|]+)(}})',
93 bygroups(
94 Punctuation,
95 Keyword, Punctuation, String,
96 Punctuation)),
97 (r'({{)([^[\]|]+)(}})',
98 bygroups(
99 Punctuation, Keyword, Punctuation)),
100
101 # Unordered Lists, Ordered Lists
102 (r'^\s*\*\*\S', _StrongInHold), # bold special case
103 (r'^\s*\*+', Generic.Subheading),
104 (r'^\s*#+', Generic.Subheading),
105
106 # Table (cell separator)
107 (r'\|=?', Generic.Subheading),
108
109 # Horizontal Rule
110 # Whitespace is optional before and after the hyphens, but no
111 # whitespace is allowed between them. The four hyphens must
112 # be the only characters (other than whitespace) on that line.
113 (r'\s*\-{4}\s*$', Generic.Subheading),
114
115 # Bold and Italics
116 # Note that `Creole 1.0 <http://www.wikicreole.org/wiki/Creole1.0>`_
117 # says `__**xxx__**` and `**__xxx**__` are not acceptable,
118 # but some engines (for example `wikicreole <https://bitbucket.org/thesheep/wikicreole>`_)
119 # allow these.
120 (r'\*\*', _StrongInHold),
121 (r'(http|https|ftp|nntp|news|mailto|telnet|file|irc)://', Text),
122 (r'//', _ItalicInHold),
123 (r'\n\n', _ParaEnd),
124
125 (r'.', Text),
126 ],
127 'nowikiblock': [
128 (r'~.', Comment.Preproc),
129 (r'^}}}', Generic.Subheading, '#pop'),
130 (r'[^~}]+', Comment.Preproc),
131 (r'.', Comment.Preproc),
132 ],
133 }
134
135 in_hold_tk = []
136 in_hold_iv = []
137
138 def _flush(self, acc, idx=None, tok=None, val=None):
139 if tok is not None:
140 self.in_hold_tk.append(tok)
141 self.in_hold_iv.append((idx, val))
142 if not self.in_hold_tk:
143 return
144
145 stk = Generic.Strong if acc else Text
146 itk = Generic.Emph if acc else Text
147 ctk = Text
148 for i, tk in enumerate(self.in_hold_tk):
149 modtk = tk
150 if tk == _StrongInHold:
151 modtk = stk
152 ctk = modtk
153 elif tk == _ItalicInHold:
154 modtk = itk
155 ctk = modtk
156 elif tk == _ParaEnd:
157 modtk = Text
158 ctk = modtk
159 yield self.in_hold_iv[i][0], ctk, self.in_hold_iv[i][1]
160 self.in_hold_tk = []
161 self.in_hold_iv = []
162
163 def get_tokens_unprocessed(self, text):
164 self.in_hold_tk = []
165 self.in_hold_iv = []
166
167 #
168 def _extract_innertoks():
169 return [t for t in self.in_hold_tk
170 if t in (_StrongInHold, _ItalicInHold, _ParaEnd)]
171
172 #
173 for idx, tok, val in RegexLexer.get_tokens_unprocessed(self, text):
174 can_yield = False
175 if self.in_hold_tk and tok not in (
176 _StrongInHold, _ItalicInHold, _ParaEnd):
177
178 self.in_hold_tk.append(tok)
179 self.in_hold_iv.append((idx, val))
180
181 elif tok == _StrongInHold:
182 if _StrongInHold not in self.in_hold_tk:
183 self.in_hold_tk.append(tok)
184 self.in_hold_iv.append((idx, val))
185 else:
186 tmp = _extract_innertoks()
187 # S-S: acceptable
188 # S-I-I-S: acceptable
189 if tmp == [_StrongInHold] or \
190 tmp == [_StrongInHold, _ItalicInHold,
191 _ItalicInHold]:
192 for i, t, v in self._flush(True, idx, tok, val):
193 yield i, t, v
194 # S-I-S: unacceptable
195 elif tmp == [_StrongInHold, _ItalicInHold]:
196 for i, t, v in self._flush(False, idx, tok, val):
197 yield i, t, v
198 # else(I-S-S): keep in-hold
199 else:
200 self.in_hold_tk.append(tok)
201 self.in_hold_iv.append((idx, val))
202
203 elif tok == _ItalicInHold:
204 if _ItalicInHold not in self.in_hold_tk:
205 self.in_hold_tk.append(tok)
206 self.in_hold_iv.append((idx, val))
207 else:
208 tmp = _extract_innertoks()
209 # I-I: acceptable
210 # I-S-S-I: acceptable
211 if tmp == [_ItalicInHold] or \
212 tmp == [_ItalicInHold, _StrongInHold,
213 _StrongInHold]:
214 for i, t, v in self._flush(True, idx, tok, val):
215 yield i, t, v
216 # I-S-I: unacceptable
217 elif tmp == [_ItalicInHold, _StrongInHold]:
218 for i, t, v in self._flush(False, idx, tok, val):
219 yield i, t, v
220 # else(S-I-I): keep in-hold
221 else:
222 self.in_hold_tk.append(tok)
223 self.in_hold_iv.append((idx, val))
224
225 elif tok == _ParaEnd:
226 if self.in_hold_tk:
227 for i, t, v in self._flush(False, idx, tok, val):
228 yield i, t, v
229 else:
230 can_yield = True
231
232 else:
233 can_yield = True
234
235 if can_yield:
236 yield idx, tok, val
237 #
238 for i, t, v in self._flush(False):
239 yield i, t, v
ひとまず .. versionadded:: 2.1
にしてあるけど、まだ PR あげるつもりなくて、Texinfo とまとめてあげようかなと思ってるので、結果的に .. versionadded:: 2.2
になるかもしれんです。
「あっちゅうまに出来た」つぅ割には lexer がちょい複雑なのは、実は「BitBucket の Wiki を CREOLE で書くとちょいとヘンな話」「wikicreole と Creole1.0 と BitBucket の Creole もどきがそれぞれ別物な件」のどちらでも触れなかった、ちょいと繊細な仕様に関係してる。これ:
1 //**Bold italics**//\\
2 **//Bold italics//**\\
3 **This is //also// good**
4
5 Unacceptable:
6 **//bold italics**//
7 //**bold italics//**
「ちゃんと綺麗に囲めよ」と XML チックなことを要求してるわけね。これそのものは「御意」なんだけれど、ただし実現はかえって大変だ、てわけで。「Unacceptable」とは言ってるけどどう振る舞えとも言ってないので、「反応しない」という、実現が一番手間なものを選んだ。なんでこうしたかと言うと、出回ってるエンジンによってここの扱いが違っちゃってるから。
wikicreole だと、例えば「**//bold italics**//」を、「《ボールド開始》《イタリック開始》bold italic《イタリック開始》《ボールド開始》(をっと閉じてないので…)《ボールド終了》《イタリック終了》《ボールド終了》《イタリック終了》」なんつぅ空気の読み方をする。
で、これって違反じゃなあい? って作者にコンタクト取ってみたら、「別にどう振る舞えって言ってないじゃん、あたしゃ「寛大に受け取るけれども厳密に言われた通りに出力」、それでも最大限に空気読むことにしたんだぜ」と。どうも「Creole 1.0 の仕様策定者が特定の実現方法を念頭に置いた恣意的な仕様、なんだから従わなくたって別にいいじゃん」てのが本音らしい。
個人的にはこれは違うと思うのね。実装をベースに仕様を決めたんじゃなくて、ユーザのための便宜なんだと思うんだわ。つまり「プレインテキストとして読んだ場合の視認性」まで考えたんじゃないか、ってこと。(普通の括弧で考えてみればわかる。「 ( [ あいうえお ) ]
」って、気持ち悪いでしょーよ。
けどまぁ…「Wiki システムそのもの」が本題の wikicreole 作者にとっては当然「自分の wikicreole のユーザは wiki で執筆する人(つまりプレビューしながらトライアンドエラーで書く人)」という認識しか持たないで済んでるのは理解出来るし、「所詮 wiki」なわけで、なのでちょっと言いたいこともあったけど、言いたいことは飲み込んだ。どっちみち直して欲しくてコンタクト取ったわけでもなかったから。どういう考えで、どういう反応になるかな、って興味あったの、単に。(前にも書いたけど、既にコンテンツが書かれてるマークアップ言語の仕様を気軽に変えちゃダメなの。)
さて。いよいよ本題の Texinfo いきますか。