Terminal & Code: components 321-340
This commit is contained in:
108 files changed
+8733
-1
No files matched your search
@@ -1,3 +1,245 @@
|
||||
package main
|
||||
|
||||
var codePages = []page{}
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
)
|
||||
|
||||
const codeCategory = "Terminal & Code"
|
||||
|
||||
var codePages = []page{
|
||||
{ID: "321", Name: "Terminal", ZhName: "终端", Category: codeCategory, Demo: codeTerminalDemo, Code: codeTerminalCode,
|
||||
Keys: "Focused: keys and text go to the PTY; Ctrl+letters send control codes; Cmd+C/V (Ctrl+Shift+C/V elsewhere) copy and paste; Shift+PageUp/PageDown scroll back; drag selects."},
|
||||
{ID: "322", Name: "TerminalTabs", ZhName: "终端标签", Category: codeCategory, Demo: codeTerminalTabsDemo, Code: codeTerminalTabsCode,
|
||||
Keys: "Left/Right, Home/End switch tabs; F2 or double-click renames (Enter saves, Escape cancels); Delete closes, asking first while a process runs."},
|
||||
{ID: "323", Name: "TerminalSearch", ZhName: "终端搜索", Category: codeCategory, Demo: codeTerminalSearchDemo, Code: codeTerminalSearchCode,
|
||||
Keys: "Enter / Shift+Enter go to the next / previous match; Cmd+G (F3 elsewhere) too."},
|
||||
{ID: "324", Name: "CommandHistory", ZhName: "命令历史", Category: codeCategory, Demo: codeCommandHistoryDemo, Code: codeCommandHistoryCode,
|
||||
Keys: "Up/Down choose; Enter or double-click replays; typing in the list jumps by command."},
|
||||
{ID: "325", Name: "LogViewer", ZhName: "日志查看器", Category: codeCategory, Demo: codeLogViewerDemo, Code: codeLogViewerCode,
|
||||
Keys: "Space toggles a level or Follow; arrows, PageUp/PageDown, Home/End scroll the log."},
|
||||
{ID: "326", Name: "ProcessList", ZhName: "进程列表", Category: codeCategory, Demo: codeProcessListDemo, Code: codeProcessListCode,
|
||||
Keys: "Up/Down choose; Delete ends the chosen process after confirming; click a header to sort, again to reverse."},
|
||||
{ID: "327", Name: "CodeViewer", ZhName: "代码查看器", Category: codeCategory, Demo: codeCodeViewerDemo, Code: codeCodeViewerCode,
|
||||
Keys: "Up/Down move the current line; Shift extends; Cmd/Ctrl+C copies; Left folds, Right unfolds."},
|
||||
{ID: "328", Name: "Minimap", ZhName: "代码缩略图", Category: codeCategory, Demo: codeMinimapDemo, Code: codeMinimapCode,
|
||||
Keys: "Up/Down move a line, PageUp/PageDown a screen, Home/End to the ends; click or drag jumps."},
|
||||
{ID: "329", Name: "FindWidget", ZhName: "查找替换", Category: codeCategory, Demo: codeFindWidgetDemo, Code: codeFindWidgetCode,
|
||||
Keys: "Enter / Shift+Enter next / previous match; Enter in the replace field replaces the current match."},
|
||||
{ID: "330", Name: "SearchPanel", ZhName: "多文件搜索", Category: codeCategory, Demo: codeSearchPanelDemo, Code: codeSearchPanelCode,
|
||||
Keys: "Up/Down choose; Enter opens a match or folds a file; Left folds, Right unfolds a file."},
|
||||
{ID: "331", Name: "ProblemsPanel", ZhName: "问题面板", Category: codeCategory, Demo: codeProblemsDemo, Code: codeProblemsPanelCode,
|
||||
Keys: "Space toggles a severity; Up/Down choose; Enter opens a problem or folds a file; Left/Right fold and unfold."},
|
||||
{ID: "332", Name: "OutputPanel", ZhName: "输出面板", Category: codeCategory, Demo: codeOutputDemo, Code: codeOutputPanelCode,
|
||||
Keys: "Tab reaches the channel menu, Follow and Clear; arrows choose a channel in the open menu."},
|
||||
{ID: "333", Name: "SymbolOutline", ZhName: "符号大纲", Category: codeCategory, Demo: codeSymbolDemo, Code: codeSymbolOutlineCode,
|
||||
Keys: "Up/Down move; Right opens, Left closes; Enter or double-click jumps; typing jumps by name."},
|
||||
{ID: "334", Name: "CompletionMenu", ZhName: "补全菜单", Category: codeCategory, Demo: codeCompletionDemo, Code: codeCompletionMenuCode,
|
||||
Keys: "While typing: Down/Up move, Tab or Enter accepts, Escape closes; the editor keeps the focus."},
|
||||
{ID: "335", Name: "DebugToolbar", ZhName: "调试工具栏", Category: codeCategory, Demo: codeDebugDemo, Code: codeDebugToolbarCode,
|
||||
Keys: "F5 continue, F6 pause, F10 step over, F11 step into, Shift+F11 step out, Cmd/Ctrl+Shift+F5 restart, Shift+F5 stop; Left/Right inside the toolbar."},
|
||||
{ID: "336", Name: "BreakpointList", ZhName: "断点列表", Category: codeCategory, Demo: codeBreakpointDemo, Code: codeBreakpointListCode,
|
||||
Keys: "Space toggles; Enter on a location opens it; the condition field saves with Enter, cancels with Escape."},
|
||||
{ID: "337", Name: "CallStack", ZhName: "调用栈", Category: codeCategory, Demo: codeCallStackDemo, Code: codeCallStackCode,
|
||||
Keys: "Up/Down choose a frame; Enter folds a thread; Left/Right fold and unfold."},
|
||||
{ID: "338", Name: "VariablesPanel", ZhName: "变量面板", Category: codeCategory, Demo: codeVariablesDemo, Code: codeVariablesPanelCode,
|
||||
Keys: "Up/Down move; Right opens, Left closes; Enter, F2 or double-click edits a leaf; Enter saves, Escape cancels."},
|
||||
{ID: "339", Name: "HexViewer", ZhName: "十六进制查看器", Category: codeCategory, Demo: codeHexDemo, Code: codeHexViewerCode,
|
||||
Keys: "Arrows move by byte and row; Shift extends; Home/End go to the row ends; Cmd/Ctrl+C copies hex."},
|
||||
{ID: "340", Name: "Flamegraph", ZhName: "火焰图", Category: codeCategory, Demo: codeFlamegraphDemo, Code: codeFlamegraphCode,
|
||||
Keys: "Up parent, Down first child, Left/Right siblings; Enter zooms in, Escape zooms out; the readout is announced."},
|
||||
}
|
||||
|
||||
// codeDemoCheck is a switch that logs its changes.
|
||||
func codeDemoCheck(c *ui.Context, v *bool, label string, log func(string)) {
|
||||
if mujica.Switch(c, v, label, mujica.SwitchOptions{}).Changed() {
|
||||
log(fmt.Sprintf("%s: %v", label, *v))
|
||||
}
|
||||
}
|
||||
|
||||
func codeDemoControls(c *ui.Context, fn func()) {
|
||||
ui.Row(c).Gap(16).Wrap().AlignItems(ui.Center).Margin(0, 0, 16, 0).Children(fn)
|
||||
}
|
||||
|
||||
func codeTerminalDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
term mujica.TerminalState
|
||||
line string
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "321", func() *state {
|
||||
s := &state{}
|
||||
s.term.Write("Welcome to the \x1b[1;35mcourt\x1b[0m shell. Type and press Enter.\r\n$ ")
|
||||
return s
|
||||
})
|
||||
st := *s
|
||||
codeDemoControls(c, func() {
|
||||
codeDemoCheck(c, &st.disabled, "Disabled", log)
|
||||
if mujica.Button(c, "Clear screen", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
st.term.Write("\x1b[2J\x1b[H$ ")
|
||||
log("clear screen")
|
||||
}
|
||||
})
|
||||
// A tiny echo shell stands in for the PTY.
|
||||
mujica.Terminal(c, &st.term, mujica.TerminalOptions{Disabled: st.disabled,
|
||||
Input: func(data string) {
|
||||
log(fmt.Sprintf("input %q", data))
|
||||
for _, r := range data {
|
||||
switch r {
|
||||
case '\r':
|
||||
st.term.Write("\r\n" + codeShell(st.line) + "$ ")
|
||||
st.line = ""
|
||||
case 0x7f:
|
||||
if st.line != "" {
|
||||
st.line = st.line[:len(st.line)-1]
|
||||
st.term.Write("\b \b")
|
||||
}
|
||||
default:
|
||||
if r >= ' ' {
|
||||
st.line += string(r)
|
||||
st.term.Write(string(r))
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Resized: func(cols, rows int) { log(fmt.Sprintf("resized %d×%d", cols, rows)) },
|
||||
}).Size(640, 260)
|
||||
}
|
||||
|
||||
// codeShell answers a few commands for the demo terminal.
|
||||
func codeShell(line string) string {
|
||||
switch f := strings.Fields(line); {
|
||||
case len(f) == 0:
|
||||
return ""
|
||||
case f[0] == "ls":
|
||||
return "\x1b[34mchronicles\x1b[0m decrees.txt \x1b[32mseal.sh\x1b[0m\r\n"
|
||||
case f[0] == "echo":
|
||||
return strings.Join(f[1:], " ") + "\r\n"
|
||||
case f[0] == "date":
|
||||
return time.Now().Format(time.RFC1123) + "\r\n"
|
||||
}
|
||||
return "\x1b[31m" + line + ": command not found\x1b[0m\r\n"
|
||||
}
|
||||
|
||||
func codeTerminalTabsDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
tabs []mujica.TerminalTab
|
||||
active string
|
||||
next int
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "322", func() *state {
|
||||
return &state{tabs: []mujica.TerminalTab{{ID: "1", Title: "zsh"}, {ID: "2", Title: "build", Busy: true}, {ID: "3", Title: "logs"}}, active: "1", next: 4}
|
||||
})
|
||||
st := *s
|
||||
codeDemoControls(c, func() {
|
||||
codeDemoCheck(c, &st.disabled, "Disabled", log)
|
||||
if mujica.Button(c, "New terminal", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
id := fmt.Sprint(st.next)
|
||||
st.next++
|
||||
st.tabs = append(st.tabs, mujica.TerminalTab{ID: id, Title: "shell " + id})
|
||||
st.active = id
|
||||
log("new " + id)
|
||||
}
|
||||
})
|
||||
r := mujica.TerminalTabs(c, &st.tabs, &st.active, mujica.TerminalTabsOptions{Disabled: st.disabled}, func(id string) {
|
||||
ui.Text(c, "Terminal "+id+" content").TextColor(mujica.Tokens(c).TextMuted)
|
||||
})
|
||||
r.Element.Width(560)
|
||||
if r.Changed {
|
||||
log("active " + st.active)
|
||||
}
|
||||
if r.Renamed != "" {
|
||||
log("renamed " + r.Renamed)
|
||||
}
|
||||
if r.Closed != "" {
|
||||
log("closed " + r.Closed)
|
||||
}
|
||||
}
|
||||
|
||||
func codeTerminalSearchDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
term mujica.TerminalState
|
||||
search mujica.TerminalSearchState
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "323", func() *state {
|
||||
s := &state{}
|
||||
for i := range 60 {
|
||||
if i%9 == 4 {
|
||||
s.term.Write(fmt.Sprintf("[%02d] \x1b[31merror\x1b[0m: seal %d missing\r\n", i, i))
|
||||
} else {
|
||||
s.term.Write(fmt.Sprintf("[%02d] ledger page %d copied\r\n", i, i))
|
||||
}
|
||||
}
|
||||
return s
|
||||
})
|
||||
st := *s
|
||||
codeDemoControls(c, func() { codeDemoCheck(c, &st.disabled, "Disabled", log) })
|
||||
ui.Column(c).Width(600).Gap(8).Children(func() {
|
||||
if mujica.TerminalSearch(c, &st.term, &st.search, mujica.TerminalSearchOptions{Disabled: st.disabled}).Changed() {
|
||||
log(fmt.Sprintf("query %q, match %d", st.search.Query, st.search.Current+1))
|
||||
}
|
||||
mujica.Terminal(c, &st.term, mujica.TerminalOptions{}).Height(220)
|
||||
})
|
||||
}
|
||||
|
||||
func codeCommandHistoryDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
entries []mujica.HistoryEntry
|
||||
s mujica.CommandHistoryState
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "324", func() *state {
|
||||
at := time.Date(2026, 10, 5, 9, 0, 0, 0, time.UTC)
|
||||
var e []mujica.HistoryEntry
|
||||
for i, cmd := range []string{"git pull", "go build ./...", "go test ./...", "make seal", "ls -la", "git status"} {
|
||||
e = append(e, mujica.HistoryEntry{Command: cmd, Time: at.Add(time.Duration(i) * time.Minute)})
|
||||
}
|
||||
e[3].ExitCode = 2
|
||||
return &state{entries: e}
|
||||
})
|
||||
st := *s
|
||||
codeDemoControls(c, func() { codeDemoCheck(c, &st.disabled, "Disabled", log) })
|
||||
r := mujica.CommandHistory(c, st.entries, &st.s, mujica.CommandHistoryOptions{Disabled: st.disabled})
|
||||
r.Element.Size(480, 300)
|
||||
if r.Replayed != nil {
|
||||
log("replay " + r.Replayed.Command)
|
||||
st.entries = append(st.entries, mujica.HistoryEntry{Command: r.Replayed.Command, Time: time.Now()})
|
||||
}
|
||||
}
|
||||
|
||||
func codeLogViewerDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
lines []mujica.LogLine
|
||||
s mujica.LogViewerState
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "325", func() *state {
|
||||
at := time.Date(2026, 10, 5, 9, 0, 0, 0, time.UTC)
|
||||
lines := make([]mujica.LogLine, 100_000)
|
||||
words := []string{"ledger synced", "seal applied", "herald dispatched", "archive rotated"}
|
||||
for i := range lines {
|
||||
lines[i] = mujica.LogLine{Time: at.Add(time.Duration(i) * time.Second), Level: mujica.LogLevel(i % 7 % 4), Text: fmt.Sprintf("%s #%d", words[i%4], i)}
|
||||
}
|
||||
return &state{lines: lines}
|
||||
})
|
||||
st := *s
|
||||
codeDemoControls(c, func() {
|
||||
codeDemoCheck(c, &st.disabled, "Disabled", log)
|
||||
if mujica.Button(c, "Append line", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
st.lines = append(st.lines, mujica.LogLine{Time: time.Now(), Level: mujica.LogWarn, Text: fmt.Sprintf("late seal #%d", len(st.lines))})
|
||||
log("append")
|
||||
}
|
||||
})
|
||||
r := mujica.LogViewer(c, st.lines, &st.s, mujica.LogViewerOptions{Disabled: st.disabled})
|
||||
r.Element.Size(720, 360)
|
||||
if r.Changed() {
|
||||
log(fmt.Sprintf("hidden %v, follow %v", st.s.Hidden, st.s.Follow))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
)
|
||||
|
||||
func codeSymbolDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
s mujica.SymbolOutlineState
|
||||
cursor float64
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "333", func() *state { return &state{cursor: 15} })
|
||||
st := *s
|
||||
codeDemoControls(c, func() {
|
||||
codeDemoCheck(c, &st.disabled, "Disabled", log)
|
||||
ui.Text(c, fmt.Sprintf("Cursor line %.0f", st.cursor))
|
||||
if mujica.Slider(c, &st.cursor, mujica.SliderOptions{Min: 1, Max: 34, Step: 1}).Changed() {
|
||||
log(fmt.Sprintf("cursor %.0f", st.cursor))
|
||||
}
|
||||
})
|
||||
symbols := []mujica.Symbol{
|
||||
{Name: "Decree", Kind: mujica.SymbolType, Line: 6, EndLine: 9, Detail: "struct", Children: []mujica.Symbol{
|
||||
{Name: "ID", Kind: mujica.SymbolField, Line: 7, EndLine: 7, Detail: "int"},
|
||||
{Name: "Title", Kind: mujica.SymbolField, Line: 8, EndLine: 8, Detail: "string"},
|
||||
}},
|
||||
{Name: "Seal", Kind: mujica.SymbolFunction, Line: 13, EndLine: 21, Detail: "(d Decree) (string, error)"},
|
||||
{Name: "Herald", Kind: mujica.SymbolFunction, Line: 23, EndLine: 31, Detail: "(ds []Decree) int"},
|
||||
}
|
||||
r := mujica.SymbolOutline(c, symbols, &st.s, mujica.SymbolOutlineOptions{CursorLine: int(st.cursor), Disabled: st.disabled})
|
||||
r.Element.Size(420, 320)
|
||||
if r.Jumped != nil {
|
||||
log(fmt.Sprintf("jump to %s, line %d", r.Jumped.Name, r.Jumped.Line))
|
||||
}
|
||||
}
|
||||
|
||||
func codeCompletionDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
text string
|
||||
open bool
|
||||
s mujica.CompletionMenuState
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "334", func() *state { return &state{} })
|
||||
st := *s
|
||||
codeDemoControls(c, func() { codeDemoCheck(c, &st.disabled, "Disabled", log) })
|
||||
all := []mujica.Completion{
|
||||
{Label: "Seal", Kind: mujica.SymbolFunction, Detail: "func(d Decree) (string, error)", Doc: "Seal stamps a decree with the royal wax."},
|
||||
{Label: "Search", Kind: mujica.SymbolFunction, Detail: "func(q string) []Decree"},
|
||||
{Label: "Season", Kind: mujica.SymbolConstant, Detail: "= 4"},
|
||||
{Label: "Herald", Kind: mujica.SymbolFunction, Detail: "func(ds []Decree) int"},
|
||||
{Label: "Decree", Kind: mujica.SymbolType, Detail: "struct", Doc: "Decree is an order of the court."},
|
||||
}
|
||||
word := st.text[strings.LastIndexAny(st.text, " .(")+1:]
|
||||
var items []mujica.Completion
|
||||
for _, it := range all {
|
||||
if word != "" && strings.HasPrefix(strings.ToLower(it.Label), strings.ToLower(word)) {
|
||||
items = append(items, it)
|
||||
}
|
||||
}
|
||||
ui.Column(c).Width(420).Gap(8).Children(func() {
|
||||
ui.Text(c, "Type “se” or “de” to see suggestions.").TextColor(mujica.Tokens(c).TextMuted)
|
||||
in := mujica.TextInput(c, &st.text, mujica.TextInputOptions{Label: "Code", Placeholder: "court.", Disabled: st.disabled})
|
||||
if in.Changed() {
|
||||
st.open = len(items) > 0
|
||||
}
|
||||
r := mujica.CompletionMenu(c, in.Input, &st.open, items, &st.s, mujica.CompletionMenuOptions{Disabled: st.disabled})
|
||||
if r.Accepted != nil {
|
||||
st.text = st.text[:len(st.text)-len(word)] + r.Accepted.Label
|
||||
log("accept " + r.Accepted.Label)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func codeDebugDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
dbg mujica.DebugState
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "335", func() *state { return &state{dbg: mujica.DebugPaused} })
|
||||
st := *s
|
||||
names := []string{"Stopped", "Running", "Paused"}
|
||||
codeDemoControls(c, func() {
|
||||
codeDemoCheck(c, &st.disabled, "Disabled", log)
|
||||
ui.Text(c, "State: "+names[st.dbg])
|
||||
})
|
||||
a := mujica.DebugToolbar(c, st.dbg, mujica.DebugToolbarOptions{Disabled: st.disabled}).Action
|
||||
switch a {
|
||||
case mujica.DebugNone:
|
||||
return
|
||||
case mujica.DebugContinue, mujica.DebugRestart:
|
||||
st.dbg = mujica.DebugRunning
|
||||
case mujica.DebugPause, mujica.DebugStepOver, mujica.DebugStepInto, mujica.DebugStepOut:
|
||||
st.dbg = mujica.DebugPaused
|
||||
case mujica.DebugStop:
|
||||
st.dbg = mujica.DebugStopped
|
||||
}
|
||||
log(fmt.Sprintf("action %d, now %s", a, names[st.dbg]))
|
||||
}
|
||||
|
||||
func codeBreakpointDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
bps []mujica.Breakpoint
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "336", func() *state {
|
||||
return &state{bps: []mujica.Breakpoint{
|
||||
{ID: "1", File: "court/seal.go", Line: 14, Enabled: true},
|
||||
{ID: "2", File: "court/ledger.go", Line: 40, Condition: "n > 3"},
|
||||
{ID: "3", File: "main.go", Line: 9, Enabled: true},
|
||||
}}
|
||||
})
|
||||
st := *s
|
||||
codeDemoControls(c, func() { codeDemoCheck(c, &st.disabled, "Disabled", log) })
|
||||
r := mujica.BreakpointList(c, &st.bps, mujica.BreakpointListOptions{Disabled: st.disabled})
|
||||
r.Element.Size(480, 240)
|
||||
for _, e := range []struct{ what, id string }{{"toggled", r.Toggled}, {"condition", r.Edited}, {"open", r.Opened}} {
|
||||
if e.id != "" {
|
||||
log(e.what + " " + e.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func codeCallStackDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
s mujica.CallStackState
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "337", func() *state { return &state{s: mujica.CallStackState{Current: "f1"}} })
|
||||
st := *s
|
||||
codeDemoControls(c, func() { codeDemoCheck(c, &st.disabled, "Disabled", log) })
|
||||
threads := []mujica.DebugThread{
|
||||
{ID: "t1", Name: "main", Frames: []mujica.StackFrame{
|
||||
{ID: "f1", Name: "court.Seal", File: "court/seal.go", Line: 14},
|
||||
{ID: "f2", Name: "court.Herald", File: "court/seal.go", Line: 27},
|
||||
{ID: "f3", Name: "main.main", File: "main.go", Line: 9},
|
||||
}},
|
||||
{ID: "t2", Name: "ledger writer", Frames: []mujica.StackFrame{{ID: "f4", Name: "ledger.Flush", File: "ledger/write.go", Line: 51}}},
|
||||
}
|
||||
r := mujica.CallStack(c, threads, &st.s, mujica.CallStackOptions{Disabled: st.disabled})
|
||||
r.Element.Size(440, 300)
|
||||
if r.Changed() {
|
||||
log("frame " + st.s.Current)
|
||||
}
|
||||
}
|
||||
|
||||
func codeVariablesDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
vars []mujica.Variable
|
||||
s mujica.VariablesPanelState
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "338", func() *state {
|
||||
return &state{vars: []mujica.Variable{
|
||||
{Name: "d", Type: "court.Decree", Value: "{…}", Children: []mujica.Variable{
|
||||
{Name: "ID", Value: "42", Type: "int", Changed: true},
|
||||
{Name: "Title", Value: `"Of seals"`, Type: "string"},
|
||||
}},
|
||||
{Name: "i", Value: "2", Type: "int", Changed: true},
|
||||
{Name: "err", Value: "nil", Type: "error"},
|
||||
}}
|
||||
})
|
||||
st := *s
|
||||
codeDemoControls(c, func() { codeDemoCheck(c, &st.disabled, "Disabled", log) })
|
||||
r := mujica.VariablesPanel(c, st.vars, &st.s, mujica.VariablesPanelOptions{Disabled: st.disabled})
|
||||
r.Element.Size(440, 260)
|
||||
if r.Edited != nil {
|
||||
log(fmt.Sprintf("set %s = %s", r.Edited.Path, r.Edited.Value))
|
||||
codeDemoSet(st.vars, strings.Split(r.Edited.Path, "."), r.Edited.Value)
|
||||
}
|
||||
}
|
||||
|
||||
// codeDemoSet writes value at the dotted path.
|
||||
func codeDemoSet(vars []mujica.Variable, path []string, value string) {
|
||||
for i := range vars {
|
||||
if vars[i].Name != path[0] {
|
||||
continue
|
||||
}
|
||||
if len(path) == 1 {
|
||||
vars[i].Value, vars[i].Changed = value, true
|
||||
return
|
||||
}
|
||||
codeDemoSet(vars[i].Children, path[1:], value)
|
||||
}
|
||||
}
|
||||
|
||||
func codeHexDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
s mujica.HexViewerState
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "339", func() *state { return &state{} })
|
||||
st := *s
|
||||
codeDemoControls(c, func() { codeDemoCheck(c, &st.disabled, "Disabled", log) })
|
||||
data := []byte(strings.Join(codeDemoSource, "\n"))
|
||||
r := mujica.HexViewer(c, data, &st.s, mujica.HexViewerOptions{Disabled: st.disabled})
|
||||
r.Element.Size(720, 300)
|
||||
if r.Changed() {
|
||||
a, b := st.s.Selection()
|
||||
log(fmt.Sprintf("bytes %d..%d", a, b))
|
||||
}
|
||||
}
|
||||
|
||||
func codeFlamegraphDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
s mujica.FlamegraphState
|
||||
status string
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "340", func() *state { return &state{status: "Ready"} })
|
||||
st := *s
|
||||
codeDemoControls(c, func() {
|
||||
codeDemoCheck(c, &st.disabled, "Disabled", log)
|
||||
opts := []mujica.SelectOption[string]{{Value: "Ready", Label: "Ready"}, {Value: "Loading", Label: "Loading"}, {Value: "Failed", Label: "Failed"}}
|
||||
if mujica.Select(c, &st.status, opts, mujica.SelectOptions{Label: "Status"}).Changed() {
|
||||
log("status " + st.status)
|
||||
}
|
||||
})
|
||||
root := &mujica.FlameNode{Name: "main", Value: 240, Children: []mujica.FlameNode{
|
||||
{Name: "court.Herald", Value: 150, Children: []mujica.FlameNode{
|
||||
{Name: "court.Seal", Value: 110, Children: []mujica.FlameNode{{Name: "wax.Melt", Value: 70}, {Name: "ink.Dry", Value: 25}}},
|
||||
{Name: "ledger.Write", Value: 30},
|
||||
}},
|
||||
{Name: "archive.Load", Value: 60, Children: []mujica.FlameNode{{Name: "json.Decode", Value: 45}}},
|
||||
{Name: "gc", Value: 20},
|
||||
}}
|
||||
status := map[string]mujica.DataStatus{"Ready": mujica.DataReady, "Loading": mujica.DataLoading, "Failed": mujica.DataFailed}[st.status]
|
||||
ui.Column(c).Width(680).Children(func() {
|
||||
if mujica.Flamegraph(c, root, &st.s, mujica.FlamegraphOptions{Unit: "ms", Status: status, Error: "Profile could not be read", Disabled: st.disabled}).Changed() {
|
||||
log(fmt.Sprintf("zoom %v focus %v", st.s.Zoom, st.s.Focus))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
package main
|
||||
|
||||
// Snippets are the bodies of the Example functions in example_code_test.go.
|
||||
|
||||
const codeTerminalCode = `var term mujica.TerminalState
|
||||
term.Write("\x1b[1m~/court\x1b[0m $ ls\r\n\x1b[34mdecrees\x1b[0m ledger.txt\r\n$ ")
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.Terminal(c, &term, mujica.TerminalOptions{
|
||||
Input: func(data string) { fmt.Printf("to pty: %q\n", data) },
|
||||
Resized: func(cols, rows int) { fmt.Println("pty size", cols, rows) },
|
||||
}).Size(380, 200)
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeTerminalTabsCode = `tabs := []mujica.TerminalTab{{ID: "a", Title: "zsh"}, {ID: "b", Title: "build", Busy: true}}
|
||||
active := "a"
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.TerminalTabs(c, &tabs, &active, mujica.TerminalTabsOptions{}, func(id string) {
|
||||
ui.Text(c, "terminal "+id)
|
||||
})
|
||||
if r.Closed != "" {
|
||||
fmt.Println("close pty", r.Closed)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeTerminalSearchCode = `var term mujica.TerminalState
|
||||
term.Write("build ok\r\nerror: missing seal\r\n")
|
||||
var search mujica.TerminalSearchState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
ui.Column(c).Gap(8).Children(func() {
|
||||
mujica.TerminalSearch(c, &term, &search, mujica.TerminalSearchOptions{})
|
||||
mujica.Terminal(c, &term, mujica.TerminalOptions{}).Height(160)
|
||||
})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeCommandHistoryCode = `history := []mujica.HistoryEntry{{Command: "git status"}, {Command: "go test ./...", ExitCode: 1}}
|
||||
var state mujica.CommandHistoryState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.CommandHistory(c, history, &state, mujica.CommandHistoryOptions{})
|
||||
r.Element.Height(240)
|
||||
if r.Replayed != nil {
|
||||
fmt.Println("run", r.Replayed.Command)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeLogViewerCode = `lines := []mujica.LogLine{{Level: mujica.LogInfo, Text: "court opened"}, {Level: mujica.LogError, Text: "seal missing"}}
|
||||
var state mujica.LogViewerState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.LogViewer(c, lines, &state, mujica.LogViewerOptions{}).Element.Height(240)
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeProcessListCode = `procs := []mujica.Process{{PID: 7, Name: "archivist", CPU: 42, Memory: 50 << 20}, {PID: 9, Name: "herald", CPU: 3, Memory: 8 << 20}}
|
||||
var state mujica.ProcessListState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.ProcessList(c, procs, &state, mujica.ProcessListOptions{})
|
||||
r.Element.Height(240)
|
||||
if r.End != nil {
|
||||
fmt.Println("kill", r.End.PID)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeCodeViewerCode = `lines := []string{"func Seal(d string) int {", "\treturn len(d)", "}"}
|
||||
var state mujica.CodeViewerState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.CodeViewer(c, lines, &state, mujica.CodeViewerOptions{Language: "go"}).Element.Height(240)
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeMinimapCode = `lines := []string{"package court", "", "func Seal() {}", "func Herald() {}"}
|
||||
view := mujica.MinimapView{Top: 0, Visible: 2}
|
||||
draw := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.Minimap(c, lines, &view, mujica.MinimapOptions{Language: "go"}).Changed() {
|
||||
fmt.Println("scroll editor to", view.Top)
|
||||
}
|
||||
}
|
||||
ui.Render(draw, 400, 300, 1)`
|
||||
|
||||
const codeFindWidgetCode = `text := "the seal and the seal"
|
||||
state := mujica.FindState{Query: "seal", Replacement: "signet", ShowReplace: true}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
if mujica.FindWidget(c, &text, &state, mujica.FindWidgetOptions{}).Replaced > 0 {
|
||||
fmt.Println(text)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeSearchPanelCode = `results := []mujica.SearchFileResult{{Path: "court/seal.go", Matches: []mujica.SearchMatch{{Line: 12, Text: "func Seal() {", Start: 5, End: 9}}}}
|
||||
state := mujica.SearchPanelState{Query: "seal"}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.SearchPanel(c, results, &state, mujica.SearchPanelOptions{})
|
||||
r.Element.Height(280)
|
||||
if r.Opened != nil {
|
||||
fmt.Println("open", r.Opened.Path, r.Opened.Line)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeProblemsPanelCode = `problems := []mujica.Problem{{Severity: mujica.ProblemError, File: "court/seal.go", Line: 12, Col: 5, Message: "undefined: Decree"}}
|
||||
var state mujica.ProblemsPanelState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.ProblemsPanel(c, problems, &state, mujica.ProblemsPanelOptions{})
|
||||
r.Element.Height(240)
|
||||
if r.Opened != nil {
|
||||
fmt.Println("go to", r.Opened.File, r.Opened.Line)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeOutputPanelCode = `channels := []mujica.OutputChannel{{Name: "Build", Lines: []string{"compiling court", "\x1b[32mok\x1b[0m"}}}
|
||||
var state mujica.OutputPanelState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.OutputPanel(c, channels, &state, mujica.OutputPanelOptions{})
|
||||
r.Element.Height(240)
|
||||
if r.Cleared != "" {
|
||||
channels[state.Channel].Lines = nil
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeSymbolOutlineCode = `symbols := []mujica.Symbol{{Name: "Court", Kind: mujica.SymbolType, Line: 3, EndLine: 40, Children: []mujica.Symbol{
|
||||
{Name: "Seal", Kind: mujica.SymbolMethod, Line: 10, EndLine: 20},
|
||||
}}}
|
||||
var state mujica.SymbolOutlineState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.SymbolOutline(c, symbols, &state, mujica.SymbolOutlineOptions{CursorLine: 12})
|
||||
r.Element.Height(240)
|
||||
if r.Jumped != nil {
|
||||
fmt.Println("go to line", r.Jumped.Line)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeCompletionMenuCode = `text := "se"
|
||||
open := true
|
||||
items := []mujica.Completion{{Label: "Seal", Kind: mujica.SymbolMethod, Detail: "func()"}, {Label: "Search", Kind: mujica.SymbolFunction}}
|
||||
var state mujica.CompletionMenuState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
in := mujica.TextInput(c, &text, mujica.TextInputOptions{Label: "Code"})
|
||||
if r := mujica.CompletionMenu(c, in.Input, &open, items, &state, mujica.CompletionMenuOptions{}); r.Accepted != nil {
|
||||
text = r.Accepted.Label
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeDebugToolbarCode = `state := mujica.DebugPaused
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
switch mujica.DebugToolbar(c, state, mujica.DebugToolbarOptions{}).Action {
|
||||
case mujica.DebugContinue:
|
||||
state = mujica.DebugRunning
|
||||
case mujica.DebugStop:
|
||||
state = mujica.DebugStopped
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeBreakpointListCode = `bps := []mujica.Breakpoint{{ID: "a", File: "court/seal.go", Line: 12, Enabled: true}, {ID: "b", File: "main.go", Line: 9, Condition: "n > 3"}}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.BreakpointList(c, &bps, mujica.BreakpointListOptions{})
|
||||
r.Element.Height(200)
|
||||
if r.Toggled != "" || r.Edited != "" {
|
||||
fmt.Println("sync breakpoints with the debugger")
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeCallStackCode = `threads := []mujica.DebugThread{{ID: "t1", Name: "main", Frames: []mujica.StackFrame{
|
||||
{ID: "f1", Name: "court.Seal", File: "court/seal.go", Line: 14},
|
||||
{ID: "f2", Name: "main.main", File: "main.go", Line: 9},
|
||||
}}}
|
||||
state := mujica.CallStackState{Current: "f1"}
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.CallStack(c, threads, &state, mujica.CallStackOptions{})
|
||||
r.Element.Height(200)
|
||||
if r.Changed() {
|
||||
fmt.Println("show frame", state.Current)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeVariablesPanelCode = `vars := []mujica.Variable{{Name: "decree", Value: "{…}", Children: []mujica.Variable{{Name: "id", Value: "42", Changed: true}}}, {Name: "count", Value: "7"}}
|
||||
var state mujica.VariablesPanelState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
r := mujica.VariablesPanel(c, vars, &state, mujica.VariablesPanelOptions{})
|
||||
r.Element.Height(200)
|
||||
if r.Edited != nil {
|
||||
fmt.Println("set", r.Edited.Path, "=", r.Edited.Value)
|
||||
}
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeHexViewerCode = `data := []byte("MUJICA court ledger\x00\x01")
|
||||
var state mujica.HexViewerState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.HexViewer(c, data, &state, mujica.HexViewerOptions{}).Element.Height(160)
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
|
||||
const codeFlamegraphCode = `root := &mujica.FlameNode{Name: "main", Value: 100, Children: []mujica.FlameNode{
|
||||
{Name: "court.Seal", Value: 60}, {Name: "ledger.Write", Value: 30},
|
||||
}}
|
||||
var state mujica.FlamegraphState
|
||||
view := func(c *ui.Context) {
|
||||
mujica.Use(c, mujica.Settings{})
|
||||
mujica.Flamegraph(c, root, &state, mujica.FlamegraphOptions{Unit: "ms"})
|
||||
}
|
||||
ui.Render(view, 400, 300, 1)`
|
||||
@@ -0,0 +1,264 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
mujica "mujicaui"
|
||||
)
|
||||
|
||||
func codeProcessListDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
procs []mujica.Process
|
||||
s mujica.ProcessListState
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "326", func() *state {
|
||||
return &state{procs: []mujica.Process{
|
||||
{PID: 101, Name: "chancellor", CPU: 3.5, Memory: 200 << 20}, {PID: 7, Name: "archivist", CPU: 42.1, Memory: 50 << 20},
|
||||
{PID: 330, Name: "herald", CPU: 0.2, Memory: 2 << 30}, {PID: 412, Name: "scribe", CPU: 12.8, Memory: 96 << 20},
|
||||
}}
|
||||
})
|
||||
st := *s
|
||||
codeDemoControls(c, func() { codeDemoCheck(c, &st.disabled, "Disabled", log) })
|
||||
r := mujica.ProcessList(c, st.procs, &st.s, mujica.ProcessListOptions{Disabled: st.disabled})
|
||||
r.Element.Size(560, 260)
|
||||
if r.End != nil {
|
||||
log(fmt.Sprintf("end %s (PID %d)", r.End.Name, r.End.PID))
|
||||
var kept []mujica.Process
|
||||
for _, p := range st.procs {
|
||||
if p.PID != r.End.PID {
|
||||
kept = append(kept, p)
|
||||
}
|
||||
}
|
||||
st.procs = kept
|
||||
}
|
||||
}
|
||||
|
||||
var codeDemoSource = strings.Split(`package court
|
||||
|
||||
import "errors"
|
||||
|
||||
// Decree is an order of the court.
|
||||
type Decree struct {
|
||||
ID int
|
||||
Title string
|
||||
}
|
||||
|
||||
/* Seal stamps a decree
|
||||
with the royal wax. */
|
||||
func Seal(d Decree) (string, error) {
|
||||
if d.Title == "" {
|
||||
return "", errors.New("untitled decree")
|
||||
}
|
||||
for i := 0; i < 3; i++ {
|
||||
d.ID += i
|
||||
}
|
||||
return "sealed: " + d.Title, nil
|
||||
}
|
||||
|
||||
func Herald(ds []Decree) int {
|
||||
n := 0
|
||||
for _, d := range ds {
|
||||
if _, err := Seal(d); err == nil {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return n
|
||||
}`, "\n")
|
||||
|
||||
func codeCodeViewerDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
s mujica.CodeViewerState
|
||||
lang string
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "327", func() *state { return &state{lang: "go"} })
|
||||
st := *s
|
||||
codeDemoControls(c, func() {
|
||||
codeDemoCheck(c, &st.disabled, "Disabled", log)
|
||||
langs := []mujica.SelectOption[string]{{Value: "go", Label: "Go"}, {Value: "", Label: "Plain"}}
|
||||
if mujica.Select(c, &st.lang, langs, mujica.SelectOptions{Label: "Language"}).Changed() {
|
||||
log("language " + st.lang)
|
||||
}
|
||||
if mujica.Button(c, "Go to line 20", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
st.s.GoTo(19)
|
||||
log("go to 20")
|
||||
}
|
||||
})
|
||||
r := mujica.CodeViewer(c, codeDemoSource, &st.s, mujica.CodeViewerOptions{Language: st.lang, Label: "Source", Disabled: st.disabled})
|
||||
r.Element.Size(600, 360)
|
||||
if r.Changed() {
|
||||
log(fmt.Sprintf("line %d", st.s.Line()+1))
|
||||
}
|
||||
}
|
||||
|
||||
func codeMinimapDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
s mujica.CodeViewerState
|
||||
view mujica.MinimapView
|
||||
lines []string
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "328", func() *state {
|
||||
var lines []string
|
||||
for range 12 {
|
||||
lines = append(lines, codeDemoSource...)
|
||||
}
|
||||
return &state{lines: lines}
|
||||
})
|
||||
st := *s
|
||||
codeDemoControls(c, func() { codeDemoCheck(c, &st.disabled, "Disabled", log) })
|
||||
first, last := st.s.Visible()
|
||||
st.view = mujica.MinimapView{Top: first, Visible: max(last-first+1, 1)}
|
||||
ui.Row(c).Gap(8).Height(360).AlignItems(ui.Stretch).Children(func() {
|
||||
mujica.CodeViewer(c, st.lines, &st.s, mujica.CodeViewerOptions{Language: "go"}).Element.Width(520)
|
||||
if mujica.Minimap(c, st.lines, &st.view, mujica.MinimapOptions{Language: "go", Disabled: st.disabled}).Changed() {
|
||||
st.s.ScrollTo(st.view.Top)
|
||||
log(fmt.Sprintf("jump to line %d", st.view.Top+1))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func codeFindWidgetDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
text string
|
||||
s mujica.FindState
|
||||
readOnly, disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "329", func() *state {
|
||||
return &state{text: "The seal of the court; seals are kept sealed. Seal 42 and seal 7.", s: mujica.FindState{Query: "seal"}}
|
||||
})
|
||||
st := *s
|
||||
codeDemoControls(c, func() {
|
||||
codeDemoCheck(c, &st.readOnly, "Read-only", log)
|
||||
codeDemoCheck(c, &st.disabled, "Disabled", log)
|
||||
})
|
||||
ui.Column(c).Width(620).Gap(12).Children(func() {
|
||||
r := mujica.FindWidget(c, &st.text, &st.s, mujica.FindWidgetOptions{ReadOnly: st.readOnly, Disabled: st.disabled})
|
||||
if r.Moved {
|
||||
log(fmt.Sprintf("%d matches, at %d", len(st.s.Matches()), st.s.Current+1))
|
||||
}
|
||||
if r.Replaced > 0 {
|
||||
log(fmt.Sprintf("replaced %d", r.Replaced))
|
||||
}
|
||||
var spans []ui.Span
|
||||
at := 0
|
||||
for i, m := range st.s.Matches() {
|
||||
spans = append(spans, ui.Span{Text: st.text[at:m[0]]})
|
||||
sp := ui.Span{Text: st.text[m[0]:m[1]], Background: mujica.Tokens(c).Selection, Underline: true}
|
||||
if i == st.s.Current {
|
||||
sp.Weight = 700
|
||||
}
|
||||
spans = append(spans, sp)
|
||||
at = m[1]
|
||||
}
|
||||
spans = append(spans, ui.Span{Text: st.text[at:]})
|
||||
ui.RichText(c, spans...)
|
||||
})
|
||||
}
|
||||
|
||||
func codeSearchPanelDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
s mujica.SearchPanelState
|
||||
results []mujica.SearchFileResult
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "330", func() *state { return &state{s: mujica.SearchPanelState{Query: "seal"}} })
|
||||
st := *s
|
||||
codeDemoControls(c, func() { codeDemoCheck(c, &st.disabled, "Disabled", log) })
|
||||
// The demo searches its own two files.
|
||||
files := map[string][]string{"court/seal.go": codeDemoSource, "docs/ledger.md": {"# Ledger", "The seal is kept by the chancellor.", "Seals expire after a year."}}
|
||||
if st.results == nil || st.s.Query != "" && len(st.results) == 0 {
|
||||
st.results = codeDemoSearch(files, st.s.Query, st.s.Include, st.s.Exclude)
|
||||
}
|
||||
r := mujica.SearchPanel(c, st.results, &st.s, mujica.SearchPanelOptions{Disabled: st.disabled})
|
||||
r.Element.Size(560, 380)
|
||||
if r.Changed {
|
||||
st.results = codeDemoSearch(files, st.s.Query, st.s.Include, st.s.Exclude)
|
||||
log(fmt.Sprintf("search %q include %q exclude %q", st.s.Query, st.s.Include, st.s.Exclude))
|
||||
}
|
||||
if r.Opened != nil {
|
||||
log(fmt.Sprintf("open %s:%d", r.Opened.Path, r.Opened.Line))
|
||||
}
|
||||
}
|
||||
|
||||
// codeDemoSearch finds q in the demo files.
|
||||
func codeDemoSearch(files map[string][]string, q, include, exclude string) []mujica.SearchFileResult {
|
||||
out := []mujica.SearchFileResult{}
|
||||
if q == "" {
|
||||
return out
|
||||
}
|
||||
for _, path := range []string{"court/seal.go", "docs/ledger.md"} {
|
||||
if include != "" && !strings.Contains(path, strings.Trim(include, "*")) || exclude != "" && strings.Contains(path, strings.Trim(exclude, "*")) {
|
||||
continue
|
||||
}
|
||||
f := mujica.SearchFileResult{Path: path}
|
||||
for i, line := range files[path] {
|
||||
if at := strings.Index(strings.ToLower(line), strings.ToLower(q)); at >= 0 {
|
||||
f.Matches = append(f.Matches, mujica.SearchMatch{Line: i + 1, Text: line, Start: at, End: at + len(q)})
|
||||
}
|
||||
}
|
||||
if len(f.Matches) > 0 {
|
||||
out = append(out, f)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func codeProblemsDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
s mujica.ProblemsPanelState
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "331", func() *state { return &state{} })
|
||||
st := *s
|
||||
codeDemoControls(c, func() { codeDemoCheck(c, &st.disabled, "Disabled", log) })
|
||||
problems := []mujica.Problem{
|
||||
{Severity: mujica.ProblemError, File: "court/seal.go", Line: 12, Col: 5, Message: "undefined: Decree", Source: "compiler"},
|
||||
{Severity: mujica.ProblemWarning, File: "court/seal.go", Line: 30, Col: 2, Message: "unused variable wax", Source: "vet"},
|
||||
{Severity: mujica.ProblemInfo, File: "docs/ledger.md", Line: 4, Col: 1, Message: "line is longer than 80", Source: "lint"},
|
||||
{Severity: mujica.ProblemError, File: "main.go", Line: 9, Col: 14, Message: "missing return", Source: "compiler"},
|
||||
}
|
||||
r := mujica.ProblemsPanel(c, problems, &st.s, mujica.ProblemsPanelOptions{Disabled: st.disabled})
|
||||
r.Element.Size(620, 320)
|
||||
if r.Opened != nil {
|
||||
log(fmt.Sprintf("go to %s:%d:%d", r.Opened.File, r.Opened.Line, r.Opened.Col))
|
||||
}
|
||||
}
|
||||
|
||||
func codeOutputDemo(c *ui.Context, log func(string)) {
|
||||
type state struct {
|
||||
channels []mujica.OutputChannel
|
||||
s mujica.OutputPanelState
|
||||
disabled bool
|
||||
}
|
||||
s := ui.Local(c.Root(), "332", func() *state {
|
||||
var build []string
|
||||
for i := range 300 {
|
||||
build = append(build, fmt.Sprintf("compiling unit %d of 300", i+1))
|
||||
}
|
||||
return &state{channels: []mujica.OutputChannel{{Name: "Build", Lines: build},
|
||||
{Name: "Tests", Lines: []string{"\x1b[32mPASS\x1b[0m court/seal", "\x1b[31mFAIL\x1b[0m court/ledger: expected 3 seals", "\x1b[1mok\x1b[0m 2 of 3 packages"}}}}
|
||||
})
|
||||
st := *s
|
||||
codeDemoControls(c, func() {
|
||||
codeDemoCheck(c, &st.disabled, "Disabled", log)
|
||||
if mujica.Button(c, "Write line", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
||||
ch := &st.channels[st.s.Channel]
|
||||
ch.Lines = append(ch.Lines, fmt.Sprintf("line %d", len(ch.Lines)+1))
|
||||
log("write to " + ch.Name)
|
||||
}
|
||||
})
|
||||
r := mujica.OutputPanel(c, st.channels, &st.s, mujica.OutputPanelOptions{Disabled: st.disabled})
|
||||
r.Element.Size(560, 300)
|
||||
if r.Changed {
|
||||
log("channel " + st.channels[st.s.Channel].Name)
|
||||
}
|
||||
if r.Cleared != "" {
|
||||
st.channels[st.s.Channel].Lines = nil
|
||||
log("cleared " + r.Cleared)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user