unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Support paragraph-local tab stops
@ 2020-05-26 23:26 Yuan Fu
  2020-05-27  0:27 ` Yuan Fu
  2020-05-27 15:29 ` Eli Zaretskii
  0 siblings, 2 replies; 6+ messages in thread
From: Yuan Fu @ 2020-05-26 23:26 UTC (permalink / raw)
  To: emacs-devel

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

The motivation is variable pitch table in Emacs. Right now, although you can use tab stops to properly align a table, because the tabs stops are buffer-global, all the tables in a buffer has to share the same tab stop configurations. Then a good tab stop configuration for one table could mess up the alignment for another table. Let me explain:

In a word processor, if I have two tables. One with short headers, one with long headers. The image below shows the two tables and the blue arrows on the ruler represents the tab stops for the first table.



Here, the blue arrows show the tab stops of the second table:



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?

Yuan

[-- Attachment #2.1: Type: text/html, Size: 2123 bytes --]

[-- Attachment #2.2: Screen Shot 2020-05-26 at 7.15.31 PM.png --]
[-- Type: image/png, Size: 57934 bytes --]

[-- Attachment #2.3: Screen Shot 2020-05-26 at 7.17.23 PM.png --]
[-- Type: image/png, Size: 56909 bytes --]

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Support paragraph-local tab stops
  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
  1 sibling, 0 replies; 6+ messages in thread
From: Yuan Fu @ 2020-05-27  0:27 UTC (permalink / raw)
  To: emacs-devel

Actually it seems that that tab-stop-list doesn’t affect how is tab displayed, and the display only depends on tab-width. Then maybe we should also have a GUI-tab-stop-list that specifies the pixel position at where each tab stop should be.

Yuan




^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Support paragraph-local tab stops
  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
  1 sibling, 1 reply; 6+ messages in thread
From: Eli Zaretskii @ 2020-05-27 15:29 UTC (permalink / raw)
  To: Yuan Fu; +Cc: emacs-devel

> 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.



^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Support paragraph-local tab stops
  2020-05-27 15:29 ` Eli Zaretskii
@ 2020-05-27 15:48   ` Yuan Fu
  2020-05-27 16:26     ` Eli Zaretskii
  0 siblings, 1 reply; 6+ messages in thread
From: Yuan Fu @ 2020-05-27 15:48 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

[-- 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.  */

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: Support paragraph-local tab stops
  2020-05-27 15:48   ` Yuan Fu
@ 2020-05-27 16:26     ` Eli Zaretskii
  2020-05-27 16:51       ` Yuan Fu
  0 siblings, 1 reply; 6+ messages in thread
From: Eli Zaretskii @ 2020-05-27 16:26 UTC (permalink / raw)
  To: Yuan Fu; +Cc: emacs-devel

> From: Yuan Fu <casouri@gmail.com>
> Date: Wed, 27 May 2020 11:48:35 -0400
> Cc: emacs-devel <emacs-devel@gnu.org>
> 
> > 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.

TABs in Emacs use columns, so their resolution is more coarse.
Therefore, they fit this job less well, because they cannot handle
variable-pitch fonts.

> 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))

This is not TAB stops, this is a new text property that does the same
job as :align-to.  Why invent a new property when we already have an
existing one that does the same?



^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Support paragraph-local tab stops
  2020-05-27 16:26     ` Eli Zaretskii
@ 2020-05-27 16:51       ` Yuan Fu
  0 siblings, 0 replies; 6+ messages in thread
From: Yuan Fu @ 2020-05-27 16:51 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel



> On May 27, 2020, at 12:26 PM, Eli Zaretskii <eliz@gnu.org> wrote:
> 
>> From: Yuan Fu <casouri@gmail.com>
>> Date: Wed, 27 May 2020 11:48:35 -0400
>> Cc: emacs-devel <emacs-devel@gnu.org>
>> 
>>> 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.
> 
> TABs in Emacs use columns, so their resolution is more coarse.
> Therefore, they fit this job less well, because they cannot handle
> variable-pitch fonts.
> 
>> 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))
> 
> This is not TAB stops, this is a new text property that does the same
> job as :align-to.  Why invent a new property when we already have an
> existing one that does the same?


I just thought this is how TAB should work, you know, like word processors. But of course, if this is not reasonable, I can use :align-to. 

Yuan




^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2020-05-27 16:51 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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
2020-05-27 16:26     ` Eli Zaretskii
2020-05-27 16:51       ` Yuan Fu

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).