Refine character deletion
This commit is contained in:
parent
3f98479e17
commit
4cb1e810ef
1 changed files with 92 additions and 64 deletions
146
cli/cli.odin
146
cli/cli.odin
|
|
@ -10,6 +10,7 @@ import "core:mem"
|
|||
import "core:slice"
|
||||
import "core:bytes"
|
||||
import "core:strings"
|
||||
import "core:unicode"
|
||||
import "core:sys/posix"
|
||||
import "core:path/filepath"
|
||||
import "core:unicode/utf8"
|
||||
|
|
@ -37,20 +38,7 @@ Term :: struct {
|
|||
|
||||
term := Term{}
|
||||
|
||||
init_cli :: proc () {
|
||||
mem_err: mem.Allocator_Error
|
||||
term.input.buffer, mem_err = make([dynamic]rune, context.allocator)
|
||||
log.assertf(mem_err == nil, "Memory allocation failed")
|
||||
|
||||
term.history.data, mem_err = make([dynamic][]rune, context.allocator)
|
||||
log.assertf(mem_err == nil, "Memory allocation failed")
|
||||
|
||||
home_path := string(posix.getenv("HOME"))
|
||||
log.assertf(home_path != "", "Home path not found")
|
||||
|
||||
term.history.file = filepath.join({home_path, config.HISTORY_FILE}, context.allocator)
|
||||
log.assertf(mem_err == nil, "Memory allocation failed")
|
||||
|
||||
fill_history_cache :: proc () {
|
||||
USER_PERMISSIONS :: 0o600
|
||||
if !os.exists(term.history.file) {
|
||||
handle, ferr := os.open(term.history.file, os.O_CREATE | os.O_RDWR, USER_PERMISSIONS)
|
||||
|
|
@ -66,7 +54,23 @@ init_cli :: proc () {
|
|||
for line in strings.split_lines_iterator(&history_content_it) {
|
||||
append(&term.history.data, utf8.string_to_runes(line))
|
||||
}
|
||||
}
|
||||
|
||||
init_cli :: proc () {
|
||||
mem_err: mem.Allocator_Error
|
||||
term.input.buffer, mem_err = make([dynamic]rune, context.allocator)
|
||||
log.assertf(mem_err == nil, "Memory allocation failed")
|
||||
|
||||
term.history.data, mem_err = make([dynamic][]rune, context.allocator)
|
||||
log.assertf(mem_err == nil, "Memory allocation failed")
|
||||
|
||||
home_path := string(posix.getenv("HOME"))
|
||||
log.assertf(home_path != "", "Home path not found")
|
||||
|
||||
term.history.file = filepath.join({home_path, config.HISTORY_FILE}, context.allocator)
|
||||
log.assertf(mem_err == nil, "Memory allocation failed")
|
||||
|
||||
fill_history_cache()
|
||||
}
|
||||
|
||||
deinit_cli :: proc () {
|
||||
|
|
@ -95,7 +99,6 @@ get_maybe_git_prompt :: proc(dir: string) -> Maybe(string) {
|
|||
GIT_FOLDER :: ".git"
|
||||
git_path := filepath.join({dir, GIT_FOLDER}, context.temp_allocator)
|
||||
uses_git := os.exists(git_path)
|
||||
|
||||
if !uses_git do return nil
|
||||
|
||||
HEAD_FILE_NAME :: "HEAD"
|
||||
|
|
@ -107,20 +110,22 @@ get_maybe_git_prompt :: proc(dir: string) -> Maybe(string) {
|
|||
ref_parts, merr := strings.split(string(head_data), "/", context.temp_allocator)
|
||||
log.assertf(merr == nil, "Memory allocation failed")
|
||||
|
||||
branch, prompt_part: string
|
||||
branch: string
|
||||
ok: bool
|
||||
is_raw_hash := len(ref_parts) == 1
|
||||
if is_raw_hash {
|
||||
HASH_MAX_LEN :: 9
|
||||
branch, ok = strings.substring(ref_parts[0], 0, HASH_MAX_LEN)
|
||||
branch, ok = strings.substring(string(head_data), 0, HASH_MAX_LEN)
|
||||
if !ok do return nil
|
||||
} else {
|
||||
branch = strings.trim_right_space(ref_parts[len(ref_parts) - 1])
|
||||
}
|
||||
|
||||
// todo: add if branch has changes or not
|
||||
prompt_part: string
|
||||
prompt_part, merr = strings.concatenate({"‹", branch, "›"}, context.temp_allocator)
|
||||
log.assertf(merr == nil, "Memory allocation failed")
|
||||
|
||||
return prompt_part
|
||||
}
|
||||
|
||||
|
|
@ -147,24 +152,41 @@ get_prompt_prefix :: proc() -> string {
|
|||
user = string(pw.pw_name)
|
||||
}
|
||||
|
||||
git_prompt := get_maybe_git_prompt(dir).? or_else ""
|
||||
|
||||
FIRST_DETAIL :: " :: "
|
||||
SPACE :: " "
|
||||
SECOND_DETAIL :: " » "
|
||||
|
||||
prompt_parts := []string {
|
||||
config.USER_COLOR,
|
||||
user,
|
||||
config.DETAILS_COLOR,
|
||||
" :: ",
|
||||
FIRST_DETAIL,
|
||||
config.DIR_COLOR,
|
||||
prompt_dir,
|
||||
" ",
|
||||
SPACE,
|
||||
config.GIT_COLOR,
|
||||
get_maybe_git_prompt(dir).? or_else "",
|
||||
git_prompt,
|
||||
config.DETAILS_COLOR,
|
||||
" » ",
|
||||
config.BASE_COLOR}
|
||||
SECOND_DETAIL,
|
||||
config.BASE_COLOR
|
||||
}
|
||||
|
||||
prompt, err := strings.concatenate(prompt_parts[:], context.temp_allocator)
|
||||
log.assertf(err == nil, "Memory allocation failed")
|
||||
|
||||
raw_prompt := []string {
|
||||
user,
|
||||
FIRST_DETAIL,
|
||||
prompt_dir,
|
||||
SPACE,
|
||||
git_prompt,
|
||||
SECOND_DETAIL,
|
||||
}
|
||||
raw_prompt_str, _ := strings.concatenate(raw_prompt[:], context.temp_allocator)
|
||||
term.input.prompt_size = i32(len(utf8.string_to_runes(raw_prompt_str, context.temp_allocator)))
|
||||
|
||||
return prompt
|
||||
}
|
||||
|
||||
|
|
@ -197,21 +219,18 @@ handle_nav :: proc(in_stream: ^io.Stream) {
|
|||
return
|
||||
}
|
||||
|
||||
pop_runes(i32(len(term.input.buffer)))
|
||||
|
||||
history_idx := term.history.maybe_idx.? or_else i32(len(term.history.data))
|
||||
history_idx -= 1
|
||||
term.history.maybe_idx = history_idx
|
||||
|
||||
new_input := term.history.data[history_idx]
|
||||
write_runes(new_input[:])
|
||||
|
||||
replace_input(new_input)
|
||||
|
||||
case DOWN:
|
||||
history_idx, scrolling_history := term.history.maybe_idx.?
|
||||
if !scrolling_history do return
|
||||
|
||||
pop_runes(i32(len(term.input.buffer)))
|
||||
|
||||
history_idx += 1
|
||||
history_len := i32(len(term.history.data))
|
||||
new_input: []rune
|
||||
|
|
@ -227,7 +246,8 @@ handle_nav :: proc(in_stream: ^io.Stream) {
|
|||
term.history.maybe_idx = history_idx
|
||||
new_input = term.history.data[history_idx]
|
||||
}
|
||||
write_runes(new_input[:])
|
||||
|
||||
replace_input(new_input)
|
||||
|
||||
case RIGHT:
|
||||
if term.input.cursor_offset > 0 {
|
||||
|
|
@ -244,45 +264,47 @@ handle_nav :: proc(in_stream: ^io.Stream) {
|
|||
}
|
||||
|
||||
|
||||
@(private)
|
||||
write_runes :: proc(rns: []rune) {
|
||||
append(&term.input.buffer, ..rns[:])
|
||||
fmt.print(utf8.runes_to_string(rns[:]))
|
||||
}
|
||||
|
||||
@(private)
|
||||
write_rune :: proc(rn: rune) {
|
||||
append(&term.input.buffer, rn)
|
||||
fmt.print(rn)
|
||||
idx_to_inject := i32(len(term.input.buffer)) - term.input.cursor_offset
|
||||
inject_at(&term.input.buffer, idx_to_inject, rn)
|
||||
CLEAR_ALL :: "\r\x1b[2K"
|
||||
fmt.print(CLEAR_ALL)
|
||||
print_prompt()
|
||||
|
||||
fmt.print(utf8.runes_to_string(term.input.buffer[:], context.temp_allocator))
|
||||
}
|
||||
|
||||
@(private)
|
||||
pop_runes :: proc(amount: i32) {
|
||||
DELETE_AND_REVERSE :: "\b \b"
|
||||
log.assertf(amount <= i32(len(term.input.buffer)), "Cannot remove more runes that written in buffer")
|
||||
replace_input :: proc(rns: []rune) {
|
||||
clear_input()
|
||||
|
||||
start_idx := i32(len(term.input.buffer)) - amount
|
||||
runes := term.input.buffer[start_idx:]
|
||||
_, _, width := utf8.grapheme_count(utf8.runes_to_string(runes, context.temp_allocator))
|
||||
resize(&term.input.buffer, i32(len(term.input.buffer)) - amount)
|
||||
CLEAR_ALL :: "\r\x1b[2K"
|
||||
fmt.print(CLEAR_ALL)
|
||||
print_prompt()
|
||||
|
||||
for _ in 0..<width {
|
||||
fmt.print(DELETE_AND_REVERSE)
|
||||
}
|
||||
append(&term.input.buffer, ..rns[:])
|
||||
fmt.print(utf8.runes_to_string(term.input.buffer[:], context.temp_allocator))
|
||||
}
|
||||
|
||||
@(private)
|
||||
pop_rune_at_cursor :: proc() {
|
||||
DELETE_AND_REVERSE :: "\b \b"
|
||||
remove_rune_at_cursor :: proc() {
|
||||
buffer_len := i32(len(term.input.buffer))
|
||||
idx_to_remove := buffer_len - 1 - term.input.cursor_offset
|
||||
if idx_to_remove < 0 do return
|
||||
|
||||
if len(term.input.buffer) == 0 do return
|
||||
LEFT :: "\x1b[C"
|
||||
for _ in 0..<buffer_len - term.input.cursor_offset {
|
||||
fmt.print(LEFT)
|
||||
}
|
||||
posix.sleep(3)
|
||||
fmt.print("\x1b[K")
|
||||
|
||||
last_rune := term.input.buffer[len(term.input.buffer)-1]
|
||||
_, _, width := utf8.grapheme_count(utf8.runes_to_string({last_rune}, context.temp_allocator))
|
||||
resize(&term.input.buffer, len(term.input.buffer)-1)
|
||||
ordered_remove(&term.input.buffer, idx_to_remove)
|
||||
fmt.printf(utf8.runes_to_string(term.input.buffer[:], context.temp_allocator))
|
||||
|
||||
for _ in 0..<width {
|
||||
fmt.print(DELETE_AND_REVERSE)
|
||||
for _ in 0..<term.input.cursor_offset {
|
||||
fmt.print("\x1b[D")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -301,12 +323,15 @@ append_history_file :: proc(data: []rune) {
|
|||
log.assertf(werr == nil, "Failed to write to history file")
|
||||
}
|
||||
|
||||
clear_input :: proc() {
|
||||
clear(&term.input.buffer)
|
||||
print_prompt :: proc() {
|
||||
prompt_prefix := get_prompt_prefix()
|
||||
fmt.printf("%s", prompt_prefix)
|
||||
}
|
||||
|
||||
term.input.prompt_size = i32(len(prompt_prefix))
|
||||
clear_input :: proc() {
|
||||
clear(&term.input.buffer)
|
||||
term.input.cursor_offset = 0
|
||||
print_prompt()
|
||||
}
|
||||
|
||||
skip_and_clear :: proc() {
|
||||
|
|
@ -328,7 +353,7 @@ run_prompt :: proc() -> Maybe(string) {
|
|||
case '\n':
|
||||
fmt.println()
|
||||
if len(term.input.buffer) == 0 do return ""
|
||||
|
||||
term.input.cursor_offset = 0
|
||||
append_history_file(term.input.buffer[:])
|
||||
input := utf8.runes_to_string(term.input.buffer[:], context.temp_allocator)
|
||||
|
||||
|
|
@ -337,8 +362,11 @@ run_prompt :: proc() -> Maybe(string) {
|
|||
case '\f':
|
||||
return "clear" // This is a terminal history wipe, usually isn't
|
||||
|
||||
case '\t':
|
||||
|
||||
|
||||
case '\u007f':
|
||||
pop_rune_at_cursor()
|
||||
remove_rune_at_cursor()
|
||||
|
||||
case '\x1b':
|
||||
handle_esc_seq(&in_stream)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue