v2 / vlib / readline / readline_windows.c.v
149 lines · 136 sloc · 4.53 KB · c9d530504fc28fbc16540c97f292f60c6179e96a
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4//
5// Windows currently supports basic line reading and raw-character input.
6// The richer line-editing functionality is still implemented only on nix.
7// Will serve as more advanced input method
8// based on the work of https://github.com/AmokHuginnsson/replxx
9//
10module readline
11
12import os
13
14#include <conio.h>
15#include <windows.h>
16
17// needed for parity with readline_default.c.v
18struct Termios {
19}
20
21fn C._getwch() i32
22
23fn (mut r Readline) set_raw_mode(keep_processed_input bool) {
24 if os.is_atty(0) <= 0 || os.getenv('TERM') == 'dumb' {
25 r.is_tty = false
26 r.is_raw = false
27 return
28 }
29 stdin_handle := C.GetStdHandle(C.STD_INPUT_HANDLE)
30 if stdin_handle == C.INVALID_HANDLE_VALUE || !C.GetConsoleMode(stdin_handle, &r.orig_stdin_mode) {
31 r.is_tty = false
32 r.is_raw = false
33 return
34 }
35 mut raw_mode := r.orig_stdin_mode & (~u32(C.ENABLE_LINE_INPUT | C.ENABLE_ECHO_INPUT))
36 if !keep_processed_input {
37 raw_mode &= ~u32(C.ENABLE_PROCESSED_INPUT)
38 }
39 if !C.SetConsoleMode(stdin_handle, raw_mode) {
40 r.is_tty = false
41 r.is_raw = false
42 return
43 }
44 r.is_tty = true
45 r.is_raw = true
46}
47
48// enable_raw_mode enables the raw mode of the terminal.
49// In raw mode all key presses are directly sent to the program and no interpretation is done.
50pub fn (mut r Readline) enable_raw_mode() {
51 r.set_raw_mode(false)
52}
53
54// enable_raw_mode_nosig enables the raw mode of the terminal while keeping console signal processing enabled.
55pub fn (mut r Readline) enable_raw_mode_nosig() {
56 r.set_raw_mode(true)
57}
58
59// disable_raw_mode disables the raw mode of the terminal.
60pub fn (mut r Readline) disable_raw_mode() {
61 if !r.is_raw {
62 return
63 }
64 stdin_handle := C.GetStdHandle(C.STD_INPUT_HANDLE)
65 if stdin_handle != C.INVALID_HANDLE_VALUE {
66 C.SetConsoleMode(stdin_handle, r.orig_stdin_mode)
67 }
68 r.is_raw = false
69}
70
71// read_char reads a single character.
72pub fn (r Readline) read_char() !int {
73 ch := int(C._getwch())
74 if ch < 0 {
75 return error('failed to read character')
76 }
77 if ch !in [0, 0xe0] {
78 return ch
79 }
80 extended := int(C._getwch())
81 if extended < 0 {
82 return error('failed to read character')
83 }
84 return int((u32(ch) << 16) | u32(extended))
85}
86
87// Only use standard os.get_line
88// Need implementation for readline capabilities
89//
90// read_line_utf8 blocks execution in a loop and awaits user input
91// characters from a terminal until `EOF` or `Enter` key is encountered
92// in the input stream.
93// read_line_utf8 returns the complete UTF-8 input line as an UTF-32 encoded `[]rune` or
94// an error if the line is empty.
95// The `prompt` `string` is output as a prefix text for the input capturing.
96// read_line_utf8 is the main method of the `readline` module and `Readline` struct.
97pub fn (mut r Readline) read_line_utf8(prompt string) ![]rune {
98 r.current = []rune{}
99 r.cursor = 0
100 r.prompt = prompt
101 r.search_index = 0
102 if r.previous_lines.len <= 1 {
103 r.previous_lines << []rune{}
104 r.previous_lines << []rune{}
105 } else {
106 r.previous_lines[0] = []rune{}
107 }
108 print(r.prompt)
109 flush_stdout()
110 r.current = os.get_raw_line().runes()
111 r.previous_lines[0] = []rune{}
112 r.search_index = 0
113 if r.current.len == 0 {
114 return error('empty line')
115 }
116 return r.current
117}
118
119// read_line does the same as `read_line_utf8` but returns user input as a `string`.
120// (As opposed to `[]rune` returned by `read_line_utf8`).
121pub fn (mut r Readline) read_line(prompt string) !string {
122 s := r.read_line_utf8(prompt)!
123 return s.string()
124}
125
126// read_line_utf8 blocks execution in a loop and awaits user input
127// characters from a terminal until `EOF` or `Enter` key is encountered
128// in the input stream.
129// read_line_utf8 returns the complete UTF-8 input line as an UTF-32 encoded `[]rune` or
130// an error if the line is empty.
131// The `prompt` `string` is output as a prefix text for the input capturing.
132// read_line_utf8 is the main method of the `readline` module and `Readline` struct.
133// NOTE that this version of `read_line_utf8` is a standalone function without
134// persistent functionalities (e.g. history).
135pub fn read_line_utf8(prompt string) ![]rune {
136 mut r := Readline{}
137 s := r.read_line_utf8(prompt)!
138 return s
139}
140
141// read_line does the same as `read_line_utf8` but returns user input as a `string`.
142// (As opposed to `[]rune` as returned by `read_line_utf8`).
143// NOTE that this version of `read_line` is a standalone function without
144// persistent functionalities (e.g. history).
145pub fn read_line(prompt string) !string {
146 mut r := Readline{}
147 s := r.read_line(prompt)!
148 return s
149}
150