summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--buffer.c24
-rw-r--r--filesystem-posix.c7
-rw-r--r--filesystem.h1
-rw-r--r--string32.c13
-rw-r--r--ui.c79
5 files changed, 113 insertions, 11 deletions
diff --git a/buffer.c b/buffer.c
index 3663df4..db47e2c 100644
--- a/buffer.c
+++ b/buffer.c
@@ -34,6 +34,19 @@ static void buffer_clear_redo_history(TextBuffer *buffer) {
arr_clear(buffer->redo_history);
}
+static void buffer_clear_undo_history(TextBuffer *buffer) {
+ arr_foreach_ptr(buffer->undo_history, BufferEdit, edit) {
+ buffer_edit_free(edit);
+ }
+ arr_clear(buffer->undo_history);
+}
+
+// clear all undo and redo events
+void buffer_clear_undo_redo(TextBuffer *buffer) {
+ buffer_clear_undo_history(buffer);
+ buffer_clear_redo_history(buffer);
+}
+
// add this edit to the undo history
static void buffer_append_edit(TextBuffer *buffer, BufferEdit const *edit) {
// whenever an edit is made, clear the redo history
@@ -97,6 +110,10 @@ static void buffer_pos_validate(TextBuffer *buffer, BufferPos *p) {
p->index = line_len;
}
+static void buffer_validate_cursor(TextBuffer *buffer) {
+ buffer_pos_validate(buffer, &buffer->cursor_pos);
+}
+
static bool buffer_pos_valid(TextBuffer *buffer, BufferPos p) {
return p.line < buffer->nlines && p.index <= buffer->lines[p.line].len;
}
@@ -1501,6 +1518,9 @@ void buffer_delete_chars_at_pos(TextBuffer *buffer, BufferPos pos, i64 nchars_)
}
buffer_remove_last_edit_if_empty(buffer);
+
+ // cursor position could have been invalidated by this edit
+ buffer_validate_cursor(buffer);
}
// Delete characters between the given buffer positions. Returns number of characters deleted.
@@ -1537,7 +1557,7 @@ void buffer_insert_text_at_cursor(TextBuffer *buffer, String32 str) {
}
void buffer_insert_char_at_cursor(TextBuffer *buffer, char32_t c) {
- String32 s = {1, &c};
+ String32 s = {&c, 1};
buffer_insert_text_at_cursor(buffer, s);
}
@@ -1607,7 +1627,7 @@ static Status buffer_undo_edit(TextBuffer *buffer, BufferEdit const *edit, Buffe
// create inverse edit
if (buffer_edit_create(buffer, inverse, edit->pos, edit->new_len, edit->prev_len)) {
buffer_delete_chars_at_pos(buffer, edit->pos, (i64)edit->new_len);
- String32 str = {edit->prev_len, edit->prev_text};
+ String32 str = {edit->prev_text, edit->prev_len};
buffer_insert_text_at_pos(buffer, edit->pos, str);
success = true;
}
diff --git a/filesystem-posix.c b/filesystem-posix.c
index ebf1407..cf4a04b 100644
--- a/filesystem-posix.c
+++ b/filesystem-posix.c
@@ -7,8 +7,15 @@
FsType fs_path_type(char const *path) {
struct stat statbuf = {0};
+ char linkbuf[8];
+ if (readlink(path, linkbuf, sizeof linkbuf) != -1) {
+ // unfortunately there is no way of telling from stat alone whether a directory is a symbolic link >:(
+ return FS_LINK;
+ }
if (stat(path, &statbuf) != 0)
return FS_NON_EXISTENT;
+ if (S_ISLNK(statbuf.st_mode))
+ return FS_LINK;
if (S_ISREG(statbuf.st_mode))
return FS_FILE;
if (S_ISDIR(statbuf.st_mode))
diff --git a/filesystem.h b/filesystem.h
index 2e561cd..764f45a 100644
--- a/filesystem.h
+++ b/filesystem.h
@@ -5,6 +5,7 @@ typedef enum {
FS_NON_EXISTENT,
FS_FILE,
FS_DIRECTORY,
+ FS_LINK,
FS_OTHER
} FsType;
diff --git a/string32.c b/string32.c
index 4d033c0..f678ad1 100644
--- a/string32.c
+++ b/string32.c
@@ -1,9 +1,18 @@
// UTF-32 string
typedef struct {
- size_t len;
char32_t *str;
+ size_t len;
} String32;
+String32 str32(char32_t *str, size_t len) {
+ String32 s = {str, len};
+ return s;
+}
+
+String32 str32_substr(String32 s, size_t from, size_t len) {
+ return str32(s.str + from, len);
+}
+
void str32_free(String32 *s) {
free(s->str);
s->str = NULL;
@@ -13,7 +22,7 @@ void str32_free(String32 *s) {
// the string returned should be str32_free'd.
// this will return an empty string if the allocation failed or the string is invalid UTF-8
String32 str32_from_utf8(char const *utf8) {
- String32 string = {0, NULL};
+ String32 string = {NULL, 0};
size_t len = strlen(utf8);
if (len) {
// the wide string uses at most as many "characters" (elements?) as the UTF-8 string
diff --git a/ui.c b/ui.c
index d682e83..852428e 100644
--- a/ui.c
+++ b/ui.c
@@ -43,13 +43,13 @@ static int qsort_file_entry_cmp(void const *av, void const *bv) {
}
// cd to the directory `name`. `name` cannot include any path separators.
-static void file_selector_cd(FileSelector *fs, char const *name) {
- if (*name == '\0' || streq(name, ".")) {
+static void file_selector_cd1(FileSelector *fs, char const *name, size_t name_len) {
+ if (name_len == 0 || (name_len == 1 && name[0] == '.')) {
// no name, or .
return;
}
- if (streq(name, "..")) {
+ if (name_len == 2 && name[0] == '.' && name[1] == '.') {
// ..
char *last_sep = strrchr(fs->cwd, PATH_SEPARATOR);
if (last_sep) {
@@ -64,21 +64,84 @@ static void file_selector_cd(FileSelector *fs, char const *name) {
}
}
} else {
- // add path separator to end
- arrcstr_append_str(fs->cwd, PATH_SEPARATOR_STR);
+ // add path separator to end if not already there (which could happen in the case of /)
+ if (fs->cwd[strlen(fs->cwd) - 1] != PATH_SEPARATOR)
+ arrcstr_append_str(fs->cwd, PATH_SEPARATOR_STR);
// add name itself
- arrcstr_append_str(fs->cwd, name);
+ arrcstr_append_strn(fs->cwd, name, name_len);
+ }
+}
+
+// go to the directory `path`. make sure `path` only contains path separators like PATH_SEPARATOR, not any
+// other members of ALL_PATH_SEPARATORS
+static void file_selector_cd(FileSelector *fs, char const *path) {
+ size_t path_len = strlen(path);
+ if (path_len == 0) return;
+
+ if (path[0] == PATH_SEPARATOR
+ #if _WIN32
+ || path[1] == ':' && path[2] == PATH_SEPARATOR
+ #endif
+ ) {
+ // absolute path (e.g. /foo, c:\foo)
+ arr_clear(fs->cwd);
+ if (path_len > 1 &&
+#if _WIN32
+ !(path_len == 3 && path[1] == ':')
+#endif
+ path[path_len - 1] == PATH_SEPARATOR) {
+ // path ends with path separator
+ --path_len;
+ }
+ arrcstr_append_strn(fs->cwd, path, path_len);
+ return;
+ }
+
+ char const *p = path;
+
+ while (*p) {
+ size_t len = strcspn(p, PATH_SEPARATOR_STR);
+ file_selector_cd1(fs, p, len);
+ p += len;
+ p += strspn(p, PATH_SEPARATOR_STR);
}
}
// returns the name of the selected file, or NULL
// if none was selected. the returned pointer should be freed.
static char *file_selector_update(Ted *ted, FileSelector *fs) {
- String32 search_term32 = buffer_get_line(&ted->line_buffer, 0);
+ TextBuffer *line_buffer = &ted->line_buffer;
+ String32 search_term32 = buffer_get_line(line_buffer, 0);
if (!fs->cwd) {
// set the file selector's directory to our current directory.
arrcstr_append_str(fs->cwd, ted->cwd);
}
+
+
+ // check if the search term contains a path separator. if so, cd to the dirname.
+ u32 last_path_sep = U32_MAX;
+ for (u32 i = 0; i < search_term32.len; ++i) {
+ char32_t c = search_term32.str[i];
+ if (c < CHAR_MAX && strchr(ALL_PATH_SEPARATORS, (char)c))
+ last_path_sep = i;
+ }
+
+ if (last_path_sep != U32_MAX) {
+ bool include_last_path_sep = last_path_sep == 0;
+ String32 dir_name32 = str32_substr(search_term32, 0, last_path_sep + include_last_path_sep);
+ char *dir_name = str32_to_utf8_cstr(dir_name32);
+ if (dir_name) {
+ // replace all members of ALL_PATH_SEPARATORS with PATH_SEPARATOR in dir_name (i.e. change / to \ on windows)
+ for (char *p = dir_name; *p; ++p)
+ if (strchr(ALL_PATH_SEPARATORS, *p))
+ *p = PATH_SEPARATOR;
+
+ file_selector_cd(fs, dir_name);
+ buffer_delete_chars_at_pos(line_buffer, buffer_start_of_file(line_buffer), last_path_sep + 1); // delete up to and including the last path separator
+ buffer_clear_undo_redo(line_buffer);
+ }
+ }
+
char *search_term = search_term32.len ? str32_to_utf8_cstr(search_term32) : NULL;
bool submitted = fs->submitted;
@@ -103,6 +166,7 @@ static char *file_selector_update(Ted *ted, FileSelector *fs) {
break;
case FS_DIRECTORY:
file_selector_cd(fs, name);
+ buffer_clear(line_buffer); // clear search term
break;
default: break;
}
@@ -119,6 +183,7 @@ static char *file_selector_update(Ted *ted, FileSelector *fs) {
break;
case FS_DIRECTORY:
file_selector_cd(fs, name);
+ buffer_clear(line_buffer); // clear search term
break;
default: break;
}