mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-08 12:13:31 -04:00
e50ac208e3
- Hide workspace tags during Mango overview - Add HJKL focus/move defaults - Add Mango natural touchpad scrolling & cursor configs - Fix Mango startup
667 lines
17 KiB
Go
667 lines
17 KiB
Go
package providers
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/config"
|
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/keybinds"
|
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/utils"
|
|
)
|
|
|
|
type MangoWCProvider struct {
|
|
configPath string
|
|
dmsBindsIncluded bool
|
|
parsed bool
|
|
}
|
|
|
|
func NewMangoWCProvider(configPath string) *MangoWCProvider {
|
|
if configPath == "" {
|
|
configPath = defaultMangoWCConfigDir()
|
|
}
|
|
return &MangoWCProvider{
|
|
configPath: configPath,
|
|
}
|
|
}
|
|
|
|
func defaultMangoWCConfigDir() string {
|
|
configDir, err := os.UserConfigDir()
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return filepath.Join(configDir, "mango")
|
|
}
|
|
|
|
func (m *MangoWCProvider) Name() string {
|
|
return "mangowc"
|
|
}
|
|
|
|
func (m *MangoWCProvider) GetCheatSheet() (*keybinds.CheatSheet, error) {
|
|
result, err := ParseMangoWCKeysWithDMS(m.configPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse mangowc config: %w", err)
|
|
}
|
|
|
|
m.dmsBindsIncluded = result.DMSBindsIncluded
|
|
m.parsed = true
|
|
|
|
categorizedBinds := make(map[string][]keybinds.Keybind)
|
|
for _, kb := range result.Keybinds {
|
|
category := m.categorizeByCommand(kb.Command)
|
|
bind := m.convertKeybind(&kb, result.ConflictingConfigs)
|
|
categorizedBinds[category] = append(categorizedBinds[category], bind)
|
|
}
|
|
|
|
sheet := &keybinds.CheatSheet{
|
|
Title: "MangoWC Keybinds",
|
|
Provider: m.Name(),
|
|
Binds: categorizedBinds,
|
|
DMSBindsIncluded: result.DMSBindsIncluded,
|
|
}
|
|
|
|
if result.DMSStatus != nil {
|
|
sheet.DMSStatus = &keybinds.DMSBindsStatus{
|
|
Exists: result.DMSStatus.Exists,
|
|
Included: result.DMSStatus.Included,
|
|
IncludePosition: result.DMSStatus.IncludePosition,
|
|
TotalIncludes: result.DMSStatus.TotalIncludes,
|
|
BindsAfterDMS: result.DMSStatus.BindsAfterDMS,
|
|
Effective: result.DMSStatus.Effective,
|
|
OverriddenBy: result.DMSStatus.OverriddenBy,
|
|
StatusMessage: result.DMSStatus.StatusMessage,
|
|
}
|
|
}
|
|
|
|
return sheet, nil
|
|
}
|
|
|
|
func (m *MangoWCProvider) HasDMSBindsIncluded() bool {
|
|
if m.parsed {
|
|
return m.dmsBindsIncluded
|
|
}
|
|
|
|
result, err := ParseMangoWCKeysWithDMS(m.configPath)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
m.dmsBindsIncluded = result.DMSBindsIncluded
|
|
m.parsed = true
|
|
return m.dmsBindsIncluded
|
|
}
|
|
|
|
func (m *MangoWCProvider) categorizeByCommand(command string) string {
|
|
switch {
|
|
case strings.Contains(command, "mon"):
|
|
return "Monitor"
|
|
case command == "toggleoverview":
|
|
return "Overview"
|
|
case command == "toggle_scratchpad":
|
|
return "Scratchpad"
|
|
case strings.Contains(command, "layout") || strings.Contains(command, "proportion"):
|
|
return "Layout"
|
|
case strings.Contains(command, "gaps"):
|
|
return "Gaps"
|
|
case strings.Contains(command, "view") || strings.Contains(command, "tag"):
|
|
return "Tags"
|
|
case command == "focusstack" ||
|
|
command == "focusdir" ||
|
|
command == "exchange_client" ||
|
|
command == "killclient" ||
|
|
command == "togglefloating" ||
|
|
command == "togglefullscreen" ||
|
|
command == "togglefakefullscreen" ||
|
|
command == "togglemaximizescreen" ||
|
|
command == "toggleglobal" ||
|
|
command == "toggleoverlay" ||
|
|
command == "minimized" ||
|
|
command == "restore_minimized" ||
|
|
command == "movewin" ||
|
|
command == "resizewin":
|
|
return "Window"
|
|
case command == "spawn" || command == "spawn_shell":
|
|
return "Execute"
|
|
case command == "quit" || command == "reload_config":
|
|
return "System"
|
|
default:
|
|
return "Other"
|
|
}
|
|
}
|
|
|
|
func (m *MangoWCProvider) convertKeybind(kb *MangoWCKeyBinding, conflicts map[string]*MangoWCKeyBinding) keybinds.Keybind {
|
|
keyStr := m.formatKey(kb)
|
|
rawAction := m.formatRawAction(kb.Command, kb.Params)
|
|
desc := kb.Comment
|
|
|
|
if desc == "" {
|
|
desc = rawAction
|
|
}
|
|
|
|
source := "config"
|
|
if strings.Contains(kb.Source, "dms/binds.conf") || strings.Contains(kb.Source, "dms"+string(filepath.Separator)+"binds.conf") {
|
|
source = "dms-default"
|
|
}
|
|
|
|
bind := keybinds.Keybind{
|
|
Key: keyStr,
|
|
Description: desc,
|
|
Action: rawAction,
|
|
Source: source,
|
|
}
|
|
|
|
if source == "dms-default" && conflicts != nil {
|
|
normalizedKey := strings.ToLower(keyStr)
|
|
if conflictKb, ok := conflicts[normalizedKey]; ok {
|
|
bind.Conflict = &keybinds.Keybind{
|
|
Key: keyStr,
|
|
Description: conflictKb.Comment,
|
|
Action: m.formatRawAction(conflictKb.Command, conflictKb.Params),
|
|
Source: "config",
|
|
}
|
|
}
|
|
}
|
|
|
|
return bind
|
|
}
|
|
|
|
func (m *MangoWCProvider) formatRawAction(command, params string) string {
|
|
if params != "" {
|
|
return command + " " + params
|
|
}
|
|
return command
|
|
}
|
|
|
|
func (m *MangoWCProvider) formatKey(kb *MangoWCKeyBinding) string {
|
|
parts := make([]string, 0, len(kb.Mods)+1)
|
|
parts = append(parts, kb.Mods...)
|
|
parts = append(parts, kb.Key)
|
|
return strings.Join(parts, "+")
|
|
}
|
|
|
|
func (m *MangoWCProvider) GetOverridePath() string {
|
|
expanded, err := utils.ExpandPath(m.configPath)
|
|
if err != nil {
|
|
return filepath.Join(m.configPath, "dms", "binds.conf")
|
|
}
|
|
return filepath.Join(expanded, "dms", "binds.conf")
|
|
}
|
|
|
|
func (m *MangoWCProvider) validateAction(action string) error {
|
|
action = strings.TrimSpace(action)
|
|
switch {
|
|
case action == "":
|
|
return fmt.Errorf("action cannot be empty")
|
|
case action == "spawn" || action == "spawn ":
|
|
return fmt.Errorf("spawn command requires arguments")
|
|
case action == "spawn_shell" || action == "spawn_shell ":
|
|
return fmt.Errorf("spawn_shell command requires arguments")
|
|
case strings.HasPrefix(action, "spawn "):
|
|
rest := strings.TrimSpace(strings.TrimPrefix(action, "spawn "))
|
|
if rest == "" {
|
|
return fmt.Errorf("spawn command requires arguments")
|
|
}
|
|
case strings.HasPrefix(action, "spawn_shell "):
|
|
rest := strings.TrimSpace(strings.TrimPrefix(action, "spawn_shell "))
|
|
if rest == "" {
|
|
return fmt.Errorf("spawn_shell command requires arguments")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *MangoWCProvider) SetBind(key, action, description string, options map[string]any) error {
|
|
if err := m.validateAction(action); err != nil {
|
|
return err
|
|
}
|
|
|
|
overridePath := m.GetOverridePath()
|
|
|
|
if err := os.MkdirAll(filepath.Dir(overridePath), 0o755); err != nil {
|
|
return fmt.Errorf("failed to create dms directory: %w", err)
|
|
}
|
|
|
|
existingBinds, err := m.loadOverrideBinds()
|
|
if err != nil {
|
|
existingBinds = make(map[string]*mangowcOverrideBind)
|
|
}
|
|
|
|
normalizedKey := strings.ToLower(key)
|
|
prefix := "bind"
|
|
if existing, ok := existingBinds[normalizedKey]; ok && existing.Prefix != "" {
|
|
prefix = existing.Prefix
|
|
}
|
|
if optionPrefix := m.bindPrefixFromOptions(options); optionPrefix != "" {
|
|
prefix = optionPrefix
|
|
}
|
|
|
|
existingBinds[normalizedKey] = &mangowcOverrideBind{
|
|
Key: key,
|
|
Action: action,
|
|
Description: description,
|
|
Options: options,
|
|
Prefix: prefix,
|
|
}
|
|
|
|
return m.writeOverrideBinds(existingBinds)
|
|
}
|
|
|
|
func (m *MangoWCProvider) RemoveBind(key string) error {
|
|
existingBinds, err := m.loadOverrideBinds()
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
normalizedKey := strings.ToLower(key)
|
|
delete(existingBinds, normalizedKey)
|
|
return m.writeOverrideBindsWithRemoved(existingBinds, map[string]bool{normalizedKey: true})
|
|
}
|
|
|
|
func (m *MangoWCProvider) ResetBind(key string) error {
|
|
return m.RemoveBind(key)
|
|
}
|
|
|
|
type mangowcOverrideBind struct {
|
|
Key string
|
|
Action string
|
|
Description string
|
|
Options map[string]any
|
|
Prefix string
|
|
}
|
|
|
|
func (m *MangoWCProvider) loadOverrideBinds() (map[string]*mangowcOverrideBind, error) {
|
|
overridePath := m.GetOverridePath()
|
|
binds := make(map[string]*mangowcOverrideBind)
|
|
|
|
data, err := os.ReadFile(overridePath)
|
|
if os.IsNotExist(err) {
|
|
return binds, nil
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var pendingComment string
|
|
for _, line := range strings.Split(string(data), "\n") {
|
|
trimmed := strings.TrimSpace(line)
|
|
if trimmed == "" {
|
|
pendingComment = ""
|
|
continue
|
|
}
|
|
if strings.HasPrefix(trimmed, "#") {
|
|
pendingComment = strings.TrimSpace(strings.TrimPrefix(trimmed, "#"))
|
|
if isMangoWCSectionComment(pendingComment) {
|
|
pendingComment = ""
|
|
}
|
|
continue
|
|
}
|
|
|
|
bind, ok := m.parseOverrideBindLine(line, pendingComment)
|
|
pendingComment = ""
|
|
if !ok || bind == nil {
|
|
continue
|
|
}
|
|
|
|
binds[strings.ToLower(bind.Key)] = bind
|
|
}
|
|
|
|
return binds, nil
|
|
}
|
|
|
|
func (m *MangoWCProvider) parseOverrideBindLine(line, precedingComment string) (*mangowcOverrideBind, bool) {
|
|
trimmed := strings.TrimSpace(line)
|
|
parts := strings.SplitN(trimmed, "=", 2)
|
|
if len(parts) < 2 {
|
|
return nil, false
|
|
}
|
|
|
|
prefix := strings.TrimSpace(parts[0])
|
|
if !m.isBindPrefix(prefix) {
|
|
return nil, false
|
|
}
|
|
|
|
content := strings.TrimSpace(parts[1])
|
|
commentParts := strings.SplitN(content, "#", 2)
|
|
bindContent := strings.TrimSpace(commentParts[0])
|
|
|
|
description := strings.TrimSpace(precedingComment)
|
|
if isMangoWCSectionComment(description) {
|
|
description = ""
|
|
}
|
|
if len(commentParts) > 1 {
|
|
description = strings.TrimSpace(commentParts[1])
|
|
}
|
|
if strings.HasPrefix(description, MangoWCHideComment) {
|
|
return nil, true
|
|
}
|
|
|
|
fields := strings.SplitN(bindContent, ",", 4)
|
|
if len(fields) < 3 {
|
|
return nil, false
|
|
}
|
|
|
|
mods := strings.TrimSpace(fields[0])
|
|
keyName := strings.TrimSpace(fields[1])
|
|
command := strings.TrimSpace(fields[2])
|
|
|
|
var params string
|
|
if len(fields) > 3 {
|
|
params = strings.TrimSpace(fields[3])
|
|
}
|
|
|
|
action := command
|
|
if params != "" {
|
|
action = command + " " + params
|
|
}
|
|
|
|
return &mangowcOverrideBind{
|
|
Key: m.buildKeyString(mods, keyName),
|
|
Action: action,
|
|
Description: description,
|
|
Prefix: prefix,
|
|
}, true
|
|
}
|
|
|
|
func (m *MangoWCProvider) isBindPrefix(prefix string) bool {
|
|
if !strings.HasPrefix(prefix, "bind") {
|
|
return false
|
|
}
|
|
for _, ch := range strings.TrimPrefix(prefix, "bind") {
|
|
if !strings.ContainsRune("lsrp", ch) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (m *MangoWCProvider) buildKeyString(mods, key string) string {
|
|
if mods == "" || strings.EqualFold(mods, "none") {
|
|
return key
|
|
}
|
|
|
|
modList := strings.FieldsFunc(mods, func(r rune) bool {
|
|
return r == '+' || r == ' '
|
|
})
|
|
|
|
parts := append(modList, key)
|
|
return strings.Join(parts, "+")
|
|
}
|
|
|
|
func (m *MangoWCProvider) getBindSortPriority(action string) int {
|
|
switch {
|
|
case strings.HasPrefix(action, "spawn") && strings.Contains(action, "dms"):
|
|
return 0
|
|
case strings.Contains(action, "view") || strings.Contains(action, "tag"):
|
|
return 1
|
|
case strings.Contains(action, "focus") || strings.Contains(action, "exchange") ||
|
|
strings.Contains(action, "resize") || strings.Contains(action, "move"):
|
|
return 2
|
|
case strings.Contains(action, "mon"):
|
|
return 3
|
|
case strings.HasPrefix(action, "spawn"):
|
|
return 4
|
|
case action == "quit" || action == "reload_config":
|
|
return 5
|
|
default:
|
|
return 6
|
|
}
|
|
}
|
|
|
|
func (m *MangoWCProvider) writeOverrideBinds(binds map[string]*mangowcOverrideBind) error {
|
|
return m.writeOverrideBindsWithRemoved(binds, nil)
|
|
}
|
|
|
|
func (m *MangoWCProvider) writeOverrideBindsWithRemoved(binds map[string]*mangowcOverrideBind, removed map[string]bool) error {
|
|
overridePath := m.GetOverridePath()
|
|
existingContent := ""
|
|
if data, err := os.ReadFile(overridePath); err == nil {
|
|
existingContent = string(data)
|
|
}
|
|
|
|
content := m.generatePreservedBindsContent(existingContent, binds, removed)
|
|
return os.WriteFile(overridePath, []byte(content), 0o644)
|
|
}
|
|
|
|
func (m *MangoWCProvider) generatePreservedBindsContent(existingContent string, binds map[string]*mangowcOverrideBind, removed map[string]bool) string {
|
|
useStockScaffold := m.shouldUseStockScaffold(existingContent)
|
|
source := existingContent
|
|
if useStockScaffold {
|
|
source = m.stockBindsScaffold(binds)
|
|
}
|
|
|
|
remaining := make(map[string]*mangowcOverrideBind, len(binds))
|
|
for key, bind := range binds {
|
|
remaining[key] = bind
|
|
}
|
|
if useStockScaffold {
|
|
m.dropReplacedStockBinds(remaining)
|
|
}
|
|
|
|
var lines []string
|
|
for _, line := range strings.Split(source, "\n") {
|
|
templateBind, ok := m.parseOverrideBindLine(line, m.previousComment(lines))
|
|
if !ok || templateBind == nil {
|
|
lines = append(lines, line)
|
|
continue
|
|
}
|
|
|
|
normalizedKey := strings.ToLower(templateBind.Key)
|
|
m.dropPreviousDescriptionComment(&lines)
|
|
|
|
if bind, exists := remaining[normalizedKey]; exists {
|
|
if useStockScaffold && bind.Description == "" {
|
|
bind = m.copyBindWithDescription(bind, templateBind.Description)
|
|
}
|
|
m.writeBindLineToLines(&lines, bind)
|
|
delete(remaining, normalizedKey)
|
|
continue
|
|
}
|
|
|
|
if useStockScaffold && !removed[normalizedKey] {
|
|
m.writeBindLineToLines(&lines, templateBind)
|
|
}
|
|
}
|
|
|
|
if len(remaining) > 0 {
|
|
m.trimTrailingEmptyLines(&lines)
|
|
if len(lines) > 0 {
|
|
lines = append(lines, "")
|
|
}
|
|
lines = append(lines, "# === Custom Keybinds ===")
|
|
for _, bind := range m.sortedBinds(remaining) {
|
|
m.writeBindLineToLines(&lines, bind)
|
|
}
|
|
}
|
|
|
|
m.trimTrailingEmptyLines(&lines)
|
|
if len(lines) == 0 {
|
|
return ""
|
|
}
|
|
return strings.Join(lines, "\n") + "\n"
|
|
}
|
|
|
|
func (m *MangoWCProvider) shouldUseStockScaffold(content string) bool {
|
|
if strings.TrimSpace(content) == "" {
|
|
return true
|
|
}
|
|
if strings.Contains(content, "gesturebind=") && strings.Contains(content, "# ===") {
|
|
return false
|
|
}
|
|
return !strings.Contains(content, "gesturebind=") && (strings.Count(content, "\nbind=")+strings.Count(content, "\nbindl=")+strings.Count(content, "\nbinds=")+strings.Count(content, "\nbindr=")+strings.Count(content, "\nbindp=") >= 10 || strings.Contains(content, "dms ipc call"))
|
|
}
|
|
|
|
func (m *MangoWCProvider) stockBindsScaffold(binds map[string]*mangowcOverrideBind) string {
|
|
terminalCommand := "ghostty"
|
|
for _, key := range []string{"super+t", "super+return"} {
|
|
if bind, ok := binds[key]; ok {
|
|
command, params := m.parseAction(bind.Action)
|
|
if command == "spawn" && strings.TrimSpace(params) != "" && !strings.Contains(params, "dms ") {
|
|
terminalCommand = params
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return strings.ReplaceAll(config.MangoBindsConfig, "{{TERMINAL_COMMAND}}", terminalCommand)
|
|
}
|
|
|
|
func (m *MangoWCProvider) dropReplacedStockBinds(binds map[string]*mangowcOverrideBind) {
|
|
if bind, ok := binds["super+j"]; ok && bind.Action == "switch_layout" {
|
|
delete(binds, "super+j")
|
|
}
|
|
}
|
|
|
|
func (m *MangoWCProvider) sortedBinds(binds map[string]*mangowcOverrideBind) []*mangowcOverrideBind {
|
|
bindList := make([]*mangowcOverrideBind, 0, len(binds))
|
|
for _, bind := range binds {
|
|
bindList = append(bindList, bind)
|
|
}
|
|
sort.Slice(bindList, func(i, j int) bool {
|
|
pi, pj := m.getBindSortPriority(bindList[i].Action), m.getBindSortPriority(bindList[j].Action)
|
|
if pi != pj {
|
|
return pi < pj
|
|
}
|
|
return bindList[i].Key < bindList[j].Key
|
|
})
|
|
return bindList
|
|
}
|
|
|
|
func (m *MangoWCProvider) writeBindLineToLines(lines *[]string, bind *mangowcOverrideBind) {
|
|
var sb strings.Builder
|
|
m.writeBindLine(&sb, bind)
|
|
text := strings.TrimSuffix(sb.String(), "\n")
|
|
if text == "" {
|
|
return
|
|
}
|
|
*lines = append(*lines, strings.Split(text, "\n")...)
|
|
}
|
|
|
|
func (m *MangoWCProvider) previousComment(lines []string) string {
|
|
if len(lines) == 0 {
|
|
return ""
|
|
}
|
|
trimmed := strings.TrimSpace(lines[len(lines)-1])
|
|
if !strings.HasPrefix(trimmed, "#") {
|
|
return ""
|
|
}
|
|
comment := strings.TrimSpace(strings.TrimPrefix(trimmed, "#"))
|
|
if isMangoWCSectionComment(comment) {
|
|
return ""
|
|
}
|
|
return comment
|
|
}
|
|
|
|
func (m *MangoWCProvider) dropPreviousDescriptionComment(lines *[]string) {
|
|
if len(*lines) == 0 {
|
|
return
|
|
}
|
|
trimmed := strings.TrimSpace((*lines)[len(*lines)-1])
|
|
if !strings.HasPrefix(trimmed, "#") || strings.HasPrefix(trimmed, "# ===") {
|
|
return
|
|
}
|
|
*lines = (*lines)[:len(*lines)-1]
|
|
}
|
|
|
|
func (m *MangoWCProvider) trimTrailingEmptyLines(lines *[]string) {
|
|
for len(*lines) > 0 && strings.TrimSpace((*lines)[len(*lines)-1]) == "" {
|
|
*lines = (*lines)[:len(*lines)-1]
|
|
}
|
|
}
|
|
|
|
func (m *MangoWCProvider) copyBindWithDescription(bind *mangowcOverrideBind, description string) *mangowcOverrideBind {
|
|
copy := *bind
|
|
copy.Description = description
|
|
return ©
|
|
}
|
|
|
|
func (m *MangoWCProvider) writeBindLine(sb *strings.Builder, bind *mangowcOverrideBind) {
|
|
mods, key := m.parseKeyString(bind.Key)
|
|
command, params := m.parseAction(bind.Action)
|
|
|
|
// Description goes on the line ABOVE the bind: mango doesn't strip inline `#`
|
|
// comments from a value, so a trailing comment would break spawn (extra argv).
|
|
if bind.Description != "" {
|
|
sb.WriteString("# ")
|
|
sb.WriteString(bind.Description)
|
|
sb.WriteString("\n")
|
|
}
|
|
|
|
prefix := bind.Prefix
|
|
if prefix == "" {
|
|
prefix = "bind"
|
|
}
|
|
sb.WriteString(prefix)
|
|
sb.WriteString("=")
|
|
if mods == "" {
|
|
sb.WriteString("none")
|
|
} else {
|
|
sb.WriteString(mods)
|
|
}
|
|
sb.WriteString(",")
|
|
sb.WriteString(key)
|
|
sb.WriteString(",")
|
|
sb.WriteString(command)
|
|
|
|
if params != "" {
|
|
sb.WriteString(",")
|
|
sb.WriteString(params)
|
|
}
|
|
|
|
sb.WriteString("\n")
|
|
}
|
|
|
|
func (m *MangoWCProvider) bindPrefixFromOptions(options map[string]any) string {
|
|
if options == nil {
|
|
return ""
|
|
}
|
|
value, ok := options["flags"]
|
|
if !ok {
|
|
return ""
|
|
}
|
|
flags := ""
|
|
switch v := value.(type) {
|
|
case string:
|
|
flags = v
|
|
case fmt.Stringer:
|
|
flags = v.String()
|
|
default:
|
|
return ""
|
|
}
|
|
flags = strings.TrimSpace(flags)
|
|
if flags == "" {
|
|
return "bind"
|
|
}
|
|
var clean strings.Builder
|
|
for _, ch := range flags {
|
|
if strings.ContainsRune("lsrp", ch) && !strings.ContainsRune(clean.String(), ch) {
|
|
clean.WriteRune(ch)
|
|
}
|
|
}
|
|
return "bind" + clean.String()
|
|
}
|
|
|
|
func (m *MangoWCProvider) parseKeyString(keyStr string) (mods, key string) {
|
|
parts := strings.Split(keyStr, "+")
|
|
switch len(parts) {
|
|
case 0:
|
|
return "", keyStr
|
|
case 1:
|
|
return "", parts[0]
|
|
default:
|
|
return strings.Join(parts[:len(parts)-1], "+"), parts[len(parts)-1]
|
|
}
|
|
}
|
|
|
|
func (m *MangoWCProvider) parseAction(action string) (command, params string) {
|
|
parts := strings.SplitN(action, " ", 2)
|
|
switch len(parts) {
|
|
case 0:
|
|
return action, ""
|
|
case 1:
|
|
return parts[0], ""
|
|
default:
|
|
return parts[0], parts[1]
|
|
}
|
|
}
|