15
15
#endif
16
16
17
17
#if defined (_WIN32)
18
+ #define WIN32_LEAN_AND_MEAN
19
+ #define NOMINMAX
20
+ #include < windows.h>
18
21
#include < fcntl.h>
19
22
#include < io.h>
20
- #pragma comment(lib,"kernel32.lib")
21
- extern " C" __declspec(dllimport) void * __stdcall GetStdHandle (unsigned long nStdHandle);
22
- extern " C" __declspec(dllimport) int __stdcall GetConsoleMode (void * hConsoleHandle, unsigned long * lpMode);
23
- extern " C" __declspec(dllimport) int __stdcall SetConsoleMode (void * hConsoleHandle, unsigned long dwMode);
24
- extern " C" __declspec(dllimport) int __stdcall SetConsoleCP (unsigned int wCodePageID);
25
- extern " C" __declspec(dllimport) int __stdcall SetConsoleOutputCP (unsigned int wCodePageID);
26
- extern " C" __declspec(dllimport) int __stdcall WideCharToMultiByte (unsigned int CodePage, unsigned long dwFlags,
27
- const wchar_t * lpWideCharStr, int cchWideChar,
28
- char * lpMultiByteStr, int cbMultiByte,
29
- const char * lpDefaultChar, bool * lpUsedDefaultChar);
30
- #define ENABLE_LINE_INPUT 0x0002
31
- #define ENABLE_ECHO_INPUT 0x0004
32
- #define CP_UTF8 65001
33
- #define CONSOLE_CHAR_TYPE wchar_t
34
- #define CONSOLE_GET_CHAR () getwchar()
35
- #define CONSOLE_EOF WEOF
36
23
#else
37
24
#include < unistd.h>
38
- #define CONSOLE_CHAR_TYPE char
39
- #define CONSOLE_GET_CHAR () getchar()
40
- #define CONSOLE_EOF EOF
41
25
#endif
42
26
43
27
int32_t get_num_physical_cores () {
@@ -545,6 +529,7 @@ void console_init(console_state & con_st) {
545
529
new_termios.c_cc [VTIME] = 0 ;
546
530
tcsetattr (STDIN_FILENO, TCSANOW, &new_termios);
547
531
#endif
532
+ setlocale (LC_ALL, " " );
548
533
}
549
534
550
535
void console_cleanup (console_state & con_st) {
@@ -557,9 +542,80 @@ void console_cleanup(console_state & con_st) {
557
542
console_set_color (con_st, CONSOLE_COLOR_DEFAULT);
558
543
}
559
544
545
+ #if defined (_WIN32)
546
+ int puts_get_width (_In_z_ CONST CHAR* lpBuffer) {
547
+ DWORD nNumberOfCharsToWrite = strlen (lpBuffer);
548
+
549
+ HANDLE hConsole = GetStdHandle (STD_OUTPUT_HANDLE);
550
+ CONSOLE_SCREEN_BUFFER_INFO bufferInfo;
551
+ if (!GetConsoleScreenBufferInfo (hConsole, &bufferInfo)) {
552
+ // Make a guess
553
+ return 1 ;
554
+ }
555
+ COORD initialPosition = bufferInfo.dwCursorPosition ;
556
+
557
+ DWORD written = 0 ;
558
+ WriteConsole (hConsole, lpBuffer, nNumberOfCharsToWrite, &written, nullptr );
559
+
560
+ CONSOLE_SCREEN_BUFFER_INFO newBufferInfo;
561
+ GetConsoleScreenBufferInfo (hConsole, &newBufferInfo);
562
+
563
+ int width = newBufferInfo.dwCursorPosition .X - initialPosition.X ;
564
+ if (newBufferInfo.dwCursorPosition .Y > initialPosition.Y ) {
565
+ width += (newBufferInfo.dwSize .X - initialPosition.X );
566
+ }
567
+
568
+ return width;
569
+ }
570
+ #endif
571
+
572
+ char32_t getchar32 () {
573
+ wchar_t wc = getwchar ();
574
+ if (static_cast <wint_t >(wc) == WEOF) {
575
+ return WEOF;
576
+ }
577
+
578
+ #if WCHAR_MAX == 0xFFFF
579
+ if ((wc >= 0xD800 ) && (wc <= 0xDBFF )) { // Check if wc is a high surrogate
580
+ wchar_t low_surrogate = getwchar ();
581
+ if ((low_surrogate >= 0xDC00 ) && (low_surrogate <= 0xDFFF )) { // Check if the next wchar is a low surrogate
582
+ return (static_cast <char32_t >(wc & 0x03FF ) << 10 ) + (low_surrogate & 0x03FF ) + 0x10000 ;
583
+ }
584
+ }
585
+ if ((wc >= 0xD800 ) && (wc <= 0xDFFF )) { // Invalid surrogate pair
586
+ return 0xFFFD ; // Return the replacement character U+FFFD
587
+ }
588
+ #endif
589
+
590
+ return static_cast <char32_t >(wc);
591
+ }
592
+
593
+ void append_utf8 (char32_t ch, std::string & out) {
594
+ if (ch <= 0x7F ) {
595
+ out.push_back (static_cast <unsigned char >(ch));
596
+ } else if (ch <= 0x7FF ) {
597
+ out.push_back (static_cast <unsigned char >(0xC0 | ((ch >> 6 ) & 0x1F )));
598
+ out.push_back (static_cast <unsigned char >(0x80 | (ch & 0x3F )));
599
+ } else if (ch <= 0xFFFF ) {
600
+ out.push_back (static_cast <unsigned char >(0xE0 | ((ch >> 12 ) & 0x0F )));
601
+ out.push_back (static_cast <unsigned char >(0x80 | ((ch >> 6 ) & 0x3F )));
602
+ out.push_back (static_cast <unsigned char >(0x80 | (ch & 0x3F )));
603
+ } else if (ch <= 0x10FFFF ) {
604
+ out.push_back (static_cast <unsigned char >(0xF0 | ((ch >> 18 ) & 0x07 )));
605
+ out.push_back (static_cast <unsigned char >(0x80 | ((ch >> 12 ) & 0x3F )));
606
+ out.push_back (static_cast <unsigned char >(0x80 | ((ch >> 6 ) & 0x3F )));
607
+ out.push_back (static_cast <unsigned char >(0x80 | (ch & 0x3F )));
608
+ } else {
609
+ // Invalid Unicode code point
610
+ }
611
+ }
612
+
560
613
// Helper function to remove the last UTF-8 character from a string
561
- void remove_last_utf8_char (std::string & line) {
562
- if (line.empty ()) return ;
614
+ void pop_back_utf8_char (std::string & line) {
615
+ if (line.empty ()) {
616
+ return ;
617
+ }
618
+
563
619
size_t pos = line.length () - 1 ;
564
620
565
621
// Find the start of the last UTF-8 character (checking up to 4 bytes back)
@@ -569,33 +625,24 @@ void remove_last_utf8_char(std::string & line) {
569
625
line.erase (pos);
570
626
}
571
627
572
- #if defined (_WIN32)
573
- // Convert a wide Unicode string to an UTF8 string
574
- void win32_utf8_encode (const std::wstring & wstr, std::string & str) {
575
- int size_needed = WideCharToMultiByte (CP_UTF8, 0 , &wstr[0 ], (int )wstr.size (), NULL , 0 , NULL , NULL );
576
- std::string strTo (size_needed, 0 );
577
- WideCharToMultiByte (CP_UTF8, 0 , &wstr[0 ], (int )wstr.size (), &strTo[0 ], size_needed, NULL , NULL );
578
- str = strTo;
579
- }
580
- #endif
581
-
582
628
bool console_readline (console_state & con_st, std::string & line) {
629
+ console_set_color (con_st, CONSOLE_COLOR_USER_INPUT);
630
+
583
631
line.clear ();
632
+ std::vector<int > widths;
584
633
bool is_special_char = false ;
585
634
bool end_of_stream = false ;
586
635
587
- console_set_color (con_st, CONSOLE_COLOR_USER_INPUT);
588
-
589
- CONSOLE_CHAR_TYPE input_char;
636
+ char32_t input_char;
590
637
while (true ) {
591
638
fflush (stdout); // Ensure all output is displayed before waiting for input
592
- input_char = CONSOLE_GET_CHAR ();
639
+ input_char = getchar32 ();
593
640
594
641
if (input_char == ' \r ' || input_char == ' \n ' ) {
595
642
break ;
596
643
}
597
644
598
- if (input_char == CONSOLE_EOF || input_char == 0x04 /* Ctrl+D*/ ) {
645
+ if (input_char == WEOF || input_char == 0x04 /* Ctrl+D*/ ) {
599
646
end_of_stream = true ;
600
647
break ;
601
648
}
@@ -608,31 +655,39 @@ bool console_readline(console_state & con_st, std::string & line) {
608
655
}
609
656
610
657
if (input_char == ' \033 ' ) { // Escape sequence
611
- CONSOLE_CHAR_TYPE code = CONSOLE_GET_CHAR ();
612
- if (code == ' [' ) {
658
+ char32_t code = getchar32 ();
659
+ if (code == ' [' || code == 0x1B ) {
613
660
// Discard the rest of the escape sequence
614
- while ((code = CONSOLE_GET_CHAR ()) != CONSOLE_EOF ) {
661
+ while ((code = getchar32 ()) != WEOF ) {
615
662
if ((code >= ' A' && code <= ' Z' ) || (code >= ' a' && code <= ' z' ) || code == ' ~' ) {
616
663
break ;
617
664
}
618
665
}
619
666
}
620
667
} else if (input_char == 0x08 || input_char == 0x7F ) { // Backspace
621
- if (!line.empty ()) {
622
- fputs (" \b \b " , stdout); // Move cursor back, print a space, and move cursor back again
623
- remove_last_utf8_char (line);
668
+ if (!widths.empty ()) {
669
+ int count;
670
+ do {
671
+ count = widths.back ();
672
+ widths.pop_back ();
673
+ // Move cursor back, print spaces, and move cursor back again
674
+ for (int i = 0 ; i < count; i++) {
675
+ fputs (" \b \b " , stdout);
676
+ }
677
+ pop_back_utf8_char (line);
678
+ } while (count == 0 && !widths.empty ());
624
679
}
625
- } else if (static_cast < unsigned >( input_char) < 32 ) {
680
+ } else if (input_char < 32 ) {
626
681
// Ignore control characters
627
682
} else {
628
- # if defined(_WIN32)
629
- std::string utf8_char ;
630
- win32_utf8_encode ( std::wstring ( 1 , input_char), utf8_char);
631
- line += utf8_char ;
632
- fputs (utf8_char. c_str (), stdout );
683
+ int offset = line. length ();
684
+ append_utf8 (input_char, line) ;
685
+ # if defined (_WIN32)
686
+ int width = puts_get_width ( line. c_str () + offset) ;
687
+ widths. push_back (width );
633
688
#else
634
- line += input_char ;
635
- putchar ( input_char);
689
+ fputs ( line. c_str () + offset, stdout) ;
690
+ widths. push_back ( wcwidth ( input_char) );
636
691
#endif
637
692
}
638
693
@@ -655,7 +710,7 @@ bool console_readline(console_state & con_st, std::string & line) {
655
710
putchar (' \n ' );
656
711
has_more = !has_more;
657
712
} else {
658
- // llama doesn't seem to process a single space
713
+ // llama will just eat the single space
659
714
if (line.length () == 1 && line.back () == ' ' ) {
660
715
line.clear ();
661
716
putchar (' \b ' );
0 commit comments