From 65b6850a7dc3211566993f8a2cfacf61f5b4d6a2 Mon Sep 17 00:00:00 2001
From: Leo Tenenbaum <pommicket@gmail.com>
Date: Tue, 26 Jan 2021 15:42:34 -0500
Subject: auto-indent

---
 buffer.c  | 25 +++++++++++++++++++++++++
 command.c | 32 +++++++++++++++++++-------------
 command.h | 11 +++++++----
 config.c  | 18 ++++++++++++++++++
 main.c    | 16 +---------------
 ted.cfg   |  6 +++++-
 ted.h     |  1 +
 7 files changed, 76 insertions(+), 33 deletions(-)

diff --git a/buffer.c b/buffer.c
index 41ad74c..5f6faec 100644
--- a/buffer.c
+++ b/buffer.c
@@ -1603,6 +1603,31 @@ void buffer_insert_utf8_at_cursor(TextBuffer *buffer, char const *utf8) {
 	}
 }
 
+// insert newline at cursor and auto-indent
+void buffer_newline(TextBuffer *buffer) {
+	Settings const *settings = buffer_settings(buffer);
+	BufferPos cursor_pos = buffer->cursor_pos;
+	String32 line = buffer_get_line(buffer, cursor_pos.line);
+	u32 whitespace_len;
+	for (whitespace_len = 0; whitespace_len < line.len; ++whitespace_len) {
+		if (line.str[whitespace_len] != ' ' && line.str[whitespace_len] != '\t')
+			break; // found end of indentation
+	}
+	if (settings->auto_indent) {
+		// newline + auto-indent
+		char32_t *text = buffer_calloc(buffer, whitespace_len + 1, sizeof *text); // @OPTIMIZE: don't allocate on heap if whitespace_len is small
+		if (text) {
+			text[0] = '\n';
+			memcpy(&text[1], line.str, whitespace_len * sizeof *text);
+			buffer_insert_text_at_cursor(buffer, str32(text, whitespace_len + 1));
+			free(text);
+		}
+	} else {
+		// just newline
+		buffer_insert_char_at_cursor(buffer, '\n');
+	}
+}
+
 void buffer_delete_chars_at_cursor(TextBuffer *buffer, i64 nchars) {
 	if (buffer->selection)
 		buffer_delete_selection(buffer);
diff --git a/command.c b/command.c
index a2c424d..0ec5059 100644
--- a/command.c
+++ b/command.c
@@ -97,6 +97,25 @@ void command_execute(Ted *ted, Command c, i64 argument) {
 		if (buffer) buffer_select_all(buffer);
 		break;
 
+	case CMD_TAB:
+		buffer_insert_char_at_cursor(buffer, '\t');
+		break;
+	case CMD_NEWLINE:
+		if (buffer->is_line_buffer) {
+			switch (ted->menu) {
+			case MENU_NONE:
+				assert(0);
+				break;
+			case MENU_OPEN:
+			case MENU_SAVE_AS: {
+				ted->file_selector.submitted = true;
+			} break;
+			}
+		} else {
+			buffer_newline(buffer);
+		}
+		break;
+
 	case CMD_BACKSPACE:
 		if (buffer) buffer_backspace_at_cursor(buffer, argument);
 		break;
@@ -172,19 +191,6 @@ void command_execute(Ted *ted, Command c, i64 argument) {
 			buffer_disable_selection(buffer);
 		}
 		break;
-	case CMD_SUBMIT_LINE_BUFFER:
-		if (buffer->is_line_buffer) {
-			switch (ted->menu) {
-			case MENU_NONE:
-				assert(0);
-				break;
-			case MENU_OPEN:
-			case MENU_SAVE_AS: {
-				ted->file_selector.submitted = true;
-			} break;
-			}
-		}
-		break;
 	}
 
 	if (buffer && buffer_haserr(buffer)) {
diff --git a/command.h b/command.h
index 79d8a1d..9793d2a 100644
--- a/command.h
+++ b/command.h
@@ -24,6 +24,10 @@ ENUM_U16 {
 	CMD_SELECT_END_OF_FILE,
 	CMD_SELECT_ALL, // select entire buffer
 
+	// insertion
+	CMD_TAB, // insert '\t'
+	CMD_NEWLINE, // insert '\n' + autoindent -- also used to submit line buffers
+
 	// scrolling
 	CMD_PAGE_UP, // move cursor up one page up (where one page is however tall the buffer is)
 	CMD_PAGE_DOWN,
@@ -48,8 +52,6 @@ ENUM_U16 {
 
 	CMD_ESCAPE, // by default this is the escape key. closes menus, etc.
 
-	CMD_SUBMIT_LINE_BUFFER, // submit "line buffer" value -- the line buffer is where you type file names when opening files, etc.
-
 	CMD_COUNT
 } ENUM_U16_END(Command);
 
@@ -83,6 +85,8 @@ static CommandName const command_names[CMD_COUNT] = {
 	{"select-all", CMD_SELECT_ALL},
 	{"page-up", CMD_PAGE_UP},
 	{"page-down", CMD_PAGE_DOWN},
+	{"tab", CMD_TAB},
+	{"newline", CMD_NEWLINE},
 	{"backspace", CMD_BACKSPACE},
 	{"delete", CMD_DELETE},
 	{"backspace-word", CMD_BACKSPACE_WORD},
@@ -97,7 +101,6 @@ static CommandName const command_names[CMD_COUNT] = {
 	{"paste", CMD_PASTE},
 	{"increase-text-size", CMD_TEXT_SIZE_INCREASE},
 	{"decrease-text-size", CMD_TEXT_SIZE_DECREASE},
-	{"escape", CMD_ESCAPE},
-	{"submit-line-buffer", CMD_SUBMIT_LINE_BUFFER}
+	{"escape", CMD_ESCAPE}
 };
 
diff --git a/config.c b/config.c
index 868122d..3baa943 100644
--- a/config.c
+++ b/config.c
@@ -257,6 +257,16 @@ void config_read(Ted *ted, char const *filename) {
 									bool const is_integer = *endptr == '\0';
 									double const floating = strtod(value, (char **)&endptr);
 									bool const is_floating = *endptr == '\0';
+									bool is_bool = false;
+									bool boolean = false;
+								#define BOOL_HELP "(should be yes/no/on/off)"
+									if (streq(value, "yes") || streq(value, "on")) {
+										is_bool = true;
+										boolean = true;
+									} else if (streq(value, "no") || streq(value, "off")) {
+										is_bool = true;
+										boolean = false;
+									}
 
 									if (streq(key, "tab-width")) {
 										if (is_integer && integer > 0 && integer < 100) {
@@ -324,9 +334,17 @@ void config_read(Ted *ted, char const *filename) {
 										} else {
 											config_err(cfg, "Invalid error display time: %s.", value);
 										}
+									} else if (streq(key, "auto-indent")) {
+										if (is_bool) {
+											settings->auto_indent = boolean;
+										} else {
+											config_err(cfg, "Invalid auto indent: %s " BOOL_HELP ".", value);
+										}
 									} else {
 										config_err(cfg, "Unrecognized core setting: %s.", key);
 									}
+
+								#undef BOOL_HELP
 								} break;
 								}
 							}
diff --git a/main.c b/main.c
index 747c95f..81490b6 100644
--- a/main.c
+++ b/main.c
@@ -1,9 +1,8 @@
 // @TODO:
-// - save as
 // - warn on:
 //  - overwrite (from save as menu)
 //  - unsaved changes
-// - auto-indent
+// - tabs, split
 // - Windows installation
 #include "base.h"
 no_warn_start
@@ -362,22 +361,9 @@ int main(int argc, char **argv) {
 					(u32)((modifier & (KMOD_LALT|KMOD_RALT)) != 0) << KEY_MODIFIER_ALT_BIT;
 				if (key_combo < KEY_COMBO_COUNT) {
 					KeyAction *action = &ted->key_actions[key_combo];
-					bool was_in_line_buffer = buffer && buffer->is_line_buffer;
 					if (action->command) {
 						command_execute(ted, action->command, action->argument);
 					}
-
-					if (buffer) {
-						switch (key_combo) {
-						case SDL_SCANCODE_RETURN << 3:
-							if (!was_in_line_buffer) // make sure return to submit line buffer doesn't get added to newly-active buffer
-								buffer_insert_char_at_cursor(buffer, '\n');
-							break;
-						case SDL_SCANCODE_TAB << 3:
-							buffer_insert_char_at_cursor(buffer, '\t');
-							break;
-						}
-					}
 				}
 			} break;
 			case SDL_TEXTINPUT: {
diff --git a/ted.cfg b/ted.cfg
index 1705862..0d9089e 100644
--- a/ted.cfg
+++ b/ted.cfg
@@ -16,6 +16,7 @@ border-thickness = 1
 max-menu-width = 600
 padding = 6
 error-display-time = 10
+auto-indent = on
 
 [keyboard]
 # motion and selection
@@ -45,6 +46,10 @@ Ctrl+End = :end-of-file
 Ctrl+Shift+End = :select-end-of-file
 Ctrl+a = :select-all
 
+# insertion
+Tab = :tab
+Return = :newline
+
 # deletion
 Delete = :delete
 Ctrl+Delete = :delete-word
@@ -70,7 +75,6 @@ Ctrl++ = 3 :increase-text-size
 Ctrl+- = 3 :decrease-text-size
 
 Escape = :escape
-Return = :submit-line-buffer
 
 [colors]
 border = #a77
diff --git a/ted.h b/ted.h
index e1f6498..18892ec 100644
--- a/ted.h
+++ b/ted.h
@@ -9,6 +9,7 @@ typedef struct {
 	u16 text_size;
 	u16 max_menu_width;
 	u16 error_display_time;
+	bool auto_indent;
 	u8 tab_width;
 	u8 cursor_width;
 	u8 undo_save_time;
-- 
cgit v1.2.3