Allow transformations to be indented.
[matthijs/master-project/report.git] / pret-trans.lua
1 -- filename : type-trans.lua
2 -- comment  : Pretty printing of core transformations. Parses a specific
3 --            syntax, consisting of a before / after with a dashed lined in
4 --            between, and some extra conditions to the right of the line.
5 --            Recreates this layout using frames and line commands.
6 -- author   : Matthijs Kooijman, Universiteit Twente, NL
7 -- copyright: Matthijs Kooijman
8 -- license  : None
9
10 local utf = unicode.utf8
11
12 local vis = buffers.newvisualizer("trans")
13
14 local commands = {}
15 -- A command to create a horizontal rule.
16 commands.rule = "\\HLine[width=.40 * \\the\\textwidth]"
17 -- Pretty printer to use for the stuff before and after the line
18 commands.before_pret = "lam"
19 commands.after_pret = "lam"
20 -- Frame commands to use for the left (before + line + after) and right
21 -- (conditions) parts. Should include an opening {, which will be closed
22 -- automatically.
23 commands.leftframe = "\\framed[offset=0mm,location=middle,strut=no,align=right,frame=off,width=.48\\textwidth]{\\sans"
24 commands.rightframe = "\\framed[offset=0mm,location=middle,strut=no,align=right,frame=off,width=.48\\textwidth]{"
25
26 -- A table to keep the lines in this buffer, so we can process them all at
27 -- once at the end.
28 local lines
29 -- A counter to keep track of the mininum amount of indentation found in each
30 -- display.
31 local min_indent
32
33 -- Some helper functions
34 local function ltrim(s)
35     return (string.gsub(s, "^%s*", ""))
36 end
37
38 local function rtrim(s)
39     return (string.gsub(s, "%s*$", ""))
40 end
41
42 -- Insert n blank lines
43 local function blanks(n)
44     for i = 1,n do
45         buffers.visualizers.handlers.default.empty_line()
46     end
47 end
48
49 -- Prettyprint the given lines using the given pretty printer
50 local function prettyprint(ppr, lines)
51     -- Change the current visualizer
52     buffers.setvisualizer('lam')
53
54     -- Output the lines
55     buffers.hooks.begin_of_display()
56     line = 0
57     for i = 1,#lines do
58         _, line = buffers.typeline(lines[i], i, #lines, line)
59     end
60     buffers.hooks.end_of_display()
61
62     -- Change the visualizer back
63     buffers.setvisualizer('trans')
64 end
65
66 -- Capture all lines, without generating any output
67 function vis.begin_of_display()
68     lines = {}
69     -- Let all the lambda pretty printing in this buffer use shared subscript
70     -- detection
71     buffers.visualizers.handlers.lam.begin_of_block()
72     min_indent = nil
73 end
74 function vis.begin_of_line(n)
75     -- Don't generate output here
76 end
77 function vis.flush_line(str, nested)
78     -- Keep track of the minimum indent level of all lines. Note that we don't
79     -- look at empty lines, of course.
80     indent = utf.len(utf.match(str, "^%s*"))
81         -- Find the lowest indent (but don't count empty lines)
82     if (not min_indent or indent < min_indent) then
83         min_indent = indent
84     end
85
86     table.insert(lines, str)
87     -- Don't generate output here
88 end
89 function vis.end_of_line(n)
90     -- Don't generate output here
91 end
92 function vis.empty_line()
93     table.insert(lines, '')
94     -- Don't generate output here
95 end
96
97 -- We do the actual work here. Process all the lines in the buffer and
98 -- generate output for them.
99 function vis.end_of_display()
100     -- Strip indent that is present on every line
101     min_indent = min_indent or 0 
102     for i = 1,#lines do
103         lines[i] = utf.sub(lines[i], min_indent + 1)
104     end
105
106     -- Find the horizontal rule, and see how long it is.
107     len = nil
108     for i = 1,#lines do
109         match = utf.match(lines[i], "^%-%-%-*")
110         if match then
111             len = utf.len(match)
112             break
113         end
114     end
115   
116     if not len then
117         error("No horizontal separator found in:\n" .. table.concat(lines, "\n"))
118     end
119
120     -- Split the input in three parts. Stuff before the line, stuff
121     -- after the line, stuff to the right of the line.
122     before, after, rights = {}, {}, {}
123     found_line = false
124     for i = 1,#lines do
125         line = lines[i]
126         -- Split the line into a left and right part
127         left = rtrim(utf.sub(line, 1, len))
128         right = ltrim(utf.sub(line, len + 1))
129         if utf.match(left, "^%-%-%-*") then
130             found_line = true
131         else
132             if utf.len(left) > 0 then
133                 if not found_line then
134                     table.insert(before, left)
135                 else
136                     table.insert(after, left)
137                 end
138             end
139         end
140         if utf.len(right) > 0 then
141             table.insert(rights, right)
142         end
143     end
144
145     --
146     -- Real output starts here
147     --
148
149     -- Ensure the left and right frames end up next to each other.
150     tex.sprint("\\dontleavehmode")
151
152     -- Open and fill the left frame
153     tex.sprint(commands.leftframe)
154
155     prettyprint('lam', before)
156
157     tex.sprint(commands.rule)
158
159     prettyprint('lam', after)
160
161     -- Close the left frame
162     tex.sprint("}")
163
164     -- Open and fill the right frame
165     tex.sprint(commands.rightframe)
166
167     -- Insert spacer blank lines to align the middle condition with the
168     -- conclusion rule
169     n_blanks = #before - math.ceil((#rights - 1) / 2)
170     n_blanks = math.max(0, n_blanks)
171     blanks(n_blanks)
172
173     -- Print the conditions
174     for i = 1,#rights do
175         tex.sprint(rights[i])
176         buffers.visualizers.handlers.default.end_of_line()
177     end
178
179     -- Fill up the remaining space with blanks
180     n_blanks = (#before + 1 + #after) - (n_blanks + #rights)
181     n_blanks = math.max(0, n_blanks)
182     blanks(n_blanks)
183
184     -- Close the right frame 
185     tex.sprint("}")
186     
187     -- Clean up
188     lines = {}
189     buffers.visualizers.handlers.lam.end_of_block()
190 end
191
192 -- vim: set sw=4 sts=4 expandtab ai: