mirror of
https://github.com/vlang/v.git
synced 2025-09-11 00:20:26 -04:00
readline: add completion support (#20559)
This commit is contained in:
parent
4b0a2cb7c9
commit
8d5f95d604
@ -21,16 +21,20 @@ struct Winsize {
|
|||||||
// Example: import readline { Readline }
|
// Example: import readline { Readline }
|
||||||
pub struct Readline {
|
pub struct Readline {
|
||||||
mut:
|
mut:
|
||||||
is_raw bool
|
is_raw bool
|
||||||
orig_termios termios.Termios // Linux
|
orig_termios termios.Termios // Linux
|
||||||
current []rune // Line being edited
|
current []rune // Line being edited
|
||||||
cursor int // Cursor position
|
cursor int // Cursor position
|
||||||
overwrite bool
|
overwrite bool
|
||||||
cursor_row_offset int
|
cursor_row_offset int
|
||||||
prompt string
|
prompt string
|
||||||
prompt_offset int
|
prompt_offset int
|
||||||
previous_lines [][]rune
|
previous_lines [][]rune
|
||||||
skip_empty bool // skip the empty lines when calling .history_previous()
|
skip_empty bool // skip the empty lines when calling .history_previous()
|
||||||
search_index int
|
search_index int
|
||||||
is_tty bool
|
is_tty bool
|
||||||
|
last_prefix_completion []rune
|
||||||
|
last_completion_offset int
|
||||||
|
completion_list []string
|
||||||
|
completion_callback fn (string) []string = unsafe { nil }
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ enum Action {
|
|||||||
overwrite
|
overwrite
|
||||||
clear_screen
|
clear_screen
|
||||||
suspend
|
suspend
|
||||||
|
completion
|
||||||
}
|
}
|
||||||
|
|
||||||
// enable_raw_mode enables the raw mode of the terminal.
|
// enable_raw_mode enables the raw mode of the terminal.
|
||||||
@ -174,7 +175,7 @@ pub fn read_line(prompt string) !string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// analyse returns an `Action` based on the type of input byte given in `c`.
|
// analyse returns an `Action` based on the type of input byte given in `c`.
|
||||||
fn (r Readline) analyse(c int) Action {
|
fn (mut r Readline) analyse(c int) Action {
|
||||||
if c > 255 {
|
if c > 255 {
|
||||||
return Action.insert_character
|
return Action.insert_character
|
||||||
}
|
}
|
||||||
@ -183,8 +184,12 @@ fn (r Readline) analyse(c int) Action {
|
|||||||
return .eof
|
return .eof
|
||||||
} // NUL, End of Text, End of Transmission
|
} // NUL, End of Text, End of Transmission
|
||||||
`\n`, `\r` {
|
`\n`, `\r` {
|
||||||
|
r.last_prefix_completion.clear()
|
||||||
return .commit_line
|
return .commit_line
|
||||||
}
|
}
|
||||||
|
`\t` {
|
||||||
|
return .completion
|
||||||
|
}
|
||||||
`\f` {
|
`\f` {
|
||||||
return .clear_screen
|
return .clear_screen
|
||||||
} // CTRL + L
|
} // CTRL + L
|
||||||
@ -211,6 +216,7 @@ fn (r Readline) analyse(c int) Action {
|
|||||||
} // CTRL + Z, SUB
|
} // CTRL + Z, SUB
|
||||||
else {
|
else {
|
||||||
if c >= ` ` {
|
if c >= ` ` {
|
||||||
|
r.last_prefix_completion.clear()
|
||||||
return Action.insert_character
|
return Action.insert_character
|
||||||
}
|
}
|
||||||
return Action.nothing
|
return Action.nothing
|
||||||
@ -299,6 +305,7 @@ fn (mut r Readline) execute(a Action, c int) bool {
|
|||||||
.overwrite { r.switch_overwrite() }
|
.overwrite { r.switch_overwrite() }
|
||||||
.clear_screen { r.clear_screen() }
|
.clear_screen { r.clear_screen() }
|
||||||
.suspend { r.suspend() }
|
.suspend { r.suspend() }
|
||||||
|
.completion { r.completion() }
|
||||||
else {}
|
else {}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@ -416,6 +423,7 @@ fn (mut r Readline) delete_character() {
|
|||||||
r.cursor--
|
r.cursor--
|
||||||
r.current.delete(r.cursor)
|
r.current.delete(r.cursor)
|
||||||
r.refresh_line()
|
r.refresh_line()
|
||||||
|
r.completion_clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut r Readline) delete_word_left() {
|
fn (mut r Readline) delete_word_left() {
|
||||||
@ -450,12 +458,14 @@ fn (mut r Readline) delete_word_left() {
|
|||||||
|
|
||||||
r.current.delete_many(r.cursor, orig_cursor - r.cursor)
|
r.current.delete_many(r.cursor, orig_cursor - r.cursor)
|
||||||
r.refresh_line()
|
r.refresh_line()
|
||||||
|
r.completion_clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut r Readline) delete_line() {
|
fn (mut r Readline) delete_line() {
|
||||||
r.current = []
|
r.current = []
|
||||||
r.cursor = 0
|
r.cursor = 0
|
||||||
r.refresh_line()
|
r.refresh_line()
|
||||||
|
r.completion_clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
// suppr_character removes (suppresses) the character in front of the cursor.
|
// suppr_character removes (suppresses) the character in front of the cursor.
|
||||||
@ -577,6 +587,56 @@ fn (mut r Readline) history_next() {
|
|||||||
r.refresh_line()
|
r.refresh_line()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// completion implements the tab completion feature
|
||||||
|
fn (mut r Readline) completion() {
|
||||||
|
// check if completion is used
|
||||||
|
if r.completion_list.len == 0 && r.completion_callback == unsafe { nil } {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// use last prefix for completion or current input
|
||||||
|
prefix := if r.last_prefix_completion.len > 0 { r.last_prefix_completion } else { r.current }
|
||||||
|
if prefix.len == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// filtering by prefix
|
||||||
|
opts := if r.completion_list.len > 0 {
|
||||||
|
sprefix := prefix.string()
|
||||||
|
r.completion_list.filter(it.starts_with(sprefix))
|
||||||
|
} else if r.completion_callback != unsafe { nil } {
|
||||||
|
r.completion_callback(prefix.string())
|
||||||
|
} else {
|
||||||
|
[]string{}
|
||||||
|
}
|
||||||
|
if opts.len == 0 {
|
||||||
|
r.completion_clear()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// moving for next possible completion match using saved prefix
|
||||||
|
if r.last_prefix_completion.len != 0 {
|
||||||
|
if opts.len > r.last_completion_offset + 1 {
|
||||||
|
r.last_completion_offset += 1
|
||||||
|
} else {
|
||||||
|
// reset for initial option in completion list
|
||||||
|
r.last_completion_offset = 0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// save current prefix before tab'ing
|
||||||
|
r.last_prefix_completion = r.current
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the text to the current completion match in the completion list
|
||||||
|
r.current = opts[r.last_completion_offset].runes()
|
||||||
|
r.cursor = r.current.len
|
||||||
|
r.refresh_line()
|
||||||
|
}
|
||||||
|
|
||||||
|
// completion_clear resets the completion state
|
||||||
|
fn (mut r Readline) completion_clear() {
|
||||||
|
r.last_prefix_completion.clear()
|
||||||
|
r.last_completion_offset = 0
|
||||||
|
}
|
||||||
|
|
||||||
// suspend sends the `SIGSTOP` signal to the terminal.
|
// suspend sends the `SIGSTOP` signal to the terminal.
|
||||||
fn (mut r Readline) suspend() {
|
fn (mut r Readline) suspend() {
|
||||||
is_standalone := os.getenv('VCHILD') != 'true'
|
is_standalone := os.getenv('VCHILD') != 'true'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user