* Re: How to write the "interactive" form for a command acting on a region [not found] <mailman.17860.1421186716.1147.help-gnu-emacs@gnu.org> @ 2015-01-13 22:38 ` Pascal J. Bourguignon 2015-01-13 23:06 ` Marcin Borkowski [not found] ` <mailman.17861.1421190402.1147.help-gnu-emacs@gnu.org> 0 siblings, 2 replies; 7+ messages in thread From: Pascal J. Bourguignon @ 2015-01-13 22:38 UTC (permalink / raw) To: help-gnu-emacs Marcin Borkowski <mbork@wmi.amu.edu.pl> writes: > Hi all, > > so I want to have a function which should do something on the region. > If no region is active, I want it to act on the whole buffer. If called > from Lisp code, I want to be able to supply "begin" and/or "end" > parameters, which (if nil) should default to (point-min) and > (point-max). Finally, I want my command to behave differently depending > on whether it was called interactively or programmatically. If you want a different behavior, then you should have different functions: (defun my-FUNCTION (…) …) (defun my-COMMAND (…) (interactive …) … (my-function …) …) (defun my-command (start end) (interactive "r") (message "start=%s end=%s" start end)) A region is always defined, whether transient-mark-mode is on or off, and whether the region is active or not. 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)))) 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. But since you want to force the arguments when it's called interactively without an active region, you will have to duplicate some code. 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))) …) -- __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 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: How to write the "interactive" form for a command acting on a region 2015-01-13 22:38 ` How to write the "interactive" form for a command acting on a region Pascal J. Bourguignon @ 2015-01-13 23:06 ` Marcin Borkowski [not found] ` <mailman.17861.1421190402.1147.help-gnu-emacs@gnu.org> 1 sibling, 0 replies; 7+ messages in thread From: Marcin Borkowski @ 2015-01-13 23:06 UTC (permalink / raw) To: help-gnu-emacs On 2015-01-13, at 23:38, Pascal J. Bourguignon <pjb@informatimago.com> wrote: > Marcin Borkowski <mbork@wmi.amu.edu.pl> writes: > >> Hi all, >> >> so I want to have a function which should do something on the region. >> If no region is active, I want it to act on the whole buffer. If called >> from Lisp code, I want to be able to supply "begin" and/or "end" >> parameters, which (if nil) should default to (point-min) and >> (point-max). Finally, I want my command to behave differently depending >> on whether it was called interactively or programmatically. > > 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. > (defun my-FUNCTION (…) > …) > > (defun my-COMMAND (…) > (interactive …) > … > (my-function …) > …) > > (defun my-command (start end) > (interactive "r") > (message "start=%s end=%s" start end)) > > A region is always defined, whether transient-mark-mode is on or off, > and whether the region is active or not. Yes, of course. (Incidentally, I didn't notice your snippet above at first, and just to make sure, I wrote an identical one, differing only in the names of the parameters and the text of the message;-).) > 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... > 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))? > 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.) > 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. 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). Regards, -- Marcin Borkowski http://octd.wmi.amu.edu.pl/en/Marcin_Borkowski Faculty of Mathematics and Computer Science Adam Mickiewicz University ^ permalink raw reply [flat|nested] 7+ messages in thread
[parent not found: <mailman.17861.1421190402.1147.help-gnu-emacs@gnu.org>]
* Re: How to write the "interactive" form for a command acting on a region [not found] ` <mailman.17861.1421190402.1147.help-gnu-emacs@gnu.org> @ 2015-01-14 2:04 ` Pascal J. Bourguignon 2015-01-14 3:49 ` Drew Adams 0 siblings, 1 reply; 7+ messages in thread From: Pascal J. Bourguignon @ 2015-01-14 2:04 UTC (permalink / raw) To: help-gnu-emacs 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 ^ permalink raw reply [flat|nested] 7+ messages in thread
* RE: How to write the "interactive" form for a command acting on a region 2015-01-14 2:04 ` Pascal J. Bourguignon @ 2015-01-14 3:49 ` Drew Adams 0 siblings, 0 replies; 7+ messages in thread From: Drew Adams @ 2015-01-14 3:49 UTC (permalink / raw) To: Pascal J. Bourguignon, help-gnu-emacs > >> (defun my-command (start end) > >> (interactive "r") > >> (if (use-region-p) > >> (my-function start end) > >> (my-function (point-min) (point-max)))) > > 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). It is more likely (typical) that the automatic choice (predefined decision) to use (a) the region when it is active and nonempty, versus (b) the current buffer limits otherwise (i.e., respecting narrowing restrictions), is a behavior difference _only_ for interactive use. And in that (typical) case, the standard, simple approach is to limit that behavior decision to the `interactive' spec. Others have already shown the simple code used to do that. In that way, the body of the command _is_ what you proposed to "factor" out as a separate `my-function' for non-interactive use. Instead of having a command and a function, the typical approach is to just put all of the "command" stuff in the `interactive' spec and then use the one function, interactively or not. Your proposed dilemma ("it would become much harder...") then evaporates. Any non-interactive use of the command simply specifies the limits to use, whether they correspond to the region (active or not), the buffer limits, or anything else. In sum, the typical approach in this common scenario is to define a function that is useful both as a command and more generally (from Lisp). You could even say that this is a main raison d'etre for the `interactive' spec: put all of the logic that pertains only to interactive use in that one place, when possible. That's the factoring that is usually done. > The requirements of a functional API are not the same as > of a user interface command set. Yes, not necessarily the same, and not in general - true. But sometimes (often) the interactive use of a function is a simple specialization of its more general use. When that is the case it can make sense to factor out that difference into an `interactive' spec. --- In fact, there are probably more Emacs functions that it wouldn't hurt to define as commands, IMHO. I've been surprised more than once to find that a function I intended only for Lisp is useful bound to a key in some contexts. ^ permalink raw reply [flat|nested] 7+ messages in thread
* How to write the "interactive" form for a command acting on a region @ 2015-01-13 22:05 Marcin Borkowski 2015-01-13 23:24 ` Nicolas Richard 0 siblings, 1 reply; 7+ messages in thread From: Marcin Borkowski @ 2015-01-13 22:05 UTC (permalink / raw) To: Help Gnu Emacs mailing list Hi all, so I want to have a function which should do something on the region. If no region is active, I want it to act on the whole buffer. If called from Lisp code, I want to be able to supply "begin" and/or "end" parameters, which (if nil) should default to (point-min) and (point-max). Finally, I want my command to behave differently depending on whether it was called interactively or programmatically. I did some RTFMing, and after a few iterations I came up with this: (defun my-function (&optional begin end print-message) "Do something clever on region or buffer." (interactive (if (use-region-p) (list (region-beginning) (region-end) t) (list (point-min) (point-max) t))) (save-excursion (save-restriction (narrow-to-region (or begin (point-min)) (or end (point-max))) (let ((result)) (ding) ; do something clever here (if print-message (message "Result: %s." result) result))))) I'm wondering whether it can be made better? Regards, -- Marcin Borkowski This email was proudly sent http://mbork.pl from my Emacs. ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: How to write the "interactive" form for a command acting on a region 2015-01-13 22:05 Marcin Borkowski @ 2015-01-13 23:24 ` Nicolas Richard 2015-01-13 23:35 ` Marcin Borkowski 0 siblings, 1 reply; 7+ messages in thread From: Nicolas Richard @ 2015-01-13 23:24 UTC (permalink / raw) To: Marcin Borkowski; +Cc: Help Gnu Emacs mailing list Hello, Marcin Borkowski <mbork@wmi.amu.edu.pl> writes: > (if (use-region-p) > (list (region-beginning) (region-end) t) > (list (point-min) (point-max) t))) You could do (if (use-region-p) (list (region-beginning) (region-end) t) (list nil nil t)) (naive suggestion -- I have no experience !) -- Nicolas Richard ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: How to write the "interactive" form for a command acting on a region 2015-01-13 23:24 ` Nicolas Richard @ 2015-01-13 23:35 ` Marcin Borkowski 0 siblings, 0 replies; 7+ messages in thread From: Marcin Borkowski @ 2015-01-13 23:35 UTC (permalink / raw) To: Help Gnu Emacs mailing list On 2015-01-14, at 00:24, Nicolas Richard <theonewiththeevillook@yahoo.fr> wrote: > Hello, > > > Marcin Borkowski <mbork@wmi.amu.edu.pl> writes: >> (if (use-region-p) >> (list (region-beginning) (region-end) t) (list (point-min) >> (point-max) t))) > > You could do (if (use-region-p) > (list (region-beginning) (region-end) t) (list nil nil t)) > > (naive suggestion -- I have no experience !) Ah, this looks nice! Seems a bit artificial, but defers the binding of the default values to the one and only one place, and hence avoids duplication. Thanks, I like it! Best, -- Marcin Borkowski This email was proudly sent http://mbork.pl from my Emacs. ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2015-01-14 3:49 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- [not found] <mailman.17860.1421186716.1147.help-gnu-emacs@gnu.org> 2015-01-13 22:38 ` How to write the "interactive" form for a command acting on a region Pascal J. Bourguignon 2015-01-13 23:06 ` Marcin Borkowski [not found] ` <mailman.17861.1421190402.1147.help-gnu-emacs@gnu.org> 2015-01-14 2:04 ` Pascal J. Bourguignon 2015-01-14 3:49 ` Drew Adams 2015-01-13 22:05 Marcin Borkowski 2015-01-13 23:24 ` Nicolas Richard 2015-01-13 23:35 ` Marcin Borkowski
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).