[gnu.emacs] Minor modes

mesard@bbn.com (Wayne Mesard) (02/08/89)

[[[Note followup-to line!]]]

My line-number article [specifically my claim that minor modes aren't
doable in GNU Elisp] prompted a discussion about minor modes which I was
prepared to carry on via email, but jr suggested that others on this
list/group/entity might want to think on it a bit, so here is the reply
I sent to tale's posting to comp.emacs.

----
To: tale@pawl.rpi.edu
Subject: Re: line numbers in emacs
In-Reply-To: <TALE.89Feb6153713@consult1.its.rpi.edu>
References: <22@euteal.UUCP> <35659@bbn.COM>
Organization: Bolt Beranek and Newman Inc., Cambridge MA
Date: Tue, 07 Feb 89 14:09:53 -0500
From: mesard@bbn.com

In article <TALE.89Feb6153713@consult1.its.rpi.edu> you write:
>Whoa!  Hang on ... that's just not so.  You don't have to redefine
>entire keymaps or anything.  Whoa.
> 
>Heck, auto-fill can be written in Elisp.  Overwrite could too, though
>it's not as nice and pretty as in C.

Auto fill IS mostly written in lisp.  But it wouldn't do much without
the support for auto-fill-hook in the C code.

> More to the point though, minor
> modes can most certainly be written in Elisp.

Not without rebinding keys and/or putting hooks on functions.  To my
mind, once you start doing that, you're writing a major mode.  [Can you
think of an existing minor mode that rebinds keys?]

Now I know that you can have your minor mode function call the function
that the user really asked for (cf. Mark Weissman's hooks.el code).  As
I mentioned in my article, this was the first path I took in writing
display-line-nums.  And it can be done.  But the code would be ugly and
cumbersome given that you need to meet the following constraints:

    - possibly bind it to any or every key and command.
    - have the asked-for function (i.e. the major mode function called
      with the correct arguments.
    - do the right thing with other "minor modes" that are also
      rebinding keys and putting hooks on functions.
    - then there's subtler issues like  what happens when the user
      types "C-h c C-v" and Emacs replies "Waynes-minor-mode-function".
      [But now, I'm picking at nits.]


Try it.  Maybe you can get it to work.  But if you did I'd say you'd
done it in spite of Emacs, not because of its superb support of user
programmable minor modes.

So I guess I should have said that there's no clean way to do minor
modes.                                        ^^^^^

What would be adequate support of minor modes?  Well, lessee, I hadn't
given it much thought (since I didn't anticipate this discussion).  But
off the top of my head how 'bout something like [possibly argumentless]
hooks which can be attached any function.  Or maybe an "event
specification" system, so you can say things like:

  (register-event-func 'maybe-wrap '(> (current-column) fill-column))
  (register-event-func 'line-nums 'window-movedP) ;window-movedP is a function.

In fact, lets just keep 'em on an alist a la auto-mode-alist.  The
conditional form gets evaluated periodically (once each time through the
body of command_loop() ?), and if non-nil, the other form is evaluated.

Note that there are two types of minor modes.  Those which respond to
events, and those which modify the behavior of the major mode (like
overwrite).  This scheme only addresses the former.  The latter has to
be done with hooks, or by explicitly including code in the functions
which implement the to-be-modified behavior.
-- 
void Wayne_Mesard();
Mesard@BBN.COM             Edith Keillor died for your sins.
BBN, Cambridge, MA

jr@bbn.com (John Robinson) (02/08/89)

In article <35717@bbn.COM>, mesard@bbn (Wayne Mesard) writes:
>					    Or maybe an "event
>specification" system, so you can say things like:
>
>  (register-event-func 'maybe-wrap '(> (current-column) fill-column))
>  (register-event-func 'line-nums 'window-movedP) ;window-movedP is a function.
>
>In fact, lets just keep 'em on an alist a la auto-mode-alist.  The
>conditional form gets evaluated periodically (once each time through the
>body of command_loop() ?), and if non-nil, the other form is evaluated.

So emacs should be a blackboard system in addition to everything else?
I love it!  Slow?  maybe...  Could it run before each redisplay call
(no more unprocessed characters in the input buffer)?

On line numbers - I seem to remember back in the cowebs somewhere -
was it BBN's PEN editor, or maybe ITS EMACS? - there was a way to link
two windows together so that if one scrolled, the other would too.
This might be a cooler way to get line numbers to work; helpful for
some types of file compares too.  

Another kind of link might allow one window to grow while the next one
shrank, and if there were "window-local" variables, we might have a
way to do "sections" discussed here recently.  Particularly if the
mode line were optional...

One of Wayne's event checks could detect when the growing one reached
a section end and open the next "window" section.

Just inventing on the fly, that's all, thinking out loud.
--
/jr
jr@bbn.com or bbn!jr

liberte@m.cs.uiuc.edu (02/09/89)

I had posted a note about minor modes in bug-gnu-emacs.  That
note follows.  Stallman's response was that minor modes for
character insertion are an unsolved problem.  (I wrote some
change-hooks that are called before or after each change to a
buffer.  This solves most of the problems, but may introduce
garbage collection problems that I have to look into.)
The last solution below looks like it would be too slow.

Dan LaLiberte
uiucdcs!liberte
liberte@cs.uiuc.edu
liberte%a.cs.uiuc.edu@uiucvmd.bitnet
======================================

I've been revising the elisp manual section on minor modes, and
I have come to the conclusion that there are no simple ways
to implement (in Lisp) minor modes that affect character insertion
commands.

It does not work to substitute a lisp function for self-insert-command
since that function is called directly, or simulated, in the command loop.

It does not work to use substitute-key-definition to replace the
self-insert-command with a differently named lisp function because
another minor mode might wish to do the same, or the major mode might
have already done that.  A minor mode could replace bindings for
a set of keys, but the major mode might be doing something completely
different with them.

Even if we could catch all self-inserts, we wouldnt catch insertions
(or deletions for that matter) caused by other commands.  The
change-hooks are needed for that.

But there is a general problem with minor modes implemented by
changing the keymap.  To reliably deactivate several such minor modes
(so the keymaps are restored to the original), the user must
deactivate them in the inverse order they were activated.  Here is a
bizarre scheme to handle this: every minor mode that needs to be
automatically deactivated should wrap an unwind-protect around a catch
around a recursive-edit.  Then to deactivate a mode farther up the
stack, a throw is done with the corresponding tag and any intermediate
minor modes would be able to automatically deactivate themselves.

An example of this kind of minor mode is Leif mode.  Leif mode is a
rather large minor mode that remembers the keymap it was started with
and restores to that when deactivated.  It is rather tricky to make
Leif mode work with vip-mode too.

Here is another scheme to avoid the above problems.  Replace each key
binding with a function that first calls your-minor-mode-hook, if it
is non-nil, and then calls the original function, or whatever is
appropriate.  When a minor mode is deactivated, the hook is simply
niled out so the function behaves as the original did.  When
reactivated, the minor mode should test whether the hook is already
in place, by testing a global variable.  This scheme would not work
well with minor modes that replace keymaps (or entries) when
deactivated because they would restore the keymap to use the
original function rather than the hooked function.

Any other ideas?

mende@athos.rutgers.edu (Bob Mende Pie) (02/09/89)

what is needed for minor modes (in my opion) is an alist.  It's contents will
be run thru for each character typed (a minor mode really does have to do
this).  If you had somthing like:
	(setq minor-mode-alist
		'( auto-fill . do-auto-fill
		   auto-spell . do-auto-spell
		   abbrev-mode . do-abbrev))
this would give the name of the minor mode (is this even nessassary) and
the lisp function to do it.   In the case of some things like auto-fill
where the code is written in C, you could have the lisp code call the C
routine.  

A while ago when I brought up the subject of minor modes, someone suggested
that you should need a check both before the character is typed and after
the character is typed.   You could emulate the "before character" version
with the after character version.   I think that most minor modes want to
work after.  While two lists would be more flexable, one list would be
faster.



					/Bob...
--