help-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: How to write the "interactive" form for a command acting on a region


From: Pascal J. Bourguignon
Subject: Re: How to write the "interactive" form for a command acting on a region
Date: Wed, 14 Jan 2015 03:04:41 +0100
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.3 (gnu/linux)

Marcin Borkowski <mbork@wmi.amu.edu.pl> writes:

> On 2015-01-13, at 23:38, Pascal J. Bourguignon <pjb@informatimago.com> wrote:
>
>> If you want a different behavior, then you should have different
>> functions:
>
> Why?  In many Emacs functions/commands it works like what I want to
> have.  What's wrong with this approach?  And in fact, I /don't/ want
> different behavior: I want both the function and the command to
> (essentially) do the same, with the (minor) difference that the function
> will return a value and the command will print a message.

Indeed, the question is how you define _different_ behavior, and whether
you really have a different behavior or not.  

Usually you need just to be able to run the function interactively
easily. (You can always use M-: to call a function interactively, eg.:

        M-: (my-function (point-min) (point-max)) RET

if you don't have a command to do that.


 
>> Therefore interactive "r" will always give you start and end points.
>> You could have a command such as:
>>
>>     (defun my-command (start end)
>>        (interactive "r")
>>        (if (use-region-p) ; region is active
>>           (my-function start end)
>>           (my-function (point-min) (point-max))))
>
> This does not seem very lispy to me, though most probably have much less
> experience than you...

Notice how short this command is (it's always good to write short
functions and commands).

But more importantly, I feel that emacs lisp code would benefit in using
a more abstracted and layered approach.

The requirements of a functional API are not the same as of a user
interface command set.  

This is why you could want to define a set of data structures and
functions,  and later add a command layer calling those functions, with
variants such as in this my-command example.  We could assume that
my-function could be used also by other functions or other commands.

In any case, if the code of my-function was put inside my-command,
instead of being factored out, it would become much harder to use
my-command from other functions or commands (perhaps those other
commands or functions really want to work on the whole buffer even when
there's an active region).

When you see a command in emacs that has hundreds of lines of code (and
where the best "abstraction" they can do is:

    (defun bad-command (…)
       (interactive …)
       … hundreds of lines …
       (bad-command-1 …))

    (defun bad-command-1 (…)
       … hundreds of lines …
       (bad-command-2 …))

    (defun bad-command-2 (…)
       … hundreds of lines …)

and there are more than one such bad examples :-(, well, this is not
good at all.


>> Otherwise, if the behavior of your command and your function was the
>> same, you could write a single command, using (require 'cl) to deal with
>> the default values.  
>
> I'll have to check cl (I use it anyway for (incf)), but again: what's
> wrong with (or start (point-min))?

Nothing wrong.  Just that (interactive "r") will never pass nil for
start or end.



Notice that you can also just use (interactive) and (region-beginning) and
(region-end) inside the command.  But then one might worry about
commands that don't declare with interactive the parameters they use and
require from the user. If you can declare them with interactive, it's better.



>> But since you want to force the arguments when it's called interactively
>> without an active region, you will have to duplicate some code.
>
> This I don't understand.  (Though I /do/ have some duplication, see
> below.)

In this case, the duplication is minime, since it's only the
(setf start (point-min) end (point-max)) in the code, and hidden in the
lambda list.


>> Separating the function and command is probablyh preferable in your
>> situation.
>>
>>     (require 'cl)
>>     (defun* my-command (&optional (start (point-min)) (end (point-max)))
>>        (interactive "r")
>>        (when (and (called-interactively-p)
>>                   (not (use-region-p)))
>>           (setf start (point-min)
>>                 end   (point-max)))
>>        …)
>
> No offence, but this seems plain ugly for me, especially the setf part.

Absolutely.  This works better when you don't need this
called-interactive-p case.


> IMHO, using the (interactive) form to define default arguments is more
> elegant, though of course I also have some duplicate code (point-min and
> point-max appear twice - though for different reasons, so to speak -
> which I don't like).  I can't see why your proposal is better - I would
> prefer to use defun and not defun*, and the Emacs manual says it's
> better to use the interactive form and not called-interactively-p (and
> I can see the reason).

I hope we all do.

-- 
__Pascal Bourguignon__                 http://www.informatimago.com/
“The factory of the future will have only two employees, a man and a
dog. The man will be there to feed the dog. The dog will be there to
keep the man from touching the equipment.” -- Carl Bass CEO Autodesk


reply via email to

[Prev in Thread] Current Thread [Next in Thread]