Update tea and lexer

This commit is contained in:
Pablu
2026-01-19 22:49:22 +01:00
parent a8c7ad60c3
commit 1af6539deb
7 changed files with 293 additions and 38 deletions

67
cmd/sqv-tea/debug.log Normal file
View File

@@ -0,0 +1,67 @@
2026/01/19 11:46:36 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:46:36 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:46:53 DEBU Enter was pressed
2026/01/19 11:46:53 DEBU Selected item okay and batched
2026/01/19 11:47:01 DEBU Enter was pressed
2026/01/19 11:47:01 DEBU Selected item okay and batched
2026/01/19 11:47:03 DEBU Enter was pressed
2026/01/19 11:47:03 DEBU Selected item okay and batched
2026/01/19 11:47:08 DEBU Enter was pressed
2026/01/19 11:47:08 DEBU Selected item okay and batched
2026/01/19 11:47:21 DEBU Enter was pressed
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:09 DEBU Enter was pressed
2026/01/19 11:50:09 DEBU Selected item okay and batched
2026/01/19 11:50:10 DEBU Enter was pressed
2026/01/19 11:50:10 DEBU Selected item okay and batched
2026/01/19 11:50:11 DEBU Enter was pressed
2026/01/19 11:50:11 DEBU Selected item okay and batched
2026/01/19 11:50:12 DEBU Enter was pressed
2026/01/19 11:50:12 DEBU Selected item okay and batched
2026/01/19 11:50:13 DEBU Enter was pressed
2026/01/19 11:50:13 DEBU Selected item okay and batched
2026/01/19 11:50:15 DEBU Enter was pressed
2026/01/19 11:50:15 DEBU Selected item okay and batched
2026/01/19 11:50:17 DEBU Enter was pressed
2026/01/19 11:50:17 DEBU Selected item okay and batched
2026/01/19 11:50:21 DEBU Enter was pressed
2026/01/19 11:50:21 DEBU Selected item okay and batched
2026/01/19 11:50:22 DEBU Enter was pressed
2026/01/19 11:50:22 DEBU Selected item okay and batched

View File

@@ -1,18 +1,26 @@
package main
import (
"log"
"fmt"
"os"
engine "git.pablu.de/pablu/sqv-engine"
"github.com/charmbracelet/bubbles/list"
"github.com/charmbracelet/bubbles/table"
"github.com/charmbracelet/bubbles/textarea"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/charmbracelet/log"
)
type mainModel struct {
width, height int
focused int
pickerFocused bool
manager *engine.Manager
lastFocused int
viewStyle lipgloss.Style
editorStyle lipgloss.Style
@@ -21,23 +29,30 @@ type mainModel struct {
table table.Model
editor textarea.Model
picker list.Model
debugMsgs []string
}
var (
defaultStyle = lipgloss.NewStyle().
Align(lipgloss.Center).
BorderStyle(lipgloss.NormalBorder())
Align(lipgloss.Center).
BorderStyle(lipgloss.NormalBorder())
focusedStyle = defaultStyle.BorderForeground(lipgloss.Color("202"))
baseTableStyle = lipgloss.NewStyle().
BorderStyle(lipgloss.NormalBorder())
)
type item struct {
title, desc string
title string
}
func (i item) Title() string { return i.title }
func (i item) Description() string { return i.desc }
func (i item) Description() string { return i.title }
func (i item) FilterValue() string { return i.title }
func newMainModerl() mainModel {
func newMainModerl(manager *engine.Manager) mainModel {
ed := textarea.New()
ed.Placeholder = "Try \"SELECT * FROM ?;\""
@@ -66,15 +81,15 @@ func newMainModerl() mainModel {
items := []list.Item{
item{
title: "user",
desc: "users table",
// desc: "users table",
},
item{
title: "job",
desc: "jobs table",
// desc: "jobs table",
},
item{
title: "user_has_job",
desc: "user has a job",
// desc: "user has a job",
},
}
@@ -82,72 +97,199 @@ func newMainModerl() mainModel {
li.Title = "Table Picker"
return mainModel{
focused: 0,
viewStyle: defaultStyle,
editorStyle: defaultStyle,
pickerStyle: defaultStyle.BorderStyle(lipgloss.RoundedBorder()).BorderForeground(lipgloss.Color("67")),
editor: ed,
table: ta,
picker: li,
pickerFocused: false,
viewStyle: baseTableStyle,
editorStyle: defaultStyle,
pickerStyle: defaultStyle.BorderStyle(lipgloss.RoundedBorder()).BorderForeground(lipgloss.Color("67")),
editor: ed,
table: ta,
picker: li,
manager: manager,
}
}
func (m mainModel) Init() tea.Cmd {
return nil
return m.GetTableDefinitions
}
func (m mainModel) GetTableDefinitions() tea.Msg {
tables := m.manager.GetTables()
tableNames := make([]list.Item, len(tables))
for i, table := range tables {
tableNames[i] = item{table.Name}
}
return tableDefinitionsMsg{
tableNames,
}
}
type tableDefinitionsMsg struct {
Tables []list.Item
}
func (m mainModel) GetFirstTable() tea.Msg {
t := m.manager.GetTables()[0]
cmd := m.GetTable(t.Name)
return cmd()
}
func (m mainModel) GetTable(name string) tea.Cmd {
return func() tea.Msg {
t, ok := m.manager.GetTable(name)
if !ok {
return nil
}
m.manager.LoadTable(&t)
msg := tableMsg{
Columns: make([]table.Column, len(t.Columns)),
Rows: make([]table.Row, len(t.Rows)),
}
lRow := make([]int, len(t.Columns))
lRowC := 0
for i, r := range t.Rows {
msg.Rows[i] = r.Values
// Pure stupid estemation of how long each row should be
if i == 0 {
for j, v := range r.Values {
vLen := len(v)
vLen = max(vLen, len(t.Columns[j].Name))
lRow[j] = vLen
lRowC += vLen
}
}
}
h, _ := m.viewStyle.GetFrameSize()
width := m.viewStyle.GetWidth() - h
width -= (len(t.Columns) + 1) * 2
for i, c := range t.Columns {
columnWidth := float64(width) * (float64(lRow[i]) / float64(lRowC))
msg.Columns[i] = table.Column{
Title: c.Name,
Width: int(columnWidth),
}
}
return msg
}
}
type tableMsg struct {
Columns []table.Column
Rows []table.Row
}
func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var (
edCmd tea.Cmd
taCmd tea.Cmd
liCmd tea.Cmd
edCmd tea.Cmd
taCmd tea.Cmd
liCmd tea.Cmd
pickerCmd tea.Cmd
cmds tea.Cmd
)
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case "ctrl+c", "q":
case "ctrl+c":
return m, tea.Quit
case "tab":
if m.table.Focused() {
m.table.Blur()
m.editor.Focus()
m.lastFocused = 0
} else {
m.editor.Blur()
m.table.Focus()
m.lastFocused = 1
}
case "ctrl+e":
if m.focused == 1 {
m.focused = 0
m.pickerFocused = !m.pickerFocused
// Picker is now focused
if m.pickerFocused {
m.table.Blur()
m.editor.Blur()
} else {
m.focused = 1
if m.lastFocused == 0 {
m.editor.Focus()
} else {
m.table.Focus()
}
}
case "enter":
log.Debug("Enter was pressed")
if m.pickerFocused {
i, ok := m.picker.SelectedItem().(item)
if ok {
log.Debug("Selected item okay and batched")
cmds = tea.Batch(cmds, m.GetTable(i.title))
}
m.pickerFocused = !m.pickerFocused
if m.lastFocused == 0 {
m.editor.Focus()
} else {
m.table.Focus()
}
}
}
case tea.WindowSizeMsg:
m = m.updateStyles(msg.Width, msg.Height)
cmds = tea.Batch(cmds, m.GetFirstTable)
case tableMsg:
// log.Debug("Setting new table view", "columns", msg.Columns, "rows", msg.Rows)
m.table = table.New(
table.WithColumns(msg.Columns),
table.WithRows(msg.Rows),
)
m.table.SetWidth(m.viewStyle.GetWidth() - 2)
m.table.SetHeight(m.viewStyle.GetHeight() - 2)
// m.table.SetColumns(msg.Columns)
// m.table.SetRows(msg.Rows)
case tableDefinitionsMsg:
pickerCmd = m.picker.SetItems(msg.Tables)
}
m.editor, edCmd = m.editor.Update(msg)
m.table, taCmd = m.table.Update(msg)
if m.focused == 1 {
if m.pickerFocused {
m.picker, liCmd = m.picker.Update(msg)
}
return m, tea.Batch(edCmd, taCmd, liCmd)
return m, tea.Batch(edCmd, taCmd, liCmd, cmds, pickerCmd)
}
func (m mainModel) updateStyles(width, height int) mainModel {
h, v := defaultStyle.GetFrameSize()
bh, bv := baseTableStyle.GetFrameSize()
topHeight := (height * 3 / 4) - h
log.Debug("Update styles", "bh", bh, "bv", bv, "h", h, "v", v)
topHeight := (height * 3 / 4) - bh
editorHeight := (height * 1 / 4) - h
m.editorStyle = defaultStyle.
Width(width - v).
Height(editorHeight)
m.viewStyle = defaultStyle.
Width(width - v).
m.viewStyle = baseTableStyle.
Width(width - bv).
Height(topHeight)
m.editor.SetWidth(m.editorStyle.GetWidth())
@@ -175,7 +317,6 @@ func (m mainModel) updateStyles(width, height int) mainModel {
}
func (m mainModel) View() string {
view := m.viewStyle.
Render(m.table.View())
@@ -183,9 +324,8 @@ func (m mainModel) View() string {
Render(m.editor.View())
main := lipgloss.JoinVertical(lipgloss.Top, view, editor)
_ = main
if m.focused == 1 {
if m.pickerFocused {
x := (m.width / 2) - m.picker.Width()/2
y := (m.height / 2) - m.picker.Height()/2
@@ -196,8 +336,31 @@ func (m mainModel) View() string {
}
func main() {
p := tea.NewProgram(newMainModerl(), tea.WithAltScreen())
f, err := tea.LogToFile("debug.log", "debug")
if err != nil {
fmt.Println("fatal:", err)
os.Exit(1)
}
defer f.Close()
log.SetLevel(log.DebugLevel)
log.SetOutput(f)
m, err := engine.NewManager("../vdcmp/db.sqlite")
if err != nil {
fmt.Println("fatal:", err)
os.Exit(1)
}
err = m.Start()
if err != nil {
fmt.Println("fatal:", err)
os.Exit(1)
}
p := tea.NewProgram(newMainModerl(m), tea.WithAltScreen())
if _, err := p.Run(); err != nil {
log.Fatalf("could not start program: %v\n", err)
fmt.Println("fatal:", err)
os.Exit(1)
}
}

View File

@@ -168,6 +168,10 @@ func main() {
return event
})
menuView.SetFocusFunc(func() {
menuView.SetBorderColor(tcell.ColorRed)
})
if err := app.SetRoot(horizontalFlex, true).EnableMouse(true).Run(); err != nil {
panic(err)
}

3
go.mod
View File

@@ -17,6 +17,7 @@ require (
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/gdamore/encoding v1.0.1 // indirect
github.com/go-logfmt/logfmt v0.6.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
@@ -27,6 +28,7 @@ require (
github.com/rivo/uniseg v0.4.7 // indirect
github.com/sahilm/fuzzy v0.1.1 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
golang.org/x/term v0.28.0 // indirect
golang.org/x/text v0.25.0 // indirect
)
@@ -35,6 +37,7 @@ require (
github.com/charmbracelet/bubbles v0.21.0
github.com/charmbracelet/bubbletea v1.3.10
github.com/charmbracelet/lipgloss v1.1.0
github.com/charmbracelet/log v0.4.2
github.com/muesli/reflow v0.3.0
golang.org/x/sys v0.36.0 // indirect
)

6
go.sum
View File

@@ -10,6 +10,8 @@ github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4p
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
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/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.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ=
@@ -24,6 +26,8 @@ github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uh
github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo=
github.com/gdamore/tcell/v2 v2.8.1 h1:KPNxyqclpWpWQlPLx6Xui1pMk8S+7+R37h3g07997NU=
github.com/gdamore/tcell/v2 v2.8.1/go.mod h1:bj8ori1BG3OYMjmb3IklZVWfZUJ1UBQt9JXrOCOhGWw=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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=
@@ -61,6 +65,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
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/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=

View File

@@ -25,6 +25,10 @@ const (
QUOTE
SINGLE_QUOTE
// TYPES
TYPE_NUMERIC
TYPE_TEXT
// Keywords
CREATE
TABLE
@@ -157,6 +161,12 @@ func (l *Lexer) Lex() (Position, Token, string) {
}
return startPos, IDENT, lit
} else if unicode.IsNumber(r) {
startPos := l.pos
l.backup()
lit := l.lexIdent()
return startPos, TYPE_NUMERIC, lit
} else {
return l.pos, ILLEGAL, string(r)
}
@@ -176,7 +186,9 @@ func (l *Lexer) lexIdent() string {
}
l.pos.column++
if unicode.IsLetter(r) || unicode.IsNumber(r) || r == '_' {
// Dont allow dot, just for testing with numeric
if unicode.IsLetter(r) || unicode.IsNumber(r) || r == '_' || r == '.' {
// Change this to stringstream or something similar
lit = lit + string(r)
} else {

View File

@@ -39,7 +39,7 @@ func (p *Parser) parseInsert() (*InsertStatement, error) {
for loop := true; loop; {
_, tok, val := p.scan()
switch tok {
case IDENT:
case IDENT, TYPE_NUMERIC:
// TODO, convert to actual datatype?
values = append(values, val)
case COMMA, QUOTE, SINGLE_QUOTE, BACKQUOTE:
@@ -47,7 +47,7 @@ func (p *Parser) parseInsert() (*InsertStatement, error) {
case RPAREN:
loop = false
default:
return nil, p.unexpectedToken(IDENT, RPAREN, COMMA)
return nil, p.unexpectedToken(IDENT, RPAREN, COMMA, TYPE_NUMERIC)
}
}