summaryrefslogtreecommitdiff
path: root/buffer.c
diff options
context:
space:
mode:
Diffstat (limited to 'buffer.c')
-rw-r--r--buffer.c262
1 files changed, 179 insertions, 83 deletions
diff --git a/buffer.c b/buffer.c
index f742950..6df6c9c 100644
--- a/buffer.c
+++ b/buffer.c
@@ -67,6 +67,10 @@ bool buffer_is_untitled(TextBuffer *buffer) {
return false;
}
+bool buffer_is_named_file(TextBuffer *buffer) {
+ return buffer->filename && !buffer_is_untitled(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
@@ -245,6 +249,8 @@ static inline Font *buffer_font(TextBuffer *buffer) {
// what programming language is this?
Language buffer_language(TextBuffer *buffer) {
+ // @TODO: cache this?
+ // (we're calling buffer_lsp on every edit and that calls this)
if (buffer->manual_language >= 1 && buffer->manual_language <= LANG_COUNT)
return (Language)(buffer->manual_language - 1);
Settings const *settings = buffer->ted->default_settings; // important we don't use buffer_settings here since that would cause a loop!
@@ -280,6 +286,13 @@ Language buffer_language(TextBuffer *buffer) {
return match;
}
+LSP *buffer_lsp(TextBuffer *buffer) {
+ if (!buffer_is_named_file(buffer))
+ return NULL;
+ return ted_get_lsp(buffer->ted, buffer_language(buffer));
+}
+
+
// score is higher if context is closer match.
static long context_score(const char *path, Language lang, const SettingsContext *context) {
long score = 0;
@@ -545,6 +558,10 @@ void buffer_check_valid(TextBuffer *buffer) {
}
}
#else
+static void buffer_pos_check_valid(TextBuffer *buffer, BufferPos p) {
+ (void)buffer; (void)p;
+}
+
void buffer_check_valid(TextBuffer *buffer) {
(void)buffer;
}
@@ -729,6 +746,15 @@ static void buffer_line_free(Line *line) {
// Free a buffer. Once a buffer is freed, you can call buffer_create on it again.
// Does not free the pointer `buffer` (buffer might not have even been allocated with malloc)
void buffer_free(TextBuffer *buffer) {
+ LSP *lsp = buffer_lsp(buffer);
+ if (lsp) {
+ LSPRequest did_close = {.type = LSP_REQUEST_DID_CLOSE};
+ did_close.data.close = (LSPRequestDidClose){
+ .document = lsp_document_id(lsp, buffer->filename)
+ };
+ lsp_send_request(lsp, &did_close);
+ }
+
Line *lines = buffer->lines;
u32 nlines = buffer->nlines;
for (u32 i = 0; i < nlines; ++i) {
@@ -1180,16 +1206,6 @@ i64 buffer_cursor_move_down(TextBuffer *buffer, i64 by) {
return ret;
}
-// Is this character a "word" character?
-// This determines how buffer_pos_move_words (i.e. ctrl+left/right) works
-static bool is_word(char32_t c) {
- return c > WCHAR_MAX || c == '_' || iswalnum((wint_t)c);
-}
-
-static bool is_space(char32_t c) {
- return c > WCHAR_MAX || iswspace((wint_t)c);
-}
-
// move left / right by the specified number of words
// returns the number of words successfully moved forward
i64 buffer_pos_move_words(TextBuffer *buffer, BufferPos *pos, i64 nwords) {
@@ -1370,6 +1386,36 @@ static Status buffer_insert_lines(TextBuffer *buffer, u32 where, u32 number) {
return false;
}
+// LSP uses UTF-16 indices because Microsoft fucking loves UTF-16 and won't let it die
+LSPPosition buffer_pos_to_lsp(TextBuffer *buffer, BufferPos pos) {
+ LSPPosition lsp_pos = {
+ .line = pos.line
+ };
+ buffer_pos_validate(buffer, &pos);
+ const Line *line = &buffer->lines[pos.line];
+ const char32_t *str = line->str;
+ for (uint32_t i = 0; i < pos.index; ++i) {
+ if (str[i] < 0x10000)
+ lsp_pos.character += 1; // this codepoint needs 1 UTF-16 word
+ else
+ lsp_pos.character += 2; // this codepoint needs 2 UTF-16 words
+ }
+ return lsp_pos;
+}
+
+static void buffer_send_lsp_did_change_request(LSP *lsp, TextBuffer *buffer, BufferPos pos,
+ u32 nchars_deleted, String32 new_text) {
+ if (!buffer_is_named_file(buffer))
+ return; // this isn't a named buffer so we can't send a didChange request.
+ LSPDocumentChangeEvent event = {0};
+ if (new_text.len > 0)
+ event.text = str32_to_utf8_cstr(new_text);
+ event.range.start = buffer_pos_to_lsp(buffer, pos);
+ BufferPos pos_end = buffer_pos_advance(buffer, pos, nchars_deleted);
+ event.range.end = buffer_pos_to_lsp(buffer, pos_end);
+ lsp_document_changed(lsp, buffer->filename, event);
+}
+
// inserts the given text, returning the position of the end of the text
BufferPos buffer_insert_text_at_pos(TextBuffer *buffer, BufferPos pos, String32 str) {
buffer_pos_validate(buffer, &pos);
@@ -1399,12 +1445,30 @@ BufferPos buffer_insert_text_at_pos(TextBuffer *buffer, BufferPos pos, String32
str.str = str_copy;
}
+
if (buffer->is_line_buffer) {
// remove all the newlines from str.
str32_remove_all_instances_of_char(&str, '\n');
}
str32_remove_all_instances_of_char(&str, '\r');
+
+ if (buffer->ted->autocomplete.open) {
+ // close completions if a non-word character is typed
+ bool close_completions = false;
+ for (u32 i = 0; i < str.len; ++i) {
+ if (!is_word(str.str[i])) {
+ close_completions = true;
+ break;
+ }
+ }
+ if (close_completions)
+ autocomplete_close(buffer->ted);
+ }
+
+ LSP *lsp = buffer_lsp(buffer);
+ if (lsp)
+ buffer_send_lsp_did_change_request(lsp, buffer, pos, 0, str);
if (buffer->store_undo_events) {
BufferEdit *last_edit = arr_lastp(buffer->undo_history);
@@ -1649,6 +1713,34 @@ void buffer_delete_chars_at_pos(TextBuffer *buffer, BufferPos pos, i64 nchars_)
// When generating undo events, we allocate nchars characters of memory (see buffer_edit below).
// Not doing this might also cause other bugs, best to keep it here just in case.
nchars = (u32)buffer_get_text_at_pos(buffer, pos, NULL, nchars);
+
+ if (buffer->ted->autocomplete.open) {
+ // close completions if a non-word character is deleted
+ bool close_completions = false;
+ if (nchars > 256) {
+ // this is a massive deletion
+ // even if it's all word characters, let's close the completion menu anyways
+ close_completions = true;
+ } else {
+ char32_t text[256];
+ size_t n = buffer_get_text_at_pos(buffer, pos, text, nchars);
+ (void)n;
+ assert(n == nchars);
+ for (u32 i = 0; i < nchars; ++i) {
+ if (!is_word(text[i])) {
+ close_completions = true;
+ break;
+ }
+ }
+ }
+ if (close_completions)
+ autocomplete_close(buffer->ted);
+ }
+
+
+ LSP *lsp = buffer_lsp(buffer);
+ if (lsp)
+ buffer_send_lsp_did_change_request(lsp, buffer, pos, nchars, (String32){0});
if (buffer->store_undo_events) {
// we need to make sure the undo history keeps track of the edit.
@@ -2040,7 +2132,6 @@ void buffer_paste(TextBuffer *buffer) {
}
}
-
// if an error occurs, buffer is left untouched (except for the error field) and the function returns false.
Status buffer_load_file(TextBuffer *buffer, char const *filename) {
FILE *fp = fopen(filename, "rb");
@@ -2059,88 +2150,93 @@ Status buffer_load_file(TextBuffer *buffer, char const *filename) {
buffer_seterr(buffer, "File too big (size: %zu).", file_size);
success = false;
} else {
- u8 *file_contents = buffer_calloc(buffer, 1, file_size);
- if (file_contents) {
- lines_capacity = 4;
- lines = buffer_calloc(buffer, lines_capacity, sizeof *buffer->lines); // initial lines
- if (lines) {
- nlines = 1;
- size_t bytes_read = fread(file_contents, 1, file_size, fp);
- if (bytes_read == file_size) {
- char32_t c = 0;
- for (u8 *p = file_contents, *end = p + file_size; p != end; ) {
- if (*p == '\r' && p != end-1 && p[1] == '\n') {
- // CRLF line endings
- p += 2;
- c = '\n';
- } else {
- size_t n = unicode_utf8_to_utf32(&c, (char *)p, (size_t)(end - p));
- if (n == 0) {
- // null character
- c = 0;
- ++p;
- } else if (n >= (size_t)(-2)) {
- // invalid UTF-8
- success = false;
- buffer_seterr(buffer, "Invalid UTF-8 (position: %td).", p - file_contents);
- break;
- } else {
- p += n;
- }
- }
- if (c == '\n') {
- if (buffer_lines_set_min_capacity(buffer, &lines, &lines_capacity, nlines + 1))
- ++nlines;
- } else {
- u32 line_idx = nlines - 1;
- Line *line = &lines[line_idx];
- buffer_line_append_char(buffer, line, c);
- }
- }
+ u8 *file_contents = buffer_calloc(buffer, 1, file_size + 1);
+ lines_capacity = 4;
+ lines = buffer_calloc(buffer, lines_capacity, sizeof *buffer->lines); // initial lines
+ nlines = 1;
+ size_t bytes_read = fread(file_contents, 1, file_size, fp);
+ if (bytes_read == file_size) {
+ char32_t c = 0;
+ for (u8 *p = file_contents, *end = p + file_size; p != end; ) {
+ if (*p == '\r' && p != end-1 && p[1] == '\n') {
+ // CRLF line endings
+ p += 2;
+ c = '\n';
} else {
- success = false;
+ size_t n = unicode_utf8_to_utf32(&c, (char *)p, (size_t)(end - p));
+ if (n == 0) {
+ // null character
+ c = 0;
+ ++p;
+ } else if (n >= (size_t)(-2)) {
+ // invalid UTF-8
+ success = false;
+ buffer_seterr(buffer, "Invalid UTF-8 (position: %td).", p - file_contents);
+ break;
+ } else {
+ p += n;
+ }
}
- if (!success) {
- // something went wrong; we need to free all the memory we used
- for (u32 i = 0; i < nlines; ++i)
- buffer_line_free(&lines[i]);
- free(lines);
+ if (c == '\n') {
+ if (buffer_lines_set_min_capacity(buffer, &lines, &lines_capacity, nlines + 1))
+ ++nlines;
+ } else {
+ u32 line_idx = nlines - 1;
+ Line *line = &lines[line_idx];
+ buffer_line_append_char(buffer, line, c);
}
}
- free(file_contents);
}
- if (ferror(fp)) {
+ if (ferror(fp)) {
buffer_seterr(buffer, "Error reading from file.");
success = false;
}
+ if (!success) {
+ // something went wrong; we need to free all the memory we used
+ for (u32 i = 0; i < nlines; ++i)
+ buffer_line_free(&lines[i]);
+ free(lines);
+ }
+
+ if (success) {
+ char *filename_copy = buffer_strdup(buffer, filename);
+ if (!filename_copy) success = false;
+ if (success) {
+ // everything is good
+ buffer_clear(buffer);
+ buffer->lines = lines;
+ buffer->nlines = nlines;
+ buffer->frame_earliest_line_modified = 0;
+ buffer->frame_latest_line_modified = nlines - 1;
+ buffer->lines_capacity = lines_capacity;
+ buffer->filename = filename_copy;
+ buffer->last_write_time = time_last_modified(buffer->filename);
+ if (!(fs_path_permission(filename) & FS_PERMISSION_WRITE)) {
+ // can't write to this file; make the buffer view only.
+ buffer->view_only = true;
+ }
+ }
+
+ LSP *lsp = buffer_lsp(buffer);
+ if (lsp) {
+ // send didOpen
+ LSPRequest request = {.type = LSP_REQUEST_DID_OPEN};
+ LSPRequestDidOpen *open = &request.data.open;
+ open->file_contents = (char *)file_contents;
+ open->document = lsp_document_id(lsp, filename);
+ open->language = buffer_language(buffer);
+ lsp_send_request(lsp, &request);
+ file_contents = NULL; // don't free
+ }
+ }
+
+ free(file_contents);
}
- if (fclose(fp) != 0) {
- buffer_seterr(buffer, "Error closing file.");
- success = false;
- }
+ fclose(fp);
} else {
buffer_seterr(buffer, "Couldn't open file %s: %s.", filename, strerror(errno));
success = false;
}
- if (success) {
- char *filename_copy = buffer_strdup(buffer, filename);
- if (!filename_copy) success = false;
- if (success) {
- // everything is good
- buffer_clear(buffer);
- buffer->lines = lines;
- buffer->nlines = nlines;
- buffer->frame_earliest_line_modified = 0;
- buffer->frame_latest_line_modified = nlines - 1;
- buffer->lines_capacity = lines_capacity;
- buffer->filename = filename_copy;
- buffer->last_write_time = time_last_modified(buffer->filename);
- if (!(fs_path_permission(filename) & FS_PERMISSION_WRITE)) {
- // can't write to this file; make the buffer view only.
- buffer->view_only = true;
- }
- }
- }
return success;
}
@@ -2284,11 +2380,11 @@ u32 buffer_last_rendered_line(TextBuffer *buffer) {
// returns true if the buffer "used" this event
bool buffer_handle_click(Ted *ted, TextBuffer *buffer, v2 click, u8 times) {
BufferPos buffer_pos;
- if (ted->autocomplete) {
- if (rect_contains_point(ted->autocomplete_rect, click))
+ if (ted->autocomplete.open) {
+ if (rect_contains_point(ted->autocomplete.rect, click))
return false; // don't look at clicks in the autocomplete menu
else
- ted->autocomplete = false; // close autocomplete menu if user clicks outside of it
+ autocomplete_close(ted); // close autocomplete menu if user clicks outside of it
}
if (buffer_pixels_to_pos(buffer, click, &buffer_pos)) {
// user clicked on buffer