Skip to content

Commit 56c3e12

Browse files
committed
Rework code as a library
1 parent e26d8f2 commit 56c3e12

12 files changed

Lines changed: 646 additions & 1 deletion

File tree

.idea/.gitignore

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/bubblehelp.iml

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/dictionaries/project.xml

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/inspectionProfiles/Project_Default.xml

Lines changed: 30 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/modules.xml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/vcs.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2025 Halsten
3+
Copyright (c) 2025 Lionel Leeser
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

bubblehelp.go

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
// Package bubblehelp is a manager to help render, contextualize and manage keybinds for bubbletea.
2+
package bubblehelp
3+
4+
import (
5+
"github.com/charmbracelet/bubbles/key"
6+
"github.com/charmbracelet/lipgloss"
7+
"log"
8+
"math"
9+
"strings"
10+
)
11+
12+
var (
13+
// CurrentContext holds the current context identifier.
14+
CurrentContext KeymapContext
15+
16+
// Contexts map of single every registered contexts.
17+
Contexts map[KeymapContext]*Keymap
18+
19+
// ShowAll is the flag to define whether it's the full or short help that will be rendered.
20+
ShowAll bool
21+
)
22+
23+
// Init is the first function that needs to be called in the start of the app.
24+
func Init() {
25+
Contexts = make(map[KeymapContext]*Keymap)
26+
}
27+
28+
// RegisterContext allows to register a new Keymap context and link it with the given identifier.
29+
func RegisterContext(context KeymapContext, keymap *Keymap) {
30+
Contexts[context] = keymap
31+
}
32+
33+
// GetCurrentContextKeymap returns a pointer on the current Keymap context.
34+
func GetCurrentContextKeymap() *Keymap {
35+
ctx, ok := Contexts[CurrentContext]
36+
37+
if !ok {
38+
return nil
39+
}
40+
41+
return ctx
42+
}
43+
44+
// SwitchContext take care of properly changing context. Resets the current context before switching.
45+
func SwitchContext(context KeymapContext) {
46+
_, ok := Contexts[context]
47+
48+
if !ok {
49+
log.Println("bubblehelp: context not found")
50+
}
51+
52+
keymap := GetCurrentContextKeymap()
53+
54+
if keymap != nil {
55+
keymap.Reset()
56+
}
57+
58+
CurrentContext = context
59+
ShowAll = false
60+
}
61+
62+
// UpdateKeybindHelpDesc allows to temporary change the help description for a keybind in the current Keymap context.
63+
func UpdateKeybindHelpDesc(keybind key.Binding, desc string) {
64+
keymap := GetCurrentContextKeymap()
65+
66+
if keymap == nil {
67+
return
68+
}
69+
70+
keymap.updateHelpDesc(keybind, desc)
71+
}
72+
73+
// SetKeybindVisible allows to set keybinds visibility in the current Keymap context.
74+
func SetKeybindVisible(keybind key.Binding, visible bool) {
75+
keymap := GetCurrentContextKeymap()
76+
77+
if keymap == nil {
78+
return
79+
}
80+
81+
keymap.setVisible(keybind, visible)
82+
}
83+
84+
// IsKeybindVisible returns the current visibility of a Keybind.
85+
// If the Keybind do not exist in the current context, returns false.
86+
func IsKeybindVisible(keybind key.Binding) bool {
87+
keymap := GetCurrentContextKeymap()
88+
89+
if keymap == nil {
90+
return false
91+
}
92+
93+
return keymap.isVisible(keybind)
94+
}
95+
96+
// View is the main render function of bubblehelp.
97+
// Take the ShowAll flag into account to render full or short help.
98+
func View(width int) string {
99+
keymap := GetCurrentContextKeymap()
100+
101+
if keymap == nil {
102+
return "ERROR : UNKNOWN KEYMAP CONTEXT"
103+
}
104+
105+
if ShowAll {
106+
return ViewAll(keymap, width)
107+
} else {
108+
return ViewEssential(keymap, width)
109+
}
110+
}
111+
112+
// ViewAll is the full help render function, can be called directly.
113+
func ViewAll(keymap *Keymap, width int) string {
114+
var keys []Key
115+
var columns []string
116+
var keyStr, sepStr, descStr strings.Builder
117+
118+
keys = keymap.AllBindings()
119+
120+
colCount := keymap.ShowAllColumnCount
121+
rowCount := int(math.Ceil(float64(len(keys)) / float64(colCount)))
122+
123+
columns = make([]string, 0)
124+
125+
for i, key := range keys {
126+
remainingCount := len(keys) - i
127+
notLastCol := len(columns)+1 < colCount
128+
129+
if i%rowCount > 0 || (remainingCount == 1 && notLastCol) {
130+
keyStr.WriteString("\n")
131+
sepStr.WriteString("\n")
132+
descStr.WriteString("\n")
133+
}
134+
135+
keyStr.WriteString(keymap.Style.FullKey.
136+
Render(key.Binding.Help().Key))
137+
sepStr.WriteString(keymap.Style.FullKeySeparator.
138+
Render(keymap.Style.FullKeySeparatorValue))
139+
descStr.WriteString(keymap.Style.FullKeyDescription.
140+
Render(key.GetHelpDesc()))
141+
142+
if ((i+1)%rowCount == 0 && i != 0 && notLastCol) || (remainingCount == 1) {
143+
columns = append(columns, lipgloss.
144+
JoinHorizontal(lipgloss.Center,
145+
keyStr.String(),
146+
sepStr.String(),
147+
descStr.String()))
148+
149+
if i < len(keys)-1 {
150+
columns = append(columns, keymap.Style.FullColSeparatorValue)
151+
colCount++
152+
}
153+
154+
keyStr.Reset()
155+
sepStr.Reset()
156+
descStr.Reset()
157+
}
158+
}
159+
160+
return lipgloss.JoinHorizontal(lipgloss.Top, columns...)
161+
}
162+
163+
// ViewEssential is the short help render function, can be called directly
164+
func ViewEssential(keymap *Keymap, width int) string {
165+
var b strings.Builder
166+
var keys []Key
167+
168+
keys = keymap.EssentialBindings()
169+
170+
for i, key := range keys {
171+
if i > 0 {
172+
b.WriteString(keymap.Style.EssentialColSeparator.
173+
Render(keymap.Style.EssentialColSeparatorValue))
174+
}
175+
176+
b.WriteString(keymap.Style.EssentialKey.
177+
Render(key.Binding.Help().Key))
178+
179+
b.WriteString(keymap.Style.EssentialKeySeparator.
180+
Render(keymap.Style.EssentialKeySeparatorValue))
181+
182+
b.WriteString(keymap.Style.EssentialKeyDescription.
183+
Render(key.GetHelpDesc()))
184+
}
185+
186+
return lipgloss.NewStyle().
187+
AlignHorizontal(lipgloss.Center).
188+
Width(width).Render(b.String())
189+
}

0 commit comments

Comments
 (0)