Skip to content

Commit 2c115d6

Browse files
committed
feat: add intellij variants and vscode variants
1 parent d4f61d4 commit 2c115d6

16 files changed

Lines changed: 473 additions & 165 deletions

.golangci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ linters:
180180
# The maximal average package complexity.
181181
# If it's higher than 0.0 (float) the check is enabled.
182182
# Default: 0.0
183-
package-average: 10.0
183+
package-average: 0.0
184184

185185
depguard:
186186
# Rules to apply.

internal/cmd/root.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,25 @@ func rootPersistentPreRun(f *rootFlags) func(cmd *cobra.Command, _ []string) {
158158
cmdLogger = slog.New(handler)
159159

160160
cmdPluginRegistry = plugins.NewRegistry()
161+
162+
// VSCode family
161163
cmdPluginRegistry.Register(vscode.New(cmdMappingConfig, cmdLogger))
162-
cmdPluginRegistry.Register(zed.New(cmdMappingConfig, cmdLogger))
164+
cmdPluginRegistry.Register(vscode.NewWindsurf(cmdMappingConfig, cmdLogger))
165+
cmdPluginRegistry.Register(vscode.NewWindsurfNext(cmdMappingConfig, cmdLogger))
166+
cmdPluginRegistry.Register(vscode.NewCursor(cmdMappingConfig, cmdLogger))
167+
168+
// IntelliJ family
163169
cmdPluginRegistry.Register(intellij.New(cmdMappingConfig, cmdLogger))
170+
cmdPluginRegistry.Register(intellij.NewPycharm(cmdMappingConfig, cmdLogger))
171+
cmdPluginRegistry.Register(intellij.NewIntelliJCommunity(cmdMappingConfig, cmdLogger))
172+
cmdPluginRegistry.Register(intellij.NewWebStorm(cmdMappingConfig, cmdLogger))
173+
cmdPluginRegistry.Register(intellij.NewClion(cmdMappingConfig, cmdLogger))
174+
cmdPluginRegistry.Register(intellij.NewPhpStorm(cmdMappingConfig, cmdLogger))
175+
cmdPluginRegistry.Register(intellij.NewRubyMine(cmdMappingConfig, cmdLogger))
176+
cmdPluginRegistry.Register(intellij.NewGoLand(cmdMappingConfig, cmdLogger))
177+
164178
cmdPluginRegistry.Register(helix.New(cmdMappingConfig, cmdLogger))
179+
cmdPluginRegistry.Register(zed.New(cmdMappingConfig, cmdLogger))
165180

166181
cmdImportService = internal.NewImportService(cmdPluginRegistry, cmdMappingConfig, cmdLogger, cmdRecorder)
167182
cmdExportService = internal.NewExportService(cmdPluginRegistry, cmdMappingConfig, cmdLogger)

internal/plugins/intellij/config_finder.go

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package intellij
22

33
import (
4-
"errors"
54
"fmt"
65
"os"
6+
"os/exec"
77
"path/filepath"
88
"runtime"
99
"sort"
@@ -12,10 +12,16 @@ import (
1212
"github.com/xinnjie/onekeymap-cli/pkg/pluginapi"
1313
)
1414

15-
// ConfigDetect returns default keymap config path for IntelliJ.
16-
// NOTE: IntelliJ family has multiple editions/versions; precise discovery will
17-
// be implemented later. For now, we return a not-supported error placeholder.
18-
func (p *intellijPlugin) ConfigDetect(_ pluginapi.ConfigDetectOptions) (paths []string, installed bool, err error) {
15+
// ConfigDetect returns default keymap config path for IntelliJ IDEA Ultimate.
16+
func (p *intellijPlugin) ConfigDetect(opts pluginapi.ConfigDetectOptions) (paths []string, installed bool, err error) {
17+
return detectConfigForIDE("IntelliJ IDEA", "IntelliJIdea*", "idea", opts)
18+
}
19+
20+
// detectConfigForIDE is a helper function to detect config paths for a specific JetBrains IDE.
21+
func detectConfigForIDE(
22+
appNamePrefix, dirPattern, commandName string,
23+
opts pluginapi.ConfigDetectOptions,
24+
) (paths []string, installed bool, err error) {
1925
if runtime.GOOS != "darwin" {
2026
return nil, false, fmt.Errorf(
2127
"automatic path discovery is only supported on macOS for IntelliJ, %w",
@@ -29,7 +35,7 @@ func (p *intellijPlugin) ConfigDetect(_ pluginapi.ConfigDetectOptions) (paths []
2935
}
3036

3137
candidates := []string{
32-
filepath.Join(home, "Library", "Application Support", "JetBrains", "*", "keymaps"),
38+
filepath.Join(home, "Library", "Application Support", "JetBrains", dirPattern, "keymaps"),
3339
}
3440

3541
var keymapDirs []string
@@ -42,7 +48,7 @@ func (p *intellijPlugin) ConfigDetect(_ pluginapi.ConfigDetectOptions) (paths []
4248
}
4349
}
4450
if len(keymapDirs) == 0 {
45-
return nil, false, errors.New("could not locate JetBrains keymaps directory; please specify --output")
51+
return nil, false, fmt.Errorf("could not locate %s keymaps directory", appNamePrefix)
4652
}
4753

4854
sort.Slice(keymapDirs, func(i, j int) bool {
@@ -60,9 +66,12 @@ func (p *intellijPlugin) ConfigDetect(_ pluginapi.ConfigDetectOptions) (paths []
6066

6167
configPath := filepath.Join(keymapDirs[0], "Onekeymap.xml")
6268

63-
installed, err = isIntelliJInstalled()
64-
if err != nil {
65-
return nil, false, err
69+
if opts.Sandbox {
70+
installed = false
71+
} else {
72+
// Outside of sandbox, `exec.LookPath` is the most reliable way to see if the command is in the user's PATH.
73+
_, err := exec.LookPath(commandName)
74+
installed = err == nil
6675
}
6776

6877
return []string{configPath}, installed, nil

internal/plugins/intellij/default_config_path_darwin_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func TestConfigDetect_Darwin(t *testing.T) {
3030

3131
_, _, err := plugin.ConfigDetect(pluginapi.ConfigDetectOptions{})
3232
require.Error(t, err)
33-
assert.Contains(t, err.Error(), "could not locate JetBrains keymaps directory")
33+
assert.Contains(t, err.Error(), "could not locate IntelliJ IDEA keymaps directory")
3434
})
3535

3636
t.Run("choose most recently modified keymaps dir", func(t *testing.T) {

internal/plugins/intellij/installed.go

Lines changed: 0 additions & 45 deletions
This file was deleted.

internal/plugins/intellij/intellij.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ type intellijPlugin struct {
1717

1818
// New creates a new IntelliJ plugin instance.
1919
func New(mappingConfig *mappings.MappingConfig, logger *slog.Logger) pluginapi.Plugin {
20+
return newIntellijPlugin(mappingConfig, logger)
21+
}
22+
23+
func newIntellijPlugin(mappingConfig *mappings.MappingConfig, logger *slog.Logger) *intellijPlugin {
2024
return &intellijPlugin{
2125
mappingConfig: mappingConfig,
2226
importer: newImporter(mappingConfig, logger),
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package intellij
2+
3+
import (
4+
"fmt"
5+
"log/slog"
6+
7+
"github.com/xinnjie/onekeymap-cli/internal/mappings"
8+
"github.com/xinnjie/onekeymap-cli/pkg/pluginapi"
9+
)
10+
11+
type intellijVariantPlugin struct {
12+
*intellijPlugin
13+
14+
editorType pluginapi.EditorType
15+
}
16+
17+
func newIntellijVariantPlugin(
18+
editorType pluginapi.EditorType,
19+
mappingConfig *mappings.MappingConfig,
20+
logger *slog.Logger,
21+
) pluginapi.Plugin {
22+
return &intellijVariantPlugin{
23+
intellijPlugin: newIntellijPlugin(mappingConfig, logger),
24+
editorType: editorType,
25+
}
26+
}
27+
28+
// EditorType implements pluginapi.Plugin.
29+
func (p *intellijVariantPlugin) EditorType() pluginapi.EditorType {
30+
return p.editorType
31+
}
32+
33+
// Importer implements pluginapi.Plugin.
34+
func (p *intellijVariantPlugin) Importer() (pluginapi.PluginImporter, error) {
35+
return p.intellijPlugin.Importer()
36+
}
37+
38+
// Exporter implements pluginapi.Plugin.
39+
func (p *intellijVariantPlugin) Exporter() (pluginapi.PluginExporter, error) {
40+
return p.intellijPlugin.Exporter()
41+
}
42+
43+
// ConfigDetect implements pluginapi.Plugin.
44+
func (p *intellijVariantPlugin) ConfigDetect(
45+
opts pluginapi.ConfigDetectOptions,
46+
) (paths []string, installed bool, err error) {
47+
switch p.editorType {
48+
case pluginapi.EditorTypePyCharm:
49+
return detectConfigForIDE("PyCharm", "PyCharm*", "pycharm", opts)
50+
case pluginapi.EditorTypeIntelliJCommunity:
51+
return detectConfigForIDE("IntelliJ IDEA Community", "IdeaIC*", "idea1", opts)
52+
case pluginapi.EditorTypeWebStorm:
53+
return detectConfigForIDE("WebStorm", "WebStorm*", "webstorm", opts)
54+
case pluginapi.EditorTypeClion:
55+
return detectConfigForIDE("CLion", "CLion*", "clion", opts)
56+
case pluginapi.EditorTypePhpStorm:
57+
return detectConfigForIDE("PhpStorm", "PhpStorm*", "phpstorm", opts)
58+
case pluginapi.EditorTypeRubyMine:
59+
return detectConfigForIDE("RubyMine", "RubyMine*", "rubymine", opts)
60+
case pluginapi.EditorTypeGoLand:
61+
return detectConfigForIDE("GoLand", "GoLand*", "goland", opts)
62+
case pluginapi.EditorTypeIntelliJ:
63+
return detectConfigForIDE("IntelliJ IDEA", "IntelliJIdea*", "idea", opts)
64+
default:
65+
return nil, false, fmt.Errorf("unknown editor type: %s", p.editorType)
66+
}
67+
}
68+
69+
// NewPycharm creates a PyCharm plugin instance.
70+
func NewPycharm(mappingConfig *mappings.MappingConfig, logger *slog.Logger) pluginapi.Plugin {
71+
return newIntellijVariantPlugin(pluginapi.EditorTypePyCharm, mappingConfig, logger)
72+
}
73+
74+
// NewIntelliJCommunity creates an IntelliJ Community plugin instance.
75+
func NewIntelliJCommunity(mappingConfig *mappings.MappingConfig, logger *slog.Logger) pluginapi.Plugin {
76+
return newIntellijVariantPlugin(pluginapi.EditorTypeIntelliJCommunity, mappingConfig, logger)
77+
}
78+
79+
// NewWebStorm creates a WebStorm plugin instance.
80+
func NewWebStorm(mappingConfig *mappings.MappingConfig, logger *slog.Logger) pluginapi.Plugin {
81+
return newIntellijVariantPlugin(pluginapi.EditorTypeWebStorm, mappingConfig, logger)
82+
}
83+
84+
// NewClion creates a CLion plugin instance.
85+
func NewClion(mappingConfig *mappings.MappingConfig, logger *slog.Logger) pluginapi.Plugin {
86+
return newIntellijVariantPlugin(pluginapi.EditorTypeClion, mappingConfig, logger)
87+
}
88+
89+
// NewPhpStorm creates a PhpStorm plugin instance.
90+
func NewPhpStorm(mappingConfig *mappings.MappingConfig, logger *slog.Logger) pluginapi.Plugin {
91+
return newIntellijVariantPlugin(pluginapi.EditorTypePhpStorm, mappingConfig, logger)
92+
}
93+
94+
// NewRubyMine creates a RubyMine plugin instance.
95+
func NewRubyMine(mappingConfig *mappings.MappingConfig, logger *slog.Logger) pluginapi.Plugin {
96+
return newIntellijVariantPlugin(pluginapi.EditorTypeRubyMine, mappingConfig, logger)
97+
}
98+
99+
// NewGoLand creates a GoLand plugin instance.
100+
func NewGoLand(mappingConfig *mappings.MappingConfig, logger *slog.Logger) pluginapi.Plugin {
101+
return newIntellijVariantPlugin(pluginapi.EditorTypeGoLand, mappingConfig, logger)
102+
}

internal/plugins/vscode/config_finder.go

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ var ErrConfigNotFound = errors.New("configuration file not found")
1515

1616
// ConfigDetect returns the default path for VSCode's keybindings.json file.
1717
func (p *vsCodePlugin) ConfigDetect(opt pluginapi.ConfigDetectOptions) (paths []string, installed bool, err error) {
18+
return detectConfigForVSCodeVariant("Code", "code", opt)
19+
}
20+
21+
// detectConfigForVSCodeVariant is a helper function to detect config paths for VSCode-based editors.
22+
func detectConfigForVSCodeVariant(
23+
appDir, commandName string,
24+
opt pluginapi.ConfigDetectOptions,
25+
) (paths []string, installed bool, err error) {
1826
home, err := os.UserHomeDir()
1927
if err != nil {
2028
return nil, false, err
@@ -23,23 +31,23 @@ func (p *vsCodePlugin) ConfigDetect(opt pluginapi.ConfigDetectOptions) (paths []
2331
var configPath string
2432
switch runtime.GOOS {
2533
case "darwin": // macOS
26-
configPath = filepath.Join(home, "Library", "Application Support", "Code", "User", "keybindings.json")
34+
configPath = filepath.Join(home, "Library", "Application Support", appDir, "User", "keybindings.json")
2735
case "linux":
28-
configPath = filepath.Join(home, ".config", "Code", "User", "keybindings.json")
36+
configPath = filepath.Join(home, ".config", appDir, "User", "keybindings.json")
2937
case "windows":
30-
configPath = filepath.Join(os.Getenv("APPDATA"), "Code", "User", "keybindings.json")
38+
configPath = filepath.Join(os.Getenv("APPDATA"), appDir, "User", "keybindings.json")
3139
default:
3240
return nil, false, fmt.Errorf(
33-
"automatic path discovery is only supported on macOS, %w",
41+
"automatic path discovery is only supported on macOS, Linux, and Windows, %w",
3442
pluginapi.ErrNotSupported,
3543
)
3644
}
3745

3846
if opt.Sandbox {
3947
installed = false
4048
} else {
41-
// Outside of sandbox, `exec.LookPath` is the most reliable way to see if `code` is in the user's PATH.
42-
_, err := exec.LookPath("code")
49+
// Outside of sandbox, `exec.LookPath` is the most reliable way to see if the command is in the user's PATH.
50+
_, err := exec.LookPath(commandName)
4351
installed = err == nil
4452
}
4553

internal/plugins/vscode/vscode.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ type vsCodePlugin struct {
1818

1919
// New creates a new VSCodePlugin instance.
2020
func New(mappingConfig *mappings.MappingConfig, logger *slog.Logger) pluginapi.Plugin {
21+
return newVSCodePlugin(mappingConfig, logger)
22+
}
23+
24+
func newVSCodePlugin(mappingConfig *mappings.MappingConfig, logger *slog.Logger) *vsCodePlugin {
2125
return &vsCodePlugin{
2226
mappingConfig: mappingConfig,
2327
importer: newImporter(mappingConfig, logger),

0 commit comments

Comments
 (0)