Improve compatibility with zenity.

This commit is contained in:
Nuno Cruces 2022-12-20 12:23:15 +00:00
parent 9816e864ae
commit 6c17f894e1
10 changed files with 143 additions and 17 deletions

View file

@ -520,9 +520,13 @@ func loadFlags() []zenity.Option {
if ellipsize { if ellipsize {
opts = append(opts, zenity.Ellipsize()) opts = append(opts, zenity.Ellipsize())
} }
if !noMarkup { switch {
switch { case entryDlg:
case errorDlg, infoDlg, warningDlg, questionDlg: text = zencmd.StripMnemonic(text)
case calendarDlg, progressDlg:
text = zencmd.StripMarkup(text)
case errorDlg, infoDlg, warningDlg, questionDlg:
if !noMarkup {
text = zencmd.StripMarkup(text) text = zencmd.StripMarkup(text)
} }
} }

View file

@ -10,7 +10,7 @@ import (
) )
func calendar(text string, opts options) (time.Time, error) { func calendar(text string, opts options) (time.Time, error) {
args := []string{"--calendar", "--text", text, "--date-format", zenutil.DateFormat} args := []string{"--calendar", "--text", quoteMarkup(text), "--date-format", zenutil.DateFormat}
args = appendGeneral(args, opts) args = appendGeneral(args, opts)
args = appendButtons(args, opts) args = appendButtons(args, opts)
args = appendWidthHeight(args, opts) args = appendWidthHeight(args, opts)

View file

@ -2,12 +2,10 @@
package zenity package zenity
import ( import "github.com/ncruces/zenity/internal/zenutil"
"github.com/ncruces/zenity/internal/zenutil"
)
func entry(text string, opts options) (string, error) { func entry(text string, opts options) (string, error) {
args := []string{"--entry", "--text", text} args := []string{"--entry", "--text", quoteMnemonics(text)}
args = appendGeneral(args, opts) args = appendGeneral(args, opts)
args = appendButtons(args, opts) args = appendButtons(args, opts)
args = appendWidthHeight(args, opts) args = appendWidthHeight(args, opts)

View file

@ -12,18 +12,18 @@ func StripMarkup(s string) string {
// https://docs.gtk.org/Pango/pango_markup.html // https://docs.gtk.org/Pango/pango_markup.html
dec := xml.NewDecoder(strings.NewReader(s)) dec := xml.NewDecoder(strings.NewReader(s))
var buf strings.Builder var res strings.Builder
for { for {
t, err := dec.Token() t, err := dec.Token()
if err == io.EOF { if err == io.EOF {
return buf.String() return res.String()
} }
if err != nil { if err != nil {
return s return s
} }
if t, ok := t.(xml.CharData); ok { if t, ok := t.(xml.CharData); ok {
buf.Write(t) res.Write(t)
} }
} }
} }

View file

@ -1,8 +1,6 @@
package zencmd package zencmd
import ( import "testing"
"testing"
)
var markupTests = []struct { var markupTests = []struct {
data string data string

View file

@ -0,0 +1,25 @@
package zencmd
import "strings"
// StripMnemonic is internal.
func StripMnemonic(s string) string {
// Strips mnemonics described in:
// https: //docs.gtk.org/gtk4/class.Label.html#mnemonics
var res strings.Builder
underscore := false
for _, b := range []byte(s) {
switch {
case underscore:
underscore = false
case b == '_':
underscore = true
continue
}
res.WriteByte(b)
}
return res.String()
}

View file

@ -0,0 +1,46 @@
package zencmd
import (
"strings"
"testing"
)
var mnemonicTests = []struct {
data string
want string
}{
{"", ""},
{"abc", "abc"},
{"_abc", `abc`},
{"_a_b_c_", "abc"},
{"a__c", `a_c`},
{"a___c", `a_c`},
{"a____c", `a__c`},
}
func TestStripMnemonic(t *testing.T) {
t.Parallel()
for _, test := range mnemonicTests {
if got := StripMnemonic(test.data); got != test.want {
t.Errorf("StripMnemonic(%q) = %q; want %q", test.data, got, test.want)
}
}
}
func FuzzStripMnemonic(f *testing.F) {
for _, test := range mnemonicTests {
f.Add(test.data)
}
f.Fuzz(func(t *testing.T, s string) {
q := quoteMnemonic(s)
u := StripMnemonic(q)
if s != u {
t.Errorf("strip(quote(%q)) = strip(%q) = %q", s, q, u)
}
})
}
func quoteMnemonic(s string) string {
return strings.ReplaceAll(s, "_", "__")
}

View file

@ -1,8 +1,6 @@
package zencmd package zencmd
import ( import "testing"
"testing"
)
var unescapeTests = []struct { var unescapeTests = []struct {
data string data string

14
util.go
View file

@ -2,6 +2,7 @@ package zenity
import ( import (
"bytes" "bytes"
"encoding/xml"
"fmt" "fmt"
"os/exec" "os/exec"
"strconv" "strconv"
@ -14,6 +15,19 @@ func quoteAccelerators(text string) string {
return strings.ReplaceAll(text, "&", "&&") return strings.ReplaceAll(text, "&", "&&")
} }
func quoteMnemonics(text string) string {
return strings.ReplaceAll(text, "_", "__")
}
func quoteMarkup(text string) string {
var res strings.Builder
err := xml.EscapeText(&res, []byte(text))
if err != nil {
return text
}
return res.String()
}
func appendGeneral(args []string, opts options) []string { func appendGeneral(args []string, opts options) []string {
if opts.title != nil { if opts.title != nil {
args = append(args, "--title", *opts.title) args = append(args, "--title", *opts.title)

View file

@ -31,6 +31,49 @@ func Test_quoteAccelerators(t *testing.T) {
} }
} }
func Test_quoteMnemonics(t *testing.T) {
t.Parallel()
tests := []struct {
name string
text string
want string
}{
{name: "None", text: "abc", want: "abc"},
{name: "One", text: "_abc", want: "__abc"},
{name: "Two", text: "_a_bc", want: "__a__bc"},
{name: "Three", text: "ab__c", want: "ab____c"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := quoteMnemonics(tt.text); got != tt.want {
t.Errorf("quoteMnemonics(%q) = %q; want %q", tt.text, got, tt.want)
}
})
}
}
func Test_quoteMarkup(t *testing.T) {
t.Parallel()
tests := []struct {
name string
text string
want string
}{
{name: "None", text: `abc`, want: "abc"},
{name: "LT", text: `<`, want: "&lt;"},
{name: "Amp", text: `&`, want: "&amp;"},
{name: "Quot", text: `"`, want: "&#34;"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := quoteMarkup(tt.text); got != tt.want {
t.Errorf("quoteMarkup(%q) = %q; want %q", tt.text, got, tt.want)
}
})
}
}
func Test_appendGeneral(t *testing.T) { func Test_appendGeneral(t *testing.T) {
t.Parallel() t.Parallel()
got := appendGeneral(nil, options{ got := appendGeneral(nil, options{