Adopt house golangci-lint config; fix all approved-linter findings
.golangci.yml is the prompts-repo standard verbatim plus one approved
exception (paralleltest, sneak 2026-07-06). Roughly 1,500 findings
fixed:
- autofix sweep (wsl_v5/nlreturn/intrange/modernize formatting), with
the misspell autofix REVERTED where it rewrote authentic C game text
("missle vanishes" stays, with nolint and a comment)
- error handling: errcheck sites get real handling — saveFile now
closes explicitly and removes corrupt saves, scoreboard writes are
documented best-effort, err113 sentinel errors (ErrSaveOutOfDate,
ErrScreenTooSmall); panics allowed for unrecoverable states per
MEMORY.md policy
- gosec: real fixes (ParseInt for SEED, 0600 scorefile) and justified
per-line nolints for provably-bounded conversions (randomMonsterLetter
helper collapses eight rnd(26)+'A' sites)
- API tidying from linters: pointer receivers on flag types
(recvcheck), msg helpers renamed addmsgf/doaddf/Printwf/MvPrintwf
(goprintffuncname), myExit()/findFloor(monst)/mkGameInput(t) drop
always-constant params (unparam), gameEnd carries no status
- gocritic/staticcheck: if-else chains to switches, the pack.c
inventory filter untangled into matchesFilter, main.go split so
defers run before exit (exitAfterDefer, funlen)
- revive doc comments on all exported flag types/consts/methods
Remaining findings are confined to linters pending sneak's exception
decision (mnd, gochecknoglobals, cyclop, nestif, gocognit, exhaustive,
goconst, testpackage); TODO.md records the state. MEMORY.md added at
repo root as the project's agent working notes.
This commit is contained in:
@@ -5,6 +5,8 @@
|
||||
package term
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -21,6 +23,9 @@ type Tcell struct {
|
||||
last *game.Window // last rendered window, for resize redraws
|
||||
}
|
||||
|
||||
// ErrScreenTooSmall reports a terminal below the required 80x24.
|
||||
var ErrScreenTooSmall = errors.New("screen too small")
|
||||
|
||||
// New initializes the terminal. The screen must be at least 80x24, as the
|
||||
// C game required.
|
||||
func New() (*Tcell, error) {
|
||||
@@ -28,16 +33,22 @@ func New() (*Tcell, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.Init(); err != nil {
|
||||
return nil, err
|
||||
|
||||
initErr := s.Init()
|
||||
if initErr != nil {
|
||||
return nil, initErr
|
||||
}
|
||||
|
||||
w, h := s.Size()
|
||||
if h < game.NumLines || w < game.NumCols {
|
||||
s.Fini()
|
||||
return nil, fmt.Errorf("sorry, the screen must be at least %dx%d",
|
||||
game.NumLines, game.NumCols)
|
||||
|
||||
return nil, fmt.Errorf("sorry, %w: %dx%d required",
|
||||
ErrScreenTooSmall, game.NumCols, game.NumLines)
|
||||
}
|
||||
|
||||
s.HideCursor()
|
||||
|
||||
return &Tcell{screen: s}, nil
|
||||
}
|
||||
|
||||
@@ -49,17 +60,21 @@ func (t *Tcell) Fini() {
|
||||
// Render blits a game window to the terminal (curses refresh).
|
||||
func (t *Tcell) Render(w *game.Window) {
|
||||
t.last = w
|
||||
|
||||
rows, cols := w.Size()
|
||||
for y := 0; y < rows; y++ {
|
||||
for x := 0; x < cols; x++ {
|
||||
for y := range rows {
|
||||
for x := range cols {
|
||||
ch, standout := w.CellAt(y, x)
|
||||
|
||||
style := tcell.StyleDefault
|
||||
if standout {
|
||||
style = style.Reverse(true)
|
||||
}
|
||||
|
||||
t.screen.SetContent(x, y, rune(ch), nil, style)
|
||||
}
|
||||
}
|
||||
|
||||
t.screen.Show()
|
||||
}
|
||||
|
||||
@@ -105,8 +120,9 @@ func (t *Tcell) ReadChar() byte {
|
||||
return 3
|
||||
default:
|
||||
if ev.Key() >= tcell.KeyCtrlA && ev.Key() <= tcell.KeyCtrlZ {
|
||||
return byte(ev.Key())
|
||||
return byte(ev.Key()) //nolint:gosec // G115: 1..26 fits
|
||||
}
|
||||
|
||||
if r := ev.Rune(); r > 0 && r < 0x80 {
|
||||
return byte(r)
|
||||
}
|
||||
@@ -118,18 +134,29 @@ func (t *Tcell) ReadChar() byte {
|
||||
// ShellEscape suspends the screen and runs the user's shell (main.c
|
||||
// shell + md_shellescape).
|
||||
func (t *Tcell) ShellEscape() {
|
||||
if err := t.screen.Suspend(); err != nil {
|
||||
err := t.screen.Suspend()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
shell := os.Getenv("SHELL")
|
||||
if shell == "" {
|
||||
shell = "/bin/sh"
|
||||
}
|
||||
fmt.Println("[Entering shell; exit to return to the game]")
|
||||
cmd := exec.Command(shell)
|
||||
|
||||
_, _ = fmt.Fprintln(os.Stdout,
|
||||
"[Entering shell; exit to return to the game]")
|
||||
|
||||
// The shell session has no deadline by design; Background context.
|
||||
cmd := exec.CommandContext(context.Background(), //nolint:gosec // G204: the user's own $SHELL
|
||||
shell)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Run()
|
||||
t.screen.Resume()
|
||||
_ = cmd.Run() // best effort: the shell is the user's business
|
||||
|
||||
resumeErr := t.screen.Resume()
|
||||
if resumeErr != nil {
|
||||
panic(resumeErr) // terminal resume failure is unrecoverable
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user