Function: bidi-find-overridden-directionality

bidi-find-overridden-directionality is a function defined in xdisp.c.

Signature

(bidi-find-overridden-directionality FROM TO OBJECT &optional BASE-DIR)

Documentation

Return position between FROM and TO where directionality was overridden.

This function returns the first character position in the specified region of OBJECT where characters have their bidirectional properties affected in a way that might make its text look confusingly on display. For example, characters whose bidi-class property is L, could be forced to display as R by a directional override, and likewise characters whose bidi-class is R or AL that are forced to display as L.

If no such character is found, the function returns nil.

OBJECT is a Lisp string or buffer to search for overridden directionality, and defaults to the current buffer if nil. OBJECT can also be a window, in which case the function will search the buffer displayed in that window. Passing the window instead of a buffer is preferable when the buffer is displayed in some window, because this function will then be able to correctly account for window-specific overlays, which can affect the results.

Optional argument BASE-DIR specifies the base paragraph directory of the text. It should be a symbol, either left-to-right or right-to-left, and defaults to left-to-right.

Strong directional characters L, R, and AL can have their intrinsic directionality overridden by directional override control characters RLO (u+202E) and LRO (u+202D). They can also have their directionality affected by other formatting control characters: LRE
(u+202A), RLE (u+202B), LRI (u+2066), and RLI (u+2067). See the
function get-char-code-property for a way to inquire about the bidi-class property of a character. Characters whose intrinsic directionality is weak or neutral, such as numbers or punctuation characters, can be forced to display in a very different place with respect of its surrounding characters, so as to make the surrounding text confuse the user regarding what the text says.

Also see the highlight-confusing-reorderings function, which can be useful in similar circumstances as this function.

View in manual

Probably introduced at or before Emacs version 25.1.

Source Code

// Defined in /usr/src/emacs/src/xdisp.c
{
  struct buffer *buf = current_buffer;
  struct buffer *old = buf;
  struct window *w = NULL;
  bool frame_window_p = FRAME_WINDOW_P (SELECTED_FRAME ());
  struct bidi_it itb;
  ptrdiff_t from_pos, to_pos, from_bpos;
  void *itb_data;

  if (!NILP (object))
    {
      if (BUFFERP (object))
	buf = XBUFFER (object);
      else if (WINDOWP (object))
	{
	  w = decode_live_window (object);
	  buf = XBUFFER (w->contents);
	  frame_window_p = FRAME_WINDOW_P (XFRAME (w->frame));
	}
      else
	CHECK_STRING (object);
    }

  if (STRINGP (object))
    {
      /* Characters in unibyte strings are always treated by bidi.c as
	 strong LTR.  */
      if (!STRING_MULTIBYTE (object)
	  /* When we are loading loadup.el, the character property
	     tables needed for bidi iteration are not yet
	     available.  */
	  || redisplay__inhibit_bidi)
	return Qnil;

      validate_subarray (object, from, to, SCHARS (object), &from_pos, &to_pos);
      if (from_pos >= SCHARS (object))
	return Qnil;

      /* Set up the bidi iterator.  */
      itb_data = bidi_shelve_cache ();
      itb.paragraph_dir = NEUTRAL_DIR;
      itb.string.lstring = object;
      itb.string.s = NULL;
      itb.string.schars = SCHARS (object);
      itb.string.bufpos = 0;
      itb.string.from_disp_str = false;
      itb.string.unibyte = false;
      itb.w = w;
      bidi_init_it (0, 0, frame_window_p, &itb);
    }
  else
    {
      /* Nothing this fancy can happen in unibyte buffers, or in a
	 buffer that disabled reordering, or if FROM is at EOB.  */
      if (NILP (BVAR (buf, bidi_display_reordering))
	  || NILP (BVAR (buf, enable_multibyte_characters))
	  /* When we are loading loadup.el, the character property
	     tables needed for bidi iteration are not yet
	     available.  */
	  || redisplay__inhibit_bidi)
	return Qnil;

      set_buffer_temp (buf);
      validate_region (&from, &to);
      from_pos = XFIXNUM (from);
      to_pos = XFIXNUM (to);
      if (from_pos >= ZV)
	return Qnil;

      /* Set up the bidi iterator.  */
      itb_data = bidi_shelve_cache ();
      from_bpos = CHAR_TO_BYTE (from_pos);
      if (from_pos == BEGV)
	{
	  itb.charpos = BEGV;
	  itb.bytepos = BEGV_BYTE;
	}
      else if (FETCH_BYTE (from_bpos - 1) == '\n')
	{
	  itb.charpos = from_pos;
	  itb.bytepos = from_bpos;
	}
      else
	itb.charpos = find_newline_no_quit (from_pos, CHAR_TO_BYTE (from_pos),
					    -1, &itb.bytepos);
      itb.paragraph_dir = NEUTRAL_DIR;
      itb.string.s = NULL;
      itb.string.lstring = Qnil;
      itb.string.bufpos = 0;
      itb.string.from_disp_str = false;
      itb.string.unibyte = false;
      itb.w = w;
      bidi_init_it (itb.charpos, itb.bytepos, frame_window_p, &itb);
    }

  ptrdiff_t found;
  bidi_dir_t bdir = EQ (base_dir, Qright_to_left) ? R2L : L2R;
  do {
    bidi_paragraph_init (bdir, &itb, false);
    while ((found = bidi_find_first_overridden (&itb)) < from_pos)
      ;
  } while (found == ZV && itb.ch == '\n' && itb.charpos < to_pos);

  bidi_unshelve_cache (itb_data, false);
  set_buffer_temp (old);

  return (from_pos <= found && found < to_pos) ? make_fixnum (found) : Qnil;
}