Subject: bug#62892: proposal to extend mark-sexp to go forward and backward on command

From: Eli Zaretskii <[email-masked]>
Date: Thu, 27 Apr 2023 15:25:59 +0300

> From: Zachary Kanfer <[email-masked]>
> Date: Wed, 26 Apr 2023 22:37:58 -0400
> Cc: [email-masked], [email-masked], [email-masked], 
> 	[email-masked]
> 
> > > Worse still is the next part:
> > >
> > > > Interactively, if this command is repeated
> > > > or (in Transient Mark mode) if the mark is active,
> > > > it marks the next ARG sexps after the ones already marked.
> > >
> > > This says it marks sexps *after* the ones already marked. This is incorrect; if point is *after* mark,
> and
> > > mark is active, this function marks sexps *before* the ones already marked.
> >
> > I cannot reproduce this behavior, if I understand your description
> > correctly.  Please show a recipe, starting from "emacs -Q", to
> > reproduce.
> 
> Open Emacs with:
> 
> emacs -Q --eval '(progn (insert "foo bar baz") (goto-char 8) (set-mark 5))'
> 
> It will insert "foo bar baz", put point after "bar", and mark before "bar". Region contains "bar".
> 
> Then when we run mark-sexp, we expect to mark some sexps "after the ones already marked". That's
> what the docstring says.
> 
> After running the command above to open emacs, press C-M-@ to run mark-sexp. We should see
> sexps marked "after the ones already marked"; that is, we should see "bar baz" marked. But we don't.
> Instead, we see that "foo bar" is highlighted. That is, we have marked sexps *before* the ones already
> marked.
> 
> > > The documentation for mark-sexp says:
> > >
> > > > Set mark ARG sexps from point.
> > > > The place mark goes is the same place C-M-<right> would
> > > > move to with the same argument.
> > >
> > > This is correct but misleading.
> >
> > Please elaborate: how could it mislead?
> 
> "The same argument" is the misleading part. If I call mark-sexp with no argument, sometimes that code
> passes -1 to forward-sexp. So while I think I'm effectively passing a "1" to mark-sexp, instead -1 is
> passed to forward-sexp. This is somewhat explained by the next part of the docstring, but not entirely.
> Read with the rest of the docstring, it reinforces the belief that the command only marks sexps *after*
> point.

Thanks.

Yes, we have a small mess on our hands here.  Over the years, as
features were added to this command, the doc string became more and
more inaccurate.  Moreover, the behavior itself is somewhat
inconsistent: AFAIK this is the only command that accepts a numeric
argument and yet behaves differently when invoked without an argument
vs with "C-u 1".

I tried to describe the behavior in the doc string as follows:

  (defun mark-sexp (&optional arg allow-extend)
    "Set mark ARG sexps from point or move mark one sexp.
  When called from Lisp with ALLOW-EXTEND ommitted or nil, mark is
  set ARG sexps from point; ARG defaults to 1.
  With ALLOW-EXTEND non-nil (interactively, with prefix argument),
  the place mark goes is the same place \\[forward-sexp] would move
  with the same value of ARG; if the mark is active, it moves ARG
  sexps from its current position, otherwise it is set ARG sexps
  from point; ARG defaults to 1.
  When invoked interactively without a prefix argument and no active
  region, mark moves one sexp forward.
  When invoked interactively without a prefix argument, and region
  is active, mark moves one sexp away of point (i.e., forward
  if mark is at or after point, back if mark is before point), thus
  extending the region by one sexp.  Since the direction of region
  extension depends on the relative position of mark and point, you
  can change the direction by \\[exchange-point-and-mark].
  This command assumes point is not in a string or comment."

It is still somewhat complicated and confusing, but at least it's
accurate, I think.