mirror of
https://github.com/wavetermdev/inshellisense.git
synced 2026-08-05 13:43:30 -07:00
feat: implement starting ui for autocomplete
Signed-off-by: cpendery <cpendery@vt.edu>
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
package autocomplete
|
||||
|
||||
import "strings"
|
||||
|
||||
func LoadSuggestions(cmd string) []string {
|
||||
params := strings.Split(cmd, " ")
|
||||
lastParam := params[len(params)-1]
|
||||
if lastParam == "git" {
|
||||
return []string{"go", "get"}
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
@@ -1,13 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/cpendery/clac/ui"
|
||||
)
|
||||
|
||||
func main() {
|
||||
f, err := tea.LogToFile("debug.log", "debug")
|
||||
if err != nil {
|
||||
fmt.Println("fatal:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer f.Close()
|
||||
p := tea.NewProgram(ui.Start())
|
||||
if _, err := p.Run(); err != nil {
|
||||
log.Fatal(err)
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
package suggestions
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/cpendery/clac/autocomplete"
|
||||
"github.com/cpendery/clac/ui/utils"
|
||||
)
|
||||
|
||||
type Model struct {
|
||||
cursor int
|
||||
suggestions []string
|
||||
keyMap KeyMap
|
||||
}
|
||||
|
||||
const (
|
||||
SuggestionWidth = 50
|
||||
BorderOffset = 2
|
||||
)
|
||||
|
||||
type KeyMap struct {
|
||||
LineUp key.Binding
|
||||
LineDown key.Binding
|
||||
PageUp key.Binding
|
||||
PageDown key.Binding
|
||||
}
|
||||
|
||||
func New() Model {
|
||||
keyBindings := KeyMap{
|
||||
LineUp: key.NewBinding(
|
||||
key.WithKeys("up"),
|
||||
),
|
||||
LineDown: key.NewBinding(
|
||||
key.WithKeys("down"),
|
||||
),
|
||||
PageUp: key.NewBinding(
|
||||
key.WithKeys("pgup"),
|
||||
),
|
||||
PageDown: key.NewBinding(
|
||||
key.WithKeys("pgdown"),
|
||||
),
|
||||
}
|
||||
return Model{
|
||||
cursor: 0,
|
||||
suggestions: []string{"div", "table"},
|
||||
keyMap: keyBindings,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Model) cursorUp() {
|
||||
m.cursor = utils.Clamp(m.cursor-1, 0, len(m.suggestions)-1)
|
||||
}
|
||||
|
||||
func (m *Model) cursorDown() {
|
||||
m.cursor = utils.Clamp(m.cursor+1, 0, len(m.suggestions)-1)
|
||||
}
|
||||
|
||||
func (m Model) ActiveSuggestion() string {
|
||||
return m.suggestions[m.cursor]
|
||||
}
|
||||
|
||||
func (m Model) Update(msg tea.Msg, command string) Model {
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch {
|
||||
case key.Matches(msg, m.keyMap.LineUp):
|
||||
log.Println("up")
|
||||
m.cursorUp()
|
||||
case key.Matches(msg, m.keyMap.LineDown):
|
||||
log.Println("down")
|
||||
m.cursorDown()
|
||||
}
|
||||
}
|
||||
m.suggestions = autocomplete.LoadSuggestions(command)
|
||||
return m
|
||||
}
|
||||
|
||||
func (m Model) renderSuggestion(suggestion string, position int) string {
|
||||
if position == m.cursor {
|
||||
return lipgloss.NewStyle().Background(lipgloss.Color("#7D56F4")).Width(SuggestionWidth).Render(suggestion)
|
||||
}
|
||||
return lipgloss.NewStyle().Render(suggestion)
|
||||
}
|
||||
|
||||
func (m Model) View() string {
|
||||
if len(m.suggestions) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
var style = lipgloss.NewStyle().
|
||||
Bold(true).
|
||||
Width(SuggestionWidth).
|
||||
BorderStyle(lipgloss.NormalBorder()).
|
||||
BorderForeground(lipgloss.Color("63"))
|
||||
|
||||
r := make([]string, len(m.suggestions))
|
||||
for idx, suggestion := range m.suggestions {
|
||||
r[idx] = m.renderSuggestion(suggestion, idx)
|
||||
}
|
||||
|
||||
return style.Render(strings.Join(r, "\n"))
|
||||
}
|
||||
@@ -6,21 +6,31 @@ import (
|
||||
"github.com/charmbracelet/bubbles/textinput"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/cpendery/clac/ui/suggestions"
|
||||
"github.com/cpendery/clac/ui/utils"
|
||||
)
|
||||
|
||||
|
||||
|
||||
type model struct {
|
||||
textInput textinput.Model
|
||||
suggestions suggestions.Model
|
||||
windowHeight int
|
||||
windowWidth int
|
||||
}
|
||||
|
||||
const (
|
||||
prompt = "> "
|
||||
promptOffset = len(prompt)
|
||||
cursorOffset = 1
|
||||
widthOffset = promptOffset + cursorOffset
|
||||
)
|
||||
|
||||
func Start() model {
|
||||
ti := textinput.New()
|
||||
ti.Cursor.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("63"))
|
||||
ti.Focus()
|
||||
return model{textInput: ti}
|
||||
sug := suggestions.New()
|
||||
|
||||
return model{textInput: ti, suggestions: sug}
|
||||
}
|
||||
|
||||
func (m model) Init() tea.Cmd {
|
||||
@@ -32,44 +42,34 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
case tea.KeyMsg:
|
||||
switch msg.Type {
|
||||
case tea.KeyEnter, tea.KeyCtrlC, tea.KeyEsc:
|
||||
// return m, tea.ClearScreen
|
||||
return m, tea.Quit
|
||||
case tea.KeyTab:
|
||||
activeSuggestion := m.suggestions.ActiveSuggestion()
|
||||
s := m.textInput.Value() + activeSuggestion
|
||||
m.textInput.SetValue(s)
|
||||
m.textInput.SetCursor(len(s))
|
||||
return m, nil
|
||||
}
|
||||
case tea.WindowSizeMsg:
|
||||
m.windowHeight = msg.Height
|
||||
m.windowWidth = msg.Width
|
||||
m.textInput.Width = m.windowWidth - widthOffset
|
||||
}
|
||||
|
||||
var cmd tea.Cmd
|
||||
m.suggestions = m.suggestions.Update(msg, m.textInput.Value())
|
||||
m.textInput, cmd = m.textInput.Update(msg)
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
func (m model) View() string {
|
||||
|
||||
sugWidth := 22
|
||||
marginLeft := clamp(len(m.textInput.Value())+len(m.textInput.Prompt), 0, m.windowWidth-sugWidth)
|
||||
|
||||
var style = lipgloss.NewStyle().
|
||||
Bold(true).
|
||||
Foreground(lipgloss.Color("#FAFAFA")).
|
||||
Background(lipgloss.Color("#7D56F4")).
|
||||
PaddingTop(2).
|
||||
PaddingLeft(4).
|
||||
Width(sugWidth).
|
||||
MarginLeft(marginLeft)
|
||||
suggestionsSpacing := lipgloss.NewStyle().MarginLeft(
|
||||
utils.Clamp(len(m.textInput.Value())+len(m.textInput.Prompt), 0, m.windowWidth-suggestions.SuggestionWidth-suggestions.BorderOffset),
|
||||
)
|
||||
|
||||
return fmt.Sprintf(
|
||||
"%s\n%s",
|
||||
m.textInput.View(),
|
||||
style.Render("Hello, kitty"),
|
||||
suggestionsSpacing.Render(m.suggestions.View()),
|
||||
)
|
||||
}
|
||||
|
||||
// work
|
||||
func clamp(v, low, high int) int {
|
||||
if high < low {
|
||||
low, high = high, low
|
||||
}
|
||||
return min(high, max(low, v))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package utils
|
||||
|
||||
func Clamp(v, low, high int) int {
|
||||
if high < low {
|
||||
low, high = high, low
|
||||
}
|
||||
return min(high, max(low, v))
|
||||
}
|
||||
Reference in New Issue
Block a user