mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-11 07:52:50 -05:00
rename backend to core
This commit is contained in:
331
core/internal/hyprland/keybinds.go
Normal file
331
core/internal/hyprland/keybinds.go
Normal file
@@ -0,0 +1,331 @@
|
||||
package hyprland
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
TitleRegex = "#+!"
|
||||
HideComment = "[hidden]"
|
||||
CommentBindPattern = "#/#"
|
||||
)
|
||||
|
||||
var ModSeparators = []rune{'+', ' '}
|
||||
|
||||
type KeyBinding struct {
|
||||
Mods []string `json:"mods"`
|
||||
Key string `json:"key"`
|
||||
Dispatcher string `json:"dispatcher"`
|
||||
Params string `json:"params"`
|
||||
Comment string `json:"comment"`
|
||||
}
|
||||
|
||||
type Section struct {
|
||||
Children []Section `json:"children"`
|
||||
Keybinds []KeyBinding `json:"keybinds"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type Parser struct {
|
||||
contentLines []string
|
||||
readingLine int
|
||||
}
|
||||
|
||||
func NewParser() *Parser {
|
||||
return &Parser{
|
||||
contentLines: []string{},
|
||||
readingLine: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) ReadContent(directory string) error {
|
||||
expandedDir := os.ExpandEnv(directory)
|
||||
expandedDir = filepath.Clean(expandedDir)
|
||||
if strings.HasPrefix(expandedDir, "~") {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
expandedDir = filepath.Join(home, expandedDir[1:])
|
||||
}
|
||||
|
||||
info, err := os.Stat(expandedDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !info.IsDir() {
|
||||
return os.ErrNotExist
|
||||
}
|
||||
|
||||
confFiles, err := filepath.Glob(filepath.Join(expandedDir, "*.conf"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(confFiles) == 0 {
|
||||
return os.ErrNotExist
|
||||
}
|
||||
|
||||
var combinedContent []string
|
||||
for _, confFile := range confFiles {
|
||||
if fileInfo, err := os.Stat(confFile); err == nil && fileInfo.Mode().IsRegular() {
|
||||
data, err := os.ReadFile(confFile)
|
||||
if err == nil {
|
||||
combinedContent = append(combinedContent, string(data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(combinedContent) == 0 {
|
||||
return os.ErrNotExist
|
||||
}
|
||||
|
||||
fullContent := strings.Join(combinedContent, "\n")
|
||||
p.contentLines = strings.Split(fullContent, "\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
func autogenerateComment(dispatcher, params string) string {
|
||||
switch dispatcher {
|
||||
case "resizewindow":
|
||||
return "Resize window"
|
||||
|
||||
case "movewindow":
|
||||
if params == "" {
|
||||
return "Move window"
|
||||
}
|
||||
dirMap := map[string]string{
|
||||
"l": "left",
|
||||
"r": "right",
|
||||
"u": "up",
|
||||
"d": "down",
|
||||
}
|
||||
if dir, ok := dirMap[params]; ok {
|
||||
return "move in " + dir + " direction"
|
||||
}
|
||||
return "move in null direction"
|
||||
|
||||
case "pin":
|
||||
return "pin (show on all workspaces)"
|
||||
|
||||
case "splitratio":
|
||||
return "Window split ratio " + params
|
||||
|
||||
case "togglefloating":
|
||||
return "Float/unfloat window"
|
||||
|
||||
case "resizeactive":
|
||||
return "Resize window by " + params
|
||||
|
||||
case "killactive":
|
||||
return "Close window"
|
||||
|
||||
case "fullscreen":
|
||||
fsMap := map[string]string{
|
||||
"0": "fullscreen",
|
||||
"1": "maximization",
|
||||
"2": "fullscreen on Hyprland's side",
|
||||
}
|
||||
if fs, ok := fsMap[params]; ok {
|
||||
return "Toggle " + fs
|
||||
}
|
||||
return "Toggle null"
|
||||
|
||||
case "fakefullscreen":
|
||||
return "Toggle fake fullscreen"
|
||||
|
||||
case "workspace":
|
||||
switch params {
|
||||
case "+1":
|
||||
return "focus right"
|
||||
case "-1":
|
||||
return "focus left"
|
||||
}
|
||||
return "focus workspace " + params
|
||||
case "movefocus":
|
||||
dirMap := map[string]string{
|
||||
"l": "left",
|
||||
"r": "right",
|
||||
"u": "up",
|
||||
"d": "down",
|
||||
}
|
||||
if dir, ok := dirMap[params]; ok {
|
||||
return "move focus " + dir
|
||||
}
|
||||
return "move focus null"
|
||||
|
||||
case "swapwindow":
|
||||
dirMap := map[string]string{
|
||||
"l": "left",
|
||||
"r": "right",
|
||||
"u": "up",
|
||||
"d": "down",
|
||||
}
|
||||
if dir, ok := dirMap[params]; ok {
|
||||
return "swap in " + dir + " direction"
|
||||
}
|
||||
return "swap in null direction"
|
||||
|
||||
case "movetoworkspace":
|
||||
switch params {
|
||||
case "+1":
|
||||
return "move to right workspace (non-silent)"
|
||||
case "-1":
|
||||
return "move to left workspace (non-silent)"
|
||||
}
|
||||
return "move to workspace " + params + " (non-silent)"
|
||||
case "movetoworkspacesilent":
|
||||
switch params {
|
||||
case "+1":
|
||||
return "move to right workspace"
|
||||
case "-1":
|
||||
return "move to right workspace"
|
||||
}
|
||||
return "move to workspace " + params
|
||||
|
||||
case "togglespecialworkspace":
|
||||
return "toggle special"
|
||||
|
||||
case "exec":
|
||||
return params
|
||||
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) getKeybindAtLine(lineNumber int) *KeyBinding {
|
||||
line := p.contentLines[lineNumber]
|
||||
parts := strings.SplitN(line, "=", 2)
|
||||
if len(parts) < 2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
keys := parts[1]
|
||||
keyParts := strings.SplitN(keys, "#", 2)
|
||||
keys = keyParts[0]
|
||||
|
||||
var comment string
|
||||
if len(keyParts) > 1 {
|
||||
comment = strings.TrimSpace(keyParts[1])
|
||||
}
|
||||
|
||||
keyFields := strings.SplitN(keys, ",", 5)
|
||||
if len(keyFields) < 3 {
|
||||
return nil
|
||||
}
|
||||
|
||||
mods := strings.TrimSpace(keyFields[0])
|
||||
key := strings.TrimSpace(keyFields[1])
|
||||
dispatcher := strings.TrimSpace(keyFields[2])
|
||||
|
||||
var params string
|
||||
if len(keyFields) > 3 {
|
||||
paramParts := keyFields[3:]
|
||||
params = strings.TrimSpace(strings.Join(paramParts, ","))
|
||||
}
|
||||
|
||||
if comment != "" {
|
||||
if strings.HasPrefix(comment, HideComment) {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
comment = autogenerateComment(dispatcher, params)
|
||||
}
|
||||
|
||||
var modList []string
|
||||
if mods != "" {
|
||||
modstring := mods + string(ModSeparators[0])
|
||||
p := 0
|
||||
for index, char := range modstring {
|
||||
isModSep := false
|
||||
for _, sep := range ModSeparators {
|
||||
if char == sep {
|
||||
isModSep = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if isModSep {
|
||||
if index-p > 1 {
|
||||
modList = append(modList, modstring[p:index])
|
||||
}
|
||||
p = index + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &KeyBinding{
|
||||
Mods: modList,
|
||||
Key: key,
|
||||
Dispatcher: dispatcher,
|
||||
Params: params,
|
||||
Comment: comment,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) getBindsRecursive(currentContent *Section, scope int) *Section {
|
||||
titleRegex := regexp.MustCompile(TitleRegex)
|
||||
|
||||
for p.readingLine < len(p.contentLines) {
|
||||
line := p.contentLines[p.readingLine]
|
||||
|
||||
loc := titleRegex.FindStringIndex(line)
|
||||
if loc != nil && loc[0] == 0 {
|
||||
headingScope := strings.Index(line, "!")
|
||||
|
||||
if headingScope <= scope {
|
||||
p.readingLine--
|
||||
return currentContent
|
||||
}
|
||||
|
||||
sectionName := strings.TrimSpace(line[headingScope+1:])
|
||||
p.readingLine++
|
||||
|
||||
childSection := &Section{
|
||||
Children: []Section{},
|
||||
Keybinds: []KeyBinding{},
|
||||
Name: sectionName,
|
||||
}
|
||||
result := p.getBindsRecursive(childSection, headingScope)
|
||||
currentContent.Children = append(currentContent.Children, *result)
|
||||
|
||||
} else if strings.HasPrefix(line, CommentBindPattern) {
|
||||
keybind := p.getKeybindAtLine(p.readingLine)
|
||||
if keybind != nil {
|
||||
currentContent.Keybinds = append(currentContent.Keybinds, *keybind)
|
||||
}
|
||||
|
||||
} else if line == "" || !strings.HasPrefix(strings.TrimSpace(line), "bind") {
|
||||
|
||||
} else {
|
||||
keybind := p.getKeybindAtLine(p.readingLine)
|
||||
if keybind != nil {
|
||||
currentContent.Keybinds = append(currentContent.Keybinds, *keybind)
|
||||
}
|
||||
}
|
||||
|
||||
p.readingLine++
|
||||
}
|
||||
|
||||
return currentContent
|
||||
}
|
||||
|
||||
func (p *Parser) ParseKeys() *Section {
|
||||
p.readingLine = 0
|
||||
rootSection := &Section{
|
||||
Children: []Section{},
|
||||
Keybinds: []KeyBinding{},
|
||||
Name: "",
|
||||
}
|
||||
return p.getBindsRecursive(rootSection, 0)
|
||||
}
|
||||
|
||||
func ParseKeys(path string) (*Section, error) {
|
||||
parser := NewParser()
|
||||
if err := parser.ReadContent(path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parser.ParseKeys(), nil
|
||||
}
|
||||
396
core/internal/hyprland/keybinds_test.go
Normal file
396
core/internal/hyprland/keybinds_test.go
Normal file
@@ -0,0 +1,396 @@
|
||||
package hyprland
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAutogenerateComment(t *testing.T) {
|
||||
tests := []struct {
|
||||
dispatcher string
|
||||
params string
|
||||
expected string
|
||||
}{
|
||||
{"resizewindow", "", "Resize window"},
|
||||
{"movewindow", "", "Move window"},
|
||||
{"movewindow", "l", "move in left direction"},
|
||||
{"movewindow", "r", "move in right direction"},
|
||||
{"movewindow", "u", "move in up direction"},
|
||||
{"movewindow", "d", "move in down direction"},
|
||||
{"pin", "", "pin (show on all workspaces)"},
|
||||
{"splitratio", "0.5", "Window split ratio 0.5"},
|
||||
{"togglefloating", "", "Float/unfloat window"},
|
||||
{"resizeactive", "10 20", "Resize window by 10 20"},
|
||||
{"killactive", "", "Close window"},
|
||||
{"fullscreen", "0", "Toggle fullscreen"},
|
||||
{"fullscreen", "1", "Toggle maximization"},
|
||||
{"fullscreen", "2", "Toggle fullscreen on Hyprland's side"},
|
||||
{"fakefullscreen", "", "Toggle fake fullscreen"},
|
||||
{"workspace", "+1", "focus right"},
|
||||
{"workspace", "-1", "focus left"},
|
||||
{"workspace", "5", "focus workspace 5"},
|
||||
{"movefocus", "l", "move focus left"},
|
||||
{"movefocus", "r", "move focus right"},
|
||||
{"movefocus", "u", "move focus up"},
|
||||
{"movefocus", "d", "move focus down"},
|
||||
{"swapwindow", "l", "swap in left direction"},
|
||||
{"swapwindow", "r", "swap in right direction"},
|
||||
{"swapwindow", "u", "swap in up direction"},
|
||||
{"swapwindow", "d", "swap in down direction"},
|
||||
{"movetoworkspace", "+1", "move to right workspace (non-silent)"},
|
||||
{"movetoworkspace", "-1", "move to left workspace (non-silent)"},
|
||||
{"movetoworkspace", "3", "move to workspace 3 (non-silent)"},
|
||||
{"movetoworkspacesilent", "+1", "move to right workspace"},
|
||||
{"movetoworkspacesilent", "-1", "move to right workspace"},
|
||||
{"movetoworkspacesilent", "2", "move to workspace 2"},
|
||||
{"togglespecialworkspace", "", "toggle special"},
|
||||
{"exec", "firefox", "firefox"},
|
||||
{"unknown", "", ""},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.dispatcher+"_"+tt.params, func(t *testing.T) {
|
||||
result := autogenerateComment(tt.dispatcher, tt.params)
|
||||
if result != tt.expected {
|
||||
t.Errorf("autogenerateComment(%q, %q) = %q, want %q",
|
||||
tt.dispatcher, tt.params, result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetKeybindAtLine(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
line string
|
||||
expected *KeyBinding
|
||||
}{
|
||||
{
|
||||
name: "basic_keybind",
|
||||
line: "bind = SUPER, Q, killactive",
|
||||
expected: &KeyBinding{
|
||||
Mods: []string{"SUPER"},
|
||||
Key: "Q",
|
||||
Dispatcher: "killactive",
|
||||
Params: "",
|
||||
Comment: "Close window",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "keybind_with_params",
|
||||
line: "bind = SUPER, left, movefocus, l",
|
||||
expected: &KeyBinding{
|
||||
Mods: []string{"SUPER"},
|
||||
Key: "left",
|
||||
Dispatcher: "movefocus",
|
||||
Params: "l",
|
||||
Comment: "move focus left",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "keybind_with_comment",
|
||||
line: "bind = SUPER, T, exec, kitty # Open terminal",
|
||||
expected: &KeyBinding{
|
||||
Mods: []string{"SUPER"},
|
||||
Key: "T",
|
||||
Dispatcher: "exec",
|
||||
Params: "kitty",
|
||||
Comment: "Open terminal",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "keybind_hidden",
|
||||
line: "bind = SUPER, H, exec, secret # [hidden]",
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "keybind_multiple_mods",
|
||||
line: "bind = SUPER+SHIFT, F, fullscreen, 0",
|
||||
expected: &KeyBinding{
|
||||
Mods: []string{"SUPER", "SHIFT"},
|
||||
Key: "F",
|
||||
Dispatcher: "fullscreen",
|
||||
Params: "0",
|
||||
Comment: "Toggle fullscreen",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "keybind_no_mods",
|
||||
line: "bind = , Print, exec, screenshot",
|
||||
expected: &KeyBinding{
|
||||
Mods: []string{},
|
||||
Key: "Print",
|
||||
Dispatcher: "exec",
|
||||
Params: "screenshot",
|
||||
Comment: "screenshot",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
parser := NewParser()
|
||||
parser.contentLines = []string{tt.line}
|
||||
result := parser.getKeybindAtLine(0)
|
||||
|
||||
if tt.expected == nil {
|
||||
if result != nil {
|
||||
t.Errorf("expected nil, got %+v", result)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if result == nil {
|
||||
t.Errorf("expected %+v, got nil", tt.expected)
|
||||
return
|
||||
}
|
||||
|
||||
if result.Key != tt.expected.Key {
|
||||
t.Errorf("Key = %q, want %q", result.Key, tt.expected.Key)
|
||||
}
|
||||
if result.Dispatcher != tt.expected.Dispatcher {
|
||||
t.Errorf("Dispatcher = %q, want %q", result.Dispatcher, tt.expected.Dispatcher)
|
||||
}
|
||||
if result.Params != tt.expected.Params {
|
||||
t.Errorf("Params = %q, want %q", result.Params, tt.expected.Params)
|
||||
}
|
||||
if result.Comment != tt.expected.Comment {
|
||||
t.Errorf("Comment = %q, want %q", result.Comment, tt.expected.Comment)
|
||||
}
|
||||
if len(result.Mods) != len(tt.expected.Mods) {
|
||||
t.Errorf("Mods length = %d, want %d", len(result.Mods), len(tt.expected.Mods))
|
||||
} else {
|
||||
for i := range result.Mods {
|
||||
if result.Mods[i] != tt.expected.Mods[i] {
|
||||
t.Errorf("Mods[%d] = %q, want %q", i, result.Mods[i], tt.expected.Mods[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseKeysWithSections(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
configFile := filepath.Join(tmpDir, "hyprland.conf")
|
||||
|
||||
content := `##! Window Management
|
||||
bind = SUPER, Q, killactive
|
||||
bind = SUPER, F, fullscreen, 0
|
||||
|
||||
###! Movement
|
||||
bind = SUPER, left, movefocus, l
|
||||
bind = SUPER, right, movefocus, r
|
||||
|
||||
##! Applications
|
||||
bind = SUPER, T, exec, kitty # Terminal
|
||||
`
|
||||
|
||||
if err := os.WriteFile(configFile, []byte(content), 0644); err != nil {
|
||||
t.Fatalf("Failed to write test config: %v", err)
|
||||
}
|
||||
|
||||
section, err := ParseKeys(tmpDir)
|
||||
if err != nil {
|
||||
t.Fatalf("ParseKeys failed: %v", err)
|
||||
}
|
||||
|
||||
if len(section.Children) != 2 {
|
||||
t.Errorf("Expected 2 top-level sections, got %d", len(section.Children))
|
||||
}
|
||||
|
||||
if len(section.Children) >= 1 {
|
||||
windowMgmt := section.Children[0]
|
||||
if windowMgmt.Name != "Window Management" {
|
||||
t.Errorf("First section name = %q, want %q", windowMgmt.Name, "Window Management")
|
||||
}
|
||||
if len(windowMgmt.Keybinds) != 2 {
|
||||
t.Errorf("Window Management keybinds = %d, want 2", len(windowMgmt.Keybinds))
|
||||
}
|
||||
|
||||
if len(windowMgmt.Children) != 1 {
|
||||
t.Errorf("Window Management children = %d, want 1", len(windowMgmt.Children))
|
||||
} else {
|
||||
movement := windowMgmt.Children[0]
|
||||
if movement.Name != "Movement" {
|
||||
t.Errorf("Movement section name = %q, want %q", movement.Name, "Movement")
|
||||
}
|
||||
if len(movement.Keybinds) != 2 {
|
||||
t.Errorf("Movement keybinds = %d, want 2", len(movement.Keybinds))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(section.Children) >= 2 {
|
||||
apps := section.Children[1]
|
||||
if apps.Name != "Applications" {
|
||||
t.Errorf("Second section name = %q, want %q", apps.Name, "Applications")
|
||||
}
|
||||
if len(apps.Keybinds) != 1 {
|
||||
t.Errorf("Applications keybinds = %d, want 1", len(apps.Keybinds))
|
||||
}
|
||||
if len(apps.Keybinds) > 0 && apps.Keybinds[0].Comment != "Terminal" {
|
||||
t.Errorf("Applications keybind comment = %q, want %q", apps.Keybinds[0].Comment, "Terminal")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseKeysWithCommentBinds(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
configFile := filepath.Join(tmpDir, "test.conf")
|
||||
|
||||
content := `#/# = SUPER, A, exec, app1
|
||||
bind = SUPER, B, exec, app2
|
||||
#/# = SUPER, C, exec, app3 # Custom comment
|
||||
`
|
||||
|
||||
if err := os.WriteFile(configFile, []byte(content), 0644); err != nil {
|
||||
t.Fatalf("Failed to write test config: %v", err)
|
||||
}
|
||||
|
||||
section, err := ParseKeys(tmpDir)
|
||||
if err != nil {
|
||||
t.Fatalf("ParseKeys failed: %v", err)
|
||||
}
|
||||
|
||||
if len(section.Keybinds) != 3 {
|
||||
t.Errorf("Expected 3 keybinds, got %d", len(section.Keybinds))
|
||||
}
|
||||
|
||||
if len(section.Keybinds) > 0 && section.Keybinds[0].Key != "A" {
|
||||
t.Errorf("First keybind key = %q, want %q", section.Keybinds[0].Key, "A")
|
||||
}
|
||||
if len(section.Keybinds) > 1 && section.Keybinds[1].Key != "B" {
|
||||
t.Errorf("Second keybind key = %q, want %q", section.Keybinds[1].Key, "B")
|
||||
}
|
||||
if len(section.Keybinds) > 2 && section.Keybinds[2].Comment != "Custom comment" {
|
||||
t.Errorf("Third keybind comment = %q, want %q", section.Keybinds[2].Comment, "Custom comment")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadContentMultipleFiles(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
file1 := filepath.Join(tmpDir, "a.conf")
|
||||
file2 := filepath.Join(tmpDir, "b.conf")
|
||||
|
||||
content1 := "bind = SUPER, Q, killactive\n"
|
||||
content2 := "bind = SUPER, T, exec, kitty\n"
|
||||
|
||||
if err := os.WriteFile(file1, []byte(content1), 0644); err != nil {
|
||||
t.Fatalf("Failed to write file1: %v", err)
|
||||
}
|
||||
if err := os.WriteFile(file2, []byte(content2), 0644); err != nil {
|
||||
t.Fatalf("Failed to write file2: %v", err)
|
||||
}
|
||||
|
||||
parser := NewParser()
|
||||
if err := parser.ReadContent(tmpDir); err != nil {
|
||||
t.Fatalf("ReadContent failed: %v", err)
|
||||
}
|
||||
|
||||
section := parser.ParseKeys()
|
||||
if len(section.Keybinds) != 2 {
|
||||
t.Errorf("Expected 2 keybinds from multiple files, got %d", len(section.Keybinds))
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadContentErrors(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
path string
|
||||
}{
|
||||
{
|
||||
name: "nonexistent_directory",
|
||||
path: "/nonexistent/path/that/does/not/exist",
|
||||
},
|
||||
{
|
||||
name: "empty_directory",
|
||||
path: t.TempDir(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := ParseKeys(tt.path)
|
||||
if err == nil {
|
||||
t.Error("Expected error, got nil")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadContentWithTildeExpansion(t *testing.T) {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
t.Skip("Cannot get home directory")
|
||||
}
|
||||
|
||||
tmpSubdir := filepath.Join(homeDir, ".config", "test-hypr-"+t.Name())
|
||||
if err := os.MkdirAll(tmpSubdir, 0755); err != nil {
|
||||
t.Skip("Cannot create test directory in home")
|
||||
}
|
||||
defer os.RemoveAll(tmpSubdir)
|
||||
|
||||
configFile := filepath.Join(tmpSubdir, "test.conf")
|
||||
if err := os.WriteFile(configFile, []byte("bind = SUPER, Q, killactive\n"), 0644); err != nil {
|
||||
t.Fatalf("Failed to write test config: %v", err)
|
||||
}
|
||||
|
||||
relPath, err := filepath.Rel(homeDir, tmpSubdir)
|
||||
if err != nil {
|
||||
t.Skip("Cannot create relative path")
|
||||
}
|
||||
|
||||
parser := NewParser()
|
||||
tildePathMatch := "~/" + relPath
|
||||
err = parser.ReadContent(tildePathMatch)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("ReadContent with tilde path failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeybindWithParamsContainingCommas(t *testing.T) {
|
||||
parser := NewParser()
|
||||
parser.contentLines = []string{"bind = SUPER, R, exec, notify-send 'Title' 'Message, with comma'"}
|
||||
|
||||
result := parser.getKeybindAtLine(0)
|
||||
|
||||
if result == nil {
|
||||
t.Fatal("Expected keybind, got nil")
|
||||
}
|
||||
|
||||
expected := "notify-send 'Title' 'Message, with comma'"
|
||||
if result.Params != expected {
|
||||
t.Errorf("Params = %q, want %q", result.Params, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyAndCommentLines(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
configFile := filepath.Join(tmpDir, "test.conf")
|
||||
|
||||
content := `
|
||||
# This is a comment
|
||||
bind = SUPER, Q, killactive
|
||||
|
||||
# Another comment
|
||||
|
||||
bind = SUPER, T, exec, kitty
|
||||
`
|
||||
|
||||
if err := os.WriteFile(configFile, []byte(content), 0644); err != nil {
|
||||
t.Fatalf("Failed to write test config: %v", err)
|
||||
}
|
||||
|
||||
section, err := ParseKeys(tmpDir)
|
||||
if err != nil {
|
||||
t.Fatalf("ParseKeys failed: %v", err)
|
||||
}
|
||||
|
||||
if len(section.Keybinds) != 2 {
|
||||
t.Errorf("Expected 2 keybinds (comments ignored), got %d", len(section.Keybinds))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user