1 /* Simple built-in editing commands.
3 Copyright (C) 1985, 1993-1998, 2001-2017 Free Software Foundation, Inc.
5 This file is part of GNU Emacs.
7 GNU Emacs is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or (at
10 your option) any later version.
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
25 #include "character.h"
32 static int internal_self_insert (int, EMACS_INT
);
34 DEFUN ("forward-point", Fforward_point
, Sforward_point
, 1, 1, 0,
35 doc
: /* Return buffer position N characters after (before if N negative) point. */)
40 return make_number (PT
+ XINT (n
));
43 /* Add N to point; or subtract N if FORWARD is false. N defaults to 1.
44 Validate the new location. Return nil. */
46 move_point (Lisp_Object n
, bool forward
)
48 /* This used to just set point to point + XINT (n), and then check
49 to see if it was within boundaries. But now that SET_PT can
50 potentially do a lot of stuff (calling entering and exiting
51 hooks, etcetera), that's not a good approach. So we validate the
52 proposed position, then set point. */
61 new_point
= PT
+ (forward
? XINT (n
) : - XINT (n
));
66 xsignal0 (Qbeginning_of_buffer
);
71 xsignal0 (Qend_of_buffer
);
78 DEFUN ("forward-char", Fforward_char
, Sforward_char
, 0, 1, "^p",
79 doc
: /* Move point N characters forward (backward if N is negative).
80 On reaching end or beginning of buffer, stop and signal error.
81 Interactively, N is the numeric prefix argument.
82 If N is omitted or nil, move point 1 character forward.
84 Depending on the bidirectional context, the movement may be to the
85 right or to the left on the screen. This is in contrast with
86 \\[right-char], which see. */)
89 return move_point (n
, 1);
92 DEFUN ("backward-char", Fbackward_char
, Sbackward_char
, 0, 1, "^p",
93 doc
: /* Move point N characters backward (forward if N is negative).
94 On attempt to pass beginning or end of buffer, stop and signal error.
95 Interactively, N is the numeric prefix argument.
96 If N is omitted or nil, move point 1 character backward.
98 Depending on the bidirectional context, the movement may be to the
99 right or to the left on the screen. This is in contrast with
100 \\[left-char], which see. */)
103 return move_point (n
, 0);
106 DEFUN ("forward-line", Fforward_line
, Sforward_line
, 0, 1, "^p",
107 doc
: /* Move N lines forward (backward if N is negative).
108 Precisely, if point is on line I, move to the start of line I + N
109 \("start of line" in the logical order).
110 If there isn't room, go as far as possible (no error).
112 Returns the count of lines left to move. If moving forward,
113 that is N minus number of lines moved; if backward, N plus number
116 Exception: With positive N, a non-empty line at the end of the
117 buffer, or of its accessible portion, counts as one line
118 successfully moved (for the return value). This means that the
119 function will move point to the end of such a line and will count
120 it as a line moved across, even though there is no next line to
121 go to its beginning. */)
124 ptrdiff_t opoint
= PT
, pos
, pos_byte
, shortage
, count
;
134 shortage
= scan_newline_from_point (count
, &pos
, &pos_byte
);
136 SET_PT_BOTH (pos
, pos_byte
);
142 && (FETCH_BYTE (PT_BYTE
- 1) != '\n'))))
145 return make_number (count
<= 0 ? - shortage
: shortage
);
148 DEFUN ("beginning-of-line", Fbeginning_of_line
, Sbeginning_of_line
, 0, 1, "^p",
149 doc
: /* Move point to beginning of current line (in the logical order).
150 With argument N not nil or 1, move forward N - 1 lines first.
151 If point reaches the beginning or end of buffer, it stops there.
153 This function constrains point to the current field unless this moves
154 point to a different line than the original, unconstrained result.
155 If N is nil or 1, and a front-sticky field starts at point, the point
156 does not move. To ignore field boundaries bind
157 `inhibit-field-text-motion' to t, or use the `forward-line' function
158 instead. For instance, `(forward-line 0)' does the same thing as
159 `(beginning-of-line)', except that it ignores field boundaries. */)
167 SET_PT (XINT (Fline_beginning_position (n
)));
172 DEFUN ("end-of-line", Fend_of_line
, Send_of_line
, 0, 1, "^p",
173 doc
: /* Move point to end of current line (in the logical order).
174 With argument N not nil or 1, move forward N - 1 lines first.
175 If point reaches the beginning or end of buffer, it stops there.
176 To ignore intangibility, bind `inhibit-point-motion-hooks' to t.
178 This function constrains point to the current field unless this moves
179 point to a different line than the original, unconstrained result. If
180 N is nil or 1, and a rear-sticky field ends at point, the point does
181 not move. To ignore field boundaries bind `inhibit-field-text-motion'
194 newpos
= XINT (Fline_end_position (n
));
198 && FETCH_CHAR (PT
- 1) == '\n')
200 /* If we skipped over a newline that follows
201 an invisible intangible run,
202 move back to the last tangible position
208 else if (PT
> newpos
&& PT
< ZV
209 && FETCH_CHAR (PT
) != '\n')
210 /* If we skipped something intangible
211 and now we're not really at eol,
221 DEFUN ("delete-char", Fdelete_char
, Sdelete_char
, 1, 2, "p\nP",
222 doc
: /* Delete the following N characters (previous if N is negative).
223 Optional second arg KILLFLAG non-nil means kill instead (save in kill ring).
224 Interactively, N is the prefix arg, and KILLFLAG is set if
225 N was explicitly specified.
227 The command `delete-forward-char' is preferable for interactive use, e.g.
228 because it respects values of `delete-active-region' and `overwrite-mode'. */)
229 (Lisp_Object n
, Lisp_Object killflag
)
235 if (eabs (XINT (n
)) < 2)
236 call0 (Qundo_auto_amalgamate
);
244 xsignal0 (Qbeginning_of_buffer
);
251 xsignal0 (Qend_of_buffer
);
258 call1 (Qkill_forward_chars
, n
);
263 /* Note that there's code in command_loop_1 which typically avoids
265 DEFUN ("self-insert-command", Fself_insert_command
, Sself_insert_command
, 1, 1, "p",
266 doc
: /* Insert the character you type.
267 Whichever character you type to run this command is inserted.
268 The numeric prefix argument N says how many times to repeat the insertion.
269 Before insertion, `expand-abbrev' is executed if the inserted character does
270 not have word syntax and the previous character in the buffer does.
271 After insertion, the value of `auto-fill-function' is called if the
272 `auto-fill-chars' table has a non-nil value for the inserted character.
273 At the end, it runs `post-self-insert-hook'. */)
279 error ("Negative repetition argument %"pI
"d", XINT (n
));
281 if (XFASTINT (n
) < 2)
282 call0 (Qundo_auto_amalgamate
);
284 /* Barf if the key that invoked this was not a character. */
285 if (!CHARACTERP (last_command_event
))
288 int character
= translate_char (Vtranslation_table_for_input
,
289 XINT (last_command_event
));
290 int val
= internal_self_insert (character
, XFASTINT (n
));
292 Fset (Qundo_auto__this_command_amalgamating
, Qnil
);
293 frame_make_pointer_invisible (SELECTED_FRAME ());
299 /* Insert N times character C
301 If this insertion is suitable for direct output (completely simple),
302 return 0. A value of 1 indicates this *might* not have been simple.
303 A value of 2 means this did things that call for an undo boundary. */
306 internal_self_insert (int c
, EMACS_INT n
)
310 register enum syntaxcode synt
;
311 Lisp_Object overwrite
;
312 /* Length of multi-byte form of C. */
314 /* Working buffer and pointer for multi-byte form of C. */
315 unsigned char str
[MAX_MULTIBYTE_LENGTH
];
316 ptrdiff_t chars_to_delete
= 0;
317 ptrdiff_t spaces_to_insert
= 0;
319 overwrite
= BVAR (current_buffer
, overwrite_mode
);
320 if (!NILP (Vbefore_change_functions
) || !NILP (Vafter_change_functions
))
323 /* At first, get multi-byte form of C in STR. */
324 if (!NILP (BVAR (current_buffer
, enable_multibyte_characters
)))
326 len
= CHAR_STRING (c
, str
);
328 /* If C has modifier bits, this makes C an appropriate
334 str
[0] = SINGLE_BYTE_CHAR_P (c
) ? c
: CHAR_TO_BYTE8 (c
);
337 if (!NILP (overwrite
)
340 /* In overwrite-mode, we substitute a character at point (C2,
341 hereafter) by C. For that, we delete C2 in advance. But,
342 just substituting C2 by C may move a remaining text in the
343 line to the right or to the left, which is not preferable.
344 So we insert more spaces or delete more characters in the
345 following cases: if C is narrower than C2, after deleting C2,
346 we fill columns with spaces, if C is wider than C2, we delete
347 C2 and several characters following C2. */
349 /* This is the character after point. */
350 int c2
= FETCH_CHAR (PT_BYTE
);
354 /* Overwriting in binary-mode always replaces C2 by C.
355 Overwriting in textual-mode doesn't always do that.
356 It inserts newlines in the usual way,
357 and inserts any character at end of line
358 or before a tab if it doesn't use the whole width of the tab. */
359 if (EQ (overwrite
, Qoverwrite_mode_binary
))
360 chars_to_delete
= min (n
, PTRDIFF_MAX
);
361 else if (c
!= '\n' && c2
!= '\n'
362 && (cwidth
= XFASTINT (Fchar_width (make_number (c
)))) != 0)
365 ptrdiff_t pos_byte
= PT_BYTE
;
366 ptrdiff_t curcol
= current_column ();
368 if (n
<= (min (MOST_POSITIVE_FIXNUM
, PTRDIFF_MAX
) - curcol
) / cwidth
)
370 /* Column the cursor should be placed at after this insertion.
371 The value should be calculated only when necessary. */
372 ptrdiff_t target_clm
= curcol
+ n
* cwidth
;
374 /* The actual cursor position after the trial of moving
375 to column TARGET_CLM. It is greater than TARGET_CLM
376 if the TARGET_CLM is middle of multi-column
377 character. In that case, the new point is set after
380 = XFASTINT (Fmove_to_column (make_number (target_clm
), Qnil
));
382 chars_to_delete
= PT
- pos
;
384 if (actual_clm
> target_clm
)
386 /* We will delete too many columns. Let's fill columns
387 by spaces so that the remaining text won't move. */
388 ptrdiff_t actual
= PT_BYTE
;
390 if (FETCH_CHAR (actual
) == '\t')
391 /* Rather than add spaces, let's just keep the tab. */
394 spaces_to_insert
= actual_clm
- target_clm
;
397 SET_PT_BOTH (pos
, pos_byte
);
405 if (!NILP (BVAR (current_buffer
, abbrev_mode
))
407 && NILP (BVAR (current_buffer
, read_only
))
409 && (SYNTAX (!NILP (BVAR (current_buffer
, enable_multibyte_characters
))
410 ? XFASTINT (Fprevious_char ())
411 : UNIBYTE_TO_CHAR (XFASTINT (Fprevious_char ())))
414 EMACS_INT modiff
= MODIFF
;
417 sym
= call0 (Qexpand_abbrev
);
419 /* If we expanded an abbrev which has a hook,
420 and the hook has a non-nil `no-self-insert' property,
421 return right away--don't really self-insert. */
422 if (SYMBOLP (sym
) && ! NILP (sym
)
423 && ! NILP (XSYMBOL (sym
)->function
)
424 && SYMBOLP (XSYMBOL (sym
)->function
))
427 prop
= Fget (XSYMBOL (sym
)->function
, intern ("no-self-insert"));
432 if (MODIFF
!= modiff
)
438 int mc
= ((NILP (BVAR (current_buffer
, enable_multibyte_characters
))
439 && SINGLE_BYTE_CHAR_P (c
))
440 ? UNIBYTE_TO_CHAR (c
) : c
);
441 Lisp_Object string
= Fmake_string (make_number (n
), make_number (mc
));
443 if (spaces_to_insert
)
445 tem
= Fmake_string (make_number (spaces_to_insert
),
447 string
= concat2 (string
, tem
);
450 replace_range (PT
, PT
+ chars_to_delete
, string
, 1, 1, 1, 0);
451 Fforward_char (make_number (n
));
457 SAFE_NALLOCA (strn
, len
, n
);
458 for (p
= strn
; n
> 0; n
--, p
+= len
)
459 memcpy (p
, str
, len
);
460 insert_and_inherit (strn
, p
- strn
);
464 insert_and_inherit ((char *) str
, len
);
466 if ((CHAR_TABLE_P (Vauto_fill_chars
)
467 ? !NILP (CHAR_TABLE_REF (Vauto_fill_chars
, c
))
468 : (c
== ' ' || c
== '\n'))
469 && !NILP (BVAR (current_buffer
, auto_fill_function
)))
471 Lisp_Object auto_fill_result
;
474 /* After inserting a newline, move to previous line and fill
475 that. Must have the newline in place already so filling and
476 justification, if any, know where the end is going to be. */
477 SET_PT_BOTH (PT
- 1, PT_BYTE
- 1);
478 auto_fill_result
= call0 (BVAR (current_buffer
, auto_fill_function
));
479 /* Test PT < ZV in case the auto-fill-function is strange. */
480 if (c
== '\n' && PT
< ZV
)
481 SET_PT_BOTH (PT
+ 1, PT_BYTE
+ 1);
482 if (!NILP (auto_fill_result
))
486 /* Run hooks for electric keys. */
487 run_hook (Qpost_self_insert_hook
);
492 /* module initialization */
497 DEFSYM (Qundo_auto_amalgamate
, "undo-auto-amalgamate");
498 DEFSYM (Qundo_auto__this_command_amalgamating
,
499 "undo-auto--this-command-amalgamating");
501 DEFSYM (Qkill_forward_chars
, "kill-forward-chars");
503 /* A possible value for a buffer's overwrite-mode variable. */
504 DEFSYM (Qoverwrite_mode_binary
, "overwrite-mode-binary");
506 DEFSYM (Qexpand_abbrev
, "expand-abbrev");
507 DEFSYM (Qpost_self_insert_hook
, "post-self-insert-hook");
509 DEFVAR_LISP ("post-self-insert-hook", Vpost_self_insert_hook
,
510 doc
: /* Hook run at the end of `self-insert-command'.
511 This is run after inserting the character. */);
512 Vpost_self_insert_hook
= Qnil
;
514 defsubr (&Sforward_point
);
515 defsubr (&Sforward_char
);
516 defsubr (&Sbackward_char
);
517 defsubr (&Sforward_line
);
518 defsubr (&Sbeginning_of_line
);
519 defsubr (&Send_of_line
);
521 defsubr (&Sdelete_char
);
522 defsubr (&Sself_insert_command
);
530 initial_define_key (global_map
, Ctl ('I'), "self-insert-command");
531 for (n
= 040; n
< 0177; n
++)
532 initial_define_key (global_map
, n
, "self-insert-command");
534 for (n
= 0200; n
< 0240; n
++)
535 initial_define_key (global_map
, n
, "self-insert-command");
537 for (n
= 0240; n
< 0400; n
++)
538 initial_define_key (global_map
, n
, "self-insert-command");
540 initial_define_key (global_map
, Ctl ('A'), "beginning-of-line");
541 initial_define_key (global_map
, Ctl ('B'), "backward-char");
542 initial_define_key (global_map
, Ctl ('E'), "end-of-line");
543 initial_define_key (global_map
, Ctl ('F'), "forward-char");