summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Tenenbaum <pommicket@gmail.com>2021-04-16 15:54:35 -0400
committerLeo Tenenbaum <pommicket@gmail.com>2021-04-16 15:54:35 -0400
commit51ac447d409bb565178ab9d78d4b5200e89f2cf4 (patch)
tree0a45c9ba2215d00dd04afe75803ad103bc4236fb
parent2a3bbb6674fe3b0e4ff996cae750a6e2880315ef (diff)
comment/uncomment selection
-rw-r--r--buffer.c108
-rw-r--r--command.c3
-rw-r--r--command.h2
-rw-r--r--main.c1
-rw-r--r--syntax.c24
-rw-r--r--ted.cfg6
6 files changed, 141 insertions, 3 deletions
diff --git a/buffer.c b/buffer.c
index bdf0c01..7e59e06 100644
--- a/buffer.c
+++ b/buffer.c
@@ -137,6 +137,12 @@ static void buffer_validate_cursor(TextBuffer *buffer) {
buffer_pos_validate(buffer, &buffer->selection_pos);
}
+// ensure *line points to a line in buffer.
+static inline void buffer_validate_line(TextBuffer *buffer, u32 *line) {
+ if (*line >= buffer->nlines)
+ *line = buffer->nlines - 1;
+}
+
// update *pos, given that nchars characters were deleted at del_pos.
static void buffer_pos_handle_deleted_chars(BufferPos *pos, BufferPos del_pos, u32 nchars) {
if (pos->line != del_pos.line) return;
@@ -2555,6 +2561,9 @@ void buffer_indent_lines(TextBuffer *buffer, u32 first_line, u32 last_line) {
void buffer_dedent_lines(TextBuffer *buffer, u32 first_line, u32 last_line) {
assert(first_line <= last_line);
+ buffer_validate_line(buffer, &first_line);
+ buffer_validate_line(buffer, &last_line);
+
buffer_start_edit_chain(buffer);
Settings const *settings = buffer_settings(buffer);
u8 const tab_width = settings->tab_width;
@@ -2583,6 +2592,7 @@ void buffer_dedent_lines(TextBuffer *buffer, u32 first_line, u32 last_line) {
buffer_end_edit_chain(buffer);
}
+
void buffer_indent_selection(TextBuffer *buffer) {
if (!buffer->selection) return;
u32 l1 = buffer->cursor_pos.line;
@@ -2608,3 +2618,101 @@ void buffer_dedent_cursor_line(TextBuffer *buffer) {
buffer_dedent_lines(buffer, line, line);
}
+
+void buffer_comment_lines(TextBuffer *buffer, u32 first_line, u32 last_line) {
+ Language lang = buffer_language(buffer);
+ const char *start = language_comment_start(lang), *end = language_comment_end(lang);
+ String32 start32 = str32_from_utf8(start), end32 = str32_from_utf8(end);
+
+ buffer_start_edit_chain(buffer);
+
+ for (u32 line_idx = first_line; line_idx <= last_line; ++line_idx) {
+ // insert comment start
+ if (start32.len) {
+ BufferPos sol = buffer_pos_start_of_line(buffer, line_idx);
+ buffer_insert_text_at_pos(buffer, sol, start32);
+ }
+ // insert comment end
+ if (end32.len) {
+ BufferPos eol = buffer_pos_end_of_line(buffer, line_idx);
+ buffer_insert_text_at_pos(buffer, eol, end32);
+ }
+ }
+
+ str32_free(&start32);
+ str32_free(&end32);
+
+ buffer_end_edit_chain(buffer);
+}
+
+static bool buffer_line_starts_with_ascii(TextBuffer *buffer, u32 line_idx, char const *prefix) {
+ buffer_validate_line(buffer, &line_idx);
+ Line *line = &buffer->lines[line_idx];
+ size_t prefix_len = strlen(prefix);
+ if (line->len < prefix_len)
+ return false;
+ for (size_t i = 0; i < prefix_len; ++i) {
+ assert(prefix[i] > 0 && prefix[i] <= 127); // check if actually ASCII
+ if ((char32_t)prefix[i] != line->str[i])
+ return false;
+ }
+ return true;
+}
+static bool buffer_line_ends_with_ascii(TextBuffer *buffer, u32 line_idx, char const *suffix) {
+ buffer_validate_line(buffer, &line_idx);
+ Line *line = &buffer->lines[line_idx];
+ size_t suffix_len = strlen(suffix), line_len = line->len;
+ if (line_len < suffix_len)
+ return false;
+ for (size_t i = 0; i < suffix_len; ++i) {
+ assert(suffix[i] > 0 && suffix[i] <= 127); // check if actually ASCII
+ if ((char32_t)suffix[i] != line->str[line_len-suffix_len+i])
+ return false;
+ }
+ return true;
+}
+
+void buffer_uncomment_lines(TextBuffer *buffer, u32 first_line, u32 last_line) {
+ Language lang = buffer_language(buffer);
+ const char *start = language_comment_start(lang), *end = language_comment_end(lang);
+ u32 start_len = (u32)strlen(start), end_len = (u32)strlen(end);
+ buffer_start_edit_chain(buffer);
+ for (u32 line_idx = first_line; line_idx <= last_line; ++line_idx) {
+ // make sure line is actually commented
+ if (buffer_line_starts_with_ascii(buffer, line_idx, start)
+ && buffer_line_ends_with_ascii(buffer, line_idx, end)) {
+ // we should do the end first, because start and end might be overlapping,
+ // and it would cause an underflow if we deleted the start first.
+ BufferPos end_pos = buffer_pos_end_of_line(buffer, line_idx);
+ end_pos.index -= end_len;
+ buffer_delete_chars_at_pos(buffer, end_pos, end_len);
+
+ BufferPos start_pos = buffer_pos_start_of_line(buffer, line_idx);
+ buffer_delete_chars_at_pos(buffer, start_pos, start_len);
+ }
+ }
+ buffer_end_edit_chain(buffer);
+}
+
+void buffer_toggle_comment_lines(TextBuffer *buffer, u32 first_line, u32 last_line) {
+ Language lang = buffer_language(buffer);
+ const char *start = language_comment_start(lang), *end = language_comment_end(lang);
+ // if first line is a comment, uncomment lines, otherwise, comment lines
+ if (buffer_line_starts_with_ascii(buffer, first_line, start)
+ && buffer_line_ends_with_ascii(buffer, first_line, end))
+ buffer_uncomment_lines(buffer, first_line, last_line);
+ else
+ buffer_comment_lines(buffer, first_line, last_line);
+}
+
+void buffer_toggle_comment_selection(TextBuffer *buffer) {
+ u32 l1, l2;
+ if (buffer->selection) {
+ l1 = buffer->cursor_pos.line;
+ l2 = buffer->selection_pos.line;
+ sort2_u32(&l1, &l2); // ensure l1 <= l2
+ } else {
+ l1 = l2 = buffer->cursor_pos.line;
+ }
+ buffer_toggle_comment_lines(buffer, l1, l2);
+}
diff --git a/command.c b/command.c
index e1c2ee3..c28bce5 100644
--- a/command.c
+++ b/command.c
@@ -147,6 +147,9 @@ void command_execute(Ted *ted, Command c, i64 argument) {
buffer_newline(buffer);
}
break;
+ case CMD_COMMENT_SELECTION:
+ if (buffer) buffer_toggle_comment_selection(buffer);
+ break;
case CMD_BACKSPACE:
if (buffer) buffer_backspace_at_cursor(buffer, argument);
diff --git a/command.h b/command.h
index 80bd78b..b2da2c5 100644
--- a/command.h
+++ b/command.h
@@ -32,6 +32,7 @@ ENUM_U16 {
CMD_BACKTAB,
CMD_NEWLINE, // insert '\n' + autoindent -- also used to submit line buffers
CMD_NEWLINE_BACK,
+ CMD_COMMENT_SELECTION,
// scrolling
CMD_PAGE_UP, // move cursor up one page up (where one page is however tall the buffer is)
@@ -130,6 +131,7 @@ static CommandName const command_names[] = {
{"backtab", CMD_BACKTAB},
{"newline", CMD_NEWLINE},
{"newline-back", CMD_NEWLINE_BACK},
+ {"comment-selection", CMD_COMMENT_SELECTION},
{"backspace", CMD_BACKSPACE},
{"delete", CMD_DELETE},
{"backspace-word", CMD_BACKSPACE_WORD},
diff --git a/main.c b/main.c
index 668065a..ff118cc 100644
--- a/main.c
+++ b/main.c
@@ -1,6 +1,5 @@
// @TODO:
// - terminate process not working on windows?
-// - comment/uncomment selection
#include "base.h"
no_warn_start
#if _WIN32
diff --git a/syntax.c b/syntax.c
index 26ce234..b8efa93 100644
--- a/syntax.c
+++ b/syntax.c
@@ -13,6 +13,30 @@ Language language_from_str(char const *str) {
return LANG_NONE;
}
+// start of single line comment for language l
+char const *language_comment_start(Language l) {
+ switch (l) {
+ case LANG_C: return "/* ";
+ case LANG_RUST:
+ case LANG_CPP: return "// ";
+ case LANG_PYTHON: return "# ";
+ case LANG_NONE:
+ case LANG_COUNT:
+ break;
+ }
+ return "";
+}
+
+// end of single line comment for language l
+char const *language_comment_end(Language l) {
+ switch (l) {
+ case LANG_C:
+ return " */";
+ default:
+ return "";
+ }
+}
+
// NOTE: returns the color setting, not the color
ColorSetting syntax_char_type_to_color(SyntaxCharType t) {
switch (t) {
diff --git a/ted.cfg b/ted.cfg
index 96bc03b..8d6b251 100644
--- a/ted.cfg
+++ b/ted.cfg
@@ -69,6 +69,8 @@ Enter = :newline
Shift+Enter = :newline-back
Keypad Enter = :newline
Shift+Keypad Enter = :newline-back
+# toggle selection between commented/uncommented
+Ctrl+/ = :comment-selection
# deletion
Delete = :delete
@@ -137,8 +139,8 @@ Ctrl+t = :generate-tags
Ctrl+d = :goto-definition
Ctrl+g = :goto-line
-Ctrl+/ = :split-horizontal
-Ctrl+Shift+/ = :split-vertical
+Ctrl+\ = :split-horizontal
+Ctrl+Shift+\ = :split-vertical
# unsplit
Ctrl+j = :split-join
# switch to the other side of a split