tcsh-org / tcsh

This is a read-only mirror of the tcsh code repository.
https://www.tcsh.org/
Other
238 stars 42 forks source link

multi-byte characters in prompt(chars) causes in-line editing to fail #78

Open Tux opened 1 year ago

Tux commented 1 year ago
% set prompt='%# '
% set promptchars='>#'
> echo $prompt
%#
> ecgo $x

Correct > echo $x [y|n|E|a]? edit
> ecgo $x

The cursor is now after the x, as expected. Left arrow, home, etc all work as expected

> set promptchars="\U1F427#"
\

Not what I hoped for

\ set promptchars="๐Ÿง#"
๐Ÿง

Ahhhhh, beautiful :)

๐Ÿง ecgo $x

Correct > echo $x [y|n|E|a]? edit
๐Ÿง ecgo $x

The cursor is now after the x where it is expected, but using left-arrow will move the cursor 5 positions to the right instead. Now pressing the Home key puts the cursor on the x instead of on the e. FWIW the same happens without using special characters in promptchars:

% set promptchars=">#"
> set prompt="๐Ÿง "
๐Ÿง ecgo $x

etc, same behavior.

My goal was to use "๐Ÿงไทก" on my devel box, where the U1f427 \N{PENGUIN} was for user "tux" and U04de1 \N{HEXAGRAM FOR GREAT POWER} was for user "root"

Tux commented 1 year ago

90% is debugging. Look at the change around line 1155

diff --git a/ed.refresh.c b/ed.refresh.c
index 183050e0..8b22a128 100644
--- a/ed.refresh.c
+++ b/ed.refresh.c
@@ -33,6 +33,7 @@
 #include "ed.h"
 /* #define DEBUG_UPDATE */
 /* #define DEBUG_REFRESH */
+/* #define DEBUG_REFCURSOR */
 /* #define DEBUG_LITERAL */

 /* refresh.c -- refresh the current set of lines on the screen */
@@ -1139,6 +1140,10 @@ cpy_pad_spaces(Char *dst, Char *src, int width)
 static void
 CalcPosition(int w, int th, int *h, int *v)
 {
+#ifdef DEBUG_REFCURSOR
+    int is = NLSCLASS_ILLEGAL_SIZE (w);
+    reprintf ("CP:%d W: %2d/%d, H: %d, V: %d, th: %d\r\n", __LINE__, w, is, *h, *v, th);
+#endif
     switch(w) {
    case NLSCLASS_NL:
        *h = 0;
@@ -1155,6 +1160,8 @@ CalcPosition(int w, int th, int *h, int *v)
        *h += 4;
        break;
    case NLSCLASS_ILLEGAL2:
+       *h += NLSCLASS_ILLEGAL_SIZE(w);
+       break;
    case NLSCLASS_ILLEGAL3:
    case NLSCLASS_ILLEGAL4:
    case NLSCLASS_ILLEGAL5:
@@ -1163,23 +1170,33 @@ CalcPosition(int w, int th, int *h, int *v)
    default:
        *h += w;
     }
+#ifdef DEBUG_REFCURSOR
+    reprintf ("cP:%d W: %2d/%d, H: %d, V: %d, th: %d\r\n", __LINE__, w, is, *h, *v, th);
+#endif
     if (*h >= th) {        /* check, extra long tabs picked up here also */
    *h -= th;
    (*v)++;
     }
+#ifdef DEBUG_REFCURSOR
+    reprintf ("cP:%d W: %2d/%d, H: %d, V: %d, th: %d\r\n", __LINE__, w, is, *h, *v, th);
+#endif
 }

 void
 RefCursor(void)
 {              /* only move to new cursor pos */
     Char *cp;
-    int w, h, th, v;
+    int w, h, th, v, pl;

     /* first we must find where the cursor is... */
     h = 0;
     v = 0;
     th = TermH;            /* optimize for speed */

+    /* Prompt length in bytes and in characters */
+    for (cp = Prompt; *cp; cp++);
+    pl = cp - Prompt;
+
     for (cp = Prompt; cp != NULL && *cp; ) {   /* do prompt */
    if (*cp & LITERAL) {
        cp++;
@@ -1189,12 +1206,18 @@ RefCursor(void)
    cp++;
    CalcPosition(w, th, &h, &v);
     }
+#ifdef DEBUG_REFCURSOR
+    reprintf ("RC:%d '%s'/%d => H: %d, V: %d, th: %d, W: %d\r\n", __LINE__, Prompt, pl, h, v, th, w);
+#endif

     for (cp = InputBuf; cp < Cursor;) {    /* do input buffer to Cursor */
    w = NLSClassify(*cp & CHAR, cp == InputBuf, 0);
    cp++;
    CalcPosition(w, th, &h, &v);
     }
+#ifdef DEBUG_REFCURSOR
+    reprintf ("RC:%d '%s'/%d => H: %d, V: %d, th: %d, W: %d\r\n", __LINE__, Prompt, pl, h, v, th, w);
+#endif

     /* now go there */
     MoveToLine(v);

Fixed the problem. Does that seem reasonable? What would break? Are there any tests I should run?

Tux commented 1 year ago

https://github.com/Tux/tcsh/commit/899eefb023bdd9e53d6a68c0a5b3e4cf4f7067a0 if you want to cherry-pick. I can turn it into a PR, but I guess it does not meet your standards