[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Issue: STREAM-INFO (Version 4)



Don't panic -- Versions 1-3 were circulated between Dick Waters and myself
and did not reach this list. This is the first version CL-Cleanup should
have seen.

Also, please be sure to cc Dick@WHEATIES.AI.MIT.EDU in any correspondence
on this issue since he's not on CL-Cleanup.
 -kmp

----------
Issue:        STREAM-INFO
References:   FORMAT ~T (pp398-9) and ~<...~> (pp404-6), PPRINT (p383)
Category:     ADDITION
Edit history: 22-Jun-88, Version 1 by Pitman (2d model)
	      23-Jun-88, Version 2 by Waters (1d model, modified 2d model)
	      24-Jun-88, Version 3 by Pitman (minor reformatting)
	      24-Jun-88, Version 4 by Pitman (remove 2d model for submission)
Status:	      For Internal Discussion

Problem Description:

  Currently, there is no portable way to inquire about the line width
  of an output stream, the current character position on a line
  or the amount of space that the display of a string will take up.
  This makes it essentially impossible to write a portable
  implementation of a pretty printer.

Proposal (STREAM-INFO: ONE-DIMENSIONAL-FUNCTIONS):

  Introduce four new functions:

   OUTPUT-WIDTH &optional (OUTPUT-STREAM *STANDARD-OUTPUT*)       [Function]

    Returns the maximum line width that can be printed on the
    OUTPUT-STREAM without causing truncation or wraparound.  The
    result is returned as a non-negative integer, or NIL if the stream
    has no meaningful width (or the width cannot be computed).  The
    unit of width is arbitrary, however, given a particular output
    stream, the unit must have some fixed value.

   OUTPUT-POSITION &optional (OUTPUT-STREAM *STANDARD-OUTPUT*)    [Function]

    Returns the current horizontal position of the cursor on the given
    OUTPUT-STREAM as a non-negative integer, or NIL if it cannot be
    computed.  (The position is the position of the distance of the
    left edge of the cursor from the left margin.  I.e., 0 means
    cursor is at the left end of a line.)  The units are arbitrary,
    but for a given stream, they must be the same as the units used by
    OUTPUT-WIDTH.

   STRING-WIDTH STRING &optional (OUTPUT-STREAM *STANDARD-OUTPUT*)
		       &key (START 0) (END NIL) 		  [Function]

    The START and END parameters delimit a substring of string in the
    usual manner.  STRING-WIDTH returns the change in output position
    that would occur if STRING were written to OUTPUT-STREAM using
    (WRITE-STRING STRING OUTPUT-STREAM :start START :end END) given
    the current state of OUTPUT-STREAM or NIL if the change cannot be
    computed.  The returned value is an integer.  The units are
    arbitrary, but for a given stream, they must be the same as the
    units used by OUTPUT-WIDTH.  STRING-WIDTH satisfies the following
    constraint.

    (LET* ((STREAM ...)
           (STRING ...)
           (CURRENT-X (OUTPUT-POSITION STREAM)))
      (= (STRING-WIDTH STRING STREAM)
         (- (PROGN (WRITE-STRING STRING STREAM) 
                   (OUTPUT-POSITION STREAM))
            CURRENT-X)))

    STRING-WIDTH does not return any indication of the vertical
    distance required when printing string.  Merely the difference in
    horizontal position before and after printing.  This difference
    may be negative (e.g., if STRING contains backspace or newline
    characters.)  It is possible that the width of a string will
    depend on the horizontal position where output begins (e.g., if
    the string contains tab characters.)  If this is the case, the
    width returned assumes that the output occurs starting at the
    current horizontal position in the stream.  Similarly, the width
    of string may depend on other aspects of the state of
    OUTPUT-STREAM (e.g., the font being used).  In all respects the
    width is computed based on the current state of the stream.
    STRING-WIDTH never causes any change in the state of OUTPUT-STREAM.

   OUTPUT-SPACE WIDTH &optional (OUTPUT-STREAM *STANDARD-OUTPUT*) [function]

    This function causes blank space to be inserted in OUTPUT-STREAM
    so that the output position is increased by WIDTH.  WIDTH is an
    integer.  The units are arbitrary, but for a given stream, they
    must be the same as the units used by OUTPUT-WIDTH.  A negative
    WIDTH parameter indicates backspacing.  The unit of WIDTH should
    be chosen so that it is possible to move the output position left
    and right by a single unit.  OUTPUT-SPACE satisfies the following
    constraint.

    (LET* ((STREAM ...)
           (N ...))
      (= (+ (OUTPUT-POSITION STREAM) N)
         (PROGN (OUTPUT-SPACE N STREAM)
                (OUTPUT-POSITION STREAM))))

    OUTPUT-SPACE returns T if the spacing operation has been achieved
    and NIL otherwise (e.g., if the requested spacing would move off
    of the end of the line or if the the operation cannot be supported
    for the given stream.)

   A key motivation behind the functions above is dealing with
   output streams that support variable length fonts.  The unit of
   width is allowed to vary from stream to stream in order to allow
   for differences between output devices.  The only thing that
   matters is that the four functions above operate on the same units
   when given the same stream.

   If an output stream only supports a single fixed width font, the
   logical choice of width unit is the length of a single character.
   In this situation (and ignoring characters such as tab, newline,
   and other control characters that do not have an output width of
   one) the functions above have the following simple meanings.
   OUTPUT-WIDTH returns the maximum number of characters which can be
   printed on a single line.  OUTPUT-POSITION returns the number of
   characters which have already been printed on the current line.
   STRING-LENGTH returns the number of characters in the string.
   OUTPUT-SPACE prints the specified number of space, or backspace,
   characters.

   Another key feature of the functions above is that they are all
   permitted to return NIL without performing any action.  This is to
   allow for the fact that the required operations might not be
   supported for every kind of stream.  However, it is hoped that the
   functions would in fact be supported for most kinds of streams.

Test Case:

  Suppose that S is an output stream that supports a single fixed
  width font which can display 72 characters on a line and that the
  associated width unit is the width of one character.  Evaluating the
  following will produce the results shown.

  (output-width S) => 72
  (terpri S)
  (output-position S) => 0
  (string-width "testing: " S) => 9
  (write-string "testing: " S)
  (output-position S) => 9
  (write-string "foo" S)
  (terpri S)
  (output-space 9 S) => T
  (write-string "bar" S)

  The output produced is
testing: foo
	 bar

Rationale:

  Pretty printing requires the function OUTPUT-WIDTH in order to know
  how wide the output it produces can be.  Pretty printing requires
  OUTPUT-POSITION in order to determine where on the line output is
  when pretty printing starts.  Pretty printing requires STRING-WIDTH
  in order to determine how much space things will take in the output.
  (If a variable width font is being used, this cannot be determined
  without a detailed knowledge of the font being used.)  Pretty
  printing requires OUTPUT-SPACE in order to get proper indentations.
  (If a variable width font is being used, indentations may be
  required that cannot be obtained by outputting spaces.)

Current Practice:

  Essentially every implementation of Common Lisp must support the
  functionality above internally in order to support PPRINT and the
  FORMAT directives ~T and ~<...~>.  However, there is no documented
  interface to this functionality in CLTL.  As a result, while some
  implementations of Common Lisp make this functionality available to
  users, some do not.  Further, the implementations that do provide
  this functionality do so in a variety of incompatible ways.

Cost to Implementors:

  This proposal is written in such a way as to allow implementations which
  do not have the ability to compute difficult values to just return NIL.
  Very little work is forced. The idea is to offer implementors a common way
  to provide this useful information to portable programs where possible.  

Cost to Users:

  None. This change is upward compatible.

Cost of Non-Adoption:

  Complex output programs such as pretty printers cannot be written portably.

Benefits:

  A wide range of programs can gain better control of the format of output.

Aesthetics:

  No significant aesthetic impact other than a slight increase in the
  number of functions defined.

Discussion:

  Dick Waters submitted a request for changes along the line of the
  horizontal aspects of these functions in a letter to X3J13 dated
  June 14, 1988.  Pitman and Waters wrote up the request formally.

  STREAM-INFO:ONE-DIMENSIONAL-FUNCTIONS is the minimum which is
  required in order to support pretty printing into a stream which
  displays output using a variable width font.

  We drafted an alternate proposal, STREAM-INFO:TWO-DIMENSIONAL-FUNCTIONS,
  which goes significantly beyond what is needed merely for pretty printing
  and provides primitives OUTPUT-DIMENSIONS, OUTPUT-POSITION,
  STRING-DIMENSIONS, and OUTPUT-SPACE but it is not included here.
  A key point of contention which would be likely to swamp the 2d proposal
  is the age old question of how to handle the issue of vertical distance
  (where is the origin, which way do you count, ...). If anyone would
  prefer to see larger problem 2d proposal, it could be circulated, but at
  the last minute Pitman got worried that even the 1d version was going to
  be controversial enough and decided to keep things focused on that.

  For his own needs, Waters is strongly interested in having either
  ONE-DIMENSIONAL-FUNCTIONS or TWO-DIMENSIONAL-FUNCTIONS proposal accepted,
  but does not care which. Pitman concurs.

  One variation of the 1d proposal might be useful to consider:
   STRING-WIDTH could return two additional values: the number of newlines
    that WRITE-STRING of the string would execute and the maximum X position
    encountered (which might differ from the first value if the number of
    newlines was non-zero).
  This feature wasn't necessary for Waters' minimalist proposal, but Pitman
  would be willing to write it in here if people thought it would be useful
  enough for other purposes.