Skip to content

Commit

Permalink
Add Universal Actions and Hotkeys
Browse files Browse the repository at this point in the history
  • Loading branch information
deanishe committed Nov 12, 2021
1 parent 2008c94 commit 29cf973
Show file tree
Hide file tree
Showing 9 changed files with 820 additions and 165 deletions.
70 changes: 70 additions & 0 deletions FinderSelection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/osascript -l JavaScript

ObjC.import('stdlib')

const finderId = 'com.apple.Finder',
pathFinderId = 'com.cocoatech.PathFinder'

const file2Path = fi => Path(decodeURI(fi.url()).slice(7)).toString()

function getEnv(key) {
try {
return $.getenv(key)
} catch(e) {
return null
}
}

function pathFinderPaths() {
const pf = Application(pathFinderId)
let selection = pf.selection(),
paths = []

if (selection) {
selection.forEach(pfi => {
let p = pfi.posixPath()
console.log(`[Path Finder] selection=${p}`)
paths.push(p)
})
} else {
let p = pf.finderWindows[0].target.posixPath()
console.log(`[Path Finder] target=${p}`)
paths.push(p)
}

return paths
}

function finderPaths() {
const finder = Application(finderId)
let paths = [],
selection = finder.selection()

if (selection) {
selection.forEach(fi => {
let p = file2Path(fi)
console.log(`[Finder] selection=${p}`)
paths.push(p)
})
} else {
let p = file2Path(finder.finderWindows[0].target)
console.log(`[Finder] target=${p}`)
paths.push(p)
}

return paths
}

function run() {
console.log('🍻')
const activeApp = getEnv('focusedapp')
console.log(`activeApp=${activeApp}`)
let paths = []
if (activeApp === pathFinderId) {
paths = pathFinderPaths()
} else {
paths = finderPaths()
}

return JSON.stringify({alfredworkflow: {arg: paths}})
}
134 changes: 102 additions & 32 deletions README.html

Large diffs are not rendered by default.

46 changes: 43 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@

<div align="center">
<img src="icon.png" width="128" height="128">
</div>

Sublime Text Projects Alfred Workflow
=====================================

Expand All @@ -11,6 +15,9 @@ View, filter and open your Sublime Text (or VSCode) project files.
- [Download & Installation](#download--installation)
- [Catalina and later](#catalina-and-later)
- [Usage](#usage)
- [Universal Actions](#universal-actions)
- [Hotkeys](#hotkeys)
- [External Triggers](#external-triggers)
- [How it works](#how-it-works)
- [Configuration](#configuration)
- [Licensing, thanks](#licensing-thanks)
Expand All @@ -26,7 +33,7 @@ Download the workflow from [GitHub][gh-releases] and install by double-clicking


<a id="catalina-and-later"></a>
### Catalina and later ###
### Catalina and later

If you're running Catalina or later (macOS 10.15+), you'll need to [grant the workflow executable permission to run][catalina].

Expand All @@ -35,6 +42,8 @@ If you're running Catalina or later (macOS 10.15+), you'll need to [grant the wo
Usage
-----

There is one keyword, `.st`, which works as follows:

- `.st [<query>]` — List/filter your `.sublime-project` files
+ `↩` — Open result in Sublime Text
+ `⌘+↩` — Reveal file in Finder
Expand All @@ -47,7 +56,36 @@ Usage
- `Report Issue` — Open GitHub issue tracker in your browser
- `Visit Forum Thread` — Open workflow's thread on [alfredforum.com][forum]

You can enter the words `search` or `config` as a search query anywhere to jump to that screen.
You can enter `search` or `config` as a search query anywhere to jump to the corresponding screen.


<a id="universal-actions"></a>
### Universal Actions

There are Universal Actions for files, URLs and text. Files are opened, and text is inserted into a new document.

Multiple URLs are treated as text, but a single URL is retrieved with curl and a new document is created with its contents.


<a id="hotkeys"></a>
### Hotkeys

The workflow has two Hotkeys (marked red) that you can set to open the currently-selected files in any application in Sublime Text. One Hotkey is for Finder and Path Finder only, and the other is for all other applications. You should set them to the same keyboard shortcut.

The Finder/Path Finder variant doesn't rely on Alfred's "Selection in macOS" feature, and will open the frontmost window's target (the folder whose contents it's showing) if nothing is selected.


<a id="external-triggers"></a>
### External Triggers

The workflow has the following External Triggers that can be used from scripts or other workflows:

| Name | Description |
|------------|--------------------------------------------------------|
| `new` | Create a new document containing the given text |
| `open` | Open the specified path in Sublime Text |
| `open-url` | Create a new document with the data retrieved from URL |
| `search` | Show project search results for given query |


<a id="how-it-works"></a>
Expand All @@ -58,6 +96,8 @@ The workflow scans your system for `.sublime-project` (or `.code-workspace`) fil

As the `locate` database isn't enabled on most machines (and isn't updated frequently in any case), and `mdfind` ignores hidden directories, there is an additional, optional `find`-based scanner to "fill the gaps", which you must specifically configure (see below).

**NOTE**: When the workflow is asked to open a directory (e.g. via External Trigger or Universal Action), it looks for a project file in the directory, and opens that instead if one is found.


<a id="configuration"></a>
Configuration
Expand All @@ -74,7 +114,7 @@ Scan intervals are configured in the [workflow's configuration sheet in Alfred P

The interval values should be of the form `10m` or `2h`. Set to `0` to disable a particular scanner.

The workflow should work "out of the box", but if you have project files in directories that `mdfind` doesn't see (hidden directories, network shares), you may have to explicitly add some search paths to the `sublime.toml` configuration file in the workflow's data directory. The file is created on first run, and you can use `.stconfig > Edit Config File` to open it.
The workflow should work "out of the box", but if you have project files in directories that `mdfind` doesn't see (hidden directories, network shares), you may have to explicitly add some search paths to the `sublime.toml` configuration file in the workflow's data directory. The file is created on first run, and you can use `.st config > Workflow Settings > Edit Config File` to open it.

These directories are searched with `find`.

Expand Down
92 changes: 62 additions & 30 deletions cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"time"

Expand Down Expand Up @@ -48,8 +49,7 @@ type options struct {
Config bool
Ignore bool
Open bool
OpenProject bool
OpenFolder bool
OpenFolders bool
Rescan bool

// Options
Expand All @@ -63,8 +63,7 @@ func init() {
cli.BoolVar(&opts.Search, "search", false, "search projects")
cli.BoolVar(&opts.Config, "conf", false, "show/filter configuration")
cli.BoolVar(&opts.Open, "open", false, "open specified file in default app")
cli.BoolVar(&opts.OpenProject, "project", false, "open specified project")
cli.BoolVar(&opts.OpenFolder, "folder", false, "open specified project")
cli.BoolVar(&opts.OpenFolders, "folders", false, "open specified project")
cli.BoolVar(&opts.Rescan, "rescan", false, "re-scan for projects")
cli.BoolVar(&opts.Force, "force", false, "force rescan")
cli.Usage = func() {
Expand All @@ -73,11 +72,12 @@ func init() {
Alfred workflow to show Sublime Text/VSCode projects.
Usage:
alfred-sublime <file>...
alfred-sublime -
alfred-sublime -search [<query>]
alfred-sublime -conf [<query>]
alfred-sublime -open <path>
alfred-sublime -project <project file>
alfred-sublime -folder <project file>
alfred-sublime -folders <project file>
alfred-sublime -rescan [-force]
alfred-sublime -h|-help
Expand All @@ -88,7 +88,9 @@ Options:
}
}

func commandForProject(path string) *exec.Cmd {
func openCommand(path string) *exec.Cmd {
// name, args := appArgs()
// return exec.Command(name, append(args, path)...)
var (
app = "Sublime Text"
progs = sublPaths
Expand All @@ -107,47 +109,75 @@ func commandForProject(path string) *exec.Cmd {
return exec.Command("/usr/bin/open", "-a", app, path)
}

// Open a project file. CLI programs `subl` or `code` are preferred.
// If they can't be found application "Sublime Text.app" or
// "Visual Studio Code.app" is called instead.
func runOpenProject() {
// Try to open each command-line argument in turn.
// If argument is a directory, search it for a project file.
func runOpenPaths() {
wf.Configure(aw.TextErrors(true))

log.Printf("opening project %q ...", opts.Query)
cmd := commandForProject(opts.Query)
for _, path := range cli.Args() {
cmd := openCommand(findProject(path))
if path == "-" {
cmd.Stdin = os.Stdin
}

if _, err := util.RunCmd(cmd); err != nil {
wf.Fatalf("exec command %#v: %v", cmd, err)
log.Printf("opening %q ...", path)
if _, err := util.RunCmd(cmd); err != nil {
log.Printf("error opening %q: %v", path, err)
}
}
}

func findProject(dir string) string {
fi, err := os.Stat(dir)
if err != nil {
log.Printf("error inspecting file %q: %v", dir, err)
return dir
}
if !fi.IsDir() {
return dir
}
files, err := os.ReadDir(dir)
if err != nil {
log.Printf("error reading directory %q: %v", dir, err)
return dir
}
for _, de := range files {
if de.IsDir() {
continue
}
if strings.ToLower(filepath.Ext(de.Name())) == fileExtension {
return filepath.Join(dir, de.Name())
}
}
return dir
}

// Open a project's folders
func runOpenFolder() {
func runOpenFolders() {
wf.Configure(aw.TextErrors(true))

var (
projs []Project
sm = NewScanManager(conf)
projs []Project
err error
)

if projs, err = sm.Load(); err != nil {
wf.Fatalf("load projects: %v", err)
}

for _, proj := range projs {
if proj.Path == opts.Query {
for _, path := range proj.Folders {

log.Printf("opening folder %q ...", path)
cmd := exec.Command("/usr/bin/open", path)
if proj.Path != opts.Query {
continue
}

if _, err := util.RunCmd(cmd); err != nil {
wf.Fatalf("run command %#v: %v", cmd, err)
}
for _, path := range proj.Folders {
log.Printf("opening folder %q ...", path)
cmd := exec.Command("/usr/bin/open", path)
if _, err := util.RunCmd(cmd); err != nil {
log.Printf("error opening folder %q: %v", path, err)
}
return
}
return
}

wf.Fatalf("no folders found for project %q", opts.Query)
Expand Down Expand Up @@ -313,7 +343,9 @@ func runSearch() {
it := wf.NewItem(proj.Name()).
Subtitle(util.PrettyPath(proj.Path)).
Valid(true).
Arg("-project", "--", proj.Path).
// Arg("-project", "--", proj.Path).
Arg(proj.Path).
IsFile(true).
UID(proj.Path).
Copytext(proj.Folder()).
Action(proj.Folder()).
Expand All @@ -327,8 +359,8 @@ func runSearch() {
}
it.NewModifier("cmd").
Subtitle(sub).
Icon(&aw.Icon{Value: "public.folder", Type: "filetype"}).
Arg("-folder", proj.Path)
Icon(&aw.Icon{Value: proj.Folder(), Type: "fileicon"}).
Arg("-folders", proj.Path)
}
}

Expand Down
Binary file modified demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 29cf973

Please sign in to comment.