unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Yuan Fu <casouri@gmail.com>
To: Eli Zaretskii <eliz@gnu.org>
Cc: emacs-devel <emacs-devel@gnu.org>
Subject: Re: Support paragraph-local tab stops
Date: Wed, 27 May 2020 11:48:35 -0400	[thread overview]
Message-ID: <C7AC86AB-A451-427B-A943-8D67A7239BEB@gmail.com> (raw)
In-Reply-To: <83a71ttn0b.fsf@gnu.org>

[-- Attachment #1: Type: text/plain, Size: 1451 bytes --]



> On May 27, 2020, at 11:29 AM, Eli Zaretskii <eliz@gnu.org> wrote:
> 
>> From: Yuan Fu <casouri@gmail.com>
>> Date: Tue, 26 May 2020 19:26:01 -0400
>> 
>> As show above, the second table has longer tab stops since they have longer headers.
>> 
>> In Emacs, both table would have to use long tab stops, and the first table with short headers would waste a lot of space. In order to set proper tab stops, you have to go through the whole buffer and use the longest header for each column.
>> 
>> Word processors doesn’t have this problem because they have paragraph-local tab stops, so each table can have their own tabs stops. I wonder if Emacs can do the same. Can we add a text property that points to a list that specifies the tabs stops to use? Is that possible?
> 
> IMO, TAB stops are not the right tool for the job here.  You need to
> use the :align-to display property, which allows to align text with
> pixel granularity.

I think both approach are equally appropriate. And the tab-based one aligns more closely to word processors. If we want to gradually add WYSIWYG editor features, and enable Emacs to edit rich text, this is an unavoidable feature.

I just wrote a (very rough) proof-of-concept patch—it’s much easier than I thought. With the patch you can do something like

(put-text-property (region-beginning) (region-end)
                   'pixel-tab-stop-list '(70 140 210 280 350 420))


Yuan


[-- Attachment #2: tab-stop.patch --]
[-- Type: application/octet-stream, Size: 2945 bytes --]

diff --git a/src/xdisp.c b/src/xdisp.c
index 01f272033e..303bb7df37 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -29818,6 +29818,25 @@ #define IT_APPLY_FACE_BOX(it, face)				\
       }								\
     } while (false)
 
+
+/*  Return the next tab stop according to the current x position
+    CURRENT_X_POS and given TAB_STOP_LIST.  Return -1 if none
+    found.  */
+static int next_tab_stop_x (Lisp_Object tab_stop_list,
+                            ptrdiff_t current_x_pos)
+{
+  Lisp_Object tail = tab_stop_list;
+  for (; CONSP (tail); tail = XCDR (tail))
+    {
+      CHECK_FIXNAT (XCAR (tail));
+      double this_tab_stop = XFIXNUM (XCAR (tail));
+      if (current_x_pos < this_tab_stop)
+          return (int) this_tab_stop;
+    }
+  return -1;
+}
+
+
 /* RIF:
    Produce glyphs/get display metrics for the display element IT is
    loaded with.  See the description of struct it in dispextern.h
@@ -30090,13 +30109,20 @@ gui_produce_glyphs (struct it *it)
 		    x += it->tab_offset;
 		}
 
-	      int next_tab_x = ((1 + x + tab_width - 1) / tab_width) * tab_width;
+              /* Try to calculate tab stop by text property.  */
+              ptrdiff_t charpos = IT_CHARPOS (*it);
+              Lisp_Object pixel_tab_stop_list = Fget_text_property
+                (make_fixnum (charpos), Qpixel_tab_stop_list, Qnil);
+
+              int next_tab_x = next_tab_stop_x(pixel_tab_stop_list, x);
+              if (next_tab_x == -1)
+                next_tab_x = ((1 + x + tab_width - 1) / tab_width) * tab_width;
 
 	      /* If the distance from the current position to the next tab
 		 stop is less than a space character width, use the
 		 tab stop after that.  */
-	      if (next_tab_x - x < font->space_width)
-		next_tab_x += tab_width;
+	      /* if (next_tab_x - x < font->space_width) */
+	      /*   next_tab_x += tab_width; */
 	      if (!NILP (Vdisplay_line_numbers) && it->line_number_produced_p)
 		{
 		  next_tab_x += it->lnum_pixel_width;
@@ -30138,7 +30164,7 @@ gui_produce_glyphs (struct it *it)
 		  if (it->descent < 0)
 		    it->descent = 0;
 		}
-	      else
+	      else /* I.e., !(FONT_TOO_HIGH (font)).  */
 		{
 		  it->ascent = FONT_BASE (font) + boff;
 		  it->descent = FONT_DESCENT (font) - boff;
@@ -30152,11 +30178,12 @@ gui_produce_glyphs (struct it *it)
 					it->ascent + it->descent, it->ascent);
 		}
 	    }
-	  else
+	  else /* I.e., (font->space_width <= 0).  */
 	    {
 	      it->pixel_width = 0;
 	      it->nglyphs = 1;
 	    }
+          /* End of (it->char_to_display == '\t').  */
 	}
 
       if (FONT_TOO_HIGH (font))
@@ -34231,6 +34258,7 @@ syms_of_xdisp (void)
   DEFSYM (QCfile, ":file");
   DEFSYM (Qfontified, "fontified");
   DEFSYM (Qfontification_functions, "fontification-functions");
+  DEFSYM (Qpixel_tab_stop_list, "pixel-tab-stop-list");
 
   /* Name of the symbol which disables Lisp evaluation in 'display'
      properties.  This is used by enriched.el.  */

  reply	other threads:[~2020-05-27 15:48 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-05-26 23:26 Support paragraph-local tab stops Yuan Fu
2020-05-27  0:27 ` Yuan Fu
2020-05-27 15:29 ` Eli Zaretskii
2020-05-27 15:48   ` Yuan Fu [this message]
2020-05-27 16:26     ` Eli Zaretskii
2020-05-27 16:51       ` Yuan Fu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=C7AC86AB-A451-427B-A943-8D67A7239BEB@gmail.com \
    --to=casouri@gmail.com \
    --cc=eliz@gnu.org \
    --cc=emacs-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).