all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* self-insert-command source code?
@ 2014-12-04 21:58 Marcin Borkowski
  2014-12-04 22:23 ` Doug Lewan
  2014-12-04 22:51 ` Stefan Monnier
  0 siblings, 2 replies; 5+ messages in thread
From: Marcin Borkowski @ 2014-12-04 21:58 UTC (permalink / raw)
  To: Help Gnu Emacs mailing list

To my disappointment, I've just found out that self-insert-command is
written in C and not in Elisp.  I don't speak C very well (and don't
have the sources for Emacs installed (yet)), so could anyone shed some
light on these two questions (just in some spare time – it's not that I
/need/ it now).

1. Why is that so?  Since this is an interactive function, performance
should not be the issue.

2. What it might look like /if/ it was written in Elisp?

TIA,

-- 
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] 5+ messages in thread

* RE: self-insert-command source code?
  2014-12-04 21:58 self-insert-command source code? Marcin Borkowski
@ 2014-12-04 22:23 ` Doug Lewan
  2014-12-05 12:20   ` Nicolas Richard
  2014-12-04 22:51 ` Stefan Monnier
  1 sibling, 1 reply; 5+ messages in thread
From: Doug Lewan @ 2014-12-04 22:23 UTC (permalink / raw)
  To: Marcin Borkowski, Help Gnu Emacs mailing list




> -----Original Message-----
> Behalf Of Marcin Borkowski
> Sent: Thursday, 2014 December 04 16:59
> Subject: self-insert-command source code?
> 
> To my disappointment, I've just found out that self-insert-command is
> written in C and not in Elisp.  I don't speak C very well (and don't
> have the sources for Emacs installed (yet)), so could anyone shed some
> light on these two questions (just in some spare time – it's not that I
> /need/ it now).
> 
> 1. Why is that so?  Since this is an interactive function, performance
> should not be the issue.

Performance comes to mind in a big way.
Even compiled, there's an element of passing it to the lisp interpreter
and handling it as an interactive command.
There's look-up in the lisp name-space (instead of in C-space)
and loading the definition (which would require deciding 
if it's compiled or not and then doing the right thing)
Then there's a check for pre- and post-command hooks.
All that sounds pretty resource intensive to me
compared to the direct look-up and execution that could happen in C.
 
> 2. What it might look like /if/ it was written in Elisp?

Here's a quick implementation that I did.
It's not entirely optimal, but it demonstrates the basic ideas.
See info "(elisp) Command Loop Info"; info "(elisp) Input Events"
should also be informative.

    (defun my-self-insert ()
      "Insert the key sequence that invoked me."
      (interactive)
      (let ((my-keys (this-command-keys)))
        (message (select-random (list "Ouch!"
                                      "Ooh!"
                                      "Ah."
                                      "That tickles!")))
        (insert my-keys)))

M-x local-set-key <RET> a <RET> my-self-insert <RET>
should activate it in the current buffer on key "a".

I hope this helps.

-- 
,Doug
Douglas Lewan
Shubert Ticketing
(201) 489-8600 ext 224 or ext 4335

"This is a slow pup," he said continuing his ascent.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: self-insert-command source code?
  2014-12-04 21:58 self-insert-command source code? Marcin Borkowski
  2014-12-04 22:23 ` Doug Lewan
@ 2014-12-04 22:51 ` Stefan Monnier
  1 sibling, 0 replies; 5+ messages in thread
From: Stefan Monnier @ 2014-12-04 22:51 UTC (permalink / raw)
  To: help-gnu-emacs

> 1. Why is that so?  Since this is an interactive function, performance
> should not be the issue.

I think it's largely historical: back when Emacs started, machine
resources were incredibly more limited, so while it was OK for some
commands to be "not quite immediate", it was important for
self-insert-command to be fast enough to be immediate, even when sharing
the machine with many other users at the same time.  Also it was
important not to slowdown other users too much either, so common
commands were optimized so that the average resource use was limited.

So there was a lot of special casing for self-insert-command and
forward/backward-char (the redisplay had special shortcuts just for
those commands).  While this was not 100% incompatible with an Elisp
implementation, it was much more natural to write it in the C given
those constraints.

> 2. What it might look like /if/ it was written in Elisp?

The important part of the code is quoted below.  We do not actually
distinguish between a return value of 0 and 1 any more, so only the
value 2 is relevant.

So, here's what it does, more or less:
- call expand-abbrev is applicable.
- insert N chars at point.
- if in overwrite-mode, remove the corresponding number of chars.
- perform auto-fill if applicable.
- run post_self_insert_hook.

Rewriting it in Elisp would probably be a good idea.


        Stefan


/* Insert N times character C

   If this insertion is suitable for direct output (completely simple),
   return 0.  A value of 1 indicates this *might* not have been simple.
   A value of 2 means this did things that call for an undo boundary.  */

internal_self_insert (int c, EMACS_INT n)
{
  int hairy = 0;
  Lisp_Object tem;
  register enum syntaxcode synt;
  Lisp_Object overwrite;
  /* Length of multi-byte form of C.  */
  int len;
  /* Working buffer and pointer for multi-byte form of C.  */
  unsigned char str[MAX_MULTIBYTE_LENGTH];
  ptrdiff_t chars_to_delete = 0;
  ptrdiff_t spaces_to_insert = 0;

  overwrite = BVAR (current_buffer, overwrite_mode);
  if (!NILP (Vbefore_change_functions) || !NILP (Vafter_change_functions))
    hairy = 1;

  /* At first, get multi-byte form of C in STR.  */
  if (!NILP (BVAR (current_buffer, enable_multibyte_characters)))
    {
      len = CHAR_STRING (c, str);
      if (len == 1)
	/* If C has modifier bits, this makes C an appropriate
           one-byte char.  */
	c = *str;
    }
  else
    {
      str[0] = SINGLE_BYTE_CHAR_P (c) ? c : CHAR_TO_BYTE8 (c);
      len = 1;
    }
  if (!NILP (overwrite)
      && PT < ZV)
    {
      /* In overwrite-mode, we substitute a character at point (C2,
	 hereafter) by C.  For that, we delete C2 in advance.  But,
	 just substituting C2 by C may move a remaining text in the
	 line to the right or to the left, which is not preferable.
	 So we insert more spaces or delete more characters in the
	 following cases: if C is narrower than C2, after deleting C2,
	 we fill columns with spaces, if C is wider than C2, we delete
	 C2 and several characters following C2.  */

      /* This is the character after point.  */
      int c2 = FETCH_CHAR (PT_BYTE);

      int cwidth;

      /* Overwriting in binary-mode always replaces C2 by C.
	 Overwriting in textual-mode doesn't always do that.
	 It inserts newlines in the usual way,
	 and inserts any character at end of line
	 or before a tab if it doesn't use the whole width of the tab.  */
      if (EQ (overwrite, Qoverwrite_mode_binary))
	chars_to_delete = min (n, PTRDIFF_MAX);
      else if (c != '\n' && c2 != '\n'
	       && (cwidth = XFASTINT (Fchar_width (make_number (c)))) != 0)
	{
	  ptrdiff_t pos = PT;
	  ptrdiff_t pos_byte = PT_BYTE;
	  ptrdiff_t curcol = current_column ();

	  if (n <= (min (MOST_POSITIVE_FIXNUM, PTRDIFF_MAX) - curcol) / cwidth)
	    {
	      /* Column the cursor should be placed at after this insertion.
		 The value should be calculated only when necessary.  */
	      ptrdiff_t target_clm = curcol + n * cwidth;

	      /* The actual cursor position after the trial of moving
		 to column TARGET_CLM.  It is greater than TARGET_CLM
		 if the TARGET_CLM is middle of multi-column
		 character.  In that case, the new point is set after
		 that character.  */
	      ptrdiff_t actual_clm
		= XFASTINT (Fmove_to_column (make_number (target_clm), Qnil));

	      chars_to_delete = PT - pos;

	      if (actual_clm > target_clm)
		{
		  /* We will delete too many columns.  Let's fill columns
		     by spaces so that the remaining text won't move.  */
		  ptrdiff_t actual = PT_BYTE;
		  DEC_POS (actual);
		  if (FETCH_CHAR (actual) == '\t')
		    /* Rather than add spaces, let's just keep the tab. */
		    chars_to_delete--;
		  else
		    spaces_to_insert = actual_clm - target_clm;
		}

	      SET_PT_BOTH (pos, pos_byte);
	    }
	}
      hairy = 2;
    }

  synt = SYNTAX (c);

  if (!NILP (BVAR (current_buffer, abbrev_mode))
      && synt != Sword
      && NILP (BVAR (current_buffer, read_only))
      && PT > BEGV
      && (SYNTAX (!NILP (BVAR (current_buffer, enable_multibyte_characters))
		  ? XFASTINT (Fprevious_char ())
		  : UNIBYTE_TO_CHAR (XFASTINT (Fprevious_char ())))
	  == Sword))
    {
      EMACS_INT modiff = MODIFF;
      Lisp_Object sym;

      sym = call0 (Qexpand_abbrev);

      /* If we expanded an abbrev which has a hook,
	 and the hook has a non-nil `no-self-insert' property,
	 return right away--don't really self-insert.  */
      if (SYMBOLP (sym) && ! NILP (sym)
	  && ! NILP (XSYMBOL (sym)->function)
	  && SYMBOLP (XSYMBOL (sym)->function))
	{
	  Lisp_Object prop;
	  prop = Fget (XSYMBOL (sym)->function, intern ("no-self-insert"));
	  if (! NILP (prop))
	    return 1;
	}

      if (MODIFF != modiff)
	hairy = 2;
    }

  if (chars_to_delete)
    {
      int mc = ((NILP (BVAR (current_buffer, enable_multibyte_characters))
		 && SINGLE_BYTE_CHAR_P (c))
		? UNIBYTE_TO_CHAR (c) : c);
      Lisp_Object string = Fmake_string (make_number (n), make_number (mc));

      if (spaces_to_insert)
	{
	  tem = Fmake_string (make_number (spaces_to_insert),
			      make_number (' '));
	  string = concat2 (string, tem);
	}

      replace_range (PT, PT + chars_to_delete, string, 1, 1, 1);
      Fforward_char (make_number (n + spaces_to_insert));
    }
  else if (n > 1)
    {
      USE_SAFE_ALLOCA;
      char *strn, *p;
      SAFE_NALLOCA (strn, len, n);
      for (p = strn; n > 0; n--, p += len)
	memcpy (p, str, len);
      insert_and_inherit (strn, p - strn);
      SAFE_FREE ();
    }
  else if (n > 0)
    insert_and_inherit ((char *) str, len);

  if ((CHAR_TABLE_P (Vauto_fill_chars)
       ? !NILP (CHAR_TABLE_REF (Vauto_fill_chars, c))
       : (c == ' ' || c == '\n'))
      && !NILP (BVAR (current_buffer, auto_fill_function)))
    {
      Lisp_Object auto_fill_result;

      if (c == '\n')
	/* After inserting a newline, move to previous line and fill
	   that.  Must have the newline in place already so filling and
	   justification, if any, know where the end is going to be.  */
	SET_PT_BOTH (PT - 1, PT_BYTE - 1);
      auto_fill_result = call0 (BVAR (current_buffer, auto_fill_function));
      /* Test PT < ZV in case the auto-fill-function is strange.  */
      if (c == '\n' && PT < ZV)
	SET_PT_BOTH (PT + 1, PT_BYTE + 1);
      if (!NILP (auto_fill_result))
	hairy = 2;
    }

  /* Run hooks for electric keys.  */
  Frun_hooks (1, &Qpost_self_insert_hook);

  return hairy;
}




^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: self-insert-command source code?
       [not found] <mailman.15288.1417730351.1147.help-gnu-emacs@gnu.org>
@ 2014-12-05  9:01 ` Pascal J. Bourguignon
  0 siblings, 0 replies; 5+ messages in thread
From: Pascal J. Bourguignon @ 2014-12-05  9:01 UTC (permalink / raw)
  To: help-gnu-emacs

Marcin Borkowski <mbork@wmi.amu.edu.pl> writes:

> To my disappointment, I've just found out that self-insert-command is
> written in C and not in Elisp.  I don't speak C very well (and don't
> have the sources for Emacs installed (yet)), so could anyone shed some
> light on these two questions (just in some spare time – it's not that I
> /need/ it now).

Always install the sources!


> 1. Why is that so?  Since this is an interactive function, performance
> should not be the issue.

It could have been, 30 years ago!


> 2. What it might look like /if/ it was written in Elisp?

    /* Note that there's code in command_loop_1 which typically avoids
       calling this.  */
    DEFUN ("self-insert-command", Fself_insert_command, Sself_insert_command, 1, 1, "p",
           doc: /* Insert the character you type.
    Whichever character you type to run this command is inserted.
    Before insertion, `expand-abbrev' is executed if the inserted character does
    not have word syntax and the previous character in the buffer does.
    After insertion, the value of `auto-fill-function' is called if the
    `auto-fill-chars' table has a non-nil value for the inserted character.
    At the end, it runs `post-self-insert-hook'.  */)
      (Lisp_Object n)
    {
      bool remove_boundary = 1;
      CHECK_NATNUM (n);

      if (!EQ (Vthis_command, KVAR (current_kboard, Vlast_command)))
        nonundocount = 0;

      if (NILP (Vexecuting_kbd_macro)
          && !EQ (minibuf_window, selected_window))
        {
          if (nonundocount <= 0 || nonundocount >= 20)
        {
          remove_boundary = 0;
          nonundocount = 0;
        }
          nonundocount++;
        }

      if (remove_boundary
          && CONSP (BVAR (current_buffer, undo_list))
          && NILP (XCAR (BVAR (current_buffer, undo_list)))
          /* Only remove auto-added boundaries, not boundaries
         added be explicit calls to undo-boundary.  */
          && EQ (BVAR (current_buffer, undo_list), last_undo_boundary))
        /* Remove the undo_boundary that was just pushed.  */
        bset_undo_list (current_buffer, XCDR (BVAR (current_buffer, undo_list)));

      /* Barf if the key that invoked this was not a character.  */
      if (!CHARACTERP (last_command_event))
        bitch_at_user ();
      {
        int character = translate_char (Vtranslation_table_for_input,
                        XINT (last_command_event));
        int val = internal_self_insert (character, XFASTINT (n));
        if (val == 2)
          nonundocount = 0;
        frame_make_pointer_invisible ();
      }

      return Qnil;
    }


would translate to:

    (defvar nonundocount 0)

    (defun self-insert-command (n)
      "Insert the character you type.
    Whichever character you type to run this command is inserted.
    Before insertion, `expand-abbrev' is executed if the inserted character does
    not have word syntax and the previous character in the buffer does.
    After insertion, the value of `auto-fill-function' is called if the
    `auto-fill-chars' table has a non-nil value for the inserted character.
    At the end, it runs `post-self-insert-hook'."
      (interactive "p")
      (let ((remove-boundary t))
        (check-type times (integer 0))
        (unless (eq this-command (last-command current-kboard))
          (setf nonundocount 0))
        (when (and (null executing-kbd-macro)
                   (not (eq minibuf-window selected-window)))
          (when (or (<= nonundocount 0) (<= 20 nonundocount))
            (setf remove-boundary nil
                  nonundocount 0))
          (incf nonundocount))
        (when (and remove-boundary
                   (consp (undo-list current-buffer))
                   (null (car (undo-list current-buffer)))
                   ;; Only remove auto-added boundaries, not boundaries
                   ;; added be explicit calls to undo-boundary. 
                   (eq (undo-list current-buffer) last-undo-boundary))
          ;; Remove the undo_boundary that was just pushed. 
          (bset-undo-list current-buffer (cdr (undo-list current-buffer))))
        (unless (characterp last-command-event)
          (bitch-at-user))
        (let* ((character (translate-char translation-table-for-input
                                          (coerce last-command-event 'integer)))
               (val (internal-self-insert character (coerce n 'fixnum))))
          (when (= 2 val)
            (setf nonundocount 0))
          (frame-make-pointer-invisible))
        nil))


-- 
__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] 5+ messages in thread

* Re: self-insert-command source code?
  2014-12-04 22:23 ` Doug Lewan
@ 2014-12-05 12:20   ` Nicolas Richard
  0 siblings, 0 replies; 5+ messages in thread
From: Nicolas Richard @ 2014-12-05 12:20 UTC (permalink / raw)
  To: Doug Lewan; +Cc: Help Gnu Emacs mailing list, Marcin Borkowski

Doug Lewan <dougl@shubertticketing.com> writes:
> M-x local-set-key <RET> a <RET> my-self-insert <RET>
> should activate it in the current buffer on key "a".

Beware, local-set-key uses the local map, which is usually the major
mode map. i.e. shared by all buffers with the same major mode (existing
and future).

-- 
Nicolas Richard



^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2014-12-05 12:20 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-12-04 21:58 self-insert-command source code? Marcin Borkowski
2014-12-04 22:23 ` Doug Lewan
2014-12-05 12:20   ` Nicolas Richard
2014-12-04 22:51 ` Stefan Monnier
     [not found] <mailman.15288.1417730351.1147.help-gnu-emacs@gnu.org>
2014-12-05  9:01 ` Pascal J. Bourguignon

Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.