workspace

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: 13 Imported by: 0

Documentation

Overview

Package workspace es el proyecto: qué archivos hay, cómo encontrarlos y cómo buscar dentro de ellos.

No depende de nada gráfico. Recorrer un directorio, ordenar candidatos por parecido y buscar un texto en diez mil archivos son operaciones que se prueban con una carpeta temporal y una tabla de casos, sin abrir una ventana.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func FuzzyMatch

func FuzzyMatch(query, candidate string) (score int, ok bool)

FuzzyMatch puntúa cuánto se parece un candidato a la consulta.

El algoritmo es el de todos los buscadores difusos: recorrer el candidato buscando las letras de la consulta en orden y puntuar según dónde caen. No es una distancia de edición —eso mediría el parecido entre dos palabras, que no es lo que se busca— sino cuánto se parece el candidato a lo que alguien escribiría de memoria para llegar a él.

Por qué se prueba dos veces

Un recorrido voraz de izquierda a derecha se queda con la primera letra que encaja, y esa suele ser la peor: buscando "main" en `cmd/flowcode/main.go`, la eme que encuentra primero es la de `cmd`, y a partir de ahí el resto del emparejado queda repartido por la ruta con huecos. El resultado era que `docs/mantenimiento.md` ganaba a `cmd/flowcode/main.go`, que es exactamente lo contrario de lo que hace falta.

La solución completa es una tabla de programación dinámica que pruebe todos los emparejados posibles. La solución que basta es probar dos: el candidato entero y solo su nombre de archivo, y quedarse con el mejor. Cubre el caso que falla —la consulta se refiere al nombre— con dos recorridos lineales en lugar de una tabla cuadrática por cada uno de los cien mil archivos del índice.

La consulta debe venir en minúsculas; el candidato se compara sin distinguir caja, pero se mira la caja original para detectar el principio de una palabra en nombres tipo camello.

Types

type Candidate

type Candidate struct {
	Path  string
	Score int
}

Candidate es un archivo puntuado.

type Entry

type Entry struct {
	Name   string
	Path   string
	IsDir  bool
	Hidden bool
}

Entry es una entrada del árbol de archivos.

type Grep

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

Grep es una búsqueda en marcha.

Los resultados se publican de forma incremental con un puntero atómico: el hilo de dibujo toma la instantánea que haya en ese momento y no espera nunca. Es el mismo patrón que el índice de líneas y que el índice de archivos, y por la misma razón.

func (*Grep) Cancel

func (g *Grep) Cancel()

Cancel detiene la búsqueda.

Se cancela en cuanto el usuario teclea otra letra, y por eso importa que los trabajadores lo comprueben a menudo: una búsqueda abandonada que sigue leyendo el disco compite con la que de verdad interesa.

func (*Grep) Duration

func (g *Grep) Duration() time.Duration

Duration es lo que tardó, o lo que lleva.

func (*Grep) Hits

func (g *Grep) Hits() []Hit

Hits devuelve las coincidencias encontradas hasta ahora.

func (*Grep) Limited

func (g *Grep) Limited() bool

Limited indica que se alcanzó el tope de resultados.

func (*Grep) Running

func (g *Grep) Running() bool

Running indica si la búsqueda sigue en marcha.

func (*Grep) Scanned

func (g *Grep) Scanned() int

Scanned es cuántos archivos se han mirado.

type Hit

type Hit struct {
	// Path es relativa a la raíz del proyecto.
	Path string

	// Line es el índice de línea, contando desde cero.
	Line int

	// Col es la columna en bytes donde empieza la coincidencia.
	Col int

	// Text es la línea entera, recortada si era desmesurada.
	Text string
}

Hit es una coincidencia dentro de un archivo.

type Ignore

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

Ignore decide qué archivos no forman parte del proyecto.

Qué se implementa de .gitignore y qué no

El formato de Git tiene esquinas que casi nadie usa y que cuestan mucho más de lo que aportan. Aquí están las reglas que aparecen en cualquier repositorio real: comentarios, negación con `!`, patrones anclados con `/`, patrones de solo directorio con `/` final, comodines `*`, `?` y `**`, y patrones sin barra que se comparan contra el nombre a cualquier profundidad.

Lo que no está: los `.gitignore` anidados con precedencia por directorio —se leen todos pero se aplican como si estuvieran en la raíz—, y las clases de caracteres `[a-z]`. Se añadirán cuando aparezca un proyecto al que le importe; mientras tanto, esconder algo de más en el árbol es un incordio y esconder de menos también, pero ninguno de los dos pierde trabajo.

func LoadIgnore

func LoadIgnore(dir string) *Ignore

LoadIgnore lee el .gitignore de un directorio. Que no exista no es un error: un proyecto sin .gitignore es un proyecto en el que se ve todo.

func NewIgnore

func NewIgnore() *Ignore

NewIgnore construye un conjunto de reglas vacío.

func (*Ignore) Add

func (ig *Ignore) Add(line string)

Add añade una línea de .gitignore.

func (*Ignore) Match

func (ig *Ignore) Match(rel string, isDir bool) bool

Match indica si una ruta relativa al proyecto debe ignorarse.

Gana la última regla que encaje, que es la regla de Git y la que hace útil la negación: `*.log` seguido de `!importante.log` esconde los registros menos uno.

type Project

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

Project es la carpeta abierta y lo que se sabe de ella.

El índice se construye en segundo plano y se publica de una vez con un puntero atómico: quien lo consulta trabaja sobre una instantánea inmutable, no necesita cerrojos y nunca ve una lista a medio construir. Es el mismo patrón que usa el índice de líneas de un archivo grande, y por la misma razón: el hilo de dibujo no puede esperar a nadie.

func Open

func Open(root string, onScanned func()) *Project

Open abre una carpeta y arranca su indexado en segundo plano.

func (*Project) Abs

func (p *Project) Abs(rel string) string

Abs convierte una ruta relativa del índice en absoluta.

func (*Project) Entries

func (p *Project) Entries(dir string) []Entry

Entries lee un nivel del árbol de archivos, ya filtrado y ordenado.

Vive aquí y no en la capa de composición para que el árbol esconda exactamente lo mismo que esconde el buscador: dos filtros distintos para la misma carpeta acabarían discrepando, y el usuario vería un archivo en un sitio y no en el otro.

func (*Project) Files

func (p *Project) Files() []string

Files devuelve la lista de rutas relativas indexadas. No debe modificarse.

func (*Project) Grep

func (p *Project) Grep(q Query, limit int, onProgress func()) *Grep

Grep busca un texto en todos los archivos indexados.

El reparto deja un núcleo libre, igual que el indexado de líneas y por el mismo motivo medido allí: saturar la máquina mientras el usuario mira la pantalla hace perder fotogramas, y una búsqueda que tarda un 10% más pero no da tirones se percibe como más rápida.

func (*Project) Ignore

func (p *Project) Ignore() *Ignore

Ignore da acceso a las reglas, para que el árbol de archivos esconda lo mismo que el buscador.

func (*Project) Root

func (p *Project) Root() string

Root devuelve la carpeta del proyecto.

func (*Project) ScanDuration

func (p *Project) ScanDuration() int64

ScanDuration devuelve lo que tardó el recorrido.

func (*Project) Scanning

func (p *Project) Scanning() bool

Scanning indica si el recorrido sigue en marcha.

func (*Project) Search

func (p *Project) Search(query string, limit int) []Candidate

Search ordena los archivos del proyecto por parecido con la consulta.

Devuelve como mucho limit resultados.

Esto corre entero en cada pulsación de tecla y en el hilo de dibujo, así que entra directamente en el presupuesto de 8,3 ms del fotograma. Medido sobre un índice de cien mil rutas, la versión de un solo hilo que puntuaba y ordenaba todos los candidatos costaba 31 ms con una consulta de una letra: casi cuatro fotogramas perdidos por tecla, justo en el momento en que alguien está escribiendo y más se nota.

Se atacó por los dos lados. Puntuar se reparte entre los núcleos —dejando uno libre, por lo medido en el indexado de líneas— y cada trabajador se queda solo con sus mejores limit candidatos, de modo que lo que se ordena al final son unos cientos y no cien mil. Ordenar era la mitad del coste, y era trabajo tirado: nadie mira el resultado número mil.

func (*Project) Truncated

func (p *Project) Truncated() bool

Truncated indica que se alcanzó el límite de archivos y el índice está incompleto. Decirlo importa: una búsqueda que no encuentra algo porque el índice se cortó parece un fallo del editor.

type Query

type Query struct {
	Text       string
	IgnoreCase bool
}

Query es lo que se busca.

Jump to

Keyboard shortcuts

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