Function: constrain-to-field

constrain-to-field is a function defined in editfns.c.

Signature

(constrain-to-field NEW-POS OLD-POS &optional ESCAPE-FROM-EDGE ONLY-IN-LINE INHIBIT-CAPTURE-PROPERTY)

Documentation

Return the position closest to NEW-POS that is in the same field as OLD-POS.

A field is a region of text with the same field property.

If NEW-POS is nil, then use the current point instead, and move point to the resulting constrained position, in addition to returning that position.

If OLD-POS is at the boundary of two fields, then the allowable positions for NEW-POS depends on the value of the optional argument ESCAPE-FROM-EDGE: If ESCAPE-FROM-EDGE is nil, then NEW-POS is constrained to the field that has the same field char-property as any new characters inserted at OLD-POS, whereas if ESCAPE-FROM-EDGE is non-nil, NEW-POS is constrained to the union of the two adjacent fields. Additionally, if two fields are separated by another field with the special value boundary, then any point within this special field is also considered to be on the boundary.

If the optional argument ONLY-IN-LINE is non-nil and constraining NEW-POS would move it to a different line, NEW-POS is returned unconstrained. This is useful for commands that move by line, like C-n (next-line) or M-x beginning-of-line (beginning-of-line), which should generally respect field boundaries only in the case where they can still move to the right line.

If the optional argument INHIBIT-CAPTURE-PROPERTY is non-nil, and OLD-POS has a non-nil property of that name, then any field boundaries are ignored.

Field boundaries are not noticed if inhibit-field-text-motion is non-nil.

View in manual

Probably introduced at or before Emacs version 21.1.

Source Code

// Defined in /usr/src/emacs/src/editfns.c
{
  /* If non-zero, then the original point, before re-positioning.  */
  ptrdiff_t orig_point = 0;
  bool fwd;
  Lisp_Object prev_old, prev_new;

  if (NILP (new_pos))
    /* Use the current point, and afterwards, set it.  */
    {
      orig_point = PT;
      XSETFASTINT (new_pos, PT);
    }

  CHECK_FIXNUM_COERCE_MARKER (new_pos);
  CHECK_FIXNUM_COERCE_MARKER (old_pos);

  fwd = (XFIXNUM (new_pos) > XFIXNUM (old_pos));

  prev_old = make_fixnum (XFIXNUM (old_pos) - 1);
  prev_new = make_fixnum (XFIXNUM (new_pos) - 1);

  if (NILP (Vinhibit_field_text_motion)
      && !BASE_EQ (new_pos, old_pos)
      && (!NILP (Fget_char_property (new_pos, Qfield, Qnil))
          || !NILP (Fget_char_property (old_pos, Qfield, Qnil))
          /* To recognize field boundaries, we must also look at the
             previous positions; we could use `Fget_pos_property'
             instead, but in itself that would fail inside non-sticky
             fields (like comint prompts).  */
          || (XFIXNAT (new_pos) > BEGV
              && !NILP (Fget_char_property (prev_new, Qfield, Qnil)))
          || (XFIXNAT (old_pos) > BEGV
              && !NILP (Fget_char_property (prev_old, Qfield, Qnil))))
      && (NILP (inhibit_capture_property)
          /* Field boundaries are again a problem; but now we must
             decide the case exactly, so we need to call
             `get_pos_property' as well.  */
          || (NILP (Fget_pos_property (old_pos, inhibit_capture_property, Qnil))
              && (XFIXNAT (old_pos) <= BEGV
                  || NILP (Fget_char_property
			   (old_pos, inhibit_capture_property, Qnil))
                  || NILP (Fget_char_property
			   (prev_old, inhibit_capture_property, Qnil))))))
    /* It is possible that NEW_POS is not within the same field as
       OLD_POS; try to move NEW_POS so that it is.  */
    {
      ptrdiff_t counted;
      Lisp_Object field_bound;

      if (fwd)
	field_bound = Ffield_end (old_pos, escape_from_edge, new_pos);
      else
	field_bound = Ffield_beginning (old_pos, escape_from_edge, new_pos);

      if (/* See if ESCAPE_FROM_EDGE caused FIELD_BOUND to jump to the
             other side of NEW_POS, which would mean that NEW_POS is
             already acceptable, and it's not necessary to constrain it
             to FIELD_BOUND.  */
	  ((XFIXNAT (field_bound) < XFIXNAT (new_pos)) ? fwd : !fwd)
	  /* NEW_POS should be constrained, but only if either
	     ONLY_IN_LINE is nil (in which case any constraint is OK),
	     or NEW_POS and FIELD_BOUND are on the same line (in which
	     case the constraint is OK even if ONLY_IN_LINE is non-nil).  */
	  && (NILP (only_in_line)
	      /* This is the ONLY_IN_LINE case, check that NEW_POS and
		 FIELD_BOUND are on the same line by seeing whether
		 there's an intervening newline or not.  */
	      || (find_newline (XFIXNAT (new_pos), -1,
				XFIXNAT (field_bound), -1,
				fwd ? -1 : 1, &counted, NULL, 1),
		  counted == 0)))
	/* Constrain NEW_POS to FIELD_BOUND.  */
	new_pos = field_bound;

      if (orig_point && XFIXNAT (new_pos) != orig_point)
	/* The NEW_POS argument was originally nil, so automatically set PT. */
	SET_PT (XFIXNAT (new_pos));
    }

  return new_pos;
}