Non so se questo suggerimento è "buono" per le definizioni sofisticate di "buono", ma è facile e divertente. Ho spesso impostato un esercizio per scrivere il nucleo di un editor di testo in Haskell, collegandolo con il codice di rendering che fornisco. Il modello dati è il seguente.
Innanzitutto, definire ciò che deve essere un cursore all'interno di un elenco di x
-elementi, in cui le informazioni disponibili al cursore contiene qualche tipo m
. (Il x
si rivelerà essere Char
o String
.)
type Cursor x m = (Bwd x, m, [x])
Questa Bwd
cosa è solo il arretrate "SNOC-liste". Voglio mantenere forti intuizioni spaziali, quindi cambio le cose nel mio codice, non nella mia testa. L'idea è che la roba più vicina al cursore sia la più facilmente accessibile. Questo è lo spirito di The Zipper.
data Bwd x = B0 | Bwd x :< x deriving (Show, Eq)
fornisco un tipo Singleton gratuita di agire come marcatore leggibile per il cursore ...
data Here = Here deriving Show
... e posso quindi dire che cosa è di essere da qualche parte in un String
type StringCursor = Cursor Char Here
Ora, per rappresentare un buffer di più linee, dobbiamo String
s sopra e sotto la linea con il cursore, ed una StringCursor
nel mezzo, per la linea che' attualmente in fase di modifica.
type TextCursor = Cursor String StringCursor
Questo tipo TextCursor
è tutto che uso per rappresentare lo stato del buffer di modifica. È una cerniera a due strati. Fornisco agli studenti codice per rendere una finestra sul testo in una finestra di shell abilitata per ANSI-escape, assicurando che il viewport contenga il cursore. Tutto quello che devono fare è implementare il codice che aggiorna lo TextCursor
in risposta alle sequenze di tasti.
handleKey :: Key -> TextCursor -> Maybe (Damage, TextCursor)
dove handleKey
dovrebbe restituire Nothing
se il tasto è priva di senso, ma per il resto fornire Just
un aggiornamento TextCursor
e un "rapporto danni", quest'ultimo è uno dei
data Damage
= NoChange -- use this if nothing at all happened
| PointChanged -- use this if you moved the cursor but kept the text
| LineChanged -- use this if you changed text only on the current line
| LotsChanged -- use this if you changed text off the current line
deriving (Show, Eq, Ord)
(Se vi state chiedendo qual è la differenza tra restituire Nothing
e restituire Just (NoChange, ...)
, considerare se si desidera che l'editor venga emesso un segnale acustico.) Il rapporto sui danni indica al renderer quanto lavoro deve svolgere per aggiornare l'immagine visualizzata.
Il tipo Key
fornisce semplicemente una rappresentazione di dataype leggibile alle possibili sequenze di tasti, allontanandosi dalle sequenze di escape ANSI non elaborate. È insignificante.
ho fornire agli studenti un grande indizio per andare su e giù con questo modello di dati, offrendo questi pezzi di corredo:
deactivate :: Cursor x Here -> (Int, [x])
deactivate c = outward 0 c where
outward i (B0, Here, xs) = (i, xs)
outward i (xz :< x, Here, xs) = outward (i + 1) (xz, Here, x : xs)
La funzione deactivate
viene utilizzata per spostare l'attenzione da un Cursor
, dando una lista ordinaria, ma che ti dice dove il cursore era. La funzione corrispondente activate
tenta di posizionare il cursore in una determinata posizione in un elenco:
activate :: (Int, [x]) -> Cursor x Here
activate (i, xs) = inward i (B0, Here, xs) where
inward _ [email protected](_, Here, []) = c -- we can go no further
inward 0 c = c -- we should go no further
inward i (xz, Here, x : xs) = inward (i - 1) (xz :< x, Here, xs) -- and on!
offro agli studenti una definizione volutamente scorretta e incompleta di handleKey
handleKey :: Key -> TextCursor -> Maybe (Damage, TextCursor)
handleKey (CharKey c) (sz,
(cz, Here, cs),
ss)
= Just (LineChanged, (sz,
(cz, Here, c : cs),
ss))
handleKey _ _ = Nothing
che gestisce solo le battiture carattere ordinario, ma fa uscire il testo all'indietro. È facile vedere che il carattere c
appare a destra di Here
. Li invito a correggere il bug e aggiungere funzionalità per i tasti freccia, backspace, delete, return e così via.
Potrebbe non essere la rappresentazione più efficiente di sempre, ma è puramente funzionale e consente al codice di conformarsi concretamente alle nostre intuizioni spaziali sul testo che viene modificato.
si può guardare in cerniere .. – Satvik
Potreste essere costretti dal framework GUI che si sta utilizzando (a meno che non hai intenzione di un'applicazione basata console) che potrebbero gestire tutto il montaggio del testo per te direttamente dalla scatola. – AndrewC
Cerca la lista con cerniera. –