From 35e3387e8e0321a724592d5669b4faa06de1d72f Mon Sep 17 00:00:00 2001 From: Sandro Heinzelmann Date: Sun, 2 May 2021 01:06:42 +0200 Subject: [PATCH] Implement main features --- Makefile | 6 +- go.mod | 5 +- go.sum | 15 ++-- main.go | 220 +++++++++++++++++++++++++++++++++++++++++++++++++------ 4 files changed, 212 insertions(+), 34 deletions(-) diff --git a/Makefile b/Makefile index 64e3dd9..acd05e5 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -BASE_VERSION=0.0.1 +BASE_VERSION=0.0.2 REPO_OWNER=sandro-h BUILD_CENTOS_IMAGE_VERSION=0.0.1 BUILD_CENTOS_IMAGE_TAG=ghcr.io/${REPO_OWNER}/snippet-centos-build:${BUILD_CENTOS_IMAGE_VERSION} @@ -75,7 +75,7 @@ print-version: .PHONY: build-all-optimized build-all-optimized: EXTRA_BUILD_ARGS=-ldflags='-s -w' -build-all-optimized: build-linux build-centos # build-windows +build-all-optimized: build-linux build-centos build-windows upx: wget https://github.com/upx/upx/releases/download/v3.96/upx-3.96-amd64_linux.tar.xz @@ -87,4 +87,4 @@ compress-binaries: upx chmod +x snippet snippet-centos upx/upx -q --brute snippet upx/upx -q --brute snippet-centos - # upx/upx -q --brute snippet.exe \ No newline at end of file + upx/upx -q --brute snippet.exe \ No newline at end of file diff --git a/go.mod b/go.mod index 5cb82f7..5beef7c 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,9 @@ module github.com/sandro-h/snippet go 1.16 require ( - fyne.io/fyne v1.4.3 - fyne.io/fyne/v2 v2.0.3 // indirect + fyne.io/fyne/v2 v2.0.3 github.com/go-vgo/robotgo v0.93.1 + github.com/lithammer/fuzzysearch v1.1.2 github.com/robotn/gohook v0.30.5 + gopkg.in/yaml.v2 v2.2.8 ) diff --git a/go.sum b/go.sum index 2803ef9..ea17e81 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -fyne.io/fyne v1.4.3 h1:356CnXCiYrrfaLGsB7qLK3c6ktzyh8WR05v/2RBu51I= -fyne.io/fyne v1.4.3/go.mod h1:8kiPBNSDmuplxs9WnKCkaWYqbcXFy0DeAzwa6PBO9Z8= fyne.io/fyne/v2 v2.0.3 h1:qzd2uLLrAVrNeqnLY44QZCsMxZwjoo1my+lMzHicMXY= fyne.io/fyne/v2 v2.0.3/go.mod h1:nNpgL7sZkDVLraGtQII2ArNRnnl6kHup/KfQRxIhbvs= github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298/go.mod h1:D+QujdIlUNfa0igpNMk6UIvlb6C252URs4yupRUV4lQ= @@ -11,22 +9,20 @@ github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkK 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/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 h1:FDqhDm7pcsLhhWl1QtD8vlzI4mm59llRvNzrFg6/LAA= github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3/go.mod h1:CzM2G82Q9BDUvMTGHnXf/6OExw/Dz2ivDj48nVg7Lg8= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fyne-io/mobile v0.1.2/go.mod h1:/kOrWrZB6sasLbEy2JIvr4arEzQTXBTZGb3Y96yWbHY= github.com/fyne-io/mobile v0.1.3-0.20210412090810-650a3139866a h1:3TAJhl8vXyli0tooKB0vd6gLCyBdWL4QEYbDoJpHEZk= github.com/fyne-io/mobile v0.1.3-0.20210412090810-650a3139866a/go.mod h1:/kOrWrZB6sasLbEy2JIvr4arEzQTXBTZGb3Y96yWbHY= github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 h1:SCYMcCJ89LjRGwEa0tRluNRiMjZHalQZrVrvTbPh+qw= github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200625191551-73d3c3675aa3/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210410170116-ea3d685f79fb h1:T6gaWBvRzJjuOrdCtg8fXXjKai2xSDqWTcKFUPuw8Tw= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210410170116-ea3d685f79fb/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= github.com/go-vgo/robotgo v0.93.1 h1:bSuya586zK6/NpA5+mMPenzw4KlvbWGhLBISzGlEZoM= github.com/go-vgo/robotgo v0.93.1/go.mod h1:HUCwEV5VvBtnX3Vh0EmlAkETEdcJKeHVmAPMmHaqT+A= -github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff h1:W71vTCKoxtdXgnm1ECDFkfQnpdqAO00zzGXLA5yaEX8= @@ -34,16 +30,21 @@ github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBs github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc= github.com/josephspurrier/goversioninfo v0.0.0-20200309025242-14b0ab84c6ca/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +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/lithammer/fuzzysearch v1.1.2 h1:ePUtm14xKxbpCxozcFbIDRtvANxnVnE+RKpJUqkr2gA= +github.com/lithammer/fuzzysearch v1.1.2/go.mod h1:v6tYW/9kpfV6LNcweXdSjQsfCku/1M/oObmSox1fzP8= github.com/lucor/goinfo v0.0.0-20200401173949-526b5363a13a/go.mod h1:ORP3/rB5IsulLEBwQZCJyyV6niqmI7P4EWSmkug+1Ng= github.com/lxn/win v0.0.0-20201111105847-2a20daff6a55 h1:4BxFx5XCtXc+nFtXDGDW+Uu5sPtsAbvPh6RObj3fG9o= github.com/lxn/win v0.0.0-20201111105847-2a20daff6a55/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= github.com/otiai10/gosseract v2.2.1+incompatible h1:Ry5ltVdpdp4LAa2bMjsSJH34XHVOV7XMi41HtzL8X2I= github.com/otiai10/gosseract v2.2.1+incompatible/go.mod h1:XrzWItCzCpFRZ35n3YtVTgq5bLAhFIkascoRo8G32QE= +github.com/otiai10/mint v1.3.0 h1:Ady6MKVezQwHBkGzLFbrsywyp09Ah7rkmfjV3Bcr5uc= github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -104,8 +105,9 @@ golang.org/x/sys v0.0.0-20210104204734-6f8348627aad h1:MCsdmFSdEd4UEa5TKS5JztCRH golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190808195139-e713427fea3f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -114,6 +116,7 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= diff --git a/main.go b/main.go index 03c1a42..49c2eb1 100644 --- a/main.go +++ b/main.go @@ -2,44 +2,218 @@ package main import ( "fmt" + "math" + "os" + "path/filepath" + "sort" + "strings" - "fyne.io/fyne/app" - "fyne.io/fyne/container" - "fyne.io/fyne/widget" + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/app" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/driver/desktop" + "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/widget" "github.com/go-vgo/robotgo" + "github.com/lithammer/fuzzysearch/fuzzy" hook "github.com/robotn/gohook" + "gopkg.in/yaml.v2" ) +type snippet struct { + label string + content string +} + func main() { - fmt.Println("hi!") - // robotgo.TypeStr("Hello World") - go add() + snippets, snippetsText, err := loadSnippets() + if err != nil { + panic(err) + } + a := app.New() - w := a.NewWindow("Hello") + a.Settings().SetTheme(theme.DarkTheme()) + var w fyne.Window + if drv, ok := fyne.CurrentApp().Driver().(desktop.Driver); ok { + w = drv.CreateSplashWindow() + } else { + w = a.NewWindow("") + } + + list, entry := createSearchWidget(snippets, + snippetsText, + func(snippet *snippet) { + w.Hide() + typeSnippet(snippet.content) + }, + func() { + w.Hide() + }, + ) - hello := widget.NewLabel("Hello Fyne!") - w.SetContent(container.NewVBox( - hello, - widget.NewButton("Hi!", func() { - robotgo.MoveMouse(100, 100) - }), - )) + split := container.NewVSplit(entry, list) + split.Offset = 0 + w.SetContent(split) + w.Resize(fyne.NewSize(400, 400)) + w.Canvas().Focus(entry) + w.CenterOnScreen() + + go listenForHotkeys(w) w.ShowAndRun() } -func add() { - fmt.Println("--- Please press ctrl + shift + q to stop hook ---") - robotgo.EventHook(hook.KeyDown, []string{"q", "ctrl", "shift"}, func(e hook.Event) { - fmt.Println("ctrl-shift-q") - robotgo.EventEnd() - }) +func loadSnippets() ([]*snippet, []string, error) { + dir, _ := filepath.Abs(filepath.Dir(os.Args[0])) + bytes, err := os.ReadFile(filepath.Join(dir, "snippets.yml")) + if err != nil { + return nil, nil, err + } + + var rawSnippets map[string]string + err = yaml.Unmarshal(bytes, &rawSnippets) + if err != nil { + return nil, nil, err + } + + var snippets []*snippet + var snippetsText []string + for k, v := range rawSnippets { + snippets = append(snippets, &snippet{ + label: k, + content: v, + }) + snippetsText = append(snippetsText, fmt.Sprintf("%s: %s", k, v)) + } + return snippets, snippetsText, nil +} + +func createSearchWidget(snippets []*snippet, snippetsText []string, onSubmit func(snippet *snippet), onCancel func()) (*widget.List, *snippetEntry) { + filteredSnippets := snippets + + list := widget.NewList( + func() int { + return len(filteredSnippets) + }, + func() fyne.CanvasObject { + label := widget.NewLabel("tmpl lbl") + label.TextStyle.Bold = true + content := widget.NewLabel("tmpl content") + content.Wrapping = fyne.TextWrapBreak + return container.NewHBox(label, content) + }, + func(id widget.ListItemID, item fyne.CanvasObject) { + container := item.(*fyne.Container) + label := container.Objects[0].(*widget.Label) + content := container.Objects[1].(*widget.Label) + label.SetText(filteredSnippets[id].label + ":") + content.SetText(strings.ReplaceAll(filteredSnippets[id].content, "\n", "\\n")) + ellipsis(container, content) + }, + ) + + var selectedID widget.ListItemID = -1 + list.OnSelected = func(id widget.ListItemID) { + selectedID = id + } + list.Select(0) + + entry := newSnippetEntry() + + resetSearch := func() { + entry.Text = "" + entry.OnChanged(entry.Text) + list.Select(0) + } - fmt.Println("--- Please press w---") - robotgo.EventHook(hook.KeyDown, []string{"w"}, func(e hook.Event) { - fmt.Println("omg w was pressed") + entry.onTypedKey = func(key *fyne.KeyEvent) { + if key.Name == "Down" { + list.Select((selectedID + 1) % len(filteredSnippets)) + } else if key.Name == "Up" { + list.Select((len(filteredSnippets) + selectedID - 1) % len(filteredSnippets)) + } else if key.Name == "Return" { + if selectedID >= 0 && selectedID < len(filteredSnippets) { + onSubmit(filteredSnippets[selectedID]) + resetSearch() + } + } else if key.Name == "Escape" { + onCancel() + resetSearch() + } + } + entry.OnChanged = func(s string) { + ranked := fuzzy.RankFindFold(s, snippetsText) + sort.Sort(ranked) + filteredSnippets = make([]*snippet, 0) + for _, r := range ranked { + filteredSnippets = append(filteredSnippets, snippets[r.OriginalIndex]) + } + list.Refresh() + list.Select(0) + } + + return list, entry +} + +func listenForHotkeys(w fyne.Window) { + robotgo.EventHook(hook.KeyDown, []string{"q", "alt"}, func(e hook.Event) { + w.Show() + // robotgo.EventEnd() }) s := robotgo.EventStart() <-robotgo.EventProcess(s) } + +func typeSnippet(content string) { + lines := strings.Split(content, "\n") + first := true + for _, l := range lines { + if !first { + robotgo.MicroSleep(100) + robotgo.KeyTap("enter") + } + robotgo.TypeStr(l) + first = false + } +} + +type snippetEntry struct { + widget.Entry + onTypedKey func(key *fyne.KeyEvent) +} + +func newSnippetEntry() *snippetEntry { + e := &snippetEntry{} + e.ExtendBaseWidget(e) + return e +} + +func (e *snippetEntry) TypedKey(key *fyne.KeyEvent) { + e.Entry.TypedKey(key) + if e.onTypedKey != nil { + e.onTypedKey(key) + } +} + +func ellipsis(container *fyne.Container, label *widget.Label) { + w := fyne.MeasureText(string(label.Text), theme.TextSize(), label.TextStyle).Width + if label.Position().X+w > container.Size().Width { + wellipsis := fyne.MeasureText(string("..."), theme.TextSize(), label.TextStyle).Width + wmax := container.Size().Width - wellipsis + wpc := float64(w) / float64(len(label.Text)) + k := 0 + for label.Position().X+w > wmax { + overlap := label.Position().X + w - wmax + overlapc := int(math.Ceil(math.Max(1, float64(overlap)/wpc))) + if overlapc > len(label.Text) { + break + } + label.Text = label.Text[:len(label.Text)-overlapc] + w = fyne.MeasureText(string(label.Text), theme.TextSize(), label.TextStyle).Width + k++ + } + label.Text += "..." + label.Refresh() + } +}