comparison src/editfns.c @ 26058:c11f0832a7c5

(Fconstrain_to_field): Make sure we don't violate the argument preconditions of find_before_next_newline in the case where both ONLY_IN_LINE and ESCAPE_FROM_EDGE are set and OLD_POS was indeed at the edge. (text_property_eq, text_property_stickiness): Don't use initializers for auto variables of type Lisp_Object. (find_field): Likewise. Use braces around nested ifs. (Fline_end_position): Store the raw eol in a variable, so that the final expression doesn't look so ugly. (Fconstrain_to_field): Doc fix. (preceding_pos): Renamed from `preceeding_pos'. (text_property_stickiness, find_field): Call preceding_pos, not preceeding_pos. (Ffield_string_no_properties): New function. (text_property_stickiness, preceeding_pos): New functions. (Ffield_string): Remove PROPS parameter. (find_field): Add MERGE_AT_BOUNDARY parameter. Rewrite to use stickiness of `field' property to resolve ambiguous cases. (Ffield_beginning, Ffield_end): Add ESCAPE_FROM_EDGE parameter. (Fconstrain_to_field): Likewise. (syms_of_editfns): Init Sfield_string_no_properties. (Ffield_string, Ferase_field, Ffield_end): Supply new MERGE_AT_BOUNDARY argument to find_field. (Fline_beginning_position, Fline_end_position): Supply new ESCAPE_FROM_EDGE parameter to Fconstrain_to_field. Pass a value of Qt for the ONLY_IN_LINE argument to Fconstrain_to_field (only matters if N != 1). (Fconstrain_to_field): Add get/set-current-point behavior when NEW_POS is nil. (find_field): Use XSETFASTINT instead of make_number. (Qfield): New variable. (find_field, Ferase_field, Ffield_string, Ffield_beginning, Ffield_end, Fconstrain_to_field): New functions. (Fline_beginning_position, Fline_end_position): Constrain to any field. (make_buffer_string_both): Remove minibuffer-prompt hack. (syms_of_editfns): Initialize Qfield, and subr entries for field functions above.
author Gerd Moellmann <gerd@gnu.org>
date Sun, 17 Oct 1999 12:55:00 +0000
parents 65cab65c4a28
children b7aa6ac26872
comparison
equal deleted inserted replaced
26057:0816bcaebc8c 26058:c11f0832a7c5
1 /* Lisp functions pertaining to editing. 1 /* Lisp functions pertaining to editing.
2 Copyright (C) 1985,86,87,89,93,94,95,96,97,98 Free Software Foundation, Inc. 2 Copyright (C) 1985,86,87,89,93,94,95,96,97,98, 1999 Free Software Foundation, Inc.
3 3
4 This file is part of GNU Emacs. 4 This file is part of GNU Emacs.
5 5
6 GNU Emacs is free software; you can redistribute it and/or modify 6 GNU Emacs is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by 7 it under the terms of the GNU General Public License as published by
274 () 274 ()
275 { 275 {
276 return current_buffer->mark; 276 return current_buffer->mark;
277 } 277 }
278 278
279 /* Returns the position before POS in the current buffer. POS must not
280 be at the beginning of the buffer. */
281 static Lisp_Object
282 preceding_pos (int pos)
283 {
284 int pos_byte = CHAR_TO_BYTE (pos);
285
286 /* Decrement POS_BYTE (is all this cruft really necessary?). */
287 if (NILP (current_buffer->enable_multibyte_characters))
288 pos_byte--;
289 else
290 DEC_POS (pos_byte);
291
292 return make_number (BYTE_TO_CHAR (pos_byte));
293 }
294
295 /* Returns true if POS1 and POS2 have the same value for text property PROP. */
296 static int
297 text_property_eq (prop, pos1, pos2)
298 Lisp_Object prop;
299 Lisp_Object pos1, pos2;
300 {
301 Lisp_Object pval1, pval2;
302
303 pval1 = Fget_text_property (pos1, prop, Qnil);
304 pval2 = Fget_text_property (pos2, prop, Qnil);
305
306 return EQ (pval1, pval2);
307 }
308
309 /* Returns the direction that the text-property PROP would be inherited
310 by any new text inserted at POS: 1 if it would be inherited from POS,
311 -1 if it would be inherited from POS-1, and 0 if from neither. */
312 static int
313 text_property_stickiness (prop, pos)
314 Lisp_Object prop;
315 Lisp_Object pos;
316 {
317 Lisp_Object front_sticky;
318
319 if (PT > BEGV)
320 /* Consider previous position. */
321 {
322 Lisp_Object prev_pos, rear_non_sticky;
323
324 prev_pos = preceding_pos (pos);
325 rear_non_sticky = Fget_text_property (prev_pos, Qrear_nonsticky, Qnil);
326
327 if (EQ (rear_non_sticky, Qnil)
328 || (CONSP (rear_non_sticky)
329 && !Fmemq (prop, rear_non_sticky)))
330 /* PROP is not rear-non-sticky, and since this takes precedence over
331 any front-stickiness, that must be the answer. */
332 return -1;
333 }
334
335 /* Consider current position. */
336 front_sticky = Fget_text_property (pos, Qfront_sticky, Qnil);
337
338 if (EQ (front_sticky, Qt)
339 || (CONSP (front_sticky)
340 && Fmemq (prop, front_sticky)))
341 /* PROP is front-sticky. */
342 return 1;
343
344 /* PROP is not sticky at all. */
345 return 0;
346 }
347
348 /* Name for the text property we use to distinguish fields. */
349 Lisp_Object Qfield;
350
351 /* Returns the field surrounding POS in *BEG and *END; an
352 `field' is a region of text with the same `field' property.
353 If POS is nil, the position of the current buffer's point is used.
354 If MERGE_AT_BOUNDARY is true, then if POS is at the very first
355 position of a field, then the beginning of the previous field
356 is returned instead of the beginning of POS's field (since the end of
357 a field is actually also the beginning of the next input
358 field, this behavior is sometimes useful). BEG or END may be 0, in
359 which case the corresponding value is not returned. */
360 void
361 find_field (pos, merge_at_boundary, beg, end)
362 Lisp_Object pos;
363 Lisp_Object merge_at_boundary;
364 int *beg, *end;
365 {
366 /* If POS is at the edge of a field, then -1 or 1 depending on
367 whether it should be considered as the beginning of the following
368 field, or the end of the previous field, respectively. If POS is
369 not at a field-boundary, then STICKINESS is 0. */
370 int stickiness = 0;
371
372 if (NILP (pos))
373 XSETFASTINT (pos, PT);
374 else
375 CHECK_NUMBER_COERCE_MARKER (pos, 0);
376
377 if (NILP (merge_at_boundary) && XFASTINT (pos) > BEGV)
378 /* See if we need to handle the case where POS is at beginning of a
379 field, which can also be interpreted as the end of the previous
380 field. We decide which one by seeing which field the `field'
381 property sticks to. The case where if MERGE_AT_BOUNDARY is
382 non-nil (see function comment) is actually the more natural one;
383 then we avoid treating the beginning of a field specially. */
384 {
385 /* First see if POS is actually *at* a boundary. */
386 Lisp_Object after_field, before_field;
387
388 after_field = Fget_text_property (pos, Qfield, Qnil);
389 before_field = Fget_text_property (preceding_pos (pos), Qfield, Qnil);
390
391 if (! EQ (after_field, before_field))
392 /* We are at a boundary, see which direction is inclusive. */
393 {
394 stickiness = text_property_stickiness (Qfield, pos);
395
396 if (stickiness == 0)
397 /* STICKINESS == 0 means that any inserted text will get a
398 `field' text-property of nil, so check to see if that
399 matches either of the adjacent characters (this being a
400 kind of `stickiness by default'). */
401 {
402 if (NILP (before_field))
403 stickiness = -1; /* Sticks to the left. */
404 else if (NILP (after_field))
405 stickiness = 1; /* Sticks to the right. */
406 }
407 }
408 }
409
410 if (beg)
411 {
412 if (stickiness > 0)
413 /* POS is at the edge of a field, and we should consider it as
414 the beginning of the following field. */
415 *beg = XFASTINT (pos);
416 else
417 /* Find the previous field boundary. */
418 {
419 Lisp_Object prev;
420 prev = Fprevious_single_property_change (pos, Qfield, Qnil, Qnil);
421 *beg = NILP(prev) ? BEGV : XFASTINT (prev);
422 }
423 }
424
425 if (end)
426 {
427 if (stickiness < 0)
428 /* POS is at the edge of a field, and we should consider it as
429 the end of the previous field. */
430 *end = XFASTINT (pos);
431 else
432 /* Find the next field boundary. */
433 {
434 Lisp_Object next;
435 next = Fnext_single_property_change (pos, Qfield, Qnil, Qnil);
436 *end = NILP(next) ? ZV : XFASTINT (next);
437 }
438 }
439 }
440
441 DEFUN ("erase-field", Ferase_field, Serase_field, 0, 1, "d",
442 "Erases the field surrounding POS.\n\
443 A field is a region of text with the same `field' property.\n\
444 If POS is nil, the position of the current buffer's point is used.")
445 (pos)
446 Lisp_Object pos;
447 {
448 int beg, end;
449 find_field (pos, Qnil, &beg, &end);
450 if (beg != end)
451 del_range (beg, end);
452 }
453
454 DEFUN ("field-string", Ffield_string, Sfield_string, 0, 1, 0,
455 "Return the contents of the field surrounding POS as a string.\n\
456 A field is a region of text with the same `field' property.\n\
457 If POS is nil, the position of the current buffer's point is used.")
458 (pos)
459 Lisp_Object pos;
460 {
461 int beg, end;
462 find_field (pos, Qnil, &beg, &end);
463 return make_buffer_string (beg, end, 1);
464 }
465
466 DEFUN ("field-string-no-properties", Ffield_string_no_properties, Sfield_string_no_properties, 0, 1, 0,
467 "Return the contents of the field around POS, without text-properties.\n\
468 A field is a region of text with the same `field' property.\n\
469 If POS is nil, the position of the current buffer's point is used.")
470 (pos)
471 Lisp_Object pos;
472 {
473 int beg, end;
474 find_field (pos, Qnil, &beg, &end);
475 return make_buffer_string (beg, end, 0);
476 }
477
478 DEFUN ("field-beginning", Ffield_beginning, Sfield_beginning, 0, 2, 0,
479 "Return the beginning of the field surrounding POS.\n\
480 A field is a region of text with the same `field' property.\n\
481 If POS is nil, the position of the current buffer's point is used.\n\
482 If ESCAPE-FROM-EDGE is non-nil and POS is already at beginning of an\n\
483 field, then the beginning of the *previous* field is returned.")
484 (pos, escape_from_edge)
485 Lisp_Object pos, escape_from_edge;
486 {
487 int beg;
488 find_field (pos, escape_from_edge, &beg, 0);
489 return make_number (beg);
490 }
491
492 DEFUN ("field-end", Ffield_end, Sfield_end, 0, 2, 0,
493 "Return the end of the field surrounding POS.\n\
494 A field is a region of text with the same `field' property.\n\
495 If POS is nil, the position of the current buffer's point is used.\n\
496 If ESCAPE-FROM-EDGE is non-nil and POS is already at end of a field,\n\
497 then the end of the *following* field is returned.")
498 (pos, escape_from_edge)
499 Lisp_Object pos, escape_from_edge;
500 {
501 int end;
502 find_field (pos, escape_from_edge, 0, &end);
503 return make_number (end);
504 }
505
506 DEFUN ("constrain-to-field", Fconstrain_to_field, Sconstrain_to_field, 2, 4, 0,
507 "Return the position closest to NEW-POS that is in the same field as OLD-POS.\n\
508 A field is a region of text with the same `field' property.\n\
509 If NEW-POS is nil, then the current point is used instead, and set to the\n\
510 constrained position if that is is different.\n\
511 \n\
512 If OLD-POS is at the boundary of two fields, then the allowable\n\
513 positions for NEW-POS depends on the value of the optional argument\n\
514 ESCAPE-FROM-EDGE: If ESCAPE-FROM-EDGE is nil, then NEW-POS is\n\
515 constrained to the field that has the same `field' text-property\n\
516 as any new characters inserted at OLD-POS, whereas if ESCAPE-FROM-EDGE\n\
517 is non-nil, NEW-POS is constrained to the union of the two adjacent\n\
518 fields.\n\
519 \n\
520 If the optional argument ONLY-IN-LINE is non-nil and constraining\n\
521 NEW-POS would move it to a different line, NEW-POS is returned\n\
522 unconstrained. This useful for commands that move by line, like\n\
523 \\[next-line] or \\[beginning-of-line], which should generally respect field boundaries\n\
524 only in the case where they can still move to the right line.")
525 (new_pos, old_pos, escape_from_edge, only_in_line)
526 Lisp_Object new_pos, old_pos, escape_from_edge, only_in_line;
527 {
528 /* If non-zero, then the original point, before re-positioning. */
529 int orig_point = 0;
530
531 if (NILP (new_pos))
532 /* Use the current point, and afterwards, set it. */
533 {
534 orig_point = PT;
535 XSETFASTINT (new_pos, PT);
536 }
537
538 if (!EQ (new_pos, old_pos) && !text_property_eq (Qfield, new_pos, old_pos))
539 /* NEW_POS is not within the same field as OLD_POS; try to
540 move NEW_POS so that it is. */
541 {
542 int fwd;
543 Lisp_Object field_bound;
544
545 CHECK_NUMBER_COERCE_MARKER (new_pos, 0);
546 CHECK_NUMBER_COERCE_MARKER (old_pos, 0);
547
548 fwd = (XFASTINT (new_pos) > XFASTINT (old_pos));
549
550 if (fwd)
551 field_bound = Ffield_end (old_pos, escape_from_edge);
552 else
553 field_bound = Ffield_beginning (old_pos, escape_from_edge);
554
555 if (/* If ONLY_IN_LINE is non-nil, we only constrain NEW_POS if doing
556 so would remain within the same line. */
557 NILP (only_in_line)
558 /* In that case, see if ESCAPE_FROM_EDGE caused FIELD_BOUND
559 to jump to the other side of NEW_POS, which would mean
560 that NEW_POS is already acceptable, and that we don't
561 have to do the line-check. */
562 || ((XFASTINT (field_bound) < XFASTINT (new_pos)) ? !fwd : fwd)
563 /* If not, see if there's no newline intervening between
564 NEW_POS and FIELD_BOUND. */
565 || (find_before_next_newline (XFASTINT (new_pos),
566 XFASTINT (field_bound),
567 fwd ? -1 : 1)
568 == XFASTINT (field_bound)))
569 /* Constrain NEW_POS to FIELD_BOUND. */
570 new_pos = field_bound;
571
572 if (orig_point && XFASTINT (new_pos) != orig_point)
573 /* The NEW_POS argument was originally nil, so automatically set PT. */
574 SET_PT (XFASTINT (new_pos));
575 }
576
577 return new_pos;
578 }
579
279 DEFUN ("line-beginning-position", Fline_beginning_position, Sline_beginning_position, 580 DEFUN ("line-beginning-position", Fline_beginning_position, Sline_beginning_position,
280 0, 1, 0, 581 0, 1, 0,
281 "Return the character position of the first character on the current line.\n\ 582 "Return the character position of the first character on the current line.\n\
282 With argument N not nil or 1, move forward N - 1 lines first.\n\ 583 With argument N not nil or 1, move forward N - 1 lines first.\n\
283 If scan reaches end of buffer, return that position.\n\ 584 If scan reaches end of buffer, return that position.\n\
298 orig = PT; 599 orig = PT;
299 orig_byte = PT_BYTE; 600 orig_byte = PT_BYTE;
300 Fforward_line (make_number (XINT (n) - 1)); 601 Fforward_line (make_number (XINT (n) - 1));
301 end = PT; 602 end = PT;
302 603
303 if (INTEGERP (current_buffer->prompt_end_charpos)
304 && orig >= XFASTINT (current_buffer->prompt_end_charpos)
305 && end < XFASTINT (current_buffer->prompt_end_charpos))
306 end = XFASTINT (current_buffer->prompt_end_charpos);
307
308 SET_PT_BOTH (orig, orig_byte); 604 SET_PT_BOTH (orig, orig_byte);
309 605
310 return make_number (end); 606 /* Return END constrained to the current input field. */
607 return Fconstrain_to_field (make_number (end), make_number (orig), Qnil, Qt);
311 } 608 }
312 609
313 DEFUN ("line-end-position", Fline_end_position, Sline_end_position, 610 DEFUN ("line-end-position", Fline_end_position, Sline_end_position,
314 0, 1, 0, 611 0, 1, 0,
315 "Return the character position of the last character on the current line.\n\ 612 "Return the character position of the last character on the current line.\n\
317 If scan reaches end of buffer, return that position.\n\ 614 If scan reaches end of buffer, return that position.\n\
318 This function does not move point.") 615 This function does not move point.")
319 (n) 616 (n)
320 Lisp_Object n; 617 Lisp_Object n;
321 { 618 {
619 int end_pos;
620 register int orig = PT;
621
322 if (NILP (n)) 622 if (NILP (n))
323 XSETFASTINT (n, 1); 623 XSETFASTINT (n, 1);
324 else 624 else
325 CHECK_NUMBER (n, 0); 625 CHECK_NUMBER (n, 0);
326 626
327 return make_number (find_before_next_newline 627 end_pos = find_before_next_newline (orig, 0, XINT (n) - (XINT (n) <= 0));
328 (PT, 0, XINT (n) - (XINT (n) <= 0))); 628
629 /* Return END_POS constrained to the current input field. */
630 return
631 Fconstrain_to_field (make_number (end_pos), make_number (orig), Qnil, Qt);
329 } 632 }
330 633
331 Lisp_Object 634 Lisp_Object
332 save_excursion_save () 635 save_excursion_save ()
333 { 636 {
1722 If narrowing is in effect, this function returns only the visible part\n\ 2025 If narrowing is in effect, this function returns only the visible part\n\
1723 of the buffer. If in a mini-buffer, don't include the prompt in the\n\ 2026 of the buffer. If in a mini-buffer, don't include the prompt in the\n\
1724 string returned.") 2027 string returned.")
1725 () 2028 ()
1726 { 2029 {
1727 int start = BEGV; 2030 return make_buffer_string (BEGV, ZV, 1);
1728
1729 if (INTEGERP (current_buffer->prompt_end_charpos))
1730 {
1731 int len = XFASTINT (current_buffer->prompt_end_charpos);
1732 start = min (ZV, max (len, start));
1733 }
1734
1735 return make_buffer_string (start, ZV, 1);
1736 } 2031 }
1737 2032
1738 DEFUN ("insert-buffer-substring", Finsert_buffer_substring, Sinsert_buffer_substring, 2033 DEFUN ("insert-buffer-substring", Finsert_buffer_substring, Sinsert_buffer_substring,
1739 1, 3, 0, 2034 1, 3, 0,
1740 "Insert before point a substring of the contents of buffer BUFFER.\n\ 2035 "Insert before point a substring of the contents of buffer BUFFER.\n\
3417 defsubr (&Smark_marker); 3712 defsubr (&Smark_marker);
3418 defsubr (&Spoint); 3713 defsubr (&Spoint);
3419 defsubr (&Sregion_beginning); 3714 defsubr (&Sregion_beginning);
3420 defsubr (&Sregion_end); 3715 defsubr (&Sregion_end);
3421 3716
3717 staticpro (&Qfield);
3718 Qfield = intern ("field");
3719 defsubr (&Sfield_beginning);
3720 defsubr (&Sfield_end);
3721 defsubr (&Sfield_string);
3722 defsubr (&Sfield_string_no_properties);
3723 defsubr (&Serase_field);
3724 defsubr (&Sconstrain_to_field);
3725
3422 defsubr (&Sline_beginning_position); 3726 defsubr (&Sline_beginning_position);
3423 defsubr (&Sline_end_position); 3727 defsubr (&Sline_end_position);
3424 3728
3425 /* defsubr (&Smark); */ 3729 /* defsubr (&Smark); */
3426 /* defsubr (&Sset_mark); */ 3730 /* defsubr (&Sset_mark); */