diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..dd84ea7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..bbcbbe7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d944991..c0e481b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,57 +1,20 @@ version: 2 - updates: - package-ecosystem: "gomod" directory: "/" schedule: - interval: "weekly" - day: "monday" - time: "05:00" - timezone: "America/New_York" + interval: "daily" labels: - "dependencies" commit-message: prefix: "chore" include: "scope" - groups: - all: - patterns: - - "*" - ignore: - - dependency-name: github.com/charmbracelet/bubbletea/v2 - versions: - - v2.0.0-beta1 - - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "weekly" - day: "monday" - time: "05:00" - timezone: "America/New_York" + interval: "daily" labels: - "dependencies" commit-message: prefix: "chore" include: "scope" - groups: - all: - patterns: - - "*" - - - package-ecosystem: "docker" - directory: "/" - schedule: - interval: "weekly" - day: "monday" - time: "05:00" - timezone: "America/New_York" - labels: - - "dependencies" - commit-message: - prefix: "chore" - include: "scope" - groups: - all: - patterns: - - "*" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2330819..5be3fe8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,13 +1,53 @@ name: build -on: - push: - branches: - - main - pull_request: +on: [push, pull_request] jobs: build: - uses: charmbracelet/meta/.github/workflows/build.yml@main + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + env: + GO111MODULE: "on" + steps: + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version: ~1.21 + + - name: Checkout code + uses: actions/checkout@v4 + + - name: Download Go modules + run: go mod download + + - name: Build + run: go build -v ./... + + - name: Test + run: go test -v -cover -timeout=30s ./... + + snapshot: + uses: charmbracelet/meta/.github/workflows/snapshot.yml@main secrets: - gh_pat: ${{ secrets.PERSONAL_ACCESS_TOKEN }} + goreleaser_key: ${{ secrets.GORELEASER_KEY }} + + dependabot: + needs: [build] + runs-on: ubuntu-latest + permissions: + pull-requests: write + contents: write + if: ${{ github.actor == 'dependabot[bot]' && github.event_name == 'pull_request'}} + steps: + - id: metadata + uses: dependabot/fetch-metadata@v2 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + - run: | + gh pr review --approve "$PR_URL" + gh pr merge --squash --auto "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/dependabot-sync.yml b/.github/workflows/dependabot-sync.yml deleted file mode 100644 index 9b08259..0000000 --- a/.github/workflows/dependabot-sync.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: dependabot-sync -on: - schedule: - - cron: "0 0 * * 0" # every Sunday at midnight - workflow_dispatch: # allows manual triggering - -permissions: - contents: write - pull-requests: write - -jobs: - dependabot-sync: - uses: charmbracelet/meta/.github/workflows/dependabot-sync.yml@main - with: - repo_name: ${{ github.event.repository.name }} - secrets: - gh_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} diff --git a/.github/workflows/lint-soft.yml b/.github/workflows/lint-soft.yml new file mode 100644 index 0000000..87d1e1f --- /dev/null +++ b/.github/workflows/lint-soft.yml @@ -0,0 +1,28 @@ +name: lint-soft +on: + push: + pull_request: + +permissions: + contents: read + # Optional: allow read access to pull request. Use with `only-new-issues` option. + pull-requests: read + +jobs: + golangci: + name: lint-soft + runs-on: ubuntu-latest + steps: + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version: ^1 + + - uses: actions/checkout@v4 + - name: golangci-lint + uses: golangci/golangci-lint-action@v6 + with: + # Optional: golangci-lint command line arguments. + args: --config .golangci-soft.yml --issues-exit-code=0 + # Optional: show only new issues if it's a pull request. The default value is `false`. + only-new-issues: true diff --git a/.github/workflows/lint-sync.yml b/.github/workflows/lint-sync.yml deleted file mode 100644 index ecf8580..0000000 --- a/.github/workflows/lint-sync.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: lint-sync -on: - schedule: - # every Sunday at midnight - - cron: "0 0 * * 0" - workflow_dispatch: # allows manual triggering - -permissions: - contents: write - pull-requests: write - -jobs: - lint: - uses: charmbracelet/meta/.github/workflows/lint-sync.yml@main diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a1d6d0e..f617a5a 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -3,6 +3,26 @@ on: push: pull_request: +permissions: + contents: read + # Optional: allow read access to pull request. Use with `only-new-issues` option. + pull-requests: read + jobs: - lint: - uses: charmbracelet/meta/.github/workflows/lint.yml@main + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version: ^1 + + - uses: actions/checkout@v4 + - name: golangci-lint + uses: golangci/golangci-lint-action@v6 + with: + # Optional: golangci-lint command line arguments. + #args: + # Optional: show only new issues if it's a pull request. The default value is `false`. + only-new-issues: true diff --git a/.golangci-soft.yml b/.golangci-soft.yml new file mode 100644 index 0000000..82c337e --- /dev/null +++ b/.golangci-soft.yml @@ -0,0 +1,39 @@ +run: + tests: false + +issues: + include: + - EXC0001 + - EXC0005 + - EXC0011 + - EXC0012 + - EXC0013 + + max-issues-per-linter: 0 + max-same-issues: 0 + +linters: + enable: + - exhaustive + - goconst + - godot + - godox + - mnd + - gomoddirectives + - goprintffuncname + - misspell + - nakedret + - nestif + - noctx + - nolintlint + - prealloc + + # disable default linters, they are already enabled in .golangci.yml + disable: + - wrapcheck + - errcheck + - gosimple + - govet + - ineffassign + - staticcheck + - typecheck diff --git a/.golangci.yml b/.golangci.yml index c90f031..684d54b 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,22 +1,24 @@ -version: "2" run: tests: false + +issues: + include: + - EXC0001 + - EXC0005 + - EXC0011 + - EXC0012 + - EXC0013 + + max-issues-per-linter: 0 + max-same-issues: 0 + linters: enable: - bodyclose - - exhaustive - - goconst - - godot - - gomoddirectives - - goprintffuncname + - goimports - gosec - - misspell - - nakedret - - nestif - nilerr - - noctx - - nolintlint - - prealloc + - predeclared - revive - rowserrcheck - sqlclosecheck @@ -24,24 +26,3 @@ linters: - unconvert - unparam - whitespace - - wrapcheck - exclusions: - rules: - - text: '(slog|log)\.\w+' - linters: - - noctx - generated: lax - presets: - - common-false-positives - settings: - exhaustive: - default-signifies-exhaustive: true -issues: - max-issues-per-linter: 0 - max-same-issues: 0 -formatters: - enable: - - gofumpt - - goimports - exclusions: - generated: lax diff --git a/README.md b/README.md index 80a2b67..a538089 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ The above example is running from a single shell script ([source](./examples/dem ## Tutorial Gum provides highly configurable, ready-to-use utilities to help you write -useful shell scripts and dotfile aliases with just a few lines of code. +useful shell scripts and dotfiles aliases with just a few lines of code. Let's build a simple script to help you write [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/#summary) for your dotfiles. @@ -68,9 +68,6 @@ brew install gum # Arch Linux (btw) pacman -S gum -# Fedora or EPEL 10 -dnf install gum - # Nix nix-env -iA nixpkgs.gum @@ -116,19 +113,6 @@ sudo zypper install gum -
-FreeBSD - -```bash -# packages -sudo pkg install gum - -# ports -cd /usr/ports/devel/gum && sudo make install clean -``` - -
- Or download it: - [Packages][releases] are available in Debian, RPM, and Alpine formats @@ -267,7 +251,7 @@ gum confirm && rm file.txt || echo "File not removed" Prompt the user to select a file from the file tree. ```bash -$EDITOR $(gum file $HOME) +EDITOR $(gum file $HOME) ``` Shell running gum file @@ -460,12 +444,6 @@ gum filter < $HISTFILE --height 20 alias please="gum input --password | sudo -nS" ``` -## Contributing - -See [contributing][contribute]. - -[contribute]: https://github.com/charmbracelet/gum/contribute - ## Feedback We’d love to hear your thoughts on this project. Feel free to drop us a note! diff --git a/choose/choose.go b/choose/choose.go index c6be614..6594676 100644 --- a/choose/choose.go +++ b/choose/choose.go @@ -18,7 +18,6 @@ import ( "github.com/charmbracelet/bubbles/paginator" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" - "github.com/charmbracelet/x/exp/ordered" ) func defaultKeymap() keymap { @@ -98,7 +97,6 @@ func (k keymap) ShortHelp() []key.Binding { type model struct { height int - padding []int cursor string selectedPrefix string unselectedPrefix string @@ -159,10 +157,10 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.paginator.PrevPage() } case key.Matches(msg, km.Right): - m.index = ordered.Clamp(m.index+m.height, 0, len(m.items)-1) + m.index = clamp(m.index+m.height, 0, len(m.items)-1) m.paginator.NextPage() case key.Matches(msg, km.Left): - m.index = ordered.Clamp(m.index-m.height, 0, len(m.items)-1) + m.index = clamp(m.index-m.height, 0, len(m.items)-1) m.paginator.PrevPage() case key.Matches(msg, km.End): m.index = len(m.items) - 1 @@ -279,11 +277,18 @@ func (m model) View() string { } parts = append(parts, s.String()) if m.showHelp { - parts = append(parts, "", m.help.View(m.keymap)) + parts = append(parts, m.help.View(m.keymap)) } - view := lipgloss.JoinVertical(lipgloss.Left, parts...) - return lipgloss.NewStyle(). - Padding(m.padding...). - Render(view) + return lipgloss.JoinVertical(lipgloss.Left, parts...) +} + +func clamp(x, low, high int) int { + if x < low { + return low + } + if x > high { + return high + } + return x } diff --git a/choose/command.go b/choose/command.go index 70b8a9f..47e3549 100644 --- a/choose/command.go +++ b/choose/command.go @@ -3,6 +3,7 @@ package choose import ( "errors" "fmt" + "maps" "os" "slices" "sort" @@ -14,7 +15,6 @@ import ( "github.com/charmbracelet/gum/internal/stdin" "github.com/charmbracelet/gum/internal/timeout" "github.com/charmbracelet/gum/internal/tty" - "github.com/charmbracelet/gum/style" "github.com/charmbracelet/lipgloss" ) @@ -38,8 +38,6 @@ func (o Options) Run() error { // normalize options into a map options := map[string]string{} - // keep the labels in the user-provided order - var labels []string //nolint:prealloc for _, opt := range o.Options { if o.LabelDelimiter == "" { options[opt] = opt @@ -49,11 +47,10 @@ func (o Options) Run() error { if !ok { return fmt.Errorf("invalid option format: %q", opt) } - labels = append(labels, label) options[label] = value } if o.LabelDelimiter != "" { - o.Options = labels + o.Options = slices.Collect(maps.Keys(options)) } if o.SelectIfOne && len(o.Options) == 1 { @@ -108,7 +105,6 @@ func (o Options) Run() error { // Use the pagination model to display the current and total number of // pages. - top, right, bottom, left := style.ParsePadding(o.Padding) pager := paginator.New() pager.SetTotalPages((len(items) + o.Height - 1) / o.Height) pager.PerPage = o.Height @@ -130,7 +126,6 @@ func (o Options) Run() error { index: startingIndex, currentOrder: currentOrder, height: o.Height, - padding: []int{top, right, bottom, left}, cursor: o.Cursor, header: o.Header, selectedPrefix: o.SelectedPrefix, diff --git a/choose/options.go b/choose/options.go index abfca22..e5ea6b2 100644 --- a/choose/options.go +++ b/choose/options.go @@ -15,18 +15,17 @@ type Options struct { Height int `help:"Height of the list" default:"10" env:"GUM_CHOOSE_HEIGHT"` Cursor string `help:"Prefix to show on item that corresponds to the cursor position" default:"> " env:"GUM_CHOOSE_CURSOR"` ShowHelp bool `help:"Show help keybinds" default:"true" negatable:"" env:"GUM_CHOOSE_SHOW_HELP"` - Timeout time.Duration `help:"Timeout until choose returns selected element" default:"0s" env:"GUM_CHOOSE_TIMEOUT"` // including timeout command options [Timeout,...] + Timeout time.Duration `help:"Timeout until choose returns selected element" default:"0s" env:"GUM_CCHOOSE_TIMEOUT"` // including timeout command options [Timeout,...] Header string `help:"Header value" default:"Choose:" env:"GUM_CHOOSE_HEADER"` CursorPrefix string `help:"Prefix to show on the cursor item (hidden if limit is 1)" default:"• " env:"GUM_CHOOSE_CURSOR_PREFIX"` SelectedPrefix string `help:"Prefix to show on selected items (hidden if limit is 1)" default:"✓ " env:"GUM_CHOOSE_SELECTED_PREFIX"` UnselectedPrefix string `help:"Prefix to show on unselected items (hidden if limit is 1)" default:"• " env:"GUM_CHOOSE_UNSELECTED_PREFIX"` - Selected []string `help:"Options that should start as selected (selects all if given *)" default:"" env:"GUM_CHOOSE_SELECTED"` + Selected []string `help:"Options that should start as selected (selects all if given '*')" default:"" env:"GUM_CHOOSE_SELECTED"` SelectIfOne bool `help:"Select the given option if there is only one" group:"Selection"` InputDelimiter string `help:"Option delimiter when reading from STDIN" default:"\n" env:"GUM_CHOOSE_INPUT_DELIMITER"` OutputDelimiter string `help:"Option delimiter when writing to STDOUT" default:"\n" env:"GUM_CHOOSE_OUTPUT_DELIMITER"` LabelDelimiter string `help:"Allows to set a delimiter, so options can be set as label:value" default:"" env:"GUM_CHOOSE_LABEL_DELIMITER"` StripANSI bool `help:"Strip ANSI sequences when reading from STDIN" default:"true" negatable:"" env:"GUM_CHOOSE_STRIP_ANSI"` - Padding string `help:"Padding" default:"${defaultPadding}" group:"Style Flags" env:"GUM_CHOOSE_PADDING"` CursorStyle style.Styles `embed:"" prefix:"cursor." set:"defaultForeground=212" envprefix:"GUM_CHOOSE_CURSOR_"` HeaderStyle style.Styles `embed:"" prefix:"header." set:"defaultForeground=99" envprefix:"GUM_CHOOSE_HEADER_"` diff --git a/completion/bash.go b/completion/bash.go index 33d309b..3064ec1 100644 --- a/completion/bash.go +++ b/completion/bash.go @@ -1,5 +1,3 @@ -// Package completion provides a bash completion generator for Kong -// applications. package completion import ( @@ -630,7 +628,6 @@ func writeCmdAliases(buf io.StringWriter, cmd *kong.Node) { writeString(buf, ` fi`) writeString(buf, "\n") } - func writeArgAliases(buf io.StringWriter, cmd *kong.Node) { writeString(buf, " noun_aliases=()\n") sort.Strings(cmd.Aliases) diff --git a/completion/fish.go b/completion/fish.go index 9fc80d2..8335356 100644 --- a/completion/fish.go +++ b/completion/fish.go @@ -79,7 +79,7 @@ func (f Fish) gen(buf io.StringWriter, cmd *kong.Node) { _, _ = buf.WriteString(fmt.Sprintf(" -s %c", f.Short)) } _, _ = buf.WriteString(fmt.Sprintf(" -l %s", f.Name)) - _, _ = buf.WriteString(fmt.Sprintf(" -d \"%s\"", f.Help)) + _, _ = buf.WriteString(fmt.Sprintf(" -d '%s'", f.Help)) _, _ = buf.WriteString("\n") } _, _ = buf.WriteString("\n") diff --git a/confirm/command.go b/confirm/command.go index fc0a02b..ed5cf6e 100644 --- a/confirm/command.go +++ b/confirm/command.go @@ -1,7 +1,6 @@ package confirm import ( - "context" "fmt" "os" @@ -10,7 +9,6 @@ import ( "github.com/charmbracelet/gum/internal/exit" "github.com/charmbracelet/gum/internal/stdin" "github.com/charmbracelet/gum/internal/timeout" - "github.com/charmbracelet/gum/style" ) // Run provides a shell script interface for prompting a user to confirm an @@ -29,7 +27,6 @@ func (o Options) Run() error { ctx, cancel := timeout.Context(o.Timeout) defer cancel() - top, right, bottom, left := style.ParsePadding(o.Padding) m := model{ affirmative: o.Affirmative, negative: o.Negative, @@ -43,17 +40,15 @@ func (o Options) Run() error { selectedStyle: o.SelectedStyle.ToLipgloss(), unselectedStyle: o.UnselectedStyle.ToLipgloss(), promptStyle: o.PromptStyle.ToLipgloss(), - padding: []int{top, right, bottom, left}, } tm, err := tea.NewProgram( m, tea.WithOutput(os.Stderr), tea.WithContext(ctx), ).Run() - if err != nil && ctx.Err() != context.DeadlineExceeded { + if err != nil { return fmt.Errorf("unable to confirm: %w", err) } - m = tm.(model) if o.ShowOutput { confirmationText := m.negative @@ -63,6 +58,7 @@ func (o Options) Run() error { fmt.Println(m.prompt, confirmationText) } + m = tm.(model) if m.confirmation { return nil } diff --git a/confirm/confirm.go b/confirm/confirm.go index ac35c39..c32ade9 100644 --- a/confirm/confirm.go +++ b/confirm/confirm.go @@ -91,7 +91,6 @@ type model struct { promptStyle lipgloss.Style selectedStyle lipgloss.Style unselectedStyle lipgloss.Style - padding []int } func (m model) Init() tea.Cmd { return nil } @@ -150,19 +149,18 @@ func (m model) View() string { neg = "" } - parts := []string{ - m.promptStyle.Render(m.prompt) + "\n", - lipgloss.JoinHorizontal(lipgloss.Left, aff, neg), - } - if m.showHelp { - parts = append(parts, "", m.help.View(m.keys)) + return lipgloss.JoinVertical( + lipgloss.Left, + m.promptStyle.Render(m.prompt)+"\n", + lipgloss.JoinHorizontal(lipgloss.Left, aff, neg), + "\n"+m.help.View(m.keys), + ) } - return lipgloss.NewStyle(). - Padding(m.padding...). - Render(lipgloss.JoinVertical( - lipgloss.Left, - parts..., - )) + return lipgloss.JoinVertical( + lipgloss.Left, + m.promptStyle.Render(m.prompt)+"\n", + lipgloss.JoinHorizontal(lipgloss.Left, aff, neg), + ) } diff --git a/confirm/options.go b/confirm/options.go index 9740885..b38d7e0 100644 --- a/confirm/options.go +++ b/confirm/options.go @@ -21,5 +21,4 @@ type Options struct { UnselectedStyle style.Styles `embed:"" prefix:"unselected." help:"The style of the unselected action" set:"defaultBackground=235" set:"defaultForeground=254" set:"defaultPadding=0 3" set:"defaultMargin=0 1" envprefix:"GUM_CONFIRM_UNSELECTED_"` ShowHelp bool `help:"Show help key binds" negatable:"" default:"true" env:"GUM_CONFIRM_SHOW_HELP"` Timeout time.Duration `help:"Timeout until confirm returns selected value or default if provided" default:"0s" env:"GUM_CONFIRM_TIMEOUT"` - Padding string `help:"Padding" default:"${defaultPadding}" group:"Style Flags" env:"GUM_CONFIRM_PADDING"` } diff --git a/cursor/cursor.go b/cursor/cursor.go index 3c49849..aa49c05 100644 --- a/cursor/cursor.go +++ b/cursor/cursor.go @@ -1,4 +1,3 @@ -// Package cursor provides cursor modes. package cursor import ( diff --git a/default.nix b/default.nix index 511568f..5c14976 100644 --- a/default.nix +++ b/default.nix @@ -2,11 +2,11 @@ pkgs.buildGoModule rec { pname = "gum"; - version = "0.15.2"; + version = "0.15.0"; src = ./.; - vendorHash = "sha256-TK2Fc4bTkiSpyYrg4dJOzamEnii03P7kyHZdah9izqY="; + vendorHash = "sha256-i/KBe41ufYA+tqnB5LCC1geIc2Jnh97JLXcXfBgxdM4="; ldflags = [ "-s" "-w" "-X=main.Version=${version}" ]; } diff --git a/file/command.go b/file/command.go index b7cc546..89472dd 100644 --- a/file/command.go +++ b/file/command.go @@ -10,7 +10,6 @@ import ( "github.com/charmbracelet/bubbles/help" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/gum/internal/timeout" - "github.com/charmbracelet/gum/style" ) // Run is the interface to picking a file. @@ -31,7 +30,7 @@ func (o Options) Run() error { fp := filepicker.New() fp.CurrentDirectory = path fp.Path = path - fp.SetHeight(o.Height) + fp.Height = o.Height fp.AutoHeight = o.Height == 0 fp.Cursor = o.Cursor fp.DirAllowed = o.Directory @@ -47,10 +46,8 @@ func (o Options) Run() error { fp.Styles.Permission = o.PermissionsStyle.ToLipgloss() fp.Styles.Selected = o.SelectedStyle.ToLipgloss() fp.Styles.FileSize = o.FileSizeStyle.ToLipgloss() - top, right, bottom, left := style.ParsePadding(o.Padding) m := model{ filepicker: fp, - padding: []int{top, right, bottom, left}, showHelp: o.ShowHelp, help: help.New(), keymap: defaultKeymap(), diff --git a/file/file.go b/file/file.go index 33c8233..a9878af 100644 --- a/file/file.go +++ b/file/file.go @@ -59,7 +59,6 @@ type model struct { selectedPath string quitting bool showHelp bool - padding []int help help.Model keymap keymap } @@ -69,11 +68,9 @@ func (m model) Init() tea.Cmd { return m.filepicker.Init() } func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.WindowSizeMsg: - height := msg.Height - m.padding[0] - m.padding[2] if m.showHelp { - height -= lipgloss.Height(m.helpView()) + m.filepicker.Height -= lipgloss.Height(m.helpView()) } - m.filepicker.SetHeight(height) case tea.KeyMsg: switch { case key.Matches(msg, keyAbort): @@ -106,12 +103,7 @@ func (m model) View() string { if m.showHelp { parts = append(parts, m.helpView()) } - return lipgloss.NewStyle(). - Padding(m.padding...). - Render(lipgloss.JoinVertical( - lipgloss.Left, - parts..., - )) + return lipgloss.JoinVertical(lipgloss.Left, parts...) } func (m model) helpView() string { diff --git a/file/options.go b/file/options.go index 61c9ced..72bfb8f 100644 --- a/file/options.go +++ b/file/options.go @@ -30,5 +30,4 @@ type Options struct { SelectedStyle style.Styles `embed:"" prefix:"selected." help:"The style to use for the selected item" set:"defaultBold=true" set:"defaultForeground=212" envprefix:"GUM_FILE_SELECTED_"` //nolint:staticcheck FileSizeStyle style.Styles `embed:"" prefix:"file-size." help:"The style to use for file sizes" set:"defaultWidth=8" set:"defaultAlign=right" set:"defaultForeground=240" envprefix:"GUM_FILE_FILE_SIZE_"` //nolint:staticcheck HeaderStyle style.Styles `embed:"" prefix:"header." set:"defaultForeground=99" envprefix:"GUM_FILE_HEADER_"` - Padding string `help:"Padding" default:"${defaultPadding}" group:"Style Flags" env:"GUM_FILE_PADDING"` } diff --git a/filter/command.go b/filter/command.go index 6c9b2a3..4185100 100644 --- a/filter/command.go +++ b/filter/command.go @@ -15,7 +15,6 @@ import ( "github.com/charmbracelet/gum/internal/stdin" "github.com/charmbracelet/gum/internal/timeout" "github.com/charmbracelet/gum/internal/tty" - "github.com/charmbracelet/gum/style" "github.com/charmbracelet/x/ansi" "github.com/sahilm/fuzzy" ) @@ -95,7 +94,7 @@ func (o Options) Run() error { km.ToggleAndNext.SetEnabled(true) km.ToggleAll.SetEnabled(true) } - top, right, bottom, left := style.ParsePadding(o.Padding) + m := model{ choices: choices, filteringChoices: filteringChoices, @@ -114,7 +113,6 @@ func (o Options) Run() error { textStyle: o.TextStyle.ToLipgloss(), cursorTextStyle: o.CursorTextStyle.ToLipgloss(), height: o.Height, - padding: []int{top, right, bottom, left}, selected: make(map[string]struct{}), limit: o.Limit, reverse: o.Reverse, diff --git a/filter/filter.go b/filter/filter.go index 5e433cd..d321bcb 100644 --- a/filter/filter.go +++ b/filter/filter.go @@ -19,7 +19,6 @@ import ( "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" - "github.com/charmbracelet/x/exp/ordered" "github.com/rivo/uniseg" "github.com/sahilm/fuzzy" ) @@ -138,7 +137,6 @@ type model struct { selectedPrefix string unselectedPrefix string height int - padding []int quitting bool headerStyle lipgloss.Style matchStyle lipgloss.Style @@ -232,35 +230,33 @@ func (m model) View() string { m.viewport.SetContent(s.String()) + help := "" + if m.showHelp { + help = m.helpView() + } + // View the input and the filtered choices header := m.headerStyle.Render(m.header) if m.reverse { - view := m.viewport.View() - if m.header != "" { - view += "\n" + header - } - view += "\n" + m.textinput.View() + view := m.viewport.View() + "\n" + m.textinput.View() if m.showHelp { - view += m.helpView() + view += help } - return lipgloss.NewStyle(). - Padding(m.padding...). - Render(view) + if m.header != "" { + return lipgloss.JoinVertical(lipgloss.Left, view, header) + } + + return view } view := m.textinput.View() + "\n" + m.viewport.View() if m.showHelp { - view += m.helpView() + view += help } if m.header != "" { - return lipgloss.NewStyle(). - Padding(m.padding...). - Render(header + "\n" + view) + return lipgloss.JoinVertical(lipgloss.Left, header, view) } - - return lipgloss.NewStyle(). - Padding(m.padding...). - Render(view) + return view } func (m model) helpView() string { @@ -283,11 +279,9 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if m.showHelp { m.viewport.Height = m.viewport.Height - lipgloss.Height(m.helpView()) } - m.viewport.Height = m.viewport.Height - m.padding[0] - m.padding[2] - m.viewport.Width = msg.Width - m.padding[1] - m.padding[3] - m.textinput.Width = msg.Width - m.padding[1] - m.padding[3] + m.viewport.Width = msg.Width if m.reverse { - m.viewport.YOffset = ordered.Clamp(len(m.matches)-m.viewport.Height, 0, len(m.matches)) + m.viewport.YOffset = clamp(0, len(m.matches), len(m.matches)-m.viewport.Height) } case tea.KeyMsg: km := m.keymap @@ -379,7 +373,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // it remains at a constant position relative to the cursor. if m.reverse { maxYOffset := max(0, len(m.matches)-m.viewport.Height) - m.viewport.YOffset = ordered.Clamp(len(m.matches)-yOffsetFromBottom, 0, maxYOffset) + m.viewport.YOffset = clamp(0, maxYOffset, len(m.matches)-yOffsetFromBottom) } } } @@ -393,7 +387,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // It's possible that filtering items have caused fewer matches. So, ensure // that the selected index is within the bounds of the number of matches. - m.cursor = ordered.Clamp(m.cursor, 0, len(m.matches)-1) + m.cursor = clamp(0, len(m.matches)-1, m.cursor) return m, tea.Batch(cmd, icmd) } @@ -404,7 +398,7 @@ func (m *model) CursorUp() { if m.reverse { //nolint:nestif m.cursor = (m.cursor + 1) % len(m.matches) if len(m.matches)-m.cursor <= m.viewport.YOffset { - m.viewport.ScrollUp(1) + m.viewport.LineUp(1) } if len(m.matches)-m.cursor > m.viewport.Height+m.viewport.YOffset { m.viewport.SetYOffset(len(m.matches) - m.viewport.Height) @@ -412,7 +406,7 @@ func (m *model) CursorUp() { } else { m.cursor = (m.cursor - 1 + len(m.matches)) % len(m.matches) if m.cursor < m.viewport.YOffset { - m.viewport.ScrollUp(1) + m.viewport.LineUp(1) } if m.cursor >= m.viewport.YOffset+m.viewport.Height { m.viewport.SetYOffset(len(m.matches) - m.viewport.Height) @@ -427,7 +421,7 @@ func (m *model) CursorDown() { if m.reverse { //nolint:nestif m.cursor = (m.cursor - 1 + len(m.matches)) % len(m.matches) if len(m.matches)-m.cursor > m.viewport.Height+m.viewport.YOffset { - m.viewport.ScrollDown(1) + m.viewport.LineDown(1) } if len(m.matches)-m.cursor <= m.viewport.YOffset { m.viewport.GotoTop() @@ -435,7 +429,7 @@ func (m *model) CursorDown() { } else { m.cursor = (m.cursor + 1) % len(m.matches) if m.cursor >= m.viewport.YOffset+m.viewport.Height { - m.viewport.ScrollDown(1) + m.viewport.LineDown(1) } if m.cursor < m.viewport.YOffset { m.viewport.GotoTop() @@ -504,6 +498,16 @@ func exactMatches(search string, choices []string) []fuzzy.Match { return matches } +func clamp(low, high, val int) int { + if val < low { + return low + } + if val > high { + return high + } + return val +} + func matchedRanges(in []int) [][2]int { if len(in) == 0 { return [][2]int{} diff --git a/filter/options.go b/filter/options.go index 26eb3ea..07c50ce 100644 --- a/filter/options.go +++ b/filter/options.go @@ -15,7 +15,7 @@ type Options struct { Limit int `help:"Maximum number of options to pick" default:"1" group:"Selection"` NoLimit bool `help:"Pick unlimited number of options (ignores limit)" group:"Selection"` SelectIfOne bool `help:"Select the given option if there is only one" group:"Selection"` - Selected []string `help:"Options that should start as selected (selects all if given *)" default:"" env:"GUM_FILTER_SELECTED"` + Selected []string `help:"Options that should start as selected (selects all if given '*')" default:"" env:"GUM_FILTER_SELECTED"` ShowHelp bool `help:"Show help keybinds" default:"true" negatable:"" env:"GUM_FILTER_SHOW_HELP"` Strict bool `help:"Only returns if anything matched. Otherwise return Filter" negatable:"" default:"true" group:"Selection"` SelectedPrefix string `help:"Character to indicate selected items (hidden if limit is 1)" default:" ◉ " env:"GUM_FILTER_SELECTED_PREFIX"` @@ -41,7 +41,6 @@ type Options struct { InputDelimiter string `help:"Option delimiter when reading from STDIN" default:"\n" env:"GUM_FILTER_INPUT_DELIMITER"` OutputDelimiter string `help:"Option delimiter when writing to STDOUT" default:"\n" env:"GUM_FILTER_OUTPUT_DELIMITER"` StripANSI bool `help:"Strip ANSI sequences when reading from STDIN" default:"true" negatable:"" env:"GUM_FILTER_STRIP_ANSI"` - Padding string `help:"Padding" default:"${defaultPadding}" group:"Style Flags" env:"GUM_FILTER_PADDING"` // Deprecated: use [FuzzySort]. This will be removed at some point. Sort bool `help:"Sort fuzzy results by their scores" default:"true" env:"GUM_FILTER_FUZZY_SORT" negatable:"" hidden:""` diff --git a/go.mod b/go.mod index 55e2352..49a2839 100644 --- a/go.mod +++ b/go.mod @@ -1,26 +1,24 @@ module github.com/charmbracelet/gum -go 1.24.2 +go 1.23.0 require ( - github.com/Masterminds/semver/v3 v3.4.0 - github.com/alecthomas/kong v1.14.0 + github.com/Masterminds/semver/v3 v3.3.1 + github.com/alecthomas/kong v1.6.1 github.com/alecthomas/mango-kong v0.1.0 - github.com/charmbracelet/bubbles v1.0.0 - github.com/charmbracelet/bubbletea v1.3.10 - github.com/charmbracelet/glamour v0.10.0 - github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 - github.com/charmbracelet/log v0.4.2 - github.com/charmbracelet/x/ansi v0.11.6 - github.com/charmbracelet/x/editor v0.2.0 - github.com/charmbracelet/x/exp/ordered v0.1.0 - github.com/charmbracelet/x/term v0.2.2 - github.com/charmbracelet/x/xpty v0.1.3 + github.com/charmbracelet/bubbles v0.20.0 + github.com/charmbracelet/bubbletea v1.2.5-0.20241207142916-e0515bc22ad1 + github.com/charmbracelet/glamour v0.8.0 + github.com/charmbracelet/lipgloss v1.0.1-0.20250110214317-ecc1bd014d51 + github.com/charmbracelet/log v0.4.0 + github.com/charmbracelet/x/ansi v0.8.0 + github.com/charmbracelet/x/editor v0.1.0 + github.com/charmbracelet/x/term v0.2.1 github.com/muesli/roff v0.1.0 - github.com/muesli/termenv v0.16.0 + github.com/muesli/termenv v0.15.3-0.20241211131612-0d230cb6eb15 github.com/rivo/uniseg v0.4.7 github.com/sahilm/fuzzy v0.1.1 - golang.org/x/text v0.34.0 + golang.org/x/text v0.21.0 ) require ( @@ -28,35 +26,25 @@ require ( github.com/atotto/clipboard v0.1.4 // indirect 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/x/cellbuf v0.0.15 // indirect - github.com/charmbracelet/x/conpty v0.1.1 // indirect - github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86 // indirect - github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf // indirect - github.com/charmbracelet/x/termios v0.1.1 // indirect - github.com/clipperhouse/displaywidth v0.9.0 // indirect - github.com/clipperhouse/stringish v0.1.1 // indirect - github.com/clipperhouse/uax29/v2 v2.5.0 // indirect - github.com/creack/pty v1.1.24 // indirect github.com/dlclark/regexp2 v1.11.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/gorilla/css v1.0.1 // indirect - github.com/lucasb-eyer/go-colorful v1.3.0 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-isatty v0.0.20 // 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.16 // indirect github.com/microcosm-cc/bluemonday v1.0.27 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/mango v0.2.0 // indirect github.com/muesli/reflow v0.3.0 // indirect - github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect - github.com/yuin/goldmark v1.7.8 // indirect - github.com/yuin/goldmark-emoji v1.0.5 // indirect + github.com/yuin/goldmark v1.7.4 // indirect + github.com/yuin/goldmark-emoji v1.0.4 // indirect golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect - golang.org/x/net v0.40.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/term v0.32.0 // indirect + golang.org/x/net v0.33.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/term v0.27.0 // indirect ) diff --git a/go.sum b/go.sum index c3d39b7..7b3c806 100644 --- a/go.sum +++ b/go.sum @@ -1,67 +1,43 @@ github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= -github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= -github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= +github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= 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.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E= github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I= -github.com/alecthomas/kong v1.14.0 h1:gFgEUZWu2ZmZ+UhyZ1bDhuutbKN1nTtJTwh19Wsn21s= -github.com/alecthomas/kong v1.14.0/go.mod h1:wrlbXem1CWqUV5Vbmss5ISYhsVPkBb1Yo7YKJghju2I= +github.com/alecthomas/kong v1.6.1 h1:/7bVimARU3uxPD0hbryPE8qWrS3Oz3kPQoxA/H2NKG8= +github.com/alecthomas/kong v1.6.1/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU= github.com/alecthomas/mango-kong v0.1.0 h1:iFVfP1k1K4qpml3JUQmD5I8MCQYfIvsD9mRdrw7jJC4= github.com/alecthomas/mango-kong v0.1.0/go.mod h1:t+TYVdsONUolf/BwVcm+15eqcdAj15h4Qe9MMFAwwT4= -github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs= -github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= +github.com/alecthomas/repr v0.4.0/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/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= +github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= 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 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.4.1 h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco3ej+ngLk1kk= -github.com/charmbracelet/colorprofile v0.4.1/go.mod h1:U1d9Dljmdf9DLegaJ0nGZNJvoXAhayhmidOdcBwAvKk= -github.com/charmbracelet/glamour v0.10.0 h1:MtZvfwsYCx8jEPFJm3rIBFIMZUfUJ765oX8V6kXldcY= -github.com/charmbracelet/glamour v0.10.0/go.mod h1:f+uf+I/ChNmqo087elLnVdCiVgjSKWuXa/l6NU2ndYk= -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/log v0.4.2 h1:hYt8Qj6a8yLnvR+h7MwsJv/XvmBJXiueUcI3cIxsyig= -github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw= -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.15 h1:ur3pZy0o6z/R7EylET877CBxaiE1Sp1GMxoFPAIztPI= -github.com/charmbracelet/x/cellbuf v0.0.15/go.mod h1:J1YVbR7MUuEGIFPCaaZ96KDl5NoS0DAWkskup+mOY+Q= -github.com/charmbracelet/x/conpty v0.1.1 h1:s1bUxjoi7EpqiXysVtC+a8RrvPPNcNvAjfi4jxsAuEs= -github.com/charmbracelet/x/conpty v0.1.1/go.mod h1:OmtR77VODEFbiTzGE9G1XiRJAga6011PIm4u5fTNZpk= -github.com/charmbracelet/x/editor v0.2.0 h1:7XLUKtaRaB8jN7bWU2p2UChiySyaAuIfYiIRg8gGWwk= -github.com/charmbracelet/x/editor v0.2.0/go.mod h1:p3oQ28TSL3YPd+GKJ1fHWcp+7bVGpedHpXmo0D6t1dY= -github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86 h1:JSt3B+U9iqk37QUU2Rvb6DSBYRLtWqFqfxf8l5hOZUA= -github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0= -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/ordered v0.1.0 h1:55/qLwjIh0gL0Vni+QAWk7T/qRVP6sBf+2agPBgnOFE= -github.com/charmbracelet/x/exp/ordered v0.1.0/go.mod h1:5UHwmG+is5THxMyCJHNPCn2/ecI07aKNrW+LcResjJ8= -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.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk= -github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI= -github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY= -github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo= -github.com/charmbracelet/x/xpty v0.1.3 h1:eGSitii4suhzrISYH50ZfufV3v085BXQwIytcOdFSsw= -github.com/charmbracelet/x/xpty v0.1.3/go.mod h1:poPYpWuLDBFCKmKLDnhBp51ATa0ooD8FhypRwEFtH3Y= -github.com/clipperhouse/displaywidth v0.9.0 h1:Qb4KOhYwRiN3viMv1v/3cTBlz3AcAZX3+y9OLhMtAtA= -github.com/clipperhouse/displaywidth v0.9.0/go.mod h1:aCAAqTlh4GIVkhQnJpbL0T/WfcrJXHcj8C0yjYcjOZA= -github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= -github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= -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/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= -github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= +github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE= +github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= +github.com/charmbracelet/bubbletea v1.2.5-0.20241207142916-e0515bc22ad1 h1:osd3dk14DEriOrqJBWzeDE9eN2Yd00BkKzFAiLXxkS8= +github.com/charmbracelet/bubbletea v1.2.5-0.20241207142916-e0515bc22ad1/go.mod h1:Hbk5+oE4a7cDyjfdPi4sHZ42aGTMYcmHnVDhsRswn7A= +github.com/charmbracelet/glamour v0.8.0 h1:tPrjL3aRcQbn++7t18wOpgLyl8wrOHUEDS7IZ68QtZs= +github.com/charmbracelet/glamour v0.8.0/go.mod h1:ViRgmKkf3u5S7uakt2czJ272WSg2ZenlYEZXT2x7Bjw= +github.com/charmbracelet/lipgloss v1.0.1-0.20250110214317-ecc1bd014d51 h1:f+0mEkhorXNiBaHb4V9wyd364OH/aF7md7ZngkS+1gU= +github.com/charmbracelet/lipgloss v1.0.1-0.20250110214317-ecc1bd014d51/go.mod h1:QRGthpgH59/perglqXZC8xPHqDGZ9BB45ChJCFEWEMI= +github.com/charmbracelet/log v0.4.0 h1:G9bQAcx8rWA2T3pWvx7YtPTPwgqpk7D68BX21IRW8ZM= +github.com/charmbracelet/log v0.4.0/go.mod h1:63bXt/djrizTec0l11H20t8FDSvA4CRZJ1KH22MdptM= +github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE= +github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q= +github.com/charmbracelet/x/editor v0.1.0 h1:p69/dpvlwRTs9uYiPeAWruwsHqTFzHhTvQOd/WVSX98= +github.com/charmbracelet/x/editor v0.1.0/go.mod h1:oivrEbcP/AYt/Hpvk5pwDXXrQ933gQS6UzL6fxqAGSA= +github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b h1:MnAMdlwSltxJyULnrYbkZpp4k58Co7Tah3ciKhSNo0Q= +github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= +github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= +github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= 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/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= @@ -78,15 +54,15 @@ github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUq github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -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/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/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 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/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -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/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= @@ -99,8 +75,8 @@ github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= 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/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= -github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= +github.com/muesli/termenv v0.15.3-0.20241211131612-0d230cb6eb15 h1:XGrI/sVwKFHXvDVSGfan37w1AFt14RLDqBqY0ThTgk0= +github.com/muesli/termenv v0.15.3-0.20241211131612-0d230cb6eb15/go.mod h1:hxSnBBYLK21Vtq/PHd0S2FYCxBXzBua8ov5s1RobyRQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -109,26 +85,26 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA= github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= -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/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/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= -github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic= -github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= -github.com/yuin/goldmark-emoji v1.0.5 h1:EMVWyCGPlXJfUXBXpuMu+ii3TIaxbVBnEX9uaDC4cIk= -github.com/yuin/goldmark-emoji v1.0.5/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U= +github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg= +github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= +github.com/yuin/goldmark-emoji v1.0.4 h1:vCwMkPZSNefSUnOW2ZKRUjBSD5Ok3W78IXhGxxAEF90= +github.com/yuin/goldmark-emoji v1.0.4/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U= golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 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.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= -golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= -golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= -golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/input/command.go b/input/command.go index 0900d8d..7d96e72 100644 --- a/input/command.go +++ b/input/command.go @@ -11,7 +11,6 @@ import ( "github.com/charmbracelet/gum/cursor" "github.com/charmbracelet/gum/internal/stdin" "github.com/charmbracelet/gum/internal/timeout" - "github.com/charmbracelet/gum/style" ) // Run provides a shell script interface for the text input bubble. @@ -44,12 +43,10 @@ func (o Options) Run() error { i.EchoCharacter = '•' } - top, right, bottom, left := style.ParsePadding(o.Padding) m := model{ textinput: i, header: o.Header, headerStyle: o.HeaderStyle.ToLipgloss(), - padding: []int{top, right, bottom, left}, autoWidth: o.Width < 1, showHelp: o.ShowHelp, help: help.New(), diff --git a/input/input.go b/input/input.go index f63ac6a..505b052 100644 --- a/input/input.go +++ b/input/input.go @@ -38,7 +38,6 @@ func (k keymap) ShortHelp() []key.Binding { type model struct { autoWidth bool header string - padding []int headerStyle lipgloss.Style textinput textinput.Model quitting bool @@ -54,30 +53,27 @@ func (m model) View() string { if m.quitting { return "" } - var parts []string if m.header != "" { - parts = append(parts, m.headerStyle.Render(m.header)) + header := m.headerStyle.Render(m.header) + return lipgloss.JoinVertical(lipgloss.Left, header, m.textinput.View()) } - parts = append(parts, m.textinput.View()) - if m.showHelp { - parts = append(parts, "", m.help.View(m.keymap)) + if !m.showHelp { + return m.textinput.View() } - return lipgloss.NewStyle(). - Padding(m.padding...). - Render(lipgloss.JoinVertical( - lipgloss.Top, - parts..., - )) + return lipgloss.JoinVertical( + lipgloss.Top, + m.textinput.View(), + "", + m.help.View(m.keymap), + ) } func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.WindowSizeMsg: if m.autoWidth { - m.textinput.Width = msg.Width - 1 - - lipgloss.Width(m.textinput.Prompt) - - m.padding[1] - m.padding[3] + m.textinput.Width = msg.Width - lipgloss.Width(m.textinput.Prompt) - 1 } case tea.KeyMsg: switch msg.String() { diff --git a/input/options.go b/input/options.go index 57cbf53..7463adb 100644 --- a/input/options.go +++ b/input/options.go @@ -23,5 +23,4 @@ type Options struct { HeaderStyle style.Styles `embed:"" prefix:"header." set:"defaultForeground=240" envprefix:"GUM_INPUT_HEADER_"` Timeout time.Duration `help:"Timeout until input aborts" default:"0s" env:"GUM_INPUT_TIMEOUT"` StripANSI bool `help:"Strip ANSI sequences when reading from STDIN" default:"true" negatable:"" env:"GUM_INPUT_STRIP_ANSI"` - Padding string `help:"Padding" default:"${defaultPadding}" group:"Style Flags" env:"GUM_INPUT_PADDING"` } diff --git a/internal/decode/align.go b/internal/decode/align.go index 555f13c..813bcdd 100644 --- a/internal/decode/align.go +++ b/internal/decode/align.go @@ -1,4 +1,3 @@ -// Package decode position strings to lipgloss. package decode import "github.com/charmbracelet/lipgloss" diff --git a/internal/exit/exit.go b/internal/exit/exit.go index f523efc..c4f3387 100644 --- a/internal/exit/exit.go +++ b/internal/exit/exit.go @@ -1,7 +1,8 @@ -// Package exit code implementation. package exit -import "strconv" +import ( + "strconv" +) // StatusTimeout is the exit code for timed out commands. const StatusTimeout = 124 diff --git a/internal/files/files.go b/internal/files/files.go index d1cd19e..51e6950 100644 --- a/internal/files/files.go +++ b/internal/files/files.go @@ -1,4 +1,3 @@ -// Package files handles files. package files import ( @@ -19,6 +18,7 @@ func List() []string { files = append(files, path) return nil }) + if err != nil { return []string{} } diff --git a/internal/log/log.go b/internal/log/log.go new file mode 100644 index 0000000..2a9f9a9 --- /dev/null +++ b/internal/log/log.go @@ -0,0 +1,8 @@ +package log + +import "fmt" + +// Error prints an error message to the user. +func Error(message string) { + fmt.Println("Error:", message) +} diff --git a/internal/stack/stack.go b/internal/stack/stack.go new file mode 100644 index 0000000..b28fedb --- /dev/null +++ b/internal/stack/stack.go @@ -0,0 +1,26 @@ +package stack + +// Stack is a stack interface for integers. +type Stack struct { + Push func(int) + Pop func() int + Length func() int +} + +// NewStack returns a new stack of integers. +func NewStack() Stack { + slice := make([]int, 0) + return Stack{ + Push: func(i int) { + slice = append(slice, i) + }, + Pop: func() int { + res := slice[len(slice)-1] + slice = slice[:len(slice)-1] + return res + }, + Length: func() int { + return len(slice) + }, + } +} diff --git a/internal/stdin/stdin.go b/internal/stdin/stdin.go index 2efdcfd..42b0162 100644 --- a/internal/stdin/stdin.go +++ b/internal/stdin/stdin.go @@ -1,4 +1,3 @@ -// Package stdin handles processing input from stdin. package stdin import ( diff --git a/internal/timeout/context.go b/internal/timeout/context.go index ffd39e9..99eaf34 100644 --- a/internal/timeout/context.go +++ b/internal/timeout/context.go @@ -1,4 +1,3 @@ -// Package timeout handles context timeouts. package timeout import ( diff --git a/internal/utils/utils.go b/internal/utils/utils.go new file mode 100644 index 0000000..0e38598 --- /dev/null +++ b/internal/utils/utils.go @@ -0,0 +1,15 @@ +package utils + +import ( + "strings" + + "github.com/charmbracelet/lipgloss" +) + +// LipglossPadding calculates how much padding a string is given by a style. +func LipglossPadding(style lipgloss.Style) (int, int) { + render := style.Render(" ") + before := strings.Index(render, " ") + after := len(render) - len(" ") - before + return before, after +} diff --git a/log/command.go b/log/command.go index 1240083..aff18cf 100644 --- a/log/command.go +++ b/log/command.go @@ -1,4 +1,3 @@ -// Package log the log command. package log import ( @@ -17,7 +16,7 @@ func (o Options) Run() error { l := log.New(os.Stderr) if o.File != "" { - f, err := os.OpenFile(o.File, os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModePerm) //nolint:gosec + f, err := os.OpenFile(o.File, os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModePerm) if err != nil { return fmt.Errorf("error opening file: %w", err) } @@ -32,7 +31,7 @@ func (o Options) Run() error { if o.MinLevel != "" { lvl, err := log.ParseLevel(o.MinLevel) if err != nil { - return err //nolint:wrapcheck + return err } l.SetLevel(lvl) } diff --git a/log/options.go b/log/options.go index 12949e3..73fbcec 100644 --- a/log/options.go +++ b/log/options.go @@ -18,7 +18,7 @@ type Options struct { MinLevel string `help:"Minimal level to show" default:"" env:"GUM_LOG_LEVEL"` - LevelStyle style.Styles `embed:"" prefix:"level." help:"The style of the level being used" set:"defaultBold=true" envprefix:"GUM_LOG_LEVEL_"` + LevelStyle style.Styles `embed:"" prefix:"level." help:"The style of the level being used" set:"defaultBold=true" envprefix:"GUM_LOG_LEVEL_"` //nolint:staticcheck TimeStyle style.Styles `embed:"" prefix:"time." help:"The style of the time" envprefix:"GUM_LOG_TIME_"` PrefixStyle style.Styles `embed:"" prefix:"prefix." help:"The style of the prefix" set:"defaultBold=true" set:"defaultFaint=true" envprefix:"GUM_LOG_PREFIX_"` //nolint:staticcheck MessageStyle style.Styles `embed:"" prefix:"message." help:"The style of the message" envprefix:"GUM_LOG_MESSAGE_"` diff --git a/main.go b/main.go index a61916e..18fa9f0 100644 --- a/main.go +++ b/main.go @@ -1,4 +1,3 @@ -// Package main is Gum: a tool for glamorous shell scripts. package main import ( @@ -78,13 +77,13 @@ func main() { if errors.As(err, &ex) { os.Exit(int(ex)) } - if errors.Is(err, tea.ErrInterrupted) { - os.Exit(exit.StatusAborted) - } if errors.Is(err, tea.ErrProgramKilled) { fmt.Fprintln(os.Stderr, "timed out") os.Exit(exit.StatusTimeout) } + if errors.Is(err, tea.ErrInterrupted) { + os.Exit(exit.StatusAborted) + } fmt.Fprintln(os.Stderr, err) os.Exit(1) } diff --git a/man/command.go b/man/command.go index 22f5bec..1d83dab 100644 --- a/man/command.go +++ b/man/command.go @@ -1,4 +1,3 @@ -// Package man the man command. package man import ( diff --git a/pager/pager.go b/pager/pager.go index 324a314..6b38403 100644 --- a/pager/pager.go +++ b/pager/pager.go @@ -158,7 +158,7 @@ func (m *model) processText(msg tea.WindowSizeMsg) { text.WriteString("\n") } } else { - text.WriteString(textStyle.Render(line)) + text.WriteString(textStyle.Render(line)) //nolint: gosec text.WriteString("\n") } } diff --git a/pager/search.go b/pager/search.go index 134096c..be59a53 100644 --- a/pager/search.go +++ b/pager/search.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/charmbracelet/bubbles/textinput" + "github.com/charmbracelet/gum/internal/utils" "github.com/charmbracelet/lipgloss" "github.com/charmbracelet/x/ansi" ) @@ -51,7 +52,7 @@ func (s *search) Execute(m *model) { m.content = query.ReplaceAllString(m.content, m.matchStyle.Render("$1")) // Recompile the regex to match the an replace the highlights. - leftPad, _ := lipglossPadding(m.matchStyle) + leftPad, _ := utils.LipglossPadding(m.matchStyle) matchingString := regexp.QuoteMeta(m.matchStyle.Render()[:leftPad]) + s.query.String() + regexp.QuoteMeta(m.matchStyle.Render()[leftPad:]) s.query, err = regexp.Compile(matchingString) if err != nil { @@ -81,7 +82,7 @@ func (s *search) NextMatch(m *model) { return } - leftPad, rightPad := lipglossPadding(m.matchStyle) + leftPad, rightPad := utils.LipglossPadding(m.matchStyle) s.matchIndex = (s.matchIndex + 1) % len(allMatches) match := allMatches[s.matchIndex] lhs := m.content[:match[0]] @@ -124,7 +125,7 @@ func (s *search) PrevMatch(m *model) { s.matchIndex = len(allMatches) - 1 } - leftPad, rightPad := lipglossPadding(m.matchStyle) + leftPad, rightPad := utils.LipglossPadding(m.matchStyle) match := allMatches[s.matchIndex] lhs := m.content[:match[0]] rhs := m.content[match[0]:] @@ -158,18 +159,10 @@ func softWrapEm(str string, maxWidth int, softWrap bool) string { text.WriteString("\n") } } else { - text.WriteString(line) + text.WriteString(line) //nolint: gosec text.WriteString("\n") } } return text.String() } - -// lipglossPadding calculates how much padding a string is given by a style. -func lipglossPadding(style lipgloss.Style) (int, int) { - render := style.Render(" ") - before := strings.Index(render, " ") - after := len(render) - len(" ") - before - return before, after -} diff --git a/spin/command.go b/spin/command.go index cff2797..02213f0 100644 --- a/spin/command.go +++ b/spin/command.go @@ -8,7 +8,6 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/gum/internal/exit" "github.com/charmbracelet/gum/internal/timeout" - "github.com/charmbracelet/gum/style" "github.com/charmbracelet/x/term" ) @@ -21,7 +20,6 @@ func (o Options) Run() error { s := spinner.New() s.Style = o.SpinnerStyle.ToLipgloss() s.Spinner = spinnerMap[o.Spinner] - top, right, bottom, left := style.ParsePadding(o.Padding) m := model{ spinner: s, title: o.TitleStyle.ToLipgloss().Render(o.Title), @@ -31,7 +29,6 @@ func (o Options) Run() error { showStderr: (o.ShowOutput || o.ShowStderr) && isErrTTY, showError: o.ShowError, isTTY: isErrTTY, - padding: []int{top, right, bottom, left}, } ctx, cancel := timeout.Context(o.Timeout) @@ -51,29 +48,22 @@ func (o Options) Run() error { // If the command succeeds, and we are printing output and we are in a TTY then push the STDOUT we got to the actual // STDOUT for piping or other things. //nolint:nestif - if m.err != nil { - if _, err := fmt.Fprintf(os.Stderr, "%s\n", m.err.Error()); err != nil { - return fmt.Errorf("failed to write to stdout: %w", err) - } - return exit.ErrExit(1) - } else if m.status == 0 { - var output string - if o.ShowOutput || (o.ShowStdout && o.ShowStderr) { - output = m.output - } else if o.ShowStdout { - output = m.stdout - } else if o.ShowStderr { - output = m.stderr - } - if output != "" { - if _, err := os.Stdout.WriteString(output); err != nil { - return fmt.Errorf("failed to write to stdout: %w", err) + if m.status == 0 { + if o.ShowOutput { + // BubbleTea writes the View() to stderr. + // If the program is being piped then put the accumulated output in stdout. + if !isOutTTY { + _, err := os.Stdout.WriteString(m.stdout) + if err != nil { + return fmt.Errorf("failed to write to stdout: %w", err) + } } } } else if o.ShowError { // Otherwise if we are showing errors and the command did not exit with a 0 status code then push all of the command // output to the terminal. This way failed commands can be debugged. - if _, err := os.Stdout.WriteString(m.output); err != nil { + _, err := os.Stdout.WriteString(m.output) + if err != nil { return fmt.Errorf("failed to write to stdout: %w", err) } } diff --git a/spin/options.go b/spin/options.go index 702cc2a..5243eab 100644 --- a/spin/options.go +++ b/spin/options.go @@ -20,5 +20,4 @@ type Options struct { TitleStyle style.Styles `embed:"" prefix:"title." envprefix:"GUM_SPIN_TITLE_"` Align string `help:"Alignment of spinner with regard to the title" short:"a" type:"align" enum:"left,right" default:"left" env:"GUM_SPIN_ALIGN"` Timeout time.Duration `help:"Timeout until spin command aborts" default:"0s" env:"GUM_SPIN_TIMEOUT"` - Padding string `help:"Padding" default:"${defaultPadding}" group:"Style Flags" env:"GUM_SPIN_PADDING"` } diff --git a/spin/pty.go b/spin/pty.go deleted file mode 100644 index 9562434..0000000 --- a/spin/pty.go +++ /dev/null @@ -1,22 +0,0 @@ -package spin - -import ( - "os" - - "github.com/charmbracelet/x/term" - "github.com/charmbracelet/x/xpty" -) - -func openPty(f *os.File) (pty xpty.Pty, err error) { - width, height, err := term.GetSize(f.Fd()) - if err != nil { - return nil, err //nolint:wrapcheck - } - - pty, err = xpty.NewPty(width, height) - if err != nil { - return nil, err //nolint:wrapcheck - } - - return pty, nil -} diff --git a/spin/spin.go b/spin/spin.go index 6e41690..d675bab 100644 --- a/spin/spin.go +++ b/spin/spin.go @@ -15,25 +15,20 @@ package spin import ( - "bytes" - "context" "io" "os" "os/exec" - "runtime" + "strings" "syscall" "github.com/charmbracelet/bubbles/spinner" tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" "github.com/charmbracelet/x/term" - "github.com/charmbracelet/x/xpty" ) type model struct { spinner spinner.Model title string - padding []int align string command []string quitting bool @@ -45,19 +40,16 @@ type model struct { showStdout bool showStderr bool showError bool - err error } var ( - bothbuf bytes.Buffer - outbuf bytes.Buffer - errbuf bytes.Buffer + bothbuf strings.Builder + outbuf strings.Builder + errbuf strings.Builder executing *exec.Cmd ) -type errorMsg error - type finishCommandMsg struct { stdout string stderr string @@ -72,52 +64,16 @@ func commandStart(command []string) tea.Cmd { args = command[1:] } - executing = exec.CommandContext(context.Background(), command[0], args...) //nolint:gosec - executing.Stdin = os.Stdin - - isTerminal := term.IsTerminal(os.Stdout.Fd()) - - // NOTE(@andreynering): We had issues with Git Bash on Windows - // when it comes to handling PTYs, so we're falling back to - // to redirecting stdout/stderr as usual to avoid issues. - //nolint:nestif - if isTerminal && runtime.GOOS == "windows" { + executing = exec.Command(command[0], args...) //nolint:gosec + if term.IsTerminal(os.Stdout.Fd()) { executing.Stdout = io.MultiWriter(&bothbuf, &outbuf) executing.Stderr = io.MultiWriter(&bothbuf, &errbuf) - _ = executing.Run() - } else if isTerminal { - stdoutPty, err := openPty(os.Stdout) - if err != nil { - return errorMsg(err) - } - defer stdoutPty.Close() //nolint:errcheck - - stderrPty, err := openPty(os.Stderr) - if err != nil { - return errorMsg(err) - } - defer stderrPty.Close() //nolint:errcheck - - if outUnixPty, isOutUnixPty := stdoutPty.(*xpty.UnixPty); isOutUnixPty { - executing.Stdout = outUnixPty.Slave() - } - if errUnixPty, isErrUnixPty := stderrPty.(*xpty.UnixPty); isErrUnixPty { - executing.Stderr = errUnixPty.Slave() - } - - go io.Copy(io.MultiWriter(&bothbuf, &outbuf), stdoutPty) //nolint:errcheck - go io.Copy(io.MultiWriter(&bothbuf, &errbuf), stderrPty) //nolint:errcheck - - if err = stdoutPty.Start(executing); err != nil { - return errorMsg(err) - } - _ = xpty.WaitProcess(context.Background(), executing) } else { executing.Stdout = os.Stdout executing.Stderr = os.Stderr - _ = executing.Run() } - + executing.Stdin = os.Stdin + _ = executing.Run() status := executing.ProcessState.ExitCode() if status == -1 { status = 1 @@ -147,10 +103,6 @@ func (m model) Init() tea.Cmd { } func (m model) View() string { - if m.quitting { - return "" - } - var out string if m.showStderr { out += errbuf.String() @@ -159,6 +111,10 @@ func (m model) View() string { out += outbuf.String() } + if m.quitting { + return out + } + if !m.isTTY { return m.title } @@ -169,12 +125,11 @@ func (m model) View() string { } else { header = m.title + " " + m.spinner.View() } - return lipgloss.NewStyle(). - Padding(m.padding...). - Render(header, "", out) + return header + "\n" + out } func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + var cmd tea.Cmd switch msg := msg.(type) { case finishCommandMsg: m.stdout = msg.stdout @@ -188,13 +143,8 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case "ctrl+c": return m, commandAbort } - case errorMsg: - m.err = msg - m.quitting = true - return m, tea.Quit } - var cmd tea.Cmd m.spinner, cmd = m.spinner.Update(msg) return m, cmd } diff --git a/style/lipgloss.go b/style/lipgloss.go index 07ad0c8..9ba52a2 100644 --- a/style/lipgloss.go +++ b/style/lipgloss.go @@ -19,7 +19,7 @@ func (s Styles) ToLipgloss() lipgloss.Style { Height(s.Height). Width(s.Width). Margin(parseMargin(s.Margin)). - Padding(ParsePadding(s.Padding)). + Padding(parsePadding(s.Padding)). Bold(s.Bold). Faint(s.Faint). Italic(s.Italic). @@ -40,7 +40,7 @@ func (s StylesNotHidden) ToLipgloss() lipgloss.Style { Height(s.Height). Width(s.Width). Margin(parseMargin(s.Margin)). - Padding(ParsePadding(s.Padding)). + Padding(parsePadding(s.Padding)). Bold(s.Bold). Faint(s.Faint). Italic(s.Italic). diff --git a/style/spacing.go b/style/spacing.go index 57ea7db..6b3fe26 100644 --- a/style/spacing.go +++ b/style/spacing.go @@ -5,15 +5,13 @@ import ( "strings" ) -const ( - minTokens = 1 - halfTokens = 2 - maxTokens = 4 -) +const minTokens = 1 +const halfTokens = 2 +const maxTokens = 4 -// ParsePadding parses 1 - 4 integers from a string and returns them in a top, +// parsePadding parses 1 - 4 integers from a string and returns them in a top, // right, bottom, left order for use in the lipgloss.Padding() method. -func ParsePadding(s string) (int, int, int, int) { +func parsePadding(s string) (int, int, int, int) { var ints [maxTokens]int tokens := strings.Split(s, " ") @@ -48,4 +46,4 @@ func ParsePadding(s string) (int, int, int, int) { // parseMargin is an alias for parsePadding since they involve the same logic // to parse integers to the same format. -var parseMargin = ParsePadding +var parseMargin = parsePadding diff --git a/table/command.go b/table/command.go index 479cd93..ae52bbf 100644 --- a/table/command.go +++ b/table/command.go @@ -79,7 +79,6 @@ func (o Options) Run() error { } defaultStyles := table.DefaultStyles() - top, right, bottom, left := style.ParsePadding(o.Padding) styles := table.Styles{ Cell: defaultStyles.Cell.Inherit(o.CellStyle.ToLipgloss()), @@ -134,7 +133,7 @@ func (o Options) Run() error { table.WithStyles(styles), } if o.Height > 0 { - opts = append(opts, table.WithHeight(o.Height-top-bottom)) + opts = append(opts, table.WithHeight(o.Height)) } table := table.New(opts...) @@ -143,12 +142,10 @@ func (o Options) Run() error { defer cancel() m := model{ - table: table, - showHelp: o.ShowHelp, - hideCount: o.HideCount, - help: help.New(), - keymap: defaultKeymap(), - padding: []int{top, right, bottom, left}, + table: table, + showHelp: o.ShowHelp, + help: help.New(), + keymap: defaultKeymap(), } tm, err := tea.NewProgram( m, diff --git a/table/options.go b/table/options.go index d7a241f..5949c92 100644 --- a/table/options.go +++ b/table/options.go @@ -16,7 +16,6 @@ type Options struct { File string `short:"f" help:"file path" default:""` Border string `short:"b" help:"border style" default:"rounded" enum:"rounded,thick,normal,hidden,double,none"` ShowHelp bool `help:"Show help keybinds" default:"true" negatable:"" env:"GUM_TABLE_SHOW_HELP"` - HideCount bool `help:"Hide item count on help keybinds" default:"false" negatable:"" env:"GUM_TABLE_HIDE_COUNT"` LazyQuotes bool `help:"If LazyQuotes is true, a quote may appear in an unquoted field and a non-doubled quote may appear in a quoted field" default:"false" env:"GUM_TABLE_LAZY_QUOTES"` FieldsPerRecord int `help:"Sets the number of expected fields per record" default:"0" env:"GUM_TABLE_FIELDS_PER_RECORD"` @@ -26,5 +25,4 @@ type Options struct { SelectedStyle style.Styles `embed:"" prefix:"selected." set:"defaultForeground=212" envprefix:"GUM_TABLE_SELECTED_"` ReturnColumn int `short:"r" help:"Which column number should be returned instead of whole row as string. Default=0 returns whole Row" default:"0"` Timeout time.Duration `help:"Timeout until choose returns selected element" default:"0s" env:"GUM_TABLE_TIMEOUT"` - Padding string `help:"Padding" default:"${defaultPadding}" group:"Style Flags" env:"GUM_TABLE_PADDING"` } diff --git a/table/table.go b/table/table.go index c0d389f..cbbb213 100644 --- a/table/table.go +++ b/table/table.go @@ -15,14 +15,10 @@ package table import ( - "fmt" - "strconv" - "github.com/charmbracelet/bubbles/help" "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/table" tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" ) type keymap struct { @@ -66,32 +62,16 @@ func defaultKeymap() keymap { } type model struct { - table table.Model - selected table.Row - quitting bool - showHelp bool - hideCount bool - help help.Model - keymap keymap - padding []int + table table.Model + selected table.Row + quitting bool + showHelp bool + help help.Model + keymap keymap } func (m model) Init() tea.Cmd { return nil } -func (m model) countView() string { - if m.hideCount { - return "" - } - - padding := strconv.Itoa(numLen(len(m.table.Rows()))) - return m.help.Styles.FullDesc.Render(fmt.Sprintf( - "%"+padding+"d/%d%s", - m.table.Cursor()+1, - len(m.table.Rows()), - m.help.ShortSeparator, - )) -} - func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmd tea.Cmd @@ -122,21 +102,7 @@ func (m model) View() string { } s := m.table.View() if m.showHelp { - s += "\n" + m.countView() + m.help.View(m.keymap) + s += "\n" + m.help.View(m.keymap) } - return lipgloss.NewStyle(). - Padding(m.padding...). - Render(s) -} - -func numLen(i int) int { - if i == 0 { - return 1 - } - count := 0 - for i != 0 { - i /= 10 - count++ - } - return count + return s } diff --git a/version/command.go b/version/command.go index f90177d..5102b34 100644 --- a/version/command.go +++ b/version/command.go @@ -1,4 +1,3 @@ -// Package version the version command. package version import ( diff --git a/write/command.go b/write/command.go index 6a745fb..adc3c69 100644 --- a/write/command.go +++ b/write/command.go @@ -12,7 +12,6 @@ import ( "github.com/charmbracelet/gum/cursor" "github.com/charmbracelet/gum/internal/stdin" "github.com/charmbracelet/gum/internal/timeout" - "github.com/charmbracelet/gum/style" ) // Run provides a shell script interface for the text area bubble. @@ -31,7 +30,6 @@ func (o Options) Run() error { a.ShowLineNumbers = o.ShowLineNumbers a.CharLimit = o.CharLimit a.MaxHeight = o.MaxLines - top, right, bottom, left := style.ParsePadding(o.Padding) style := textarea.Style{ Base: o.BaseStyle.ToLipgloss(), @@ -48,8 +46,8 @@ func (o Options) Run() error { a.Cursor.Style = o.CursorStyle.ToLipgloss() a.Cursor.SetMode(cursor.Modes[o.CursorMode]) - a.SetWidth(max(0, o.Width-left-right)) - a.SetHeight(max(0, o.Height-top-bottom)) + a.SetWidth(o.Width) + a.SetHeight(o.Height) a.SetValue(o.Value) m := model{ @@ -60,7 +58,6 @@ func (o Options) Run() error { help: help.New(), showHelp: o.ShowHelp, keymap: defaultKeymap(), - padding: []int{top, right, bottom, left}, } m.textarea.KeyMap.InsertNewline = m.keymap.InsertNewline diff --git a/write/options.go b/write/options.go index 63c7b0c..16653eb 100644 --- a/write/options.go +++ b/write/options.go @@ -32,5 +32,4 @@ type Options struct { HeaderStyle style.Styles `embed:"" prefix:"header." set:"defaultForeground=240" envprefix:"GUM_WRITE_HEADER_"` PlaceholderStyle style.Styles `embed:"" prefix:"placeholder." set:"defaultForeground=240" envprefix:"GUM_WRITE_PLACEHOLDER_"` PromptStyle style.Styles `embed:"" prefix:"prompt." set:"defaultForeground=7" envprefix:"GUM_WRITE_PROMPT_"` - Padding string `help:"Padding" default:"${defaultPadding}" group:"Style Flags" env:"GUM_WRITE_PADDING"` } diff --git a/write/write.go b/write/write.go index b7e65a3..978bf0d 100644 --- a/write/write.go +++ b/write/write.go @@ -77,7 +77,6 @@ type model struct { showHelp bool help help.Model keymap keymap - padding []int } func (m model) Init() tea.Cmd { return textarea.Blink } @@ -95,21 +94,16 @@ func (m model) View() string { } parts = append(parts, m.textarea.View()) if m.showHelp { - parts = append(parts, "", m.help.View(m.keymap)) + parts = append(parts, m.help.View(m.keymap)) } - return lipgloss.NewStyle(). - Padding(m.padding...). - Render(lipgloss.JoinVertical( - lipgloss.Left, - parts..., - )) + return lipgloss.JoinVertical(lipgloss.Left, parts...) } func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.WindowSizeMsg: if m.autoWidth { - m.textarea.SetWidth(msg.Width - m.padding[1] - m.padding[3]) + m.textarea.SetWidth(msg.Width) } case tea.FocusMsg, tea.BlurMsg: var cmd tea.Cmd @@ -137,7 +131,8 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.submitted = true return m, tea.Quit case key.Matches(msg, km.OpenInEditor): - return m, createTempFile(m.textarea.Value(), m.textarea.Line()+1) + //nolint: gosec + return m, createTempFile(m.textarea.Value(), uint(m.textarea.Line())+1) } } @@ -148,7 +143,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { type startEditorMsg struct { path string - lineno int + lineno uint } type editorFinishedMsg struct { @@ -156,7 +151,7 @@ type editorFinishedMsg struct { err error } -func createTempFile(content string, lineno int) tea.Cmd { +func createTempFile(content string, lineno uint) tea.Cmd { return func() tea.Msg { f, err := os.CreateTemp("", "gum.*.md") if err != nil { @@ -174,7 +169,7 @@ func createTempFile(content string, lineno int) tea.Cmd { } } -func openEditor(path string, lineno int) tea.Cmd { +func openEditor(path string, lineno uint) tea.Cmd { cb := func(err error) tea.Msg { if err != nil { return editorFinishedMsg{