Skip to content

Commit

Permalink
Refactor plugin host
Browse files Browse the repository at this point in the history
  • Loading branch information
qianlifeng committed Dec 22, 2024
1 parent 6feb7cf commit ccfdbc5
Show file tree
Hide file tree
Showing 52 changed files with 1,434 additions and 1,253 deletions.
18 changes: 18 additions & 0 deletions .cursorrules
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
项目基本结构如下:

wox.core Go实现的Wox后端,通过websocket与http与wox.ui.flutter通信
wox.core/setting wox.core的设置相关定义
wox.core/plugin wox.core的API定义与实现
wox.plugin.python python插件需要引用的类库
wox.plugin.host.python python插件的host,通过websocket与wox.core通信,负责加载python插件
wox.plugin.nodejs nodejs插件需要引用的类库
wox.plugin.host.nodejs nodejs插件的host,通过websocket与wox.core通信,负责加载nodejs插件
wox.ui.flutter flutter实现的Wox前端,通过websocket与wox.core通信

所有的插件类库(wox.plugin.python,wox.plugin.nodejs)的定义都必须对齐wox.core的定义, 例如:
wox.core/plugin/api.go 定义了API接口, 所有的插件类库都必须定义这些接口, 且名称,类型,参数,返回值必须完全一致


本项目中所有的python项目都使用如下类库:
* orjson进行数据序列化, 请参考wox.plugin.python/src/wox_plugin/models/query.py
* dataclass进行数据模型定义, 请参考wox.plugin.python/src/wox_plugin/models/query.py
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ __debug_bin*
wox.core/log/
wox.plugin.python/dist/
wox.plugin.python/wox_plugin.egg-info/
.venv/
.venv/
.ruff_cache/
.mypy_cache/
4 changes: 2 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/Wox",
"program": "${workspaceFolder}/wox.core",
"env": {
"CGO_ENABLED": "1"
}
},
{
"name": "Run Flutter",
"cwd": "Wox.UI.Flutter/wox",
"cwd": "${workspaceFolder}/wox.ui.flutter/wox",
"request": "launch",
"type": "dart",
// "flutterMode": "release"
Expand Down
34 changes: 10 additions & 24 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,32 +1,18 @@
{
"dart.lineLength": 200,
"python.languageServer": "Pylance",
"python.analysis.typeCheckingMode": "strict",
"python.analysis.diagnosticMode": "workspace",
"python.analysis.inlayHints.functionReturnTypes": true,
"python.analysis.inlayHints.variableTypes": true,
"python.analysis.autoFormatStrings": true,
"python.analysis.autoImportCompletions": true,
"python.analysis.diagnosticSeverityOverrides": {
"reportUnknownMemberType": "error",
"reportUnknownVariableType": "error",
"reportUnknownArgumentType": "error",
"reportUnknownParameterType": "error",
"reportMissingTypeStubs": "error",
"reportUnknownLambdaType": "error",
"reportOptionalCall": "error",
"reportOptionalMemberAccess": "error",
"reportOptionalSubscript": "error",
"reportOptionalIterable": "error",
"reportOptionalContextManager": "error",
"reportOptionalOperand": "error"
},
"files.exclude": {
"wox.plugin.host.nodejs": true,
"wox.plugin.host.python": true,
"wox.plugin.nodejs": true,
"wox.plugin.python": true,
"wox.ui.flutter": true,
"wox.core": true
}
}
},
"[python]": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll": "explicit",
"source.organizeImports": "explicit"
},
"editor.defaultFormatter": "charliermarsh.ruff"
},
}
7 changes: 4 additions & 3 deletions Wox.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,17 @@
}
],
"settings": {
"python.languageServer": "Pylance"
"python.languageServer": "Default",
"makefile.configureOnOpen": false
},
"extensions": {
"recommendations": [
// for go
"golang.go",
// for python
"ms-python.python",
"ms-python.vscode-pylance",
"ms-python.black-formatter",
"charliermarsh.ruff",
"ms-python.mypy-type-checker",
// for flutter
"dart-code.flutter",
// for js
Expand Down
1 change: 0 additions & 1 deletion wox.core/i18n/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ func (m *Manager) TranslatePlugin(ctx context.Context, key string, pluginDirecto

jsonPath := path.Join(pluginDirectory, "lang", fmt.Sprintf("%s.json", m.currentLangCode))
if _, err := os.Stat(jsonPath); os.IsNotExist(err) {
util.GetLogger().Error(ctx, fmt.Sprintf("lang file not found: %s", jsonPath))
return key
}

Expand Down
5 changes: 2 additions & 3 deletions wox.core/plugin/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"wox/ai"
"wox/i18n"
"wox/setting"
"wox/setting/definition"
"wox/share"
"wox/util"

Expand All @@ -36,7 +35,7 @@ type API interface {
GetSetting(ctx context.Context, key string) string
SaveSetting(ctx context.Context, key string, value string, isPlatformSpecific bool)
OnSettingChanged(ctx context.Context, callback func(key string, value string))
OnGetDynamicSetting(ctx context.Context, callback func(key string) definition.PluginSettingDefinitionItem)
OnGetDynamicSetting(ctx context.Context, callback func(key string) string)
OnDeepLink(ctx context.Context, callback func(arguments map[string]string))
OnUnload(ctx context.Context, callback func())
RegisterQueryCommands(ctx context.Context, commands []MetadataCommand)
Expand Down Expand Up @@ -148,7 +147,7 @@ func (a *APIImpl) OnSettingChanged(ctx context.Context, callback func(key string
a.pluginInstance.SettingChangeCallbacks = append(a.pluginInstance.SettingChangeCallbacks, callback)
}

func (a *APIImpl) OnGetDynamicSetting(ctx context.Context, callback func(key string) definition.PluginSettingDefinitionItem) {
func (a *APIImpl) OnGetDynamicSetting(ctx context.Context, callback func(key string) string) {
a.pluginInstance.DynamicSettingCallbacks = append(a.pluginInstance.DynamicSettingCallbacks, callback)
}

Expand Down
7 changes: 4 additions & 3 deletions wox.core/plugin/host/host_nodejs.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ package host
import (
"context"
"fmt"
"github.com/Masterminds/semver/v3"
"github.com/mitchellh/go-homedir"
"path"
"strings"
"wox/plugin"
"wox/util"

"github.com/Masterminds/semver/v3"
"github.com/mitchellh/go-homedir"
)

func init() {
Expand All @@ -29,7 +30,7 @@ func (n *NodejsHost) GetRuntime(ctx context.Context) plugin.Runtime {
}

func (n *NodejsHost) Start(ctx context.Context) error {
return n.websocketHost.StartHost(ctx, n.findNodejsPath(ctx), path.Join(util.GetLocation().GetHostDirectory(), "node-host.js"))
return n.websocketHost.StartHost(ctx, n.findNodejsPath(ctx), path.Join(util.GetLocation().GetHostDirectory(), "node-host.js"), nil)
}

func (n *NodejsHost) findNodejsPath(ctx context.Context) string {
Expand Down
2 changes: 1 addition & 1 deletion wox.core/plugin/host/host_python.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func (n *PythonHost) GetRuntime(ctx context.Context) plugin.Runtime {
}

func (n *PythonHost) Start(ctx context.Context) error {
return n.websocketHost.StartHost(ctx, n.findPythonPath(ctx), path.Join(util.GetLocation().GetHostDirectory(), "python-host.pyz"))
return n.websocketHost.StartHost(ctx, n.findPythonPath(ctx), path.Join(util.GetLocation().GetHostDirectory(), "python-host.pyz"), []string{"SHIV_ROOT=" + util.GetLocation().GetCacheDirectory()})
}

func (n *PythonHost) findPythonPath(ctx context.Context) string {
Expand Down
25 changes: 18 additions & 7 deletions wox.core/plugin/host/host_websocket.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func (w *WebsocketHost) getHostName(ctx context.Context) string {
return fmt.Sprintf("%s Host Impl", w.host.GetRuntime(ctx))
}

func (w *WebsocketHost) StartHost(ctx context.Context, executablePath string, entry string, executableArgs ...string) error {
func (w *WebsocketHost) StartHost(ctx context.Context, executablePath string, entry string, envs []string, executableArgs ...string) error {
port, portErr := util.GetAvailableTcpPort(ctx)
if portErr != nil {
return fmt.Errorf("failed to get available port: %w", portErr)
Expand All @@ -44,7 +44,7 @@ func (w *WebsocketHost) StartHost(ctx context.Context, executablePath string, en
args = append(args, executableArgs...)
args = append(args, entry, fmt.Sprintf("%d", port), util.GetLocation().GetLogHostsDirectory(), fmt.Sprintf("%d", os.Getpid()))

cmd, err := util.ShellRun(executablePath, args...)
cmd, err := util.ShellRunWithEnv(executablePath, envs, args...)
if err != nil {
return fmt.Errorf("failed to start host: %w", err)
}
Expand Down Expand Up @@ -98,7 +98,7 @@ func (w *WebsocketHost) UnloadPlugin(ctx context.Context, metadata plugin.Metada
}

func (w *WebsocketHost) invokeMethod(ctx context.Context, metadata plugin.Metadata, method string, params map[string]string) (result any, err error) {
if !w.ws.IsConnected() {
if w.ws == nil || !w.ws.IsConnected() {
return "", fmt.Errorf("host is not connected")
}

Expand Down Expand Up @@ -342,34 +342,45 @@ func (w *WebsocketHost) handleRequestFromPlugin(ctx context.Context, request Jso
}

metadata := pluginInstance.Metadata
pluginInstance.API.OnGetDynamicSetting(ctx, func(key string) definition.PluginSettingDefinitionItem {
pluginInstance.API.OnGetDynamicSetting(ctx, func(key string) string {
result, err := w.invokeMethod(ctx, metadata, "onGetDynamicSetting", map[string]string{
"CallbackId": callbackId,
"Key": key,
})
if err != nil {
util.GetLogger().Error(ctx, fmt.Sprintf("[%s] failed to get dynamic setting: %s", request.PluginName, err))
return definition.PluginSettingDefinitionItem{
settingJson, marshalErr := json.Marshal(definition.PluginSettingDefinitionItem{
Type: definition.PluginSettingDefinitionTypeLabel,
Value: &definition.PluginSettingValueLabel{
Content: fmt.Sprintf("failed to get dynamic setting: %s", err),
},
})
if marshalErr != nil {
util.GetLogger().Error(ctx, fmt.Sprintf("[%s] failed to marshal dynamic setting: %s", request.PluginName, marshalErr))
return ""
}
return string(settingJson)
}

// validate the result is a valid definition.PluginSettingDefinitionItem json string
var setting definition.PluginSettingDefinitionItem
unmarshalErr := json.Unmarshal([]byte(result.(string)), &setting)
if unmarshalErr != nil {
util.GetLogger().Error(ctx, fmt.Sprintf("[%s] failed to unmarshal dynamic setting: %s", request.PluginName, unmarshalErr))
return definition.PluginSettingDefinitionItem{
settingJson, marshalErr := json.Marshal(definition.PluginSettingDefinitionItem{
Type: definition.PluginSettingDefinitionTypeLabel,
Value: &definition.PluginSettingValueLabel{
Content: fmt.Sprintf("failed to unmarshal dynamic setting: %s", unmarshalErr),
},
})
if marshalErr != nil {
util.GetLogger().Error(ctx, fmt.Sprintf("[%s] failed to marshal dynamic setting: %s", request.PluginName, marshalErr))
return ""
}
return string(settingJson)
}

return setting
return result.(string)
})
w.sendResponseToHost(ctx, request, "")
case "OnDeepLink":
Expand Down
3 changes: 1 addition & 2 deletions wox.core/plugin/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package plugin
import (
"context"
"wox/setting"
"wox/setting/definition"
)

type Instance struct {
Expand All @@ -17,7 +16,7 @@ type Instance struct {
Host Host // plugin host to run this plugin
Setting *setting.PluginSetting // setting for this plugin

DynamicSettingCallbacks []func(key string) definition.PluginSettingDefinitionItem // dynamic setting callbacks
DynamicSettingCallbacks []func(key string) string // dynamic setting callbacks
SettingChangeCallbacks []func(key string, value string)
DeepLinkCallbacks []func(arguments map[string]string)
UnloadCallbacks []func()
Expand Down
2 changes: 1 addition & 1 deletion wox.core/plugin/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,7 @@ func (m *Manager) PolishResult(ctx context.Context, pluginInstance *Instance, qu

// store preview for ui invoke later
// because preview may contain some heavy data (E.g. image or large text), we will store preview in cache and only send preview to ui when user select the result
if result.Preview.PreviewType != "" && result.Preview.PreviewType != WoxPreviewTypeRemote {
if result.Preview.PreviewType != "" && result.Preview.PreviewData != "" && result.Preview.PreviewType != WoxPreviewTypeRemote {
resultCache.Preview = result.Preview
result.Preview = WoxPreview{
PreviewType: WoxPreviewTypeRemote,
Expand Down
3 changes: 1 addition & 2 deletions wox.core/plugin/system/app/app_darwin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"testing"
"wox/ai"
"wox/plugin"
"wox/setting/definition"
"wox/share"
"wox/util"

Expand All @@ -15,7 +14,7 @@ import (
type emptyAPIImpl struct {
}

func (e emptyAPIImpl) OnGetDynamicSetting(ctx context.Context, callback func(key string) definition.PluginSettingDefinitionItem) {
func (e emptyAPIImpl) OnGetDynamicSetting(ctx context.Context, callback func(key string) string) {
}

func (e emptyAPIImpl) ChangeQuery(ctx context.Context, query share.PlainQuery) {
Expand Down
3 changes: 2 additions & 1 deletion wox.core/setting/definition/definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import (
"context"
"encoding/json"
"errors"
"github.com/tidwall/gjson"
"wox/util"

"github.com/tidwall/gjson"
)

type PluginSettingDefinitionType string
Expand Down
8 changes: 7 additions & 1 deletion wox.core/ui/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,13 @@ func convertPluginDto(ctx context.Context, pluginDto dto.PluginDto, pluginInstan
if settingDefinition.Type == definition.PluginSettingDefinitionTypeDynamic {
replaced := false
for _, callback := range pluginInstance.DynamicSettingCallbacks {
newSettingDefinition := callback(settingDefinition.Value.GetKey())
newSettingDefinitionJson := callback(settingDefinition.Value.GetKey())
var newSettingDefinition definition.PluginSettingDefinitionItem
unmarshalErr := json.Unmarshal([]byte(newSettingDefinitionJson), &newSettingDefinition)
if unmarshalErr != nil {
logger.Error(ctx, fmt.Sprintf("failed to unmarshal dynamic setting: %s", unmarshalErr.Error()))
continue
}
if newSettingDefinition.Value != nil && newSettingDefinition.Type != definition.PluginSettingDefinitionTypeDynamic {
logger.Debug(ctx, fmt.Sprintf("dynamic setting replaced: %s(%s) -> %s(%s)", settingDefinition.Value.GetKey(), settingDefinition.Type, newSettingDefinition.Value.GetKey(), newSettingDefinition.Type))
pluginDto.SettingDefinitions[i] = newSettingDefinition
Expand Down
3 changes: 2 additions & 1 deletion wox.core/util/location.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ package util

import (
"fmt"
"github.com/mitchellh/go-homedir"
"os"
"path"
"strings"
"sync"

"github.com/mitchellh/go-homedir"
)

var locationInstance *Location
Expand Down
18 changes: 18 additions & 0 deletions wox.core/util/open_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package util

import (
"fmt"
"os"
"os/exec"
)

Expand All @@ -21,6 +22,23 @@ func ShellRun(name string, arg ...string) (*exec.Cmd, error) {
return cmd, nil
}

func ShellRunWithEnv(name string, envs []string, arg ...string) (*exec.Cmd, error) {
if len(envs) == 0 {
return ShellRun(name, arg...)
}

cmd := exec.Command(name, arg...)
cmd.Stdout = GetLogger().GetWriter()
cmd.Stderr = GetLogger().GetWriter()
cmd.Env = append(os.Environ(), envs...)
cmdErr := cmd.Start()
if cmdErr != nil {
return nil, cmdErr
}

return cmd, nil
}

func ShellRunOutput(name string, arg ...string) ([]byte, error) {
cmd := exec.Command(name, arg...)
output, err := cmd.CombinedOutput()
Expand Down
3 changes: 2 additions & 1 deletion wox.core/util/websocket_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ package util
import (
"context"
"fmt"
"github.com/gorilla/websocket"
"sync"
"time"

"github.com/gorilla/websocket"
)

type WebsocketClient struct {
Expand Down
2 changes: 1 addition & 1 deletion wox.plugin.host.python/.python-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.10
3.10
Loading

0 comments on commit ccfdbc5

Please sign in to comment.