diff options
| author | Stefan Monnier | 2022-04-26 10:36:52 -0400 |
|---|---|---|
| committer | Stefan Monnier | 2022-04-26 10:36:52 -0400 |
| commit | bffc4cb39dc7b83fc4a1bffd23eeed2774b79444 (patch) | |
| tree | 103b22b517aafd70b16fe2d1dea06cb4673668f5 /src/eval.c | |
| parent | 756b7cf5d9a817503437b3e8a9e8d912b7ee6c75 (diff) | |
| download | emacs-bffc4cb39dc7b83fc4a1bffd23eeed2774b79444.tar.gz emacs-bffc4cb39dc7b83fc4a1bffd23eeed2774b79444.zip | |
New generic function `oclosure-interactive-form`
It's used by `interactive-form` when it encounters an OClosure.
This lets one compute the `interactive-form` of OClosures
dynamically by adding appropriate methods.
This does not include support for `command-modes` for Oclosures.
* lisp/simple.el (oclosure-interactive-form): New generic function.
* src/data.c (Finteractive_form): Delegate to
`oclosure-interactive-form` if the arg is an OClosure.
(syms_of_data): New symbol `Qoclosure_interactive_form`.
* src/eval.c (Fcommandp): Delegate to `interactive-form` if the arg is
an OClosure.
* src/lisp.h (VALID_DOCSTRING_P): New function, extracted from
`store_function_docstring`.
* src/doc.c (store_function_docstring): Use it.
* lisp/kmacro.el (kmacro): Don't carry any interactive form.
(oclosure-interactive-form) <kmacro>: New method, instead.
* test/lisp/emacs-lisp/oclosure-tests.el (oclosure-interactive-form)
<oclosure-test>: New method.
(oclosure-test-interactive-form): New test.
* doc/lispref/commands.texi (Using Interactive):
Document `oclosure-interactive-form`.
Diffstat (limited to 'src/eval.c')
| -rw-r--r-- | src/eval.c | 94 |
1 files changed, 65 insertions, 29 deletions
diff --git a/src/eval.c b/src/eval.c index 37bc03465cc..77ec47e2b79 100644 --- a/src/eval.c +++ b/src/eval.c | |||
| @@ -2032,8 +2032,7 @@ then strings and vectors are not accepted. */) | |||
| 2032 | (Lisp_Object function, Lisp_Object for_call_interactively) | 2032 | (Lisp_Object function, Lisp_Object for_call_interactively) |
| 2033 | { | 2033 | { |
| 2034 | register Lisp_Object fun; | 2034 | register Lisp_Object fun; |
| 2035 | register Lisp_Object funcar; | 2035 | bool genfun = false; /* If true, we should consult `interactive-form'. */ |
| 2036 | Lisp_Object if_prop = Qnil; | ||
| 2037 | 2036 | ||
| 2038 | fun = function; | 2037 | fun = function; |
| 2039 | 2038 | ||
| @@ -2041,52 +2040,89 @@ then strings and vectors are not accepted. */) | |||
| 2041 | if (NILP (fun)) | 2040 | if (NILP (fun)) |
| 2042 | return Qnil; | 2041 | return Qnil; |
| 2043 | 2042 | ||
| 2044 | /* Check an `interactive-form' property if present, analogous to the | ||
| 2045 | function-documentation property. */ | ||
| 2046 | fun = function; | ||
| 2047 | while (SYMBOLP (fun)) | ||
| 2048 | { | ||
| 2049 | Lisp_Object tmp = Fget (fun, Qinteractive_form); | ||
| 2050 | if (!NILP (tmp)) | ||
| 2051 | if_prop = Qt; | ||
| 2052 | fun = Fsymbol_function (fun); | ||
| 2053 | } | ||
| 2054 | |||
| 2055 | /* Emacs primitives are interactive if their DEFUN specifies an | 2043 | /* Emacs primitives are interactive if their DEFUN specifies an |
| 2056 | interactive spec. */ | 2044 | interactive spec. */ |
| 2057 | if (SUBRP (fun)) | 2045 | if (SUBRP (fun)) |
| 2058 | return XSUBR (fun)->intspec.string ? Qt : if_prop; | 2046 | { |
| 2059 | 2047 | if (XSUBR (fun)->intspec.string) | |
| 2048 | return Qt; | ||
| 2049 | } | ||
| 2060 | /* Bytecode objects are interactive if they are long enough to | 2050 | /* Bytecode objects are interactive if they are long enough to |
| 2061 | have an element whose index is COMPILED_INTERACTIVE, which is | 2051 | have an element whose index is COMPILED_INTERACTIVE, which is |
| 2062 | where the interactive spec is stored. */ | 2052 | where the interactive spec is stored. */ |
| 2063 | else if (COMPILEDP (fun)) | 2053 | else if (COMPILEDP (fun)) |
| 2064 | return (PVSIZE (fun) > COMPILED_INTERACTIVE ? Qt : if_prop); | 2054 | { |
| 2055 | if (PVSIZE (fun) > COMPILED_INTERACTIVE) | ||
| 2056 | return Qt; | ||
| 2057 | else if (PVSIZE (fun) > COMPILED_DOC_STRING) | ||
| 2058 | { | ||
| 2059 | Lisp_Object doc = AREF (fun, COMPILED_DOC_STRING); | ||
| 2060 | /* An invalid "docstring" is a sign that we have an OClosure. */ | ||
| 2061 | genfun = !(NILP (doc) || VALID_DOCSTRING_P (doc)); | ||
| 2062 | } | ||
| 2063 | } | ||
| 2065 | 2064 | ||
| 2066 | #ifdef HAVE_MODULES | 2065 | #ifdef HAVE_MODULES |
| 2067 | /* Module functions are interactive if their `interactive_form' | 2066 | /* Module functions are interactive if their `interactive_form' |
| 2068 | field is non-nil. */ | 2067 | field is non-nil. */ |
| 2069 | else if (MODULE_FUNCTIONP (fun)) | 2068 | else if (MODULE_FUNCTIONP (fun)) |
| 2070 | return NILP (module_function_interactive_form (XMODULE_FUNCTION (fun))) | 2069 | { |
| 2071 | ? if_prop | 2070 | if (!NILP (module_function_interactive_form (XMODULE_FUNCTION (fun)))) |
| 2072 | : Qt; | 2071 | return Qt; |
| 2072 | } | ||
| 2073 | #endif | 2073 | #endif |
| 2074 | 2074 | ||
| 2075 | /* Strings and vectors are keyboard macros. */ | 2075 | /* Strings and vectors are keyboard macros. */ |
| 2076 | if (STRINGP (fun) || VECTORP (fun)) | 2076 | else if (STRINGP (fun) || VECTORP (fun)) |
| 2077 | return (NILP (for_call_interactively) ? Qt : Qnil); | 2077 | return (NILP (for_call_interactively) ? Qt : Qnil); |
| 2078 | 2078 | ||
| 2079 | /* Lists may represent commands. */ | 2079 | /* Lists may represent commands. */ |
| 2080 | if (!CONSP (fun)) | 2080 | else if (!CONSP (fun)) |
| 2081 | return Qnil; | 2081 | return Qnil; |
| 2082 | funcar = XCAR (fun); | 2082 | else |
| 2083 | if (EQ (funcar, Qclosure)) | 2083 | { |
| 2084 | return (!NILP (Fassq (Qinteractive, Fcdr (Fcdr (XCDR (fun))))) | 2084 | Lisp_Object funcar = XCAR (fun); |
| 2085 | ? Qt : if_prop); | 2085 | if (EQ (funcar, Qautoload)) |
| 2086 | else if (EQ (funcar, Qlambda)) | 2086 | { |
| 2087 | return !NILP (Fassq (Qinteractive, Fcdr (XCDR (fun)))) ? Qt : if_prop; | 2087 | if (!NILP (Fcar (Fcdr (Fcdr (XCDR (fun)))))) |
| 2088 | else if (EQ (funcar, Qautoload)) | 2088 | return Qt; |
| 2089 | return !NILP (Fcar (Fcdr (Fcdr (XCDR (fun))))) ? Qt : if_prop; | 2089 | } |
| 2090 | else | ||
| 2091 | { | ||
| 2092 | Lisp_Object body = CDR_SAFE (XCDR (fun)); | ||
| 2093 | if (EQ (funcar, Qclosure)) | ||
| 2094 | body = CDR_SAFE (body); | ||
| 2095 | else if (!EQ (funcar, Qlambda)) | ||
| 2096 | return Qnil; | ||
| 2097 | if (!NILP (Fassq (Qinteractive, body))) | ||
| 2098 | return Qt; | ||
| 2099 | else if (VALID_DOCSTRING_P (CAR_SAFE (body))) | ||
| 2100 | /* A "docstring" is a sign that we may have an OClosure. */ | ||
| 2101 | genfun = true; | ||
| 2102 | } | ||
| 2103 | } | ||
| 2104 | |||
| 2105 | /* By now, if it's not a function we already returned nil. */ | ||
| 2106 | |||
| 2107 | /* Check an `interactive-form' property if present, analogous to the | ||
| 2108 | function-documentation property. */ | ||
| 2109 | fun = function; | ||
| 2110 | while (SYMBOLP (fun)) | ||
| 2111 | { | ||
| 2112 | Lisp_Object tmp = Fget (fun, Qinteractive_form); | ||
| 2113 | if (!NILP (tmp)) | ||
| 2114 | error ("Found an 'interactive-form' property!"); | ||
| 2115 | fun = Fsymbol_function (fun); | ||
| 2116 | } | ||
| 2117 | |||
| 2118 | /* If there's no immediate interactive form but it's an OClosure, | ||
| 2119 | then delegate to the generic-function in case it has | ||
| 2120 | a type-specific interactive-form. */ | ||
| 2121 | if (genfun) | ||
| 2122 | { | ||
| 2123 | Lisp_Object iform = call1 (Qinteractive_form, fun); | ||
| 2124 | return NILP (iform) ? Qnil : Qt; | ||
| 2125 | } | ||
| 2090 | else | 2126 | else |
| 2091 | return Qnil; | 2127 | return Qnil; |
| 2092 | } | 2128 | } |