Subject: bug#59388: Open emacsclient file at last line

From: Gregory Heytings <[email-masked]>
Date: Sat, 19 Nov 2022 17:10:02 +0000

>> I don't think there is any option right now to start emacsclient on the 
>> last line of the file, barring adding lots of 9's.
>
> What about the --eval command-line option of emacsclient?
>

It's not very convenient, you'd have to put the filename inside the 
expression.  What do you think of the attached patch?

From c2992ed2da5b0225bec25be82ef868ae835cac89 Mon Sep 17 00:00:00 2001
From: Gregory Heytings <[email-masked]>
Date: Sat, 19 Nov 2022 17:08:10 +0000
Subject: [PATCH] Add option to visit the end of a file.

* lisp/startup.el (command-line-1): Count from the end of the file
when the line number is negative.

* lisp/server.el (server-process-filter): Allow negative line numbers.
(server-goto-line-column): Count from the end of the file when the
line number is negative.

* src/emacs.c (usage_message): Mention the optional '-'.

* lib-src/emacsclient.c (main): Allow negative line numbers.
(print_help_and_exit): Mention the optional '-'.

* doc/emacs/cmdargs.texi (Action Arguments):
* doc/emacs/misc.texi (emacsclient Options):
* doc/man/emacs.1.in:
* doc/man/emacsclient.1:
Document negative line numbers.
---
 doc/emacs/cmdargs.texi |  9 +++++++++
 doc/emacs/misc.texi    |  8 +++++---
 doc/man/emacs.1.in     |  4 +++-
 doc/man/emacsclient.1  |  7 ++++---
 lib-src/emacsclient.c  |  3 ++-
 lisp/server.el         | 11 ++++++++---
 lisp/startup.el        | 15 ++++++++++-----
 src/emacs.c            |  4 ++--
 8 files changed, 43 insertions(+), 18 deletions(-)

diff --git a/doc/emacs/cmdargs.texi b/doc/emacs/cmdargs.texi
index 0f7acd8797..4a7484860a 100644
--- a/doc/emacs/cmdargs.texi
+++ b/doc/emacs/cmdargs.texi
@@ -119,6 +119,15 @@ Action Arguments
 Visit the specified @var{file}, then go to line number @var{linenum}
 and put point at column number @var{columnnum}.
 
+@item +-@var{linenum} @var{file}
+Visit the specified @var{file}, then go to line number @var{linenum}
+in it, counting from the end of the file.
+
+@item +-@var{linenum}:@var{columnnum} @var{file}
+Visit the specified @var{file}, then go to line number @var{linenum},
+counting from the end of the file, and put point at column number
+@var{columnnum}.
+
 @item -l @var{file}
 @opindex -l
 @itemx --load=@var{file}
diff --git a/doc/emacs/misc.texi b/doc/emacs/misc.texi
index 29c0bed19c..240d408a10 100644
--- a/doc/emacs/misc.texi
+++ b/doc/emacs/misc.texi
@@ -2011,10 +2011,12 @@ emacsclient Options
 @end example
 
 @noindent
-The @samp{+@var{line}} or @samp{+@var{line}:@var{column}} arguments
+The @samp{+@var{line}}, @samp{+@var{line}:@var{column}},
+@samp{+-@var{line}} or @samp{+-@var{line}:@var{column}} arguments
 specify line numbers, or line and column numbers, for the next file
-argument.  These behave like the command line arguments for Emacs
-itself.  @xref{Action Arguments}.
+argument.  With a negative line number, the actual line is counted
+from the end of the file.  These behave like the command line
+arguments for Emacs itself.  @xref{Action Arguments}.
 
   The other optional arguments recognized by @command{emacsclient} are
 listed below:
diff --git a/doc/man/emacs.1.in b/doc/man/emacs.1.in
index 7b2b553979..3856c3b1c5 100644
--- a/doc/man/emacs.1.in
+++ b/doc/man/emacs.1.in
@@ -66,11 +66,13 @@ The same as specifying
 .I file
 directly as an argument.
 .TP
-.BI + number
+.BI + \fR[-]\fPnumber
 Go to the line specified by
 .I number
 (do not insert a space between the "+" sign and
 the number).
+With a negative line number, the actual line is counted
+from the end of the file.
 This applies only to the next file specified.
 .TP
 .BI + line:column
diff --git a/doc/man/emacsclient.1 b/doc/man/emacsclient.1
index 83c8a366f8..53af0dad43 100644
--- a/doc/man/emacsclient.1
+++ b/doc/man/emacsclient.1
@@ -50,15 +50,16 @@ If you set the variable "server-window" to a window or a frame, "C-x
 Most options follow the usual GNU command line syntax, with long
 options starting with two dashes ("\-").
 .TP
-.BI + line\fR[\fP\fB:\fPcolumn\fR]\fP
+.BI + \fR[-]\fPline\fR[\fP\fB:\fPcolumn\fR]\fP
 Go to the specified
 .I line
 and
 .IR column .
 A missing
 .I column
-is treated as column 1.
-This option applies only to the next file specified.
+is treated as column 1.  With a negative line number, the actual line
+is counted from the end of the file.  This option applies only to the
+next file specified.
 .TP
 .B \-a, \-\-alternate-editor=COMMAND
 If the Emacs server is not running, run the specified shell command instead.
diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c
index 425db8cfac..948c1a8f0f 100644
--- a/lib-src/emacsclient.c
+++ b/lib-src/emacsclient.c
@@ -674,7 +674,7 @@ print_help_and_exit (void)
   message (false,
 	   "Usage: %s [OPTIONS] FILE...\n%s%s%s", progname, "\
 Tell the Emacs server to visit the specified files.\n\
-Every FILE can be either just a FILENAME or [+LINE[:COLUMN]] FILENAME.\n\
+Every FILE can be either just a FILENAME or [+[-]LINE[:COLUMN]] FILENAME.\n\
 \n\
 The following OPTIONS are accepted:\n\
 -V, --version		Just print version info and return\n\
@@ -2083,6 +2083,7 @@ main (int argc, char **argv)
 	  if (*p == '+')
             {
 	      unsigned char c;
+	      if (*(p + 1) == '-') p++;
 	      do
 		c = *++p;
 	      while (isdigit (c) || c == ':');
diff --git a/lisp/server.el b/lisp/server.el
index 553890ce29..37ccf7b9f4 100644
--- a/lisp/server.el
+++ b/lisp/server.el
@@ -1237,7 +1237,7 @@ server-process-filter
                 ;; -position +LINE[:COLUMN]:  Set point to the given
                 ;;  position in the next file.
                 ("-position"
-                 (if (not (string-match "\\+\\([0-9]+\\)\\(?::\\([0-9]+\\)\\)?"
+                 (if (not (string-match "\\+\\(-?[0-9]+\\)\\(?::\\([0-9]+\\)\\)?"
                                         (car args-left)))
                      (error "Invalid -position command in client args"))
                  (let ((arg (pop args-left)))
@@ -1437,8 +1437,13 @@ server-goto-line-column
   "Move point to the position indicated in LINE-COL.
 LINE-COL should be a pair (LINE . COL)."
   (when line-col
-    (goto-char (point-min))
-    (forward-line (1- (car line-col)))
+    (if (>= (car line-col) 0)
+        (progn
+          (goto-char (point-min))
+          (forward-line (1- (car line-col))))
+      (progn
+        (goto-char (point-max))
+        (forward-line (car line-col))))
     (let ((column-number (cdr line-col)))
       (when (> column-number 0)
         (move-to-column (1- column-number))))))
diff --git a/lisp/startup.el b/lisp/startup.el
index 5e0a47d3f8..7cbd7687ad 100644
--- a/lisp/startup.el
+++ b/lisp/startup.el
@@ -2570,8 +2570,13 @@ command-line-1
                       ;; Put the point at `line':`column' in the file
                       ;; buffer, and reset `line' and `column' to 0.
                       (unless (zerop line)
-                        (goto-char (point-min))
-                        (forward-line (1- line)))
+                        (if (>= line 0)
+                            (progn
+                              (goto-char (point-min))
+                              (forward-line (1- line)))
+                          (progn
+                            (goto-char (point-max))
+                            (forward-line line))))
                       (setq line 0)
                       (unless (< column 1)
                         (move-to-column (1- column)))
@@ -2718,10 +2723,10 @@ command-line-1
                     ((equal argi "-no-desktop")
                      (message "\"--no-desktop\" ignored because the Desktop package is not loaded"))
 
-                    ((string-match "^\\+[0-9]+\\'" argi)
-                     (setq line (string-to-number argi)))
+                    ((string-match "^\\+\\(-?[0-9]+\\)\\'" argi)
+                     (setq line (string-to-number (match-string 1 argi))))
 
-                    ((string-match "^\\+\\([0-9]+\\):\\([0-9]+\\)\\'" argi)
+                    ((string-match "^\\+\\(-?[0-9]+\\):\\([0-9]+\\)\\'" argi)
                      (setq line (string-to-number (match-string 1 argi))
                            column (string-to-number (match-string 2 argi))))
 
diff --git a/src/emacs.c b/src/emacs.c
index 85102acd28..86264df7a9 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -308,8 +308,8 @@ #define MAIN_PROGRAM
 Action options:\n\
 \n\
 FILE                    visit FILE\n\
-+LINE                   go to line LINE in next FILE\n\
-+LINE:COLUMN            go to line LINE, column COLUMN, in next FILE\n\
++[-]LINE                go to line LINE in next FILE\n\
++[-]LINE:COLUMN         go to line LINE, column COLUMN, in next FILE\n\
 --directory, -L DIR     prepend DIR to load-path (with :DIR, append DIR)\n\
 --eval EXPR             evaluate Emacs Lisp expression EXPR\n\
 --execute EXPR          evaluate Emacs Lisp expression EXPR\n\
-- 
2.35.1