dired

package
v0.12.0 Latest Latest
Warning

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

Go to latest
Published: Sep 27, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package dired formats a directory listing for nem's dired mode: reading entries, ordering them, laying out the buffer text and colouring it, plus the file operations dired performs.

It is pure in the sense that matters to the editor: it holds no editor state. Reading and formatting are separate steps so the editor can re-format the same entries when the user toggles hidden files, details or the sort order, without touching the disk again, and so the layout can be tested against fixed Entry values and a fixed clock rather than a real directory.

Index

Constants

View Source
const FirstEntry = 2

FirstEntry is the buffer line of the first entry: line 0 is the header, line 1 is blank.

View Source
const MarkColumn = 1

MarkColumn is the rune column of the mark on an entry line.

View Source
const ParentName = ".."

ParentName names the entry for the directory above, which Read lists so a listing can be left the same way it was entered: by RET on a directory.

Variables

This section is empty.

Functions

func Copy

func Copy(src, dst string) error

Copy copies src to dst without following a symlink at src.

A file keeps its contents, permission bits and, where the filesystem allows, its modification time. A directory is copied recursively. A symlink is recreated with the same link text. Copy refuses when dst exists (an error satisfying errors.Is(err, fs.ErrExist)), and when dst is src or lies inside it, since copying a directory into itself would never finish.

A file is written to a temporary name beside dst and renamed into place, so a failure part-way never leaves a truncated dst that looks complete. A directory copy that fails part-way may leave a partial tree.

func FormatEntry

func FormatEntry(e Entry, mark rune, opts Options, now time.Time) (string, []syntax.Span)

FormatEntry lays out one entry line exactly as Format would put it in a listing, so the editor can redraw a single line when its mark changes without re-formatting the whole directory.

func Move

func Move(src, dst string) error

Move renames src to dst, falling back to copy-then-remove when they are on different filesystems, which rename cannot cross. It refuses exactly what Copy refuses.

func NameColumn

func NameColumn(opts Options) int

NameColumn is the rune column at which an entry's name starts on its line. It depends only on opts: everything before the name is fixed-width.

func NameEnd added in v0.5.0

func NameEnd(e Entry, opts Options) int

NameEnd is the rune column just past an entry's name on its line. What follows, if anything, is a symlink's target.

func Remove

func Remove(path string) error

Remove deletes path: a directory with everything in it, anything else on its own. A symlink to a directory removes the link and never touches the target's contents. Unlike os.RemoveAll, a path that does not exist is an error wrapping fs.ErrNotExist: the user asked to delete something they saw, and it silently not being there is worth telling them.

func RenameAll added in v0.5.0

func RenameAll(dir string, rs []Rename) error

RenameAll renames files as if all at the same instant, which is what editing a listing's names as text asks for: a name one file gives up can be taken by another, so two files can swap names, or a numbered run shift along by one.

Nothing is overwritten, and nothing moves until every rename has been checked: no two may take one name, a name may be taken only if one of the renames frees it, and a file may go only into a directory that exists and is not itself being renamed. Then each file goes to a temporary name beside it, and from there to its new one. If a step fails, everything already moved is moved back, so either all of it happens or none of it does - short of the moving back failing too, which the error then says.

Errors name the files as the renames give them, since they are meant for the user who typed those names.

func ShownName added in v0.5.0

func ShownName(e Entry) string

ShownName is an entry's name as its line shows it: made safe for one line, and a directory's with a "/" after it - a marker as in ls -F, not a path separator, so "/" on every OS.

Types

type Entry

type Entry struct {
	// Name is the base name as it is on disk, unsanitised: it is what file
	// operations are given, so it must round-trip exactly.
	Name string
	// Mode comes from Lstat, so a symlink carries fs.ModeSymlink rather than
	// its target's type.
	Mode fs.FileMode
	// Size is the Lstat size; for a symlink that is the length of its text.
	Size    int64
	ModTime time.Time
	// Target is a symlink's link text (os.Readlink), and "" for anything else.
	Target string
	// Broken reports a symlink whose target does not exist.
	Broken bool
	// IsDir is true for a directory, and for a symlink that resolves to one:
	// both are things the user can descend into.
	IsDir bool
	// contains filtered or unexported fields
}

Entry is one directory entry.

func Read

func Read(dir string) ([]Entry, error)

Read lists every entry of dir (not recursive), hidden ones included; Format does the filtering and ordering, so toggling either needs no second read. Unless dir is a filesystem root, the list also holds a ParentName entry for the directory above.

An entry whose Lstat fails - it vanished between readdir and lstat, or the directory is readable but not searchable - is still listed with its name and zero Mode, Size and ModTime. Dropping it would hide from the user a file they can see with ls. The error is only for failing to read the directory itself; entries read before such a failure are returned alongside it.

func (Entry) Executable

func (e Entry) Executable() bool

Executable reports whether the entry is a regular file, or a symlink to one, with any execute bit set. Directories are excluded: their x bit means "searchable", and colouring every directory as a program would be noise.

func (Entry) Hidden

func (e Entry) Hidden() bool

Hidden reports whether the name starts with a dot. The parent entry is not hidden: it is the way out, not a dotfile.

func (Entry) IsParent

func (e Entry) IsParent() bool

IsParent reports whether e is the entry for the directory above.

type Listing

type Listing struct {
	// Dir is the directory, absolute and cleaned.
	Dir string
	// Lines are the header, a blank line, then one line per shown entry - or a
	// single placeholder line when nothing is shown.
	Lines []string
	// Spans colour Lines; len(Spans) == len(Lines).
	Spans [][]syntax.Span
	// Entries are the shown entries in display order; Entries[i] is on line
	// FirstEntry+i.
	Entries []Entry
	// Hidden counts the hidden entries this listing omits (0 when ShowHidden).
	Hidden int
}

Listing is a formatted directory: the buffer text line by line and, line for line, how to colour it.

func Format

func Format(dir string, entries []Entry, marks map[string]rune, opts Options, now time.Time, home string) *Listing

Format lays out entries (as Read returns them, in any order) for display.

marks maps an entry's Name to its mark: ' ' or absent is unmarked, '*' is marked, 'D' is flagged for deletion, and any other rune is shown as it is and coloured like '*'. now decides between showing a time and a year, and is a parameter so a test's output does not depend on when it runs. home, if not "", is abbreviated to "~" in the header. The caller passes an absolute dir.

func (*Listing) EntryAt

func (l *Listing) EntryAt(line int) (Entry, bool)

EntryAt returns the entry on buffer line line, and false for the header, the blank line, the placeholder, or a line out of range.

func (*Listing) LineOf

func (l *Listing) LineOf(name string) (int, bool)

LineOf returns the buffer line of the shown entry called name.

It scans rather than consulting an index built by Format: the editor may edit Entries in place after an operation, and a scan cannot go stale.

type Options

type Options struct {
	ShowHidden  bool
	HideDetails bool
	Sort        SortKey
	// Icons puts a Nerd Font glyph before each name. Off in the zero value
	// because it needs a font this package cannot know the terminal has.
	Icons bool
}

Options controls what a listing shows. The zero value is the default: details shown, hidden entries omitted, sorted by name, no icons.

type Rename added in v0.5.0

type Rename struct{ From, To string }

Rename is one file's new name, for RenameAll. Both are relative to the directory RenameAll is given, or absolute.

type SortKey

type SortKey int

SortKey orders a listing. Directories always come before everything else, whatever the key, because the first thing a user scans a listing for is where they can go next.

const (
	// ByName is case-insensitive and ignores one leading dot, so ".bashrc"
	// sorts among the b's rather than in a clump at the top. Ties are broken
	// by the raw name, which makes the order total.
	ByName SortKey = iota
	// ByTime puts the newest first; ties fall back to name order.
	ByTime
	// BySize puts the largest first; directories, grouped first anyway, are
	// ordered by name since their size says nothing about their contents.
	// Ties fall back to name order.
	BySize
)

func (SortKey) Next

func (k SortKey) Next() SortKey

Next cycles ByName -> ByTime -> BySize -> ByName, for a single key that steps through the orders.

func (SortKey) String

func (k SortKey) String() string

String names the key as the editor shows it in the mode line.

Jump to

Keyboard shortcuts

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