1 Lisp Text
dxtr edited this page 2026-10-19 07:17:49 +02:00

Lisp Text

pyssel/editor/lisp-text reads Lisp's text without the reader, as the code editor reads it to colour, indent and find forms: it takes text that does not read, as code being typed is, interns nothing and evaluates nothing. It depends on Common Lisp alone and asks what it needs of a Lisp, as whether a name is a macro's, of functions you give it, so that it can become a library of its own (code-editor.org, decision 6).

(asdf:load-system :pyssel/editor)

(pyssel/editor/lisp-text:scan-line "(defun f (x) ; doubles" :code)
;; => #(0 :TEXT 1 :KEYWORD 6 :TEXT 7 :DEFINITION 8 :TEXT 13 :COMMENT), :CODE

(pyssel/editor/lisp-text:line-indentation (vector "(defun f (x)" "x)") 1)
;; => 2

Texts

A Lisp text is read through line-count and line-text, generic functions with methods for vectors of strings and for strings, whose line feeds part their lines. A lisp-text, as make-lisp-text makes of a vector of lines, also knows the state each line starts in and how many lists are open there, as Emacs's parse from a text's start counts them, and the lines its in-package forms are on: it finds them as they are asked for, reading from the first line once, without allocating, and keeps them, so that what is read at a place is read from the top-level form the place is in, and a text's package is found at once, however long the text. Call lisp-text-changed with the first line changed when its lines change. A text of your own, as the Script screen's code edits' lines are, subclasses lisp-text, with methods on line-count and line-text. Without the depths, as for a plain vector, a bracket at a line's start is taken for a top-level form's, as Emacs's beginning of defun takes one.

Colours

scan-line reads a line from the state the line before ended in, +start-state+ at a text's start, and gives its runs, a simple vector of each run's start and its kind, as a code edit's highlighter gives them, and the state it ends in: :code, :string, :bar in a symbol's multiple escape, or the count of block comments open. The kinds: :comment; :string, strings and multiple escapes, whose brackets do not count, as Emacs's syntax has it; :constant, characters, keywords, T and NIL; :number; :type, lambda-list keywords; :symbol, quotes, commas and #'; :keyword, :control-flow or :function for the operator at a form's head, as its :operator-kind, a function of the operator's text, says, operator-kind by default, which knows Common Lisp's standard operators; the kind it gives as a second value for the name a definition defines, as :definition after defun, :variable after defvar and :type after defclass; and :text.

Forms

read-forms reads lines into forms, as Emacs's Lisp mode counts sexps: #'car is one, #+sbcl another and #+(or a b) two. A form knows its kind, :list, :string or :token, where it starts, its prefixes as ' and #' included, where what follows them starts, where it ends, or that it is not closed, its children and a token's or a string's text. Text that does not read is read as far as it goes.

  • top-level-form-at gives the top-level form at a place, or the last before it, as a form is compiled at the caret; form-before the form just before a place, as SLY evaluates the last sexp; forward-form and backward-form where Emacs's forward-sexp and backward-sexp go. Each reads the forms around the place only.
  • enclosing-lists gives the lists that hold a place, the innermost first, and argument-index which argument of one the place is at, as an argument hint wants.
  • form-at-path follows a compiler's source path, counted the reader's way, feature expressions in or out as a function you give says, a dispatch as #2A one object with what follows it, and 'x read as (quote x); form-number-path turns SBCL's form number of a definition into such a path.
  • text-package-name names the package a place is read in, as SLY finds a buffer's: the last in-package before it, or else the first after it, its argument's name as the reader makes it, token-symbol-name giving a token's symbol's name and its package's.

Indentation

line-indentation gives the column a line starts at, as Emacs with SLY indents Common Lisp, rules written anew from what they do (decision 4): the list holding the line, Emacs's normal indentation under its first element or first argument, then SLY's specs of operators, by name, out through the lists holding it, six at most, def and with- names guessed as SLY guesses them, quoted lists, vectors, feature expressions and keyword arguments aligned, and LOOP, TAGBODY, DO and DEFMETHOD by rules of their own. It returns NIL for a line Emacs leaves as it is, inside a string, or a comment of three semicolons, and puts a comment of one at column 40. Its :specs, a function of an operator's text in lower case, its package prefix included, gives the specs of operators that are not standard, an indentation-spec, in the language of SLY's specs: numbers, &lambda, &body, &rest, lists that start with &whole, keywords as :loop for a form's own rules, and (as "name"), compared by name so that they may be in any package; macro-indentation-spec makes one from a macro's lambda list, as Slynk makes SLY's. A spec that is none is a malformed-spec. The indentation is tested against what Emacs with SLY makes of 148,000 lines of the workspace's code, and of a corpus of every rule, line for line.

Thread rules

Its functions keep no state between calls but what a lisp-text keeps, which is used from one thread at a time.