This library provides protocols and implementations of those protocols for text editing operations which are required by, for example, text editors or commandline processors (like REPLs). This library relies on the Cluffer library for the fundamental concepts of buffers, lines and cursors. The functionality provided by this library includes:
- Cursor movement (by various “units”)
- Insertion and deletion (by various “units”)
- Transformations (for example changing case or transposing)
- Killing, copying and inserting
- Multiple cursors
- “Mark” cursors and regions
- Undo (work in progress)
- Operations on expression (like paredit for Emacs)
- Search and incremental search
- Abbreviations (work in progress)
This library does not provide
- Data structures for editor buffers (the cluffer library does that)
- Input handling or a command processor
- Advanced parsing (see incrementalist for that)
- Syntax highlighting (see TODO for that)
- Presentation/rendering/display functions for the contents of text buffers
*This library is under active development. Its ASDF system structure, package structure, exported symbols and protocols may change at any time. Consult the NEWS file or the “Changelog” section of the manual for lists of changes in specific versions.*
This document only gives a very brief overview and highlights some features. Proper documentation can be found in the file:documentation directory.
Using this library typically requires a buffer class defined by the client which includes the desired mixin classes. For example
(defclass my-buffer (text.editing:multiple-site-mixin
text.editing:site-mixin
cluffer-standard-buffer:buffer)
())
#<STANDARD-CLASS COMMON-LISP-USER::MY-BUFFER>
This buffer class is like a Cluffer buffer but also has a primary and zero or more additional sites. Creating an instance of the buffer class automatically creates the primary site an attaches its point cursor to the initial line of the buffer:
(defvar *my-buffer*
(make-instance 'my-buffer :initial-line (make-instance 'cluffer-standard-line:closed-line)))
(print *my-buffer*)
(print (text.editing:site *my-buffer*))
(print (text.editing:sites *my-buffer*))
(print (text.editing:point (text.editing:site *my-buffer*)))
#<MY-BUFFER 1 line 0 items {1006791483}> #<SITE 0:0 {1006B03D03}> (#<SITE 0:0 {1006B03D03}>) #<CLUFFER-STANDARD-LINE:RIGHT-STICKY-CURSOR 0:0 {1006D4EDC3}>
This buffer instance is usable with the operations provided by this library:
(defvar *point* (text.editing:point *my-buffer*))
(setf (text.editing:items *point* text.editing:buffer :forward)
(format nil "one~%two!~%three~%"))
(defun summary ()
(values (coerce (text.editing:items *point* text.editing:buffer :forward) 'string)
(text.editing:sites *my-buffer*)))
(summary)
"one two! three " (#<SITE 3:0 {1006B03D03}>)
(text.editing:change-case *point* text.editing:word :backward :up)
(summary)
"one two! THREE " (#<SITE 2:0 {1006B03D03}>)
While many operations can be applied directly to a cursor, it is preferable use to the “operation protocol” and let the library figure out how and to which object to apply the operation in question:
(text.editing:perform *my-buffer* 'text.editing:move text.editing:word :backward)
(text.editing:perform *my-buffer* 'text.editing:delete text.editing:line :forward)
(summary)
"one THREE " (#<SITE 1:0 {1006B03D03}>)
The deleted word is now in the insertion stack (which is like the “kill ring” in Emacs) and can be inserted:
(loop :repeat 3 :do (text.editing:perform *my-buffer* 'text.editing:yank :forward))
(summary)
"one two!two!two! THREE " (#<SITE 1:12 {1006B03D03}>)
It is easy to see how the operation protocol comes into play when multiple sites are used:
(text.editing:push-site-relative *my-buffer* text.editing:line :forward)
(summary)
"one two!two!two! THREE " (#<SITE 1:12 {1006B03D03}> #<SITE 2:5 {100AE016D3}>)
(text.editing:perform *my-buffer* 'text.editing:move text.editing:line-boundary :backward)
(text.editing:perform *my-buffer* 'text.editing:change-case text.editing:word :forward :capital)
(summary)
"one Two!two!two! Three " (#<SITE 1:3 {1006B03D03}> #<SITE 2:5 {100AE016D3}>)