buffer

package
v0.0.0-...-4916209 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 2, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package buffer contiene el modelo de texto de FlowCode.

De momento solo la parte de lectura: cargar un archivo y saber dónde empieza cada línea. La estructura editable —el rope con transacciones, deshacer y múltiples cursores— llega en el Hito 3.

El paquete no depende de nada gráfico a propósito. Abrir un archivo, indexar sus líneas y consultarlas debe poder probarse sin abrir una ventana.

Index

Constants

This section is empty.

Variables

View Source
var ErrModifiedSince = errors.New("buffer: el archivo ha cambiado en el disco desde que se abrió")

ErrModifiedSince se devuelve al guardar cuando el archivo ha cambiado en el disco desde que se leyó.

Es el equivalente del FILE_MODIFIED_SINCE de VS Code, y existe por la misma razón: la regla de no recargar nunca un documento con cambios sin guardar —ver Reload— deja abierta la posibilidad de que las dos versiones hayan divergido, y el único momento honesto para decirlo es cuando se va a escribir encima. Guardar sin mirar convertiría la regla que protege el trabajo propio en una que destruye el ajeno.

View Source
var NativeEOL = nativeEOL()

NativeEOL es el final de línea que se usa en los archivos nuevos.

Functions

func Transform

func Transform(offset int, bias Bias, edits []Edit) int

Transform traslada una posición a través de un conjunto de ediciones.

Las ediciones deben venir ordenadas y sin solaparse, que es como las produce una transacción. Es la operación de la que dependen los cursores, la selección y cualquier marca que tenga que sobrevivir a lo que se escriba por encima.

Types

type Bias

type Bias uint8

Bias decide hacia dónde se mueve una posición cuando la edición ocurre justo donde está.

No hay una respuesta universal y por eso es un parámetro: el cursor de quien escribe quiere quedarse detrás de lo que acaba de teclear, y el principio de una marca de git quiere quedarse delante de lo que se inserta encima para no tragárselo.

const (
	// BiasBefore deja la posición delante del texto insertado.
	BiasBefore Bias = iota

	// BiasAfter la empuja detrás.
	BiasAfter
)

Direcciones posibles.

type EOL

type EOL uint8

EOL es el final de línea con el que se escribe el archivo.

const (
	// LF es el de Unix y el que se usa siempre por dentro.
	LF EOL = iota

	// CRLF es el de Windows. Solo aparece al leer y al escribir.
	CRLF
)

Finales de línea reconocidos.

func (EOL) String

func (e EOL) String() string

String devuelve el nombre que se muestra en la barra de estado.

type Edit

type Edit struct {
	Start, End int
	Text       string
}

Edit es una sustitución: quita el texto entre Start y End y pone Text en su lugar.

Las tres operaciones de un editor son casos de esta: insertar es un rango vacío con texto, borrar es un rango con texto vacío, y reemplazar es las dos cosas. Tener una sola forma es lo que permite que deshacer sea la misma operación al revés en lugar de tres caminos distintos que se desincronizan.

func (Edit) Delta

func (e Edit) Delta() int

Delta es cuánto crece o encoge el texto al aplicar la edición.

func (Edit) String

func (e Edit) String() string

String describe la edición para los mensajes de error y los volcados.

type Encoding

type Encoding uint8

Encoding es la codificación con la que estaba escrito el archivo.

FlowCode trabaja por dentro en UTF-8 y punto. Lo que hace esta lista es recordar de dónde venía el archivo para devolverlo igual: convertir a UTF-8 sin avisar un archivo que el resto del equipo tiene en UTF-16 produce un diff de miles de líneas por haber tocado una.

const (
	UTF8 Encoding = iota
	UTF8BOM
	UTF16LE
	UTF16BE
)

Codificaciones reconocidas.

func (Encoding) String

func (e Encoding) String() string

String devuelve el nombre que se muestra en la barra de estado.

type File

type File struct {
	// contains filtered or unexported fields
}

File es un archivo de texto cargado en memoria con su índice de líneas.

Es seguro leerlo desde el hilo de dibujo mientras el indexado avanza en otra goroutine: las consultas nunca se bloquean ni esperan.

func New

func New(data []byte, path string, onIndexed func()) *File

New construye un archivo a partir de contenido ya cargado.

func Open

func Open(path string, onIndexed func()) (*File, error)

Open carga un archivo y arranca su indexado.

onIndexed se invoca desde otra goroutine cuando el índice completo está listo. Sirve para que la interfaz se repinte con el número real de líneas; puede ser nil.

Los archivos grandes se proyectan en memoria en lugar de copiarse: abrir un gigabyte deja de costar casi un segundo de lectura y pasa a ser inmediato, porque el sistema solo trae del disco las páginas que se llegan a mirar.

func (*File) Close

func (f *File) Close() error

Close libera la proyección en memoria del archivo.

Tras llamarlo, cualquier acceso al contenido es un error grave: el sistema ya no tiene esas direcciones mapeadas y leerlas aborta el proceso. Solo debe cerrarse cuando nada vaya a volver a dibujarlo.

func (*File) IndexDuration

func (f *File) IndexDuration() int64

IndexDuration devuelve lo que tardó el índice completo, o cero si aún no ha terminado.

func (*File) Indexing

func (f *File) Indexing() bool

Indexing indica si el índice completo todavía se está construyendo. Mientras sea cierto, LineCount devuelve una cota inferior que irá creciendo.

func (*File) Len

func (f *File) Len() int

Len devuelve el tamaño en bytes. Es Size con el nombre que espera quien lo trata como un documento cualquiera.

func (*File) Line

func (f *File) Line(i int) string

Line devuelve el contenido de una línea, sin el salto final.

El resultado comparte memoria con el archivo: es una vista, no una copia, y no debe modificarse. Devolver copias haría que desplazarse por un archivo grande generase basura sin parar.

func (*File) LineCount

func (f *File) LineCount() int

LineCount devuelve el número de líneas conocidas.

func (*File) LineEnd

func (f *File) LineEnd(line int) int

LineEnd devuelve el desplazamiento donde termina una línea, sin su salto.

func (*File) LineOf

func (f *File) LineOf(offset int) (line, start int)

LineOf devuelve la línea que contiene un desplazamiento y dónde empieza esa línea.

Es una búsqueda binaria sobre el índice, no un recorrido: en un archivo de treinta millones de líneas la diferencia entre las dos cosas es la que hay entre responder al instante y bloquear el fotograma.

func (*File) LineStart

func (f *File) LineStart(line int) int

LineStart devuelve el desplazamiento donde empieza una línea.

func (*File) Mapped

func (f *File) Mapped() bool

Mapped indica si el contenido está proyectado en memoria en lugar de copiado.

func (*File) Path

func (f *File) Path() string

Path devuelve la ruta del archivo.

func (*File) Size

func (f *File) Size() int

Size devuelve el tamaño en bytes.

func (*File) Slice

func (f *File) Slice(start, end int) string

Slice devuelve el texto entre dos desplazamientos.

type History

type History struct {
	// contains filtered or unexported fields
}

History es la pila de deshacer y rehacer.

func (*History) Break

func (h *History) Break()

Break corta la agrupación: lo siguiente que se escriba empieza una operación de deshacer nueva.

Lo llama el editor cuando el cursor se mueve, se guarda el archivo o cambia la selección. Sin eso, escribir aquí, mover el cursor y escribir allá se deshacen de golpe, que no es lo que nadie espera.

func (*History) CanRedo

func (h *History) CanRedo() bool

CanRedo indica si queda algo que rehacer.

func (*History) CanUndo

func (h *History) CanUndo() bool

CanUndo indica si queda algo que deshacer.

func (*History) Depth

func (h *History) Depth() (undo, redo int)

Depth devuelve cuántas transacciones hay registradas, para diagnóstico.

func (*History) Push

func (h *History) Push(e entry)

Push registra una transacción ya aplicada.

func (*History) Redo

func (h *History) Redo() []entry

Redo devuelve el siguiente grupo a rehacer, de la más antigua a la más reciente.

func (*History) Undo

func (h *History) Undo() []entry

Undo saca el último grupo de entradas, de la más reciente a la más antigua.

type Rope

type Rope struct {
	// contains filtered or unexported fields
}

Rope es el texto editable de FlowCode: un árbol equilibrado de trozos de bytes con el número de saltos de línea cacheado en cada nodo.

Por qué un árbol y no un slice

Un editor hace dos cosas todo el rato: insertar o borrar en medio, y traducir entre desplazamiento de byte y (línea, columna). Sobre un []byte plano lo primero mueve el archivo entero en cada tecla y lo segundo obliga a recorrerlo contando saltos. Con el recuento de bytes y de líneas cacheado por nodo, las dos operaciones bajan por el árbol y cuestan O(log n).

Qué garantiza y qué no

El árbol es uniforme en profundidad: todas las hojas están al mismo nivel, porque tanto la división como la fusión ocurren de abajo arriba. No es un B-tree estricto: tras borrar se permite que un nodo quede por debajo de la mitad, y en vez de pedir prestado a un hermano se fusionan los vecinos que quepan juntos. Es bastante más simple y el resultado práctico es el mismo, porque la fusión codiciosa deja los nodos razonablemente llenos.

No es seguro para uso concurrente. Pertenece al hilo que edita.

func NewRope

func NewRope(s string) *Rope

NewRope construye un rope a partir de un texto.

func (*Rope) Delete

func (r *Rope) Delete(start, end int)

Delete borra el texto entre dos desplazamientos de byte.

func (*Rope) Insert

func (r *Rope) Insert(offset int, s string)

Insert inserta texto en un desplazamiento de byte.

func (*Rope) Len

func (r *Rope) Len() int

Len devuelve el tamaño del texto en bytes.

func (*Rope) Line

func (r *Rope) Line(line int) string

Line devuelve el contenido de una línea, sin su salto final.

func (*Rope) LineCount

func (r *Rope) LineCount() int

LineCount devuelve el número de líneas.

Un texto vacío tiene una línea, y uno terminado en salto de línea no tiene una línea vacía detrás. Es la convención de todos los editores: un archivo bien formado termina en salto y nadie espera ver una línea de más.

func (*Rope) LineEnd

func (r *Rope) LineEnd(line int) int

LineEnd devuelve el desplazamiento donde termina una línea, sin contar su salto.

func (*Rope) LineOf

func (r *Rope) LineOf(offset int) (line, start int)

LineOf devuelve la línea que contiene un desplazamiento y el desplazamiento donde empieza esa línea.

Devuelve las dos cosas porque quien pregunta por una casi siempre necesita la otra: la columna es la diferencia entre el desplazamiento y el principio de su línea. Son dos descensos del árbol —contar saltos hasta el desplazamiento, y localizar el último— en lugar de uno que lleve la cuenta de ambas cosas a la vez; ese sería más rápido y bastante más fácil de escribir mal, y dos descensos O(log n) siguen siendo O(log n).

func (*Rope) LineStart

func (r *Rope) LineStart(line int) int

LineStart devuelve el desplazamiento de byte donde empieza una línea.

func (*Rope) Offset

func (r *Rope) Offset(line, col int) int

Offset traduce (línea, columna en bytes) a un desplazamiento absoluto, ajustando a los límites reales del texto.

func (*Rope) Slice

func (r *Rope) Slice(start, end int) string

Slice devuelve el texto entre dos desplazamientos de byte.

func (*Rope) String

func (r *Rope) String() string

String materializa el texto entero. Es O(n) y reserva una copia completa: se usa al guardar, no al dibujar.

type Selection

type Selection struct {
	Anchor, Head int

	// Goal recuerda la columna a la que aspira el cursor al subir y bajar.
	//
	// Sin ella, bajar desde el final de una línea larga a una corta y volver a
	// subir dejaría el cursor a media línea: la columna se habría perdido al
	// pasar por la corta. Se mide en bytes desde el principio de la línea, como
	// todo lo demás.
	Goal int
}

Selection es un rango de texto con dirección: Anchor es donde empezó y Head donde está el cursor.

La cabeza puede quedar antes que el ancla si se seleccionó hacia atrás, y esa distinción importa: al extender con las flechas se mueve la cabeza, nunca el ancla. Un cursor sin selección es una selección con los dos extremos juntos, y no un caso aparte: tratarlos igual elimina la mitad de las ramas de cualquier operación de edición.

func Cursor

func Cursor(at int) Selection

Cursor construye una selección vacía en una posición.

func (Selection) Collapse

func (s Selection) Collapse() Selection

Collapse convierte la selección en un cursor en su cabeza.

func (Selection) Empty

func (s Selection) Empty() bool

Empty indica que no hay nada seleccionado.

func (Selection) End

func (s Selection) End() int

End es el extremo mayor.

func (Selection) Start

func (s Selection) Start() int

Start es el extremo menor.

func (Selection) WithHead

func (s Selection) WithHead(head int, extend bool) Selection

WithHead mueve la cabeza, extendiendo o colapsando según se pida.

type Selections

type Selections struct {
	// contains filtered or unexported fields
}

Selections es el conjunto ordenado de cursores del editor.

Se mantiene siempre ordenado y sin solapamientos. Esas dos propiedades no son cosmética: las ediciones de una transacción tienen que ir ordenadas y sin pisarse, y si el conjunto ya lo está, generar la transacción de multi-cursor es recorrerlo.

func NewSelections

func NewSelections() *Selections

NewSelections crea un conjunto con un único cursor al principio del texto.

func (*Selections) Add

func (s *Selections) Add(sel Selection)

Add añade un cursor y lo convierte en el principal.

func (*Selections) All

func (s *Selections) All() []Selection

All devuelve las selecciones en orden. No debe modificarse.

func (*Selections) Clone

func (s *Selections) Clone() []Selection

Clone copia el conjunto, para guardarlo en el historial.

func (*Selections) Len

func (s *Selections) Len() int

Len es el número de cursores.

func (*Selections) Primary

func (s *Selections) Primary() Selection

Primary devuelve el cursor principal.

func (*Selections) Replace

func (s *Selections) Replace(list []Selection)

Replace sustituye el conjunto entero.

func (*Selections) Set

func (s *Selections) Set(sel Selection)

Set deja un único cursor.

func (*Selections) Transform

func (s *Selections) Transform(edits []Edit)

Transform traslada todas las selecciones a través de unas ediciones.

func (*Selections) Update

func (s *Selections) Update(f func(Selection) Selection)

Update transforma cada selección con una función y vuelve a ordenar y fusionar.

Es la única forma de mover los cursores, y por eso la fusión ocurre sola: dos cursores que se encuentran al desplazarse tienen que convertirse en uno, o a partir de ahí todo lo que se escriba saldría duplicado.

type Stat

type Stat struct {
	ModTime int64 `json:"mtime"`
	Size    int64 `json:"tamano"`
}

Stat es la huella del archivo en el disco: cuándo se modificó y cuánto ocupa.

Es el equivalente del etag de VS Code, que guarda lo mismo —mtime, tamaño— y lo usa para lo mismo: saber si el archivo que hay ahora en el disco sigue siendo el que se leyó. Con dos campos basta; comparar el contenido entero obligaría a leer el archivo antes de cada guardado.

func (Stat) Known

func (s Stat) Known() bool

Known indica si la huella se llegó a tomar. Un documento que nunca ha tocado el disco no tiene con qué comparar.

type Text

type Text struct {
	// contains filtered or unexported fields
}

Text es un documento editable: el texto, su historial y lo que hace falta para volver a escribirlo en el disco tal y como estaba.

El texto vive siempre con saltos de línea Unix

Un archivo con finales de línea de Windows se convierte al cargarlo y se vuelve a convertir al guardarlo. La alternativa —arrastrar el retorno de carro por dentro— obliga a que cada operación sobre columnas, cada búsqueda y cada cálculo de longitud recuerde que a veces hay un byte invisible al final de la línea, y basta que uno se olvide para tener un error que solo aparece en las máquinas de otros.

func Load

func Load(path string) (*Text, error)

Load lee un archivo y lo deja listo para editar.

func NewText

func NewText(content, path string) *Text

NewText crea un documento a partir de un contenido ya normalizado.

func (*Text) Apply

func (t *Text) Apply(edits []Edit, sel *Selections) error

Apply aplica una transacción, la registra en el historial y traslada los cursores.

Es el único camino por el que cambia el texto. Que sea único es lo que garantiza que no pueda haber una edición sin su entrada de deshacer, ni un cursor apuntando a un sitio que ya no existe.

func (*Text) Bytes

func (t *Text) Bytes() []byte

Bytes devuelve el documento tal y como se escribiría en el disco.

func (*Text) Dirty

func (t *Text) Dirty() bool

Dirty indica si hay cambios sin guardar.

func (*Text) EOL

func (t *Text) EOL() EOL

EOL devuelve el final de línea con el que se escribirá el archivo.

func (*Text) Encoding

func (t *Text) Encoding() Encoding

Encoding devuelve la codificación detectada al cargar.

func (*Text) History

func (t *Text) History() *History

History da acceso al historial, para consultar si hay algo que deshacer.

func (*Text) Len

func (t *Text) Len() int

Len devuelve el tamaño en bytes.

func (*Text) Line

func (t *Text) Line(i int) string

Line devuelve una línea sin su salto final.

func (*Text) LineCount

func (t *Text) LineCount() int

LineCount devuelve el número de líneas.

func (*Text) LineEnd

func (t *Text) LineEnd(line int) int

LineEnd devuelve el final de una línea, sin su salto.

func (*Text) LineOf

func (t *Text) LineOf(offset int) (line, start int)

LineOf devuelve la línea que contiene un desplazamiento y dónde empieza esa línea.

func (*Text) LineStart

func (t *Text) LineStart(line int) int

LineStart y LineEnd delegan en el rope; existen para que quien tiene un Text no necesite pedirle además su estructura interna.

func (*Text) Offset

func (t *Text) Offset(line, col int) int

Offset traduce (línea, columna) a un desplazamiento absoluto.

func (*Text) Path

func (t *Text) Path() string

Path devuelve la ruta del archivo, o vacío si nunca se ha guardado.

func (*Text) Position

func (t *Text) Position(offset int) (line, col int)

Position traduce un desplazamiento a (línea, columna en bytes).

func (*Text) Redo

func (t *Text) Redo(sel *Selections) bool

Redo rehace el siguiente grupo.

func (*Text) Reload

func (t *Text) Reload(sel *Selections) error

Reload vuelve a leer el archivo del disco, descartando lo que hubiera en memoria.

Deshacer sigue funcionando después: la recarga entra como una transacción más, así que recargar sin querer sobre un documento con cambios se arregla con Ctrl+Z en vez de perder el trabajo. Es lo que hace VS Code, que aplica el contenido nuevo al modelo de texto en lugar de reemplazarlo.

func (*Text) Revision

func (t *Text) Revision() int

Revision es el número de transacciones aplicadas.

func (*Text) Rope

func (t *Text) Rope() *Rope

Rope da acceso de solo lectura a la estructura de texto.

func (*Text) Save

func (t *Text) Save() error

Save escribe el documento en su ruta, salvo que el archivo haya cambiado en el disco desde que se leyó.

func (*Text) SaveOverwrite

func (t *Text) SaveOverwrite() error

SaveOverwrite escribe el documento pase lo que pase, descartando lo que haya en el disco. Es la salida del conflicto cuando quien edita decide que su versión es la buena.

func (*Text) SetPath

func (t *Text) SetPath(path string)

SetPath cambia la ruta, para guardar como.

func (*Text) SetStat

func (t *Text) SetStat(s Stat)

SetStat fija la huella conocida del archivo.

Lo usa la restauración de una copia de seguridad: el documento vuelve con el contenido que no llegó a guardarse, pero la huella que se arrastra es la que tenía el archivo cuando se hizo la copia, no la de ahora. Así, si el archivo cambió en el disco mientras el editor estaba cerrado, el conflicto salta al guardar en vez de sobrescribir en silencio. Es exactamente lo que hace VS Code al reconstruir un modelo desde su backup.

func (*Text) Slice

func (t *Text) Slice(start, end int) string

Slice devuelve el texto entre dos desplazamientos.

func (*Text) Stat

func (t *Text) Stat() Stat

Stat devuelve la huella del archivo tal y como estaba la última vez que se leyó o se escribió.

func (*Text) String

func (t *Text) String() string

String materializa el documento entero con saltos Unix.

func (*Text) Undo

func (t *Text) Undo(sel *Selections) bool

Undo deshace el último grupo de ediciones y devuelve si había algo que deshacer.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL