diff --git a/cli.go b/cli.go index 8755bb6..ef0091d 100644 --- a/cli.go +++ b/cli.go @@ -316,12 +316,10 @@ func runSearch() { Var("close", "true") if len(proj.Folders) > 0 { - sub := "Open Project Folder" if len(proj.Folders) > 1 { sub += "s" } - it.NewModifier("cmd"). Subtitle(sub). Icon(&aw.Icon{Value: "public.folder", Type: "filetype"}). diff --git a/go.mod b/go.mod index 84d119b..e217dcd 100644 --- a/go.mod +++ b/go.mod @@ -2,16 +2,15 @@ module github.com/deanishe/alfred-sublime-text require ( github.com/BurntSushi/toml v0.3.1 - github.com/bmatcuk/doublestar v1.1.5 + github.com/bmatcuk/doublestar v1.3.0 // indirect github.com/davecgh/go-spew v1.1.1 - github.com/deanishe/awgo v0.20.4 - github.com/disintegration/imaging v1.6.1 + github.com/deanishe/awgo v0.22.1 + github.com/disintegration/imaging v1.6.2 github.com/gobwas/glob v0.2.3 github.com/magefile/mage v1.9.0 github.com/yosuke-furukawa/json5 v0.1.1 - golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 // indirect - golang.org/x/text v0.3.2 - howett.net/plist v0.0.0-20181124034731-591f970eefbb + golang.org/x/image v0.0.0-20200430140353-33d19683fad8 // indirect + howett.net/plist v0.0.0-20200419221736-3b63eb3a43b5 // indirect ) go 1.13 diff --git a/go.sum b/go.sum index 2d77cbb..4d4f0df 100644 --- a/go.sum +++ b/go.sum @@ -2,13 +2,18 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/bmatcuk/doublestar v1.1.5 h1:2bNwBOmhyFEFcoB3tGvTD5xanq+4kyOZlB8wFYbMjkk= github.com/bmatcuk/doublestar v1.1.5/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= +github.com/bmatcuk/doublestar v1.3.0 h1:1jLE2y0VpSrOn/QR9G4f2RmrCtkM3AuATcWradjHUvM= +github.com/bmatcuk/doublestar v1.3.0/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deanishe/awgo v0.20.4 h1:d6PUWriwtOtJr+wxxNwQ58N50fr9lhsf3Z5XhWfmYnI= -github.com/deanishe/awgo v0.20.4/go.mod h1:GU6eQZvFKhxX6Qjlb5Qls/Le/3r5XOs3P0L4F283R+w= -github.com/disintegration/imaging v1.6.1 h1:JnBbK6ECIZb1NsWIikP9pd8gIlTIRx7fuDNpU9fsxOE= -github.com/disintegration/imaging v1.6.1/go.mod h1:xuIt+sRxDFrHS0drzXUlCJthkJ8k7lkkUojDSR247MQ= +github.com/deanishe/awgo v0.22.1 h1:N61eAOqc2u2zMQjY0Srd3mAn1gaj/9nU4YaTe3aaHU0= +github.com/deanishe/awgo v0.22.1/go.mod h1:FIvY0LMPTrqEqSDy79rcSqL6+oFUhFL9qlXBi+qQKks= +github.com/deanishe/go-env v0.4.0 h1:tpu14o16gJGTN/w2gxntwxu2l5Eby30jSrnlgOfjzwk= +github.com/deanishe/go-env v0.4.0/go.mod h1:RgEcGAqdRnt8ybQteAbv1Ys2lWIRE7TlgON/sbdjuaY= +github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= +github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -19,8 +24,8 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magefile/mage v1.9.0 h1:t3AU2wNwehMCW97vuqQLtw6puppWXHO+O2MHo5a50XE= github.com/magefile/mage v1.9.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -28,10 +33,10 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/yosuke-furukawa/json5 v0.1.1 h1:0F9mNwTvOuDNH243hoPqvf+dxa5QsKnZzU20uNsh3ZI= github.com/yosuke-furukawa/json5 v0.1.1/go.mod h1:sw49aWDqNdRJ6DYUtIQiaA3xyj2IL9tjeNYmX2ixwcU= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81 h1:00VmoueYNlNz/aHIilyyQz/MHSqGoWJzpFv/HW8xpzI= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200430140353-33d19683fad8 h1:6WW6V3x1P/jokJBpRQYUJnMHRP6isStQwCozxnU7XQw= +golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -44,3 +49,5 @@ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= howett.net/plist v0.0.0-20181124034731-591f970eefbb h1:jhnBjNi9UFpfpl8YZhA9CrOqpnJdvzuiHsl/dnxl11M= howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= +howett.net/plist v0.0.0-20200419221736-3b63eb3a43b5 h1:AQkaJpH+/FmqRjmXZPELom5zIERYZfwTjnHpfoVMQEc= +howett.net/plist v0.0.0-20200419221736-3b63eb3a43b5/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= diff --git a/info.plist b/info.plist index 65dc81b..349a3c8 100644 --- a/info.plist +++ b/info.plist @@ -29,16 +29,6 @@ modifiersubtext vitoclose - - - - destinationuid - A18FD78E-C1F5-4B49-95D1-9ED40BA1E4D8 - modifiers - 0 - modifiersubtext - - vitoclose @@ -178,19 +168,6 @@ - A18FD78E-C1F5-4B49-95D1-9ED40BA1E4D8 - - - destinationuid - D26E55A9-2B57-4343-8C9D-9223F1E3AA70 - modifiers - 0 - modifiersubtext - - vitoclose - - - D545A35F-4FA1-469E-A8AB-A167D29E8D50 @@ -231,47 +208,31 @@ config - alfredfiltersresults - - alfredfiltersresultsmatchmode + action 0 - argumenttrimmode + argument 0 - argumenttype - 1 - escaping - 68 - keyword - .st - queuedelaycustom - 1 - queuedelayimmediatelyinitially + focusedappvariable - queuedelaymode + focusedappvariablename + + hotkey 0 - queuemode - 1 - runningsubtext - Finding projects… - script - ./alfsubl -- "$1" - scriptargtype - 1 - scriptfile + hotmod + 0 + hotstring - subtext - Search and Open Sublime Text Projects - title - Sublime Text Projects - type + leftcursor + + modsmode + 0 + relatedAppsMode 0 - withspace - type - alfred.workflow.input.scriptfilter + alfred.workflow.trigger.hotkey uid - 0D6DB001-6C1A-4973-BD3C-0CD4706096CB + DA2EB775-5859-482A-8BF0-499164DCDA0B version 2 @@ -314,60 +275,51 @@ config - action - 0 - argument - 0 - focusedappvariable + alfredfiltersresults - focusedappvariablename - - hotkey + alfredfiltersresultsmatchmode 0 - hotmod + argumenttreatemptyqueryasnil + + argumenttrimmode 0 - hotstring - - leftcursor + argumenttype + 1 + escaping + 68 + keyword + .st + queuedelaycustom + 1 + queuedelayimmediatelyinitially - modsmode + queuedelaymode 0 - relatedAppsMode + queuemode + 1 + runningsubtext + Finding projects… + script + ./alfsubl -- "$1" + scriptargtype + 1 + scriptfile + + subtext + Search and Open Sublime Text Projects + title + Sublime Text Projects + type 0 - - type - alfred.workflow.trigger.hotkey - uid - DA2EB775-5859-482A-8BF0-499164DCDA0B - version - 2 - - - type - alfred.workflow.utility.hidealfred - uid - D26E55A9-2B57-4343-8C9D-9223F1E3AA70 - version - 1 - - - config - - inputstring - {var:close} - matchcasesensitive + withspace - matchmode - 0 - matchstring - true type - alfred.workflow.utility.filter + alfred.workflow.input.scriptfilter uid - A18FD78E-C1F5-4B49-95D1-9ED40BA1E4D8 + 0D6DB001-6C1A-4973-BD3C-0CD4706096CB version - 1 + 3 config @@ -376,6 +328,8 @@ alfredfiltersresultsmatchmode 0 + argumenttreatemptyqueryasnil + argumenttrimmode 0 argumenttype @@ -414,7 +368,7 @@ uid 9981F708-6C83-44CD-BC06-B6C10A2B00F6 version - 2 + 3 config @@ -473,6 +427,25 @@ version 1 + + config + + inputstring + {var:action} + matchcasesensitive + + matchmode + 1 + matchstring + rescan + + type + alfred.workflow.utility.filter + uid + 2DBA4231-BA57-4BB8-9E1D-DD1F29FA2A8A + version + 1 + config @@ -496,25 +469,6 @@ version 1 - - config - - inputstring - {var:action} - matchcasesensitive - - matchmode - 1 - matchstring - rescan - - type - alfred.workflow.utility.filter - uid - 2DBA4231-BA57-4BB8-9E1D-DD1F29FA2A8A - version - 1 - config @@ -599,126 +553,110 @@ To add additional directories to scan, use `.stconfig > Edit Config File`.0A75A8BC-3856-4729-B16A-62B167B2E91F xpos - 40 + 45 ypos - 50 + 165 0D6DB001-6C1A-4973-BD3C-0CD4706096CB note Filter Sublime Text projects xpos - 230 + 235 ypos - 50 + 165 1958F9DC-5B69-4F0C-B4DC-FCBAE0AB1EF8 xpos - 670 + 675 ypos - 320 + 340 2DBA4231-BA57-4BB8-9E1D-DD1F29FA2A8A note $action != rescan xpos - 470 + 475 ypos - 350 + 370 3044F153-18F7-41B3-949A-22D21BA52903 xpos - 40 + 45 ypos - 320 + 340 506077C9-6BF8-401D-B34D-ACAEAA975F30 note Open project/folder xpos - 470 + 450 ypos - 50 + 165 640C6327-8251-4BC6-B952-603E863B03D2 note $action == rescan xpos - 470 + 475 ypos - 500 + 520 671FF011-6B4E-45E5-B96B-4A6B87738E93 xpos - 570 + 575 ypos - 350 + 370 6756738D-5D8A-4FEC-9F39-8AF903ED1FD2 xpos - 670 + 675 ypos - 620 + 640 6F4C4F63-0697-43E5-B54D-317C51DFE8D5 xpos - 1070 + 1075 ypos - 320 + 340 73C36700-35E0-40C2-A0AB-9D6B310FD946 xpos - 980 + 985 ypos - 350 + 370 9981F708-6C83-44CD-BC06-B6C10A2B00F6 note View workflow configuration xpos - 230 - ypos - 320 - - A18FD78E-C1F5-4B49-95D1-9ED40BA1E4D8 - - note - Hide Alfred - xpos - 470 - ypos - 220 - - D26E55A9-2B57-4343-8C9D-9223F1E3AA70 - - xpos - 570 + 235 ypos - 220 + 340 D545A35F-4FA1-469E-A8AB-A167D29E8D50 xpos - 670 + 675 ypos - 470 + 490 DA2EB775-5859-482A-8BF0-499164DCDA0B xpos - 40 + 45 ypos - 180 + 35 variables @@ -733,8 +671,8 @@ To add additional directories to scan, use `.stconfig > Edit Config File`.false version - 3.1.1 + 3.1.2 webaddress - + https://github.com/deanishe/alfred-sublime-text diff --git a/magefile.go b/magefile.go index d1388ff..a386213 100644 --- a/magefile.go +++ b/magefile.go @@ -6,32 +6,41 @@ package main import ( - "archive/zip" "fmt" - "io" - "io/ioutil" "os" "path/filepath" - "regexp" - "unicode" - "github.com/bmatcuk/doublestar" + "github.com/deanishe/awgo/util/build" "github.com/magefile/mage/mg" "github.com/magefile/mage/sh" - "golang.org/x/text/transform" - "golang.org/x/text/unicode/norm" ) // Default target to run when none is specified // If not set, running mage will list available targets // var Default = Build +const ( + green = "03ae03" + // blue = "5484f3" + // red = "b00000" + // yellow = "f8ac30" +) + +const ( + buildDir = "./build" + distDir = "./dist" +) + var ( + info *build.Info workDir string ) func init() { var err error + if info, err = build.NewInfo(); err != nil { + panic(err) + } if workDir, err = os.Getwd(); err != nil { panic(err) } @@ -39,78 +48,40 @@ func init() { func mod(args ...string) error { argv := append([]string{"mod"}, args...) - return sh.RunWith(alfredEnv(), "go", argv...) + return sh.RunWith(info.Env(), "go", argv...) } // Aliases are mage command aliases. var Aliases = map[string]interface{}{ "b": Build, + "c": Clean, "d": Dist, "l": Link, } -// Build builds workflow in ./build +// make workflow in build directory func Build() error { mg.Deps(cleanBuild, Icons) - // mg.Deps(Deps) fmt.Println("building ...") - if err := sh.RunWith(alfredEnv(), "go", "build", "-o", "./build/alfsubl", "."); err != nil { + if err := sh.RunWith(info.Env(), "go", "build", "-o", "./build/alfsubl", "."); err != nil { return err } - // link files to ./build - globs := []struct { - glob, dest string - }{ - {"*.png", ""}, - {"info.plist", ""}, - {"*.html", ""}, - {"README.md", ""}, - {"LICENCE.txt", ""}, - {"icons/*.png", ""}, - } - - pairs := []struct { - src, dest string - }{} - - for _, cfg := range globs { - files, err := doublestar.Glob(cfg.glob) - if err != nil { - return err - } - - for _, p := range files { - dest := filepath.Join("./build", cfg.dest, p) - pairs = append(pairs, struct{ src, dest string }{p, dest}) - } - } - - for _, p := range pairs { - - var ( - relPath string - dir = filepath.Dir(p.dest) - err error - ) - - if err = os.MkdirAll(dir, 0755); err != nil { - return err - } - if relPath, err = filepath.Rel(filepath.Dir(p.dest), p.src); err != nil { - return err - } - fmt.Printf("%s --> %s\n", p.dest, relPath) - if err := os.Symlink(relPath, p.dest); err != nil { - return err - } - } + // files to include in workflow + globs := build.Globs( + "*.png", + "info.plist", + "*.html", + "README.md", + "LICENCE.txt", + "icons/*.png", + ) - return nil + return build.SymlinkGlobs(buildDir, globs...) } -// Run run workflow +// run workflow func Run() error { mg.Deps(Build) fmt.Println("running ...") @@ -119,134 +90,27 @@ func Run() error { } defer os.Chdir(workDir) - return sh.RunWith(alfredEnv(), "./alfsubl", "-h") + return sh.RunWith(info.Env(), "./alfsubl", "-h") } -// Dist build an .alfredworkflow file in ./dist +// create an .alfredworkflow file in ./dist func Dist() error { mg.SerialDeps(Clean, Build) - if err := os.MkdirAll("./dist", 0700); err != nil { - return err - } - - var ( - name = slugify(fmt.Sprintf("%s-%s.alfredworkflow", Name, Version)) - path = filepath.Join("./dist", name) - f *os.File - w *zip.Writer - err error - ) - - fmt.Println("building .alfredworkflow file ...") - - if _, err = os.Stat(path); err == nil { - if err = os.Remove(path); err != nil { - return err - } - fmt.Println("deleted old .alfredworkflow file") - } - - if f, err = os.Create(path); err != nil { - return err - } - defer f.Close() - - w = zip.NewWriter(f) - - err = filepath.Walk("./build", func(path string, fi os.FileInfo, err error) error { - - if err != nil { - return err - } - - if fi.IsDir() { - return nil - } - - var name string - if name, err = filepath.Rel("./build", path); err != nil { - return err - } - - fmt.Printf(" %s (%v)\n", name, fi.Mode().Perm()) - - var ( - f *os.File - zf io.Writer - fh *zip.FileHeader - ) - - fh = &zip.FileHeader{ - Name: name, - Method: zip.Deflate, - } - - fh.SetMode(fi.Mode().Perm()) - - if f, err = os.Open(path); err != nil { - return err - } - defer f.Close() - - if zf, err = w.CreateHeader(fh); err != nil { - return err - } - if _, err = io.Copy(zf, f); err != nil { - return err - } - - return nil - }) - + p, err := build.Export(buildDir, distDir) if err != nil { return err } - if err = w.Close(); err != nil { - return err - } - - fmt.Printf("wrote %s\n", path) - + fmt.Printf("built workflow file %s\n", p) return nil } -var ( - rxAlphaNum = regexp.MustCompile(`[^a-zA-Z0-9.-]+`) - rxMultiDash = regexp.MustCompile(`-+`) -) - -// make s filesystem- and URL-safe. -func slugify(s string) string { - s = fold(s) - s = rxAlphaNum.ReplaceAllString(s, "-") - s = rxMultiDash.ReplaceAllString(s, "-") - return s -} - -var stripper = transform.Chain(norm.NFD, transform.RemoveFunc(isMn)) - -// isMn returns true if rune is a non-spacing mark -func isMn(r rune) bool { - return unicode.Is(unicode.Mn, r) // Mn: non-spacing mark -} - -// fold strips diacritics from string. -func fold(s string) string { - ascii, _, err := transform.String(stripper, s) - if err != nil { - panic(err) - } - return ascii -} - -// Link symlinks ./build directory to Alfred's workflow directory. +// symlink build directory to Alfred's workflow directory func Link() error { mg.Deps(Build) - fmt.Println("linking ./build to workflow directory ...") - target := filepath.Join(workflowDirectory(), BundleID) - // fmt.Printf("target: %s\n", target) + fmt.Printf("linking %s to workflow directory ...\n", buildDir) + target := filepath.Join(info.AlfredWorkflowDir, info.BundleID) if exists(target) { fmt.Println("removing existing workflow ...") @@ -256,34 +120,16 @@ func Link() error { return err } - build, err := filepath.Abs("build") - if err != nil { - return err - } - src, err := filepath.Rel(filepath.Dir(target), build) + src, err := filepath.Abs(buildDir) if err != nil { return err } - - if err := os.Symlink(src, target); err != nil { - return err - } - - fmt.Printf("symlinked workflow to %s\n", target) - - return nil + return build.Symlink(target, src, true) } -// Icons generate icons +// generate icons func Icons() error { - var ( - green = "03ae03" - // blue = "5484f3" - // red = "b00000" - // yellow = "f8ac30" - ) - copies := []struct { src, dest, colour string }{ @@ -308,66 +154,39 @@ func Icons() error { return rotateIcon("./icons/loading.png", []int{15, 30}) } -// Deps ensure dependencies +// download dependencies func Deps() error { mg.Deps(cleanDeps) fmt.Println("downloading deps ...") return mod("download") } -// Clean remove build files -func Clean() { - fmt.Println("cleaning ...") - mg.Deps(cleanBuild, cleanMage) -} - -func cleanDeps() error { - return mod("tidy", "-v") -} +func cleanDeps() error { return mod("tidy", "-v") } -func cleanDir(name string, exclude ...string) error { +// remove build files +func Clean() { mg.Deps(cleanBuild, cleanMage) } - if _, err := os.Stat(name); err != nil { - return nil - } - - infos, err := ioutil.ReadDir(name) - if err != nil { +func cleanBuild() error { + fmt.Printf("cleaning %s ...\n", buildDir) + if err := sh.Rm(buildDir); err != nil { return err } - for _, fi := range infos { - - var match bool - for _, glob := range exclude { - if match, err = doublestar.Match(glob, fi.Name()); err != nil { - return err - } else if match { - break - } - } - - if match { - fmt.Printf("excluded: %s\n", fi.Name()) - continue - } - - p := filepath.Join(name, fi.Name()) - if err := os.RemoveAll(p); err != nil { - return err - } - } - return nil -} - -func cleanBuild() error { - return cleanDir("./build") + return os.MkdirAll(buildDir, 0755) } func cleanMage() error { + fmt.Println("cleaning mage ...") return sh.Run("mage", "-clean") } -// CleanIcons delete all generated icons from ./icons -func CleanIcons() error { - return cleanDir("./icons") -} +// return true if path exists +func exists(path string) bool { + if _, err := os.Stat(path); err != nil { + if os.IsNotExist(err) { + return false + } + panic(err) + } + + return true +} \ No newline at end of file diff --git a/magefile_alfred.go b/magefile_alfred.go deleted file mode 100644 index 589abe4..0000000 --- a/magefile_alfred.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (c) 2019 Dean Jackson -// MIT Licence applies http://opensource.org/licenses/MIT - -// +build mage - -package main - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "strings" - - "howett.net/plist" -) - -var ( - alfredPrefsPath = os.ExpandEnv("${HOME}/Library/Preferences/com.runningwithcrayons.Alfred-Preferences-3.plist") - defaultSyncFolder = os.ExpandEnv("~/Library/Application Support/Alfred 3") - - // Read from info.plist - BundleID string - Version string - Name string - - DataDir string - CacheDir string - WorkflowDir string -) - -func init() { - home := os.ExpandEnv("$HOME") - ip := readInfo() - - BundleID = ip.BundleID - if BundleID == "" { - panic("Bundle ID is unset") - } - - Version = ip.Version - Name = ip.Name - - CacheDir = filepath.Join(home, "Library/Caches/com.runningwithcrayons.Alfred-3/Workflow Data", ip.BundleID) - DataDir = filepath.Join(home, "Library/Application Support/Alfred 3/Workflow Data", ip.BundleID) - WorkflowDir = workflowDirectory() -} - -type infoPlist struct { - BundleID string `plist:"bundleid"` - Version string `plist:"version"` - Name string `plist:"name"` -} - -func syncFolder() string { - - data, err := ioutil.ReadFile(alfredPrefsPath) - if err != nil { - panic(err) - } - - p := struct { - SyncFolder string `plist:"syncfolder"` - }{} - - if _, err := plist.Unmarshal(data, &p); err != nil { - panic(err) - } - - return p.SyncFolder -} - -func workflowDirectory() string { - - dirs := []string{} - - if p := syncFolder(); p != "" { - dirs = append(dirs, p) - } - dirs = append(dirs, defaultSyncFolder) - - for _, p := range dirs { - p = expandPath(filepath.Join(p, "Alfred.alfredpreferences/workflows")) - if _, err := os.Stat(p); err != nil { - fmt.Printf("read %q: %v\n", p, err) - continue - } - return p - } - panic("workflow directory not found") -} - -func readInfo() infoPlist { - data, err := ioutil.ReadFile("info.plist") - if err != nil { - panic(err) - } - - ip := infoPlist{} - if _, err := plist.Unmarshal(data, &ip); err != nil { - panic(err) - } - - return ip -} - -func alfredEnv() map[string]string { - return map[string]string{ - "alfred_workflow_bundleid": BundleID, - "alfred_workflow_version": Version, - "alfred_workflow_name": Name, - "alfred_workflow_cache": CacheDir, - "alfred_workflow_data": DataDir, - "GO111MODULE": "on", // for building - } -} - -// expand ~ and variables in path. -func expandPath(path string) string { - if strings.HasPrefix(path, "~") { - path = "${HOME}" + path[1:] - } - - return os.ExpandEnv(path) -} - -func exists(path string) bool { - if _, err := os.Stat(path); err != nil { - if os.IsNotExist(err) { - return false - } - panic(err) - } - - return true -} diff --git a/scan.go b/scan.go index 637c70f..b0b42e9 100644 --- a/scan.go +++ b/scan.go @@ -9,6 +9,7 @@ package main import ( + "sort" "bufio" "bytes" "fmt" @@ -75,35 +76,18 @@ func NewScanManager(conf *config) *ScanManager { // ScanDue returns true if one or more scanners needs updating. func (sm *ScanManager) ScanDue() bool { - if !wf.Cache.Exists(cacheKey) { return true } - - // age, err := wf.Cache.Age(cacheKey) - // if err != nil { - // log.Printf("[scan] cache empty") - // return true - // } - - // if fi, err := os.Stat(configFile); err == nil { - // if time.Now().Sub(fi.ModTime()) < age { - // log.Printf("[scan] config file has changed") - // return true - // } - // } - if len(sm.dueScanners()) > 0 { log.Printf("[scan] cache expired") return true } - return false } // Scan updates the cached lists of projects. func (sm *ScanManager) Scan() error { - var ( due = map[string]bool{} ins []<-chan string @@ -117,7 +101,6 @@ func (sm *ScanManager) Scan() error { } for name := range sm.Scanners { - if !sm.IsActive(name) { // Clear any cached results if err := wf.Cache.Store(sm.cacheName(name), nil); err != nil { @@ -179,17 +162,14 @@ func (sm *ScanManager) IsDue(name string) bool { // load data from cache. func (sm *ScanManager) scanFromCache(name string) <-chan string { - var ( key = sm.cacheName(name) out = make(chan string) ) go func() { - - defer util.Timed(time.Now(), fmt.Sprintf(`[cache] loaded "%s"`, name)) - defer close(out) + defer util.Timed(time.Now(), fmt.Sprintf(`[cache] loaded "%s"`, name)) if !wf.Cache.Exists(key) { return @@ -214,7 +194,6 @@ func (sm *ScanManager) scanFromCache(name string) <-chan string { } func (sm *ScanManager) dueScanners() []string { - var ( due []string force bool @@ -234,7 +213,6 @@ func (sm *ScanManager) dueScanners() []string { } for name := range sm.Scanners { - if !sm.IsActive(name) { continue } @@ -243,7 +221,6 @@ func (sm *ScanManager) dueScanners() []string { due = append(due, name) } } - return due } @@ -256,18 +233,11 @@ func (sm *ScanManager) cacheName(name string) string { } // Load loads cached Projects. -func (sm *ScanManager) Load() ([]Project, error) { - - var ( - projs []Project - err error - ) - +func (sm *ScanManager) Load() (projects []Project, err error) { if wf.Cache.Exists(cacheKey) { - err = wf.Cache.LoadJSON(cacheKey, &projs) + err = wf.Cache.LoadJSON(cacheKey, &projects) } - - return projs, err + return } // Find files with `mdfind` @@ -275,8 +245,8 @@ type mdfindScanner struct{} func (s *mdfindScanner) Name() string { return "mdfind" } func (s *mdfindScanner) Scan(conf *config) (<-chan string, error) { - cmd := exec.Command("/usr/bin/mdfind", "-name", "*"+fileExtension) - return lineCommand(cmd, s.Name()) + cmd := exec.Command("/usr/bin/mdfind", fmt.Sprintf("kMDItemFSName == '*%s'", fileExtension)) + return lineCommand(cmd, "mdfind") } // Find files with `locate` @@ -285,28 +255,23 @@ type locateScanner struct{} func (s *locateScanner) Name() string { return "locate" } func (s *locateScanner) Scan(conf *config) (<-chan string, error) { cmd := exec.Command("/usr/bin/locate", "*"+fileExtension) - return lineCommand(cmd, s.Name()) + return lineCommand(cmd, "locate") } +// Find files with `find` type findScanner struct{} func (s *findScanner) Name() string { return "find" } func (s *findScanner) Scan(conf *config) (<-chan string, error) { var chs []<-chan string - for _, sp := range conf.SearchPaths { - argv := []string{sp.Path, "-maxdepth", fmt.Sprintf("%d", sp.Depth)} argv = append(argv, "-type", "f", "-name", "*"+fileExtension) - - cmd := exec.Command("/usr/bin/find", argv...) - - ch, err := lineCommand(cmd, fmt.Sprintf("[%s] %s", s.Name(), sp.Path)) + ch, err := lineCommand(exec.Command("/usr/bin/find", argv...), "[find] " + sp.Path) if err != nil { return nil, err } - chs = append(chs, ch) } @@ -322,7 +287,6 @@ func lineCommand(cmd *exec.Cmd, name string) (chan string, error) { ) go func() { - defer close(out) defer util.Timed(time.Now(), fmt.Sprintf("%s scan", name)) @@ -331,13 +295,12 @@ func lineCommand(cmd *exec.Cmd, name string) (chan string, error) { log.Printf("[%s] command failed: %v", name, err) return } - if err := cmd.Start(); err != nil { log.Printf("[%s] command failed: %v", name, err) return } - // Read mdfind output and send it to channel + // Read output and send it to channel scanner := bufio.NewScanner(stdout) for scanner.Scan() { out <- scanner.Text() @@ -345,7 +308,6 @@ func lineCommand(cmd *exec.Cmd, name string) (chan string, error) { if err := scanner.Err(); err != nil { log.Printf("[%s] couldn't parse output: %v", name, err) } - if err != cmd.Wait() { log.Printf("[%s] command failed: %v", name, err) } @@ -395,7 +357,6 @@ func filterNotProject(in <-chan string) <-chan string { func filterNotExist(in <-chan string) <-chan string { return filterMatches(in, func(r string) bool { if _, err := os.Stat(r); err != nil { - // log.Printf("[filter] doesn't exist: %s", p) return true } return false @@ -404,16 +365,11 @@ func filterNotExist(in <-chan string) <-chan string { // Filter files that have already passed through. func filterDupes(in <-chan string) <-chan string { - seen := map[string]bool{} - return filterMatches(in, func(r string) bool { - if seen[r] { - // log.Printf("[filter] duplicate: %s", r.String()) return true } - seen[r] = true return false }) @@ -421,12 +377,9 @@ func filterDupes(in <-chan string) <-chan string { // passes through paths from in to out, ignoring those for which ignore(path) returns true. func filterMatches(in <-chan string, ignore func(r string) bool) <-chan string { - var out = make(chan string) - go func() { defer close(out) - for p := range in { if ignore(p) { continue @@ -440,19 +393,19 @@ func filterMatches(in <-chan string, ignore func(r string) bool) <-chan string { func cacheProjects(key string, in <-chan string) <-chan string { - projs := []string{} - - var out = make(chan string) + var ( + projs = []string{} + out = make(chan string) + ) go func() { - defer close(out) - for p := range in { projs = append(projs, p) out <- p } + sort.Sort(sort.StringSlice(projs)) data := []byte(strings.Join(projs, "\n")) if err := wf.Cache.Store(key, data); err != nil { log.Printf("[cache] error storing %s: %v", key, err) @@ -466,12 +419,10 @@ func cacheProjects(key string, in <-chan string) <-chan string { // Read Sublime/VSCode project files func resultToProject(in <-chan string) <-chan Project { - var out = make(chan Project) go func() { defer close(out) - for p := range in { proj, err := NewProject(p) if err != nil { @@ -493,7 +444,6 @@ func merge(ins ...<-chan string) <-chan string { ) wg.Add(len(ins)) - for _, in := range ins { go func(in <-chan string) { defer wg.Done()