+
+\f
+/* Find all the overlays in the current buffer that touch position POS.
+ Return the number found, and store them in a vector in VEC
+ of length LEN. */
+
+static int
+overlays_around (pos, vec, len)
+ int pos;
+ Lisp_Object *vec;
+ int len;
+{
+ Lisp_Object overlay, start, end;
+ struct Lisp_Overlay *tail;
+ int startpos, endpos;
+ int idx = 0;
+
+ for (tail = current_buffer->overlays_before; tail; tail = tail->next)
+ {
+ XSETMISC (overlay, tail);
+
+ end = OVERLAY_END (overlay);
+ endpos = OVERLAY_POSITION (end);
+ if (endpos < pos)
+ break;
+ start = OVERLAY_START (overlay);
+ startpos = OVERLAY_POSITION (start);
+ if (startpos <= pos)
+ {
+ if (idx < len)
+ vec[idx] = overlay;
+ /* Keep counting overlays even if we can't return them all. */
+ idx++;
+ }
+ }
+
+ for (tail = current_buffer->overlays_after; tail; tail = tail->next)
+ {
+ XSETMISC (overlay, tail);
+
+ start = OVERLAY_START (overlay);
+ startpos = OVERLAY_POSITION (start);
+ if (pos < startpos)
+ break;
+ end = OVERLAY_END (overlay);
+ endpos = OVERLAY_POSITION (end);
+ if (pos <= endpos)
+ {
+ if (idx < len)
+ vec[idx] = overlay;
+ idx++;
+ }
+ }
+
+ return idx;
+}
+
+/* Return the value of property PROP, in OBJECT at POSITION.
+ It's the value of PROP that a char inserted at POSITION would get.
+ OBJECT is optional and defaults to the current buffer.
+ If OBJECT is a buffer, then overlay properties are considered as well as
+ text properties.
+ If OBJECT is a window, then that window's buffer is used, but
+ window-specific overlays are considered only if they are associated
+ with OBJECT. */
+Lisp_Object
+get_pos_property (position, prop, object)
+ Lisp_Object position, object;
+ register Lisp_Object prop;
+{
+ CHECK_NUMBER_COERCE_MARKER (position);
+
+ if (NILP (object))
+ XSETBUFFER (object, current_buffer);
+ else if (WINDOWP (object))
+ object = XWINDOW (object)->buffer;
+
+ if (!BUFFERP (object))
+ /* pos-property only makes sense in buffers right now, since strings
+ have no overlays and no notion of insertion for which stickiness
+ could be obeyed. */
+ return Fget_text_property (position, prop, object);
+ else
+ {
+ int posn = XINT (position);
+ int noverlays;
+ Lisp_Object *overlay_vec, tem;
+ struct buffer *obuf = current_buffer;
+
+ set_buffer_temp (XBUFFER (object));
+
+ /* First try with room for 40 overlays. */
+ noverlays = 40;
+ overlay_vec = (Lisp_Object *) alloca (noverlays * sizeof (Lisp_Object));
+ noverlays = overlays_around (posn, overlay_vec, noverlays);
+
+ /* If there are more than 40,
+ make enough space for all, and try again. */
+ if (noverlays > 40)
+ {
+ overlay_vec = (Lisp_Object *) alloca (noverlays * sizeof (Lisp_Object));
+ noverlays = overlays_around (posn, overlay_vec, noverlays);
+ }
+ noverlays = sort_overlays (overlay_vec, noverlays, NULL);
+
+ set_buffer_temp (obuf);
+
+ /* Now check the overlays in order of decreasing priority. */
+ while (--noverlays >= 0)
+ {
+ Lisp_Object ol = overlay_vec[noverlays];
+ tem = Foverlay_get (ol, prop);
+ if (!NILP (tem))
+ {
+ /* Check the overlay is indeed active at point. */
+ Lisp_Object start = OVERLAY_START (ol), finish = OVERLAY_END (ol);
+ if ((OVERLAY_POSITION (start) == posn
+ && XMARKER (start)->insertion_type == 1)
+ || (OVERLAY_POSITION (finish) == posn
+ && XMARKER (finish)->insertion_type == 0))
+ ; /* The overlay will not cover a char inserted at point. */
+ else
+ {
+ return tem;
+ }
+ }
+ }
+
+ { /* Now check the text-properties. */
+ int stickiness = text_property_stickiness (prop, position, object);
+ if (stickiness > 0)
+ return Fget_text_property (position, prop, object);
+ else if (stickiness < 0
+ && XINT (position) > BUF_BEGV (XBUFFER (object)))
+ return Fget_text_property (make_number (XINT (position) - 1),
+ prop, object);
+ else
+ return Qnil;
+ }
+ }
+}
+
+/* Find the field surrounding POS in *BEG and *END. If POS is nil,
+ the value of point is used instead. If BEG or END is null,
+ means don't store the beginning or end of the field.
+
+ BEG_LIMIT and END_LIMIT serve to limit the ranged of the returned
+ results; they do not effect boundary behavior.
+
+ If MERGE_AT_BOUNDARY is nonzero, then if POS is at the very first
+ position of a field, then the beginning of the previous field is
+ returned instead of the beginning of POS's field (since the end of a
+ field is actually also the beginning of the next input field, this
+ behavior is sometimes useful). Additionally in the MERGE_AT_BOUNDARY
+ true case, if two fields are separated by a field with the special
+ value `boundary', and POS lies within it, then the two separated
+ fields are considered to be adjacent, and POS between them, when
+ finding the beginning and ending of the "merged" field.
+
+ Either BEG or END may be 0, in which case the corresponding value
+ is not stored. */
+
+static void
+find_field (pos, merge_at_boundary, beg_limit, beg, end_limit, end)
+ Lisp_Object pos;
+ Lisp_Object merge_at_boundary;
+ Lisp_Object beg_limit, end_limit;
+ int *beg, *end;
+{
+ /* Fields right before and after the point. */
+ Lisp_Object before_field, after_field;
+ /* 1 if POS counts as the start of a field. */
+ int at_field_start = 0;
+ /* 1 if POS counts as the end of a field. */
+ int at_field_end = 0;
+
+ if (NILP (pos))
+ XSETFASTINT (pos, PT);
+ else
+ CHECK_NUMBER_COERCE_MARKER (pos);
+
+ after_field
+ = get_char_property_and_overlay (pos, Qfield, Qnil, NULL);
+ before_field
+ = (XFASTINT (pos) > BEGV
+ ? get_char_property_and_overlay (make_number (XINT (pos) - 1),
+ Qfield, Qnil, NULL)
+ /* Using nil here would be a more obvious choice, but it would
+ fail when the buffer starts with a non-sticky field. */
+ : after_field);
+
+ /* See if we need to handle the case where MERGE_AT_BOUNDARY is nil
+ and POS is at beginning of a field, which can also be interpreted
+ as the end of the previous field. Note that the case where if
+ MERGE_AT_BOUNDARY is non-nil (see function comment) is actually the
+ more natural one; then we avoid treating the beginning of a field
+ specially. */
+ if (NILP (merge_at_boundary))
+ {
+ Lisp_Object field = get_pos_property (pos, Qfield, Qnil);
+ if (!EQ (field, after_field))
+ at_field_end = 1;
+ if (!EQ (field, before_field))
+ at_field_start = 1;
+ if (NILP (field) && at_field_start && at_field_end)
+ /* If an inserted char would have a nil field while the surrounding
+ text is non-nil, we're probably not looking at a
+ zero-length field, but instead at a non-nil field that's
+ not intended for editing (such as comint's prompts). */
+ at_field_end = at_field_start = 0;
+ }
+
+ /* Note about special `boundary' fields:
+
+ Consider the case where the point (`.') is between the fields `x' and `y':
+
+ xxxx.yyyy
+
+ In this situation, if merge_at_boundary is true, we consider the
+ `x' and `y' fields as forming one big merged field, and so the end
+ of the field is the end of `y'.
+
+ However, if `x' and `y' are separated by a special `boundary' field
+ (a field with a `field' char-property of 'boundary), then we ignore
+ this special field when merging adjacent fields. Here's the same
+ situation, but with a `boundary' field between the `x' and `y' fields:
+
+ xxx.BBBByyyy
+
+ Here, if point is at the end of `x', the beginning of `y', or
+ anywhere in-between (within the `boundary' field), we merge all
+ three fields and consider the beginning as being the beginning of
+ the `x' field, and the end as being the end of the `y' field. */
+
+ if (beg)
+ {
+ if (at_field_start)
+ /* POS is at the edge of a field, and we should consider it as
+ the beginning of the following field. */
+ *beg = XFASTINT (pos);
+ else
+ /* Find the previous field boundary. */
+ {
+ Lisp_Object p = pos;
+ if (!NILP (merge_at_boundary) && EQ (before_field, Qboundary))
+ /* Skip a `boundary' field. */
+ p = Fprevious_single_char_property_change (p, Qfield, Qnil,
+ beg_limit);
+
+ p = Fprevious_single_char_property_change (p, Qfield, Qnil,
+ beg_limit);
+ *beg = NILP (p) ? BEGV : XFASTINT (p);
+ }
+ }
+
+ if (end)
+ {
+ if (at_field_end)
+ /* POS is at the edge of a field, and we should consider it as
+ the end of the previous field. */
+ *end = XFASTINT (pos);
+ else
+ /* Find the next field boundary. */
+ {
+ if (!NILP (merge_at_boundary) && EQ (after_field, Qboundary))
+ /* Skip a `boundary' field. */
+ pos = Fnext_single_char_property_change (pos, Qfield, Qnil,
+ end_limit);
+
+ pos = Fnext_single_char_property_change (pos, Qfield, Qnil,
+ end_limit);
+ *end = NILP (pos) ? ZV : XFASTINT (pos);
+ }
+ }
+}
+
+\f
+DEFUN ("delete-field", Fdelete_field, Sdelete_field, 0, 1, 0,
+ doc: /* Delete the field surrounding POS.
+A field is a region of text with the same `field' property.
+If POS is nil, the value of point is used for POS. */)
+ (pos)
+ Lisp_Object pos;
+{
+ int beg, end;
+ find_field (pos, Qnil, Qnil, &beg, Qnil, &end);
+ if (beg != end)
+ del_range (beg, end);
+ return Qnil;
+}
+
+DEFUN ("field-string", Ffield_string, Sfield_string, 0, 1, 0,
+ doc: /* Return the contents of the field surrounding POS as a string.
+A field is a region of text with the same `field' property.
+If POS is nil, the value of point is used for POS. */)
+ (pos)
+ Lisp_Object pos;
+{
+ int beg, end;
+ find_field (pos, Qnil, Qnil, &beg, Qnil, &end);
+ return make_buffer_string (beg, end, 1);
+}
+
+DEFUN ("field-string-no-properties", Ffield_string_no_properties, Sfield_string_no_properties, 0, 1, 0,
+ doc: /* Return the contents of the field around POS, without text-properties.
+A field is a region of text with the same `field' property.
+If POS is nil, the value of point is used for POS. */)
+ (pos)
+ Lisp_Object pos;
+{
+ int beg, end;
+ find_field (pos, Qnil, Qnil, &beg, Qnil, &end);
+ return make_buffer_string (beg, end, 0);
+}
+
+DEFUN ("field-beginning", Ffield_beginning, Sfield_beginning, 0, 3, 0,
+ doc: /* Return the beginning of the field surrounding POS.
+A field is a region of text with the same `field' property.
+If POS is nil, the value of point is used for POS.
+If ESCAPE-FROM-EDGE is non-nil and POS is at the beginning of its
+field, then the beginning of the *previous* field is returned.
+If LIMIT is non-nil, it is a buffer position; if the beginning of the field
+is before LIMIT, then LIMIT will be returned instead. */)
+ (pos, escape_from_edge, limit)
+ Lisp_Object pos, escape_from_edge, limit;
+{
+ int beg;
+ find_field (pos, escape_from_edge, limit, &beg, Qnil, 0);
+ return make_number (beg);
+}
+
+DEFUN ("field-end", Ffield_end, Sfield_end, 0, 3, 0,
+ doc: /* Return the end of the field surrounding POS.
+A field is a region of text with the same `field' property.
+If POS is nil, the value of point is used for POS.
+If ESCAPE-FROM-EDGE is non-nil and POS is at the end of its field,
+then the end of the *following* field is returned.
+If LIMIT is non-nil, it is a buffer position; if the end of the field
+is after LIMIT, then LIMIT will be returned instead. */)
+ (pos, escape_from_edge, limit)
+ Lisp_Object pos, escape_from_edge, limit;
+{
+ int end;
+ find_field (pos, escape_from_edge, Qnil, 0, limit, &end);
+ return make_number (end);
+}
+
+DEFUN ("constrain-to-field", Fconstrain_to_field, Sconstrain_to_field, 2, 5, 0,
+ doc: /* 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 the current point is used instead, and set to the
+constrained position if that is different.
+
+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 useful for commands that move by line, like
+\\[next-line] or \\[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. */)
+ (new_pos, old_pos, escape_from_edge, only_in_line, inhibit_capture_property)
+ Lisp_Object new_pos, old_pos;
+ Lisp_Object escape_from_edge, only_in_line, inhibit_capture_property;
+{
+ /* If non-zero, then the original point, before re-positioning. */
+ int orig_point = 0;
+ int 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_NUMBER_COERCE_MARKER (new_pos);
+ CHECK_NUMBER_COERCE_MARKER (old_pos);
+
+ fwd = (XFASTINT (new_pos) > XFASTINT (old_pos));
+
+ prev_old = make_number (XFASTINT (old_pos) - 1);
+ prev_new = make_number (XFASTINT (new_pos) - 1);
+
+ if (NILP (Vinhibit_field_text_motion)
+ && !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 `get_pos_property'
+ instead, but in itself that would fail inside non-sticky
+ fields (like comint prompts). */
+ || (XFASTINT (new_pos) > BEGV
+ && !NILP (Fget_char_property (prev_new, Qfield, Qnil)))
+ || (XFASTINT (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 (get_pos_property (old_pos, inhibit_capture_property, Qnil))
+ && (XFASTINT (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. */
+ {
+ int shortage;
+ 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. */
+ ((XFASTINT (field_bound) < XFASTINT (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. */
+ || (scan_buffer ('\n',
+ XFASTINT (new_pos), XFASTINT (field_bound),
+ fwd ? -1 : 1, &shortage, 1),
+ shortage != 0)))
+ /* Constrain NEW_POS to FIELD_BOUND. */
+ new_pos = field_bound;
+
+ if (orig_point && XFASTINT (new_pos) != orig_point)
+ /* The NEW_POS argument was originally nil, so automatically set PT. */
+ SET_PT (XFASTINT (new_pos));
+ }
+
+ return new_pos;
+}
+