nim-lang / RFCs

A repository for your Nim proposals.
136 stars 26 forks source link

add `terminalWidth(a: openArray[char]): int` to get number of terminal cells of utf8 string #279

Closed timotheecour closed 3 years ago

timotheecour commented 3 years ago

proposal

add terminalWidth(a: openArray[char]): int to get number of terminal cells of a utf8 string probably not in std/terminal (because of dependencies) but could be in std/termutils

use case

for example, anything that requires terminal display with alignment (eg truncate at 80 columns), for example for nimgrep see https://github.com/nim-lang/Nim/pull/15612#discussion_r509813224

prototype

here's a working prototype adapted from https://stackoverflow.com/a/31124065/1426932

when true: # D20201028T230703
  {.emit:"""
  #define _XOPEN_SOURCE
  #include <wchar.h>
  #include <stdio.h>
  #include <locale.h>
  #include <stdlib.h>

  void setlocale2() {
    setlocale(LC_ALL, "");
  }

  int terminalWidth(char *string) {
    // allocate enough memory to hold the wide string
    size_t needed = mbstowcs(NULL, string, 0) + 1;
    wchar_t *wcstring = malloc(needed * sizeof *wcstring);
    if (!wcstring) return -1;
    // change encodings
    if (mbstowcs(wcstring, string, needed) == (size_t)-1) return -2;
    int width = wcswidth(wcstring, needed);
    free(wcstring);
    return width;
  }
  """.}

  proc setlocale2() {.importc.}
  proc terminalWidth(a: cstring): cint {.importc.}

  proc main()=
    setlocale2()
    doAssert "汉汉汉汉汉汉汉汉汉汉".terminalWidth == 20
    doAssert "åß".terminalWidth == 2
    doAssert "ab".terminalWidth == 2
  main()
Araq commented 3 years ago

The stdlib doesn't need to solve every problem.