summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-25 23:54:30 +0200
committerPaul Buetow <paul@buetow.org>2026-03-25 23:54:30 +0200
commitdd7fb519b25b75a8a33868c835ddf0b991ef8c24 (patch)
treedc5c32e2f0152d099674e770f7b607b932f21fd8
parent6af1b8afdc6cdd1e1e19ea5f23f7892e902dc7a8 (diff)
cleanup
-rw-r--r--.gitignore2
-rw-r--r--FIXES.md378
-rw-r--r--LOGO.txt12
-rw-r--r--coverage.json1
-rw-r--r--repl_coverage.out173
5 files changed, 1 insertions, 565 deletions
diff --git a/.gitignore b/.gitignore
index 8b13789..7c24463 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1 @@
-
+/gt
diff --git a/FIXES.md b/FIXES.md
deleted file mode 100644
index d6cc694..0000000
--- a/FIXES.md
+++ /dev/null
@@ -1,378 +0,0 @@
-# Proposed Fixes for 100 Go Mistakes Audit
-
-This document outlines specific code changes to address the HIGH and MEDIUM severity issues identified in the audit.
-
----
-
-## Fix #1: Proper Error Wrapping in RPN Package
-
-**File:** `internal/rpn/rpn.go`
-**Issue:** Errors not wrapped with context
-**Location:** Multiple functions including `ParseAndEvaluate`, `evaluate`
-
-### Current Code:
-```go
-func (r *RPN) ParseAndEvaluate(input string) (string, error) {
- // ...
- if len(tokens) == 0 {
- return "", fmt.Errorf("no valid tokens found")
- }
- return r.evaluate(tokens)
-}
-
-func (r *RPN) evaluate(tokens []string) (string, error) {
- // ...
- if result, err := r.handleOperator(stack, token, i); err != nil {
- return "", err // Missing context
- }
- // ...
-}
-```
-
-### Fixed Code:
-```go
-func (r *RPN) ParseAndEvaluate(input string) (string, error) {
- // ...
- if len(tokens) == 0 {
- return "", fmt.Errorf("rpn: no valid tokens found in input: %q", input)
- }
- return r.evaluate(tokens)
-}
-
-func (r *RPN) evaluate(tokens []string) (string, error) {
- // ...
- if result, err := r.handleOperator(stack, token, i); err != nil {
- return "", fmt.Errorf("rpn: failed to evaluate token '%s' at position %d: %w", token, i, err)
- }
- // ...
-}
-```
-
----
-
-## Fix #2: Proper Error Comparison in Tests
-
-**File:** `internal/rpn/variables_test.go`
-**Issue:** Direct error comparison instead of `errors.Is()`
-**Location:** `TestOperationsUseVariableUndefined`
-
-### Current Code:
-```go
-func TestOperationsUseVariableUndefined(t *testing.T) {
- // ...
- err := o.UseVariable(s, "undefined")
- if err == nil {
- t.Error("UseVariable for undefined variable should return error")
- }
- if !errors.Is(err, ErrVariableNotFound) { // This is actually correct, but some places use direct comparison
- t.Errorf("UseVariable error = %v, should be ErrVariableNotFound", err)
- }
-}
-```
-
-### Note:
-The code already uses `errors.Is()`, which is correct. No change needed here.
-
----
-
-## Fix #3: Proper Resource Cleanup in REPL
-
-**File:** `internal/repl/repl.go`
-**Issue:** Defer error ignoring in `saveHistory`
-**Location:** Lines ~160-180
-
-### Current Code:
-```go
-func saveHistory(history []string) error {
- historyPath := getHistoryPath()
- if historyPath == "" {
- return nil
- }
-
- file, err := os.Create(historyPath)
- if err != nil {
- return err
- }
-
- writer := bufio.NewWriter(file)
- for _, entry := range history {
- if _, err := writer.WriteString(entry + "\n"); err != nil {
- _ = file.Close() // Error ignored
- return err
- }
- }
- if err := writer.Flush(); err != nil {
- _ = file.Close() // Error ignored
- return err
- }
- return file.Close()
-}
-```
-
-### Fixed Code:
-```go
-func saveHistory(history []string) error {
- historyPath := getHistoryPath()
- if historyPath == "" {
- return nil
- }
-
- file, err := os.Create(historyPath)
- if err != nil {
- return err
- }
- defer func() {
- if err := file.Close(); err != nil {
- // Log the error but don't overwrite original error
- log.Printf("Warning: failed to close history file: %v", err)
- }
- }()
-
- writer := bufio.NewWriter(file)
- for _, entry := range history {
- if _, err := writer.WriteString(entry + "\n"); err != nil {
- return fmt.Errorf("failed to write history entry: %w", err)
- }
- }
- if err := writer.Flush(); err != nil {
- return fmt.Errorf("failed to flush history writer: %w", err)
- }
- return nil
-}
-```
-
----
-
-## Fix #4: Mutex Safety in REPL
-
-**File:** `internal/repl/repl.go`
-**Issue:** Mutex potential for copying
-**Location:** Lines ~35-39
-
-### Current Code:
-```go
-// RPNState holds the state for RPN operations in REPL
-type RPNState struct {
- vars rpn.VariableStore
- rpnCalc *rpn.RPN
-}
-
-// getRPNState returns or creates the RPN state
-var rpnStateMu sync.RWMutex
-var rpnState *RPNState
-
-func getRPNState() *RPNState {
- rpnStateMu.Lock()
- defer rpnStateMu.Unlock()
- if rpnState == nil {
- vars := rpn.NewVariables()
- rpnState = &RPNState{
- vars: vars,
- rpnCalc: rpn.NewRPN(vars),
- }
- }
- return rpnState
-}
-```
-
-### Fixed Code:
-```go
-// RPNState holds the state for RPN operations in REPL
-// Note: This struct should never be copied
-type RPNState struct {
- vars rpn.VariableStore
- rpnCalc *rpn.RPN
-}
-
-// rpnStateMu protects rpnState
-// Note: The mutex must NOT be copied - keep it as a top-level variable
-var rpnStateMu sync.RWMutex
-var rpnState *RPNState
-
-// getRPNState returns or creates the RPN state
-func getRPNState() *RPNState {
- // First check with read lock for performance
- rpnStateMu.RLock()
- if rpnState != nil {
- state := rpnState
- rpnStateMu.RUnlock()
- return state
- }
- rpnStateMu.RUnlock()
-
- // Need to create - use write lock
- rpnStateMu.Lock()
- defer rpnStateMu.Unlock()
- if rpnState == nil {
- vars := rpn.NewVariables()
- rpnState = &RPNState{
- vars: vars,
- rpnCalc: rpn.NewRPN(vars),
- }
- }
- return rpnState
-}
-```
-
----
-
-## Fix #5: Improved Error Context in Percentage Calculator
-
-**File:** `internal/perc/perc.go`
-**Issue:** Errors not wrapped with context
-**Location:** Various places
-
-### Current Code:
-```go
-func Parse(input string) (string, error) {
- // ...
- result, err := perc.Parse(input)
- if err != nil {
- return "", err // Missing context
- }
- return result, nil
-}
-```
-
-### Fixed Code:
-```go
-func Parse(input string) (string, error) {
- // ...
- result, err := perc.Parse(input)
- if err != nil {
- return "", fmt.Errorf("rpn fallback failed for input %q: %w", input, err)
- }
- return result, nil
-}
-```
-
----
-
-## Fix #6: Better Slice Handling in Variables
-
-**File:** `internal/rpn/variables.go`
-**Issue:** Slice capacity retention in `Clear()`
-**Location:** Lines ~65-67
-
-### Current Code:
-```go
-func (s *Stack) Clear() {
- s.values = s.values[:0] // Retains capacity
-}
-```
-
-### Fixed Code:
-```go
-func (s *Stack) Clear() {
- // Option 1: Reset to nil (releases memory)
- s.values = nil
-
- // Option 2: Keep capacity but reset length (faster for reuse)
- // s.values = s.values[:0]
-}
-```
-
-### Recommendation:
-Use Option 1 (nil) if memory usage is a concern, Option 2 if stack will be reused immediately.
-
----
-
-## Fix #7: Performance Optimization in Variables
-
-**File:** `internal/rpn/variables.go`
-**Issue:** Allocations in hot paths
-**Location:** `ListVariables`
-
-### Current Code:
-```go
-func (v *Variables) ListVariables() []VariableInfo {
- v.mu.RLock()
- defer v.mu.RUnlock()
-
- var infos []VariableInfo // New allocation each call
- for name, value := range v.variables {
- infos = append(infos, VariableInfo{Name: name, Value: value})
- }
- // ...
-}
-```
-
-### Fixed Code (Option 1 - Pre-allocation):
-```go
-func (v *Variables) ListVariables() []VariableInfo {
- v.mu.RLock()
- defer v.mu.RUnlock()
-
- // Pre-allocate slice with known capacity
- infos := make([]VariableInfo, 0, len(v.variables))
- for name, value := range v.variables {
- infos = append(infos, VariableInfo{Name: name, Value: value})
- }
-
- // Sort by name for consistent output
- sort.Slice(infos, func(i, j int) bool {
- return infos[i].Name < infos[j].Name
- })
-
- return infos
-}
-```
-
-### Fixed Code (Option 2 - Cached Result):
-```go
-// For frequently accessed data, consider caching
-func (v *Variables) ListVariables() []VariableInfo {
- v.mu.RLock()
- defer v.mu.RUnlock()
-
- // Create a copy to avoid holding the lock during sorting
- infos := make([]VariableInfo, 0, len(v.variables))
- for name, value := range v.variables {
- infos = append(infos, VariableInfo{Name: name, Value: value})
- }
-
- // Release lock before sorting (long operation)
- v.mu.RUnlock()
-
- // Sort outside the critical section
- sort.Slice(infos, func(i, j int) bool {
- return infos[i].Name < infos[j].Name
- })
-
- return infos
-}
-```
-
----
-
-## Testing the Fixes
-
-After applying fixes, run:
-
-```bash
-# Run all tests with race detector
-go test -race ./...
-
-# Run with coverage
-go test -coverprofile=coverage.out ./...
-go tool cover -func=coverage.out
-
-# Check for linter issues
-go vet ./...
-golangci-lint run
-```
-
----
-
-## Summary of Key Changes
-
-| Fix | File | Change | Impact |
-|-----|------|--------|--------|
-| #1 | `rpn.go` | Error wrapping | Better debugging |
-| #3 | `repl.go` | Proper resource cleanup | No resource leaks |
-| #4 | `repl.go` | Mutex safety | Thread safety |
-| #5 | `perc.go` | Error context | Better error messages |
-| #6 | `variables.go` | Slice handling | Memory management |
-| #7 | `variables.go` | Performance optimization | Reduced allocations |
-
-Each fix addresses specific Go anti-patterns identified in the audit while maintaining code correctness and improving maintainability.
diff --git a/LOGO.txt b/LOGO.txt
deleted file mode 100644
index 9474ff4..0000000
--- a/LOGO.txt
+++ /dev/null
@@ -1,12 +0,0 @@
- _____ _____
- | __ \ | __ \
- | | | | ___ __ _| | | | _____ __
- | | | |/ _ \/ _` | | | |/ _ \ \ / /
- | |__| | __/ (_| | |__| | __/\ V /
- |_____/ \___|\__, |_____/ \___| \_/
- __/ |
- |___/
-
-gt - Percentage Calculator with RPN Support
-
- stylized 'gt' with calculator motif
diff --git a/coverage.json b/coverage.json
deleted file mode 100644
index 6c66dfa..0000000
--- a/coverage.json
+++ /dev/null
@@ -1 +0,0 @@
-{"coverage": 80.6}
diff --git a/repl_coverage.out b/repl_coverage.out
deleted file mode 100644
index 060575e..0000000
--- a/repl_coverage.out
+++ /dev/null
@@ -1,173 +0,0 @@
-mode: atomic
-codeberg.org/snonux/perc/internal/repl/commands.go:13.33,15.2 1 185
-codeberg.org/snonux/perc/internal/repl/commands.go:18.26,20.2 1 1
-codeberg.org/snonux/perc/internal/repl/commands.go:23.49,25.20 2 42
-codeberg.org/snonux/perc/internal/repl/commands.go:25.20,27.3 1 1
-codeberg.org/snonux/perc/internal/repl/commands.go:29.2,29.34 1 41
-codeberg.org/snonux/perc/internal/repl/commands.go:30.14,31.32 1 6
-codeberg.org/snonux/perc/internal/repl/commands.go:32.15,33.24 1 6
-codeberg.org/snonux/perc/internal/repl/commands.go:34.22,35.23 1 4
-codeberg.org/snonux/perc/internal/repl/commands.go:36.21,38.17 1 24
-codeberg.org/snonux/perc/internal/repl/commands.go:39.13,41.17 1 0
-codeberg.org/snonux/perc/internal/repl/commands.go:42.10,43.121 1 1
-codeberg.org/snonux/perc/internal/repl/commands.go:47.39,92.23 2 6
-codeberg.org/snonux/perc/internal/repl/commands.go:92.23,94.3 1 3
-codeberg.org/snonux/perc/internal/repl/commands.go:96.2,97.16 2 3
-codeberg.org/snonux/perc/internal/repl/commands.go:98.14,99.64 1 1
-codeberg.org/snonux/perc/internal/repl/commands.go:100.15,101.50 1 1
-codeberg.org/snonux/perc/internal/repl/commands.go:102.22,103.60 1 0
-codeberg.org/snonux/perc/internal/repl/commands.go:104.10,105.114 1 1
-codeberg.org/snonux/perc/internal/repl/commands.go:109.23,113.2 2 6
-codeberg.org/snonux/perc/internal/repl/commands.go:115.22,118.2 2 4
-codeberg.org/snonux/perc/internal/repl/completer.go:10.52,16.16 2 40
-codeberg.org/snonux/perc/internal/repl/completer.go:16.16,19.20 2 40
-codeberg.org/snonux/perc/internal/repl/completer.go:19.20,21.55 1 38
-codeberg.org/snonux/perc/internal/repl/completer.go:21.55,24.5 1 3
-codeberg.org/snonux/perc/internal/repl/completer.go:24.10,27.5 1 35
-codeberg.org/snonux/perc/internal/repl/completer.go:31.2,31.16 1 40
-codeberg.org/snonux/perc/internal/repl/completer.go:31.16,33.3 1 2
-codeberg.org/snonux/perc/internal/repl/completer.go:35.2,36.40 2 38
-codeberg.org/snonux/perc/internal/repl/completer.go:36.40,37.69 1 266
-codeberg.org/snonux/perc/internal/repl/completer.go:37.69,42.4 1 30
-codeberg.org/snonux/perc/internal/repl/completer.go:44.2,44.20 1 38
-codeberg.org/snonux/perc/internal/repl/completer.go:48.47,58.2 2 53
-codeberg.org/snonux/perc/internal/repl/handlers.go:25.52,27.2 1 240
-codeberg.org/snonux/perc/internal/repl/handlers.go:30.95,31.19 1 63
-codeberg.org/snonux/perc/internal/repl/handlers.go:31.19,33.3 1 0
-codeberg.org/snonux/perc/internal/repl/handlers.go:34.2,34.35 1 63
-codeberg.org/snonux/perc/internal/repl/handlers.go:43.107,44.44 1 77
-codeberg.org/snonux/perc/internal/repl/handlers.go:44.44,46.20 2 37
-codeberg.org/snonux/perc/internal/repl/handlers.go:46.20,49.23 2 37
-codeberg.org/snonux/perc/internal/repl/handlers.go:49.23,51.5 1 6
-codeberg.org/snonux/perc/internal/repl/handlers.go:53.3,54.17 2 31
-codeberg.org/snonux/perc/internal/repl/handlers.go:54.17,56.4 1 0
-codeberg.org/snonux/perc/internal/repl/handlers.go:57.3,57.27 1 31
-codeberg.org/snonux/perc/internal/repl/handlers.go:59.2,59.28 1 40
-codeberg.org/snonux/perc/internal/repl/handlers.go:63.71,65.19 2 6
-codeberg.org/snonux/perc/internal/repl/handlers.go:65.19,67.3 1 1
-codeberg.org/snonux/perc/internal/repl/handlers.go:69.2,72.17 3 5
-codeberg.org/snonux/perc/internal/repl/handlers.go:73.12,75.44 2 1
-codeberg.org/snonux/perc/internal/repl/handlers.go:76.13,78.61 2 1
-codeberg.org/snonux/perc/internal/repl/handlers.go:79.16,80.50 1 2
-codeberg.org/snonux/perc/internal/repl/handlers.go:80.50,83.4 2 1
-codeberg.org/snonux/perc/internal/repl/handlers.go:83.9,86.4 2 1
-codeberg.org/snonux/perc/internal/repl/handlers.go:87.10,88.86 1 1
-codeberg.org/snonux/perc/internal/repl/handlers.go:98.96,101.85 2 40
-codeberg.org/snonux/perc/internal/repl/handlers.go:101.85,105.17 3 0
-codeberg.org/snonux/perc/internal/repl/handlers.go:105.17,107.4 1 0
-codeberg.org/snonux/perc/internal/repl/handlers.go:108.3,108.27 1 0
-codeberg.org/snonux/perc/internal/repl/handlers.go:112.2,112.47 1 40
-codeberg.org/snonux/perc/internal/repl/handlers.go:112.47,114.35 1 40
-codeberg.org/snonux/perc/internal/repl/handlers.go:114.35,116.18 2 18
-codeberg.org/snonux/perc/internal/repl/handlers.go:116.18,118.5 1 7
-codeberg.org/snonux/perc/internal/repl/handlers.go:122.3,123.23 2 33
-codeberg.org/snonux/perc/internal/repl/handlers.go:123.23,132.33 4 22
-codeberg.org/snonux/perc/internal/repl/handlers.go:132.33,134.19 2 18
-codeberg.org/snonux/perc/internal/repl/handlers.go:134.19,136.6 1 0
-codeberg.org/snonux/perc/internal/repl/handlers.go:137.5,137.29 1 18
-codeberg.org/snonux/perc/internal/repl/handlers.go:142.3,142.23 1 15
-codeberg.org/snonux/perc/internal/repl/handlers.go:142.23,143.63 1 4
-codeberg.org/snonux/perc/internal/repl/handlers.go:143.63,147.19 2 1
-codeberg.org/snonux/perc/internal/repl/handlers.go:147.19,149.6 1 0
-codeberg.org/snonux/perc/internal/repl/handlers.go:150.5,150.29 1 1
-codeberg.org/snonux/perc/internal/repl/handlers.go:155.2,155.28 1 14
-codeberg.org/snonux/perc/internal/repl/handlers.go:164.103,167.16 2 14
-codeberg.org/snonux/perc/internal/repl/handlers.go:167.16,170.3 1 9
-codeberg.org/snonux/perc/internal/repl/handlers.go:171.2,171.26 1 5
-codeberg.org/snonux/perc/internal/repl/handlers.go:180.98,183.2 1 9
-codeberg.org/snonux/perc/internal/repl/handlers.go:186.39,199.2 8 80
-codeberg.org/snonux/perc/internal/repl/history.go:17.60,22.2 1 83
-codeberg.org/snonux/perc/internal/repl/history.go:25.40,27.16 2 3
-codeberg.org/snonux/perc/internal/repl/history.go:27.16,29.3 1 0
-codeberg.org/snonux/perc/internal/repl/history.go:30.2,30.43 1 3
-codeberg.org/snonux/perc/internal/repl/history.go:34.42,36.16 2 1
-codeberg.org/snonux/perc/internal/repl/history.go:36.16,38.3 1 0
-codeberg.org/snonux/perc/internal/repl/history.go:40.2,41.16 2 1
-codeberg.org/snonux/perc/internal/repl/history.go:41.16,43.3 1 0
-codeberg.org/snonux/perc/internal/repl/history.go:44.2,48.21 4 1
-codeberg.org/snonux/perc/internal/repl/history.go:48.21,50.3 1 2
-codeberg.org/snonux/perc/internal/repl/history.go:51.2,51.38 1 1
-codeberg.org/snonux/perc/internal/repl/history.go:51.38,53.3 1 0
-codeberg.org/snonux/perc/internal/repl/history.go:54.2,54.16 1 1
-codeberg.org/snonux/perc/internal/repl/history.go:58.55,60.16 2 1
-codeberg.org/snonux/perc/internal/repl/history.go:60.16,62.3 1 0
-codeberg.org/snonux/perc/internal/repl/history.go:65.2,65.33 1 1
-codeberg.org/snonux/perc/internal/repl/history.go:65.33,67.3 1 0
-codeberg.org/snonux/perc/internal/repl/history.go:69.2,70.16 2 1
-codeberg.org/snonux/perc/internal/repl/history.go:70.16,72.3 1 0
-codeberg.org/snonux/perc/internal/repl/history.go:73.2,76.32 3 1
-codeberg.org/snonux/perc/internal/repl/history.go:76.32,77.61 1 2
-codeberg.org/snonux/perc/internal/repl/history.go:77.61,79.4 1 0
-codeberg.org/snonux/perc/internal/repl/history.go:81.2,81.39 1 1
-codeberg.org/snonux/perc/internal/repl/history.go:81.39,83.3 1 0
-codeberg.org/snonux/perc/internal/repl/history.go:84.2,84.12 1 1
-codeberg.org/snonux/perc/internal/repl/prompt.go:18.40,22.28 1 0
-codeberg.org/snonux/perc/internal/repl/prompt.go:22.29,22.30 0 0
-codeberg.org/snonux/perc/internal/repl/prompt.go:23.54,23.68 1 0
-codeberg.org/snonux/perc/internal/repl/prompt.go:24.37,24.58 1 0
-codeberg.org/snonux/perc/internal/repl/prompt.go:29.65,32.2 2 0
-codeberg.org/snonux/perc/internal/repl/prompt.go:35.63,38.2 2 0
-codeberg.org/snonux/perc/internal/repl/prompt.go:41.69,44.2 2 0
-codeberg.org/snonux/perc/internal/repl/prompt.go:47.75,50.2 2 0
-codeberg.org/snonux/perc/internal/repl/prompt.go:53.103,56.2 2 0
-codeberg.org/snonux/perc/internal/repl/prompt.go:59.88,62.2 2 0
-codeberg.org/snonux/perc/internal/repl/prompt.go:65.48,74.2 1 0
-codeberg.org/snonux/perc/internal/repl/repl.go:25.93,35.19 3 0
-codeberg.org/snonux/perc/internal/repl/repl.go:35.19,36.31 1 0
-codeberg.org/snonux/perc/internal/repl/repl.go:36.31,38.4 1 0
-codeberg.org/snonux/perc/internal/repl/repl.go:42.2,43.24 2 0
-codeberg.org/snonux/perc/internal/repl/repl.go:43.24,44.58 1 0
-codeberg.org/snonux/perc/internal/repl/repl.go:44.58,46.4 1 0
-codeberg.org/snonux/perc/internal/repl/repl.go:50.2,56.39 2 0
-codeberg.org/snonux/perc/internal/repl/repl.go:56.39,56.60 1 0
-codeberg.org/snonux/perc/internal/repl/repl.go:62.2,62.13 1 0
-codeberg.org/snonux/perc/internal/repl/repl.go:66.28,68.49 1 0
-codeberg.org/snonux/perc/internal/repl/repl.go:68.49,70.3 1 0
-codeberg.org/snonux/perc/internal/repl/repl.go:73.2,73.31 1 0
-codeberg.org/snonux/perc/internal/repl/repl.go:73.31,75.3 1 0
-codeberg.org/snonux/perc/internal/repl/repl.go:78.2,80.12 2 0
-codeberg.org/snonux/perc/internal/repl/repl.go:84.45,86.15 1 76
-codeberg.org/snonux/perc/internal/repl/repl.go:86.15,87.35 1 76
-codeberg.org/snonux/perc/internal/repl/repl.go:87.35,90.4 2 0
-codeberg.org/snonux/perc/internal/repl/repl.go:93.2,94.17 2 76
-codeberg.org/snonux/perc/internal/repl/repl.go:94.17,96.3 1 3
-codeberg.org/snonux/perc/internal/repl/repl.go:99.2,101.13 2 73
-codeberg.org/snonux/perc/internal/repl/repl.go:101.13,102.17 1 65
-codeberg.org/snonux/perc/internal/repl/repl.go:102.17,104.4 1 0
-codeberg.org/snonux/perc/internal/repl/repl.go:105.3,105.19 1 65
-codeberg.org/snonux/perc/internal/repl/repl.go:105.19,107.4 1 36
-codeberg.org/snonux/perc/internal/repl/repl.go:109.3,109.9 1 65
-codeberg.org/snonux/perc/internal/repl/repl.go:113.2,113.16 1 8
-codeberg.org/snonux/perc/internal/repl/repl.go:113.16,115.3 1 8
-codeberg.org/snonux/perc/internal/repl/repl.go:119.68,121.16 2 1
-codeberg.org/snonux/perc/internal/repl/repl.go:121.16,123.3 1 1
-codeberg.org/snonux/perc/internal/repl/repl.go:125.2,126.40 2 0
-codeberg.org/snonux/perc/internal/repl/repl.go:126.40,127.69 1 0
-codeberg.org/snonux/perc/internal/repl/repl.go:127.69,132.4 1 0
-codeberg.org/snonux/perc/internal/repl/repl.go:134.2,134.20 1 0
-codeberg.org/snonux/perc/internal/repl/repl.go:138.64,148.2 2 6
-codeberg.org/snonux/perc/internal/repl/repl.go:152.22,155.2 2 0
-codeberg.org/snonux/perc/internal/repl/repl.go:160.29,169.2 2 76
-codeberg.org/snonux/perc/internal/repl/repl.go:186.30,187.25 1 76
-codeberg.org/snonux/perc/internal/repl/repl.go:187.25,193.3 2 1
-codeberg.org/snonux/perc/internal/repl/repl.go:194.2,194.17 1 76
-codeberg.org/snonux/perc/internal/repl/repl.go:199.40,201.2 1 45
-codeberg.org/snonux/perc/internal/repl/repl.go:204.43,207.2 2 27
-codeberg.org/snonux/perc/internal/repl/repl.go:211.53,213.2 1 18
-codeberg.org/snonux/perc/internal/repl/repl.go:217.30,220.2 2 1
-codeberg.org/snonux/perc/internal/repl/repl.go:224.29,227.2 2 1
-codeberg.org/snonux/perc/internal/repl/repl.go:231.42,234.2 2 1
-codeberg.org/snonux/perc/internal/repl/repl.go:237.52,239.20 2 109
-codeberg.org/snonux/perc/internal/repl/repl.go:239.20,241.3 1 2
-codeberg.org/snonux/perc/internal/repl/repl.go:243.2,244.44 2 107
-codeberg.org/snonux/perc/internal/repl/repl.go:244.44,245.21 1 558
-codeberg.org/snonux/perc/internal/repl/repl.go:245.21,247.4 1 63
-codeberg.org/snonux/perc/internal/repl/repl.go:249.2,249.18 1 44
-codeberg.org/snonux/perc/internal/repl/signal.go:15.40,21.2 3 80
-codeberg.org/snonux/perc/internal/repl/signal.go:24.48,25.12 1 0
-codeberg.org/snonux/perc/internal/repl/signal.go:25.12,28.3 2 0
-codeberg.org/snonux/perc/internal/repl/signal.go:32.32,34.2 1 0
-codeberg.org/snonux/perc/internal/repl/tty.go:14.35,16.2 1 1
-codeberg.org/snonux/perc/internal/repl/tty.go:19.40,20.16 1 0
-codeberg.org/snonux/perc/internal/repl/tty.go:20.16,23.3 2 0
-codeberg.org/snonux/perc/internal/repl/tty.go:24.2,24.12 1 0