Moved the plugin directory
All checks were successful
Release Build / build (push) Successful in 1m53s

This commit is contained in:
Leon Mika 2026-09-09 21:08:46 +10:00
parent add62fbfa0
commit 9751be125e
5 changed files with 47 additions and 12 deletions

View file

@ -13,8 +13,9 @@ This project uses [Wails](https://wails.io) to build a desktop application with
## Plugins ## Plugins
Dequoter can be extended with new filters written in [UCL](https://ucl.lmika.dev). Dequoter can be extended with new filters written in [UCL](https://ucl.lmika.dev).
On startup, if the directory `$HOME/.config/dequoter/plugins` exists, every file ending in `.ucl` On startup, Dequoter creates `$HOME/Library/Application Support/dev.lmika.dequoter/plugins` if needed,
found in it (including subdirectories) is evaluated. Filters are defined with the `d:filter` builtin: then evaluates every file ending in `.ucl` found in it (including subdirectories).
Filters are defined with the `d:filter` builtin:
``` ```
d:filter "String: To Upper Case" { |in| d:filter "String: To Upper Case" { |in|
@ -33,6 +34,8 @@ fail to load are skipped and reported in the status bar; the remaining files are
After editing plugin files, choose File → Reload Plugins (Shift+Cmd+R) to rescan the directory without After editing plugin files, choose File → Reload Plugins (Shift+Cmd+R) to rescan the directory without
restarting. This discards the previous plugin filters and UCL interpreter state before loading the files again. restarting. This discards the previous plugin filters and UCL interpreter state before loading the files again.
Use File → Open Plugins in Finder or File → Open Plugins in Terminal to access the directory.
If you have plugins in the old `$HOME/.config/dequoter/plugins` directory, move them to the new location.
## Download ## Download
@ -52,4 +55,4 @@ The above copyright notice and this permission notice shall be included in all c
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

27
app.go
View file

@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"log" "log"
"os/exec"
"sort" "sort"
"strings" "strings"
"sync" "sync"
@ -96,10 +97,11 @@ func (a *App) loadPluginsFrom(dir string) int {
func (a *App) startup(ctx context.Context) { func (a *App) startup(ctx context.Context) {
a.baseCtx = ctx a.baseCtx = ctx
dir, err := pluginDir() dir, err := ensurePluginDir()
if err != nil { if err != nil {
log.Printf("cannot determine plugin directory: %v", err) plugins := newPluginFilters()
a.setUCLInst(newUCLInst(newPluginFilters()), nil) plugins.recordError(err)
a.setUCLInst(newUCLInst(plugins), plugins)
return return
} }
a.loadPluginsFrom(dir) a.loadPluginsFrom(dir)
@ -114,11 +116,11 @@ func (a *App) domReady(ctx context.Context) {
// ReloadPlugins discards the current plugin filters and UCL interpreter, rescans the plugin // ReloadPlugins discards the current plugin filters and UCL interpreter, rescans the plugin
// directory, and refreshes the command palette. Invoked from the File menu. // directory, and refreshes the command palette. Invoked from the File menu.
func (a *App) ReloadPlugins() { func (a *App) ReloadPlugins() {
dir, err := pluginDir() dir, err := ensurePluginDir()
if err != nil { if err != nil {
_, _, ctx := a.current() _, _, ctx := a.current()
runtime.EventsEmit(ctx, "set-statusbar-message", SetStatusbarMessage{ runtime.EventsEmit(ctx, "set-statusbar-message", SetStatusbarMessage{
Message: fmt.Sprintf("Cannot determine plugin directory: %v", err), Message: fmt.Sprintf("Cannot prepare plugin directory: %v", err),
Error: true, Error: true,
}) })
return return
@ -131,6 +133,21 @@ func (a *App) ReloadPlugins() {
a.reportPluginStatus(count, true) a.reportPluginStatus(count, true)
} }
// openPluginDir opens the plugin directory using the specified macOS application.
func (a *App) openPluginDir(application string) {
dir, err := ensurePluginDir()
if err == nil {
err = exec.Command("/usr/bin/open", "-a", application, dir).Run()
}
if err != nil {
_, _, ctx := a.current()
runtime.EventsEmit(ctx, "set-statusbar-message", SetStatusbarMessage{
Message: fmt.Sprintf("Cannot open plugins in %s: %v", application, err),
Error: true,
})
}
}
// reportPluginStatus shows plugin load errors in the status bar. If there are no errors and // reportPluginStatus shows plugin load errors in the status bar. If there are no errors and
// reloaded is true, a confirmation message is shown instead. // reloaded is true, a confirmation message is shown instead.
func (a *App) reportPluginStatus(filterCount int, reloaded bool) { func (a *App) reportPluginStatus(filterCount int, reloaded bool) {

2
go.sum
View file

@ -796,7 +796,5 @@ lmika.dev/pkg/modash v0.1.0/go.mod h1:8NDl/yR1eCCEhip9FJlVuMNXIeaztQ0Ks/tizExFcT
lmika.dev/pkg/progdoc v0.0.0-20260207235039-984257fb414b h1:uarBEkpjAnpzO98btsA6ZDw9oGnJX8zSb8gZnCFl+6Y= lmika.dev/pkg/progdoc v0.0.0-20260207235039-984257fb414b h1:uarBEkpjAnpzO98btsA6ZDw9oGnJX8zSb8gZnCFl+6Y=
lmika.dev/pkg/progdoc v0.0.0-20260207235039-984257fb414b/go.mod h1:GefQ+wxW6L0p2TfB4fzoP7ihOgEt5Lb/1qgXhruVHos= lmika.dev/pkg/progdoc v0.0.0-20260207235039-984257fb414b/go.mod h1:GefQ+wxW6L0p2TfB4fzoP7ihOgEt5Lb/1qgXhruVHos=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
ucl.lmika.dev v0.1.6 h1:cZocpR/IQcy9nvE8u+SSLY7/PE9fd1HIVWhZr/m+U5M=
ucl.lmika.dev v0.1.6/go.mod h1:Hjl6Udph2yuOPcBSytz4SFJVt5MZbM5JyiSAyTMTbNU=
ucl.lmika.dev v0.1.7 h1:3yfvWOhBnWpQIm4NhKrUBh5PajB1wzUWzoPPa8ltaVo= ucl.lmika.dev v0.1.7 h1:3yfvWOhBnWpQIm4NhKrUBh5PajB1wzUWzoPPa8ltaVo=
ucl.lmika.dev v0.1.7/go.mod h1:Hjl6Udph2yuOPcBSytz4SFJVt5MZbM5JyiSAyTMTbNU= ucl.lmika.dev v0.1.7/go.mod h1:Hjl6Udph2yuOPcBSytz4SFJVt5MZbM5JyiSAyTMTbNU=

View file

@ -40,6 +40,12 @@ func main() {
fileMenu.AddText("Reload Plugins", keys.Combo("r", keys.CmdOrCtrlKey, keys.ShiftKey), func(_ *menu.CallbackData) { fileMenu.AddText("Reload Plugins", keys.Combo("r", keys.CmdOrCtrlKey, keys.ShiftKey), func(_ *menu.CallbackData) {
app.ReloadPlugins() app.ReloadPlugins()
}) })
fileMenu.AddText("Open Plugins in Finder", nil, func(_ *menu.CallbackData) {
app.openPluginDir("Finder")
})
fileMenu.AddText("Open Plugins in Terminal", nil, func(_ *menu.CallbackData) {
app.openPluginDir("Terminal")
})
fileMenu.AddSeparator() fileMenu.AddSeparator()
fileMenu.AddText("Quit", keys.CmdOrCtrl("q"), func(_ *menu.CallbackData) { fileMenu.AddText("Quit", keys.CmdOrCtrl("q"), func(_ *menu.CallbackData) {
// `rt` is an alias of "github.com/wailsapp/wails/v2/pkg/runtime" to prevent collision with standard package // `rt` is an alias of "github.com/wailsapp/wails/v2/pkg/runtime" to prevent collision with standard package

View file

@ -173,13 +173,24 @@ func (p *PluginFilters) Errors() []error {
return p.errors return p.errors
} }
// pluginDir returns the plugin directory: $HOME/.config/dequoter/plugins // pluginDir returns the plugin directory under macOS Application Support.
func pluginDir() (string, error) { func pluginDir() (string, error) {
home, err := os.UserHomeDir() home, err := os.UserHomeDir()
if err != nil { if err != nil {
return "", err return "", err
} }
return filepath.Join(home, ".config", "dequoter", "plugins"), nil return filepath.Join(home, "Library", "Application Support", "dev.lmika.dequoter", "Plugins"), nil
}
func ensurePluginDir() (string, error) {
dir, err := pluginDir()
if err != nil {
return "", err
}
if err := os.MkdirAll(dir, 0755); err != nil {
return "", fmt.Errorf("create plugin directory: %w", err)
}
return dir, nil
} }
var nonSlugChars = regexp.MustCompile(`[^a-z0-9]+`) var nonSlugChars = regexp.MustCompile(`[^a-z0-9]+`)