hyprland/module: ordered insertion and config fix command

parent 5520a33c
...@@ -15,6 +15,18 @@ import ( ...@@ -15,6 +15,18 @@ import (
"github.com/urfave/cli/v3" "github.com/urfave/cli/v3"
) )
func HyprlandFixConfigCommand(ctx context.Context, cmd *cli.Command) error {
manager, err := GetHyprlandManager(ctx)
if err != nil {
return err
}
manager.FixConfig()
color.Green("Конфигурация исправлена")
return nil
}
func HyprlandCheckCommand(ctx context.Context, cmd *cli.Command) error { func HyprlandCheckCommand(ctx context.Context, cmd *cli.Command) error {
manager, err := GetHyprlandManager(ctx) manager, err := GetHyprlandManager(ctx)
if err != nil { if err != nil {
......
...@@ -37,6 +37,11 @@ func CommandList() *cli.Command { ...@@ -37,6 +37,11 @@ func CommandList() *cli.Command {
Action: HyprlandCheckCommand, Action: HyprlandCheckCommand,
}, },
{ {
Name: "fix",
Usage: "Fix config: sort modules into sections by order",
Action: HyprlandFixConfigCommand,
},
{
Name: "sync-xkb-layouts", Name: "sync-xkb-layouts",
Usage: "Sync layouts with xkb", Usage: "Sync layouts with xkb",
Action: HyprlandSyncSystemLayouts, Action: HyprlandSyncSystemLayouts,
......
...@@ -434,9 +434,26 @@ func (m *HyprlandManager) enableMessage(module string, disabledGroup []string) s ...@@ -434,9 +434,26 @@ func (m *HyprlandManager) enableMessage(module string, disabledGroup []string) s
for _, name := range disabledGroup { for _, name := range disabledGroup {
msg += fmt.Sprintf("\nМодуль '%s' отключён (группа)", name) msg += fmt.Sprintf("\nМодуль '%s' отключён (группа)", name)
} }
if m.hasSourcesOutsideSections() {
msg += "\n\nВнимание: обнаружены модули вне секций. Выполните 'ximperconf hyprland fix' для исправления."
}
return msg return msg
} }
func (m *HyprlandManager) hasSourcesOutsideSections() bool {
sysStart, sysEnd, sysFound := m.findSectionRange(sectionSystemModules)
userStart, userEnd, userFound := m.findSectionRange(sectionUserModules)
for _, src := range m.Sources {
inSys := sysFound && src.LineNumber > sysStart && src.LineNumber < sysEnd
inUser := userFound && src.LineNumber > userStart && src.LineNumber < userEnd
if !inSys && !inUser {
return true
}
}
return false
}
// Sections // Sections
// Порядок секций: VARS → SYSTEM MODULES → USER MODULES // Порядок секций: VARS → SYSTEM MODULES → USER MODULES
...@@ -604,6 +621,152 @@ func (m *HyprlandManager) insertLine(at int, line string) { ...@@ -604,6 +621,152 @@ func (m *HyprlandManager) insertLine(at int, line string) {
m.Changed = true m.Changed = true
} }
// Сортирует source-строки по секциям и order,
func (m *HyprlandManager) FixConfig() {
type sourceEntry struct {
line string
path string
order int
isUser bool
commented bool
}
var entries []sourceEntry
for _, src := range m.Sources {
isUser := strings.HasPrefix(src.Path, "~")
name := strings.TrimSuffix(filepath.Base(src.Path), ".conf")
meta := ParseModuleMeta(m.GetModuleFile(name, isUser))
order := 0
if meta != nil {
order = meta.Order
}
entries = append(entries, sourceEntry{
line: m.Lines[src.LineNumber],
path: src.Path,
order: order,
isUser: isUser,
commented: src.Commented,
})
}
toRemove := make(map[int]bool)
for _, src := range m.Sources {
toRemove[src.LineNumber] = true
}
if ln := m.findSectionMarker(sectionSystemModules); ln >= 0 {
toRemove[ln] = true
}
if ln := m.findSectionMarker(sectionUserModules); ln >= 0 {
toRemove[ln] = true
}
var cleaned []string
emptyRun := 0
for i, line := range m.Lines {
if toRemove[i] {
continue
}
trimmed := strings.TrimSpace(line)
if strings.HasPrefix(trimmed, sectionMarkerPrefix) {
hasContent := false
for j := i + 1; j < len(m.Lines); j++ {
if toRemove[j] {
continue
}
jTrimmed := strings.TrimSpace(m.Lines[j])
if jTrimmed == "" || strings.HasPrefix(jTrimmed, "#") {
continue
}
if strings.HasPrefix(jTrimmed, sectionMarkerPrefix) {
break
}
hasContent = true
break
}
if !hasContent {
continue
}
}
if trimmed == "" {
emptyRun++
if emptyRun <= 1 {
cleaned = append(cleaned, line)
}
} else {
emptyRun = 0
cleaned = append(cleaned, line)
}
}
for len(cleaned) > 0 && strings.TrimSpace(cleaned[len(cleaned)-1]) == "" {
cleaned = cleaned[:len(cleaned)-1]
}
m.Lines = cleaned
m.Vars = m.GetVarList()
m.Sources = nil
slices.SortStableFunc(entries, func(a, b sourceEntry) int {
if a.isUser != b.isUser {
if a.isUser {
return 1
}
return -1
}
if a.order != b.order {
if a.order == 0 {
return 1
}
if b.order == 0 {
return -1
}
return a.order - b.order
}
return 0
})
insertSection := func(sectionEntries []sourceEntry, user bool) {
if len(sectionEntries) == 0 {
return
}
markerLine := m.createModulesSection(user)
pos := markerLine + 1
prevOrder := -1
for _, e := range sectionEntries {
if prevOrder >= 0 && prevOrder != e.order {
m.insertLine(pos, "")
pos++
}
m.insertLine(pos, e.line)
m.Sources = append(m.Sources, SourceLine{
Path: e.path,
LineNumber: pos,
Commented: e.commented,
})
prevOrder = e.order
pos++
}
}
var sysEntries, userEntries []sourceEntry
for _, e := range entries {
if e.isUser {
userEntries = append(userEntries, e)
} else {
sysEntries = append(sysEntries, e)
}
}
insertSection(sysEntries, false)
insertSection(userEntries, true)
m.Changed = true
}
// Plugins // Plugins
func (m *HyprlandManager) GetLoadedPlugins() []HyprPlugin { func (m *HyprlandManager) GetLoadedPlugins() []HyprPlugin {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment