Files
five/hbrtl/dbedit.go
Charles KWON OhJun 59568f3301 Five v0.9 — Harbour + Go fusion language
- Compiler: PP → Lexer → Parser → Analyzer → Gengo pipeline
- Parser: 232/236 (98%) Harbour compatibility, registry-based dispatch
- RTL: 351 Harbour-compatible functions
- RDD: DBF/NTX/CDX engines with Rushmore bitmap optimization
- Go Interop: IMPORT + pkg.Func() + obj:Method() with FastPath (15M calls/sec)
- HB_FUNC API: Full Harbour C API compatible Go bridge
- Concurrency: SPAWN/LAUNCH/GOROUTINE, <-, WATCH, PARALLEL FOR, ASYNC/AWAIT
- Extensions: Multi-return, DEFER, Slice, f-string, Nil-safe ?:, CONST
- Macro Compiler: Runtime AST parsing and evaluation
- Debugger: TUI debugger with source display, breakpoints, stepping
- FRB: Native + Pcode dual mode runtime binary
- Tests: 13 packages ALL PASS

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 09:41:50 +09:00

263 lines
5.4 KiB
Go

// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com)
// All rights reserved.
// dbEdit() — Harbour-compatible implementation using TBrowse.
// Same pattern as Harbour's dbedit.prg:
// oBrowse := TBrowseDB() → addColumn → loop { stabilize + inkey + navigate }
package hbrtl
import (
"five/hbrt"
"five/hbrdd"
"fmt"
"strings"
)
// Key constants
const (
kUP = 5
kDOWN = 24
kLEFT = 19
kRIGHT = 4
kPGUP = 18
kPGDN = 3
kHOME = 1
kEND = 6
kESC = 27
)
// DbEdit implements Harbour dbEdit() function.
func DbEdit(t *hbrt.Thread) {
nParams := t.ParamCount()
t.Frame(nParams, 0)
defer t.EndProc()
nTop := 0
nLeft := 0
nBottom := 22
nRight := 79
if nParams >= 1 {
nTop = int(t.Local(1).AsNumInt())
}
if nParams >= 2 {
nLeft = int(t.Local(2).AsNumInt())
}
if nParams >= 3 {
nBottom = int(t.Local(3).AsNumInt())
}
if nParams >= 4 {
nRight = int(t.Local(4).AsNumInt())
}
wa := getDbEditWA(t)
if wa == nil {
t.PushBool(false)
t.RetValue()
return
}
area := wa.Current()
if area == nil {
t.PushBool(false)
t.RetValue()
return
}
// Create TBrowse object
oBrowse := hbrt.NewObject(hbrt.FindClass("TBROWSE").ID)
arr := oBrowse.AsArray()
cls := hbrt.GetClass(arr.Class)
setF(arr, cls, "NTOP", hbrt.MakeInt(nTop))
setF(arr, cls, "NLEFT", hbrt.MakeInt(nLeft))
setF(arr, cls, "NBOTTOM", hbrt.MakeInt(nBottom))
setF(arr, cls, "NRIGHT", hbrt.MakeInt(nRight))
rowCount := nBottom - nTop - 1
if rowCount < 1 {
rowCount = 1
}
setF(arr, cls, "NROWCOUNT", hbrt.MakeInt(rowCount))
setF(arr, cls, "CHEADSEP", hbrt.MakeString("-"))
setF(arr, cls, "CCOLSEP", hbrt.MakeString(" | "))
// Skip/GoTop/GoBottom blocks
setF(arr, cls, "BSKIPBLOCK", hbrt.MakeBlock(func(bt *hbrt.Thread) {
bt.Frame(1, 0)
defer bt.EndProc()
n := int(bt.Local(1).AsNumInt())
bt.RetInt(int64(dbSkipBlock(wa, n)))
}, 0))
setF(arr, cls, "BGOTOPBLOCK", hbrt.MakeBlock(func(bt *hbrt.Thread) {
bt.Frame(0, 0)
defer bt.EndProc()
if a := wa.Current(); a != nil {
a.GoTop()
}
bt.RetNil()
}, 0))
setF(arr, cls, "BGOBOTTOMBLOCK", hbrt.MakeBlock(func(bt *hbrt.Thread) {
bt.Frame(0, 0)
defer bt.EndProc()
if a := wa.Current(); a != nil {
a.GoBottom()
}
bt.RetNil()
}, 0))
// Add columns
colsArr := getFA(arr, cls, "ACOLUMNS")
for i := 0; i < area.FieldCount(); i++ {
fi := area.GetFieldInfo(i)
idx := i
oCol := hbrt.NewObject(hbrt.FindClass("TBCOLUMN").ID)
ca := oCol.AsArray()
cc := hbrt.GetClass(ca.Class)
setF(ca, cc, "CHEADING", hbrt.MakeString(fi.Name))
setF(ca, cc, "BBLOCK", hbrt.MakeBlock(func(bt *hbrt.Thread) {
bt.Frame(0, 0)
defer bt.EndProc()
val, _ := area.GetValue(idx)
bt.PushValue(val)
bt.RetValue()
}, 0))
w := fi.Len
if w < len(fi.Name) {
w = len(fi.Name)
}
if w > 25 {
w = 25
}
if w < 4 {
w = 4
}
setF(ca, cc, "NWIDTH", hbrt.MakeInt(w))
colsArr.Items = append(colsArr.Items, oCol)
}
// Set raw terminal mode
dbEditRawOn()
defer dbEditRawOff()
fmt.Print("\033[2J\033[H\033[?25l")
defer fmt.Print("\033[?25h\033[0m\n")
area.GoTop()
screenWidth := nRight - nLeft + 1
rc, _ := area.RecCount()
// Main loop — same as Harbour dbEdit
for {
// stabilize
t.PushValue(oBrowse)
t.Send("STABILIZE", 0)
t.Pop2()
// Status bar
colPos := getFI(arr, cls, "NCOLPOS")
colName := ""
if colPos >= 1 && colPos <= len(colsArr.Items) {
ca2 := colsArr.Items[colPos-1].AsArray()
cc2 := hbrt.GetClass(ca2.Class)
colName = getFS(ca2, cc2, "CHEADING")
}
eof := ""
if area.EOF() {
eof = " EOF"
}
status := fmt.Sprintf(" Rec %d/%d [%s]%s Arrows PgUp/Dn Home/End ESC=quit",
area.RecNo(), rc, strings.TrimSpace(colName), eof)
fmt.Printf("\033[%d;%dH\033[7m%-*s\033[0m", nBottom+2, nLeft+1, screenWidth, status)
// Read key
key := dbEditReadKey()
switch key {
case 'A': // ESC[A = Up
t.PushValue(oBrowse)
t.Send("UP", 0)
t.Pop2()
case 'B': // ESC[B = Down
t.PushValue(oBrowse)
t.Send("DOWN", 0)
t.Pop2()
case 'D': // ESC[D = Left
t.PushValue(oBrowse)
t.Send("LEFT", 0)
t.Pop2()
case 'C': // ESC[C = Right
t.PushValue(oBrowse)
t.Send("RIGHT", 0)
t.Pop2()
case '5': // PgUp
t.PushValue(oBrowse)
t.Send("PAGEUP", 0)
t.Pop2()
case '6': // PgDn
t.PushValue(oBrowse)
t.Send("PAGEDOWN", 0)
t.Pop2()
case 'H': // Home
t.PushValue(oBrowse)
t.Send("GOTOP", 0)
t.Pop2()
case 'F': // End
t.PushValue(oBrowse)
t.Send("GOBOTTOM", 0)
t.Pop2()
case kESC, 'q', 'Q':
fmt.Print("\033[2J\033[H")
t.PushBool(true)
t.RetValue()
return
}
}
}
func dbEditRawOn() {
InitRawTerminal() // from rawtty.go
}
func dbEditRawOff() {
RestoreTerminal() // from rawtty.go
}
func dbEditReadKey() int {
return ReadKey() // from rawtty.go
}
// helpers
func setF(a *hbrt.HbArray, c *hbrt.ClassDef, n string, v hbrt.Value) {
if i := c.FieldIndex(n); i >= 0 {
a.Items[i] = v
}
}
func getFA(a *hbrt.HbArray, c *hbrt.ClassDef, n string) *hbrt.HbArray {
if i := c.FieldIndex(n); i >= 0 {
return a.Items[i].AsArray()
}
return nil
}
func getFI(a *hbrt.HbArray, c *hbrt.ClassDef, n string) int {
if i := c.FieldIndex(n); i >= 0 {
return int(a.Items[i].AsNumInt())
}
return 0
}
func getFS(a *hbrt.HbArray, c *hbrt.ClassDef, n string) string {
if i := c.FieldIndex(n); i >= 0 {
return a.Items[i].AsString()
}
return ""
}
func getDbEditWA(t *hbrt.Thread) *hbrdd.WorkAreaManager {
if t.WA == nil {
return nil
}
wa, ok := t.WA.(*hbrdd.WorkAreaManager)
if !ok {
return nil
}
return wa
}