e: Een volledig aanpasbare, zelfbewuste Emacs-achtige editor geschreven in Chez Scheme
De editor is een Scheme-systeem. Alles is een R6RS-bibliotheek: een minimale kern met een gepubliceerde, onveranderlijke API, en extensiemodules die hierop zijn gebouwd (de syntaxis-modi, haakjes-matching, de bewerkingshulpmiddelen en M-x zelf). De kern handhaaft zijn grenzen via de taal in plaats van via discipline: interne zaken zijn onzichtbaar en exports kunnen niet opnieuw worden toegewezen.
M-x evalueert Scheme tegen het actieve top-level van de editor, inclusief symboolaanvulling, parameter-hints tijdens het typen, geschiedenis en een transcriptie-buffer.
Modules kunnen hot-reloaden. Het opslaan van de broncode van een module binnen de editor laadt deze opnieuw in de actieve sessie; bronnen die elders bewerkt zijn, worden opgepikt met reload-module!. Registraties worden vervangen, afhankelijke modules worden opnieuw gecompileerd en buffers blijven behouden. e wordt ontwikkeld vanuit zichzelf.
Er zijn geen afhankelijkheden buiten Chez Scheme en een Unix-achtige terminal. Er is geen build- of installatiestap: een checkout kan direct worden uitgevoerd. De eerste start compileert de bibliotheken; latere starts duren ongeveer 100 ms.
De Undo-functie rapporteert wat hij ongedaan maakt: bijvoorbeeld Undo insert "hello" of Undo (replace-all! "xx" "yy"). Getypte tekens worden samengevoegd tot reeksen, een plakactie is één stap, en een M-x-commando is één gelabelde stap.
Snelstart
Als dagelijkse editor, gekloond als ~/.e:
git clone https://github.com/paveluv/e ~/.e
~/.e/e file.txt # voeg ~/.e toe aan PATH voor simpelweg `e`
Of als onderdeel van een project, zodat iedereen die het project kloont een werkende editor heeft:
git clone https://github.com/paveluv/e ~/git/your_project/.e
rm -rf ~/git/your_project/.e/.git # maak er gewone bestanden van in je repo
~/git/your_project/.e/e file.txt
Elke installatie is zelfvoorzienend: de loader gebruikt strikt de lib/ naast het script zelf, gecompileerde objecten blijven lokaal in eo/, en bronnen worden automatisch opnieuw gecompileerd wanneer zij (of iets wat ze importeren) wijzigen. Projectspecifieke modules in de lib/ van een vendored checkout blijven lokaal voor dat project.
De terminalgrootte wordt gedetecteerd via ioctl en bijgehouden bij wijzigingen; als dat niet beschikbaar is, stel dan LINES en COLUMNS in. Op FreeBSD, waar de Chez-port de scheme-script interpreter hernoemt, wijzig de shebang naar #!/usr/bin/env -S chez-scheme --script of voer uit: chez-scheme --script e.
Toetsenbordcombinaties
Bestanden en afsluiten
| Toets | Actie |
|---|---|
C-x C-s | Opslaan (vraagt om een naam in een onbenoemde buffer) |
C-x C-w | Opslaan als: vraagt om een pad, de buffer bezoekt dit |
C-x C-f | Bezoek een bestand in zijn eigen buffer (maakt deze aan indien nodig) |
C-x C-c | Afsluiten (vraagt alleen als een buffer verschilt van het bestand) |
Buffers en vensters
| Toets | Actie |
|---|---|
C-x b | Buffer wisselen (standaard: meest recente andere; nieuwe naam maakt een buffer aan) |
C-x C-b | Open buffers: beweeg met Up/Down, Enter om te selecteren; klikken selecteert direct |
M-Up | Schakel naar de vorige buffer (alfabetisch) |
M-Down | Schakel naar de volgende buffer (alfabetisch) |
C-x k | Een buffer verwijderen (standaard: huidige; vraagt bij wijzigingen) |
C-x 2 | Huidige venster splitsen in tweeën (gestapeld) |
C-x 3 | Huidige venster splitsen in tweeën (naast elkaar) |
C-x 1 | Alle andere vensters verwijderen |
C-x 0 | Huidige venster verwijderen |
C-x o | Ga naar het volgende venster |
C-x t | Soft-wrapping van lange regels in dit venster aan/uitzetten |
Elk venster heeft zijn eigen statusregel, cursorpositie (point) en scrollpositie; dezelfde buffer kan in meerdere vensters tegelijk worden getoond. Buffers hebben per-buffer geschiedenis en markeringen; vensters hebben onafhankelijke cursorposities, scrolling, wrapping en status. Een scrollbalk aan de rechterkant wordt standaard getoond; C-x l schakelt een regelnummer-gutter aan/uit.
Beweging
| Toets | Actie |
|---|---|
C-b / C-f / pijlen | Eén teken naar links / rechts |
C-p / C-n / pijlen | Eén regel omhoog / omlaag |
C-a / C-e, Home/End | Begin / einde van de regel |
C-v / M-v, PgDn/PgUp | Pagina omlaag / omhoog |
M-< / M-> | Begin / einde van de buffer |
Bewerken
| Toets | Actie |
|---|---|
C-d, Delete | Verwijder het teken na de cursor |
| Backspace | Verwijder het teken voor de cursor |
C-o | Open een regel eronder, laat cursor op huidige plek |
C-k | Verwijder tot het einde van de regel (herhalingen accumuleren) |
C-@ (C-Space) | Markering zetten |
C-w | Verwijder de regio tussen markering en cursor |
C-y | Plak (yank) de laatste verwijdering |
C-_ | Ongedaan maken (Undo) |
C-M-_ | Herstellen (Redo) |
TAB | Regel inspringen volgens de regels van de huidige modus |
M-% | Query replace vanaf cursor: y/SPC vervangt, n/DEL slaat over, q/RET/C-g stopt |
M-. | Beschrijf het symbool op de cursor (alleen Scheme buffers) |
C-x C-e | Evalueer de huidige buffer als Scheme |
C-l | Scherm opnieuw tekenen en grootte opnieuw lezen |
C-g | Annuleren (prompt, zoeken, markering, actieve evaluatie) |
C-h k | Beschrijf een toets en waar de bindingen vandaan komen |
Aanpassing van toetsen
Alle toetsenbordcommando's kunnen worden hergebonden in config.e. Gebruikersbindingen overschrijven de standaardwaarden van de editor en modules. C-h k beschrijft een toets, de bron ervan en de contextuele betekenissen.
Voorbeeld:
(bind-key! "C-c s" save!!)
(unbind-key! "C-v")
Zoeken
C-s start een incrementele zoekopdracht: typ om de zoekterm uit te breiden, C-s opnieuw om naar de volgende match te springen (met wrap-around), Backspace om de term te verkorten, RET/ESC om stilzwijgend te accepteren, en C-g om te annuleren en de cursor terug te zetten naar het beginpunt. Bij een nieuwe, lege zoekopdracht roept C-s de vorige zoekterm op.
De matches in het huidige venster worden gemarkeerd tijdens het zoeken. De hoofdlettergevoeligheid werkt slim (zoals in Emacs): het negeert hoofdletters alleen als de zoekterm volledig uit kleine letters bestaat; zodra er één hoofdletter wordt getypt, wordt het een exacte zoekopdracht (de prompt toont dan I-search (exact):). M-c wisselt tussen deze modi, en (search-fold-case #f) in config.e maakt elke zoekopdracht exact. Alles wat niet incrementeel is — inclusief M-% query replace — matcht altijd exact.
Inspringen en opmaak
Inspringen is het enige dat strikt wordt afgedwongen — spaties binnen een regel zijn de keuze van de auteur. Regels worden nooit automatisch samengevoegd of gesplitst. De Scheme-regels zijn Emacs-achtig: body-forms (define, lambda, de let-familie, when, ...) springen hun bodies twee kolommen in ten opzichte van de opener. Een eenzame closer staat direct onder de opener.
TAB springt de huidige regel in, waarbij wordt gecycled tussen de beschikbare stops. Modes kunnen een eigen indenter registreren; scheme-mode doet dit. indent-region! en indent-buffer! springen regel voor regel in vanaf de bovenkant als één undo-stap.
format-region! en format-buffer! doen daarnaast het volgende:
- Breiden tabs buiten strings uit naar spaties (instelbaar via
(scheme-tab-width 2)). - Verwijderen trailing whitespace.
- Verwijderen overbodige lege regels aan het einde van het bestand (zodat het bestand met exact één newline eindigt).
- Passen haakjes-conventies toe:
[ ]voor bindingen van delet-familie,do,parameterizeenwith-syntax, en voor clausules vancond,case,case-lambda,guard,syntax-rulesensyntax-case;( )voor al het andere. Dit kan worden uitgeschakeld viascheme-format-brackets.
De Scheme-engine is de pure (scheme-format) bibliotheek, die ook de tool tools/scheme-format aanstuurt. Met (scheme-format-on-save #t) (standaard aan) wordt elke Scheme-buffer geformatteerd via een pre-save hook voor het opslaan.
Prompts
Input in prompts is regel-bewerkbaar met de gebruikelijke bindingen (C-a/C-e, pijlen, Home/End, C-k/C-y) en ondersteunt aanvulling via TAB (bestandsnamen in bestandsprompts, buffernamen in bufferprompts). TAB breidt uit tot het langste gemeenschappelijke prefix; als er geen uitbreiding mogelijk is, opent een tweede TAB een completions venster.
Input die breder is dan het scherm wordt gewrapt naar vervolgregels, gemarkeerd met een trailing \. Bij acht regels begint het prompt-gebied te scrollen.
Scheme Evalueren
M-x evalueert Scheme in het actieve top-level van de editor, waar de gepubliceerde API en geladen modules beschikbaar zijn. C-x C-e evalueert de huidige buffer. Resultaten verschijnen in de echo area en de gestructureerde log.
Voorbeeld: M-x (buffer-name (current-buffer)) eval: (buffer-name (current-buffer)) => "sa.txt"
Echo area en log
De echo area is een tijdelijke weergave van de log. Elk bericht wordt op een eigen regel geplaatst, voorafgegaan door het component in het grijs. Berichten gemarkeerd als progress vervangen de nieuwste regel van hun component in plaats van een nieuwe regel te stapelen.
Alles wat door de echo area gaat, belandt in de log buffer — het syslog van de editor met records van tijd (nanoseconde-precisie), component en tekst. De log buffer is een read-only weergave die automatisch wordt ververst. Met (log-view 'eval) kan een gefilterde weergave worden gemaakt (log eval).
Algemene bewerkingshulpmiddelen
Generieke bewerkingshulpmiddelen bevinden zich in de (edit) module en accepteren een optioneel where argument. Als dit wordt weggelaten, betekent dit de geselecteerde regio of de gehele huidige buffer.
Voorbeelden:
M-x (replace-all! "xx" "yy")— vervangt in de huidige buffer.M-x (replace-all! "xx" "yy" buffer-file)— vervangt in elke file-buffer.M-x (count-matches "xx" "notes.md")— telt matches zonder te bewerken.
Documentatie en de Scheme Manual
De Scheme-handleiding is beschikbaar in de editor:
M-x (describe eq-hashtable-ref)C-h f(describe!!) vraagt om een gedocumenteerde functienaam.
Het opent een describe buffer met informatie over vormen, retourwaarden, bibliotheken, bron en volledige tekst. Als de naam een commando is, worden ook alle gebonden toetsen getoond. De data omvat R6RS (van TSPL4) en Chez-extensies. Voer één keer M-x (fetch-describe-data!) uit om de data (ca. 1.400 vermeldingen) te downloaden.
In een Scheme-buffer beschrijft M-. het symbool waar de cursor op staat. Modules kunnen eigen pagina's toevoegen via register-descriptions!.
Pretty Parens
M-x (pretty-scheme-clusters!) schakelt een modus in waarbij haakjes van constructen worden weergegeven als Unicode-paren, terwijl het bestand zelf ASCII blijft:
- Definities:
「 」 - Lambdas:
<0xE2><0xA6><0x91> <0xE2><0xA6><0x92> - Let-familie:
⟨ ⟩ - Conditionals:
<0xE2><0xA6><0x85> <0xE2><0xA6><0x86> - Control:
⟦ ⟧ - Iteratie:
<0xE2><0x9F><0x85> <0xE2><0x9F><0x86> - Syntax:
<0xE2><0xA7><0xBC> <0xE2><0xA7><0xBD> - Modules:
⸨ ⸩ - Quoting:
‹ › set!:<0xE2><0xA7><0x98> <0xE2><0xA7><0x99>delay:⌊ ⌋
Applicaties en clausules blijven gewone haakjes, zodat de structuur zichtbaar is zonder deze te overspoelen. Er zijn ook varianten: pretty-scheme-depth! (rotatie op basis van diepte) en pretty-scheme-rainbow! (kleuren per diepteniveau).
Integriteit
De editor vergelijkt bestandsinhoud vóór het bewerken en opslaan, canonicaliseert bezochte paden en overschrijft externe wijzigingen nooit stilzwijgend. Het biedt opties voor overschrijven, opnieuw lezen of een three-way merge.
Configuratie
config.e is een eenvoudig Scheme-bestand zonder bibliotheek of shebang. Elke expressie wordt geëvalueerd in het top-level van de editor. Het wordt geladen bij opstarten en na elke module-reload.
config.e staat niet in versiebeheer. Er wordt een config.template.e meegeleverd met alle opties als commentaar. Kopieer deze naar config.e om te beginnen:
cp config.template.e config.e
Architectuur en extensiemodules
Alles in lib/ is een bibliotheek met de extensie .e.
- Core: De generieke kernel (buffers, vensters, bewerkingen, rendering, prompts).
- Sys: De systeemlaag (
libc,termios,ioctl, signalen) en de enige bibliotheek met foreign procedures.
De exports van de core vormen de gepubliceerde API. Internen en muteerbare staat zijn onzichtbaar buiten de core. De API bevat commando-procedures, read-only state accessors en extensie-hooks (bind-key!, register-mode!, etc.).
Naamgevingsconventies (Bangs)
!!(twee bangs): Wacht op input van de gebruiker (prompt, bevestiging). Neemt geen vereiste argumenten en retourneert niets.!(één bang): Acteert direct op wat wordt meegegeven, zelfs als er iets wordt getoond (bijv.list-buffers!).
Extensiemodules
Een extensiemodule is een bibliotheek die (core) importeert en een init! exporteert voor registraties. Bij opstarten laadt de core elke lib/*.e bibliotheek en roept init! aan.
Modules kunnen worden herladen zonder de editor te herstarten. Het opslaan van een .e bestand in lib/ laadt dit direct opnieuw. Voor externe bewerkingen gebruik je M-x (reload-module! "naam").
Syntaxis-highlighting
Highlighting wordt verzorgd door modes, geregistreerd via register-mode!. Er worden drie modi meegeleverd: scheme-mode.e, c-mode.e en md-mode.e (Markdown).
Contextuele highlighting (afhankelijk van de cursorpositie) wordt verzorgd door highlighters via add-highlighter!. Voorbeelden hiervan zijn haakjes-matching (paren.e) en incrementeel zoeken (search.e).
Overige details
Muisgebruik
- Klikken: Focus op venster en cursor plaatsen.
- Slepen: Selecteren (werkt samen met
C-w,C-y). - Dubbelklik: Selecteer woord.
- Slepen statusbar: Vensters redimensioneren.
- Wiel: Scrollen (1/8e van de hoogte) of horizontaal bewegen.
Native terminal-selectie vereist dat Shift wordt ingedrukt.
Overige functionaliteiten
- Bracketed paste: Plakacties worden als één edit en één undo-stap behandeld.
- Kill ring: Verwijderingen accumuleren bij opeenvolgende
C-kcommando's. - Incrementele updates: Alleen gewijzigde regels worden opnieuw getekend; styling is gememoiseerd.
Beperkingen
- De vensterindeling is simpel: horizontale banden kunnen zij-aan-zij kolommen bevatten, maar het is geen algemeen tiling-systeem.
- Tabs en andere controletekens worden weergegeven als een enkele spatie.
- Input wordt gelezen als UTF-8, maar elk teken wordt geacht één terminalkolom in beslag te nemen.
Licentie
MIT
Groetjes,