diff options
Diffstat (limited to 'src/main/lsp/format.c')
| -rw-r--r-- | src/main/lsp/format.c | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/src/main/lsp/format.c b/src/main/lsp/format.c new file mode 100644 index 0000000..2019a90 --- /dev/null +++ b/src/main/lsp/format.c @@ -0,0 +1,149 @@ +#include "format.h" + +#include "completion.h" +#include "dged/buffer.h" +#include "dged/buffer_view.h" +#include "dged/minibuffer.h" +#include "dged/settings.h" +#include "dged/window.h" +#include "main/completion.h" +#include "main/lsp.h" + +struct formatted_buffer { + struct buffer *buffer; + bool save; +}; + +static uint32_t get_tab_width(struct buffer *buffer) { + struct setting *tw = lang_setting(&buffer->lang, "tab-width"); + if (tw == NULL) { + tw = settings_get("editor.tab-width"); + } + + uint32_t tab_width = 4; + if (tw != NULL && tw->value.type == Setting_Number) { + tab_width = tw->value.data.number_value; + } + return tab_width; +} + +static bool use_tabs(struct buffer *buffer) { + struct setting *ut = lang_setting(&buffer->lang, "use-tabs"); + if (ut == NULL) { + ut = settings_get("editor.use-tabs"); + } + + bool use_tabs = false; + if (ut != NULL && ut->value.type == Setting_Bool) { + use_tabs = ut->value.data.bool_value; + } + + return use_tabs; +} + +static struct formatting_options options_from_lang(struct buffer *buffer) { + return (struct formatting_options){ + .tab_size = get_tab_width(buffer), + .use_spaces = !use_tabs(buffer), + }; +} + +void handle_format_response(struct lsp_server *server, + struct lsp_response *response, void *userdata) { + + text_edit_vec edits = text_edits_from_json(&response->value.result); + struct formatted_buffer *buffer = (struct formatted_buffer *)userdata; + + pause_completion(); + if (!VEC_EMPTY(&edits)) { + apply_edits_buffer(server, buffer->buffer, edits, NULL); + + if (buffer->save) { + buffer_to_file(buffer->buffer); + } + } + resume_completion(); + + text_edits_free(edits); + free(buffer); +} + +static void format_buffer(struct lsp_server *server, struct buffer *buffer, + bool save) { + struct formatted_buffer *b = + (struct formatted_buffer *)calloc(1, sizeof(struct formatted_buffer)); + b->buffer = buffer; + b->save = save; + + uint64_t id = new_pending_request(server, handle_format_response, b); + struct versioned_text_document_identifier doc = + versioned_identifier_from_buffer(buffer); + + struct document_formatting_params params = { + .text_document.uri = doc.uri, + .options = options_from_lang(buffer), + }; + + struct s8 json_payload = document_formatting_params_to_json(¶ms); + lsp_send(lsp_backend(server), + lsp_create_request(id, s8("textDocument/formatting"), json_payload)); + + versioned_text_document_identifier_free(&doc); + s8delete(json_payload); +} + +void format_document(struct lsp_server *server, struct buffer *buffer) { + format_buffer(server, buffer, false); +} + +void format_document_save(struct lsp_server *server, struct buffer *buffer) { + format_buffer(server, buffer, true); +} + +void format_region(struct lsp_server *server, struct buffer *buffer, + struct region region) { + struct formatted_buffer *b = + (struct formatted_buffer *)calloc(1, sizeof(struct formatted_buffer)); + b->buffer = buffer; + b->save = false; + + uint64_t id = new_pending_request(server, handle_format_response, b); + struct versioned_text_document_identifier doc = + versioned_identifier_from_buffer(buffer); + + struct document_range_formatting_params params = { + .text_document.uri = doc.uri, + .range = region_to_lsp(buffer, region, server), + .options = options_from_lang(buffer), + }; + + struct s8 json_payload = document_range_formatting_params_to_json(¶ms); + lsp_send(lsp_backend(server), + lsp_create_request(id, s8("textDocument/formatting"), json_payload)); + + versioned_text_document_identifier_free(&doc); + s8delete(json_payload); +} + +int32_t format_cmd(struct command_ctx ctx, int argc, const char **argv) { + (void)ctx; + (void)argc; + (void)argv; + + struct buffer_view *bv = window_buffer_view(windows_get_active()); + + struct lsp_server *server = lsp_server_for_lang_id(bv->buffer->lang.id); + if (server == NULL) { + return 0; + } + + struct region reg = region_new(bv->dot, bv->mark); + if (bv->mark_set && region_has_size(reg)) { + buffer_view_clear_mark(bv); + format_region(server, bv->buffer, reg); + } else { + format_document(server, bv->buffer); + } + + return 0; +} |
