hyprland/module: add metainfo for modules

parent e0f51b85
package config
import "github.com/fatih/color"
import (
"encoding/json"
"github.com/fatih/color"
)
type ItemStatus struct {
Symbol string
......@@ -18,6 +22,10 @@ func (s ItemStatus) IsEqual(other ItemStatus) bool {
return s.Label == other.Label
}
func (s ItemStatus) MarshalJSON() ([]byte, error) {
return json.Marshal(s.Label)
}
var ModuleStatus = struct {
Unknown ItemStatus
Enabled ItemStatus
......
......@@ -9,6 +9,7 @@ import (
"fmt"
"os"
"os/exec"
"strings"
"github.com/fatih/color"
"github.com/urfave/cli/v3"
......@@ -107,14 +108,12 @@ func HyprlandModuleDisableCommand(ctx context.Context, cmd *cli.Command) error {
return err
}
var msg string
action := "disable"
if remove {
action = "remove"
}
msg, err = manager.SetModule(
msg, err := manager.SetModule(
action, cmd.Args().Get(0), cmd.Bool("user"), false,
)
......@@ -169,33 +168,59 @@ func HyprlandInfoModulesCommand(ctx context.Context, cmd *cli.Command) error {
modules := manager.GetModulesList(user, filter)
if len(modules) == 0 {
if config.IsJSON(cmd) {
return ui.PrintJSON([]ui.JSONItem{})
return ui.PrintJSON([]ModuleInfoJSON{})
}
return fmt.Errorf("нет доступных модулей")
}
items := make([]ui.TreeItem, 0, len(modules))
type moduleData struct {
info HyprModule
errorNum int
}
data := make([]moduleData, 0, len(modules))
for _, info := range modules {
errors, err := manager.CheckModule(info.Name, user)
if err != nil {
errors = []HyprConfigError{}
errors = nil
}
errornum := len(errors)
desc := ""
if errornum > 0 {
desc = fmt.Sprintf("(ошибки: %d)", errornum)
}
items = append(items, ui.TreeItem{
Name: info.Name,
Status: info.Status,
Description: desc,
})
data = append(data, moduleData{info: info, errorNum: len(errors)})
}
if config.IsJSON(cmd) {
return ui.PrintJSON(ui.TreeItemsToJSON(items))
jsonItems := make([]ModuleInfoJSON, len(data))
for i, d := range data {
jsonItems[i] = ModuleInfoJSON{
Name: d.info.Name,
Status: d.info.Status.Label,
Errors: d.errorNum,
}
if d.info.Meta != nil {
jsonItems[i].Group = d.info.Meta.Group
jsonItems[i].Summary = d.info.Meta.Summary
}
}
return ui.PrintJSON(jsonItems)
}
items := make([]ui.TreeItem, 0, len(data))
for _, d := range data {
var parts []string
if d.info.Meta != nil && d.info.Meta.Summary != "" {
parts = append(parts, d.info.Meta.Summary)
}
if d.errorNum > 0 {
parts = append(parts, fmt.Sprintf("(ошибки: %d)", d.errorNum))
}
name := d.info.Name
if d.info.Meta != nil && d.info.Meta.Group != "" {
name = d.info.Meta.Group + "/" + d.info.Name
}
items = append(items, ui.TreeItem{
Name: name,
Status: d.info.Status,
Description: strings.Join(parts, " "),
})
}
ui.RenderTree(ui.RenderTreeOptions{
......@@ -268,29 +293,36 @@ func HyprlandModuleShowCommand(ctx context.Context, cmd *cli.Command) error {
jsonErrors[i] = ModuleErrorJSON{Line: e.Line, Text: e.Text}
}
return ui.PrintJSON(ModuleShowJSON{
Name: info.Name,
Status: info.Status.Label,
Path: info.Path,
ConfPath: info.ConfPath,
LineNumber: info.LineNumber,
Available: info.Available,
HyprModule: info,
Errors: jsonErrors,
})
}
fmt.Printf("Модуль: %s\n", info.Name)
fmt.Printf("Статус: %s\n", info.Status.Color("%s %s", info.Status.Symbol, info.Status.Label))
fmt.Printf("Путь: %s\n", info.Path)
fmt.Printf("Путь в конфиге: %s\n", info.ConfPath)
fmt.Printf("Строка в конфиге: %d\n", info.LineNumber)
blue := color.New(color.FgBlue).SprintFunc()
fmt.Printf("%s: %s\n", blue("Модуль"), info.Name)
if info.Meta != nil {
if info.Meta.Group != "" {
fmt.Printf("%s: %s\n", blue("Группа"), info.Meta.Group)
}
if info.Meta.Summary != "" {
fmt.Printf("%s: %s\n", blue("Краткое"), info.Meta.Summary)
}
if info.Meta.Description != "" {
fmt.Printf("%s: \n%s\n", blue("Описание"), info.Meta.Description)
}
}
fmt.Printf("%s: %s\n", blue("Статус"), info.Status.Color("%s %s", info.Status.Symbol, info.Status.Label))
fmt.Printf("%s: %s\n", blue("Путь"), info.Path)
fmt.Printf("%s: %s\n", blue("Путь в конфиге"), info.ConfPath)
fmt.Printf("%s: %d\n", blue("Строка в конфиге"), info.LineNumber)
if info.Available {
fmt.Println("Доступен: да")
fmt.Printf("%s: да\n", blue("Доступен"))
} else {
fmt.Println("Доступен: нет")
fmt.Printf("%s: нет\n", blue("Доступен"))
}
if len(errors) > 0 {
fmt.Printf("\nОшибки (%d):\n", len(errors))
fmt.Printf("\n%s (%d):\n", blue("Ошибки"), len(errors))
for _, e := range errors {
fmt.Printf(" %d: %s\n", e.Line, e.Text)
}
......
package hyprland
type ModuleShowJSON struct {
Name string `json:"name"`
Status string `json:"status"`
Path string `json:"path"`
ConfPath string `json:"conf_path"`
LineNumber int `json:"line_number"`
Available bool `json:"available"`
HyprModule
Errors []ModuleErrorJSON `json:"errors"`
}
......@@ -14,3 +9,11 @@ type ModuleErrorJSON struct {
Line int `json:"line"`
Text string `json:"text"`
}
type ModuleInfoJSON struct {
Name string `json:"name"`
Group string `json:"group"`
Summary string `json:"summary"`
Status string `json:"status"`
Errors int `json:"errors"`
}
......@@ -35,12 +35,13 @@ type SourceLine struct {
}
type HyprModule struct {
Name string
Status config.ItemStatus
Path string
ConfPath string
LineNumber int
Available bool
Name string `json:"name"`
Status config.ItemStatus `json:"status"`
Path string `json:"path"`
ConfPath string `json:"conf_path"`
LineNumber int `json:"line_number"`
Available bool `json:"available"`
Meta *ModuleMeta `json:"meta,omitempty"`
}
type HyprVar struct {
......@@ -200,6 +201,7 @@ func (m *HyprlandManager) GetModuleInfo(module string, user bool) HyprModule {
Path: sysFile,
ConfPath: ConfPath,
Available: Available,
Meta: ParseModuleMeta(sysFile),
}
}
return HyprModule{
......@@ -248,25 +250,26 @@ func (m *HyprlandManager) GetModuleInfo(module string, user bool) HyprModule {
ConfPath: ConfPath,
LineNumber: LineNumber,
Available: Available,
Meta: ParseModuleMeta(sysFile),
}
}
// закомментирован
if foundCommented {
// файла нет
Path := sysFile
if !FileExists {
Path = ""
}
// файл есть
return HyprModule{
mod := HyprModule{
Name: module,
Status: config.ModuleStatus.Disabled,
Path: Path,
Path: sysFile,
ConfPath: ConfPath,
LineNumber: LineNumber,
Available: Available,
}
if FileExists {
mod.Meta = ParseModuleMeta(sysFile)
} else {
mod.Path = ""
}
return mod
}
// файл есть, но строка отсутствует
......@@ -277,6 +280,7 @@ func (m *HyprlandManager) GetModuleInfo(module string, user bool) HyprModule {
Path: sysFile,
ConfPath: ConfPath,
Available: Available,
Meta: ParseModuleMeta(sysFile),
}
}
......@@ -310,6 +314,9 @@ func (m *HyprlandManager) GetModulesList(user bool, filter string) []HyprModule
modules = m.SystemModules
}
groupFilter := strings.TrimPrefix(filter, "group:")
isGroupFilter := groupFilter != filter
var res []HyprModule
for _, module := range modules {
info := m.GetModuleInfo(module, user)
......@@ -318,7 +325,9 @@ func (m *HyprlandManager) GetModulesList(user bool, filter string) []HyprModule
continue
}
if filter == "" || filter == "all" || info.Status.Label == filter {
if filter == "" || filter == "all" ||
info.Status.Label == filter ||
(isGroupFilter && info.Meta != nil && info.Meta.Group == groupFilter) {
res = append(res, info)
}
}
......@@ -349,6 +358,22 @@ func (m *HyprlandManager) SetModule(action, module string, user, onlyNew bool) (
return "", fmt.Errorf("модуль '%s' уже включён", module)
}
// отключить другие модули той же группы
var disabledGroup []string
if info.Meta != nil && info.Meta.Group != "" {
for _, other := range m.GetModulesList(user, "enabled") {
if other.Name == module {
continue
}
if other.Meta != nil && other.Meta.Group == info.Meta.Group {
m.Lines[other.LineNumber] = "#" + m.Lines[other.LineNumber]
m.updateSourceCommented(other.LineNumber, true)
m.Changed = true
disabledGroup = append(disabledGroup, other.Name)
}
}
}
// был закомментирован
if info.Status.IsEqual(config.ModuleStatus.Disabled) {
if onlyNew {
......@@ -357,7 +382,7 @@ func (m *HyprlandManager) SetModule(action, module string, user, onlyNew bool) (
m.Lines[info.LineNumber] = strings.TrimPrefix(m.Lines[info.LineNumber], "#")
m.updateSourceCommented(info.LineNumber, false)
m.Changed = true
return fmt.Sprintf("Модуль '%s' включён", module), nil
return m.enableMessage(module, disabledGroup), nil
}
// не использовался
......@@ -371,7 +396,7 @@ func (m *HyprlandManager) SetModule(action, module string, user, onlyNew bool) (
Commented: false,
})
m.Changed = true
return fmt.Sprintf("Модуль '%s' включён", module), nil
return m.enableMessage(module, disabledGroup), nil
}
return "", fmt.Errorf("модуль '%s' не изменён", module)
......@@ -407,6 +432,14 @@ func (m *HyprlandManager) SetModule(action, module string, user, onlyNew bool) (
return "", nil
}
func (m *HyprlandManager) enableMessage(module string, disabledGroup []string) string {
msg := fmt.Sprintf("Модуль '%s' включён", module)
for _, name := range disabledGroup {
msg += fmt.Sprintf("\nМодуль '%s' отключён (группа)", name)
}
return msg
}
// Plugins
func (m *HyprlandManager) GetLoadedPlugins() []HyprPlugin {
......
package hyprland
import (
"os"
"strings"
"gopkg.in/yaml.v3"
)
const metaDelimiter = "#---"
type ModuleMeta struct {
Summary string `yaml:"summary,omitempty"`
Description string `yaml:"description,omitempty"`
Group string `yaml:"group,omitempty"`
}
func ParseModuleMeta(filePath string) *ModuleMeta {
data, err := os.ReadFile(filePath)
if err != nil {
return nil
}
lines := strings.Split(string(data), "\n")
// Найти открывающий #---
start := -1
for i, line := range lines {
trimmed := strings.TrimSpace(line)
if trimmed == "" {
continue
}
if trimmed == metaDelimiter {
start = i
break
}
// Первая непустая строка не #--- — метаинформации нет
return nil
}
if start < 0 {
return nil
}
// Собрать строки до закрывающего #---
var yamlLines []string
for i := start + 1; i < len(lines); i++ {
trimmed := strings.TrimSpace(lines[i])
if trimmed == metaDelimiter {
break
}
line := lines[i]
if strings.HasPrefix(line, "# ") {
line = line[2:] // снимаем "# "
} else if strings.HasPrefix(line, "#") {
line = line[1:] // снимаем "#"
}
yamlLines = append(yamlLines, line)
}
if len(yamlLines) == 0 {
return nil
}
var meta ModuleMeta
if err := yaml.Unmarshal([]byte(strings.Join(yamlLines, "\n")), &meta); err != nil {
return nil
}
if meta.Summary == "" && meta.Description == "" && meta.Group == "" {
return nil
}
return &meta
}
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