Add new variable print-unreadable-function
* doc/lispref/streams.texi (Output Variables): Document it. * src/print.c (print_vectorlike): Use the variable. (syms_of_print): New variable print-unreadable-function (bug#52566).
This commit is contained in:
parent
7e596463be
commit
e4d2a7894b
4 changed files with 308 additions and 220 deletions
|
@ -872,6 +872,32 @@ If non-@code{nil}, this variable enables detection of circular and
|
|||
shared structure in printing. @xref{Circular Objects}.
|
||||
@end defvar
|
||||
|
||||
@defvar print-unreadable-function
|
||||
By default, Emacs prints unreadable objects as @samp{#<...>"}. For
|
||||
instance:
|
||||
|
||||
@example
|
||||
(prin1-to-string (make-marker))
|
||||
@result{} "#<marker in no buffer>"
|
||||
@end example
|
||||
|
||||
If this variable is non-@code{nil}, it should be a function that will
|
||||
be called to handle printing of these objects. The first argument is
|
||||
the object, and the second argument is the @var{noescape} flag used by
|
||||
the printing functions (@pxref{Output Functions}).
|
||||
|
||||
The function should return either @code{nil} (print nothing), or a
|
||||
string (which will be printed), or any other object (which means that
|
||||
the object should be printed normally). For instance:
|
||||
|
||||
@example
|
||||
(let ((print-unreadable-function
|
||||
(lambda (object escape) "hello")))
|
||||
(prin1-to-string (make-marker)))
|
||||
@result{} "hello"
|
||||
@end example
|
||||
@end defvar
|
||||
|
||||
@defvar print-gensym
|
||||
If non-@code{nil}, this variable enables detection of uninterned symbols
|
||||
(@pxref{Creating Symbols}) in printing. When this is enabled,
|
||||
|
|
8
etc/NEWS
8
etc/NEWS
|
@ -977,12 +977,16 @@ functions.
|
|||
|
||||
* Lisp Changes in Emacs 29.1
|
||||
|
||||
--
|
||||
+++
|
||||
** New variable 'print-unreadable-function'.
|
||||
This variable allows changing how Emacs prints unreadable objects.
|
||||
|
||||
---
|
||||
** The variable 'polling-period' now accepts floating point values.
|
||||
This means Emacs can now poll for input during Lisp execution more
|
||||
frequently than once in a second.
|
||||
|
||||
--
|
||||
---
|
||||
** New function 'bidi-string-strip-control-characters'.
|
||||
This utility function is meant for displaying strings when it's
|
||||
essential that there's no bidirectional context.
|
||||
|
|
483
src/print.c
483
src/print.c
|
@ -1387,6 +1387,7 @@ static bool
|
|||
print_vectorlike (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag,
|
||||
char *buf)
|
||||
{
|
||||
/* First do all the vectorlike types that have a readable syntax. */
|
||||
switch (PSEUDOVECTOR_TYPE (XVECTOR (obj)))
|
||||
{
|
||||
case PVEC_BIGNUM:
|
||||
|
@ -1398,77 +1399,7 @@ print_vectorlike (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag,
|
|||
strout (str, len, len, printcharfun);
|
||||
SAFE_FREE ();
|
||||
}
|
||||
break;
|
||||
|
||||
case PVEC_MARKER:
|
||||
print_c_string ("#<marker ", printcharfun);
|
||||
/* Do you think this is necessary? */
|
||||
if (XMARKER (obj)->insertion_type != 0)
|
||||
print_c_string ("(moves after insertion) ", printcharfun);
|
||||
if (! XMARKER (obj)->buffer)
|
||||
print_c_string ("in no buffer", printcharfun);
|
||||
else
|
||||
{
|
||||
int len = sprintf (buf, "at %"pD"d in ", marker_position (obj));
|
||||
strout (buf, len, len, printcharfun);
|
||||
print_string (BVAR (XMARKER (obj)->buffer, name), printcharfun);
|
||||
}
|
||||
printchar ('>', printcharfun);
|
||||
break;
|
||||
|
||||
case PVEC_OVERLAY:
|
||||
print_c_string ("#<overlay ", printcharfun);
|
||||
if (! XMARKER (OVERLAY_START (obj))->buffer)
|
||||
print_c_string ("in no buffer", printcharfun);
|
||||
else
|
||||
{
|
||||
int len = sprintf (buf, "from %"pD"d to %"pD"d in ",
|
||||
marker_position (OVERLAY_START (obj)),
|
||||
marker_position (OVERLAY_END (obj)));
|
||||
strout (buf, len, len, printcharfun);
|
||||
print_string (BVAR (XMARKER (OVERLAY_START (obj))->buffer, name),
|
||||
printcharfun);
|
||||
}
|
||||
printchar ('>', printcharfun);
|
||||
break;
|
||||
|
||||
case PVEC_USER_PTR:
|
||||
{
|
||||
print_c_string ("#<user-ptr ", printcharfun);
|
||||
int i = sprintf (buf, "ptr=%p finalizer=%p",
|
||||
XUSER_PTR (obj)->p,
|
||||
XUSER_PTR (obj)->finalizer);
|
||||
strout (buf, i, i, printcharfun);
|
||||
printchar ('>', printcharfun);
|
||||
}
|
||||
break;
|
||||
|
||||
case PVEC_FINALIZER:
|
||||
print_c_string ("#<finalizer", printcharfun);
|
||||
if (NILP (XFINALIZER (obj)->function))
|
||||
print_c_string (" used", printcharfun);
|
||||
printchar ('>', printcharfun);
|
||||
break;
|
||||
|
||||
case PVEC_MISC_PTR:
|
||||
{
|
||||
/* This shouldn't happen in normal usage, but let's
|
||||
print it anyway for the benefit of the debugger. */
|
||||
int i = sprintf (buf, "#<ptr %p>", xmint_pointer (obj));
|
||||
strout (buf, i, i, printcharfun);
|
||||
}
|
||||
break;
|
||||
|
||||
case PVEC_PROCESS:
|
||||
if (escapeflag)
|
||||
{
|
||||
print_c_string ("#<process ", printcharfun);
|
||||
print_string (XPROCESS (obj)->name, printcharfun);
|
||||
printchar ('>', printcharfun);
|
||||
}
|
||||
else
|
||||
print_string (XPROCESS (obj)->name, printcharfun);
|
||||
break;
|
||||
return true;
|
||||
|
||||
case PVEC_BOOL_VECTOR:
|
||||
{
|
||||
|
@ -1513,70 +1444,7 @@ print_vectorlike (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag,
|
|||
print_c_string (" ...", printcharfun);
|
||||
printchar ('\"', printcharfun);
|
||||
}
|
||||
break;
|
||||
|
||||
case PVEC_SUBR:
|
||||
print_c_string ("#<subr ", printcharfun);
|
||||
print_c_string (XSUBR (obj)->symbol_name, printcharfun);
|
||||
printchar ('>', printcharfun);
|
||||
break;
|
||||
|
||||
case PVEC_XWIDGET:
|
||||
#ifdef HAVE_XWIDGETS
|
||||
{
|
||||
if (NILP (XXWIDGET (obj)->buffer))
|
||||
print_c_string ("#<killed xwidget>", printcharfun);
|
||||
else
|
||||
{
|
||||
#ifdef USE_GTK
|
||||
int len = sprintf (buf, "#<xwidget %u %p>",
|
||||
XXWIDGET (obj)->xwidget_id,
|
||||
XXWIDGET (obj)->widget_osr);
|
||||
#else
|
||||
int len = sprintf (buf, "#<xwidget %u %p>",
|
||||
XXWIDGET (obj)->xwidget_id,
|
||||
XXWIDGET (obj)->xwWidget);
|
||||
#endif
|
||||
strout (buf, len, len, printcharfun);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#else
|
||||
emacs_abort ();
|
||||
#endif
|
||||
case PVEC_XWIDGET_VIEW:
|
||||
print_c_string ("#<xwidget view", printcharfun);
|
||||
printchar ('>', printcharfun);
|
||||
break;
|
||||
|
||||
case PVEC_WINDOW:
|
||||
{
|
||||
int len = sprintf (buf, "#<window %"pI"d",
|
||||
XWINDOW (obj)->sequence_number);
|
||||
strout (buf, len, len, printcharfun);
|
||||
if (BUFFERP (XWINDOW (obj)->contents))
|
||||
{
|
||||
print_c_string (" on ", printcharfun);
|
||||
print_string (BVAR (XBUFFER (XWINDOW (obj)->contents), name),
|
||||
printcharfun);
|
||||
}
|
||||
printchar ('>', printcharfun);
|
||||
}
|
||||
break;
|
||||
|
||||
case PVEC_TERMINAL:
|
||||
{
|
||||
struct terminal *t = XTERMINAL (obj);
|
||||
int len = sprintf (buf, "#<terminal %d", t->id);
|
||||
strout (buf, len, len, printcharfun);
|
||||
if (t->name)
|
||||
{
|
||||
print_c_string (" on ", printcharfun);
|
||||
print_c_string (t->name, printcharfun);
|
||||
}
|
||||
printchar ('>', printcharfun);
|
||||
}
|
||||
break;
|
||||
return true;
|
||||
|
||||
case PVEC_HASH_TABLE:
|
||||
{
|
||||
|
@ -1649,6 +1517,253 @@ print_vectorlike (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag,
|
|||
|
||||
print_c_string ("))", printcharfun);
|
||||
}
|
||||
return true;
|
||||
|
||||
case PVEC_RECORD:
|
||||
{
|
||||
ptrdiff_t size = PVSIZE (obj);
|
||||
|
||||
/* Don't print more elements than the specified maximum. */
|
||||
ptrdiff_t n
|
||||
= (FIXNATP (Vprint_length) && XFIXNAT (Vprint_length) < size
|
||||
? XFIXNAT (Vprint_length) : size);
|
||||
|
||||
print_c_string ("#s(", printcharfun);
|
||||
for (ptrdiff_t i = 0; i < n; i ++)
|
||||
{
|
||||
if (i) printchar (' ', printcharfun);
|
||||
print_object (AREF (obj, i), printcharfun, escapeflag);
|
||||
}
|
||||
if (n < size)
|
||||
print_c_string (" ...", printcharfun);
|
||||
printchar (')', printcharfun);
|
||||
}
|
||||
return true;
|
||||
|
||||
case PVEC_SUB_CHAR_TABLE:
|
||||
case PVEC_COMPILED:
|
||||
case PVEC_CHAR_TABLE:
|
||||
case PVEC_NORMAL_VECTOR:
|
||||
{
|
||||
ptrdiff_t size = ASIZE (obj);
|
||||
if (COMPILEDP (obj))
|
||||
{
|
||||
printchar ('#', printcharfun);
|
||||
size &= PSEUDOVECTOR_SIZE_MASK;
|
||||
}
|
||||
if (CHAR_TABLE_P (obj) || SUB_CHAR_TABLE_P (obj))
|
||||
{
|
||||
/* Print a char-table as if it were a vector,
|
||||
lumping the parent and default slots in with the
|
||||
character slots. But add #^ as a prefix. */
|
||||
|
||||
/* Make each lowest sub_char_table start a new line.
|
||||
Otherwise we'll make a line extremely long, which
|
||||
results in slow redisplay. */
|
||||
if (SUB_CHAR_TABLE_P (obj)
|
||||
&& XSUB_CHAR_TABLE (obj)->depth == 3)
|
||||
printchar ('\n', printcharfun);
|
||||
print_c_string ("#^", printcharfun);
|
||||
if (SUB_CHAR_TABLE_P (obj))
|
||||
printchar ('^', printcharfun);
|
||||
size &= PSEUDOVECTOR_SIZE_MASK;
|
||||
}
|
||||
if (size & PSEUDOVECTOR_FLAG)
|
||||
return false;
|
||||
|
||||
printchar ('[', printcharfun);
|
||||
|
||||
int idx = SUB_CHAR_TABLE_P (obj) ? SUB_CHAR_TABLE_OFFSET : 0;
|
||||
Lisp_Object tem;
|
||||
ptrdiff_t real_size = size;
|
||||
|
||||
/* For a sub char-table, print heading non-Lisp data first. */
|
||||
if (SUB_CHAR_TABLE_P (obj))
|
||||
{
|
||||
int i = sprintf (buf, "%d %d", XSUB_CHAR_TABLE (obj)->depth,
|
||||
XSUB_CHAR_TABLE (obj)->min_char);
|
||||
strout (buf, i, i, printcharfun);
|
||||
}
|
||||
|
||||
/* Don't print more elements than the specified maximum. */
|
||||
if (FIXNATP (Vprint_length)
|
||||
&& XFIXNAT (Vprint_length) < size)
|
||||
size = XFIXNAT (Vprint_length);
|
||||
|
||||
for (int i = idx; i < size; i++)
|
||||
{
|
||||
if (i) printchar (' ', printcharfun);
|
||||
tem = AREF (obj, i);
|
||||
print_object (tem, printcharfun, escapeflag);
|
||||
}
|
||||
if (size < real_size)
|
||||
print_c_string (" ...", printcharfun);
|
||||
printchar (']', printcharfun);
|
||||
}
|
||||
return true;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Then do all the pseudovector types that don't have a readable
|
||||
syntax. First check whether this is handled by
|
||||
`print-unreadable-function'. */
|
||||
if (!NILP (Vprint_unreadable_function)
|
||||
&& FUNCTIONP (Vprint_unreadable_function))
|
||||
{
|
||||
ptrdiff_t count = SPECPDL_INDEX ();
|
||||
/* Bind `print-unreadable-function' to nil to avoid accidental
|
||||
infinite recursion in the function called. */
|
||||
Lisp_Object func = Vprint_unreadable_function;
|
||||
specbind (Qprint_unreadable_function, Qnil);
|
||||
Lisp_Object result = CALLN (Ffuncall, func, obj,
|
||||
escapeflag? Qt: Qnil);
|
||||
unbind_to (count, Qnil);
|
||||
|
||||
if (!NILP (result))
|
||||
{
|
||||
if (STRINGP (result))
|
||||
print_string (result, printcharfun);
|
||||
/* It's handled, so stop processing here. */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Not handled; print unreadable object. */
|
||||
switch (PSEUDOVECTOR_TYPE (XVECTOR (obj)))
|
||||
{
|
||||
case PVEC_MARKER:
|
||||
print_c_string ("#<marker ", printcharfun);
|
||||
/* Do you think this is necessary? */
|
||||
if (XMARKER (obj)->insertion_type != 0)
|
||||
print_c_string ("(moves after insertion) ", printcharfun);
|
||||
if (! XMARKER (obj)->buffer)
|
||||
print_c_string ("in no buffer", printcharfun);
|
||||
else
|
||||
{
|
||||
int len = sprintf (buf, "at %"pD"d in ", marker_position (obj));
|
||||
strout (buf, len, len, printcharfun);
|
||||
print_string (BVAR (XMARKER (obj)->buffer, name), printcharfun);
|
||||
}
|
||||
printchar ('>', printcharfun);
|
||||
break;
|
||||
|
||||
case PVEC_OVERLAY:
|
||||
print_c_string ("#<overlay ", printcharfun);
|
||||
if (! XMARKER (OVERLAY_START (obj))->buffer)
|
||||
print_c_string ("in no buffer", printcharfun);
|
||||
else
|
||||
{
|
||||
int len = sprintf (buf, "from %"pD"d to %"pD"d in ",
|
||||
marker_position (OVERLAY_START (obj)),
|
||||
marker_position (OVERLAY_END (obj)));
|
||||
strout (buf, len, len, printcharfun);
|
||||
print_string (BVAR (XMARKER (OVERLAY_START (obj))->buffer, name),
|
||||
printcharfun);
|
||||
}
|
||||
printchar ('>', printcharfun);
|
||||
break;
|
||||
|
||||
case PVEC_USER_PTR:
|
||||
{
|
||||
print_c_string ("#<user-ptr ", printcharfun);
|
||||
int i = sprintf (buf, "ptr=%p finalizer=%p",
|
||||
XUSER_PTR (obj)->p,
|
||||
XUSER_PTR (obj)->finalizer);
|
||||
strout (buf, i, i, printcharfun);
|
||||
printchar ('>', printcharfun);
|
||||
}
|
||||
break;
|
||||
|
||||
case PVEC_FINALIZER:
|
||||
print_c_string ("#<finalizer", printcharfun);
|
||||
if (NILP (XFINALIZER (obj)->function))
|
||||
print_c_string (" used", printcharfun);
|
||||
printchar ('>', printcharfun);
|
||||
break;
|
||||
|
||||
case PVEC_MISC_PTR:
|
||||
{
|
||||
/* This shouldn't happen in normal usage, but let's
|
||||
print it anyway for the benefit of the debugger. */
|
||||
int i = sprintf (buf, "#<ptr %p>", xmint_pointer (obj));
|
||||
strout (buf, i, i, printcharfun);
|
||||
}
|
||||
break;
|
||||
|
||||
case PVEC_PROCESS:
|
||||
if (escapeflag)
|
||||
{
|
||||
print_c_string ("#<process ", printcharfun);
|
||||
print_string (XPROCESS (obj)->name, printcharfun);
|
||||
printchar ('>', printcharfun);
|
||||
}
|
||||
else
|
||||
print_string (XPROCESS (obj)->name, printcharfun);
|
||||
break;
|
||||
|
||||
case PVEC_SUBR:
|
||||
print_c_string ("#<subr ", printcharfun);
|
||||
print_c_string (XSUBR (obj)->symbol_name, printcharfun);
|
||||
printchar ('>', printcharfun);
|
||||
break;
|
||||
|
||||
case PVEC_XWIDGET:
|
||||
#ifdef HAVE_XWIDGETS
|
||||
{
|
||||
if (NILP (XXWIDGET (obj)->buffer))
|
||||
print_c_string ("#<killed xwidget>", printcharfun);
|
||||
else
|
||||
{
|
||||
#ifdef USE_GTK
|
||||
int len = sprintf (buf, "#<xwidget %u %p>",
|
||||
XXWIDGET (obj)->xwidget_id,
|
||||
XXWIDGET (obj)->widget_osr);
|
||||
#else
|
||||
int len = sprintf (buf, "#<xwidget %u %p>",
|
||||
XXWIDGET (obj)->xwidget_id,
|
||||
XXWIDGET (obj)->xwWidget);
|
||||
#endif
|
||||
strout (buf, len, len, printcharfun);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#else
|
||||
emacs_abort ();
|
||||
#endif
|
||||
case PVEC_XWIDGET_VIEW:
|
||||
print_c_string ("#<xwidget view", printcharfun);
|
||||
printchar ('>', printcharfun);
|
||||
break;
|
||||
|
||||
case PVEC_WINDOW:
|
||||
{
|
||||
int len = sprintf (buf, "#<window %"pI"d",
|
||||
XWINDOW (obj)->sequence_number);
|
||||
strout (buf, len, len, printcharfun);
|
||||
if (BUFFERP (XWINDOW (obj)->contents))
|
||||
{
|
||||
print_c_string (" on ", printcharfun);
|
||||
print_string (BVAR (XBUFFER (XWINDOW (obj)->contents), name),
|
||||
printcharfun);
|
||||
}
|
||||
printchar ('>', printcharfun);
|
||||
}
|
||||
break;
|
||||
|
||||
case PVEC_TERMINAL:
|
||||
{
|
||||
struct terminal *t = XTERMINAL (obj);
|
||||
int len = sprintf (buf, "#<terminal %d", t->id);
|
||||
strout (buf, len, len, printcharfun);
|
||||
if (t->name)
|
||||
{
|
||||
print_c_string (" on ", printcharfun);
|
||||
print_c_string (t->name, printcharfun);
|
||||
}
|
||||
printchar ('>', printcharfun);
|
||||
}
|
||||
break;
|
||||
|
||||
case PVEC_BUFFER:
|
||||
|
@ -1756,89 +1871,6 @@ print_vectorlike (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag,
|
|||
printchar ('>', printcharfun);
|
||||
break;
|
||||
|
||||
case PVEC_RECORD:
|
||||
{
|
||||
ptrdiff_t size = PVSIZE (obj);
|
||||
|
||||
/* Don't print more elements than the specified maximum. */
|
||||
ptrdiff_t n
|
||||
= (FIXNATP (Vprint_length) && XFIXNAT (Vprint_length) < size
|
||||
? XFIXNAT (Vprint_length) : size);
|
||||
|
||||
print_c_string ("#s(", printcharfun);
|
||||
for (ptrdiff_t i = 0; i < n; i ++)
|
||||
{
|
||||
if (i) printchar (' ', printcharfun);
|
||||
print_object (AREF (obj, i), printcharfun, escapeflag);
|
||||
}
|
||||
if (n < size)
|
||||
print_c_string (" ...", printcharfun);
|
||||
printchar (')', printcharfun);
|
||||
}
|
||||
break;
|
||||
|
||||
case PVEC_SUB_CHAR_TABLE:
|
||||
case PVEC_COMPILED:
|
||||
case PVEC_CHAR_TABLE:
|
||||
case PVEC_NORMAL_VECTOR:
|
||||
{
|
||||
ptrdiff_t size = ASIZE (obj);
|
||||
if (COMPILEDP (obj))
|
||||
{
|
||||
printchar ('#', printcharfun);
|
||||
size &= PSEUDOVECTOR_SIZE_MASK;
|
||||
}
|
||||
if (CHAR_TABLE_P (obj) || SUB_CHAR_TABLE_P (obj))
|
||||
{
|
||||
/* Print a char-table as if it were a vector,
|
||||
lumping the parent and default slots in with the
|
||||
character slots. But add #^ as a prefix. */
|
||||
|
||||
/* Make each lowest sub_char_table start a new line.
|
||||
Otherwise we'll make a line extremely long, which
|
||||
results in slow redisplay. */
|
||||
if (SUB_CHAR_TABLE_P (obj)
|
||||
&& XSUB_CHAR_TABLE (obj)->depth == 3)
|
||||
printchar ('\n', printcharfun);
|
||||
print_c_string ("#^", printcharfun);
|
||||
if (SUB_CHAR_TABLE_P (obj))
|
||||
printchar ('^', printcharfun);
|
||||
size &= PSEUDOVECTOR_SIZE_MASK;
|
||||
}
|
||||
if (size & PSEUDOVECTOR_FLAG)
|
||||
return false;
|
||||
|
||||
printchar ('[', printcharfun);
|
||||
|
||||
int idx = SUB_CHAR_TABLE_P (obj) ? SUB_CHAR_TABLE_OFFSET : 0;
|
||||
Lisp_Object tem;
|
||||
ptrdiff_t real_size = size;
|
||||
|
||||
/* For a sub char-table, print heading non-Lisp data first. */
|
||||
if (SUB_CHAR_TABLE_P (obj))
|
||||
{
|
||||
int i = sprintf (buf, "%d %d", XSUB_CHAR_TABLE (obj)->depth,
|
||||
XSUB_CHAR_TABLE (obj)->min_char);
|
||||
strout (buf, i, i, printcharfun);
|
||||
}
|
||||
|
||||
/* Don't print more elements than the specified maximum. */
|
||||
if (FIXNATP (Vprint_length)
|
||||
&& XFIXNAT (Vprint_length) < size)
|
||||
size = XFIXNAT (Vprint_length);
|
||||
|
||||
for (int i = idx; i < size; i++)
|
||||
{
|
||||
if (i) printchar (' ', printcharfun);
|
||||
tem = AREF (obj, i);
|
||||
print_object (tem, printcharfun, escapeflag);
|
||||
}
|
||||
if (size < real_size)
|
||||
print_c_string (" ...", printcharfun);
|
||||
printchar (']', printcharfun);
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef HAVE_MODULES
|
||||
case PVEC_MODULE_FUNCTION:
|
||||
{
|
||||
|
@ -2464,4 +2496,19 @@ priorities. Values other than nil or t are also treated as
|
|||
|
||||
print_prune_charset_plist = Qnil;
|
||||
staticpro (&print_prune_charset_plist);
|
||||
|
||||
DEFVAR_LISP ("print-unreadable-function", Vprint_unreadable_function,
|
||||
doc: /* Function called when printing unreadable objects.
|
||||
By default, Emacs printing functions (like `prin1') print unreadable
|
||||
objects like \"#<...>\", where \"...\" describes the object (for
|
||||
instance, \"#<marker in no buffer>\"). If this variable is non-nil,
|
||||
it should be a function which will be called to print the object instead.
|
||||
|
||||
It will be called with two arguments: The object to be printed, and
|
||||
noescape (see `prin1-to-string'). If this function returns nil, the
|
||||
object will be printed as normal. If it returns a string, that string
|
||||
will then be printed. If the function returns anything else, the
|
||||
object will not be printed. */);
|
||||
Vprint_unreadable_function = Qnil;
|
||||
DEFSYM (Qprint_unreadable_function, "print-unreadable-function");
|
||||
}
|
||||
|
|
|
@ -406,5 +406,16 @@ otherwise, use a different charset."
|
|||
(should (equal printed-nonprints
|
||||
"(55296 57343 778 65535 8194 8204)"))))
|
||||
|
||||
(ert-deftest test-unreadable ()
|
||||
(should (equal (prin1-to-string (make-marker)) "#<marker in no buffer>"))
|
||||
(let ((print-unreadable-function
|
||||
(lambda (_object _escape)
|
||||
"hello")))
|
||||
(should (equal (prin1-to-string (make-marker)) "hello")))
|
||||
(let ((print-unreadable-function
|
||||
(lambda (_object _escape)
|
||||
t)))
|
||||
(should (equal (prin1-to-string (make-marker)) ""))))
|
||||
|
||||
(provide 'print-tests)
|
||||
;;; print-tests.el ends here
|
||||
|
|
Loading…
Add table
Reference in a new issue