]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/stxview/StructuredText/ClassicDocumentClass.py
Got a new version of StructuredText from Zope's CVS.
[wxWidgets.git] / wxPython / samples / stxview / StructuredText / ClassicDocumentClass.py
1 ##############################################################################
2 #
3 # Zope Public License (ZPL) Version 1.0
4 # -------------------------------------
5 #
6 # Copyright (c) Digital Creations. All rights reserved.
7 #
8 # This license has been certified as Open Source(tm).
9 #
10 # Redistribution and use in source and binary forms, with or without
11 # modification, are permitted provided that the following conditions are
12 # met:
13 #
14 # 1. Redistributions in source code must retain the above copyright
15 # notice, this list of conditions, and the following disclaimer.
16 #
17 # 2. Redistributions in binary form must reproduce the above copyright
18 # notice, this list of conditions, and the following disclaimer in
19 # the documentation and/or other materials provided with the
20 # distribution.
21 #
22 # 3. Digital Creations requests that attribution be given to Zope
23 # in any manner possible. Zope includes a "Powered by Zope"
24 # button that is installed by default. While it is not a license
25 # violation to remove this button, it is requested that the
26 # attribution remain. A significant investment has been put
27 # into Zope, and this effort will continue if the Zope community
28 # continues to grow. This is one way to assure that growth.
29 #
30 # 4. All advertising materials and documentation mentioning
31 # features derived from or use of this software must display
32 # the following acknowledgement:
33 #
34 # "This product includes software developed by Digital Creations
35 # for use in the Z Object Publishing Environment
36 # (http://www.zope.org/)."
37 #
38 # In the event that the product being advertised includes an
39 # intact Zope distribution (with copyright and license included)
40 # then this clause is waived.
41 #
42 # 5. Names associated with Zope or Digital Creations must not be used to
43 # endorse or promote products derived from this software without
44 # prior written permission from Digital Creations.
45 #
46 # 6. Modified redistributions of any form whatsoever must retain
47 # the following acknowledgment:
48 #
49 # "This product includes software developed by Digital Creations
50 # for use in the Z Object Publishing Environment
51 # (http://www.zope.org/)."
52 #
53 # Intact (re-)distributions of any official Zope release do not
54 # require an external acknowledgement.
55 #
56 # 7. Modifications are encouraged but must be packaged separately as
57 # patches to official Zope releases. Distributions that do not
58 # clearly separate the patches from the original work must be clearly
59 # labeled as unofficial distributions. Modifications which do not
60 # carry the name Zope may be packaged in any form, as long as they
61 # conform to all of the clauses above.
62 #
63 #
64 # Disclaimer
65 #
66 # THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
67 # EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
68 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
69 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
70 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
71 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
72 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
73 # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
74 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
75 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
76 # OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
77 # SUCH DAMAGE.
78 #
79 #
80 # This software consists of contributions made by Digital Creations and
81 # many individuals on behalf of Digital Creations. Specific
82 # attributions are listed in the accompanying credits file.
83 #
84 ##############################################################################
85
86 import re, ST, STDOM
87 from string import split, join, replace, expandtabs, strip, find
88 from STletters import letters,lettpunc,punctuations
89
90 StringType=type('')
91 ListType=type([])
92
93 class StructuredTextExample(ST.StructuredTextParagraph):
94 """Represents a section of document with literal text, as for examples"""
95
96 def __init__(self, subs, **kw):
97 t=[]; a=t.append
98 for s in subs: a(s.getNodeValue())
99 apply(ST.StructuredTextParagraph.__init__,
100 (self, join(t,'\n\n'), ()),
101 kw)
102
103 def getColorizableTexts(self): return ()
104 def setColorizableTexts(self, src): pass # never color examples
105
106 class StructuredTextBullet(ST.StructuredTextParagraph):
107 """Represents a section of a document with a title and a body"""
108
109 class StructuredTextNumbered(ST.StructuredTextParagraph):
110 """Represents a section of a document with a title and a body"""
111
112 class StructuredTextDescriptionTitle(ST.StructuredTextParagraph):
113 """Represents a section of a document with a title and a body"""
114
115 class StructuredTextDescriptionBody(ST.StructuredTextParagraph):
116 """Represents a section of a document with a title and a body"""
117
118 class StructuredTextDescription(ST.StructuredTextParagraph):
119 """Represents a section of a document with a title and a body"""
120
121 def __init__(self, title, src, subs, **kw):
122 apply(ST.StructuredTextParagraph.__init__, (self, src, subs), kw)
123 self._title=title
124
125 def getColorizableTexts(self): return self._title, self._src
126 def setColorizableTexts(self, src): self._title, self._src = src
127
128 def getChildren(self):
129 return (StructuredTextDescriptionTitle(self._title),
130 StructuredTextDescriptionBody(self._src, self._subs))
131
132 class StructuredTextSectionTitle(ST.StructuredTextParagraph):
133 """Represents a section of a document with a title and a body"""
134
135 class StructuredTextSection(ST.StructuredTextParagraph):
136 """Represents a section of a document with a title and a body"""
137 def __init__(self, src, subs=None, **kw):
138 apply(ST.StructuredTextParagraph.__init__,
139 (self, StructuredTextSectionTitle(src), subs),
140 kw)
141
142 def getColorizableTexts(self):
143 return self._src.getColorizableTexts()
144
145 def setColorizableTexts(self,src):
146 self._src.setColorizableTexts(src)
147
148 # a StructuredTextTable holds StructuredTextRows
149 class StructuredTextTable(ST.StructuredTextDocument):
150 """
151 rows is a list of lists containing tuples, which
152 represent the columns/cells in each rows.
153 EX
154 rows = [[('row 1:column1',1)],[('row2:column1',1)]]
155 """
156
157 def __init__(self, rows, src, subs, **kw):
158 apply(ST.StructuredTextDocument.__init__,(self,subs),kw)
159 self._rows = []
160 for row in rows:
161 if row:
162 self._rows.append(StructuredTextRow(row,kw))
163
164 def getRows(self):
165 return [self._rows]
166
167 def _getRows(self):
168 return self.getRows()
169
170 def getColorizableTexts(self):
171 """
172 return a tuple where each item is a column/cell's
173 contents. The tuple, result, will be of this format.
174 ("r1 col1", "r1=col2", "r2 col1", "r2 col2")
175 """
176
177 #result = ()
178 result = []
179 for row in self._rows:
180 for column in row.getColumns()[0]:
181 #result = result[:] + (column.getColorizableTexts(),)
182 result.append(column.getColorizableTexts()[0])
183 return result
184
185 def setColorizableTexts(self,texts):
186 """
187 texts is going to a tuple where each item is the
188 result of being mapped to the colortext function.
189 Need to insert the results appropriately into the
190 individual columns/cells
191 """
192 for row_index in range(len(self._rows)):
193 for column_index in range(len(self._rows[row_index]._columns)):
194 self._rows[row_index]._columns[column_index].setColorizableTexts((texts[0],))
195 texts = texts[1:]
196
197 def _getColorizableTexts(self):
198 return self.getColorizableTexts()
199
200 def _setColorizableTexts(self):
201 return self.setColorizableTexts()
202
203 # StructuredTextRow holds StructuredTextColumns
204 class StructuredTextRow(ST.StructuredTextDocument):
205
206 def __init__(self,row,kw):
207 """
208 row is a list of tuples, where each tuple is
209 the raw text for a cell/column and the span
210 of that cell/column".
211 EX
212 [('this is column one',1), ('this is column two',1)]
213 """
214
215 apply(ST.StructuredTextDocument.__init__,(self,[]),kw)
216 self._columns = []
217 for column in row:
218 self._columns.append(StructuredTextColumn(column[0],column[1],kw))
219 def getColumns(self):
220 return [self._columns]
221
222 def _getColumns(self):
223 return [self._columns]
224
225 # this holds the raw text of a table cell
226 class StructuredTextColumn(ST.StructuredTextParagraph):
227 """
228 StructuredTextColumn is a cell/column in a table.
229 This contains the actual text of a column and is
230 thus a StructuredTextParagraph. A StructuredTextColumn
231 also holds the span of its column
232 """
233
234 def __init__(self,text,span,kw):
235 apply(ST.StructuredTextParagraph.__init__,(self,text,[]),kw)
236 self._span = span
237
238 def getSpan(self):
239 return self._span
240
241 def _getSpan(self):
242 return self._span
243
244 class StructuredTextMarkup(STDOM.Element):
245
246 def __init__(self, v, **kw):
247 self._value=v
248 self._attributes=kw.keys()
249 for k, v in kw.items(): setattr(self, k, v)
250
251 def getChildren(self, type=type, lt=type([])):
252 v=self._value
253 if type(v) is not lt: v=[v]
254 return v
255
256 def getColorizableTexts(self): return self._value,
257 def setColorizableTexts(self, v): self._value=v[0]
258
259 def __repr__(self):
260 return '%s(%s)' % (self.__class__.__name__, `self._value`)
261
262 class StructuredTextLiteral(StructuredTextMarkup):
263 def getColorizableTexts(self): return ()
264 def setColorizableTexts(self, v): pass
265
266 class StructuredTextEmphasis(StructuredTextMarkup): pass
267
268 class StructuredTextStrong(StructuredTextMarkup): pass
269
270 class StructuredTextInnerLink(StructuredTextMarkup): pass
271
272 class StructuredTextNamedLink(StructuredTextMarkup): pass
273
274 class StructuredTextUnderline(StructuredTextMarkup): pass
275
276 class StructuredTextLink(StructuredTextMarkup):
277 "A simple hyperlink"
278
279 class DocumentClass:
280 """
281 Class instance calls [ex.=> x()] require a structured text
282 structure. Doc will then parse each paragraph in the structure
283 and will find the special structures within each paragraph.
284 Each special structure will be stored as an instance. Special
285 structures within another special structure are stored within
286 the 'top' structure
287 EX : '-underline this-' => would be turned into an underline
288 instance. '-underline **this**' would be stored as an underline
289 instance with a strong instance stored in its string
290 """
291
292 paragraph_types = [
293 'doc_bullet',
294 'doc_numbered',
295 'doc_description',
296 'doc_header',
297 'doc_table',
298 ]
299
300 text_types = [
301 'doc_href',
302 'doc_strong',
303 'doc_emphasize',
304 'doc_literal',
305 'doc_inner_link',
306 'doc_named_link',
307 'doc_underline',
308 ]
309
310 def __call__(self, doc):
311 if type(doc) is type(''):
312 doc=ST.StructuredText(doc)
313 doc.setSubparagraphs(self.color_paragraphs(
314 doc.getSubparagraphs()))
315 else:
316 doc=ST.StructuredTextDocument(self.color_paragraphs(
317 doc.getSubparagraphs()))
318 return doc
319
320 def parse(self, raw_string, text_type,
321 type=type, st=type(''), lt=type([])):
322
323 """
324 Parse accepts a raw_string, an expr to test the raw_string,
325 and the raw_string's subparagraphs.
326
327 Parse will continue to search through raw_string until
328 all instances of expr in raw_string are found.
329
330 If no instances of expr are found, raw_string is returned.
331 Otherwise a list of substrings and instances is returned
332 """
333
334 tmp = [] # the list to be returned if raw_string is split
335 append=tmp.append
336
337 if type(text_type) is st: text_type=getattr(self, text_type)
338
339 while 1:
340 t = text_type(raw_string)
341 if not t: break
342 #an instance of expr was found
343 t, start, end = t
344
345 if start: append(raw_string[0:start])
346
347 tt=type(t)
348 if tt is st:
349 # if we get a string back, add it to text to be parsed
350 raw_string = t+raw_string[end:len(raw_string)]
351 else:
352 if tt is lt:
353 # is we get a list, append it's elements
354 tmp[len(tmp):]=t
355 else:
356 # normal case, an object
357 append(t)
358 raw_string = raw_string[end:len(raw_string)]
359
360 if not tmp: return raw_string # nothing found
361
362 if raw_string: append(raw_string)
363 elif len(tmp)==1: return tmp[0]
364
365 return tmp
366
367
368 def color_text(self, str, types=None):
369 """Search the paragraph for each special structure
370 """
371 if types is None: types=self.text_types
372
373 for text_type in types:
374
375 if type(str) is StringType:
376 str = self.parse(str, text_type)
377 elif type(str) is ListType:
378 r=[]; a=r.append
379 for s in str:
380 if type(s) is StringType:
381 s=self.parse(s, text_type)
382 if type(s) is ListType: r[len(r):]=s
383 else: a(s)
384 else:
385 s.setColorizableTexts(
386 map(self.color_text,
387 s.getColorizableTexts()
388 ))
389 a(s)
390 str=r
391 else:
392 r=[]; a=r.append; color=self.color_text
393 for s in str.getColorizableTexts():
394 color(s, (text_type,))
395 a(s)
396
397 str.setColorizableTexts(r)
398
399 return str
400
401 def color_paragraphs(self, raw_paragraphs,
402 type=type, sequence_types=(type([]), type(())),
403 st=type('')):
404 result=[]
405 for paragraph in raw_paragraphs:
406
407 if paragraph.getNodeName() != 'StructuredTextParagraph':
408 result.append(paragraph)
409 continue
410
411 for pt in self.paragraph_types:
412 if type(pt) is st:
413 # grab the corresponding function
414 pt=getattr(self, pt)
415 # evaluate the paragraph
416 r=pt(paragraph)
417 if r:
418 if type(r) not in sequence_types:
419 r=r,
420 new_paragraphs=r
421 for paragraph in new_paragraphs:
422 paragraph.setSubparagraphs(self.color_paragraphs(paragraph.getSubparagraphs()))
423 break
424 else:
425 new_paragraphs=ST.StructuredTextParagraph(paragraph.getColorizableTexts()[0],
426 self.color_paragraphs(paragraph.getSubparagraphs()),
427 indent=paragraph.indent),
428 # color the inline StructuredText types
429 # for each StructuredTextParagraph
430 for paragraph in new_paragraphs:
431 paragraph.setColorizableTexts(
432 map(self.color_text,
433 paragraph.getColorizableTexts()
434 ))
435 result.append(paragraph)
436
437 return result
438
439 def doc_table(self,paragraph, expr = re.compile('(\s*)([||]+)').match):
440 #print "paragraph=>", type(paragraph), paragraph, paragraph._src
441 text = paragraph.getColorizableTexts()[0]
442 m = expr(text)
443
444 if not (m):
445 return None
446 rows = []
447
448 # initial split
449 for row in split(text,"\n"):
450 rows.append(row)
451
452 # clean up the rows
453 for index in range(len(rows)):
454 tmp = []
455 rows[index] = strip(rows[index])
456 l = len(rows[index])-2
457 result = split(rows[index][:l],"||")
458 for text in result:
459 if text:
460 tmp.append(text)
461 tmp.append('')
462 else:
463 tmp.append(text)
464 rows[index] = tmp
465 # remove trailing '''s
466 for index in range(len(rows)):
467 l = len(rows[index])-1
468 rows[index] = rows[index][:l]
469
470 result = []
471 for row in rows:
472 cspan = 0
473 tmp = []
474 for item in row:
475 if item:
476 tmp.append((item,cspan))
477 cspan = 0
478 else:
479 cspan = cspan + 1
480 result.append(tmp)
481
482 subs = paragraph.getSubparagraphs()
483 indent=paragraph.indent
484 return StructuredTextTable(result,text,subs,indent=paragraph.indent)
485
486 def doc_bullet(self, paragraph, expr = re.compile('\s*[-*o]\s+').match):
487 top=paragraph.getColorizableTexts()[0]
488 m=expr(top)
489
490 if not m:
491 return None
492
493 subs=paragraph.getSubparagraphs()
494 if top[-2:]=='::':
495 subs=[StructuredTextExample(subs)]
496 top=top[:-1]
497 return StructuredTextBullet(top[m.span()[1]:], subs,
498 indent=paragraph.indent,
499 bullet=top[:m.span()[1]]
500 )
501
502 def doc_numbered(
503 self, paragraph,
504 expr = re.compile('(\s*[%s]+\.)|(\s*[0-9]+\.)|(\s*[0-9]+\s+)' % letters).match):
505
506 # This is the old expression. It had a nasty habit
507 # of grabbing paragraphs that began with a single
508 # letter word even if there was no following period.
509
510 #expr = re.compile('\s*'
511 # '(([a-zA-Z]|[0-9]+|[ivxlcdmIVXLCDM]+)\.)*'
512 # '([a-zA-Z]|[0-9]+|[ivxlcdmIVXLCDM]+)\.?'
513 # '\s+').match):
514
515 top=paragraph.getColorizableTexts()[0]
516 m=expr(top)
517 if not m: return None
518 subs=paragraph.getSubparagraphs()
519 if top[-2:]=='::':
520 subs=[StructuredTextExample(subs)]
521 top=top[:-1]
522 return StructuredTextNumbered(top[m.span()[1]:], subs,
523 indent=paragraph.indent,
524 number=top[:m.span()[1]])
525
526 def doc_description(
527 self, paragraph,
528 delim = re.compile('\s+--\s+').search,
529 nb=re.compile(r'[^\000- ]').search,
530 ):
531
532 top=paragraph.getColorizableTexts()[0]
533 d=delim(top)
534 if not d: return None
535 start, end = d.span()
536 title=top[:start]
537 if find(title, '\n') >= 0: return None
538 if not nb(title): return None
539 d=top[start:end]
540 top=top[end:]
541
542 subs=paragraph.getSubparagraphs()
543 if top[-2:]=='::':
544 subs=[StructuredTextExample(subs)]
545 top=top[:-1]
546
547 return StructuredTextDescription(
548 title, top, subs,
549 indent=paragraph.indent,
550 delim=d)
551
552 def doc_header(self, paragraph,
553 expr = re.compile('[ %s0-9.:/,-_*<>\?\'\"]+' % letters).match
554 ):
555 subs=paragraph.getSubparagraphs()
556 if not subs: return None
557 top=paragraph.getColorizableTexts()[0]
558 if not strip(top): return None
559 if top[-2:]=='::':
560 subs=StructuredTextExample(subs)
561 if strip(top)=='::': return subs
562 return ST.StructuredTextParagraph(top[:-1],
563 [subs],
564 indent=paragraph.indent,
565 level=paragraph.level)
566
567 if find(top,'\n') >= 0: return None
568 return StructuredTextSection(top, subs, indent=paragraph.indent, level=paragraph.level)
569
570 def doc_literal(
571 self, s,
572 expr=re.compile(
573 "(?:\s|^)'" # open
574 "([^ \t\n\r\f\v']|[^ \t\n\r\f\v'][^\n']*[^ \t\n\r\f\v'])" # contents
575 "'(?:\s|[,.;:!?]|$)" # close
576 ).search):
577
578 r=expr(s)
579 if r:
580 start, end = r.span(1)
581 return (StructuredTextLiteral(s[start:end]), start-1, end+1)
582 else:
583 return None
584
585 def doc_emphasize(
586 self, s,
587 expr = re.compile('\s*\*([ \n%s0-9]+)\*(?!\*|-)' % lettpunc).search
588 ):
589
590 r=expr(s)
591 if r:
592 start, end = r.span(1)
593 return (StructuredTextEmphasis(s[start:end]), start-1, end+1)
594 else:
595 return None
596
597 def doc_inner_link(self,
598 s,
599 expr1 = re.compile("\.\.\s*").search,
600 expr2 = re.compile("\[[%s0-9]+\]" % letters).search):
601
602 # make sure we dont grab a named link
603 if expr2(s) and expr1(s):
604 start1,end1 = expr1(s).span()
605 start2,end2 = expr2(s).span()
606 if end1 == start2:
607 # uh-oh, looks like a named link
608 return None
609 else:
610 # the .. is somewhere else, ignore it
611 return (StructuredTextInnerLink(s[start2+1:end2-1]),start2,end2)
612 return None
613 elif expr2(s) and not expr1(s):
614 start,end = expr2(s).span()
615 return (StructuredTextInnerLink(s[start+1:end-1]),start,end)
616 return None
617
618 def doc_named_link(self,
619 s,
620 expr=re.compile("(\.\.\s)(\[[%s0-9]+\])" % letters).search):
621
622 result = expr(s)
623 if result:
624 start,end = result.span(2)
625 a,b = result.span(1)
626 str = strip(s[a:b]) + s[start:end]
627 str = s[start+1:end-1]
628 st,en = result.span()
629 return (StructuredTextNamedLink(str),st,en)
630 #return (StructuredTextNamedLink(s[st:en]),st,en)
631 return None
632
633 def doc_underline(self,
634 s,
635 expr=re.compile("\s+\_([0-9%s ]+)\_" % lettpunc).search):
636
637 result = expr(s)
638 if result:
639 start,end = result.span(1)
640 st,e = result.span()
641 return (StructuredTextUnderline(s[start:end]),st,e)
642 else:
643 return None
644
645 def doc_strong(self,
646 s,
647 expr = re.compile('\s*\*\*([ \n%s0-9]+)\*\*' % lettpunc).search
648 ):
649
650 r=expr(s)
651 if r:
652 start, end = r.span(1)
653 return (StructuredTextStrong(s[start:end]), start-2, end+2)
654 else:
655 return None
656
657 def doc_href(
658
659 self, s,
660 expr1 = re.compile("(\"[ %s0-9\n\-\.\,\;\(\)\/\:\/\*\']+\")(:)([a-zA-Z0-9\@\.\,\?\!\/\:\;\-\#\~]+)([,]*\s*)" % letters).search,
661 expr2 = re.compile('(\"[ %s0-9\n\-\.\:\;\(\)\/\*\']+\")([,]+\s+)([a-zA-Z0-9\@\.\,\?\!\/\:\;\-\#\~]+)(\s*)' % letters).search):
662
663 r=expr1(s) or expr2(s)
664
665 if r:
666 # need to grab the href part and the
667 # beginning part
668
669 start,e = r.span(1)
670 name = s[start:e]
671 name = replace(name,'"','',2)
672 st,end = r.span(3)
673
674 if s[end-1:end] in punctuations: end-=1
675 link = s[st:end]
676
677 # name is the href title, link is the target
678 # of the href
679 return (StructuredTextLink(name, href=link),
680 start, end)
681
682
683 else:
684 return None