| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355 |
- // Copyright 2024 The tk9.0-go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- //go:build none
- package main
- import (
- "archive/zip"
- "bytes"
- "crypto/sha256"
- "fmt"
- "io"
- "io/fs"
- "os"
- "path/filepath"
- "runtime"
- "runtime/debug"
- "slices"
- "strings"
- "github.com/adrg/xdg"
- "golang.org/x/net/html"
- util "modernc.org/fileutil/ccgo"
- tcllib "modernc.org/libtcl9.0/library"
- libtk "modernc.org/libtk9.0"
- tklib "modernc.org/libtk9.0/library"
- ngrab "modernc.org/ngrab/lib"
- "modernc.org/rec/lib"
- )
- const (
- header = `// Code generated by generator.go, DO NOT EDIT.
- package tk9_0 // import "modernc.org/tk9.0"
- import "fmt"
- `
- head = `<html>
- <head>
- <style>
- div {
- margin-left: 50px;
- }
- p {
- padding : 0;
- margin : 0;
- background-color: #FDFDC9;
- }
- .comment {
- background-color: lightgray;
- }
- .TH {
- background-color: #9E9E9E;
- }
- .SH {
- background-color: #2196F3;
- }
- .SO .text {
- background-color: #00FFFF;
- }
- .OP {
- background-color: #FFEB3B;
- }
- .BS {
- background-color: #F44336;
- }
- .CS .text {
- background-color: #87CEEB;
- }
- .DS {
- background-color: #F44336;
- }
- .RS {
- background-color: #F44336;
- }
- .sso {
- background-color: #616161;
- }
- .SS {
- background-color: #8f8;
- }
- .LP {
- background-color: #8f8;
- }
- </style>
- </head>
- <body>
- `
- goarch = runtime.GOARCH
- goos = runtime.GOOS
- ofn = "generated.go"
- tempDir = "html"
- )
- type cmdOpts map[string][]string
- type pageInfo struct {
- commands map[string][]string // Go command name: option names
- ignore bool
- manual bool
- widget bool
- }
- var (
- pageInfos = map[string]*pageInfo{
- "bell": {
- manual: true, // done
- commands: cmdOpts{"Bell": []string{
- "-displayof",
- "-nice",
- }},
- },
- "bind": {manual: true}, // done
- "bindtags": {ignore: true},
- "bitmap": {
- manual: true, // done
- commands: cmdOpts{"NewBitmap": []string{
- "-background",
- "-data",
- "-file",
- "-foreground",
- "-maskdata",
- "-maskfile",
- }},
- },
- "busy": {
- manual: true, // done
- commands: cmdOpts{".Busy": []string{
- "-cursor",
- }},
- },
- "chooseColor": {
- manual: true, // done
- commands: cmdOpts{"ChooseColor": []string{
- "-initialcolor",
- "-parent",
- "-title",
- }},
- },
- "chooseDirectory": {
- manual: true, // done
- commands: cmdOpts{"ChooseDirectory": []string{
- //TODO? "-command",
- "-initialdir",
- "-message",
- "-mustexist",
- "-parent",
- "-title",
- }},
- },
- "clipboard": {
- manual: true, // done
- commands: cmdOpts{
- "ClipboardAppend": []string{
- "-displayof",
- "-format",
- "-type",
- },
- "ClipboardClear": []string{
- "-displayof",
- },
- "ClipboardGet": []string{
- "-displayof",
- "-type",
- },
- },
- },
- "colors": {manual: true}, // done
- "console": {ignore: true},
- "cursors": {manual: true}, // done
- "destroy": {manual: true}, // done
- "dialog": {ignore: true}, // ... largely deprecated by the tk_messageBox
- "event": {
- ignore: true, //MAYBE later
- commands: cmdOpts{"EventGenerate": []string{
- "-above",
- "-borderwidth",
- "-button",
- "-count",
- "-data",
- "-delta",
- "-detail",
- "-focus",
- "-height",
- "-keycode",
- "-keysym",
- "-mode",
- "-override",
- "-place",
- "-root",
- "-rootx",
- "-rooty",
- "-sendevent",
- "-serial",
- "-state",
- "-subwindow",
- "-time",
- "-warp",
- "-width",
- "-when",
- "-x",
- "-y",
- }},
- },
- "focus": {
- manual: true, // done
- commands: cmdOpts{"Focus": []string{
- "-displayof",
- "-force",
- "-lastfor",
- }},
- },
- "focusNext": {ignore: true}, //MAYBE later
- "font": {
- manual: true, // done
- commands: cmdOpts{
- "NewFont": []string{
- "-family",
- "-size",
- "-weight",
- "-slant",
- "-underline",
- "-overstrike",
- },
- "FontFamilies": []string{
- "-displayof",
- },
- // "FontMetrics": []string{
- // "-ascent",
- // "-descent",
- // "-linespace",
- // "-fixed",
- // },
- },
- },
- "fontchooser": {
- manual: true, // done
- commands: cmdOpts{"Fontchooser": []string{
- "-parent",
- "-title",
- "-font",
- "-command",
- // "-visible",
- }},
- },
- "getOpenFile": {
- manual: true, // done
- commands: cmdOpts{
- "GetOpenFile": []string{
- // "-command", // macOS only
- "-defaultextension",
- "-filetypes",
- "-initialdir",
- "-initialfile",
- // "-message", // macOS only
- "-multiple",
- "-parent",
- "-title",
- // "-typevariable",
- },
- "GetSaveFile": []string{
- // "-command",
- "-confirmoverwrite",
- "-defaultextension",
- "-filetypes",
- "-initialdir",
- "-initialfile",
- // "-message",
- "-parent",
- "-title",
- // "-typevariable",
- },
- },
- },
- "grab": {
- manual: true, //done
- },
- "grid": {
- manual: true, // done
- commands: cmdOpts{"Grid": []string{
- "-column",
- "-columnspan",
- "-in",
- "-ipadx",
- "-ipady",
- "-padx",
- "-pady",
- "-row",
- "-rowspan",
- "-sticky",
- }},
- },
- "image": {ignore: true}, //MAYBE later
- "keysyms": {ignore: true}, //MAYBE later
- "loadTk": {ignore: true},
- "lower": {manual: true}, // done
- "menu": {widget: true,
- commands: cmdOpts{
- "MenuWidget.AddCommand": menuOptions,
- "MenuWidget.AddCascade": menuOptions,
- "MenuWidget.AddSeparator": menuOptions,
- "MenuWidget.Invoke": nil,
- },
- },
- "messageBox": {
- manual: true, // done
- commands: cmdOpts{"MessageBox": []string{
- "-command",
- "-default",
- "-detail",
- "-icon",
- "-message",
- "-parent",
- "-title",
- "-type",
- }},
- },
- "nsimage": {ignore: true}, //TODO
- "option": {ignore: true},
- "optionMenu": {manual: true},
- "options": {ignore: true},
- "pack": {
- manual: true, // done
- commands: cmdOpts{"Pack": []string{
- "-after",
- "-anchor",
- "-before",
- "-expand",
- "-fill",
- "-in",
- "-ipadx",
- "-ipady",
- "-padx",
- "-pady",
- "-side",
- }},
- },
- "palette": {ignore: true}, //MAYBE later
- "photo": {
- manual: true, // done
- commands: cmdOpts{"NewPhoto": []string{
- "-data",
- "-format",
- "-file",
- "-gamma",
- "-height",
- "-metadata",
- "-palette",
- "-width",
- }},
- },
- "place": {
- manual: true, // done
- commands: cmdOpts{"Place": []string{
- "-anchor",
- "-bordermode",
- "-height",
- "-in",
- "-relheight",
- "-relwidth",
- "-relx",
- "-rely",
- "-width",
- "-x",
- "-y",
- }},
- },
- "popup": {manual: true}, // done
- "print": {manual: true}, //TODO
- "raise": {manual: true}, // done
- "selection": {
- ignore: true, //MAYBE later
- // commands: cmdOpts{
- // "SelectionClear": []string{
- // "-displayof",
- // "-selection",
- // },
- // "SelectionGet": []string{
- // "-displayof",
- // "-selection",
- // "-type",
- // },
- // "SelectionHandle": []string{
- // "-selection",
- // "-type",
- // "-format",
- // },
- // "SelectionOwn": []string{
- // "-command",
- // "-displayof",
- // "-selection",
- // },
- // },
- },
- "send": {ignore: true},
- "sysnotify": {manual: true}, //TODO
- "systray": {manual: true}, //TODO
- "text": {widget: true, //MAYBE more
- commands: cmdOpts{
- "TextWidget.TagConfigure": []string{
- "-background",
- "-bgstipple",
- "-borderwidth",
- "-elide",
- "-fgstipple",
- "-font",
- "-foreground",
- "-justify",
- "-lmargin1",
- "-lmargin2",
- "-lmargincolor",
- "-offset",
- "-overstrike",
- "-overstrikefg",
- "-relief",
- "-rmargin",
- "-rmargincolor",
- "-selectbackground",
- "-selectforeground",
- "-spacing1",
- "-spacing2",
- "-spacing3",
- "-tabs",
- "-tabstyle",
- "-underline",
- "-underlinefg",
- "-wrap",
- },
- },
- },
- "tk": {
- manual: true, //MAYBE later
- // commands: cmdOpts{
- // "Caret": []string{
- // "-x",
- // "-y",
- // "-height",
- // },
- // "Inactive": []string{
- // "-displayof",
- // "-reset",
- // },
- // "Scaling": []string{
- // "-displayof",
- // },
- // "UseInputMethods": []string{
- // "-displayof",
- // },
- // },
- },
- "tk_mac": {ignore: true},
- "tkerror": {ignore: true},
- "tkvars": {ignore: true},
- "tkwait": {manual: true}, // done
- "ttk_image": {ignore: true},
- "ttk_intro": {ignore: true},
- "ttk_style": {manual: true}, //TODO
- "ttk_vsapi": {ignore: true},
- "ttk_widget": {ignore: true},
- "winfo": {manual: true}, //TODO
- "wm": {manual: true}, //TODO
- }
- menuOptions = []string{
- "-activebackground",
- "-activeforeground",
- "-accelerator",
- "-background",
- "-bitmap",
- "-columnbreak",
- "-command",
- "-compound",
- "-font",
- "-foreground",
- "-hidemargin",
- "-image",
- "-indicatoron",
- "-label",
- "-menu",
- "-offvalue",
- "-onvalue",
- "-selectcolor",
- "-selectimage",
- "-state",
- "-underline",
- "-value",
- "-variable",
- }
- handlers = map[string]bool{
- "Command": true,
- "Invalidcommand": true,
- "Postcommand": true,
- "Tearoffcommand": true,
- "Validatecommand": true,
- "Xscrollcommand": true,
- "Yscrollcommand": true,
- }
- hideOpts = map[string]bool{
- "Data": true,
- "Font": true,
- "From": true,
- "To": true,
- "Type": true,
- "Values": true,
- }
- hideOptMethods = map[string]bool{
- "Textvariable": true,
- "Variable": true,
- }
- replaceOpt = map[string]string{
- "Button": "Btn",
- "Label": "Lbl",
- "Menu": "Mnu",
- "Message": "Msg",
- "Text": "Txt",
- }
- )
- func main() {
- hashes()
- makeTokenizer()
- w := bytes.NewBuffer(nil)
- w.WriteString(header)
- defer func() {
- if err := recover(); err != nil {
- fmt.Printf("PANIC: %v\n%s", err, debug.Stack())
- return
- }
- fmt.Printf("writing %v len=%v\n", ofn, len(w.Bytes()))
- if err := os.WriteFile(ofn, w.Bytes(), 0660); err != nil {
- panic(err)
- }
- }()
- nFilesDir := filepath.Join(xdg.ConfigHome, "ccgo", "v4", "libtk9.0", goos, goarch, libtk.Version, "doc")
- fmt.Printf("nFilesDir=%s\n", nFilesDir)
- util.MustShell(true, nil, "sh", "-c", fmt.Sprintf("rm -rf %s", tempDir))
- util.MustShell(true, nil, "mkdir", "-p", tempDir)
- fmt.Printf("nFilesDir=%s tempDir=%s\n", nFilesDir, tempDir)
- m, err := filepath.Glob(filepath.Join(nFilesDir, "*.n"))
- if err != nil {
- panic(err)
- }
- slices.Sort(m)
- t, err := ngrab.NewTask(io.Discard, m)
- if err != nil {
- panic(err)
- }
- if err := t.Main(); err != nil {
- panic(err)
- }
- htmlFiles := makeHTML(t)
- j := newJob(w, htmlFiles)
- j.main()
- }
- func makeTokenizer() {
- return // No more needed, only adds diff noise.
- args := []string{
- "-lexstring", "mlToken",
- "-pkg", "tk9_0",
- `([^$]|\\\$)*`, // Not TeX, incl. "\$"
- `\$([^$]|\\\$)*[^\\]\$`, // $TeX$, incl. $Te\$X$
- `\$\$([^$]|\\\$)*[^\\]\$\$`, // $$TeX$$, incl. $Te\$X$
- }
- var b bytes.Buffer
- rc, err := rec.Main(args, &b, io.Discard)
- if err != nil {
- panic(err)
- }
- if rc != 0 {
- panic(rc)
- }
- if err = os.WriteFile("mltoken.go", b.Bytes(), 0660); err != nil {
- panic(err)
- }
- }
- func makeHTML(t *ngrab.Task) (htmlFiles []string) {
- var documentFN string
- var w *bytes.Buffer
- var path []string
- for _, v := range t.Nodes {
- switch v.Type {
- case "document":
- if documentFN != "" {
- base := filepath.Base(documentFN)
- base = base[:len(base)-len(".n")] + ".html"
- w.WriteString("<html>\n")
- ofn := filepath.Join(tempDir, base)
- if err := os.WriteFile(ofn, w.Bytes(), 0660); err != nil {
- panic(err)
- }
- htmlFiles = append(htmlFiles, ofn)
- }
- documentFN = v.Text
- w = bytes.NewBuffer(nil)
- w.WriteString(head)
- case
- "BS",
- "CS",
- "DS",
- "RS",
- "SO":
- path = append(path, v.Type)
- fmt.Fprintf(w, "<div class=%q title=%q>\n", v.Type, strings.Join(path, "/"))
- case
- "BE",
- "CE",
- "DE",
- "RE",
- "SE":
- path = path[:len(path)-1]
- fmt.Fprintf(w, "</div>\n")
- case "so":
- fmt.Fprintf(w, "<p class=%q title=%q>%s</p>", "sso", strings.Join(append(path, "sso"), "/"), html.EscapeString(v.Text))
- default:
- fmt.Fprintf(w, "<p class=%q title=%q>%s</p>\n", v.Type, strings.Join(append(path, v.Type), "/"), html.EscapeString(v.Text))
- }
- }
- if documentFN != "" {
- base := filepath.Base(documentFN)
- base = base[:len(base)-len(".n")] + ".html"
- w.WriteString("<html>\n")
- ofn := filepath.Join(tempDir, base)
- if err := os.WriteFile(ofn, w.Bytes(), 0660); err != nil {
- panic(err)
- }
- htmlFiles = append(htmlFiles, ofn)
- }
- return htmlFiles
- }
- func walk(lvl int, n *html.Node, visitor func(n *html.Node) (dive bool)) {
- for ; n != nil; n = n.NextSibling {
- if visitor(n) {
- walk(lvl+1, n.FirstChild, visitor)
- }
- }
- }
- func class(n *html.Node) string {
- for _, v := range n.Attr {
- if v.Key == "class" {
- return v.Val
- }
- }
- return ""
- }
- type document struct {
- fn string
- root *html.Node
- sh []*html.Node
- shx map[string]*html.Node
- so []*html.Node
- }
- type option struct {
- docs []string
- goName string
- tclName string
- xref map[string]struct{}
- isWidgetOption bool
- }
- type job struct {
- documents []*document
- files []string
- o *bytes.Buffer
- optionsDoc *document
- optionsByTclName map[string]*option
- ttkOptionsDoc *document
- }
- func newJob(o *bytes.Buffer, files []string) *job {
- return &job{
- files: files,
- o: o,
- optionsByTclName: map[string]*option{},
- }
- }
- func (j *job) registerOption(tclName string, docs []string, xref string) (r *option) {
- switch {
- case strings.HasPrefix(xref, "[."):
- xref = "[Window." + xref[len("[."):]
- }
- r = j.optionsByTclName[tclName]
- if r == nil {
- r = &option{
- tclName: tclName,
- goName: tclOptName2GoName(tclName),
- docs: docs,
- xref: map[string]struct{}{},
- }
- j.optionsByTclName[tclName] = r
- }
- if xref != "" {
- r.xref[xref] = struct{}{}
- }
- return r
- }
- func (j *job) w(s string, args ...any) {
- fmt.Fprintf(j.o, s, args...)
- }
- func (j *job) analyze(file string) {
- f, err := os.Open(file)
- if err != nil {
- panic(err)
- }
- defer f.Close()
- n, err := html.Parse(f)
- if err != nil {
- panic(err)
- }
- d := &document{
- fn: file,
- root: n,
- shx: map[string]*html.Node{},
- }
- switch filepath.Base(file) {
- case "options.html":
- j.optionsDoc = d
- case "ttk_widget.html":
- j.ttkOptionsDoc = d
- }
- j.documents = append(j.documents, d)
- walk(0, n, func(n *html.Node) (dive bool) {
- switch n.Type {
- case html.ElementNode:
- switch n.Data {
- case "p":
- switch class(n) {
- case "SH":
- s := strings.TrimSpace(n.FirstChild.Data)
- if d.shx[s] != nil {
- panic("internal error")
- }
- d.shx[s] = n
- d.sh = append(d.sh, n)
- }
- case "div":
- switch class(n) {
- case "SO":
- d.so = append(d.so, n)
- }
- }
- }
- return true
- })
- }
- func base(fn string) (r string) {
- x := strings.Index(fn, "/")
- r = fn[x+1:]
- return r[:len(r)-len(".html")]
- }
- func (j *job) main() {
- for _, v := range j.files {
- j.analyze(v)
- }
- for _, v := range j.documents {
- var a []string
- for k := range v.shx {
- a = append(a, k)
- }
- slices.Sort(a)
- if len(v.so) != 0 {
- a = append(a, fmt.Sprintf("len(so)=%v", len(v.so)))
- }
- // fmt.Printf("%s %v\n", v.fn, a)
- base := base(v.fn)
- if nfo := pageInfos[base]; nfo == nil && len(v.so) != 0 {
- pageInfos[base] = &pageInfo{widget: true}
- }
- }
- var fail bool
- for _, fn := range j.files {
- base := base(fn)
- nfo := pageInfos[base]
- switch {
- case nfo == nil:
- fmt.Printf("%s: missing page info\n", base)
- case nfo.ignore, nfo.manual, nfo.widget:
- // ok
- default:
- fmt.Printf("%s: missing classificaiotn\n", base)
- fail = true
- }
- }
- if fail {
- return
- }
- for i, fn := range j.files {
- base := base(fn)
- nfo := pageInfos[base]
- switch {
- case nfo.widget:
- j.widget(fn, j.documents[i])
- if len(nfo.commands) != 0 {
- j.manual(fn, j.documents[i], nfo)
- }
- case nfo.manual:
- j.manual(fn, j.documents[i], nfo)
- }
- }
- j.stdOptions()
- j.ttkStdOptions()
- var a []string
- for k := range j.optionsByTclName {
- a = append(a, k)
- }
- slices.Sort(a)
- for _, k := range a {
- v := j.optionsByTclName[k]
- if hideOpts[v.goName] || hideOptMethods[v.goName] {
- continue
- }
- j.w("\n\n// %s option.", v.goName)
- if handlers[v.goName] {
- j.w("\n//\n// See also [Event handlers].")
- }
- if len(v.docs) != 0 {
- j.w("\n//\n// %s", strings.Join(v.docs, "\n//"))
- }
- if len(v.xref) != 0 {
- j.w("\n//\n// Known uses:")
- var a []string
- for k := range v.xref {
- a = append(a, k)
- }
- slices.Sort(a)
- for _, v := range a {
- j.w("\n// - %s", v)
- }
- }
- switch ok := handlers[v.goName]; {
- case ok:
- j.w("\n//\n// [Event handlers]: https://pkg.go.dev/modernc.org/tk9.0#hdr-Event_handlers")
- j.w("\nfunc %s(handler any) Opt {", v.goName)
- j.w("\n\treturn newEventHandler(%q, handler)", v.tclName)
- j.w("\n}")
- default:
- j.w("\nfunc %s(val any) Opt {", v.goName)
- j.w("\nreturn rawOption(fmt.Sprintf(`%s %%s`, optionString(val)))", v.tclName)
- j.w("\n}")
- if !v.isWidgetOption {
- break
- }
- j.w("\n\n// %s — Get the configured option value.", v.goName)
- if len(v.xref) != 0 {
- j.w("\n//\n// Known uses:")
- var a []string
- for k := range v.xref {
- a = append(a, k)
- }
- slices.Sort(a)
- for _, v := range a {
- if !strings.Contains(v, "command specific") {
- j.w("\n// - %s", v)
- }
- }
- }
- j.w("\nfunc (w *Window) %s() string {", v.goName)
- j.w("\nreturn evalErr(fmt.Sprintf(`%%s cget %s`, w))", v.tclName)
- j.w("\n}")
- }
- }
- }
- func (j *job) ttkStdOptions() {
- walk(0, j.ttkOptionsDoc.root, func(n *html.Node) (dive bool) {
- if nodeIs(n, "OP") {
- tclName := strings.TrimSpace(n.FirstChild.Data)
- a := strings.Fields(tclName)
- tclName = strings.TrimLeft(a[0], `"\`)
- var docs []string
- for n := n.NextSibling; n != nil; n = n.NextSibling {
- if nodeIs(n, "OP") || nodeIs(n, "SH") {
- break
- }
- if !nodeIs(n, "text") {
- continue
- }
- s := strings.TrimSpace(plain(n.FirstChild.Data))
- docs = append(docs, s)
- }
- o := j.registerOption(tclName, nil, "")
- o.isWidgetOption = true
- if len(o.docs) == 0 {
- o.docs = docs
- }
- }
- return true
- })
- }
- func (j *job) stdOptions() {
- walk(0, j.optionsDoc.root, func(n *html.Node) (dive bool) {
- if nodeIs(n, "OP") {
- tclName := strings.TrimSpace(n.FirstChild.Data)
- a := strings.Fields(tclName)
- tclName = strings.TrimLeft(a[0], `"\`)
- var docs []string
- for n := n.NextSibling; n != nil; n = n.NextSibling {
- if nodeIs(n, "OP") || nodeIs(n, "SH") {
- break
- }
- if !nodeIs(n, "text") {
- continue
- }
- s := strings.TrimSpace(plain(n.FirstChild.Data))
- docs = append(docs, s)
- }
- o := j.registerOption(tclName, nil, "")
- o.isWidgetOption = true
- o.docs = docs
- }
- return true
- })
- }
- func nodeIs(n *html.Node, pathSuffix string) bool {
- for _, v := range n.Attr {
- if v.Key == "title" && strings.HasSuffix(v.Val, pathSuffix) {
- return true
- }
- }
- return false
- }
- func (j *job) widgetStdOpts(xref string, doc *document) (r []string) {
- m := map[string]struct{}{}
- walk(0, doc.root, func(n *html.Node) (dive bool) {
- if nodeIs(n, "SO/text") {
- s := strings.TrimSpace(n.FirstChild.Data)
- a := strings.Fields(s)
- for _, v := range a {
- s := strings.TrimSpace(v)
- if s[0] == '\\' {
- s = s[1:]
- }
- j.registerOption(s, nil, xref).isWidgetOption = true
- s = tclOptName2GoName(s)
- m[s] = struct{}{}
- }
- }
- return true
- })
- for k := range m {
- r = append(r, k)
- }
- slices.Sort(r)
- return r
- }
- func (j *job) widgetStylingOptions(doc *document) (r []string) {
- for _, n := range doc.sh {
- if ch := n.FirstChild; ch != nil && strings.Contains(ch.Data, "STYLING OPTIONS") {
- for n := n.NextSibling; n != nil; n = n.NextSibling {
- if hasClass(n, "SH") {
- break
- }
- if hasClass(n, "PP") {
- r = append(r, "")
- continue
- }
- if ch := n.FirstChild; ch != nil {
- s := strings.TrimSpace(plain(ch.Data))
- if strings.HasPrefix(s, "-") {
- s = s[1:]
- a := strings.Fields(s)
- s = fmt.Sprintf(" - [%s] %s", capitalize(a[0]), strings.Join(a[1:], " "))
- for len(r) != 0 && r[len(r)-1] == "" {
- r = r[:len(r)-1]
- }
- }
- r = append(r, s)
- }
- }
- break
- }
- }
- return r
- }
- func capitalize(s string) string {
- return strings.ToUpper(string(s[0])) + s[1:]
- }
- func (j *job) widgetStdStyles(doc *document) (r []string) {
- for _, n := range doc.sh {
- if ch := n.FirstChild; ch != nil && strings.Contains(ch.Data, "STANDARD STYLES") {
- for n := n.NextSibling; n != nil; n = n.NextSibling {
- if hasClass(n, "SH") {
- break
- }
- if hasClass(n, "PP") {
- r = append(r, "")
- continue
- }
- if ch := n.FirstChild; ch != nil {
- r = append(r, strings.TrimSpace(plain(ch.Data)))
- }
- }
- break
- }
- }
- return r
- }
- func (j *job) widgetSpecificOpts(xref string, doc *document) (r []*option) {
- ok := false
- walk(0, doc.root, func(n *html.Node) (dive bool) {
- if nodeIs(n, "SH") {
- s := strings.ToLower(n.FirstChild.Data)
- // Reject options within section headings like "TAB OPTIONS".
- ok = strings.Contains(s, "widget") && strings.Contains(s, "specific") && strings.Contains(s, "options")
- }
- if ok && nodeIs(n, "OP") {
- tclName := strings.TrimSpace(n.FirstChild.Data)
- a := strings.Fields(tclName)
- tclName = strings.TrimLeft(a[0], `"\`)
- var docs []string
- for n := n.NextSibling; n != nil; n = n.NextSibling {
- if nodeIs(n, "OP") || nodeIs(n, "SH") {
- break
- }
- if !nodeIs(n, "text") {
- continue
- }
- s := strings.TrimSpace(plain(n.FirstChild.Data))
- docs = append(docs, s)
- }
- o := j.registerOption(tclName, nil, xref)
- o.isWidgetOption = true
- p := *o
- p.docs = docs
- r = append(r, &p)
- }
- return true
- })
- return r
- }
- func tclOptName2GoName(s string) (r string) {
- s = strings.TrimSpace(s)
- if strings.HasPrefix(s, "\\") {
- s = s[1:]
- }
- if strings.HasPrefix(s, "-") {
- s = s[1:]
- }
- r = export(s)
- if x := replaceOpt[r]; x != "" {
- r = x
- }
- return r
- }
- func (j *job) pageLink(page string) {
- if page == "" {
- return
- }
- page = page[:len(page)-len(".html")]
- j.w("\n//\n// More information might be available at the [Tcl/Tk %s] page.", page)
- j.w("\n//\n// [Tcl/Tk %s]: https://www.tcl-lang.org/man/tcl9.0/TkCmd/%[1]s.html", page)
- }
- func (j *job) manual(fn string, doc *document, nfo *pageInfo) {
- for nm, options := range nfo.commands {
- for _, option := range options {
- j.registerOption(option, nil, fmt.Sprintf("[%s] (command specific)", nm))
- }
- }
- }
- func (j *job) widget(fn string, doc *document) {
- shNameNode := doc.shx["NAME"]
- txtNode := shNameNode.NextSibling.NextSibling.FirstChild
- txt := txtNode.Data
- txt = strings.TrimSpace(txt[strings.Index(txt, "-")+1:])
- txt = strings.ToUpper(txt[:1]) + txt[1:]
- base := base(fn)
- gnm := tclName2GoName(base)
- doc0 := []string{fmt.Sprintf("\n\n// %s — %s", gnm, txt)}
- doc0 = append(doc0, j.description(doc.shx["DESCRIPTION"])...)
- j.w("%s", strings.Join(doc0, "\n// "))
- j.w("\n//\n// Use [Window.%s] to create a %[1]s with a particular parent.", gnm)
- j.pageLink(fn[len(tempDir)+1:])
- if opts := j.widgetStdOpts(fmt.Sprintf("[%s]", gnm), doc); len(opts) != 0 {
- j.w("\n//\n// # Standard options\n//")
- for _, v := range opts {
- if hideOpts[v] {
- continue
- }
- j.w("\n// - [%s]", v)
- }
- }
- if sos := j.widgetSpecificOpts(fmt.Sprintf("[%s] (widget specific)", gnm), doc); len(sos) != 0 {
- j.w("\n//\n// # Widget specific options")
- for _, v := range sos {
- j.w("\n//\n// [%s]", v.goName)
- j.w("\n//\n// %s", strings.Join(v.docs, "\n// "))
- }
- }
- if ss := j.widgetStdStyles(doc); len(ss) != 0 {
- j.w("\n//\n// # Standard styles\n//")
- for _, v := range ss {
- j.w("\n// %s", v)
- }
- }
- if sos := j.widgetStylingOptions(doc); len(sos) != 0 {
- j.w("\n//\n// # Styling options\n//")
- for _, v := range sos {
- j.w("\n// %s", v)
- }
- }
- j.w("\nfunc %s(options ...Opt) *%[1]sWidget {", gnm)
- j.w("\nreturn App.%s(options...)", gnm)
- j.w("\n}")
- j.w("%s", doc0[0])
- j.w("\n//\n// The resulting [Window] is a child of 'w'")
- j.w("\n//\n// For details please see [%s]", gnm)
- j.w("\nfunc (w *Window) %s(options ...Opt) *%[1]sWidget {", gnm)
- cmd := strings.Replace(base, "ttk_", "ttk::", 1)
- j.w("\nreturn &%sWidget{w.newChild(%q, options...)}", gnm, cmd)
- j.w("\n}")
- j.w("\n\n// %sWidget represents the Tcl/Tk %s widget/window", gnm, base)
- j.w("\ntype %sWidget struct {", gnm)
- j.w("\n*Window")
- j.w("\n}")
- }
- func (j *job) description(n *html.Node) (r []string) {
- if n == nil {
- return nil
- }
- r = append(r, "", "# Description\n//")
- stop := false
- walk(0, n.NextSibling, func(n *html.Node) (dive bool) {
- if stop {
- return false
- }
- if hasClass(n, "SH") {
- stop = true
- return false
- }
- if hasClass(n, "PP") {
- r = append(r, "")
- return false
- }
- switch n.Type {
- case html.TextNode:
- if s := strings.TrimSpace(plain(n.Data)); s != "" &&
- !strings.HasPrefix(s, `\"`) &&
- !strings.HasPrefix(s, "-") {
- r = append(r, s)
- }
- }
- return true
- })
- return r
- }
- func hasClass(n *html.Node, cls string) bool {
- for _, v := range n.Attr {
- if v.Key == "class" && v.Val == cls {
- return true
- }
- }
- return false
- }
- func plain(s string) (r string) {
- r = s
- r = strings.ReplaceAll(r, "\\-", "-")
- r = strings.ReplaceAll(r, "\\fB", "")
- r = strings.ReplaceAll(r, "\\fI", "")
- r = strings.ReplaceAll(r, "\\fP", "")
- r = strings.ReplaceAll(r, "\\fR", "")
- return r
- }
- func tclName2GoName(s string) string {
- switch {
- case strings.HasPrefix(s, "ttk_"):
- return "T" + export(s[len("ttk_"):])
- default:
- return export(s)
- }
- }
- func export(s string) (r string) {
- a := strings.Split(s, "_")
- for i, v := range a {
- a[i] = strings.ToUpper(v[:1]) + v[1:]
- }
- return strings.Join(a, "")
- }
- // origin returns caller's short position, skipping skip frames.
- //
- //lint:ignore U1000 debug helper
- func origin(skip int) string {
- pc, fn, fl, _ := runtime.Caller(skip)
- f := runtime.FuncForPC(pc)
- var fns string
- if f != nil {
- fns = f.Name()
- if x := strings.LastIndex(fns, "."); x > 0 {
- fns = fns[x+1:]
- }
- if strings.HasPrefix(fns, "func") {
- num := true
- for _, c := range fns[len("func"):] {
- if c < '0' || c > '9' {
- num = false
- break
- }
- }
- if num {
- return origin(skip + 2)
- }
- }
- }
- return fmt.Sprintf("%s:%d:%s", filepath.Base(fn), fl, fns)
- }
- // todo prints and return caller's position and an optional message tagged with TODO. Output goes to stderr.
- //
- //lint:ignore U1000 debug helper
- func todo(s string, args ...interface{}) string {
- switch {
- case s == "":
- s = fmt.Sprintf(strings.Repeat("%v ", len(args)), args...)
- default:
- s = fmt.Sprintf(s, args...)
- }
- r := fmt.Sprintf("%s\n\tTODO %s", origin(2), s)
- // fmt.Fprintf(os.Stderr, "%s\n", r)
- // os.Stdout.Sync()
- return r
- }
- // trc prints and return caller's position and an optional message tagged with TRC. Output goes to stderr.
- //
- //lint:ignore U1000 debug helper
- func trc(s string, args ...interface{}) string {
- switch {
- case s == "":
- s = fmt.Sprintf(strings.Repeat("%v ", len(args)), args...)
- default:
- s = fmt.Sprintf(s, args...)
- }
- r := fmt.Sprintf("%s: TRC %s", origin(2), s)
- fmt.Fprintf(os.Stderr, "%s\n", r)
- os.Stderr.Sync()
- return r
- }
- func hashes() {
- filepath.WalkDir(".", func(path string, d fs.DirEntry, err error) error {
- if err != nil {
- panic(err)
- }
- base := filepath.Base(path)
- if d.IsDir() || !strings.HasSuffix(base, ".zip") {
- return nil
- }
- r, err := zip.OpenReader(path)
- if err != nil {
- panic(err)
- }
- defer r.Close()
- fmt.Printf("// %s\n", path)
- for _, f := range r.File {
- // fmt.Printf("Contents of %s:\n", f.Name)
- r, err := f.Open()
- if err != nil {
- panic(err)
- }
- h := sha256.New()
- _, err = io.Copy(h, r)
- r.Close()
- if err != nil {
- panic(err)
- }
- fmt.Printf("%q: \"%0x\",\n", f.Name, h.Sum(nil))
- }
- return nil
- })
- fmt.Printf("// other\n")
- fmt.Printf("%q: \"%0x\",\n", "tcl_library.zip", sha256.Sum256([]byte(tcllib.Zip)))
- fmt.Printf("%q: \"%0x\",\n", "tk_library.zip", sha256.Sum256([]byte(tklib.Zip)))
- }
|