[comp.soft-sys.andrew] Meta-X package

janssen@parc.xerox.com (Bill Janssen) (07/21/90)

Here it is (meta-x) as a shar file:

---- Enclosure ----
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  Imakefile metax.c metax.ch
# Wrapped by janssen@nora on Fri Jul 20 16:05:40 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'Imakefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Imakefile'\"
else
echo shar: Extracting \"'Imakefile'\" \(248 characters\)
sed "s/^X//" >'Imakefile' <<'END_OF_FILE'
XLOCALINCLUDES	= -I/import/local/andrew/include/atk
X
XDOBJS		=	metax.do
X
XIHFILES		=	metax.ih
X
XDESTDIR		=	/import/local/andrew
X
XNormalObjectRule()
XNormalATKRule()
XDependTarget()
X
XDynamicObject(metax,,$(UTILLIB))
XInstallClassFiles($(DOBJS),$(IHFILES))
END_OF_FILE
if test 248 -ne `wc -c <'Imakefile'`; then
    echo shar: \"'Imakefile'\" unpacked with wrong size!
fi
# end of 'Imakefile'
fi
if test -f 'metax.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'metax.c'\"
else
echo shar: Extracting \"'metax.c'\" \(2947 characters\)
sed "s/^X//" >'metax.c' <<'END_OF_FILE'
X/*| -*- Mode: C -*-
X/*|------------------------------------------------------------
X/*|
X/*| File: 	metax.c
X/*| Created: 	Thu Jun  7 19:07:32 1990
X/*| Author: 	Bill Janssen (janssen@holmes) 
X/*| $Locker$
X/*|
X/*|------------------------------------------------------------
X/*|
X/*| Description: 
X/*|
X/*|   Behaviour code to implement meta-x for Andrew.
X/*|
X/*|------------------------------------------------------------
X/*|
X/*| Copyright 1990 Xerox Corporation
X/*| All Rights Reserved Worldwide.
X/*/
X    static char xerox_copyright[] = "\
X    \
X    Copyright 1990 Xerox Corporation \
X    All Rights Reserved";
X/*|------------------------------------------------------------
X/*|
X/*|  "$Header$"
X/*/
X      static char rcs_id[] = "$Header$";
X/*|
X/*|------------------------------------------------------------
X/*/
X
X#include <class.h>
X
X#include <view.ih>
X#include <proctbl.ih>
X#include <message.ih>
X#include <bind.ih>
X#include <im.ih>
X
X#include "metax.eh"
X
Xstatic char lastCommand[1000];
X
Xstatic void callProcedure (self, key)
X     struct view *self;
X     char key;
X{
X  char buf[1000];
X
X  if (message_AskForString (self, 0 , "M-x ", "",
X			    buf, sizeof(buf)) == -1)
X    {
X      message_DisplayString (self, 0, "");
X      return;
X    }
X
X  CallNamedProcedure (self, buf);
X
X  strcpy (lastCommand, buf);
X}
X 
XCallNamedProcedure (self, procname)
X     struct view *self;
X     char *procname;
X{
X  char buf[1000];
X  struct proctable_Entry *pe;
X  int stat;
X
X /* should lookup synonyms here... */
X
X  pe = proctable_Lookup (procname);
X  if (pe == NULL)
X    {
X      message_DisplayString (self, 0, "No such command.");
X      return;
X    }
X  
X  if (pe->proc != NULL
X      && (pe->module == NULL || (class_Load(pe->module) != NULL)))
X      {
X	if (pe->doc != NULL)
X	  message_DisplayString (self, 0, pe->doc);
X	im_ForceUpdate();
X	stat = (*pe->proc)(self, 0);
X	sprintf (buf, "%s returns %d.", pe->name, stat);
X	message_DisplayString (self, 0, buf);
X      };
X}
X
Xstatic void repeatProcedure (view, key)
X     struct view *view;
X     char key;
X{
X  char buf[1000];
X
X  if (*lastCommand != 0)
X    {
X      if (message_AskForString (view, 0 , "M-x ", lastCommand,
X				buf, sizeof(buf)) == -1)
X	{
X	  message_DisplayString (view, 0, "");
X	  return;
X	}
X      
X      CallNamedProcedure (view, buf);
X    }
X  else
X    message_DisplayString (view, 0, "No previous Meta-X command.");
X}
X
Xboolean metax__InitializeClass(classID)
X     struct classheader *classID;
X{
X  static struct bind_Description fns[] = {
X    {"view-call-named-procedure", "\033x", 0, NULL, 0, 0,
callProcedure, "Call the procedure named as an argument", "metax" },
X    {"view-repeat-named-procedure", "\030\033", 0, NULL, 0, 0,
repeatProcedure, "Call the procedure named as an argument", "metax" },
X    NULL };
X  struct classinfo *viewClassinfo;
X
X  viewClassinfo = class_Load("view");
X  if (viewClassinfo != NULL) {
X    bind_BindList(fns, NULL, NULL, viewClassinfo);
X    return TRUE;
X  }
X  else
X    return FALSE;
X}
X
END_OF_FILE
if test 2947 -ne `wc -c <'metax.c'`; then
    echo shar: \"'metax.c'\" unpacked with wrong size!
fi
# end of 'metax.c'
fi
if test -f 'metax.ch' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'metax.ch'\"
else
echo shar: Extracting \"'metax.ch'\" \(717 characters\)
sed "s/^X//" >'metax.ch' <<'END_OF_FILE'
X/*| -*- Mode: C -*-
X/*|------------------------------------------------------------
X/*|
X/*| File: 	metax.ch
X/*| Created: 	Thu Jun  7 19:06:27 1990
X/*| Author: 	Bill Janssen (janssen@holmes) 
X/*| $Locker$
X/*|
X/*|------------------------------------------------------------
X/*|
X/*| Description: 
X/*|
X/*|   Package declaration for meta-x code.
X/*|
X/*|------------------------------------------------------------
X/*|
X/*| Copyright 1990 Xerox Corporation
X/*| All Rights Reserved Worldwide.
X/*|------------------------------------------------------------
X/*|
X/*|  "$Header$"
X/*|
X/*|------------------------------------------------------------
X/*/
X
Xpackage metax {
X
X classprocedures:
X  InitializeClass() returns boolean;
X};
END_OF_FILE
if test 717 -ne `wc -c <'metax.ch'`; then
    echo shar: \"'metax.ch'\" unpacked with wrong size!
fi
# end of 'metax.ch'
fi
echo shar: End of shell archive.
exit 0

---- Enclosure ----

janssen@parc.xerox.com (Bill Janssen) (07/21/90)

Whoops.  I notice the Imakefile defines LOCALINCLUDES.  Just remove that
line, it isn't needed.

Bill

wjh+@ANDREW.CMU.EDU (Fred Hansen) (07/24/90)

The meta-X facility recently posted here will let you call any proctable
function, but has some problems:

	There is no provision for passing an argument to the function.

	There is no completion of the name while typing it in.

I can't offer a solution to the second problem, though someone could
easily write a completion package that inspected that proctable.

To pass arguments you can utilize Ness.  I do this by adding to my
.atkinit the line

	addkey ness-dostmt  \e\e  view  

Then when I type ESC-ESC (or meta-ESC, I suppose) it prompts for Ness
statements.  Any proctable function can be called and arguments passed. 
For instance, to classify a message into a folder (am operation I don't
have in my messages menu) I can type ESC-ESC and give the command:

	messages_classify_by_name(currentinset, "foldername")

Note that the dashes in the proctable names become underlines in Ness
and that the first argument should be currentinset because that is
expected by the proctable function.

For more information see section 5 of /usr/andrew/doc/atk/ness/nessuser.doc.

Have fun,
Fred Hansen

rr2b+@ANDREW.CMU.EDU (Robert Andrew Ryan) (07/24/90)

I have a meta-x package which will be in the contrib dir for patch 7, 
if you don't want to wait, I'll post it in a few days to a week.

This meta-x package features completion and argument passing, and even
help in the style of the filename completion used by ez.

-Rob Ryan
Student Programmer, ITC
Disclaimer: as evidenced by its placement in contrib the ITC makes no
promises of support for this package.

guy@auspex.auspex.com (Guy Harris) (07/26/90)

>	There is no provision for passing an argument to the function.
>
>	There is no completion of the name while typing it in.
>
>I can't offer a solution to the second problem, though someone could
>easily write a completion package that inspected that proctable.

I looked at it sufficiently long to conclude that "easily" may not be
the right word here; the whole completion stuff wasn't wonderfully-well
documented and seemed a bit intricate.  I'm sure it's doable; it just
looked like more work than I was about to do.

>To pass arguments you can utilize Ness.

But can you pass arguments to a function in any EMACS by doing M-X?  I
tried it on both MicroEMACS and GNU EMACS (not the latest version) with
functions that took arguments, and they prompted me for the argument,
just as they would have had they been bound to keys.  I assume they'd do
the same with Bill's M-X package.

>I do this by adding to my .atkinit the line
>
>	addkey ness-dostmt  \e\e  view  
>
>Then when I type ESC-ESC (or meta-ESC, I suppose) it prompts for Ness
>statements.  Any proctable function can be called and arguments passed.

At least in Gosling EMACS, there is a "run a command" function, done
with M-X, and an "evaluate Mlisp expression" function, done with
something else.  "ness-dostmt" seems more like the latter than the
former.

I don't see the inability to pass an argument to a function using M-X as
a problem, for those reasons.  It might be cleaner if proctable
functions worked the way GNU EMACS functions worked; i.e., if the
proctable entry included a spec for what arguments the function took,
with M-X doing the prompting, rather than the code that implements the
function doing so, and the arguments being passed to the function as,
say, an array of unions or some such.

This would centralize some of the user interaction, and shrink the code
of some proctable functions; it would also, given code that could cope
with the idea of a dialog box with more than one item, let Andrew prompt
for a multi-argument function with a dialog box if the value of
DialogPriority specified that the function in question should use a
dialog box.

Unfortunately, it could also require some rewriting of proctable
function code.

>For instance, to classify a message into a folder (am operation I don't
>have in my messages menu) I can type ESC-ESC and give the command:
>
>	messages_classify_by_name(currentinset, "foldername")
>
>Note that the dashes in the proctable names become underlines in Ness
>and that the first argument should be currentinset because that is
>expected by the proctable function.

OK, I'll bite.  Where's the code that prompts for a folder name if, say,
you have "messages-classify-by-name" bound to a key or menu item, and
you invoke it through a key sequence or menu item, rather than from
Ness?

The proctable function takes a "name" argument, but I didn't see any
code in the function for "messages-classify-by-name" that does the
prompting if there's a NULL argument, nor did I see anything in the
proctable entry telling any common code to prompt for it.  I must be
missing something.... 

wjh+@ANDREW.CMU.EDU (Fred Hansen) (07/27/90)

Excerpts from internet.info-andrew: 26-Jul-90 Re: Meta-X package Guy
Harris@uunet.uu.net (3111)

> the whole completion stuff wasn't wonderfully-well
> documented and seemed a bit intricate.

Amen.

> in Gosling EMACS, there is a "run a command" function, done
> with M-X, and an "evaluate Mlisp expression" function, done with
> something else.  "ness-dostmt" seems more like the latter than the
> former.

This is precisely right.

> OK, I'll bite.  Where's the code that prompts for a folder name if, say,
> you have "messages-classify-by-name" bound to a key or menu item, and
> you invoke it through a key sequence or menu item, rather than from
> Ness?

I must admit I didn't check to see if messages-classify-by-name prompted
for the folder name.  Apparently this function is meant to be called
with one of the messages-compound-... operations which permit calling
functions and passing arguments in a fairly ad hoc and undocumented
manner.

Fred Hansen

janssen@parc.xerox.com (Bill Janssen) (07/27/90)

Excerpts from ext.andrew: 26-Jul-90 Re: Meta-X package Guy
Harris@uunet.uu.net (3112)

> >[A reply to Fred Hansen's posting about using ESC-ESC and Ness, turning
> into a discussion about passing args to procs]

The proc system in ATK is unfortunately designed to be called by a
keystroke and/or menu item on some kind of view.  The notion is that a
single arg can be passed, through the Emacs standard of typing ^U
    argument before invoking the function (the function im_ProvideArg()
can also be called to set the argument value).  The function can then
call im_Argument() to retrieve the argument, which is passed as a long.

But in general, ATK procs are argument-less functions designed to be
called by user actions, that often prompt for input from the user.  This
severely limits their usefulness in extension languages, such as Ness. 
GNU Emacs has a triple model:

1)  Internal procedures are coded in C, and are meant to be called from
C code.  These correspond to module-private functions.

2)  Elisp functions are coded in C or Elisp, and are meant to be called
from the extension language (Elisp).  These are more like the exported
interface provided by the ATK .ch files.  Almost all Emacs functionality
is available through these, including many low-level primitives.

3)  A special set of procedures, corresponding to ATK procs, are defined
to be called by the user.  These wrap a different interface around some
set of the Elisp functions.  A stylized approach to defining them
ensures that they prompt for possible arguments, if such are defined. 
They may also be called from Elisp.

This middle layer seems to be what ATK is missing.  I think if one were
to re-do such a project, having an extension language (Scheme sounds
nice) in mind from the beginning, and implementing part of the
application in the extension language, would be a good idea.  Such a
layer can be added after the fact, though.  Ness almost does it right;
it can call Class methods as well as procs.  But is has to re-parse the
.ch file each time the function is interpreted (I think), which makes
for slow execution.  I think the class compiler should do the parsing
and leave the results in another .dox file, in a form that could be
easily loaded by the extension language.

Bill

nsb@THUMPER.BELLCORE.COM (Nathaniel Borenstein) (07/27/90)

Excerpts from internet.info-andrew: 26-Jul-90 Re: Meta-X package Fred
Hansen@andrew.cmu.e (940+0)

>> OK, I'll bite.  Where's the code that prompts for a folder name if, say,
>> you have "messages-classify-by-name" bound to a key or menu item, and
>> you invoke it through a key sequence or menu item, rather than from
>> Ness?

> I must admit I didn't check to see if messages-classify-by-name prompted
> for the folder name.  Apparently this function is meant to be called
> with one of the messages-compound-... operations which permit calling
> functions and passing arguments in a fairly ad hoc and undocumented
> manner.

I think you're looking for the last 4 functions defined in
atkams/messages/lib/ams.c.  This is an example of using the completion
package, in this case for folder name completion.  In this case, it
can't be made to take an argument, I believe.  

However, in general you can exploit the (undocumented) fact that when
you call a proctable entry from an init file and do NOT pass an argument
to it, the  argument is NULL.  Thus you can write a proctable entry that
interacts with the user if & only if that parameter is NULL.  

For an example of a function that does this successfully, look in
atkams/messages/lib/sendaux.c, at the code that implements the ^O
function in the sending window.  This is bound to the procedure
UserWantsAHeader.  If no arguments are passed, it will prompt the user
for the header, otherwise it will use what is passed in.

wjh+@ANDREW.CMU.EDU (Fred Hansen) (07/29/90)

Excerpts from internet.info-andrew: 26-Jul-90 Re: Meta-X package Bill
Janssen@parc.xerox. (2270+0)

> This middle layer seems to be what ATK is missing.  I think if one were
> to re-do such a project, having an extension language (Scheme sounds
> nice) in mind from the beginning, and implementing part of the
> application in the extension language, would be a good idea.  Such a
layer can be added after the fact, though.  

Yes, much work is needed to make ATK really accessible from an extension
language.  Scheme sounds wonderful for hackers, but not so nice for mere
mortals.

> Ness almost does it right; it can call Class methods as well as procs. 
> But is has to re-parse the .ch file each time the function is
> interpreted (I think), which makes for slow execution.  I think the
> class compiler should do the parsing and leave the results in another
> .dox file, in a form that could be easily loaded by the extension
> language.

Not only can Ness functions call methods and class procedures, they can
also access and set instance variables in objects.  The .ch files are
parsed only once, the first time they are needed.  All subsequent Ness
compilations in the same process reuse the .ch reparse already done. 
Ness compilation could indeed avoid this reparsing if .dox files were
generated, though the class compiler does not do as thorough a job of
parsing the declarations as does Ness.  (Actually, Ness could use the
.dog file directly, though then we would pay for loading it.)

A problem with access to C from Ness is the mismatch in types.  Not all
C types and structures are implemented in Ness.


Fred Hansen

ham@Neon.Stanford.EDU (Peter R. Ham) (07/29/90)

What's "Ness", I'm interested in extension languages.
I've missed the beginning of this discussion.
--