MoinLight

Annotated moinformat/serialisers/moin/moin.py

331:35ae3eea290f
16 months ago Paul Boddie Introduced support for a generic visitor interface where document nodes, when a generic visit method is invoked by a serialiser or other component, merely divert the invocation to a specific method on that visiting component. generic-visitors
paul@38 1
#!/usr/bin/env python
paul@38 2
paul@38 3
"""
paul@38 4
Moin wiki text serialiser.
paul@38 5
paul@331 6
Copyright (C) 2017, 2018, 2021, 2022, 2023 Paul Boddie <paul@boddie.org.uk>
paul@38 7
paul@38 8
This program is free software; you can redistribute it and/or modify it under
paul@38 9
the terms of the GNU General Public License as published by the Free Software
paul@38 10
Foundation; either version 3 of the License, or (at your option) any later
paul@38 11
version.
paul@38 12
paul@38 13
This program is distributed in the hope that it will be useful, but WITHOUT
paul@38 14
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
paul@38 15
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
paul@38 16
details.
paul@38 17
paul@38 18
You should have received a copy of the GNU General Public License along with
paul@38 19
this program.  If not, see <http://www.gnu.org/licenses/>.
paul@38 20
"""
paul@38 21
paul@38 22
from moinformat.serialisers.common import escape_attr, escape_text, Serialiser
paul@38 23
paul@38 24
class MoinSerialiser(Serialiser):
paul@38 25
paul@38 26
    "Serialisation of the page."
paul@38 27
paul@301 28
    input_formats = ["moin", "wiki"]
paul@301 29
    formats = ["moin", "wiki"]
paul@85 30
paul@331 31
    # Node handler methods.
paul@331 32
paul@331 33
    def region(self, region):
paul@38 34
        out = self.out
paul@52 35
paul@331 36
        if region.level:
paul@331 37
            out(" " * region.indent + "{" * region.level)
paul@52 38
paul@331 39
            # Produce a header for regions within a top-level region.
paul@313 40
paul@331 41
            if region.type and region.type != "inline":
paul@331 42
paul@331 43
                # Obtain individual arguments, excluding the region type.
paul@38 44
paul@331 45
                args = region.args.split(" ")[1:]
paul@331 46
                args_str = args and (" %s" % " ".join(args)) or ""
paul@313 47
paul@331 48
                out("#!%s%s\n" % (region.type, args_str))
paul@331 49
paul@331 50
        # Serialise the region content.
paul@313 51
paul@331 52
        self.region_to_string(region)
paul@38 53
paul@331 54
        if region.level:
paul@331 55
            out("%s%s" % ("}" * region.level, region.extra or ""))
paul@38 56
paul@331 57
    # Block node methods.
paul@38 58
paul@331 59
    def block(self, block):
paul@331 60
        self.container(block)
paul@38 61
paul@331 62
    def defitem(self, defitem):
paul@331 63
        self.out((defitem.extra and defitem.extra + "::" or "") + defitem.pad)
paul@331 64
        self.container(defitem)
paul@38 65
paul@331 66
    def defterm(self, defterm):
paul@331 67
        self.out(defterm.pad)
paul@331 68
        self.container(defterm)
paul@331 69
        self.out("::" + defterm.extra)
paul@38 70
paul@331 71
    def fontstyle(self, fontstyle):
paul@331 72
        if fontstyle.emphasis:
paul@331 73
            self.out("''")
paul@331 74
        elif fontstyle.strong:
paul@331 75
            self.out("'''")
paul@331 76
        self.container(fontstyle)
paul@331 77
        if fontstyle.emphasis:
paul@331 78
            self.out("''")
paul@331 79
        elif fontstyle.strong:
paul@331 80
            self.out("'''")
paul@38 81
paul@331 82
    def heading(self, heading):
paul@331 83
        self.out(heading.start_extra + "=" * heading.level + heading.start_pad)
paul@331 84
        self.container(heading)
paul@331 85
        self.out(heading.end_pad + "=" * heading.level + heading.end_extra)
paul@38 86
paul@331 87
    def larger(self, larger):
paul@38 88
        self.out("~+")
paul@331 89
        self.container(larger)
paul@38 90
        self.out("+~")
paul@38 91
paul@331 92
    def list(self, list):
paul@331 93
        self.container(list)
paul@43 94
paul@331 95
    def listitem(self, listitem):
paul@331 96
        self.out("%s%s%s%s" % (
paul@331 97
            listitem.indent * " ",
paul@331 98
            listitem.marker,
paul@331 99
            listitem.num and "#%s" % listitem.num or "",
paul@331 100
            listitem.space))
paul@331 101
        self.container(listitem)
paul@38 102
paul@331 103
    def macro(self, macro):
paul@89 104
paul@314 105
        # Special case of a deliberately unexpanded macro.
paul@314 106
paul@331 107
        if macro.nodes is None:
paul@314 108
            return
paul@314 109
paul@89 110
        # Fallback case for when macros are not replaced.
paul@89 111
paul@331 112
        if not macro.nodes:
paul@331 113
            self.out("<<%s%s>>" % (macro.name, macro.args and "(%s)" % ",".join(macro.args) or ""))
paul@87 114
paul@331 115
    def monospace(self, monospace):
paul@331 116
        self.out("`")
paul@331 117
        self.container(monospace)
paul@38 118
        self.out("`")
paul@38 119
paul@331 120
    def smaller(self, smaller):
paul@38 121
        self.out("~-")
paul@331 122
        self.container(smaller)
paul@38 123
        self.out("-~")
paul@38 124
paul@331 125
    def strikethrough(self, strikethrough):
paul@48 126
        self.out("--(")
paul@331 127
        self.container(strikethrough)
paul@48 128
        self.out(")--")
paul@48 129
paul@331 130
    def subscript(self, subscript):
paul@331 131
        self.out(",,")
paul@331 132
        self.container(subscript)
paul@38 133
        self.out(",,")
paul@38 134
paul@331 135
    def superscript(self, superscript):
paul@38 136
        self.out("^")
paul@331 137
        self.container(superscript)
paul@38 138
        self.out("^")
paul@38 139
paul@331 140
    def table(self, table):
paul@331 141
        self.container(table)
paul@38 142
paul@331 143
    def table_cell(self, table_cell):
paul@331 144
        self.out("||")
paul@331 145
        self.container(table_cell)
paul@38 146
paul@331 147
    def table_row(self, table_row):
paul@331 148
        self.container(table_row)
paul@38 149
        self.out("||")
paul@331 150
        self.out(table_row.trailing)
paul@38 151
paul@331 152
    def underline(self, underline):
paul@331 153
        self.out("__")
paul@331 154
        self.container(underline)
paul@38 155
        self.out("__")
paul@38 156
paul@331 157
    # Inline node methods.
paul@38 158
paul@331 159
    def anchor(self, anchor):
paul@331 160
        self.out("((%s))" % anchor.target)
paul@116 161
paul@331 162
    def break_(self, break_):
paul@38 163
        self.out("\n")
paul@38 164
paul@331 165
    def comment(self, comment):
paul@331 166
        self.out("##%s%s" % (comment.comment, comment.extra))
paul@151 167
paul@331 168
    def directive(self, directive):
paul@331 169
        self.out("#%s%s" % (directive.directive, directive.extra))
paul@151 170
paul@331 171
    def linebreak(self, linebreak):
paul@106 172
        self.out(r"\\")
paul@106 173
paul@331 174
    def link(self, link):
paul@331 175
        self.out("[[%s" % link.target)
paul@331 176
        for node in link.nodes:
paul@167 177
            self.out("|")
paul@331 178
            node.visit(self)
paul@167 179
        self.out("]]")
paul@167 180
paul@331 181
    def link_label(self, link_label):
paul@331 182
        self.container(link_label)
paul@167 183
paul@331 184
    def link_parameter(self, link_parameter):
paul@331 185
        s = link_parameter.text_content()
paul@331 186
        key_value = s.split("=", 1)
paul@331 187
paul@167 188
        if len(key_value) == 1:
paul@167 189
            self.out(key_value[0])
paul@167 190
        else:
paul@167 191
            self.out("=".join(key_value))
paul@167 192
paul@331 193
    def nbsp(self, nbsp):
paul@316 194
        self.out(r"\_")
paul@316 195
paul@331 196
    def rule(self, rule):
paul@331 197
        self.out("-" * (rule.height + 4))
paul@38 198
paul@331 199
    def table_attrs(self, table_attrs):
paul@331 200
        self.out("<")
paul@331 201
        self.container(table_attrs)
paul@331 202
        if not table_attrs.incomplete:
paul@331 203
            self.out(">")
paul@102 204
paul@331 205
    def table_attr(self, table_attr):
paul@331 206
        if table_attr.concise:
paul@331 207
            if table_attr.name == "bgcolor":
paul@331 208
                self.out(table_attr.value)
paul@331 209
            elif table_attr.name == "colspan":
paul@331 210
                self.out("-%s" % table_attr.value)
paul@331 211
            elif table_attr.name == "align":
paul@331 212
                self.out(table_attr.value == "left" and "(" or table_attr.value == "right" and ")" or ":")
paul@331 213
            elif table_attr.name == "rowspan":
paul@331 214
                self.out("|%s" % table_attr.value)
paul@331 215
            elif table_attr.name == "valign":
paul@331 216
                self.out(table_attr.value == "top" and "^" or "v")
paul@331 217
            elif table_attr.name == "width":
paul@331 218
                self.out(table_attr.value)
paul@38 219
        else:
paul@331 220
            self.out("%s%s" % (
paul@331 221
                escape_text(table_attr.name),
paul@331 222
                table_attr.value is not None and "=%s%s%s" % (
paul@331 223
                    table_attr.quote or '"',
paul@331 224
                    escape_attr(table_attr.value),
paul@331 225
                    table_attr.quote or '"')
paul@331 226
                    or ""))
paul@38 227
paul@331 228
    def text(self, text):
paul@331 229
        self.out(text.s)
paul@331 230
paul@331 231
    def transclusion(self, transclusion):
paul@331 232
        self.out("{{%s" % transclusion.target)
paul@331 233
        for node in transclusion.nodes:
paul@167 234
            self.out("|")
paul@331 235
            node.visit(self)
paul@167 236
        self.out("}}")
paul@167 237
paul@331 238
    def verbatim(self, verbatim):
paul@169 239
        self.out("<<<")
paul@331 240
        self.out(verbatim.text)
paul@169 241
        self.out(">>>")
paul@169 242
paul@39 243
serialiser = MoinSerialiser
paul@39 244
paul@38 245
# vim: tabstop=4 expandtab shiftwidth=4