summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt8
-rw-r--r--buffer.c25
-rw-r--r--command.c8
-rw-r--r--command.h2
-rw-r--r--ide-format.c70
-rw-r--r--ide-rename-symbol.c8
-rw-r--r--lsp-parse.c30
-rw-r--r--lsp-write.c20
-rw-r--r--lsp.c11
-rw-r--r--lsp.h29
-rw-r--r--main.c6
-rw-r--r--ted-internal.h20
-rw-r--r--ted.c4
-rw-r--r--ted.cfg7
-rw-r--r--ted.h10
15 files changed, 239 insertions, 19 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 652f407..238b113 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,10 +2,10 @@ cmake_minimum_required(VERSION 3.5)
project(ted)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(SOURCES buffer.c build.c colors.c command.c config.c find.c gl.c ide-autocomplete.c
- ide-document-link.c ide-definitions.c ide-highlights.c ide-hover.c ide-signature-help.c
- ide-usages.c ide-rename-symbol.c lsp.c lsp-json.c lsp-parse.c lsp-write.c main.c menu.c
- node.c os.c session.c stb_image.c stb_truetype.c syntax.c tags.c ted.c text.c
- ui.c util.c macro.c)
+ ide-document-link.c ide-definitions.c ide-format.c ide-highlights.c ide-hover.c
+ ide-signature-help.c ide-usages.c ide-rename-symbol.c lsp.c lsp-json.c lsp-parse.c
+ lsp-write.c main.c menu.c node.c os.c session.c stb_image.c stb_truetype.c syntax.c
+ tags.c ted.c text.c ui.c util.c macro.c)
else()
set(SOURCES main.c)
endif()
diff --git a/buffer.c b/buffer.c
index 549a99e..1214fa4 100644
--- a/buffer.c
+++ b/buffer.c
@@ -1832,6 +1832,31 @@ LSPDocumentPosition buffer_cursor_pos_as_lsp_document_position(TextBuffer *buffe
return buffer_pos_to_lsp_document_position(buffer, buffer->cursor_pos);
}
+LSPRange buffer_selection_as_lsp_range(TextBuffer *buffer) {
+ if (!buffer->selection)
+ return (LSPRange){0};
+ LSPPosition cursor = buffer_pos_to_lsp_position(buffer, buffer->cursor_pos);
+ LSPPosition sel = buffer_pos_to_lsp_position(buffer, buffer->selection_pos);
+ if (buffer_pos_cmp(buffer->cursor_pos, buffer->selection_pos) < 0) {
+ return (LSPRange){
+ .start = cursor,
+ .end = sel,
+ };
+ } else {
+ return (LSPRange){
+ .start = sel,
+ .end = cursor,
+ };
+ }
+}
+
+void buffer_apply_lsp_text_edit(TextBuffer *buffer, const LSPResponse *response, const LSPTextEdit *edit) {
+ BufferPos start = buffer_pos_from_lsp(buffer, edit->range.start);
+ BufferPos end = buffer_pos_from_lsp(buffer, edit->range.end);
+ buffer_delete_chars_between(buffer, start, end);
+ buffer_insert_utf8_at_pos(buffer, start, lsp_response_string(response, edit->new_text));
+}
+
static void buffer_send_lsp_did_change(LSP *lsp, TextBuffer *buffer, BufferPos pos,
u32 nchars_deleted, String32 new_text) {
if (!buffer_is_named_file(buffer))
diff --git a/command.c b/command.c
index 07d3856..2407551 100644
--- a/command.c
+++ b/command.c
@@ -105,6 +105,8 @@ static CommandName command_names[] = {
{"increment-number", CMD_INCREMENT_NUMBER},
{"decrement-number", CMD_DECREMENT_NUMBER},
{"rename-symbol", CMD_RENAME_SYMBOL},
+ {"format-file", CMD_FORMAT_FILE},
+ {"format-selection", CMD_FORMAT_SELECTION},
};
static_assert_if_possible(arr_count(command_names) == CMD_COUNT)
@@ -703,5 +705,11 @@ void command_execute_ex(Ted *ted, Command c, const CommandArgument *full_argumen
if (buffer && buffer_lsp(buffer))
menu_open(ted, MENU_RENAME_SYMBOL);
break;
+ case CMD_FORMAT_FILE:
+ format_file(ted);
+ break;
+ case CMD_FORMAT_SELECTION:
+ format_selection(ted);
+ break;
}
}
diff --git a/command.h b/command.h
index 25ba4a5..1beb18a 100644
--- a/command.h
+++ b/command.h
@@ -112,6 +112,8 @@ typedef enum {
CMD_GOTO_DEFINITION_AT_CURSOR,
CMD_GOTO_DECLARATION_AT_CURSOR,
CMD_GOTO_TYPE_DEFINITION_AT_CURSOR,
+ CMD_FORMAT_FILE,
+ CMD_FORMAT_SELECTION,
CMD_LSP_RESET,
CMD_COPY,
diff --git a/ide-format.c b/ide-format.c
new file mode 100644
index 0000000..70103c7
--- /dev/null
+++ b/ide-format.c
@@ -0,0 +1,70 @@
+#include "ted-internal.h"
+
+struct Formatting {
+ LSPServerRequestID last_request_id;
+};
+
+void format_init(Ted *ted) {
+ ted->formatting = calloc(1, sizeof (Formatting));
+}
+
+static void format_common(Ted *ted, bool selection) {
+ Formatting *formatting = ted->formatting;
+ ted_cancel_lsp_request(ted, &formatting->last_request_id);
+ TextBuffer *buffer = ted_active_buffer(ted);
+ if (!buffer) return;
+ if (selection && !buffer_has_selection(buffer)) return;
+ LSP *lsp = buffer_lsp(buffer);
+ if (!lsp) return;
+ Settings *settings = buffer_settings(buffer);
+ LSPRequest request = {
+ .type = selection ? LSP_REQUEST_RANGE_FORMATTING : LSP_REQUEST_FORMATTING
+ };
+ LSPRequestFormatting *req_data = &request.data.formatting;
+ req_data->document = buffer_lsp_document_id(buffer);
+ req_data->indent_with_spaces = settings->indent_with_spaces;
+ req_data->tab_width = settings->tab_width;
+ if (selection) {
+ req_data->use_range = true;
+ req_data->range = buffer_selection_as_lsp_range(buffer);
+ }
+ formatting->last_request_id = lsp_send_request(lsp, &request);
+}
+
+void format_selection(Ted *ted) {
+ format_common(ted, true);
+}
+
+void format_file(Ted *ted) {
+ format_common(ted, false);
+}
+
+void format_cancel_request(Ted *ted) {
+ Formatting *formatting = ted->formatting;
+ ted_cancel_lsp_request(ted, &formatting->last_request_id);
+}
+
+void format_process_lsp_response(Ted *ted, const LSPResponse *response) {
+ Formatting *formatting = ted->formatting;
+ const LSPRequest *request = &response->request;
+ if (request->id != formatting->last_request_id.id)
+ return;
+ if (!(request->type == LSP_REQUEST_RANGE_FORMATTING
+ || request->type == LSP_REQUEST_FORMATTING)) {
+ return;
+ }
+ TextBuffer *buffer = ted->active_buffer;
+ if (!buffer) return;
+ if (buffer_lsp_document_id(buffer) != request->data.formatting.document)
+ return; // switched document
+
+ const LSPResponseFormatting *f = &response->data.formatting;
+ arr_foreach_ptr(f->edits, const LSPTextEdit, edit) {
+ buffer_apply_lsp_text_edit(buffer, response, edit);
+ }
+}
+
+void format_quit(Ted *ted) {
+ free(ted->formatting);
+ ted->formatting = NULL;
+}
diff --git a/ide-rename-symbol.c b/ide-rename-symbol.c
index b5208cc..c32f984 100644
--- a/ide-rename-symbol.c
+++ b/ide-rename-symbol.c
@@ -152,13 +152,7 @@ void rename_symbol_process_lsp_response(Ted *ted, const LSPResponse *response) {
goto done;
}
-
- const LSPTextEdit *edit = &change_data->edit;
- BufferPos start = buffer_pos_from_lsp(buffer, edit->range.start);
- BufferPos end = buffer_pos_from_lsp(buffer, edit->range.end);
- buffer_delete_chars_between(buffer, start, end);
- buffer_insert_utf8_at_pos(buffer, start, lsp_response_string(response, edit->new_text));
-
+ buffer_apply_lsp_text_edit(buffer, response, &change_data->edit);
}
break;
case LSP_CHANGE_RENAME: {
diff --git a/lsp-parse.c b/lsp-parse.c
index 70301d3..61e44c4 100644
--- a/lsp-parse.c
+++ b/lsp-parse.c
@@ -234,6 +234,16 @@ static void parse_capabilities(LSP *lsp, const JSON *json, JSONObject capabiliti
if (workspace_symbol_value.type != JSON_UNDEFINED && workspace_symbol_value.type != JSON_FALSE) {
cap->workspace_symbols_support = true;
}
+
+ JSONValue formatting_value = json_object_get(json, capabilities, "documentFormattingProvider");
+ if (formatting_value.type == JSON_OBJECT || formatting_value.type == JSON_TRUE) {
+ cap->formatting_support = true;
+ }
+
+ JSONValue range_formatting_value = json_object_get(json, capabilities, "documentRangeFormattingProvider");
+ if (range_formatting_value.type == JSON_OBJECT || range_formatting_value.type == JSON_TRUE) {
+ cap->range_formatting_support = true;
+ }
JSONObject workspace = json_object_get_object(json, capabilities, "workspace");
// check WorkspaceFoldersServerCapabilities
@@ -993,6 +1003,22 @@ static bool parse_document_link_response(LSP *lsp, const JSON *json, LSPResponse
return true;
}
+static bool parse_formatting_response(LSP *lsp, const JSON *json, LSPResponse *response) {
+ JSONValue edits_val = json_get(json, "result");
+ if (!(edits_val.type == JSON_ARRAY || edits_val.type == JSON_NULL)) {
+ lsp_set_error(lsp, "Expected TextEdit[] or null for formatting response; got %s",
+ json_type_to_str(edits_val.type));
+ return false;
+ }
+ JSONArray edits = json_force_array(edits_val);
+ LSPResponseFormatting *f = &response->data.formatting;
+ for (u32 i = 0; i < edits.len; ++i) {
+ if (!parse_text_edit(lsp, response, json, json_array_get(json, edits, i), arr_addp(f->edits)))
+ return false;
+ }
+ return true;
+}
+
void process_message(LSP *lsp, JSON *json) {
#if 0
@@ -1066,6 +1092,10 @@ void process_message(LSP *lsp, JSON *json) {
case LSP_REQUEST_RENAME:
add_to_messages = parse_rename_response(lsp, json, &response);
break;
+ case LSP_REQUEST_FORMATTING:
+ case LSP_REQUEST_RANGE_FORMATTING:
+ add_to_messages = parse_formatting_response(lsp, json, &response);
+ break;
case LSP_REQUEST_DOCUMENT_LINK:
add_to_messages = parse_document_link_response(lsp, json, &response);
break;
diff --git a/lsp-write.c b/lsp-write.c
index 17aef7f..050d784 100644
--- a/lsp-write.c
+++ b/lsp-write.c
@@ -302,6 +302,10 @@ static const char *lsp_request_method(LSPRequest *request) {
return "workspace/didChangeConfiguration";
case LSP_REQUEST_WORKSPACE_SYMBOLS:
return "workspace/symbol";
+ case LSP_REQUEST_RANGE_FORMATTING:
+ return "textDocument/rangeFormatting";
+ case LSP_REQUEST_FORMATTING:
+ return "textDocument/formatting";
}
assert(0);
return "$/ignore";
@@ -652,6 +656,22 @@ void write_request(LSP *lsp, LSPRequest *request) {
str_builder_append(&o->builder, lsp_request_string(request, config->settings));
write_obj_end(o);
} break;
+ case LSP_REQUEST_FORMATTING:
+ case LSP_REQUEST_RANGE_FORMATTING: {
+ const LSPRequestFormatting *formatting = &request->data.formatting;
+ write_key_obj_start(o, "params");
+ write_key_obj_start(o, "textDocument");
+ write_key_file_uri(o, "uri", formatting->document);
+ write_obj_end(o);
+ write_key_obj_start(o, "options");
+ write_key_number(o, "tabSize", formatting->tab_width);
+ write_key_bool(o, "insertSpaces", formatting->indent_with_spaces);
+ write_obj_end(o);
+ if (formatting->use_range) {
+ write_key_range(o, "range", formatting->range);
+ }
+ write_obj_end(o);
+ } break;
}
write_obj_end(o);
diff --git a/lsp.c b/lsp.c
index 9d98b8c..0ab78a8 100644
--- a/lsp.c
+++ b/lsp.c
@@ -119,6 +119,8 @@ void lsp_request_free(LSPRequest *r) {
case LSP_REQUEST_DOCUMENT_LINK:
case LSP_REQUEST_CONFIGURATION:
case LSP_REQUEST_DID_OPEN:
+ case LSP_REQUEST_FORMATTING:
+ case LSP_REQUEST_RANGE_FORMATTING:
break;
case LSP_REQUEST_PUBLISH_DIAGNOSTICS: {
LSPRequestPublishDiagnostics *pub = &r->data.publish_diagnostics;
@@ -169,6 +171,9 @@ void lsp_response_free(LSPResponse *r) {
case LSP_REQUEST_DOCUMENT_LINK:
arr_free(r->data.document_link.links);
break;
+ case LSP_REQUEST_FORMATTING:
+ arr_free(r->data.formatting.edits);
+ break;
default:
break;
}
@@ -252,6 +257,10 @@ static bool lsp_supports_request(LSP *lsp, const LSPRequest *request) {
return cap->references_support;
case LSP_REQUEST_DOCUMENT_LINK:
return cap->document_link_support;
+ case LSP_REQUEST_FORMATTING:
+ return cap->formatting_support;
+ case LSP_REQUEST_RANGE_FORMATTING:
+ return cap->range_formatting_support;
}
assert(0);
return false;
@@ -293,6 +302,8 @@ static bool request_type_is_notification(LSPRequestType type) {
case LSP_REQUEST_WORKSPACE_SYMBOLS:
case LSP_REQUEST_WORKSPACE_FOLDERS:
case LSP_REQUEST_DOCUMENT_LINK:
+ case LSP_REQUEST_FORMATTING:
+ case LSP_REQUEST_RANGE_FORMATTING:
return false;
}
assert(0);
diff --git a/lsp.h b/lsp.h
index 5545c61..832d3d4 100644
--- a/lsp.h
+++ b/lsp.h
@@ -87,9 +87,10 @@ typedef enum {
LSP_REQUEST_REFERENCES, //< textDocument/references
LSP_REQUEST_RENAME, //< textDocument/rename
LSP_REQUEST_DOCUMENT_LINK, //< textDocument/documentLink
+ LSP_REQUEST_FORMATTING, //< textDocument/formatting
+ LSP_REQUEST_RANGE_FORMATTING, //< textDocument/rangeFormatting
LSP_REQUEST_WORKSPACE_SYMBOLS, //< workspace/symbol
LSP_REQUEST_DID_CHANGE_WORKSPACE_FOLDERS, //< workspace/didChangeWorkspaceFolders
-
// server-to-client
LSP_REQUEST_SHOW_MESSAGE, //< window/showMessage and window/showMessageRequest
LSP_REQUEST_LOG_MESSAGE, //< window/logMessage
@@ -240,6 +241,17 @@ typedef struct {
} LSPRequestConfiguration;
typedef struct {
+ LSPDocumentID document;
+ u8 tab_width;
+ bool indent_with_spaces;
+ bool use_range;
+ /// range to format
+ ///
+ /// only applicable if `use_range` is `true`.
+ LSPRange range;
+} LSPRequestFormatting;
+
+typedef struct {
LSPMessageType type;
/// LSP requests/responses tend to have a lot of strings.
/// to avoid doing a ton of allocations+frees,
@@ -247,6 +259,7 @@ typedef struct {
char *string_data;
} LSPMessageBase;
+/// an LSP request or notification
typedef struct {
LSPMessageBase base;
LSPRequestID id;
@@ -273,6 +286,8 @@ typedef struct {
LSPRequestRename rename;
LSPRequestDocumentLink document_link;
LSPRequestPublishDiagnostics publish_diagnostics;
+ // LSP_REQUEST_FORMATTING and LSP_REQUEST_RANGE_FORMATTING
+ LSPRequestFormatting formatting;
} data;
} LSPRequest;
@@ -525,6 +540,10 @@ typedef struct {
} LSPResponseDocumentLink;
typedef struct {
+ LSPTextEdit *edits;
+} LSPResponseFormatting;
+
+typedef struct {
LSPMessageBase base;
/// the request which this is a response to
LSPRequest request;
@@ -535,13 +554,15 @@ typedef struct {
LSPResponseCompletion completion;
LSPResponseSignatureHelp signature_help;
LSPResponseHover hover;
- // LSP_REQUEST_DEFINITION, LSP_REQUEST_DECLARATION, LSP_REQUEST_TYPE_DEFINITION, or LSP_REQUEST_IMPLEMENTATION
+ /// `LSP_REQUEST_DEFINITION`, `LSP_REQUEST_DECLARATION`, `LSP_REQUEST_TYPE_DEFINITION`, or `LSP_REQUEST_IMPLEMENTATION`
LSPResponseDefinition definition;
LSPResponseWorkspaceSymbols workspace_symbols;
LSPResponseRename rename;
LSPResponseHighlight highlight;
LSPResponseReferences references;
LSPResponseDocumentLink document_link;
+ /// `LSP_REQUEST_FORMATTING` or `LSP_REQUEST_RANGE_FORMATTING`
+ LSPResponseFormatting formatting;
} data;
} LSPResponse;
@@ -576,6 +597,8 @@ typedef struct {
bool rename_support;
bool references_support;
bool document_link_support;
+ bool formatting_support;
+ bool range_formatting_support;
} LSPCapabilities;
typedef struct LSP {
@@ -596,7 +619,7 @@ typedef struct LSP {
Process *process;
// Socket for communicating with server. Maybe be NULL if communication is done over stdio.
//
- // thread-safety: TODO
+ // thread-safety: only accessed in communication thread
// at least one of `process` and `socket` must be non-null
Socket *socket;
// port used for communication
diff --git a/main.c b/main.c
index acbeaa2..a0b6e67 100644
--- a/main.c
+++ b/main.c
@@ -1,7 +1,7 @@
/*
TODO:
+- figure out what's wrong with format-selection with clangd
- figure out what's wrong with godot language server
-- LSP textDocument/formatting and textDocument/rangeFormatting
- automatically restart server
FUTURE FEATURES:
- autodetect indentation (tabs vs spaces)
@@ -77,6 +77,7 @@ FUTURE FEATURES:
#include "ide-highlights.c"
#include "ide-usages.c"
#include "ide-document-link.c"
+#include "ide-format.c"
#include "command.c"
#include "macro.c"
#include "config.c"
@@ -503,6 +504,7 @@ int main(int argc, char **argv) {
macros_init(ted);
definitions_init(ted);
autocomplete_init(ted);
+ format_init(ted);
signature_help_init(ted);
usages_init(ted);
highlights_init(ted);
@@ -900,6 +902,7 @@ int main(int argc, char **argv) {
// it's important that we send error responses here too.
// we don't want to be waiting around for a response that's never coming.
autocomplete_process_lsp_response(ted, r);
+ format_process_lsp_response(ted, r);
signature_help_process_lsp_response(ted, r);
hover_process_lsp_response(ted, r);
definitions_process_lsp_response(ted, lsp, r);
@@ -1198,6 +1201,7 @@ int main(int argc, char **argv) {
hover_quit(ted);
signature_help_quit(ted);
autocomplete_quit(ted);
+ format_quit(ted);
highlights_quit(ted);
usages_quit(ted);
session_write(ted);
diff --git a/ted-internal.h b/ted-internal.h
index a9ee175..6868895 100644
--- a/ted-internal.h
+++ b/ted-internal.h
@@ -208,6 +208,9 @@ typedef struct Autocomplete Autocomplete;
/// data needed for finding usages
typedef struct Usages Usages;
+/// data needed for formatting code
+typedef struct Formatting Formatting;
+
/// max number of signatures to display at a time.
#define SIGNATURE_HELP_MAX 5
@@ -340,6 +343,7 @@ struct Ted {
Highlights *highlights;
Usages *usages;
RenameSymbol *rename_symbol;
+ Formatting *formatting;
FILE *log;
@@ -450,6 +454,12 @@ LSPDocumentPosition buffer_pos_to_lsp_document_position(TextBuffer *buffer, Buff
BufferPos buffer_pos_from_lsp(TextBuffer *buffer, LSPPosition lsp_pos);
/// Get the cursor position as an LSPPosition.
LSPPosition buffer_cursor_pos_as_lsp_position(TextBuffer *buffer);
+/// Get the current selection as an LSPRange.
+///
+/// Returns `(LSPRange){0}` if nothing is selected.
+LSPRange buffer_selection_as_lsp_range(TextBuffer *buffer);
+/// Apply LSP TextEdit from response
+void buffer_apply_lsp_text_edit(TextBuffer *buffer, const LSPResponse *response, const LSPTextEdit *edit);
/// Get the cursor position as an LSPDocumentPosition.
LSPDocumentPosition buffer_cursor_pos_as_lsp_document_position(TextBuffer *buffer);
/// highlight an \ref LSPRange in this buffer.
@@ -617,6 +627,16 @@ void document_link_quit(Ted *ted);
void document_link_frame(Ted *ted);
void document_link_process_lsp_response(Ted *ted, const LSPResponse *response);
+
+// === ide-format.c ===
+/// initialize formatting stuff
+void format_init(Ted *ted);
+void format_process_lsp_response(Ted *ted, const LSPResponse *response);
+/// cancel last formatting request
+void format_cancel_request(Ted *ted);
+/// free formatting stuff
+void format_quit(Ted *ted);
+
// === ide-highlights.c ===
void highlights_init(Ted *ted);
void highlights_quit(Ted *ted);
diff --git a/ted.c b/ted.c
index a21c317..5167900 100644
--- a/ted.c
+++ b/ted.c
@@ -51,11 +51,11 @@ static void ted_vset_message(Ted *ted, MessageType type, const char *fmt, va_lis
}
}
-TextBuffer *ted_get_active_buffer(Ted *ted) {
+TextBuffer *ted_active_buffer(Ted *ted) {
return ted->active_buffer;
}
-TextBuffer *ted_get_active_buffer_behind_menu(Ted *ted) {
+TextBuffer *ted_active_buffer_behind_menu(Ted *ted) {
return ted->prev_active_buffer;
}
diff --git a/ted.cfg b/ted.cfg
index 6b9aaf7..dd36a1f 100644
--- a/ted.cfg
+++ b/ted.cfg
@@ -334,6 +334,13 @@ Ctrl+x = :cut
Ctrl+v = :paste
Ctrl+Shift+p = :command-selector
+# use cargo fmt or clang-tidy or whatever the LSP server
+# decides to use to format the current selection/file.
+# by default no keybinding is set so you don't accidentally
+# format a whole file.
+# Ctrl+Alt+f = :format-selection
+# Ctrl+Alt+Shift+f = :format-file
+
Ctrl+9 = :decrement-number
Ctrl+0 = :increment-number
diff --git a/ted.h b/ted.h
index b1c8950..984ab70 100644
--- a/ted.h
+++ b/ted.h
@@ -921,6 +921,12 @@ void definition_cancel_lookup(Ted *ted);
/// so don't keep it around long.
const char *document_link_at_buffer_pos(Ted *ted, BufferPos pos);
+// === ide-format.c ===
+/// format current selection (using LSP server)
+void format_selection(Ted *ted);
+/// format current file (using LSP server)
+void format_file(Ted *ted);
+
// === ide-highlights.c ===
// === ide-hover.c ===
@@ -1094,11 +1100,11 @@ bool tag_goto(Ted *ted, const char *tag);
/// for fatal errors
void die(PRINTF_FORMAT_STRING const char *fmt, ...) ATTRIBUTE_PRINTF(1, 2);
/// returns the current active buffer, or `NULL` if no buffer is active.
-TextBuffer *ted_get_active_buffer(Ted *ted);
+TextBuffer *ted_active_buffer(Ted *ted);
/// if a menu is open, returns the buffer that was open before the menu was opened.
///
/// returns `NULL` if no menu is open or no buffer was open before the menu was opened.
-TextBuffer *ted_get_active_buffer_behind_menu(Ted *ted);
+TextBuffer *ted_active_buffer_behind_menu(Ted *ted);
/// get width of ted window
float ted_window_width(Ted *ted);
/// get height of ted window