578 lines
15 KiB
Go
578 lines
15 KiB
Go
package services
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
"voidraft/internal/models"
|
|
|
|
"github.com/fsnotify/fsnotify"
|
|
"github.com/spf13/viper"
|
|
"github.com/wailsapp/wails/v3/pkg/services/log"
|
|
)
|
|
|
|
// KeyBindingService 快捷键管理服务
|
|
type KeyBindingService struct {
|
|
viper *viper.Viper // Viper 实例
|
|
logger *log.LoggerService // 日志服务
|
|
mu sync.RWMutex // 读写锁
|
|
}
|
|
|
|
// KeyBindingError 快捷键错误
|
|
type KeyBindingError struct {
|
|
Operation string // 操作名称
|
|
Command string // 快捷键Command
|
|
Err error // 原始错误
|
|
}
|
|
|
|
// Error 实现error接口
|
|
func (e *KeyBindingError) Error() string {
|
|
if e.Command != "" {
|
|
return fmt.Sprintf("keybinding error during %s for command %s: %v", e.Operation, e.Command, e.Err)
|
|
}
|
|
return fmt.Sprintf("keybinding error during %s: %v", e.Operation, e.Err)
|
|
}
|
|
|
|
// Unwrap 获取原始错误
|
|
func (e *KeyBindingError) Unwrap() error {
|
|
return e.Err
|
|
}
|
|
|
|
// Is 实现错误匹配
|
|
func (e *KeyBindingError) Is(target error) bool {
|
|
var keyBindingError *KeyBindingError
|
|
ok := errors.As(target, &keyBindingError)
|
|
return ok
|
|
}
|
|
|
|
// NewKeyBindingService 创建新的快捷键服务实例
|
|
func NewKeyBindingService(logger *log.LoggerService) *KeyBindingService {
|
|
// 设置日志服务
|
|
if logger == nil {
|
|
logger = log.New()
|
|
}
|
|
|
|
// 获取当前工作目录
|
|
currentDir, err := os.Getwd()
|
|
if err != nil {
|
|
currentDir = "."
|
|
}
|
|
|
|
// 固定配置路径和文件名
|
|
configPath := filepath.Join(currentDir, "config")
|
|
configName := "keybindings"
|
|
|
|
// 创建 Viper 实例
|
|
v := viper.New()
|
|
|
|
// 配置 Viper
|
|
v.SetConfigName(configName)
|
|
v.SetConfigType("json")
|
|
v.AddConfigPath(configPath)
|
|
|
|
// 设置环境变量前缀
|
|
v.SetEnvPrefix("VOIDRAFT_KEYBINDING")
|
|
v.AutomaticEnv()
|
|
|
|
// 设置默认值
|
|
setKeyBindingDefaults(v)
|
|
|
|
// 构造快捷键服务实例
|
|
service := &KeyBindingService{
|
|
viper: v,
|
|
logger: logger,
|
|
}
|
|
|
|
// 初始化配置
|
|
if err := service.initConfig(); err != nil {
|
|
service.logger.Error("KeyBinding: Failed to initialize keybinding config", "error", err)
|
|
}
|
|
|
|
// 启动配置文件监听
|
|
service.startWatching()
|
|
|
|
return service
|
|
}
|
|
|
|
// setKeyBindingDefaults 设置默认快捷键配置值
|
|
func setKeyBindingDefaults(v *viper.Viper) {
|
|
defaultConfig := models.NewDefaultKeyBindingConfig()
|
|
|
|
// 快捷键列表默认值
|
|
v.SetDefault("keyBindings", defaultConfig.KeyBindings)
|
|
|
|
// 元数据默认值
|
|
v.SetDefault("metadata.lastUpdated", defaultConfig.Metadata.LastUpdated)
|
|
}
|
|
|
|
// initConfig 初始化配置
|
|
func (kbs *KeyBindingService) initConfig() error {
|
|
kbs.mu.Lock()
|
|
defer kbs.mu.Unlock()
|
|
|
|
// 尝试读取配置文件
|
|
if err := kbs.viper.ReadInConfig(); err != nil {
|
|
var configFileNotFoundError viper.ConfigFileNotFoundError
|
|
if errors.As(err, &configFileNotFoundError) {
|
|
// 配置文件不存在,创建默认配置文件
|
|
kbs.logger.Info("KeyBinding: Config file not found, creating default keybinding config")
|
|
return kbs.createDefaultConfig()
|
|
}
|
|
// 配置文件存在但读取失败
|
|
return &KeyBindingError{Operation: "read_keybinding_config", Err: err}
|
|
}
|
|
|
|
kbs.logger.Info("KeyBinding: Successfully loaded keybinding config file", "file", kbs.viper.ConfigFileUsed())
|
|
return nil
|
|
}
|
|
|
|
// createDefaultConfig 创建默认配置文件
|
|
func (kbs *KeyBindingService) createDefaultConfig() error {
|
|
// 获取配置目录路径
|
|
currentDir, err := os.Getwd()
|
|
if err != nil {
|
|
currentDir = "."
|
|
}
|
|
configDir := filepath.Join(currentDir, "config")
|
|
configPath := filepath.Join(configDir, "keybindings.json")
|
|
|
|
// 确保配置目录存在
|
|
if err := os.MkdirAll(configDir, 0755); err != nil {
|
|
return &KeyBindingError{Operation: "create_keybinding_config_dir", Err: err}
|
|
}
|
|
|
|
// 获取默认配置
|
|
defaultConfig := models.NewDefaultKeyBindingConfig()
|
|
configBytes, err := json.MarshalIndent(defaultConfig, "", " ")
|
|
if err != nil {
|
|
return &KeyBindingError{Operation: "marshal_default_keybinding_config", Err: err}
|
|
}
|
|
|
|
// 写入配置文件
|
|
if err := os.WriteFile(configPath, configBytes, 0644); err != nil {
|
|
return &KeyBindingError{Operation: "write_default_keybinding_config", Err: err}
|
|
}
|
|
|
|
// 重新读取配置文件到viper
|
|
if err := kbs.viper.ReadInConfig(); err != nil {
|
|
return &KeyBindingError{Operation: "read_created_keybinding_config", Err: err}
|
|
}
|
|
|
|
kbs.logger.Info("KeyBinding: Created default keybinding config file", "path", configPath)
|
|
return nil
|
|
}
|
|
|
|
// startWatching 启动配置文件监听
|
|
func (kbs *KeyBindingService) startWatching() {
|
|
// 设置配置变化回调
|
|
kbs.viper.OnConfigChange(func(e fsnotify.Event) {
|
|
kbs.logger.Info("KeyBinding: Config file changed", "file", e.Name, "operation", e.Op.String())
|
|
})
|
|
|
|
// 启动配置文件监听
|
|
kbs.viper.WatchConfig()
|
|
kbs.logger.Info("KeyBinding: Started watching keybinding config file for changes")
|
|
}
|
|
|
|
// GetKeyBindingConfig 获取完整快捷键配置
|
|
func (kbs *KeyBindingService) GetKeyBindingConfig() (*models.KeyBindingConfig, error) {
|
|
kbs.mu.RLock()
|
|
defer kbs.mu.RUnlock()
|
|
|
|
var config models.KeyBindingConfig
|
|
if err := kbs.viper.Unmarshal(&config); err != nil {
|
|
return nil, &KeyBindingError{Operation: "unmarshal_keybinding_config", Err: err}
|
|
}
|
|
|
|
return &config, nil
|
|
}
|
|
|
|
// GetAllKeyBindings 获取所有快捷键配置
|
|
func (kbs *KeyBindingService) GetAllKeyBindings() ([]models.KeyBinding, error) {
|
|
kbs.mu.RLock()
|
|
defer kbs.mu.RUnlock()
|
|
|
|
config, err := kbs.GetKeyBindingConfig()
|
|
if err != nil {
|
|
return nil, &KeyBindingError{Operation: "get_all_keybindings", Err: err}
|
|
}
|
|
|
|
return config.KeyBindings, nil
|
|
}
|
|
|
|
// GetKeyBindingsByCategory 根据分类获取快捷键
|
|
func (kbs *KeyBindingService) GetKeyBindingsByCategory(category models.KeyBindingCategory) ([]models.KeyBinding, error) {
|
|
kbs.mu.RLock()
|
|
defer kbs.mu.RUnlock()
|
|
|
|
allKeyBindings, err := kbs.GetAllKeyBindings()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var result []models.KeyBinding
|
|
for _, kb := range allKeyBindings {
|
|
if kb.Category == category {
|
|
result = append(result, kb)
|
|
}
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// GetKeyBindingByCommand 根据命令获取快捷键
|
|
func (kbs *KeyBindingService) GetKeyBindingByCommand(command models.KeyBindingCommand) (*models.KeyBinding, error) {
|
|
kbs.mu.RLock()
|
|
defer kbs.mu.RUnlock()
|
|
|
|
allKeyBindings, err := kbs.GetAllKeyBindings()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, kb := range allKeyBindings {
|
|
if kb.Command == command && kb.Enabled {
|
|
return &kb, nil
|
|
}
|
|
}
|
|
|
|
return nil, &KeyBindingError{
|
|
Operation: "get_keybinding_by_command",
|
|
Err: fmt.Errorf("keybinding for command %s not found", command),
|
|
}
|
|
}
|
|
|
|
// UpdateKeyBinding 更新快捷键
|
|
func (kbs *KeyBindingService) UpdateKeyBinding(command models.KeyBindingCommand, newKey string) error {
|
|
kbs.mu.Lock()
|
|
defer kbs.mu.Unlock()
|
|
|
|
// 验证新的快捷键格式
|
|
if err := kbs.validateKeyFormat(newKey); err != nil {
|
|
return &KeyBindingError{
|
|
Operation: "update_keybinding",
|
|
Command: string(command),
|
|
Err: fmt.Errorf("invalid key format: %v", err),
|
|
}
|
|
}
|
|
|
|
// 检查快捷键冲突
|
|
if err := kbs.checkKeyConflict(command, newKey); err != nil {
|
|
return &KeyBindingError{
|
|
Operation: "update_keybinding",
|
|
Command: string(command),
|
|
Err: fmt.Errorf("key conflict: %v", err),
|
|
}
|
|
}
|
|
|
|
// 获取当前配置
|
|
config, err := kbs.GetKeyBindingConfig()
|
|
if err != nil {
|
|
return &KeyBindingError{Operation: "update_keybinding", Command: string(command), Err: err}
|
|
}
|
|
|
|
// 查找并更新快捷键
|
|
found := false
|
|
for i, kb := range config.KeyBindings {
|
|
if kb.Command == command {
|
|
config.KeyBindings[i].Key = newKey
|
|
config.KeyBindings[i].IsDefault = false // 标记为非默认
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
return &KeyBindingError{
|
|
Operation: "update_keybinding",
|
|
Command: string(command),
|
|
Err: errors.New("keybinding not found"),
|
|
}
|
|
}
|
|
|
|
// 更新时间戳
|
|
config.Metadata.LastUpdated = time.Now().Format(time.RFC3339)
|
|
|
|
// 保存配置
|
|
if err := kbs.saveConfig(config); err != nil {
|
|
return &KeyBindingError{Operation: "update_keybinding", Command: string(command), Err: err}
|
|
}
|
|
|
|
kbs.logger.Info("KeyBinding: Updated keybinding", "command", command, "newKey", newKey)
|
|
return nil
|
|
}
|
|
|
|
// EnableKeyBinding 启用快捷键
|
|
func (kbs *KeyBindingService) EnableKeyBinding(command models.KeyBindingCommand) error {
|
|
return kbs.setKeyBindingEnabled(command, true)
|
|
}
|
|
|
|
// DisableKeyBinding 禁用快捷键
|
|
func (kbs *KeyBindingService) DisableKeyBinding(command models.KeyBindingCommand) error {
|
|
return kbs.setKeyBindingEnabled(command, false)
|
|
}
|
|
|
|
// setKeyBindingEnabled 设置快捷键启用状态
|
|
func (kbs *KeyBindingService) setKeyBindingEnabled(command models.KeyBindingCommand, enabled bool) error {
|
|
kbs.mu.Lock()
|
|
defer kbs.mu.Unlock()
|
|
|
|
// 获取当前配置
|
|
config, err := kbs.GetKeyBindingConfig()
|
|
if err != nil {
|
|
return &KeyBindingError{Operation: "set_keybinding_enabled", Command: string(command), Err: err}
|
|
}
|
|
|
|
// 查找并更新快捷键
|
|
found := false
|
|
for i, kb := range config.KeyBindings {
|
|
if kb.Command == command {
|
|
config.KeyBindings[i].Enabled = enabled
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
return &KeyBindingError{
|
|
Operation: "set_keybinding_enabled",
|
|
Command: string(command),
|
|
Err: errors.New("keybinding not found"),
|
|
}
|
|
}
|
|
|
|
// 更新时间戳
|
|
config.Metadata.LastUpdated = time.Now().Format(time.RFC3339)
|
|
|
|
// 保存配置
|
|
if err := kbs.saveConfig(config); err != nil {
|
|
return &KeyBindingError{Operation: "set_keybinding_enabled", Command: string(command), Err: err}
|
|
}
|
|
|
|
status := "enabled"
|
|
if !enabled {
|
|
status = "disabled"
|
|
}
|
|
kbs.logger.Info("KeyBinding: "+status+" keybinding", "command", command)
|
|
return nil
|
|
}
|
|
|
|
// ResetKeyBinding 重置快捷键到默认值
|
|
func (kbs *KeyBindingService) ResetKeyBinding(command models.KeyBindingCommand) error {
|
|
kbs.mu.Lock()
|
|
defer kbs.mu.Unlock()
|
|
|
|
// 获取默认配置
|
|
defaultKeyBindings := models.NewDefaultKeyBindings()
|
|
var defaultKeyBinding *models.KeyBinding
|
|
for _, kb := range defaultKeyBindings {
|
|
if kb.Command == command {
|
|
defaultKeyBinding = &kb
|
|
break
|
|
}
|
|
}
|
|
|
|
if defaultKeyBinding == nil {
|
|
return &KeyBindingError{
|
|
Operation: "reset_keybinding",
|
|
Command: string(command),
|
|
Err: errors.New("default keybinding not found"),
|
|
}
|
|
}
|
|
|
|
// 获取当前配置
|
|
config, err := kbs.GetKeyBindingConfig()
|
|
if err != nil {
|
|
return &KeyBindingError{Operation: "reset_keybinding", Command: string(command), Err: err}
|
|
}
|
|
|
|
// 查找并重置快捷键
|
|
found := false
|
|
for i, kb := range config.KeyBindings {
|
|
if kb.Command == command {
|
|
config.KeyBindings[i].Key = defaultKeyBinding.Key
|
|
config.KeyBindings[i].Enabled = defaultKeyBinding.Enabled
|
|
config.KeyBindings[i].IsDefault = true
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
return &KeyBindingError{
|
|
Operation: "reset_keybinding",
|
|
Command: string(command),
|
|
Err: errors.New("keybinding not found"),
|
|
}
|
|
}
|
|
|
|
// 更新时间戳
|
|
config.Metadata.LastUpdated = time.Now().Format(time.RFC3339)
|
|
|
|
// 保存配置
|
|
if err := kbs.saveConfig(config); err != nil {
|
|
return &KeyBindingError{Operation: "reset_keybinding", Command: string(command), Err: err}
|
|
}
|
|
|
|
kbs.logger.Info("KeyBinding: Reset keybinding to default", "command", command, "key", defaultKeyBinding.Key)
|
|
return nil
|
|
}
|
|
|
|
// ResetAllKeyBindings 重置所有快捷键到默认值
|
|
func (kbs *KeyBindingService) ResetAllKeyBindings() error {
|
|
kbs.mu.Lock()
|
|
defer kbs.mu.Unlock()
|
|
|
|
// 获取默认配置
|
|
defaultConfig := models.NewDefaultKeyBindingConfig()
|
|
|
|
// 保存配置
|
|
if err := kbs.saveConfig(defaultConfig); err != nil {
|
|
return &KeyBindingError{Operation: "reset_all_keybindings", Err: err}
|
|
}
|
|
|
|
kbs.logger.Info("KeyBinding: Reset all keybindings to default")
|
|
return nil
|
|
}
|
|
|
|
// saveConfig 保存配置到文件
|
|
func (kbs *KeyBindingService) saveConfig(config *models.KeyBindingConfig) error {
|
|
// 设置快捷键列表到viper
|
|
kbs.viper.Set("keyBindings", config.KeyBindings)
|
|
kbs.viper.Set("metadata.lastUpdated", config.Metadata.LastUpdated)
|
|
|
|
// 写入配置文件
|
|
if err := kbs.viper.WriteConfig(); err != nil {
|
|
return fmt.Errorf("failed to write keybinding config: %v", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// validateKeyFormat 验证快捷键格式
|
|
func (kbs *KeyBindingService) validateKeyFormat(key string) error {
|
|
if key == "" {
|
|
return errors.New("key cannot be empty")
|
|
}
|
|
|
|
// 基本格式验证
|
|
// 支持的修饰符: Mod, Ctrl, Shift, Alt, Win
|
|
// 支持的组合: Mod-f, Ctrl-Shift-p, Alt-ArrowUp 等
|
|
validModifiers := []string{"Mod", "Ctrl", "Shift", "Alt", "Win"}
|
|
parts := strings.Split(key, "-")
|
|
|
|
if len(parts) == 0 {
|
|
return errors.New("invalid key format")
|
|
}
|
|
|
|
// 检查修饰符
|
|
for i := 0; i < len(parts)-1; i++ {
|
|
modifier := parts[i]
|
|
valid := false
|
|
for _, validMod := range validModifiers {
|
|
if modifier == validMod {
|
|
valid = true
|
|
break
|
|
}
|
|
}
|
|
if !valid {
|
|
return fmt.Errorf("invalid modifier: %s", modifier)
|
|
}
|
|
}
|
|
|
|
// 最后一部分应该是主键
|
|
mainKey := parts[len(parts)-1]
|
|
if mainKey == "" {
|
|
return errors.New("main key cannot be empty")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// checkKeyConflict 检查快捷键冲突
|
|
func (kbs *KeyBindingService) checkKeyConflict(excludeCommand models.KeyBindingCommand, key string) error {
|
|
allKeyBindings, err := kbs.GetAllKeyBindings()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, kb := range allKeyBindings {
|
|
if kb.Command != excludeCommand && kb.Key == key && kb.Enabled {
|
|
return fmt.Errorf("key %s is already used by %s", key, kb.Command)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetKeyBindingCategories 获取所有快捷键分类
|
|
func (kbs *KeyBindingService) GetKeyBindingCategories() []models.KeyBindingCategory {
|
|
return []models.KeyBindingCategory{
|
|
models.CategorySearch,
|
|
models.CategoryEdit,
|
|
models.CategoryCodeBlock,
|
|
models.CategoryHistory,
|
|
models.CategoryFold,
|
|
}
|
|
}
|
|
|
|
// ExportKeyBindings 导出快捷键配置
|
|
func (kbs *KeyBindingService) ExportKeyBindings() ([]models.KeyBinding, error) {
|
|
kbs.mu.RLock()
|
|
defer kbs.mu.RUnlock()
|
|
|
|
return kbs.GetAllKeyBindings()
|
|
}
|
|
|
|
// ImportKeyBindings 导入快捷键配置
|
|
func (kbs *KeyBindingService) ImportKeyBindings(keyBindings []models.KeyBinding) error {
|
|
kbs.mu.Lock()
|
|
defer kbs.mu.Unlock()
|
|
|
|
// 验证导入的快捷键
|
|
for _, kb := range keyBindings {
|
|
if err := kbs.validateKeyFormat(kb.Key); err != nil {
|
|
return &KeyBindingError{
|
|
Operation: "import_keybindings",
|
|
Command: string(kb.Command),
|
|
Err: fmt.Errorf("invalid key format for %s: %v", kb.Command, err),
|
|
}
|
|
}
|
|
}
|
|
|
|
// 检查重复的快捷键
|
|
keyMap := make(map[string]models.KeyBindingCommand)
|
|
for _, kb := range keyBindings {
|
|
if kb.Enabled {
|
|
if existingCommand, exists := keyMap[kb.Key]; exists {
|
|
return &KeyBindingError{
|
|
Operation: "import_keybindings",
|
|
Err: fmt.Errorf("duplicate key %s found in %s and %s", kb.Key, existingCommand, kb.Command),
|
|
}
|
|
}
|
|
keyMap[kb.Key] = kb.Command
|
|
}
|
|
}
|
|
|
|
// 创建新的配置
|
|
config := &models.KeyBindingConfig{
|
|
KeyBindings: keyBindings,
|
|
Metadata: models.KeyBindingMetadata{
|
|
LastUpdated: time.Now().Format(time.RFC3339),
|
|
},
|
|
}
|
|
|
|
// 保存配置
|
|
if err := kbs.saveConfig(config); err != nil {
|
|
return &KeyBindingError{Operation: "import_keybindings", Err: err}
|
|
}
|
|
|
|
kbs.logger.Info("KeyBinding: Imported keybindings", "count", len(keyBindings))
|
|
return nil
|
|
}
|