diff --git a/cmd/cheatmd/main.go b/cmd/cheatmd/main.go index 1173ea3..b756760 100644 --- a/cmd/cheatmd/main.go +++ b/cmd/cheatmd/main.go @@ -9,10 +9,10 @@ import ( "time" "github.com/charmbracelet/lipgloss" - "github.com/gubarz/cheatmd/internal/config" - "github.com/gubarz/cheatmd/internal/executor" - "github.com/gubarz/cheatmd/internal/linter" - "github.com/gubarz/cheatmd/internal/parser" + "github.com/gubarz/cheatmd/pkg/config" + "github.com/gubarz/cheatmd/pkg/executor" + "github.com/gubarz/cheatmd/pkg/linter" + "github.com/gubarz/cheatmd/pkg/parser" "github.com/gubarz/cheatmd/internal/ui" "github.com/spf13/cobra" "github.com/spf13/viper" diff --git a/go.mod b/go.mod index e0b2773..1cd89f2 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.26.3 require ( github.com/charmbracelet/bubbles v1.0.0 github.com/charmbracelet/bubbletea v1.3.10 + github.com/charmbracelet/glamour v1.0.0 github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 github.com/spf13/cobra v1.10.2 github.com/spf13/viper v1.21.0 @@ -16,7 +17,6 @@ require ( github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/charmbracelet/colorprofile v0.4.1 // indirect - github.com/charmbracelet/glamour v1.0.0 // indirect github.com/charmbracelet/x/ansi v0.11.6 // indirect github.com/charmbracelet/x/cellbuf v0.0.15 // indirect github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf // indirect diff --git a/go.sum b/go.sum index b9cf582..3a1137a 100644 --- a/go.sum +++ b/go.sum @@ -1,39 +1,35 @@ +github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= +github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/chroma/v2 v2.20.0 h1:sfIHpxPyR07/Oylvmcai3X/exDlE8+FA820NTz+9sGw= github.com/alecthomas/chroma/v2 v2.20.0/go.mod h1:e7tViK0xh/Nf4BYHl00ycY6rV7b8iXBksI9E359yNmA= +github.com/alecthomas/repr v0.5.1 h1:E3G4t2QbHTSNpPKBgMTln5KLkZHLOcU7r37J4pXBuIg= +github.com/alecthomas/repr v0.5.1/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= 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/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3vj1nolY= +github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= -github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs= -github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg= 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/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw= github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4= -github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= -github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= github.com/charmbracelet/colorprofile v0.4.1 h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco3ej+ngLk1kk= github.com/charmbracelet/colorprofile v0.4.1/go.mod h1:U1d9Dljmdf9DLegaJ0nGZNJvoXAhayhmidOdcBwAvKk= github.com/charmbracelet/glamour v1.0.0 h1:AWMLOVFHTsysl4WV8T8QgkQ0s/ZNZo7CiE4WKhk8l08= github.com/charmbracelet/glamour v1.0.0/go.mod h1:DSdohgOBkMr2ZQNhw4LZxSGpx3SvpeujNoXrQyH2hxo= -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.1-0.20250404203927-76690c660834 h1:ZR7e0ro+SZZiIZD7msJyA+NjkCNNavuiPBLgerbOziE= github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834/go.mod h1:aKC/t2arECF6rNOnaKaVU6y4t4ZeHQzqfxedE/VkVhA= -github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ= -github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE= 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/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8= -github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= 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/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ= +github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf h1:rLG0Yb6MQSDKdB52aGX55JT1oi0P0Kuaj7wi1bLUpnI= github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf/go.mod h1:B3UgsnsBZS/eX42BlaNiJkD1pPOUa+oF1IYC6Yd2CEU= -github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= -github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk= github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI= github.com/clipperhouse/displaywidth v0.9.0 h1:Qb4KOhYwRiN3viMv1v/3cTBlz3AcAZX3+y9OLhMtAtA= @@ -59,14 +55,14 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= -github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag= github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -74,8 +70,6 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D 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-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= -github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= @@ -126,9 +120,8 @@ github.com/yuin/goldmark-emoji v1.0.6 h1:QWfF2FYaXwL74tfGOW5izeiZepUDroDJfWubQI9 github.com/yuin/goldmark-emoji v1.0.6/go.mod h1:ukxJDKFpdFb5x0a5HqbdlcKtebh086iJpI31LTKmWuA= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= -golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -137,8 +130,6 @@ golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/ui/cheat_select.go b/internal/ui/cheat_select.go index 9dd8e6f..3cb4bae 100644 --- a/internal/ui/cheat_select.go +++ b/internal/ui/cheat_select.go @@ -9,8 +9,8 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" - "github.com/gubarz/cheatmd/internal/config" - "github.com/gubarz/cheatmd/internal/parser" + "github.com/gubarz/cheatmd/pkg/config" + "github.com/gubarz/cheatmd/pkg/parser" ) // ============================================================================ diff --git a/internal/ui/history_view.go b/internal/ui/history_view.go index 99deb49..dbea048 100644 --- a/internal/ui/history_view.go +++ b/internal/ui/history_view.go @@ -6,9 +6,9 @@ import ( tea "github.com/charmbracelet/bubbletea" - "github.com/gubarz/cheatmd/internal/config" - "github.com/gubarz/cheatmd/internal/history" - "github.com/gubarz/cheatmd/internal/parser" + "github.com/gubarz/cheatmd/pkg/config" + "github.com/gubarz/cheatmd/pkg/history" + "github.com/gubarz/cheatmd/pkg/parser" ) // historyState holds the overlay state for the execution-history picker. diff --git a/internal/ui/infer_test.go b/internal/ui/infer_test.go index 977d483..6d89e8e 100644 --- a/internal/ui/infer_test.go +++ b/internal/ui/infer_test.go @@ -3,7 +3,7 @@ package ui import ( "testing" - "github.com/gubarz/cheatmd/internal/parser" + "github.com/gubarz/cheatmd/pkg/parser" ) func TestExtractEmbeddedVars(t *testing.T) { diff --git a/internal/ui/main_model.go b/internal/ui/main_model.go index 1214f39..081267d 100644 --- a/internal/ui/main_model.go +++ b/internal/ui/main_model.go @@ -7,8 +7,8 @@ import ( "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" - "github.com/gubarz/cheatmd/internal/executor" - "github.com/gubarz/cheatmd/internal/parser" + "github.com/gubarz/cheatmd/pkg/executor" + "github.com/gubarz/cheatmd/pkg/parser" ) // Executor defines the interface required by the UI for command execution and resolution. diff --git a/internal/ui/match.go b/internal/ui/match.go index 0850308..3ce4d18 100644 --- a/internal/ui/match.go +++ b/internal/ui/match.go @@ -4,9 +4,9 @@ import ( "regexp" "strings" - "github.com/gubarz/cheatmd/internal/config" - "github.com/gubarz/cheatmd/internal/executor" - "github.com/gubarz/cheatmd/internal/parser" + "github.com/gubarz/cheatmd/pkg/config" + "github.com/gubarz/cheatmd/pkg/executor" + "github.com/gubarz/cheatmd/pkg/parser" ) // findMatchingCheat finds a cheat whose command pattern matches the input. diff --git a/internal/ui/match_test.go b/internal/ui/match_test.go index 183e95a..def98da 100644 --- a/internal/ui/match_test.go +++ b/internal/ui/match_test.go @@ -3,7 +3,7 @@ package ui import ( "testing" - "github.com/gubarz/cheatmd/internal/parser" + "github.com/gubarz/cheatmd/pkg/parser" ) func TestBuildMatchPattern(t *testing.T) { diff --git a/internal/ui/preview.go b/internal/ui/preview.go index 203126e..7e2a232 100644 --- a/internal/ui/preview.go +++ b/internal/ui/preview.go @@ -10,8 +10,8 @@ import ( "github.com/charmbracelet/glamour" "github.com/charmbracelet/glamour/ansi" - "github.com/gubarz/cheatmd/internal/config" - "github.com/gubarz/cheatmd/internal/parser" + "github.com/gubarz/cheatmd/pkg/config" + "github.com/gubarz/cheatmd/pkg/parser" ) // previewOverlayState holds the state for the markdown preview overlay. diff --git a/internal/ui/preview_test.go b/internal/ui/preview_test.go index ba8f470..713730c 100644 --- a/internal/ui/preview_test.go +++ b/internal/ui/preview_test.go @@ -3,7 +3,7 @@ package ui import ( "testing" - "github.com/gubarz/cheatmd/internal/parser" + "github.com/gubarz/cheatmd/pkg/parser" ) func TestFindCheatHeaderSourceLineSkipsSameNamedPageHeader(t *testing.T) { diff --git a/internal/ui/resolve.go b/internal/ui/resolve.go index d53bd3f..59dec73 100644 --- a/internal/ui/resolve.go +++ b/internal/ui/resolve.go @@ -7,9 +7,9 @@ import ( "regexp" "strings" - "github.com/gubarz/cheatmd/internal/config" - "github.com/gubarz/cheatmd/internal/executor" - "github.com/gubarz/cheatmd/internal/parser" + "github.com/gubarz/cheatmd/pkg/config" + "github.com/gubarz/cheatmd/pkg/executor" + "github.com/gubarz/cheatmd/pkg/parser" ) // ============================================================================ diff --git a/internal/ui/run.go b/internal/ui/run.go index b5392e2..f3110c9 100644 --- a/internal/ui/run.go +++ b/internal/ui/run.go @@ -9,9 +9,9 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" - "github.com/gubarz/cheatmd/internal/config" - "github.com/gubarz/cheatmd/internal/history" - "github.com/gubarz/cheatmd/internal/parser" + "github.com/gubarz/cheatmd/pkg/config" + "github.com/gubarz/cheatmd/pkg/history" + "github.com/gubarz/cheatmd/pkg/parser" ) // recordRun appends one entry to the history file. Errors are silently diff --git a/internal/ui/styles.go b/internal/ui/styles.go index 607f489..6350d6f 100644 --- a/internal/ui/styles.go +++ b/internal/ui/styles.go @@ -2,7 +2,7 @@ package ui import ( "github.com/charmbracelet/lipgloss" - "github.com/gubarz/cheatmd/internal/config" + "github.com/gubarz/cheatmd/pkg/config" ) // StyleManager encapsulates all TUI styles and provides methods for style operations diff --git a/internal/ui/substitute_search.go b/internal/ui/substitute_search.go index 2defa30..075f868 100644 --- a/internal/ui/substitute_search.go +++ b/internal/ui/substitute_search.go @@ -6,7 +6,7 @@ import ( tea "github.com/charmbracelet/bubbletea" - "github.com/gubarz/cheatmd/internal/config" + "github.com/gubarz/cheatmd/pkg/config" ) // substituteSearchState holds the overlay state for substitute search. diff --git a/internal/ui/var_resolve.go b/internal/ui/var_resolve.go index 73b2c76..1e7277d 100644 --- a/internal/ui/var_resolve.go +++ b/internal/ui/var_resolve.go @@ -8,8 +8,8 @@ import ( "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" - "github.com/gubarz/cheatmd/internal/config" - "github.com/gubarz/cheatmd/internal/executor" + "github.com/gubarz/cheatmd/pkg/config" + "github.com/gubarz/cheatmd/pkg/executor" ) // ============================================================================ diff --git a/internal/config/config.go b/pkg/config/config.go similarity index 100% rename from internal/config/config.go rename to pkg/config/config.go diff --git a/internal/executor/executor.go b/pkg/executor/executor.go similarity index 98% rename from internal/executor/executor.go rename to pkg/executor/executor.go index 4af743e..4430126 100644 --- a/internal/executor/executor.go +++ b/pkg/executor/executor.go @@ -8,8 +8,8 @@ import ( "sort" "strings" - "github.com/gubarz/cheatmd/internal/config" - "github.com/gubarz/cheatmd/internal/parser" + "github.com/gubarz/cheatmd/pkg/config" + "github.com/gubarz/cheatmd/pkg/parser" ) // sortedNames returns variable names sorted longest-first to prevent diff --git a/internal/executor/executor_test.go b/pkg/executor/executor_test.go similarity index 98% rename from internal/executor/executor_test.go rename to pkg/executor/executor_test.go index b547a1f..07547d5 100644 --- a/internal/executor/executor_test.go +++ b/pkg/executor/executor_test.go @@ -3,7 +3,7 @@ package executor import ( "testing" - "github.com/gubarz/cheatmd/internal/parser" + "github.com/gubarz/cheatmd/pkg/parser" ) // mockClipboard implements Clipboard interface for testing diff --git a/internal/history/history.go b/pkg/history/history.go similarity index 100% rename from internal/history/history.go rename to pkg/history/history.go diff --git a/internal/linter/linter.go b/pkg/linter/linter.go similarity index 99% rename from internal/linter/linter.go rename to pkg/linter/linter.go index ad7c3dc..34eb543 100644 --- a/internal/linter/linter.go +++ b/pkg/linter/linter.go @@ -23,7 +23,7 @@ import ( "sort" "strings" - "github.com/gubarz/cheatmd/internal/parser" + "github.com/gubarz/cheatmd/pkg/parser" ) // Severity classifies a finding. diff --git a/internal/linter/linter_test.go b/pkg/linter/linter_test.go similarity index 100% rename from internal/linter/linter_test.go rename to pkg/linter/linter_test.go diff --git a/internal/parser/dsl.go b/pkg/parser/dsl.go similarity index 100% rename from internal/parser/dsl.go rename to pkg/parser/dsl.go diff --git a/internal/parser/lex.go b/pkg/parser/lex.go similarity index 100% rename from internal/parser/lex.go rename to pkg/parser/lex.go diff --git a/internal/parser/parser.go b/pkg/parser/parser.go similarity index 90% rename from internal/parser/parser.go rename to pkg/parser/parser.go index 7cb0f88..0321e73 100644 --- a/internal/parser/parser.go +++ b/pkg/parser/parser.go @@ -68,67 +68,53 @@ func parseFilesParallel(files []string) []parseResult { numFiles := len(files) estimatedCheats := max(numFiles*35, 1000) - // Stage 1: Parallel I/O - read raw bytes - type fileData struct { - path string - data []byte - } - fileDataChan := make(chan fileData, numFiles) - fileChan := make(chan string, numFiles) - - var ioWg sync.WaitGroup - ioWorkers := min(numWorkers*2, numFiles) - for w := 0; w < ioWorkers; w++ { - ioWg.Add(1) - go func() { - defer ioWg.Done() - for path := range fileChan { - if data, err := os.ReadFile(path); err == nil && len(data) > 0 { - fileDataChan <- fileData{path: path, data: data} - } - } - }() - } - - go func() { - for _, path := range files { - fileChan <- path - } - close(fileChan) - ioWg.Wait() - close(fileDataChan) - }() - - // Stage 2: Parallel parsing - parse from raw bytes resultChan := make(chan parseResult, numWorkers) var parseWg sync.WaitGroup + chunkSize := (numFiles + numWorkers - 1) / numWorkers + if chunkSize == 0 { + chunkSize = 1 + } + for w := 0; w < numWorkers; w++ { + start := w * chunkSize + if start >= numFiles { + break + } + end := start + chunkSize + if end > numFiles { + end = numFiles + } + + chunk := files[start:end] parseWg.Add(1) - go func() { + + go func(fileChunk []string) { defer parseWg.Done() localParser := NewParser() localCheats := make([]*Cheat, 0, estimatedCheats/numWorkers) localModules := make(map[string]*Module) var localDuplicates []DuplicateExport - for fd := range fileDataChan { - localParser.index = NewCheatIndex() - localParser.parseLines(fd.path, fd.data) - localCheats = append(localCheats, localParser.index.Cheats...) - for name, mod := range localParser.index.Modules { - if existing, ok := localModules[name]; ok { - localDuplicates = append(localDuplicates, DuplicateExport{ - Name: name, - File1: existing.File, - File2: mod.File, - }) + for _, path := range fileChunk { + if data, err := os.ReadFile(path); err == nil && len(data) > 0 { + localParser.index = NewCheatIndex() + localParser.parseLines(path, data) + localCheats = append(localCheats, localParser.index.Cheats...) + for name, mod := range localParser.index.Modules { + if existing, ok := localModules[name]; ok { + localDuplicates = append(localDuplicates, DuplicateExport{ + Name: name, + File1: existing.File, + File2: mod.File, + }) + } + localModules[name] = mod } - localModules[name] = mod } } resultChan <- parseResult{cheats: localCheats, modules: localModules, duplicates: localDuplicates} - }() + }(chunk) } go func() { @@ -291,13 +277,7 @@ func (p *Parser) parseLines(path string, data []byte) { } func countNewlines(b []byte) int { - count := 0 - for _, c := range b { - if c == '\n' { - count++ - } - } - return count + return bytes.Count(b, []byte{'\n'}) } // parseLine processes a single line (as bytes, no allocation) diff --git a/internal/parser/parser_test.go b/pkg/parser/parser_test.go similarity index 100% rename from internal/parser/parser_test.go rename to pkg/parser/parser_test.go diff --git a/internal/parser/tags.go b/pkg/parser/tags.go similarity index 100% rename from internal/parser/tags.go rename to pkg/parser/tags.go diff --git a/internal/parser/types.go b/pkg/parser/types.go similarity index 100% rename from internal/parser/types.go rename to pkg/parser/types.go