Книга: Practical Common Lisp
Indenting Printer
Indenting Printer
To handle generating nicely indented output, you can define a class indenting-printer
, which wraps around an output stream, and functions that use an instance of that class to emit strings to the stream while keeping track of when it's at the beginning of the line. The class looks like this:
(defclass indenting-printer ()
((out :accessor out :initarg :out)
(beginning-of-line-p :accessor beginning-of-line-p :initform t)
(indentation :accessor indentation :initform 0)
(indenting-p :accessor indenting-p :initform t)))
The main function that operates on indenting-printer
s is emit
, which takes the printer and a string and emits the string to the printer's output stream, keeping track of when it emits a newline so it can reset the beginning-of-line-p
slot.
(defun emit (ip string)
(loop for start = 0 then (1+ pos)
for pos = (position #Newline string :start start)
do (emit/no-newlines ip string :start start :end pos)
when pos do (emit-newline ip)
while pos))
To actually emit the string, it uses the function emit/no-newlines
, which emits any needed indentation, via the helper indent-if-necessary
, and then writes the string to the stream. This function can also be called directly by other code to emit a string that's known not to contain any newlines.
(defun emit/no-newlines (ip string &key (start 0) end)
(indent-if-necessary ip)
(write-sequence string (out ip) :start start :end end)
(unless (zerop (- (or end (length string)) start))
(setf (beginning-of-line-p ip) nil)))
The helper indent-if-necessary
checks beginning-of-line-p
and indenting-p
to determine whether it needs to emit indentation and, if they're both true, emits as many spaces as indicated by the value of indentation
. Code that uses the indenting-printer
can control the indentation by manipulating the indentation
and indenting-p
slots. Incrementing and decrementing indentation
changes the number of leading spaces, while setting indenting-p
to NIL
can temporarily turn off indentation.
(defun indent-if-necessary (ip)
(when (and (beginning-of-line-p ip) (indenting-p ip))
(loop repeat (indentation ip) do (write-char #Space (out ip)))
(setf (beginning-of-line-p ip) nil)))
The last two functions in the indenting-printer
API are emit-newline
and emit-freshline
, which are both used to emit a newline character, similar to the ~%
and ~& FORMAT
directives. That is, the only difference is that emit-newline
always emits a newline, while emit-freshline
does so only if beginning-of-line-p
is false. Thus, multiple calls to emit-freshline
without any intervening emit
s won't result in a blank line. This is handy when one piece of code wants to generate some output that should end with a newline while another piece of code wants to generate some output that should start on a newline but you don't want a blank line between the two bits of output.
(defun emit-newline (ip)
(write-char #Newline (out ip))
(setf (beginning-of-line-p ip) t))
(defun emit-freshline (ip)
(unless (beginning-of-line-p ip) (emit-newline ip)))
With those preliminaries out of the way, you're ready to get to the guts of the FOO processor.
- The Pretty Printer Backend
- GUI-Based Printer Configuration Quick Start
- Creating and Configuring Local Printers
- Editing Printer Settings
- Network-Attached Printer Configuration and Printing
- Avoiding Printer Support Problems
- Using USB and Legacy Printers
- Using nprint with the Line Printer Daemon
- CHAPTER 10: Administering network printers and print services
- Enabling and disabling file and printer sharing
- Installing printers
- Connecting to printers created on the network