From dd7fb519b25b75a8a33868c835ddf0b991ef8c24 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Wed, 25 Mar 2026 23:54:30 +0200 Subject: cleanup --- .gitignore | 2 +- FIXES.md | 378 ------------------------------------------------------ LOGO.txt | 12 -- coverage.json | 1 - repl_coverage.out | 173 ------------------------- 5 files changed, 1 insertion(+), 565 deletions(-) delete mode 100644 FIXES.md delete mode 100644 LOGO.txt delete mode 100644 coverage.json delete mode 100644 repl_coverage.out 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 -- cgit v1.2.3