mirror of
https://github.com/taigrr/teaqlite.git
synced 2026-04-02 04:59:03 -07:00
feat(tests): add comprehensive test suite, update deps and Go 1.26.1
- Add 20+ tests for utility functions, SharedData, and Model - Tests cover: LoadTables, LoadTableData, UpdateCell, pagination, table inference, focus/blur, empty database, invalid indices - Update Go to 1.26.1, upgrade all dependencies - Replace custom Min/Max with Go builtin min/max - Format all files with goimports - Add staticcheck to CI workflow
This commit is contained in:
3
.github/workflows/test.yml
vendored
3
.github/workflows/test.yml
vendored
@@ -14,3 +14,6 @@ jobs:
|
|||||||
- run: go test -race ./...
|
- run: go test -race ./...
|
||||||
- run: go vet ./...
|
- run: go vet ./...
|
||||||
- run: go build ./...
|
- run: go build ./...
|
||||||
|
- name: Install staticcheck
|
||||||
|
run: go install honnef.co/go/tools/cmd/staticcheck@latest
|
||||||
|
- run: staticcheck ./...
|
||||||
|
|||||||
37
go.mod
37
go.mod
@@ -1,32 +1,30 @@
|
|||||||
module github.com/taigrr/teaqlite
|
module github.com/taigrr/teaqlite
|
||||||
|
|
||||||
go 1.26.0
|
go 1.26.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/charmbracelet/bubbles v1.0.0
|
github.com/charmbracelet/bubbles v1.0.0
|
||||||
github.com/charmbracelet/bubbletea v1.3.10
|
github.com/charmbracelet/bubbletea v1.3.10
|
||||||
github.com/charmbracelet/fang v0.4.4
|
github.com/charmbracelet/fang v1.0.0
|
||||||
github.com/charmbracelet/lipgloss v1.1.0
|
github.com/charmbracelet/lipgloss v1.1.0
|
||||||
github.com/spf13/cobra v1.10.2
|
github.com/spf13/cobra v1.10.2
|
||||||
modernc.org/sqlite v1.46.1
|
modernc.org/sqlite v1.46.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251106193318-19329a3e8410 // indirect
|
charm.land/lipgloss/v2 v2.0.2 // indirect
|
||||||
github.com/atotto/clipboard v0.1.4 // indirect
|
github.com/atotto/clipboard v0.1.4 // indirect
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||||
github.com/charmbracelet/colorprofile v0.4.1 // indirect
|
github.com/charmbracelet/colorprofile v0.4.3 // indirect
|
||||||
github.com/charmbracelet/ultraviolet v0.0.0-20251106190538-99ea45596692 // indirect
|
github.com/charmbracelet/ultraviolet v0.0.0-20260309091805-903bfd0cf188 // indirect
|
||||||
github.com/charmbracelet/x/ansi v0.11.6 // indirect
|
github.com/charmbracelet/x/ansi v0.11.6 // indirect
|
||||||
github.com/charmbracelet/x/cellbuf v0.0.15 // indirect
|
github.com/charmbracelet/x/cellbuf v0.0.15 // indirect
|
||||||
github.com/charmbracelet/x/exp/charmtone v0.0.0-20250711012602-b1f986320f7e // indirect
|
github.com/charmbracelet/x/exp/charmtone v0.0.0-20260311145557-c83711a11ffa // indirect
|
||||||
github.com/charmbracelet/x/exp/color v0.0.0-20250711012602-b1f986320f7e // indirect
|
|
||||||
github.com/charmbracelet/x/term v0.2.2 // indirect
|
github.com/charmbracelet/x/term v0.2.2 // indirect
|
||||||
github.com/charmbracelet/x/termios v0.1.1 // indirect
|
github.com/charmbracelet/x/termios v0.1.1 // indirect
|
||||||
github.com/charmbracelet/x/windows v0.2.2 // indirect
|
github.com/charmbracelet/x/windows v0.2.2 // indirect
|
||||||
github.com/clipperhouse/displaywidth v0.9.0 // indirect
|
github.com/clipperhouse/displaywidth v0.11.0 // indirect
|
||||||
github.com/clipperhouse/stringish v0.1.1 // indirect
|
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
|
||||||
github.com/clipperhouse/uax29/v2 v2.5.0 // indirect
|
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
|
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
@@ -34,24 +32,25 @@ require (
|
|||||||
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-localereader v0.0.1 // indirect
|
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.19 // indirect
|
github.com/mattn/go-runewidth v0.0.21 // indirect
|
||||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
||||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||||
github.com/muesli/mango v0.2.0 // indirect
|
github.com/muesli/mango v0.2.0 // indirect
|
||||||
github.com/muesli/mango-cobra v1.2.0 // indirect
|
github.com/muesli/mango-cobra v1.3.0 // indirect
|
||||||
github.com/muesli/mango-pflag v0.1.0 // indirect
|
github.com/muesli/mango-pflag v0.2.0 // indirect
|
||||||
github.com/muesli/roff v0.1.0 // indirect
|
github.com/muesli/roff v0.1.0 // indirect
|
||||||
github.com/muesli/termenv v0.16.0 // indirect
|
github.com/muesli/termenv v0.16.0 // indirect
|
||||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/spf13/pflag v1.0.9 // indirect
|
github.com/spf13/pflag v1.0.10 // indirect
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
|
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 // indirect
|
||||||
golang.org/x/sync v0.17.0 // indirect
|
golang.org/x/sync v0.20.0 // indirect
|
||||||
golang.org/x/sys v0.38.0 // indirect
|
golang.org/x/sys v0.42.0 // indirect
|
||||||
golang.org/x/text v0.27.0 // indirect
|
golang.org/x/text v0.35.0 // indirect
|
||||||
modernc.org/libc v1.67.6 // indirect
|
golang.org/x/tools v0.43.0 // indirect
|
||||||
|
modernc.org/libc v1.70.0 // indirect
|
||||||
modernc.org/mathutil v1.7.1 // indirect
|
modernc.org/mathutil v1.7.1 // indirect
|
||||||
modernc.org/memory v1.11.0 // indirect
|
modernc.org/memory v1.11.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
91
go.sum
91
go.sum
@@ -1,33 +1,31 @@
|
|||||||
charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251106193318-19329a3e8410 h1:D9PbaszZYpB4nj+d6HTWr1onlmlyuGVNfL9gAi8iB3k=
|
charm.land/lipgloss/v2 v2.0.2 h1:xFolbF8JdpNkM2cEPTfXEcW1p6NRzOWTSamRfYEw8cs=
|
||||||
charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251106193318-19329a3e8410/go.mod h1:1qZyvvVCenJO2M1ac2mX0yyiIZJoZmDM4DG4s0udJkU=
|
charm.land/lipgloss/v2 v2.0.2/go.mod h1:KjPle2Qd3YmvP1KL5OMHiHysGcNwq6u83MUjYkFvEkM=
|
||||||
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
|
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
|
||||||
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
|
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
|
||||||
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||||
github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3vj1nolY=
|
github.com/aymanbagabas/go-udiff v0.4.1 h1:OEIrQ8maEeDBXQDoGCbbTTXYJMYRCRO1fnodZ12Gv5o=
|
||||||
github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E=
|
github.com/aymanbagabas/go-udiff v0.4.1/go.mod h1:0L9PGwj20lrtmEMeyw4WKJ/TMyDtvAoK9bf2u/mNo3w=
|
||||||
github.com/charmbracelet/bubbles v1.0.0 h1:12J8/ak/uCZEMQ6KU7pcfwceyjLlWsDLAxB5fXonfvc=
|
github.com/charmbracelet/bubbles v1.0.0 h1:12J8/ak/uCZEMQ6KU7pcfwceyjLlWsDLAxB5fXonfvc=
|
||||||
github.com/charmbracelet/bubbles v1.0.0/go.mod h1:9d/Zd5GdnauMI5ivUIVisuEm3ave1XwXtD1ckyV6r3E=
|
github.com/charmbracelet/bubbles v1.0.0/go.mod h1:9d/Zd5GdnauMI5ivUIVisuEm3ave1XwXtD1ckyV6r3E=
|
||||||
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
|
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
|
||||||
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
|
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
|
||||||
github.com/charmbracelet/colorprofile v0.4.1 h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco3ej+ngLk1kk=
|
github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex9t5KX76i20Q=
|
||||||
github.com/charmbracelet/colorprofile v0.4.1/go.mod h1:U1d9Dljmdf9DLegaJ0nGZNJvoXAhayhmidOdcBwAvKk=
|
github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q=
|
||||||
github.com/charmbracelet/fang v0.4.4 h1:G4qKxF6or/eTPgmAolwPuRNyuci3hTUGGX1rj1YkHJY=
|
github.com/charmbracelet/fang v1.0.0 h1:jESBY40agJOlLYnnv9jE0mLqDGTxEk0hkOnx7YGyRlQ=
|
||||||
github.com/charmbracelet/fang v0.4.4/go.mod h1:P5/DNb9DddQ0Z0dbc0P3ol4/ix5Po7Ofr2KMBfAqoCo=
|
github.com/charmbracelet/fang v1.0.0/go.mod h1:P5/DNb9DddQ0Z0dbc0P3ol4/ix5Po7Ofr2KMBfAqoCo=
|
||||||
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
|
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
|
||||||
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
|
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
|
||||||
github.com/charmbracelet/ultraviolet v0.0.0-20251106190538-99ea45596692 h1:r/3jQZ1LjWW6ybp8HHfhrKrwHIWiJhUuY7wwYIWZulQ=
|
github.com/charmbracelet/ultraviolet v0.0.0-20260309091805-903bfd0cf188 h1:J8v4kWJYCaxv1SLhLunN74S+jMteZ1f7Dae99ioq4Bo=
|
||||||
github.com/charmbracelet/ultraviolet v0.0.0-20251106190538-99ea45596692/go.mod h1:Y8B4DzWeTb0ama8l3+KyopZtkE8fZjwRQ3aEAPEXHE0=
|
github.com/charmbracelet/ultraviolet v0.0.0-20260309091805-903bfd0cf188/go.mod h1:FzWNAbe1jEmI+GZljSnlaSA8wJjnNIZhWBLkTsAl6eg=
|
||||||
github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8=
|
github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8=
|
||||||
github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ=
|
github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ=
|
||||||
github.com/charmbracelet/x/cellbuf v0.0.15 h1:ur3pZy0o6z/R7EylET877CBxaiE1Sp1GMxoFPAIztPI=
|
github.com/charmbracelet/x/cellbuf v0.0.15 h1:ur3pZy0o6z/R7EylET877CBxaiE1Sp1GMxoFPAIztPI=
|
||||||
github.com/charmbracelet/x/cellbuf v0.0.15/go.mod h1:J1YVbR7MUuEGIFPCaaZ96KDl5NoS0DAWkskup+mOY+Q=
|
github.com/charmbracelet/x/cellbuf v0.0.15/go.mod h1:J1YVbR7MUuEGIFPCaaZ96KDl5NoS0DAWkskup+mOY+Q=
|
||||||
github.com/charmbracelet/x/exp/charmtone v0.0.0-20250711012602-b1f986320f7e h1:sc41kBOnun1OX15Lg05ZB6Ly6AFWnntCYb8jsEDBAPs=
|
github.com/charmbracelet/x/exp/charmtone v0.0.0-20260311145557-c83711a11ffa h1:/hY9CTFQJJ7G5Hu0MFAZTUXV/JO8H8FOIdWKvRA+tTw=
|
||||||
github.com/charmbracelet/x/exp/charmtone v0.0.0-20250711012602-b1f986320f7e/go.mod h1:T9jr8CzFpjhFVHjNjKwbAD7KwBNyFnj2pntAO7F2zw0=
|
github.com/charmbracelet/x/exp/charmtone v0.0.0-20260311145557-c83711a11ffa/go.mod h1:nsExn0DGyX0lh9LwLHTn2Gg+hafdzfSXnC+QmEJTZFY=
|
||||||
github.com/charmbracelet/x/exp/color v0.0.0-20250711012602-b1f986320f7e h1:D0tltuLCSvxMznOpQg7f3MArp8ImU0zALbakI47ffkw=
|
|
||||||
github.com/charmbracelet/x/exp/color v0.0.0-20250711012602-b1f986320f7e/go.mod h1:hk/GyTELmEgX54pBAOHcFvH8Xed53JWo/g8kJXFo/PI=
|
|
||||||
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f h1:pk6gmGpCE7F3FcjaOEKYriCvpmIN4+6OS/RD0vm4uIA=
|
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f h1:pk6gmGpCE7F3FcjaOEKYriCvpmIN4+6OS/RD0vm4uIA=
|
||||||
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f/go.mod h1:IfZAMTHB6XkZSeXUqriemErjAWCCzT0LwjKFYCZyw0I=
|
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f/go.mod h1:IfZAMTHB6XkZSeXUqriemErjAWCCzT0LwjKFYCZyw0I=
|
||||||
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
|
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
|
||||||
@@ -36,12 +34,10 @@ github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8
|
|||||||
github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo=
|
github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo=
|
||||||
github.com/charmbracelet/x/windows v0.2.2 h1:IofanmuvaxnKHuV04sC0eBy/smG6kIKrWG2/jYn2GuM=
|
github.com/charmbracelet/x/windows v0.2.2 h1:IofanmuvaxnKHuV04sC0eBy/smG6kIKrWG2/jYn2GuM=
|
||||||
github.com/charmbracelet/x/windows v0.2.2/go.mod h1:/8XtdKZzedat74NQFn0NGlGL4soHB0YQZrETF96h75k=
|
github.com/charmbracelet/x/windows v0.2.2/go.mod h1:/8XtdKZzedat74NQFn0NGlGL4soHB0YQZrETF96h75k=
|
||||||
github.com/clipperhouse/displaywidth v0.9.0 h1:Qb4KOhYwRiN3viMv1v/3cTBlz3AcAZX3+y9OLhMtAtA=
|
github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
|
||||||
github.com/clipperhouse/displaywidth v0.9.0/go.mod h1:aCAAqTlh4GIVkhQnJpbL0T/WfcrJXHcj8C0yjYcjOZA=
|
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
|
||||||
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
|
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
|
||||||
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
|
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||||
github.com/clipperhouse/uax29/v2 v2.5.0 h1:x7T0T4eTHDONxFJsL94uKNKPHrclyFI0lm7+w94cO8U=
|
|
||||||
github.com/clipperhouse/uax29/v2 v2.5.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -63,18 +59,18 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
|||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||||
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||||
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
|
github.com/mattn/go-runewidth v0.0.21 h1:jJKAZiQH+2mIinzCJIaIG9Be1+0NR+5sz/lYEEjdM8w=
|
||||||
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
github.com/mattn/go-runewidth v0.0.21/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
|
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
|
||||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
|
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
|
||||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||||
github.com/muesli/mango v0.2.0 h1:iNNc0c5VLQ6fsMgAqGQofByNUBH2Q2nEbD6TaI+5yyQ=
|
github.com/muesli/mango v0.2.0 h1:iNNc0c5VLQ6fsMgAqGQofByNUBH2Q2nEbD6TaI+5yyQ=
|
||||||
github.com/muesli/mango v0.2.0/go.mod h1:5XFpbC8jY5UUv89YQciiXNlbi+iJgt29VDC5xbzrLL4=
|
github.com/muesli/mango v0.2.0/go.mod h1:5XFpbC8jY5UUv89YQciiXNlbi+iJgt29VDC5xbzrLL4=
|
||||||
github.com/muesli/mango-cobra v1.2.0 h1:DQvjzAM0PMZr85Iv9LIMaYISpTOliMEg+uMFtNbYvWg=
|
github.com/muesli/mango-cobra v1.3.0 h1:vQy5GvPg3ndOSpduxutqFoINhWk3vD5K2dXo5E8pqec=
|
||||||
github.com/muesli/mango-cobra v1.2.0/go.mod h1:vMJL54QytZAJhCT13LPVDfkvCUJ5/4jNUKF/8NC2UjA=
|
github.com/muesli/mango-cobra v1.3.0/go.mod h1:Cj1ZrBu3806Qw7UjxnAUgE+7tllUBj1NCLQDwwGx19E=
|
||||||
github.com/muesli/mango-pflag v0.1.0 h1:UADqbYgpUyRoBja3g6LUL+3LErjpsOwaC9ywvBWe7Sg=
|
github.com/muesli/mango-pflag v0.2.0 h1:QViokgKDZQCzKhYe1zH8D+UlPJzBSGoP9yx0hBG0t5k=
|
||||||
github.com/muesli/mango-pflag v0.1.0/go.mod h1:YEQomTxaCUp8PrbhFh10UfbhbQrM/xJ4i2PB8VTLLW0=
|
github.com/muesli/mango-pflag v0.2.0/go.mod h1:X9LT1p/pbGA1wjvEbtwnixujKErkP0jVmrxwrw3fL0Y=
|
||||||
github.com/muesli/roff v0.1.0 h1:YD0lalCotmYuF5HhZliKWlIx7IEhiXeSfq7hNjFqGF8=
|
github.com/muesli/roff v0.1.0 h1:YD0lalCotmYuF5HhZliKWlIx7IEhiXeSfq7hNjFqGF8=
|
||||||
github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig=
|
github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig=
|
||||||
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
|
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
|
||||||
@@ -90,44 +86,45 @@ github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc
|
|||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||||
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
|
|
||||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||||
|
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY=
|
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA=
|
||||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70=
|
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ=
|
||||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
|
||||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
|
||||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
|
||||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
|
modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
|
||||||
modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||||
modernc.org/ccgo/v4 v4.30.1 h1:4r4U1J6Fhj98NKfSjnPUN7Ze2c6MnAdL0hWw6+LrJpc=
|
modernc.org/ccgo/v4 v4.32.0 h1:hjG66bI/kqIPX1b2yT6fr/jt+QedtP2fqojG2VrFuVw=
|
||||||
modernc.org/ccgo/v4 v4.30.1/go.mod h1:bIOeI1JL54Utlxn+LwrFyjCx2n2RDiYEaJVSrgdrRfM=
|
modernc.org/ccgo/v4 v4.32.0/go.mod h1:6F08EBCx5uQc38kMGl+0Nm0oWczoo1c7cgpzEry7Uc0=
|
||||||
modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA=
|
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
|
||||||
modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=
|
||||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||||
modernc.org/gc/v3 v3.1.1 h1:k8T3gkXWY9sEiytKhcgyiZ2L0DTyCQ/nvX+LoCljoRE=
|
modernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo=
|
||||||
modernc.org/gc/v3 v3.1.1/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
modernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
||||||
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
||||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||||
modernc.org/libc v1.67.6 h1:eVOQvpModVLKOdT+LvBPjdQqfrZq+pC39BygcT+E7OI=
|
modernc.org/libc v1.70.0 h1:U58NawXqXbgpZ/dcdS9kMshu08aiA6b7gusEusqzNkw=
|
||||||
modernc.org/libc v1.67.6/go.mod h1:JAhxUVlolfYDErnwiqaLvUqc8nfb2r6S6slAgZOnaiE=
|
modernc.org/libc v1.70.0/go.mod h1:OVmxFGP1CI/Z4L3E0Q3Mf1PDE0BucwMkcXjjLntvHJo=
|
||||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||||
|
|||||||
@@ -522,20 +522,6 @@ func WrapText(text string, width int) []string {
|
|||||||
return lines
|
return lines
|
||||||
}
|
}
|
||||||
|
|
||||||
func Min(a, b int) int {
|
|
||||||
if a < b {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func Max(a, b int) int {
|
|
||||||
if a > b {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func InitialModel(db *sql.DB, opts ...Option) *Model {
|
func InitialModel(db *sql.DB, opts ...Option) *Model {
|
||||||
shared := NewSharedData(db)
|
shared := NewSharedData(db)
|
||||||
if err := shared.LoadTables(); err != nil {
|
if err := shared.LoadTables(); err != nil {
|
||||||
|
|||||||
428
internal/app/app_test.go
Normal file
428
internal/app/app_test.go
Normal file
@@ -0,0 +1,428 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
_ "modernc.org/sqlite"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTruncateString(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
maxLen int
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{"short string", "hello", 10, "hello"},
|
||||||
|
{"exact length", "hello", 5, "hello"},
|
||||||
|
{"needs truncation", "hello world", 8, "hello..."},
|
||||||
|
{"empty string", "", 5, ""},
|
||||||
|
{"min truncation", "abcdef", 4, "a..."},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := TruncateString(tt.input, tt.maxLen)
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("TruncateString(%q, %d) = %q, want %q", tt.input, tt.maxLen, got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrapText(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
text string
|
||||||
|
width int
|
||||||
|
want []string
|
||||||
|
}{
|
||||||
|
{"short text", "hello", 20, []string{"hello"}},
|
||||||
|
{"wrap at word boundary", "hello world foo", 12, []string{"hello world", "foo"}},
|
||||||
|
{"zero width", "hello", 0, []string{"hello"}},
|
||||||
|
{"empty text", "", 10, []string{""}},
|
||||||
|
{"single long word", "abcdefghij", 5, []string{"abcde", "fghij"}},
|
||||||
|
{"multiple words wrapping", "one two three four", 10, []string{"one two", "three four"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := WrapText(tt.text, tt.width)
|
||||||
|
if len(got) != len(tt.want) {
|
||||||
|
t.Errorf("WrapText(%q, %d) returned %d lines, want %d: %v", tt.text, tt.width, len(got), len(tt.want), got)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := range got {
|
||||||
|
if got[i] != tt.want[i] {
|
||||||
|
t.Errorf("WrapText(%q, %d)[%d] = %q, want %q", tt.text, tt.width, i, got[i], tt.want[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNextID(t *testing.T) {
|
||||||
|
// nextID should return monotonically increasing values
|
||||||
|
id1 := nextID()
|
||||||
|
id2 := nextID()
|
||||||
|
id3 := nextID()
|
||||||
|
|
||||||
|
if id2 <= id1 {
|
||||||
|
t.Errorf("nextID() not monotonically increasing: %d, %d", id1, id2)
|
||||||
|
}
|
||||||
|
if id3 <= id2 {
|
||||||
|
t.Errorf("nextID() not monotonically increasing: %d, %d", id2, id3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDefaultAppKeyMap(t *testing.T) {
|
||||||
|
km := DefaultAppKeyMap()
|
||||||
|
|
||||||
|
if len(km.Quit.Keys()) == 0 {
|
||||||
|
t.Error("Quit keybinding has no keys")
|
||||||
|
}
|
||||||
|
if len(km.Suspend.Keys()) == 0 {
|
||||||
|
t.Error("Suspend keybinding has no keys")
|
||||||
|
}
|
||||||
|
if len(km.ToggleHelp.Keys()) == 0 {
|
||||||
|
t.Error("ToggleHelp keybinding has no keys")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// createTestDB creates an in-memory SQLite database with test data
|
||||||
|
func createTestDB(t *testing.T) *sql.DB {
|
||||||
|
t.Helper()
|
||||||
|
db, err := sql.Open("sqlite", ":memory:")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to open test database: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Exec(`
|
||||||
|
CREATE TABLE users (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
email TEXT
|
||||||
|
);
|
||||||
|
INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');
|
||||||
|
INSERT INTO users (name, email) VALUES ('Bob', 'bob@example.com');
|
||||||
|
INSERT INTO users (name, email) VALUES ('Charlie', 'charlie@example.com');
|
||||||
|
|
||||||
|
CREATE TABLE products (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
price REAL
|
||||||
|
);
|
||||||
|
INSERT INTO products (title, price) VALUES ('Widget', 9.99);
|
||||||
|
INSERT INTO products (title, price) VALUES ('Gadget', 19.99);
|
||||||
|
`)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create test data: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewSharedData(t *testing.T) {
|
||||||
|
db := createTestDB(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
shared := NewSharedData(db)
|
||||||
|
if shared.DB != db {
|
||||||
|
t.Error("NewSharedData should store the database reference")
|
||||||
|
}
|
||||||
|
if shared.Width != 80 || shared.Height != 24 {
|
||||||
|
t.Errorf("default dimensions should be 80x24, got %dx%d", shared.Width, shared.Height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSharedDataLoadTables(t *testing.T) {
|
||||||
|
db := createTestDB(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
shared := NewSharedData(db)
|
||||||
|
if err := shared.LoadTables(); err != nil {
|
||||||
|
t.Fatalf("LoadTables() failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(shared.Tables) != 2 {
|
||||||
|
t.Fatalf("expected 2 tables, got %d: %v", len(shared.Tables), shared.Tables)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tables should be sorted alphabetically
|
||||||
|
if shared.Tables[0] != "products" || shared.Tables[1] != "users" {
|
||||||
|
t.Errorf("expected [products, users], got %v", shared.Tables)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilteredTables should be a copy of Tables
|
||||||
|
if len(shared.FilteredTables) != len(shared.Tables) {
|
||||||
|
t.Errorf("FilteredTables length mismatch: %d vs %d", len(shared.FilteredTables), len(shared.Tables))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSharedDataLoadTableData(t *testing.T) {
|
||||||
|
db := createTestDB(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
shared := NewSharedData(db)
|
||||||
|
if err := shared.LoadTables(); err != nil {
|
||||||
|
t.Fatalf("LoadTables() failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load "users" table (index 1 since sorted alphabetically)
|
||||||
|
shared.SelectedTable = 1
|
||||||
|
if err := shared.LoadTableData(); err != nil {
|
||||||
|
t.Fatalf("LoadTableData() failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(shared.Columns) != 3 {
|
||||||
|
t.Fatalf("expected 3 columns, got %d: %v", len(shared.Columns), shared.Columns)
|
||||||
|
}
|
||||||
|
if shared.Columns[0] != "id" || shared.Columns[1] != "name" || shared.Columns[2] != "email" {
|
||||||
|
t.Errorf("unexpected columns: %v", shared.Columns)
|
||||||
|
}
|
||||||
|
|
||||||
|
if shared.TotalRows != 3 {
|
||||||
|
t.Errorf("expected 3 total rows, got %d", shared.TotalRows)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(shared.TableData) != 3 {
|
||||||
|
t.Fatalf("expected 3 data rows, got %d", len(shared.TableData))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check primary keys detected
|
||||||
|
if len(shared.PrimaryKeys) != 1 || shared.PrimaryKeys[0] != "id" {
|
||||||
|
t.Errorf("expected primary key [id], got %v", shared.PrimaryKeys)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should not be marked as query result
|
||||||
|
if shared.IsQueryResult {
|
||||||
|
t.Error("regular table load should not be marked as query result")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSharedDataLoadTableDataInvalidIndex(t *testing.T) {
|
||||||
|
db := createTestDB(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
shared := NewSharedData(db)
|
||||||
|
if err := shared.LoadTables(); err != nil {
|
||||||
|
t.Fatalf("LoadTables() failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
shared.SelectedTable = 99
|
||||||
|
if err := shared.LoadTableData(); err == nil {
|
||||||
|
t.Error("LoadTableData() should fail with invalid table index")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSharedDataUpdateCell(t *testing.T) {
|
||||||
|
db := createTestDB(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
shared := NewSharedData(db)
|
||||||
|
if err := shared.LoadTables(); err != nil {
|
||||||
|
t.Fatalf("LoadTables() failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load users table
|
||||||
|
shared.SelectedTable = 1
|
||||||
|
if err := shared.LoadTableData(); err != nil {
|
||||||
|
t.Fatalf("LoadTableData() failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update name of first user (row 0, col 1 = name)
|
||||||
|
if err := shared.UpdateCell(0, 1, "Alicia"); err != nil {
|
||||||
|
t.Fatalf("UpdateCell() failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify local data updated
|
||||||
|
if shared.FilteredData[0][1] != "Alicia" {
|
||||||
|
t.Errorf("FilteredData not updated, got %q", shared.FilteredData[0][1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify database updated
|
||||||
|
var name string
|
||||||
|
err := db.QueryRow("SELECT name FROM users WHERE id = 1").Scan(&name)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to verify update: %v", err)
|
||||||
|
}
|
||||||
|
if name != "Alicia" {
|
||||||
|
t.Errorf("database not updated, got %q", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSharedDataUpdateCellInvalidIndex(t *testing.T) {
|
||||||
|
db := createTestDB(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
shared := NewSharedData(db)
|
||||||
|
if err := shared.LoadTables(); err != nil {
|
||||||
|
t.Fatalf("LoadTables() failed: %v", err)
|
||||||
|
}
|
||||||
|
shared.SelectedTable = 1
|
||||||
|
if err := shared.LoadTableData(); err != nil {
|
||||||
|
t.Fatalf("LoadTableData() failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := shared.UpdateCell(99, 0, "value"); err == nil {
|
||||||
|
t.Error("UpdateCell() should fail with invalid row index")
|
||||||
|
}
|
||||||
|
if err := shared.UpdateCell(0, 99, "value"); err == nil {
|
||||||
|
t.Error("UpdateCell() should fail with invalid column index")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSharedDataPagination(t *testing.T) {
|
||||||
|
db := createTestDB(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
// Insert enough rows to test pagination
|
||||||
|
for i := 0; i < 25; i++ {
|
||||||
|
_, err := db.Exec("INSERT INTO users (name, email) VALUES (?, ?)",
|
||||||
|
"User"+string(rune('A'+i)), "user@example.com")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to insert test data: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shared := NewSharedData(db)
|
||||||
|
if err := shared.LoadTables(); err != nil {
|
||||||
|
t.Fatalf("LoadTables() failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
shared.SelectedTable = 1
|
||||||
|
shared.CurrentPage = 0
|
||||||
|
if err := shared.LoadTableData(); err != nil {
|
||||||
|
t.Fatalf("LoadTableData() page 0 failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if shared.TotalRows != 28 { // 3 original + 25 new
|
||||||
|
t.Errorf("expected 28 total rows, got %d", shared.TotalRows)
|
||||||
|
}
|
||||||
|
if len(shared.TableData) != PageSize {
|
||||||
|
t.Errorf("page 0 should have %d rows, got %d", PageSize, len(shared.TableData))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load page 2
|
||||||
|
shared.CurrentPage = 1
|
||||||
|
if err := shared.LoadTableData(); err != nil {
|
||||||
|
t.Fatalf("LoadTableData() page 1 failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(shared.TableData) != 8 { // 28 - 20 = 8
|
||||||
|
t.Errorf("page 1 should have 8 rows, got %d", len(shared.TableData))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInitialModel(t *testing.T) {
|
||||||
|
db := createTestDB(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
m := InitialModel(db)
|
||||||
|
if m.Err() != nil {
|
||||||
|
t.Fatalf("InitialModel() returned error: %v", m.Err())
|
||||||
|
}
|
||||||
|
if m.width != 80 || m.height != 24 {
|
||||||
|
t.Errorf("default dimensions should be 80x24, got %dx%d", m.width, m.height)
|
||||||
|
}
|
||||||
|
if !m.Focused() {
|
||||||
|
t.Error("model should be focused by default")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInitialModelWithOptions(t *testing.T) {
|
||||||
|
db := createTestDB(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
km := DefaultAppKeyMap()
|
||||||
|
m := InitialModel(db, WithKeyMap(km), WithDimensions(120, 40))
|
||||||
|
if m.Err() != nil {
|
||||||
|
t.Fatalf("InitialModel() returned error: %v", m.Err())
|
||||||
|
}
|
||||||
|
if m.width != 120 || m.height != 40 {
|
||||||
|
t.Errorf("custom dimensions should be 120x40, got %dx%d", m.width, m.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestModelFocusBlur(t *testing.T) {
|
||||||
|
db := createTestDB(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
m := InitialModel(db)
|
||||||
|
if !m.Focused() {
|
||||||
|
t.Error("model should be focused initially")
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Blur()
|
||||||
|
if m.Focused() {
|
||||||
|
t.Error("model should not be focused after Blur()")
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Focus()
|
||||||
|
if !m.Focused() {
|
||||||
|
t.Error("model should be focused after Focus()")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInferTableFromQueryResult(t *testing.T) {
|
||||||
|
db := createTestDB(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
shared := NewSharedData(db)
|
||||||
|
if err := shared.LoadTables(); err != nil {
|
||||||
|
t.Fatalf("LoadTables() failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up columns matching the users table
|
||||||
|
shared.Columns = []string{"id", "name", "email"}
|
||||||
|
shared.IsQueryResult = true
|
||||||
|
shared.FilteredData = [][]string{{"1", "Alice", "alice@example.com"}}
|
||||||
|
|
||||||
|
tableName, err := shared.inferTableFromQueryResult(0, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("inferTableFromQueryResult() failed: %v", err)
|
||||||
|
}
|
||||||
|
if tableName != "users" {
|
||||||
|
t.Errorf("expected 'users', got %q", tableName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify it was cached
|
||||||
|
if shared.QueryTableName != "users" {
|
||||||
|
t.Errorf("QueryTableName should be cached as 'users', got %q", shared.QueryTableName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetTableInfo(t *testing.T) {
|
||||||
|
db := createTestDB(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
shared := NewSharedData(db)
|
||||||
|
cols, pks, err := shared.getTableInfo("users")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("getTableInfo() failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cols) != 3 {
|
||||||
|
t.Errorf("expected 3 columns, got %d", len(cols))
|
||||||
|
}
|
||||||
|
if len(pks) != 1 || pks[0] != "id" {
|
||||||
|
t.Errorf("expected primary key [id], got %v", pks)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSharedDataEmptyDatabase(t *testing.T) {
|
||||||
|
db, err := sql.Open("sqlite", ":memory:")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to open test database: %v", err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
shared := NewSharedData(db)
|
||||||
|
if err := shared.LoadTables(); err != nil {
|
||||||
|
t.Fatalf("LoadTables() on empty db failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(shared.Tables) != 0 {
|
||||||
|
t.Errorf("expected 0 tables in empty db, got %d", len(shared.Tables))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,10 +4,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
|
||||||
"github.com/charmbracelet/bubbles/help"
|
"github.com/charmbracelet/bubbles/help"
|
||||||
"github.com/charmbracelet/bubbles/key"
|
"github.com/charmbracelet/bubbles/key"
|
||||||
"github.com/charmbracelet/bubbles/textinput"
|
"github.com/charmbracelet/bubbles/textinput"
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
)
|
)
|
||||||
|
|
||||||
type EditCellModel struct {
|
type EditCellModel struct {
|
||||||
|
|||||||
@@ -4,17 +4,17 @@ import "github.com/charmbracelet/bubbles/key"
|
|||||||
|
|
||||||
// EditCellKeyMap defines keybindings for the edit cell view
|
// EditCellKeyMap defines keybindings for the edit cell view
|
||||||
type EditCellKeyMap struct {
|
type EditCellKeyMap struct {
|
||||||
Save key.Binding
|
Save key.Binding
|
||||||
Cancel key.Binding
|
Cancel key.Binding
|
||||||
CursorLeft key.Binding
|
CursorLeft key.Binding
|
||||||
CursorRight key.Binding
|
CursorRight key.Binding
|
||||||
WordLeft key.Binding
|
WordLeft key.Binding
|
||||||
WordRight key.Binding
|
WordRight key.Binding
|
||||||
LineStart key.Binding
|
LineStart key.Binding
|
||||||
LineEnd key.Binding
|
LineEnd key.Binding
|
||||||
DeleteWord key.Binding
|
DeleteWord key.Binding
|
||||||
DeleteChar key.Binding
|
DeleteChar key.Binding
|
||||||
ToggleHelp key.Binding
|
ToggleHelp key.Binding
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultEditCellKeyMap returns the default keybindings for edit cell
|
// DefaultEditCellKeyMap returns the default keybindings for edit cell
|
||||||
|
|||||||
@@ -418,7 +418,7 @@ func (m *QueryModel) View() string {
|
|||||||
content.WriteString("\n")
|
content.WriteString("\n")
|
||||||
|
|
||||||
// Data rows with scrolling
|
// Data rows with scrolling
|
||||||
visibleCount := Max(1, m.Shared.Height-10)
|
visibleCount := max(1, m.Shared.Height-10)
|
||||||
startIdx := 0
|
startIdx := 0
|
||||||
|
|
||||||
// Adjust start index if selected row is out of view
|
// Adjust start index if selected row is out of view
|
||||||
@@ -426,7 +426,7 @@ func (m *QueryModel) View() string {
|
|||||||
startIdx = m.selectedRow - visibleCount + 1
|
startIdx = m.selectedRow - visibleCount + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
endIdx := Min(len(m.results), startIdx+visibleCount)
|
endIdx := min(len(m.results), startIdx+visibleCount)
|
||||||
|
|
||||||
for i := range endIdx {
|
for i := range endIdx {
|
||||||
if i < startIdx {
|
if i < startIdx {
|
||||||
@@ -465,4 +465,3 @@ func (m *QueryModel) View() string {
|
|||||||
|
|
||||||
return content.String()
|
return content.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,25 +8,25 @@ import "github.com/charmbracelet/bubbles/key"
|
|||||||
// - G: go to end (single 'G' press)
|
// - G: go to end (single 'G' press)
|
||||||
type QueryKeyMap struct {
|
type QueryKeyMap struct {
|
||||||
// Input mode keys
|
// Input mode keys
|
||||||
Execute key.Binding
|
Execute key.Binding
|
||||||
Escape key.Binding
|
Escape key.Binding
|
||||||
CursorLeft key.Binding
|
CursorLeft key.Binding
|
||||||
CursorRight key.Binding
|
CursorRight key.Binding
|
||||||
WordLeft key.Binding
|
WordLeft key.Binding
|
||||||
WordRight key.Binding
|
WordRight key.Binding
|
||||||
LineStart key.Binding
|
LineStart key.Binding
|
||||||
LineEnd key.Binding
|
LineEnd key.Binding
|
||||||
DeleteWord key.Binding
|
DeleteWord key.Binding
|
||||||
|
|
||||||
// Results mode keys
|
// Results mode keys
|
||||||
Up key.Binding
|
Up key.Binding
|
||||||
Down key.Binding
|
Down key.Binding
|
||||||
Enter key.Binding
|
Enter key.Binding
|
||||||
EditQuery key.Binding
|
EditQuery key.Binding
|
||||||
GoToStart key.Binding
|
GoToStart key.Binding
|
||||||
GoToEnd key.Binding
|
GoToEnd key.Binding
|
||||||
Back key.Binding
|
Back key.Binding
|
||||||
ToggleHelp key.Binding
|
ToggleHelp key.Binding
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultQueryKeyMap returns the default keybindings for query view
|
// DefaultQueryKeyMap returns the default keybindings for query view
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
|
||||||
"github.com/charmbracelet/bubbles/help"
|
"github.com/charmbracelet/bubbles/help"
|
||||||
"github.com/charmbracelet/bubbles/key"
|
"github.com/charmbracelet/bubbles/key"
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RowDetailModel struct {
|
type RowDetailModel struct {
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
|
||||||
"github.com/charmbracelet/bubbles/help"
|
"github.com/charmbracelet/bubbles/help"
|
||||||
"github.com/charmbracelet/bubbles/key"
|
"github.com/charmbracelet/bubbles/key"
|
||||||
"github.com/charmbracelet/bubbles/textinput"
|
"github.com/charmbracelet/bubbles/textinput"
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TableDataModel struct {
|
type TableDataModel struct {
|
||||||
@@ -409,7 +409,7 @@ func (m *TableDataModel) View() string {
|
|||||||
content.WriteString("\n")
|
content.WriteString("\n")
|
||||||
|
|
||||||
// Show data rows with scrolling within current page
|
// Show data rows with scrolling within current page
|
||||||
visibleCount := Max(1, m.Shared.Height-10)
|
visibleCount := max(1, m.Shared.Height-10)
|
||||||
totalRows := len(m.Shared.FilteredData)
|
totalRows := len(m.Shared.FilteredData)
|
||||||
startIdx := 0
|
startIdx := 0
|
||||||
|
|
||||||
@@ -420,7 +420,7 @@ func (m *TableDataModel) View() string {
|
|||||||
startIdx = min(startIdx, totalRows-visibleCount)
|
startIdx = min(startIdx, totalRows-visibleCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
endIdx := Min(totalRows, startIdx+visibleCount)
|
endIdx := min(totalRows, startIdx+visibleCount)
|
||||||
|
|
||||||
for i := startIdx; i < endIdx; i++ {
|
for i := startIdx; i < endIdx; i++ {
|
||||||
row := m.Shared.FilteredData[i]
|
row := m.Shared.FilteredData[i]
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
|
||||||
"github.com/charmbracelet/bubbles/help"
|
"github.com/charmbracelet/bubbles/help"
|
||||||
"github.com/charmbracelet/bubbles/key"
|
"github.com/charmbracelet/bubbles/key"
|
||||||
"github.com/charmbracelet/bubbles/textinput"
|
"github.com/charmbracelet/bubbles/textinput"
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TableListModel struct {
|
type TableListModel struct {
|
||||||
@@ -356,7 +356,7 @@ func (m *TableListModel) getVisibleCount() int {
|
|||||||
if m.searching {
|
if m.searching {
|
||||||
reservedLines += 2
|
reservedLines += 2
|
||||||
}
|
}
|
||||||
return Max(1, m.Shared.Height-reservedLines)
|
return max(1, m.Shared.Height-reservedLines)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *TableListModel) adjustPage() {
|
func (m *TableListModel) adjustPage() {
|
||||||
@@ -389,7 +389,7 @@ func (m *TableListModel) View() string {
|
|||||||
} else {
|
} else {
|
||||||
visibleCount := m.getVisibleCount()
|
visibleCount := m.getVisibleCount()
|
||||||
startIdx := m.currentPage * visibleCount
|
startIdx := m.currentPage * visibleCount
|
||||||
endIdx := Min(startIdx+visibleCount, len(m.Shared.FilteredTables))
|
endIdx := min(startIdx+visibleCount, len(m.Shared.FilteredTables))
|
||||||
|
|
||||||
for i := startIdx; i < endIdx; i++ {
|
for i := startIdx; i < endIdx; i++ {
|
||||||
table := m.Shared.FilteredTables[i]
|
table := m.Shared.FilteredTables[i]
|
||||||
|
|||||||
Reference in New Issue
Block a user