Skip to content

Instantly share code, notes, and snippets.

@ethanliu
Last active June 4, 2018 12:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ethanliu/083d5bf8794ede49a6a85e99a068b3e0 to your computer and use it in GitHub Desktop.
Save ethanliu/083d5bf8794ede49a6a85e99a068b3e0 to your computer and use it in GitHub Desktop.
/**
*
* Snippets.me TextMate Distributor replacement script
*
* TextMate Distributor is not working after TextMate 2 bundle identifier changed,
* the script remove all exists snippets from TextMate, then add valid snippets.
*
* @author Ethan Liu - https://creativecrap.com
*
* References:
* http://snippets.me/distributors/textmate-distributor.html
* https://github.com/snippets/Snippets-tmbundle
* https://github.com/textmate/textmate/blob/master/Applications/TextMate/about/Changes.md#2016-10-23-v20-beta1226
* https://github.com/mattn/go-sqlite3
* https://github.com/satori/go.uuid
*
*/
package main
import (
"database/sql"
"fmt"
_ "github.com/mattn/go-sqlite3"
"github.com/satori/go.uuid"
"io/ioutil"
"os"
"os/user"
"regexp"
"strings"
)
// var dev bool = true
var deleteBeforeExport bool = true
func init() {
// dev = false
// deleteBeforeExport = true
}
func main() {
user, err := user.Current()
checkError(err)
snippetsDBPath := getSnippetsPath(user.HomeDir)
tmBundlePath := getTextMateBundlePath(user.HomeDir)
// remove cache
tmCachePath := fmt.Sprintf("%s/Library/Caches/com.macromates.TextMate/BundlesIndex.binary", user.HomeDir)
fmt.Sprintf("Remove TextMate bundle cache:\n%s\n\n", tmCachePath)
err = os.Remove(tmCachePath)
// checkError(err)
if err != nil {
fmt.Println(err)
}
if deleteBeforeExport {
files, err := ioutil.ReadDir(tmBundlePath)
checkError(err)
fmt.Printf("Remove exists snippets from bundle:\n%s\n\n", tmBundlePath)
for _, file := range files {
fmt.Println(file.Name())
filePath := fmt.Sprintf("%s/%s", tmBundlePath, file.Name())
err := os.Remove(filePath)
checkError(err)
}
fmt.Printf("\nTotal: %d\n\n", len(files))
} else {
_, err := ioutil.ReadDir(tmBundlePath)
checkError(err)
}
fmt.Println("\nBegan exporting snippets:\n")
exportSnippets(snippetsDBPath, tmBundlePath)
fmt.Println("\nExport finished")
}
func checkError(err error) {
if err != nil {
fmt.Println("Error:")
panic(err)
}
}
func exportSnippets(dbPath string, tmBundlePath string) {
db, err := sql.Open("sqlite3", dbPath)
checkError(err)
rows, err := db.Query("SELECT Key, Name, SourceCode, ExpanderTrigger FROM Snippet WHERE SourceCode != '' AND ExpanderTrigger != '' AND DateRemoved IS NULL ORDER BY Name ASC")
checkError(err)
var count int = 0
var key string
var name string
var sourceCode string
var expanderTrigger string
for rows.Next() {
err = rows.Scan(&key, &name, &sourceCode, &expanderTrigger)
checkError(err)
key := strings.Replace(strings.Trim(key, " "), "/", "_", -1)
path := fmt.Sprintf("%s/me.snippets.distributor.TextMate_%s.tmSnippet", tmBundlePath, key)
uuid := getUUID()
xml := buildXML(name, expanderTrigger, sourceCode, uuid)
_, err := os.Stat(path)
if os.IsNotExist(err) {
// create ifle
_, err := os.Create(path)
checkError(err)
}
file, err := os.OpenFile(path, os.O_RDWR, 0644)
checkError(err)
defer file.Close()
_, err = file.WriteString(xml)
checkError(err)
fmt.Printf("%s: %s\n", key, name)
count++
}
fmt.Printf("\nTotal: %d\n\n", count)
rows.Close()
db.Close()
}
func getUUID() string {
uuid := uuid.Must(uuid.NewV4())
// fmt.Println(uuid)
// return uuid
return strings.ToUpper(fmt.Sprintf("%s", uuid))
}
func getTextMateBundlePath(root string) string {
paths := []string{
fmt.Sprintf("%s/Library/Application Support/TextMate/Bundles/Snippets.tmbundle/Snippets", root),
fmt.Sprintf("%s/Library/Application Support/TextMate/Managed/Bundles/Snippets.tmbundle/Snippets", root),
"./demo/Snippets",
}
for _, path := range paths {
_, err := ioutil.ReadDir(path)
if err == nil {
return path
}
}
panic("Cannot found Snippets.tmbundle path")
return ""
}
func getSnippetsPath(root string) string {
path := fmt.Sprintf("%s/Library/Application Support/Snippets/Snippets.sqlite", root)
return path
}
func buildXML(name string, trigger string, source string, uuid string) string {
xml := `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>content</key>
<string><![CDATA[%s]]></string>
<key>name</key>
<string><![CDATA[%s]]></string>
<key>scope</key>
<string>*</string>
<key>tabTrigger</key>
<string><![CDATA[%s]]></string>
<key>uuid</key>
<string><![CDATA[%s]]></string>
</dict>
</plist>
`
name = strings.Trim(name, " ")
trigger = strings.Trim(trigger, " ")
source = strings.Replace(source, "$", "\\$", -1)
// restore textmate placeholders
re := regexp.MustCompile("\\\\(\\${[0-9]+:?.*?})")
source = re.ReplaceAllString(source, "${1}")
xml = fmt.Sprintf(xml, source, name, trigger, uuid)
return xml
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment