hyprland/module: unify user and system module namespace

parent 614ff808
......@@ -59,13 +59,12 @@ func HyprlandCheckCommand(ctx context.Context, cmd *cli.Command) error {
}
func HyprlandModuleStatusCommand(ctx context.Context, cmd *cli.Command) error {
manager, err := GetHyprlandManager(ctx)
if err != nil {
return err
}
info := manager.GetModuleInfo(cmd.Args().Get(0), cmd.Bool("user"))
info := manager.GetModuleInfo(cmd.Args().Get(0))
if !info.Available {
return errors.New(locale.T("invalid module name"))
}
......@@ -75,7 +74,6 @@ func HyprlandModuleStatusCommand(ctx context.Context, cmd *cli.Command) error {
func HyprlandModuleCheckCommand(ctx context.Context, cmd *cli.Command) error {
module := cmd.Args().Get(0)
user := cmd.Bool("user")
if module == "" {
return errors.New(locale.T("specify module name"))
......@@ -86,14 +84,13 @@ func HyprlandModuleCheckCommand(ctx context.Context, cmd *cli.Command) error {
return err
}
moduleinfo := manager.GetModuleInfo(module, user)
moduleinfo := manager.GetModuleInfo(module)
if !moduleinfo.Available {
return errors.New(locale.T("invalid module name"))
}
result, err := manager.CheckModule(module, user)
result, err := manager.CheckModule(module)
if err != nil {
return err
}
......@@ -122,7 +119,7 @@ func HyprlandModuleEnableCommand(ctx context.Context, cmd *cli.Command) error {
if err != nil {
return err
}
msg, err := manager.SetModule("enable", cmd.Args().Get(0), cmd.Bool("user"), false)
msg, err := manager.SetModule("enable", cmd.Args().Get(0), false)
if err != nil {
return err
}
......@@ -130,6 +127,7 @@ func HyprlandModuleEnableCommand(ctx context.Context, cmd *cli.Command) error {
color.Green(msg)
return nil
}
func HyprlandModuleDisableCommand(ctx context.Context, cmd *cli.Command) error {
remove := cmd.Bool("remove")
......@@ -143,10 +141,7 @@ func HyprlandModuleDisableCommand(ctx context.Context, cmd *cli.Command) error {
action = "remove"
}
msg, err := manager.SetModule(
action, cmd.Args().Get(0), cmd.Bool("user"), false,
)
msg, err := manager.SetModule(action, cmd.Args().Get(0), false)
if err != nil {
return err
}
......@@ -154,28 +149,28 @@ func HyprlandModuleDisableCommand(ctx context.Context, cmd *cli.Command) error {
color.Green(msg)
return nil
}
func HyprlandToggleModuleCommand(ctx context.Context, cmd *cli.Command) error {
module := cmd.Args().Get(0)
user := cmd.Bool("user")
manager, err := GetHyprlandManager(ctx)
if err != nil {
return err
}
info := manager.GetModuleInfo(module, user)
info := manager.GetModuleInfo(module)
var msg string
switch {
case info.Status.IsEqual(config.ModuleStatus.Enabled):
msg, err = manager.SetModule("disable", module, user, false)
msg, err = manager.SetModule("disable", module, false)
case info.Status.IsEqual(config.ModuleStatus.Disabled), info.Status.IsEqual(config.ModuleStatus.Unused):
msg, err = manager.SetModule("enable", module, user, false)
msg, err = manager.SetModule("enable", module, false)
case info.Status.IsEqual(config.ModuleStatus.Missing):
msg, err = manager.SetModule("disable", module, user, false)
msg, err = manager.SetModule("disable", module, false)
default:
msg, err = manager.SetModule("enable", module, user, false)
msg, err = manager.SetModule("enable", module, false)
}
if err != nil {
......@@ -187,7 +182,6 @@ func HyprlandToggleModuleCommand(ctx context.Context, cmd *cli.Command) error {
}
func HyprlandInfoModulesCommand(ctx context.Context, cmd *cli.Command) error {
user := cmd.Bool("user")
filter := cmd.String("filter")
manager, err := GetHyprlandManager(ctx)
......@@ -195,7 +189,7 @@ func HyprlandInfoModulesCommand(ctx context.Context, cmd *cli.Command) error {
return err
}
modules := manager.GetModulesList(user, filter)
modules := manager.GetModulesList(filter)
if len(modules) == 0 {
if config.IsJSON(cmd) {
return ui.PrintJSON([]ModuleInfoJSON{})
......@@ -210,7 +204,7 @@ func HyprlandInfoModulesCommand(ctx context.Context, cmd *cli.Command) error {
data := make([]moduleData, 0, len(modules))
for _, info := range modules {
errors, err := manager.CheckModule(info.Name, user)
errors, err := manager.CheckModule(info.Name)
if err != nil {
errors = nil
}
......@@ -222,6 +216,7 @@ func HyprlandInfoModulesCommand(ctx context.Context, cmd *cli.Command) error {
for i, d := range data {
jsonItems[i] = ModuleInfoJSON{
Name: d.info.Name,
User: d.info.User,
Status: d.info.Status.Label,
Errors: d.errorNum,
}
......@@ -233,7 +228,7 @@ func HyprlandInfoModulesCommand(ctx context.Context, cmd *cli.Command) error {
return ui.PrintJSON(jsonItems)
}
items := make([]ui.TreeItem, 0, len(data))
var sysItems, userItems []ui.TreeItem
for _, d := range data {
var parts []string
if d.info.Meta != nil && d.info.Meta.Summary != "" {
......@@ -246,46 +241,61 @@ func HyprlandInfoModulesCommand(ctx context.Context, cmd *cli.Command) error {
if d.info.Meta != nil && d.info.Meta.Group != "" {
name = d.info.Meta.Group + "/" + d.info.Name
}
items = append(items, ui.TreeItem{
item := ui.TreeItem{
Name: name,
Status: d.info.Status,
Description: strings.Join(parts, " "),
})
}
if d.info.User {
userItems = append(userItems, item)
} else {
sysItems = append(sysItems, item)
}
}
ui.RenderTree(ui.RenderTreeOptions{
Title: locale.T("Modules"),
Items: items,
opts := ui.RenderTreeOptions{
Style: ui.DefaultTreeStyle,
Color: true,
Sort: true,
})
}
if len(sysItems) > 0 {
opts.Title = locale.T("System modules")
opts.Items = sysItems
ui.RenderTree(opts)
}
if len(userItems) > 0 {
if len(sysItems) > 0 {
fmt.Println()
}
opts.Title = locale.T("User modules")
opts.Items = userItems
ui.RenderTree(opts)
}
return nil
}
func HyprlandModuleEditCommand(ctx context.Context, cmd *cli.Command) error {
module := cmd.Args().Get(0)
user := cmd.Bool("user")
manager, err := GetHyprlandManager(ctx)
if err != nil {
return err
}
modulefile := manager.GetModuleFile(module, user)
info := manager.GetModuleInfo(module, user)
info := manager.GetModuleInfo(module)
if !info.Available {
return errors.New(locale.T("invalid module name"))
}
if info.Path == "" {
return fmt.Errorf(locale.T("module '%s' not found: %s"), module, modulefile)
return fmt.Errorf(locale.T("module '%s' not found"), module)
}
editor := getEditor()
editCmd := exec.Command(editor, modulefile)
editCmd := exec.Command(editor, info.Path)
editCmd.Stdin = os.Stdin
editCmd.Stdout = os.Stdout
editCmd.Stderr = os.Stderr
......@@ -304,18 +314,17 @@ func HyprlandModuleShowCommand(ctx context.Context, cmd *cli.Command) error {
return errors.New(locale.T("specify module name"))
}
user := cmd.Bool("user")
manager, err := GetHyprlandManager(ctx)
if err != nil {
return err
}
info := manager.GetModuleInfo(module, user)
info := manager.GetModuleInfo(module)
if !info.Available {
return errors.New(locale.T("invalid module name"))
}
errors, _ := manager.CheckModule(module, user)
errors, _ := manager.CheckModule(module)
if config.IsJSON(cmd) {
jsonErrors := make([]ModuleErrorJSON, len(errors))
......@@ -392,7 +401,7 @@ func HyprlandNotifyErrorsCommand(ctx context.Context, cmd *cli.Command) error {
}
func cleanupNotifyModule(manager *HyprlandManager, logPath string) {
manager.SetModule("disable", "ximperconf-errors", false, false)
manager.SetModule("disable", "ximperconf-errors", false)
os.Remove(logPath)
}
......
......@@ -64,14 +64,6 @@ func CommandList() *cli.Command {
{
Name: "module",
Usage: locale.T("Hyprland modules"),
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "user",
Usage: locale.T("use user modules"),
Aliases: []string{"u"},
Value: false,
},
},
Commands: []*cli.Command{
{
Name: "check",
......@@ -273,7 +265,7 @@ func ShellCompleteModule(filter string) func(ctx context.Context, cmd *cli.Comma
return
}
modules := manager.GetModulesList(cmd.Bool("user"), filter)
modules := manager.GetModulesList(filter)
for _, m := range modules {
fmt.Println(m.Name)
}
......
......@@ -12,6 +12,7 @@ type ModuleErrorJSON struct {
type ModuleInfoJSON struct {
Name string `json:"name"`
User bool `json:"user"`
Group string `json:"group"`
Summary string `json:"summary"`
Status string `json:"status"`
......
......@@ -38,6 +38,7 @@ type SourceLine struct {
type HyprModule struct {
Name string `json:"name"`
User bool `json:"user"`
Status config.ItemStatus `json:"status"`
Path string `json:"path"`
ConfPath string `json:"conf_path"`
......@@ -144,12 +145,8 @@ func (m *HyprlandManager) Check(configPath string) ([]HyprConfigError, error) {
return m.parseCheckOutput(output), nil
}
func (m *HyprlandManager) CheckModule(
module string,
user bool,
) ([]HyprConfigError, error) {
info := m.GetModuleInfo(module, user)
func (m *HyprlandManager) CheckModule(module string) ([]HyprConfigError, error) {
info := m.GetModuleInfo(module)
if info.Path == "" {
return nil, fmt.Errorf(locale.T("module '%s' not found"), module)
......@@ -173,34 +170,74 @@ func (m *HyprlandManager) CheckModule(
// Modules
func (m *HyprlandManager) GetModuleInfo(module string, user bool) HyprModule {
sysFile := m.GetModuleFile(module, user)
func (m *HyprlandManager) resolveModule(name string) (user bool, found bool) {
inUser := slices.Contains(m.UserModules, name)
inSystem := slices.Contains(m.SystemModules, name)
var confPath string
switch {
case inUser:
return true, true
case inSystem:
return false, true
}
// Файла нет ни в одном каталоге — ищем по source-строкам
userConfPath := "~" + strings.TrimPrefix(m.GetModuleFile(name, true), m.Home)
sysConfPath := m.GetModuleFile(name, false)
for _, src := range m.Sources {
if src.Path == userConfPath {
return true, true
}
if src.Path == sysConfPath {
return false, true
}
}
return false, false
}
func (m *HyprlandManager) GetModuleInfo(module string) HyprModule {
user, found := m.resolveModule(module)
if !found {
return HyprModule{
Name: module,
Status: config.ModuleStatus.Unknown,
Available: !slices.Contains(config.HyprlandSkipModules, module),
}
}
return m.getModuleInfo(module, user)
}
func (m *HyprlandManager) getModuleInfo(module string, user bool) HyprModule {
modulePath := m.GetModuleFile(module, user)
var confPath string
if user {
confPath = "~" + strings.TrimPrefix(sysFile, m.Home)
confPath = "~" + strings.TrimPrefix(modulePath, m.Home)
} else {
confPath = sysFile
confPath = modulePath
}
available := !slices.Contains(config.HyprlandSkipModules, module)
fileExists := config.FileExists(sysFile)
fileExists := config.FileExists(modulePath)
// Конфиг Hyprland отсутствует
if !config.FileExists(config.Env.Hyprland.Config) {
if fileExists {
return HyprModule{
Name: module,
User: user,
Status: config.ModuleStatus.Unused,
Path: sysFile,
Path: modulePath,
ConfPath: confPath,
Available: available,
Meta: ParseModuleMeta(sysFile),
Meta: ParseModuleMeta(modulePath),
}
}
return HyprModule{
Name: module,
User: user,
Status: config.ModuleStatus.Missing,
Available: available,
}
......@@ -232,6 +269,7 @@ func (m *HyprlandManager) GetModuleInfo(module string, user bool) HyprModule {
if !fileExists {
return HyprModule{
Name: module,
User: user,
Status: config.ModuleStatus.Missing,
LineNumber: lineNumber,
Available: available,
......@@ -240,12 +278,13 @@ func (m *HyprlandManager) GetModuleInfo(module string, user bool) HyprModule {
// файл есть
return HyprModule{
Name: module,
User: user,
Status: config.ModuleStatus.Enabled,
Path: sysFile,
Path: modulePath,
ConfPath: confPath,
LineNumber: lineNumber,
Available: available,
Meta: ParseModuleMeta(sysFile),
Meta: ParseModuleMeta(modulePath),
}
}
......@@ -253,14 +292,15 @@ func (m *HyprlandManager) GetModuleInfo(module string, user bool) HyprModule {
if foundCommented {
mod := HyprModule{
Name: module,
User: user,
Status: config.ModuleStatus.Disabled,
Path: sysFile,
Path: modulePath,
ConfPath: confPath,
LineNumber: lineNumber,
Available: available,
}
if fileExists {
mod.Meta = ParseModuleMeta(sysFile)
mod.Meta = ParseModuleMeta(modulePath)
} else {
mod.Path = ""
}
......@@ -271,17 +311,19 @@ func (m *HyprlandManager) GetModuleInfo(module string, user bool) HyprModule {
if fileExists {
return HyprModule{
Name: module,
User: user,
Status: config.ModuleStatus.Unused,
Path: sysFile,
Path: modulePath,
ConfPath: confPath,
Available: available,
Meta: ParseModuleMeta(sysFile),
Meta: ParseModuleMeta(modulePath),
}
}
// Файла нет и упоминаний нет
return HyprModule{
Name: module,
User: user,
Status: config.ModuleStatus.Unknown,
Available: available,
}
......@@ -301,20 +343,27 @@ func (m *HyprlandManager) GetModuleDir(user bool) string {
return config.Env.Hyprland.SystemModulesDir
}
func (m *HyprlandManager) GetModulesList(user bool, filter string) []HyprModule {
var modules []string
if user {
modules = m.UserModules
} else {
modules = m.SystemModules
func (m *HyprlandManager) GetModulesList(filter string) []HyprModule {
seen := make(map[string]bool)
var allModules []string
// Системные, потом пользовательские; при совпадении имени — user перекрывает
for _, name := range m.SystemModules {
allModules = append(allModules, name)
seen[name] = true
}
for _, name := range m.UserModules {
if !seen[name] {
allModules = append(allModules, name)
}
}
groupFilter := strings.TrimPrefix(filter, "group:")
isGroupFilter := groupFilter != filter
var res []HyprModule
for _, module := range modules {
info := m.GetModuleInfo(module, user)
for _, module := range allModules {
info := m.GetModuleInfo(module)
if !info.Available {
continue
......@@ -322,6 +371,8 @@ func (m *HyprlandManager) GetModulesList(user bool, filter string) []HyprModule
if filter == "" || filter == "all" ||
info.Status.Label == filter ||
(filter == "user" && info.User) ||
(filter == "system" && !info.User) ||
(isGroupFilter && info.Meta != nil && info.Meta.Group == groupFilter) {
res = append(res, info)
}
......@@ -329,8 +380,8 @@ func (m *HyprlandManager) GetModulesList(user bool, filter string) []HyprModule
return res
}
func (m *HyprlandManager) SetModule(action, module string, user, onlyNew bool) (string, error) {
info := m.GetModuleInfo(module, user)
func (m *HyprlandManager) SetModule(action, module string, onlyNew bool) (string, error) {
info := m.GetModuleInfo(module)
if !info.Available {
return "", errors.New(locale.T("invalid module name"))
......@@ -342,7 +393,6 @@ func (m *HyprlandManager) SetModule(action, module string, user, onlyNew bool) (
switch action {
case "enable":
// нет файла
if info.Path == "" || info.Status.IsEqual(config.ModuleStatus.Unknown) {
return "", errors.New(locale.T("cannot enable this module"))
......@@ -353,10 +403,10 @@ func (m *HyprlandManager) SetModule(action, module string, user, onlyNew bool) (
return "", fmt.Errorf(locale.T("module '%s' is already enabled"), module)
}
// отключить другие модули той же группы
// отключить другие модули той же группы (кросс-типовое)
var disabledGroup []string
if info.Meta != nil && info.Meta.Group != "" {
for _, other := range m.GetModulesList(user, "enabled") {
for _, other := range m.GetModulesList("enabled") {
if other.Name == module {
continue
}
......@@ -386,14 +436,13 @@ func (m *HyprlandManager) SetModule(action, module string, user, onlyNew bool) (
if info.Meta != nil {
order = info.Meta.Order
}
m.insertSourceLine(info.ConfPath, user, order)
m.insertSourceLine(info.ConfPath, info.User, order)
return m.enableMessage(module, disabledGroup), nil
}
return "", fmt.Errorf(locale.T("module '%s' not changed"), module)
case "disable":
// Уже выключен
if info.Status.IsEqual(config.ModuleStatus.Disabled) ||
info.Status.IsEqual(config.ModuleStatus.Unused) {
......
......@@ -199,7 +199,7 @@ func processHyprModules(manager *hyprland.HyprlandManager, prof config.PresetPro
continue
}
if _, err := manager.SetModule(m.Action, m.Name, m.User, m.NewOnly); err != nil {
if _, err := manager.SetModule(m.Action, m.Name, m.NewOnly); err != nil {
res.Add(cat, fmt.Sprintf(locale.T("Error (%s): %v"), m.Name, err), config.OpStatus.Error)
} else {
res.Add(cat, fmt.Sprintf("%s: %s", m.Name, m.Action), config.OpStatus.Done)
......@@ -253,12 +253,12 @@ func processVerifyModules(manager *hyprland.HyprlandManager, prof config.PresetP
continue
}
info := manager.GetModuleInfo(name, isUser)
info := manager.GetModuleInfo(name)
if !info.Status.IsEqual(config.ModuleStatus.Enabled) {
continue
}
if _, err := manager.SetModule("disable", name, isUser, false); err != nil {
if _, err := manager.SetModule("disable", name, false); err != nil {
res.Add(cat, fmt.Sprintf(locale.T("Failed to disable '%s': %v"), name, err), config.OpStatus.Error)
continue
}
......@@ -274,7 +274,7 @@ func processVerifyModules(manager *hyprland.HyprlandManager, prof config.PresetP
}
// Включаем модуль уведомления об ошибках
if _, err := manager.SetModule("enable", "ximperconf-errors", false, false); err != nil {
if _, err := manager.SetModule("enable", "ximperconf-errors", false); err != nil {
res.Add(cat, fmt.Sprintf(locale.T("Failed to enable notification module: %v"), err), config.OpStatus.Error)
}
}
......
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