Support hyperlinks with capture-pane -e and add a mouse_hyperlink
authornicm <nicm@openbsd.org>
Wed, 6 Jul 2022 07:36:36 +0000 (07:36 +0000)
committernicm <nicm@openbsd.org>
Wed, 6 Jul 2022 07:36:36 +0000 (07:36 +0000)
format, GitHub issue 3247 from Jeff Chiang.

usr.bin/tmux/cmd-capture-pane.c
usr.bin/tmux/format.c
usr.bin/tmux/grid-view.c
usr.bin/tmux/grid.c
usr.bin/tmux/hyperlinks.c
usr.bin/tmux/key-bindings.c
usr.bin/tmux/tmux.1
usr.bin/tmux/tmux.h
usr.bin/tmux/tty.c

index 08ec85a..dd88bbd 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-capture-pane.c,v 1.57 2022/06/30 09:55:53 nicm Exp $ */
+/* $OpenBSD: cmd-capture-pane.c,v 1.58 2022/07/06 07:36:36 nicm Exp $ */
 
 /*
  * Copyright (c) 2009 Jonathan Alvarado <radobobo@users.sourceforge.net>
@@ -177,7 +177,7 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
        buf = NULL;
        for (i = top; i <= bottom; i++) {
                line = grid_string_cells(gd, 0, i, sx, &gc, with_codes,
-                   escape_c0, !join_lines && !no_trim);
+                   escape_c0, !join_lines && !no_trim, wp->screen);
                linelen = strlen(line);
 
                buf = cmd_capture_pane_append(buf, len, line, linelen);
index 664a217..201f133 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: format.c,v 1.307 2022/06/27 09:16:54 nicm Exp $ */
+/* $OpenBSD: format.c,v 1.308 2022/07/06 07:36:36 nicm Exp $ */
 
 /*
  * Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1145,6 +1145,25 @@ format_cb_mouse_word(struct format_tree *ft)
        return (format_grid_word(gd, x, gd->hsize + y));
 }
 
+/* Callback for mouse_hyperlink. */
+static void *
+format_cb_mouse_hyperlink(struct format_tree *ft)
+{
+       struct window_pane      *wp;
+       struct grid             *gd;
+       u_int                    x, y;
+
+       if (!ft->m.valid)
+               return (NULL);
+       wp = cmd_mouse_pane(&ft->m, NULL, NULL);
+       if (wp == NULL)
+               return (NULL);
+       if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0)
+               return (NULL);
+       gd = wp->base.grid;
+       return (format_grid_hyperlink(gd, x, gd->hsize + y, wp->screen));
+}
+
 /* Callback for mouse_line. */
 static void *
 format_cb_mouse_line(struct format_tree *ft)
@@ -2789,6 +2808,9 @@ static const struct format_table_entry format_table[] = {
        { "mouse_button_flag", FORMAT_TABLE_STRING,
          format_cb_mouse_button_flag
        },
+       { "mouse_hyperlink", FORMAT_TABLE_STRING,
+         format_cb_mouse_hyperlink
+       },
        { "mouse_line", FORMAT_TABLE_STRING,
          format_cb_mouse_line
        },
@@ -5064,3 +5086,20 @@ format_grid_line(struct grid *gd, u_int y)
        }
        return (s);
 }
+
+/* Return hyperlink at given coordinates. Caller frees. */
+char *
+format_grid_hyperlink(struct grid *gd, u_int x, u_int y, struct screen* s)
+{
+       const char              *uri;
+       struct grid_cell         gc;
+
+       grid_get_cell(gd, x, y, &gc);
+       if (gc.flags & GRID_FLAG_PADDING)
+               return (NULL);
+       if (s->hyperlinks == NULL || gc.link == 0)
+               return (NULL);
+       if (!hyperlinks_get(s->hyperlinks, gc.link, &uri, NULL, NULL))
+               return (NULL);
+       return (xstrdup(uri));
+}
index 23beae8..053b104 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: grid-view.c,v 1.34 2020/06/02 20:51:46 nicm Exp $ */
+/* $OpenBSD: grid-view.c,v 1.35 2022/07/06 07:36:36 nicm Exp $ */
 
 /*
  * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -231,5 +231,5 @@ grid_view_string_cells(struct grid *gd, u_int px, u_int py, u_int nx)
        px = grid_view_x(gd, px);
        py = grid_view_y(gd, py);
 
-       return (grid_string_cells(gd, px, py, nx, NULL, 0, 0, 0));
+       return (grid_string_cells(gd, px, py, nx, NULL, 0, 0, 0, NULL));
 }
index 3658fcc..a9f276b 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: grid.c,v 1.125 2022/06/30 09:55:53 nicm Exp $ */
+/* $OpenBSD: grid.c,v 1.126 2022/07/06 07:36:36 nicm Exp $ */
 
 /*
  * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -885,18 +885,47 @@ grid_string_cells_add_code(char *buf, size_t len, u_int n, int *s, int *newc,
        }
 }
 
+static int
+grid_string_cells_add_hyperlink(char *buf, size_t len, const char *id,
+    const char *uri, int escape_c0)
+{
+       char    *tmp;
+
+       if (strlen(uri) + strlen(id) + 17 >= len)
+               return (0);
+
+       if (escape_c0)
+               strlcat(buf, "\\033]8;", len);
+       else
+               strlcat(buf, "\033]8;", len);
+       if (*id != '\0') {
+               xasprintf(&tmp, "id=%s;", id);
+               strlcat(buf, tmp, len);
+               free(tmp);
+       } else
+               strlcat(buf, ";", len);
+       strlcat(buf, uri, len);
+       if (escape_c0)
+               strlcat(buf, "\\033\\\\", len);
+       else
+               strlcat(buf, "\033\\", len);
+       return (1);
+}
+
 /*
  * Returns ANSI code to set particular attributes (colour, bold and so on)
  * given a current state.
  */
 static void
 grid_string_cells_code(const struct grid_cell *lastgc,
-    const struct grid_cell *gc, char *buf, size_t len, int escape_c0)
+    const struct grid_cell *gc, char *buf, size_t len, int escape_c0,
+    struct screen *sc, int *has_link)
 {
-       int     oldc[64], newc[64], s[128];
-       size_t  noldc, nnewc, n, i;
-       u_int   attr = gc->attr, lastattr = lastgc->attr;
-       char    tmp[64];
+       int                      oldc[64], newc[64], s[128];
+       size_t                   noldc, nnewc, n, i;
+       u_int                    attr = gc->attr, lastattr = lastgc->attr;
+       char                     tmp[64];
+       const char              *uri, *id;
 
        struct {
                u_int   mask;
@@ -986,19 +1015,32 @@ grid_string_cells_code(const struct grid_cell *lastgc,
                else
                        strlcat(buf, "\017", len);  /* SI */
        }
+
+       /* Add hyperlink if changed. */
+       if (sc != NULL && sc->hyperlinks != NULL && lastgc->link != gc->link) {
+               if (hyperlinks_get(sc->hyperlinks, gc->link, &uri, &id, NULL)) {
+                       *has_link = grid_string_cells_add_hyperlink(buf, len,
+                           id, uri, escape_c0);
+               } else if (*has_link) {
+                       grid_string_cells_add_hyperlink(buf, len, "", "",
+                           escape_c0);
+                       *has_link = 0;
+               }
+       }
 }
 
 /* Convert cells into a string. */
 char *
 grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
-    struct grid_cell **lastgc, int with_codes, int escape_c0, int trim)
+    struct grid_cell **lastgc, int with_codes, int escape_c0, int trim,
+    struct screen *s)
 {
        struct grid_cell         gc;
        static struct grid_cell  lastgc1;
        const char              *data;
-       char                    *buf, code[128];
+       char                    *buf, code[8192];
        size_t                   len, off, size, codelen;
-       u_int                    xx;
+       u_int                    xx, has_link = 0;
        const struct grid_line  *gl;
 
        if (lastgc != NULL && *lastgc == NULL) {
@@ -1020,7 +1062,7 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
 
                if (with_codes) {
                        grid_string_cells_code(*lastgc, &gc, code, sizeof code,
-                           escape_c0);
+                           escape_c0, s, &has_link);
                        codelen = strlen(code);
                        memcpy(*lastgc, &gc, sizeof **lastgc);
                } else
@@ -1046,6 +1088,18 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
                off += size;
        }
 
+       if (has_link) {
+               grid_string_cells_add_hyperlink(code, sizeof code, "", "",
+                   escape_c0);
+               codelen = strlen(code);
+               while (len < off + size + codelen + 1) {
+                       buf = xreallocarray(buf, 2, len);
+                       len *= 2;
+               }
+               memcpy(buf + off, code, codelen);
+               off += codelen;
+       }
+
        if (trim) {
                while (off > 0 && buf[off - 1] == ' ')
                        off--;
index 6e336ec..4f2585e 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: hyperlinks.c,v 1.1 2022/06/30 09:55:53 nicm Exp $ */
+/* $OpenBSD: hyperlinks.c,v 1.2 2022/07/06 07:36:36 nicm Exp $ */
 
 /*
  * Copyright (c) 2021 Will <author@will.party>
@@ -179,7 +179,7 @@ hyperlinks_put(struct hyperlinks *hl, const char *uri_in,
 /* Get hyperlink by inner number. */
 int
 hyperlinks_get(struct hyperlinks *hl, u_int inner, const char **uri_out,
-    const char **external_id_out)
+    const char **internal_id_out, const char **external_id_out)
 {
        struct hyperlinks_uri   find, *hlu;
 
@@ -188,7 +188,10 @@ hyperlinks_get(struct hyperlinks *hl, u_int inner, const char **uri_out,
        hlu = RB_FIND(hyperlinks_by_inner_tree, &hl->by_inner, &find);
        if (hlu == NULL)
                return (0);
-       *external_id_out = hlu->external_id;
+       if (internal_id_out != NULL)
+               *internal_id_out = hlu->internal_id;
+       if (external_id_out != NULL)
+               *external_id_out = hlu->external_id;
        *uri_out = hlu->uri;
        return (1);
 }
index fcaf5de..dfb4fad 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: key-bindings.c,v 1.142 2022/02/03 07:26:43 nicm Exp $ */
+/* $OpenBSD: key-bindings.c,v 1.143 2022/07/06 07:36:36 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -54,6 +54,9 @@
        " '#{?mouse_word,Copy #[underscore]#{=/9/...:mouse_word},}' 'c' {copy-mode -q; set-buffer -- \"#{q:mouse_word}\"}" \
        " '#{?mouse_line,Copy Line,}' 'l' {copy-mode -q; set-buffer -- \"#{q:mouse_line}\"}" \
        " ''" \
+       " '#{?mouse_hyperlink,Type #[underscore]#{=/9/...:mouse_hyperlink},}' 'C-h' {copy-mode -q; send-keys -l -- \"#{q:mouse_hyperlink}\"}" \
+       " '#{?mouse_hyperlink,Copy #[underscore]#{=/9/...:mouse_hyperlink},}' 'h' {copy-mode -q; set-buffer -- \"#{q:mouse_hyperlink}\"}" \
+       " ''" \
        " 'Horizontal Split' 'h' {split-window -h}" \
        " 'Vertical Split' 'v' {split-window -v}" \
        " ''" \
index 5ac2a63..f2c8dd1 100644 (file)
@@ -1,4 +1,4 @@
-.\" $OpenBSD: tmux.1,v 1.893 2022/06/30 09:55:53 nicm Exp $
+.\" $OpenBSD: tmux.1,v 1.894 2022/07/06 07:36:36 nicm Exp $
 .\"
 .\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
 .\"
@@ -14,7 +14,7 @@
 .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: June 30 2022 $
+.Dd $Mdocdate: July 6 2022 $
 .Dt TMUX 1
 .Os
 .Sh NAME
@@ -5160,6 +5160,7 @@ The following variables are available, where appropriate:
 .It Li "mouse_all_flag" Ta "" Ta "Pane mouse all flag"
 .It Li "mouse_any_flag" Ta "" Ta "Pane mouse any flag"
 .It Li "mouse_button_flag" Ta "" Ta "Pane mouse button flag"
+.It Li "mouse_hyperlink" Ta "" Ta "Hyperlink under mouse, if any"
 .It Li "mouse_line" Ta "" Ta "Line under mouse, if any"
 .It Li "mouse_sgr_flag" Ta "" Ta "Pane mouse SGR flag"
 .It Li "mouse_standard_flag" Ta "" Ta "Pane mouse standard flag"
index 38a174e..413fb2b 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: tmux.h,v 1.1176 2022/06/30 09:55:53 nicm Exp $ */
+/* $OpenBSD: tmux.h,v 1.1177 2022/07/06 07:36:36 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -2126,6 +2126,8 @@ void               format_defaults_paste_buffer(struct format_tree *,
                     struct paste_buffer *);
 void            format_lost_client(struct client *);
 char           *format_grid_word(struct grid *, u_int, u_int);
+char           *format_grid_hyperlink(struct grid *, u_int, u_int,
+                    struct screen *);
 char           *format_grid_line(struct grid *, u_int);
 
 /* format-draw.c */
@@ -2772,7 +2774,7 @@ void       grid_clear_lines(struct grid *, u_int, u_int, u_int);
 void    grid_move_lines(struct grid *, u_int, u_int, u_int, u_int);
 void    grid_move_cells(struct grid *, u_int, u_int, u_int, u_int, u_int);
 char   *grid_string_cells(struct grid *, u_int, u_int, u_int,
-            struct grid_cell **, int, int, int);
+            struct grid_cell **, int, int, int, struct screen *);
 void    grid_duplicate_lines(struct grid *, u_int, struct grid *, u_int,
             u_int);
 void    grid_reflow(struct grid *, u_int);
@@ -3312,7 +3314,7 @@ uid_t                      server_acl_get_uid(struct server_acl_user *);
 u_int                   hyperlinks_put(struct hyperlinks *, const char *,
                             const char *);
 int                     hyperlinks_get(struct hyperlinks *, u_int,
-                            const char **, const char **);
+                            const char **, const char **, const char **);
 struct hyperlinks      *hyperlinks_init(void);
 void                    hyperlinks_reset(struct hyperlinks *);
 void                    hyperlinks_free(struct hyperlinks *);
index 8da3cec..e029c9c 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: tty.c,v 1.421 2022/06/30 09:55:53 nicm Exp $ */
+/* $OpenBSD: tty.c,v 1.422 2022/07/06 07:36:36 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -2501,7 +2501,7 @@ tty_hyperlink(struct tty *tty, const struct grid_cell *gc,
        if (hl == NULL)
                return;
 
-       if (gc->link == 0 || !hyperlinks_get(hl, gc->link, &uri, &id))
+       if (gc->link == 0 || !hyperlinks_get(hl, gc->link, &uri, NULL, &id))
                tty_putcode_ptr2(tty, TTYC_HLS, "", "");
        else
                tty_putcode_ptr2(tty, TTYC_HLS, id, uri);