feat(config): add hint mode keys
- commit
- e368cc0eff0c2ae55984613dd1118b10ab106392
- author
- brice · · 2026-10-19 16:03 UTC
- committer
- brice · · 2026-10-19 17:36 UTC
- parent
- 2082615
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +166 -0
M internal/adapters/config/config.go +74 -0
| @@ -114,6 +114,7 @@ var actions = map[string]bool{ | |||
| 114 | 114 | "move-workspace-prev": true, | |
| 115 | 115 | "move-workspace-next": true, | |
| 116 | 116 | "toggle-floating": true, | |
| 117 | + | "hint": true, | |
| 117 | 118 | } | |
| 118 | 119 | var components = map[string]bool{"core": true, "wayland": true, "input": true, "drm": true, "seat": true, "render": true, "sync": true, "config": true, "app": true} | |
| 119 | 120 | var color = regexp.MustCompile(`^#[0-9a-fA-F]{6}$`) | |
| @@ -163,6 +164,9 @@ func Defaults() ports.Config { | |||
| 163 | 164 | c.Terminal.Command = []string{"foot"} | |
| 164 | 165 | } | |
| 165 | 166 | c.Terminal.AutoOpen = "first" | |
| 167 | + | // Hint mode is experimental: off unless the config turns it on. | |
| 168 | + | c.Hint.Enable = false | |
| 169 | + | c.Hint.TextClick = ports.ModCtrl | ports.ModShift | |
| 166 | 170 | c.Xwayland = "xwayland-satellite" | |
| 167 | 171 | c.Background.Color = "#111111" | |
| 168 | 172 | c.Floating.Dim = 0.3 | |
| @@ -847,6 +851,46 @@ func set(c *ports.Config, key, v string) error { | |||
| 847 | 851 | return err | |
| 848 | 852 | } | |
| 849 | 853 | c.Focus.Flash = b | |
| 854 | + | case "hint.enable": | |
| 855 | + | b, err := onOff(v) | |
| 856 | + | if err != nil { | |
| 857 | + | return err | |
| 858 | + | } | |
| 859 | + | c.Hint.Enable = b | |
| 860 | + | case "hint.size": | |
| 861 | + | if v == "auto" { | |
| 862 | + | c.Hint.Size = 0 | |
| 863 | + | break | |
| 864 | + | } | |
| 865 | + | n, err := strconv.ParseFloat(v, 64) | |
| 866 | + | if err != nil || math.IsNaN(n) || n < 6 || n > 72 { | |
| 867 | + | return fmt.Errorf("must be auto or a number from 6 to 72 (logical pixels)") | |
| 868 | + | } | |
| 869 | + | c.Hint.Size = n | |
| 870 | + | case "hint.background", "hint.foreground": | |
| 871 | + | if v != "" && !color.MatchString(v) { | |
| 872 | + | return fmt.Errorf("must be #rrggbb or empty (derived)") | |
| 873 | + | } | |
| 874 | + | if key == "hint.background" { | |
| 875 | + | c.Hint.Background = v | |
| 876 | + | } else { | |
| 877 | + | c.Hint.Foreground = v | |
| 878 | + | } | |
| 879 | + | case "hint.targets": | |
| 880 | + | switch v { | |
| 881 | + | case "clickable": | |
| 882 | + | c.Hint.LinksOnly = false | |
| 883 | + | case "links": | |
| 884 | + | c.Hint.LinksOnly = true | |
| 885 | + | default: | |
| 886 | + | return fmt.Errorf("must be clickable or links") | |
| 887 | + | } | |
| 888 | + | case "hint.text-click": | |
| 889 | + | m, err := parseTextClick(v) | |
| 890 | + | if err != nil { | |
| 891 | + | return err | |
| 892 | + | } | |
| 893 | + | c.Hint.TextClick = m | |
| 850 | 894 | case "focus.flash-width": | |
| 851 | 895 | n, err := strconv.Atoi(v) | |
| 852 | 896 | if err != nil || n < 1 || n > 8 { | |
| @@ -1270,6 +1314,36 @@ func checkAction(v string) error { | |||
| 1270 | 1314 | return nil | |
| 1271 | 1315 | } | |
| 1272 | 1316 | ||
| 1317 | + | // parseTextClick parses hint.text-click: `+`-separated modifier names as in | |
| 1318 | + | // binds, or none. cmd is an alias of super here (it is not resolved through | |
| 1319 | + | // keyboard.cmd, so the result does not depend on key order). | |
| 1320 | + | func parseTextClick(v string) (ports.Mods, error) { | |
| 1321 | + | if strings.ToLower(v) == "none" { | |
| 1322 | + | return 0, nil | |
| 1323 | + | } | |
| 1324 | + | var mods ports.Mods | |
| 1325 | + | for _, p := range strings.Split(v, "+") { | |
| 1326 | + | var m ports.Mods | |
| 1327 | + | switch strings.ToLower(strings.TrimSpace(p)) { | |
| 1328 | + | case "shift": | |
| 1329 | + | m = ports.ModShift | |
| 1330 | + | case "ctrl": | |
| 1331 | + | m = ports.ModCtrl | |
| 1332 | + | case "alt": | |
| 1333 | + | m = ports.ModAlt | |
| 1334 | + | case "super", "cmd": | |
| 1335 | + | m = ports.ModSuper | |
| 1336 | + | default: | |
| 1337 | + | return 0, fmt.Errorf("unknown modifier %q (use shift, ctrl, alt, super or cmd joined by +, or none)", p) | |
| 1338 | + | } | |
| 1339 | + | if mods&m != 0 { | |
| 1340 | + | return 0, fmt.Errorf("duplicate modifier %q", p) | |
| 1341 | + | } | |
| 1342 | + | mods |= m | |
| 1343 | + | } | |
| 1344 | + | return mods, nil | |
| 1345 | + | } | |
| 1346 | + | ||
| 1273 | 1347 | // parseCombo turns `ctrl+cmd+é` into the canonical `Cmd+Ctrl+eacute`: | |
| 1274 | 1348 | // modifiers sorted, the key as an xkb keysym name. | |
| 1275 | 1349 | func parseCombo(s string) (string, error) { | |
| @@ -114,6 +114,7 @@ var actions = map[string]bool{ | |||||
| 114 | "move-workspace-prev": true, | 114 | "move-workspace-prev": true, | ||
| 115 | "move-workspace-next": true, | 115 | "move-workspace-next": true, | ||
| 116 | "toggle-floating": true, | 116 | "toggle-floating": true, | ||
| 117 | + | "hint": true, | |||
| 117 | } | 118 | } | ||
| 118 | var components = map[string]bool{"core": true, "wayland": true, "input": true, "drm": true, "seat": true, "render": true, "sync": true, "config": true, "app": true} | 119 | var components = map[string]bool{"core": true, "wayland": true, "input": true, "drm": true, "seat": true, "render": true, "sync": true, "config": true, "app": true} | ||
| 119 | var color = regexp.MustCompile(`^#[0-9a-fA-F]{6}$`) | 120 | var color = regexp.MustCompile(`^#[0-9a-fA-F]{6}$`) | ||
| @@ -163,6 +164,9 @@ func Defaults() ports.Config { | |||||
| 163 | c.Terminal.Command = []string{"foot"} | 164 | c.Terminal.Command = []string{"foot"} | ||
| 164 | } | 165 | } | ||
| 165 | c.Terminal.AutoOpen = "first" | 166 | c.Terminal.AutoOpen = "first" | ||
| 167 | + | // Hint mode is experimental: off unless the config turns it on. | |||
| 168 | + | c.Hint.Enable = false | |||
| 169 | + | c.Hint.TextClick = ports.ModCtrl | ports.ModShift | |||
| 166 | c.Xwayland = "xwayland-satellite" | 170 | c.Xwayland = "xwayland-satellite" | ||
| 167 | c.Background.Color = "#111111" | 171 | c.Background.Color = "#111111" | ||
| 168 | c.Floating.Dim = 0.3 | 172 | c.Floating.Dim = 0.3 | ||
| @@ -847,6 +851,46 @@ func set(c *ports.Config, key, v string) error { | |||||
| 847 | return err | 851 | return err | ||
| 848 | } | 852 | } | ||
| 849 | c.Focus.Flash = b | 853 | c.Focus.Flash = b | ||
| 854 | + | case "hint.enable": | |||
| 855 | + | b, err := onOff(v) | |||
| 856 | + | if err != nil { | |||
| 857 | + | return err | |||
| 858 | + | } | |||
| 859 | + | c.Hint.Enable = b | |||
| 860 | + | case "hint.size": | |||
| 861 | + | if v == "auto" { | |||
| 862 | + | c.Hint.Size = 0 | |||
| 863 | + | break | |||
| 864 | + | } | |||
| 865 | + | n, err := strconv.ParseFloat(v, 64) | |||
| 866 | + | if err != nil || math.IsNaN(n) || n < 6 || n > 72 { | |||
| 867 | + | return fmt.Errorf("must be auto or a number from 6 to 72 (logical pixels)") | |||
| 868 | + | } | |||
| 869 | + | c.Hint.Size = n | |||
| 870 | + | case "hint.background", "hint.foreground": | |||
| 871 | + | if v != "" && !color.MatchString(v) { | |||
| 872 | + | return fmt.Errorf("must be #rrggbb or empty (derived)") | |||
| 873 | + | } | |||
| 874 | + | if key == "hint.background" { | |||
| 875 | + | c.Hint.Background = v | |||
| 876 | + | } else { | |||
| 877 | + | c.Hint.Foreground = v | |||
| 878 | + | } | |||
| 879 | + | case "hint.targets": | |||
| 880 | + | switch v { | |||
| 881 | + | case "clickable": | |||
| 882 | + | c.Hint.LinksOnly = false | |||
| 883 | + | case "links": | |||
| 884 | + | c.Hint.LinksOnly = true | |||
| 885 | + | default: | |||
| 886 | + | return fmt.Errorf("must be clickable or links") | |||
| 887 | + | } | |||
| 888 | + | case "hint.text-click": | |||
| 889 | + | m, err := parseTextClick(v) | |||
| 890 | + | if err != nil { | |||
| 891 | + | return err | |||
| 892 | + | } | |||
| 893 | + | c.Hint.TextClick = m | |||
| 850 | case "focus.flash-width": | 894 | case "focus.flash-width": | ||
| 851 | n, err := strconv.Atoi(v) | 895 | n, err := strconv.Atoi(v) | ||
| 852 | if err != nil || n < 1 || n > 8 { | 896 | if err != nil || n < 1 || n > 8 { | ||
| @@ -1270,6 +1314,36 @@ func checkAction(v string) error { | |||||
| 1270 | return nil | 1314 | return nil | ||
| 1271 | } | 1315 | } | ||
| 1272 | 1316 | ||||
| 1317 | + | // parseTextClick parses hint.text-click: `+`-separated modifier names as in | |||
| 1318 | + | // binds, or none. cmd is an alias of super here (it is not resolved through | |||
| 1319 | + | // keyboard.cmd, so the result does not depend on key order). | |||
| 1320 | + | func parseTextClick(v string) (ports.Mods, error) { | |||
| 1321 | + | if strings.ToLower(v) == "none" { | |||
| 1322 | + | return 0, nil | |||
| 1323 | + | } | |||
| 1324 | + | var mods ports.Mods | |||
| 1325 | + | for _, p := range strings.Split(v, "+") { | |||
| 1326 | + | var m ports.Mods | |||
| 1327 | + | switch strings.ToLower(strings.TrimSpace(p)) { | |||
| 1328 | + | case "shift": | |||
| 1329 | + | m = ports.ModShift | |||
| 1330 | + | case "ctrl": | |||
| 1331 | + | m = ports.ModCtrl | |||
| 1332 | + | case "alt": | |||
| 1333 | + | m = ports.ModAlt | |||
| 1334 | + | case "super", "cmd": | |||
| 1335 | + | m = ports.ModSuper | |||
| 1336 | + | default: | |||
| 1337 | + | return 0, fmt.Errorf("unknown modifier %q (use shift, ctrl, alt, super or cmd joined by +, or none)", p) | |||
| 1338 | + | } | |||
| 1339 | + | if mods&m != 0 { | |||
| 1340 | + | return 0, fmt.Errorf("duplicate modifier %q", p) | |||
| 1341 | + | } | |||
| 1342 | + | mods |= m | |||
| 1343 | + | } | |||
| 1344 | + | return mods, nil | |||
| 1345 | + | } | |||
| 1346 | + | ||||
| 1273 | // parseCombo turns `ctrl+cmd+é` into the canonical `Cmd+Ctrl+eacute`: | 1347 | // parseCombo turns `ctrl+cmd+é` into the canonical `Cmd+Ctrl+eacute`: | ||
| 1274 | // modifiers sorted, the key as an xkb keysym name. | 1348 | // modifiers sorted, the key as an xkb keysym name. | ||
| 1275 | func parseCombo(s string) (string, error) { | 1349 | func parseCombo(s string) (string, error) { | ||
M internal/adapters/config/config_test.go +92 -0
| @@ -953,3 +953,95 @@ func TestLoadRejectsFIFO(t *testing.T) { | |||
| 953 | 953 | t.Fatal("Load blocked on a FIFO") | |
| 954 | 954 | } | |
| 955 | 955 | } | |
| 956 | + | ||
| 957 | + | func TestHintTargets(t *testing.T) { | |
| 958 | + | if Defaults().Hint.LinksOnly { | |
| 959 | + | t.Fatal("hint targets default must be clickable") | |
| 960 | + | } | |
| 961 | + | c, w := parseString(t, "hint.targets = links\n") | |
| 962 | + | if !c.Hint.LinksOnly || len(w) != 0 { | |
| 963 | + | t.Fatal(c.Hint, w) | |
| 964 | + | } | |
| 965 | + | c, w = parseString(t, "hint.targets = clickable\n") | |
| 966 | + | if c.Hint.LinksOnly || len(w) != 0 { | |
| 967 | + | t.Fatal(c.Hint, w) | |
| 968 | + | } | |
| 969 | + | // An invalid value warns and keeps the default. | |
| 970 | + | c, w = parseString(t, "hint.targets = buttons\n") | |
| 971 | + | if c.Hint.LinksOnly || len(w) != 1 { | |
| 972 | + | t.Fatal(c.Hint, w) | |
| 973 | + | } | |
| 974 | + | c, w = parseString(t, "bind.cmd+g = hint\n") | |
| 975 | + | if len(w) != 0 || c.Binds["Cmd+g"] != "hint" { | |
| 976 | + | t.Fatal(c.Binds["Cmd+g"], w) | |
| 977 | + | } | |
| 978 | + | } | |
| 979 | + | ||
| 980 | + | func TestHintStyleConfig(t *testing.T) { | |
| 981 | + | c, warnings := parseString(t, "hint.size = 17.5\nhint.background = #89b4fa\nhint.foreground = #111111\n") | |
| 982 | + | if len(warnings) != 0 || c.Hint.Size != 17.5 || c.Hint.Background != "#89b4fa" || c.Hint.Foreground != "#111111" { | |
| 983 | + | t.Fatalf("style: %+v, warnings: %v", c.Hint, warnings) | |
| 984 | + | } | |
| 985 | + | c, warnings = parseString(t, "hint.size = auto\nhint.background =\nhint.foreground =\n") | |
| 986 | + | if len(warnings) != 0 || c.Hint.Size != 0 || c.Hint.Background != "" || c.Hint.Foreground != "" { | |
| 987 | + | t.Fatalf("automatic: %+v %v", c.Hint, warnings) | |
| 988 | + | } | |
| 989 | + | for _, line := range []string{"hint.size = 0", "hint.size = -1", "hint.size = NaN", "hint.size = Inf", "hint.size = huge", "hint.size = 5.9", "hint.size = 72.1", "hint.size = 1e9", "hint.background = red", "hint.foreground = #fff"} { | |
| 990 | + | _, warnings := parseString(t, line+"\n") | |
| 991 | + | if len(warnings) == 0 { | |
| 992 | + | t.Errorf("expected validate-config warning for %s", line) | |
| 993 | + | } | |
| 994 | + | } | |
| 995 | + | } | |
| 996 | + | ||
| 997 | + | func TestHintSizeBounds(t *testing.T) { | |
| 998 | + | for _, v := range []string{"6", "72", "13"} { | |
| 999 | + | c, w := parseString(t, "hint.size = "+v+"\n") | |
| 1000 | + | if len(w) != 0 || c.Hint.Size == 0 { | |
| 1001 | + | t.Errorf("hint.size = %s: %+v %v", v, c.Hint, w) | |
| 1002 | + | } | |
| 1003 | + | } | |
| 1004 | + | } | |
| 1005 | + | ||
| 1006 | + | func TestHintTextClick(t *testing.T) { | |
| 1007 | + | if got := Defaults().Hint.TextClick; got != ports.ModCtrl|ports.ModShift { | |
| 1008 | + | t.Fatalf("default %d", got) | |
| 1009 | + | } | |
| 1010 | + | for in, want := range map[string]ports.Mods{ | |
| 1011 | + | "ctrl+shift": ports.ModCtrl | ports.ModShift, | |
| 1012 | + | "Alt": ports.ModAlt, | |
| 1013 | + | "super": ports.ModSuper, | |
| 1014 | + | "cmd+shift": ports.ModSuper | ports.ModShift, | |
| 1015 | + | "none": 0, | |
| 1016 | + | "shift+ctrl+alt": ports.ModShift | ports.ModCtrl | ports.ModAlt, | |
| 1017 | + | } { | |
| 1018 | + | c, w := parseString(t, "hint.text-click = "+in+"\n") | |
| 1019 | + | if c.Hint.TextClick != want || len(w) != 0 { | |
| 1020 | + | t.Errorf("%q: %d %v", in, c.Hint.TextClick, w) | |
| 1021 | + | } | |
| 1022 | + | } | |
| 1023 | + | for _, bad := range []string{"hyper", "ctrl+", "ctrl+ctrl", "ctrl+none", ""} { | |
| 1024 | + | c, w := parseString(t, "hint.text-click = "+bad+"\n") | |
| 1025 | + | if len(w) != 1 || c.Hint.TextClick != ports.ModCtrl|ports.ModShift { | |
| 1026 | + | t.Errorf("%q: %d %v", bad, c.Hint.TextClick, w) | |
| 1027 | + | } | |
| 1028 | + | } | |
| 1029 | + | } | |
| 1030 | + | ||
| 1031 | + | func TestHintEnable(t *testing.T) { | |
| 1032 | + | if Defaults().Hint.Enable { | |
| 1033 | + | t.Fatal("hint mode must be off by default") | |
| 1034 | + | } | |
| 1035 | + | c, w := parseString(t, "hint.enable = on\n") | |
| 1036 | + | if !c.Hint.Enable || len(w) != 0 { | |
| 1037 | + | t.Fatal(c.Hint, w) | |
| 1038 | + | } | |
| 1039 | + | c, w = parseString(t, "hint.enable = off\n") | |
| 1040 | + | if c.Hint.Enable || len(w) != 0 { | |
| 1041 | + | t.Fatal(c.Hint, w) | |
| 1042 | + | } | |
| 1043 | + | c, w = parseString(t, "hint.enable = maybe\n") | |
| 1044 | + | if c.Hint.Enable || len(w) != 1 { | |
| 1045 | + | t.Fatal(c.Hint, w) | |
| 1046 | + | } | |
| 1047 | + | } | |
| @@ -953,3 +953,95 @@ func TestLoadRejectsFIFO(t *testing.T) { | |||||
| 953 | t.Fatal("Load blocked on a FIFO") | 953 | t.Fatal("Load blocked on a FIFO") | ||
| 954 | } | 954 | } | ||
| 955 | } | 955 | } | ||
| 956 | + | ||||
| 957 | + | func TestHintTargets(t *testing.T) { | |||
| 958 | + | if Defaults().Hint.LinksOnly { | |||
| 959 | + | t.Fatal("hint targets default must be clickable") | |||
| 960 | + | } | |||
| 961 | + | c, w := parseString(t, "hint.targets = links\n") | |||
| 962 | + | if !c.Hint.LinksOnly || len(w) != 0 { | |||
| 963 | + | t.Fatal(c.Hint, w) | |||
| 964 | + | } | |||
| 965 | + | c, w = parseString(t, "hint.targets = clickable\n") | |||
| 966 | + | if c.Hint.LinksOnly || len(w) != 0 { | |||
| 967 | + | t.Fatal(c.Hint, w) | |||
| 968 | + | } | |||
| 969 | + | // An invalid value warns and keeps the default. | |||
| 970 | + | c, w = parseString(t, "hint.targets = buttons\n") | |||
| 971 | + | if c.Hint.LinksOnly || len(w) != 1 { | |||
| 972 | + | t.Fatal(c.Hint, w) | |||
| 973 | + | } | |||
| 974 | + | c, w = parseString(t, "bind.cmd+g = hint\n") | |||
| 975 | + | if len(w) != 0 || c.Binds["Cmd+g"] != "hint" { | |||
| 976 | + | t.Fatal(c.Binds["Cmd+g"], w) | |||
| 977 | + | } | |||
| 978 | + | } | |||
| 979 | + | ||||
| 980 | + | func TestHintStyleConfig(t *testing.T) { | |||
| 981 | + | c, warnings := parseString(t, "hint.size = 17.5\nhint.background = #89b4fa\nhint.foreground = #111111\n") | |||
| 982 | + | if len(warnings) != 0 || c.Hint.Size != 17.5 || c.Hint.Background != "#89b4fa" || c.Hint.Foreground != "#111111" { | |||
| 983 | + | t.Fatalf("style: %+v, warnings: %v", c.Hint, warnings) | |||
| 984 | + | } | |||
| 985 | + | c, warnings = parseString(t, "hint.size = auto\nhint.background =\nhint.foreground =\n") | |||
| 986 | + | if len(warnings) != 0 || c.Hint.Size != 0 || c.Hint.Background != "" || c.Hint.Foreground != "" { | |||
| 987 | + | t.Fatalf("automatic: %+v %v", c.Hint, warnings) | |||
| 988 | + | } | |||
| 989 | + | for _, line := range []string{"hint.size = 0", "hint.size = -1", "hint.size = NaN", "hint.size = Inf", "hint.size = huge", "hint.size = 5.9", "hint.size = 72.1", "hint.size = 1e9", "hint.background = red", "hint.foreground = #fff"} { | |||
| 990 | + | _, warnings := parseString(t, line+"\n") | |||
| 991 | + | if len(warnings) == 0 { | |||
| 992 | + | t.Errorf("expected validate-config warning for %s", line) | |||
| 993 | + | } | |||
| 994 | + | } | |||
| 995 | + | } | |||
| 996 | + | ||||
| 997 | + | func TestHintSizeBounds(t *testing.T) { | |||
| 998 | + | for _, v := range []string{"6", "72", "13"} { | |||
| 999 | + | c, w := parseString(t, "hint.size = "+v+"\n") | |||
| 1000 | + | if len(w) != 0 || c.Hint.Size == 0 { | |||
| 1001 | + | t.Errorf("hint.size = %s: %+v %v", v, c.Hint, w) | |||
| 1002 | + | } | |||
| 1003 | + | } | |||
| 1004 | + | } | |||
| 1005 | + | ||||
| 1006 | + | func TestHintTextClick(t *testing.T) { | |||
| 1007 | + | if got := Defaults().Hint.TextClick; got != ports.ModCtrl|ports.ModShift { | |||
| 1008 | + | t.Fatalf("default %d", got) | |||
| 1009 | + | } | |||
| 1010 | + | for in, want := range map[string]ports.Mods{ | |||
| 1011 | + | "ctrl+shift": ports.ModCtrl | ports.ModShift, | |||
| 1012 | + | "Alt": ports.ModAlt, | |||
| 1013 | + | "super": ports.ModSuper, | |||
| 1014 | + | "cmd+shift": ports.ModSuper | ports.ModShift, | |||
| 1015 | + | "none": 0, | |||
| 1016 | + | "shift+ctrl+alt": ports.ModShift | ports.ModCtrl | ports.ModAlt, | |||
| 1017 | + | } { | |||
| 1018 | + | c, w := parseString(t, "hint.text-click = "+in+"\n") | |||
| 1019 | + | if c.Hint.TextClick != want || len(w) != 0 { | |||
| 1020 | + | t.Errorf("%q: %d %v", in, c.Hint.TextClick, w) | |||
| 1021 | + | } | |||
| 1022 | + | } | |||
| 1023 | + | for _, bad := range []string{"hyper", "ctrl+", "ctrl+ctrl", "ctrl+none", ""} { | |||
| 1024 | + | c, w := parseString(t, "hint.text-click = "+bad+"\n") | |||
| 1025 | + | if len(w) != 1 || c.Hint.TextClick != ports.ModCtrl|ports.ModShift { | |||
| 1026 | + | t.Errorf("%q: %d %v", bad, c.Hint.TextClick, w) | |||
| 1027 | + | } | |||
| 1028 | + | } | |||
| 1029 | + | } | |||
| 1030 | + | ||||
| 1031 | + | func TestHintEnable(t *testing.T) { | |||
| 1032 | + | if Defaults().Hint.Enable { | |||
| 1033 | + | t.Fatal("hint mode must be off by default") | |||
| 1034 | + | } | |||
| 1035 | + | c, w := parseString(t, "hint.enable = on\n") | |||
| 1036 | + | if !c.Hint.Enable || len(w) != 0 { | |||
| 1037 | + | t.Fatal(c.Hint, w) | |||
| 1038 | + | } | |||
| 1039 | + | c, w = parseString(t, "hint.enable = off\n") | |||
| 1040 | + | if c.Hint.Enable || len(w) != 0 { | |||
| 1041 | + | t.Fatal(c.Hint, w) | |||
| 1042 | + | } | |||
| 1043 | + | c, w = parseString(t, "hint.enable = maybe\n") | |||
| 1044 | + | if c.Hint.Enable || len(w) != 1 { | |||
| 1045 | + | t.Fatal(c.Hint, w) | |||
| 1046 | + | } | |||
| 1047 | + | } | |||