-- UnparserBuffer.mesa, December 7, 1982 3:57 pm by Greg Nelson

-- Formatted output routines. Send a stream of characters through the buffer, delineate the stream into objects. The objects can be nested. For each object, specify within it a set of breakpoints. The routines will format the text as follows. (1) An object will be printed all on one line if possible. (2) If an object cannot fit on one line, new lines will be started at one or more of the object's breakpoints, so that no text overflows the right margin. (3) With each breakpoint there is associated an integer call the breakpoint's offset. If a new line is started at a breakpoint, then the new line will be indented by the sum of the breakpoint's offset plus the indentation of the first character of the smallest object that contains the breakpoint. (4) With each breakpoint there is associated a rope call the breakpoint's insert. If a new line is started at a breakpoint, then the insert will be inserted at the beginning of the new line, after the indentation. (5) Each breakpoint is either united or ununited. If any breakpoint of an object is broken, then all that object's united breakpoints are broken. An ununited breakpoint is broken only if it is necessary to do so to prevent overflowing the right margin.

-- The default handle is set up for a fixed width font, since it treats all characters as having width 1. This is not very satisfactory, since we rarely use fixed-width fonts. To get better results, figure out what font you are using, set width[i] to be the width of char i, for each i, using whatever units you like. Then set margin to be the desired maximum output line width, in the same units. Then set the Eachline procedure in the handle; it will be called for each output line; the line data will be in
h.buffer. Do whatever you like with the lines.
-- Last Edited by: Gnelson, November 21, 1983 4:41 pm

DIRECTORY IO, Rope, Atom;

UnparserBuffer: DEFINITIONS =
BEGIN

n : INT = 256; -- size of various internal buffers.

Handle: TYPE = REF HandleRec;

NewHandle: PROC RETURNS [Handle];

HandleRec: TYPE = RECORD [
margin: PUBLIC INTEGER ← 80,
width: ARRAY CHAR OF INTEGERALL [1],
output: IO.STREAM,
clientData: REF,
-- Do as you please with clientData.
-- ignore the remaining fields unless you are providing your
-- own output procedure
EachLine: PROC [h: Handle], -- ← IndentAndPrint
buffer: REF TEXT, -- ← NEW[TEXT[100]]
bufferIndent: INTEGER ← 0,
bufferWidth: INTEGER ← 0,
-- ignore the remaining fields.
bl, cl, br, cr, sr, srx: INTEGER ← 0,
c: ARRAY [0 .. n) OF CHAR,
b: ARRAY [0 .. n) OF RECORD [
type: {setb, breakpoint},
united: BOOL,
offset: INTEGER,
insert: Rope.ROPE,
p: [0 .. n)],
s: ARRAY [0 .. n) OF INTEGER,
indentation: INTEGER ← 0];

IndentAndPrint: PROC[h: Handle];

Init: PUBLIC PROC [h: Handle];
-- destroys any information in the internal buffers and makes the buffers all empty.
-- This may be used to "undo" an arbitrary sequence of Setbs.
-- Does not affect margin or EachLine.

Setb: PUBLIC PROC [h: Handle]; -- begin an object

Endb: PUBLIC PROC [h: Handle]; -- end an object

Bp: PUBLIC PROC[h: Handle,
     united: BOOL,
offset: INTEGER,
insert: Rope.ROPENIL];

-- insert a breakpoint..
-- if the breakpoint breaks, the next line will be indented by offset from
-- the beginning of the object to which this breakpoint belongs, and the new
-- line will begin with the rope insert.

Charb: PUBLIC PROC [h: Handle, ch: CHAR]; -- output a character

Ropeb: PUBLIC PROC[h: Handle, r: Rope.ROPE]; -- output a rope

Atomb: PUBLIC PROC[h: Handle, a: ATOM]; -- output the pname of the atom

Newlineb: PUBLIC PROC[h: Handle, offset: INTEGER, insert: Rope.ROPENIL];

-- just like a breakpoint except that it is certain to break, thus the effect is
-- to insert a new line into the output. If you call this without calling Setb first,
-- you will get an error.

END.