From e68540ca3c31bb95f7903902067f2faa70c3c883 Mon Sep 17 00:00:00 2001 From: Liam Galvin Date: Wed, 24 Oct 2018 16:28:49 +0100 Subject: [PATCH 1/2] added url detection and clicking --- buffer/buffer.go | 55 +++++++++++++++++++++++++++++++++++++++++++++--- gui/mouse.go | 33 ++++++++++++++++++++++++++--- 2 files changed, 82 insertions(+), 6 deletions(-) diff --git a/buffer/buffer.go b/buffer/buffer.go index c994eab..3b221d4 100644 --- a/buffer/buffer.go +++ b/buffer/buffer.go @@ -2,6 +2,7 @@ package buffer import ( "fmt" + "net/url" "time" ) @@ -47,6 +48,45 @@ func NewBuffer(viewCols uint16, viewLines uint16, attr CellAttributes) *Buffer { return b } +func (buffer *Buffer) GetURLAtPosition(col uint16, row uint16) string { + + cell := buffer.GetCell(col, row) + if cell == nil || cell.Rune() == 0x00 { + return "" + } + + candidate := "" + + for i := col; i >= 0; i-- { + cell := buffer.GetCell(i, row) + if cell == nil { + break + } + if isRuneURLSelectionMarker(cell.Rune()) { + break + } + candidate = fmt.Sprintf("%c%s", cell.Rune(), candidate) + } + + for i := col + 1; i < buffer.viewWidth; i++ { + cell := buffer.GetCell(i, row) + if cell == nil { + break + } + if isRuneURLSelectionMarker(cell.Rune()) { + break + } + candidate = fmt.Sprintf("%s%c", candidate, cell.Rune()) + } + + // check if url + _, err := url.ParseRequestURI(candidate) + if err != nil { + return "" + } + return candidate +} + func (buffer *Buffer) SelectWordAtPosition(col uint16, row uint16) { cell := buffer.GetCell(col, row) @@ -62,7 +102,7 @@ func (buffer *Buffer) SelectWordAtPosition(col uint16, row uint16) { if cell == nil { break } - if isRuneSelectionMarker(cell.Rune()) { + if isRuneWordSelectionMarker(cell.Rune()) { break } start = i @@ -73,7 +113,7 @@ func (buffer *Buffer) SelectWordAtPosition(col uint16, row uint16) { if cell == nil { break } - if isRuneSelectionMarker(cell.Rune()) { + if isRuneWordSelectionMarker(cell.Rune()) { break } end = i @@ -92,7 +132,7 @@ func (buffer *Buffer) SelectWordAtPosition(col uint16, row uint16) { } // bounds for word selection -func isRuneSelectionMarker(r rune) bool { +func isRuneWordSelectionMarker(r rune) bool { switch r { case ',', ' ', ':', ';', 0, '\'', '"', '[', ']', '(', ')', '{', '}': return true @@ -101,6 +141,15 @@ func isRuneSelectionMarker(r rune) bool { return false } +func isRuneURLSelectionMarker(r rune) bool { + switch r { + case ' ', 0, '\'', '"', '{', '}': + return true + } + + return false +} + func (buffer *Buffer) GetSelectedText() string { if buffer.selectionStart == nil || buffer.selectionEnd == nil { return "" diff --git a/gui/mouse.go b/gui/mouse.go index 11daa14..28e53ef 100644 --- a/gui/mouse.go +++ b/gui/mouse.go @@ -3,6 +3,8 @@ package gui import ( "fmt" "math" + "os/exec" + "runtime" "github.com/go-gl/glfw/v3.2/glfw" "github.com/liamg/aminal/terminal" @@ -10,12 +12,34 @@ import ( func (gui *GUI) mouseMoveCallback(w *glfw.Window, xpos float64, ypos float64) { + px, py := w.GetCursorPos() + x := uint16(math.Floor((px - float64(gui.renderer.areaX)) / float64(gui.renderer.CellWidth()))) + y := uint16(math.Floor((py - float64(gui.renderer.areaY)) / float64(gui.renderer.CellHeight()))) if gui.mouseDown { - px, py := w.GetCursorPos() - x := uint16(math.Floor((px - float64(gui.renderer.areaX)) / float64(gui.renderer.CellWidth()))) - y := uint16(math.Floor((py - float64(gui.renderer.areaY)) / float64(gui.renderer.CellHeight()))) gui.terminal.ActiveBuffer().EndSelection(x, y, false) } + + if url := gui.terminal.ActiveBuffer().GetURLAtPosition(x, y); url != "" { + w.SetCursor(glfw.CreateStandardCursor(glfw.HandCursor)) + } else { + w.SetCursor(glfw.CreateStandardCursor(glfw.ArrowCursor)) + } +} + +func (gui *GUI) launchTarget(target string) { + + cmd := "xdg-open" + + switch runtime.GOOS { + case "darwin": + cmd = "open" + case "windows": + cmd = "start" + } + + if err := exec.Command(cmd, target).Run(); err != nil { + gui.logger.Errorf("Failed to launch external command %s: %s", cmd, err) + } } func (gui *GUI) mouseButtonCallback(w *glfw.Window, button glfw.MouseButton, action glfw.Action, mod glfw.ModifierKey) { @@ -33,6 +57,9 @@ func (gui *GUI) mouseButtonCallback(w *glfw.Window, button glfw.MouseButton, act } else if action == glfw.Release { gui.mouseDown = false gui.terminal.ActiveBuffer().EndSelection(x, y, true) + if url := gui.terminal.ActiveBuffer().GetURLAtPosition(x, y); url != "" { + go gui.launchTarget(url) + } } // https://www.xfree86.org/4.8.0/ctlseqs.html From 2e3751c866c8fee770fff138858ea10456d6a763 Mon Sep 17 00:00:00 2001 From: Liam Galvin Date: Wed, 24 Oct 2018 16:29:44 +0100 Subject: [PATCH 2/2] added url detection and clicking --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 92f1d41..f0ceaf7 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ Ensure you have your latest graphics card drivers installed before use. | Config file | ✔ | | Scrolling | ✔ | | Mouse interaction | ✔ | +| Clickable URLs | ✔ | | Sweet render effects | | ## Keyboard Shortcuts