This document analyzes the architecture of text handling in ChrysaLisp, tracing the hierarchy from low-level data management to high-level visual rendering and application logic.
The architecture demonstrates a strict separation of concerns, utilizing a Model-View-Controller pattern adapted for ChrysaLisp's object capabilities. A shared abstraction layer ensures that text manipulation logic is identical whether driven by a graphical user interface or a headless batch script.
The core text manipulation logic resides in the library layer (lib/text/).
These classes have no knowledge of pixels, fonts, or windows; they operate
purely on coordinate tuples and strings.
The Buffer class is the base unit of text storage and atomic manipulation.
It manages the raw state of the text and the "cursors" navigating it.
-
State:
-
buffer_lines: A list of strings representing the file content. -
cursors: A list of cursor tuples. ChrysaLisp supports multi-cursor editing natively at this low level. -
undo_stack/redo_stack: Built-in history management.
-
-
The Cursor Tuple: A cursor is not just an X/Y pair. It is defined as
(cx cy ax ay sx).-
cx, cy: The active cursor position (the "head"). -
ax, ay: The anchor position (the "tail"). If distinct fromcx, cy, a selection exists. -
sx("Sticky X"): Remembers the desired horizontal column when moving vertically through lines of varying lengths.
-
-
Key Responsibilities:
-
Atomic Mutation: Methods like
:iinsert(internal insert) and:ideleteperform the actual string manipulation and record state for Undo/Redo. -
Navigation: Implements logic for
:left,:right,:up,:down,:home, and:end. It handles wrapping across line boundaries. -
Cursor Management:
:merge_cursorsensures that multiple cursors overlapping or touching are combined into single selection ranges, preventing logic errors during bulk edits. -
Clipping:
:clip_cursorensures coordinates remain within the bounds of the text content.
-
Document inherits from Buffer. It expands the low-level character
manipulations into higher-level semantic text operations. It represents a
"file" in memory.
-
Semantic Selection:
-
Uses character class logic (from
lib/text/charclass.inc) to implement:select_word. -
Implements
:select_paragraphby scanning for blank lines. -
:select_blockhandles bracket matching logic.
-
-
Text Processing:
-
Implements functional text transformations like
:to_upper,:to_lower,:sort(lines), and:unique. -
Handles Indentation (
:tab,:left_tab) and reformatting (:reflow,:split).
-
-
I/O:
-
:stream_loadand:stream_savemanage reading from and writing tostreamobjects. -
Integrates with the
Syntaxengine to generate highlighting data (buffer_syntax) during load or mutation.
-
To allow the same code to drive both a GUI editor and a headless script, ChrysaLisp utilizes a polymorphic abstraction layer.
This file defines a suite of global functions (e.g., edit-down,
edit-insert, edit-copy) that act as the public API for text
manipulation. These functions do not contain logic themselves; they operate
on a dynamic variable *edit*.
(defmacro gen-edit (n m) `(defun ,(sym (str "edit-" n)) () (. *edit* ,m)))
(gen-edit top :top)
(defun edit-insert (txt) (. *edit* :insert txt))
-
Polymorphism:
-
In a GUI context,
*edit*is bound to anEditWidget. -
In a CLI context,
*edit*is bound directly to aDocument. -
Because the
Editwidget proxies methods to its internalDocument, the API signature is identical in both contexts.
-
The GUI layer (gui/) is responsible for visualizing the Document.
ChrysaLisp uses a composition approach where the editor widget aggregates
several specialized sub-views.
The Edit class is the main visual component. It inherits from View.
Crucially, it contains a Document instance (stored in the :buffer
property) rather than inheriting from it.
-
Composition: An
Editview is a container for specific visual layers added as children:-
Vdu: Renders the actual text glyphs. -
Mask(Ink): Renders bracket matching highlights. -
Mask(Selected): Renders selection backgrounds (e.g., grey blocks). -
Mask(Found): Renders search result highlights. -
Mask(Region): Renders region locks/focus.
-
-
Proxy Delegation:
Because
Edithas-aDocumentbut needs to conform to the API expected bylib/text/edit.inc, it usesdefproxymethod.(defproxymethod :left () :buffer) (defproxymethod :insert (text) :buffer)This creates a method on
Editthat looks up the object inthis->bufferand calls the corresponding method on it. -
The
:underlayMethod:This is the rendering heart of the
Editclass. Every frame (or upon refresh), it calculates the intersection of thebufferstate with the visual viewport.-
It calculates pixel coordinates based on the
vdu_textchar size. -
It translates text coordinates (lines/cols) into visual regions (rectangles).
-
It calls
:add_opaqueon the variousMaskchildren to define where colors should be drawn. -
It loads the visible slice of text into the
Vdu.
-
-
Mask(gui/mask/lisp.inc): A lightweightView. Its:drawmethod simply sets a color and fills the+view_opaque_region. This relies on the efficient region arithmetic in the GUI kernel to draw complex, non-contiguous selection shapes without manual geometry management. -
Vdu(gui/vdu/): A specialized view for monospaced text rendering. It manages a texture cache of glyphs and a grid of characters.
Applications wire the model and view to user inputs or scripts.
This is the interactive controller.
-
Initialization: Creates an
Editor-editwidget (subclass ofEdit) and aDocument. -
Event Handling:
actions.incmaps key presses (via*key_map*) to the functions inlib/text/edit.inc(e.g.,Ctrl+C->action-copy->edit-copy). -
Feedback Loop: After an action changes the buffer,
(refresh)is called. This triggers theEditwidget's:underlaymethod to update the visual masks and VDU text.
Inherits Edit functionality via Viewer-edit but overrides mouse
interactions to support read-only behaviors (clicking to follow links)
rather than placing cursors for typing. It reuses the exact same display
logic as the Editor.
This is the headless controller, used for batch processing or scripting.
-
Architecture: It instantiates a
Documentbut not anEditwidget. -
Binding: It binds the
Documentinstance to the*edit*variable. -
Execution:
-
It accepts a script via
-c(command string) or-s(script file). -
The script executes in an environment where functions like
(edit-down)or(edit-replace "foo")are available. -
These functions operate directly on the
Documentvia the*edit*binding.
-
-
Result: Complex editing operations (multi-cursor find/replace, block moves) can be performed programmatically with zero GUI overhead, using the exact same logic code as the interactive editor.
When a command like (edit-insert "A") is executed:
-
If in GUI:
-
lib/text/edit.inccalls(. *edit* :insert "A"). -
*edit*is anEditwidget. It proxies the call to its internal:buffer. -
Bufferupdates text and cursors. -
The App calls
(refresh), causingEditto redraw masks and glyphs.
-
-
If in CLI:
-
lib/text/edit.inccalls(. *edit* :insert "A"). -
*edit*is aDocumentobject. It executes the update directly. -
No refresh or rendering occurs.
-
This architecture ensures zero duplication of effort, high performance, and total consistency between graphical and command-line text manipulation tools.