Browse Source

Merge pull request #7 from tsoding/load-cli

Load from CLI
Alexey Kutepov 4 years ago
parent
commit
7796355fec
5 changed files with 371 additions and 36 deletions
  1. 1 1
      README.md
  2. 61 27
      editor.c
  3. 5 0
      editor.h
  4. 36 8
      main.c
  5. 268 0
      sv.h

+ 1 - 1
README.md

@@ -4,5 +4,5 @@
 
 ```console
 $ make
-$ ./te
+$ ./te main.c
 ```

+ 61 - 27
editor.c

@@ -4,10 +4,13 @@
 #include <errno.h>
 #include <string.h>
 #include "./editor.h"
+#include "./sv.h"
 
 #define LINE_INIT_CAPACITY 1024
 #define EDITOR_INIT_CAPACITY 128
 
+static void editor_create_first_new_line(Editor *editor);
+
 static void line_grow(Line *line, size_t n)
 {
     size_t new_capacity = line->capacity;
@@ -27,13 +30,23 @@ static void line_grow(Line *line, size_t n)
     }
 }
 
-void line_insert_text_before(Line *line, const char *text, size_t *col)
+void line_append_text(Line *line, const char *text)
+{
+    line_append_text_sized(line, text, strlen(text));
+}
+
+void line_append_text_sized(Line *line, const char *text, size_t text_size)
+{
+    size_t col = line->size;
+    line_insert_text_sized_before(line, text, text_size, &col);
+}
+
+void line_insert_text_sized_before(Line *line, const char *text, size_t text_size, size_t *col)
 {
     if (*col > line->size) {
         *col = line->size;
     }
 
-    const size_t text_size = strlen(text);
     line_grow(line, text_size);
 
     memmove(line->chars + *col + text_size,
@@ -44,6 +57,11 @@ void line_insert_text_before(Line *line, const char *text, size_t *col)
     *col += text_size;
 }
 
+void line_insert_text_before(Line *line, const char *text, size_t *col)
+{
+    line_insert_text_sized_before(line, text, strlen(text), col);
+}
+
 void line_backspace(Line *line, size_t *col)
 {
     if (*col > line->size) {
@@ -110,49 +128,34 @@ void editor_insert_new_line(Editor *editor)
     editor->size += 1;
 }
 
-void editor_push_new_line(Editor *editor)
-{
-    editor_grow(editor, 1);
-    memset(&editor->lines[editor->size], 0, sizeof(editor->lines[0]));
-    editor->size += 1;
-}
-
-void editor_insert_text_before_cursor(Editor *editor, const char *text)
+static void editor_create_first_new_line(Editor *editor)
 {
     if (editor->cursor_row >= editor->size) {
         if (editor->size > 0) {
             editor->cursor_row = editor->size - 1;
         } else {
-            editor_push_new_line(editor);
+            editor_grow(editor, 1);
+            memset(&editor->lines[editor->size], 0, sizeof(editor->lines[0]));
+            editor->size += 1;
         }
     }
+}
 
+void editor_insert_text_before_cursor(Editor *editor, const char *text)
+{
+    editor_create_first_new_line(editor);
     line_insert_text_before(&editor->lines[editor->cursor_row], text, &editor->cursor_col);
 }
 
 void editor_backspace(Editor *editor)
 {
-    if (editor->cursor_row >= editor->size) {
-        if (editor->size > 0) {
-            editor->cursor_row = editor->size - 1;
-        } else {
-            editor_push_new_line(editor);
-        }
-    }
-
+    editor_create_first_new_line(editor);
     line_backspace(&editor->lines[editor->cursor_row], &editor->cursor_col);
 }
 
 void editor_delete(Editor *editor)
 {
-    if (editor->cursor_row >= editor->size) {
-        if (editor->size > 0) {
-            editor->cursor_row = editor->size - 1;
-        } else {
-            editor_push_new_line(editor);
-        }
-    }
-
+    editor_create_first_new_line(editor);
     line_delete(&editor->lines[editor->cursor_row], &editor->cursor_col);
 }
 
@@ -182,3 +185,34 @@ void editor_save_to_file(const Editor *editor, const char *file_path)
 
     fclose(f);
 }
+
+void editor_load_from_file(Editor *editor, FILE *file)
+{
+    assert(editor->lines == NULL && "You can only load files into an empty editor");
+    editor_create_first_new_line(editor);
+
+    static char chunk[640 * 1024];
+
+    while (!feof(file)) {
+        size_t n = fread(chunk, 1, sizeof(chunk), file);
+
+        String_View chunk_sv = {
+            .data = chunk,
+            .count = n
+        };
+
+        while (chunk_sv.count > 0) {
+            String_View chunk_line = {0};
+            Line *line = &editor->lines[editor->size - 1];
+            if (sv_try_chop_by_delim(&chunk_sv, '\n', &chunk_line)) {
+                line_append_text_sized(line, chunk_line.data, chunk_line.count);
+                editor_insert_new_line(editor);
+            } else {
+                line_append_text_sized(line, chunk_sv.data, chunk_sv.count);
+                chunk_sv = SV_NULL;
+            }
+        }
+    }
+
+    editor->cursor_row = 0;
+}

+ 5 - 0
editor.h

@@ -9,7 +9,10 @@ typedef struct {
     char *chars;
 } Line;
 
+void line_append_text(Line *line, const char *text);
+void line_append_text_sized(Line *line, const char *text, size_t text_size);
 void line_insert_text_before(Line *line, const char *text, size_t *col);
+void line_insert_text_sized_before(Line *line, const char *text, size_t text_size, size_t *col);
 void line_backspace(Line *line, size_t *col);
 void line_delete(Line *line, size_t *col);
 
@@ -22,6 +25,8 @@ typedef struct {
 } Editor;
 
 void editor_save_to_file(const Editor *editor, const char *file_path);
+void editor_load_from_file(Editor *editor, FILE *file);
+
 void editor_insert_text_before_cursor(Editor *editor, const char *text);
 void editor_insert_new_line(Editor *editor);
 void editor_backspace(Editor *editor);

+ 36 - 8
main.c

@@ -169,7 +169,14 @@ void render_cursor(SDL_Renderer *renderer, const Font *font)
     }
 }
 
-// TODO: Save/Load file
+void usage(FILE *stream)
+{
+    fprintf(stream, "Usage: te [FILE-PATH]\n");
+}
+
+// TODO: Save file
+// TODO: ncurses renderer
+// TODO: scrolling
 // TODO: Jump forward/backward by a word
 // TODO: Delete a word
 // TODO: Blinking cursor
@@ -178,8 +185,19 @@ void render_cursor(SDL_Renderer *renderer, const Font *font)
 
 int main(int argc, char **argv)
 {
-    (void) argc;
-    (void) argv;
+    const char *file_path = NULL;
+
+    if (argc > 1) {
+        file_path = argv[1];
+    }
+
+    if (file_path) {
+        FILE *file = fopen(file_path, "r");
+        if (file != NULL) {
+            editor_load_from_file(&editor, file);
+            fclose(file);
+        }
+    }
 
     scc(SDL_Init(SDL_INIT_VIDEO));
 
@@ -211,12 +229,16 @@ int main(int argc, char **argv)
                 break;
 
                 case SDLK_F2: {
-                    editor_save_to_file(&editor, "output");
-                } break;
+                    if (file_path) {
+                        editor_save_to_file(&editor, file_path);
+                    }
+                }
+                break;
 
                 case SDLK_RETURN: {
                     editor_insert_new_line(&editor);
-                } break;
+                }
+                break;
 
                 case SDLK_DELETE: {
                     editor_delete(&editor);
@@ -227,11 +249,13 @@ int main(int argc, char **argv)
                     if (editor.cursor_row > 0) {
                         editor.cursor_row -= 1;
                     }
-                } break;
+                }
+                break;
 
                 case SDLK_DOWN: {
                     editor.cursor_row += 1;
-                } break;
+                }
+                break;
 
                 case SDLK_LEFT: {
                     if (editor.cursor_col > 0) {
@@ -271,3 +295,7 @@ int main(int argc, char **argv)
 
     return 0;
 }
+
+#define SV_IMPLEMENTATION
+#include "./sv.h"
+

+ 268 - 0
sv.h

@@ -0,0 +1,268 @@
+// Copyright 2021 Alexey Kutepov <[email protected]>
+
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#ifndef SV_H_
+#define SV_H_
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <ctype.h>
+
+typedef struct {
+    size_t count;
+    const char *data;
+} String_View;
+
+#define SV(cstr_lit) \
+    ((String_View) { \
+        .count = sizeof(cstr_lit) - 1, \
+        .data = (cstr_lit) \
+    })
+
+#define SV_STATIC(cstr_lit) \
+    { \
+        .count = sizeof(cstr_lit) - 1, \
+        .data = (cstr_lit) \
+    }
+
+#define SV_NULL (String_View) {0}
+
+// printf macros for String_View
+#define SV_Fmt "%.*s"
+#define SV_Arg(sv) (int) (sv).count, (sv).data
+// USAGE:
+//   String_View name = ...;
+//   printf("Name: "SV_Fmt"\n", SV_Arg(name));
+
+String_View sv_from_cstr(const char *cstr);
+String_View sv_trim_left(String_View sv);
+String_View sv_trim_right(String_View sv);
+String_View sv_trim(String_View sv);
+String_View sv_chop_by_delim(String_View *sv, char delim);
+bool sv_try_chop_by_delim(String_View *sv, char delim, String_View *chunk);
+String_View sv_chop_left(String_View *sv, size_t n);
+String_View sv_chop_right(String_View *sv, size_t n);
+String_View sv_chop_left_while(String_View *sv, bool (*predicate)(char x));
+bool sv_index_of(String_View sv, char c, size_t *index);
+bool sv_eq(String_View a, String_View b);
+bool sv_starts_with(String_View sv, String_View prefix);
+bool sv_ends_with(String_View sv, String_View suffix);
+uint64_t sv_to_u64(String_View sv);
+
+#endif  // SV_H_
+
+#ifdef SV_IMPLEMENTATION
+String_View sv_from_cstr(const char *cstr)
+{
+    return (String_View) {
+        .count = strlen(cstr),
+        .data = cstr,
+    };
+}
+
+String_View sv_trim_left(String_View sv)
+{
+    size_t i = 0;
+    while (i < sv.count && isspace(sv.data[i])) {
+        i += 1;
+    }
+
+    return (String_View) {
+        .count = sv.count - i,
+        .data = sv.data + i,
+    };
+}
+
+String_View sv_trim_right(String_View sv)
+{
+    size_t i = 0;
+    while (i < sv.count && isspace(sv.data[sv.count - 1 - i])) {
+        i += 1;
+    }
+
+    return (String_View) {
+        .count = sv.count - i,
+        .data = sv.data
+    };
+}
+
+String_View sv_trim(String_View sv)
+{
+    return sv_trim_right(sv_trim_left(sv));
+}
+
+String_View sv_chop_left(String_View *sv, size_t n)
+{
+    if (n > sv->count) {
+        n = sv->count;
+    }
+
+    String_View result = {
+        .data = sv->data,
+        .count = n,
+    };
+
+    sv->data  += n;
+    sv->count -= n;
+
+    return result;
+}
+
+String_View sv_chop_right(String_View *sv, size_t n)
+{
+    if (n > sv->count) {
+        n = sv->count;
+    }
+
+    String_View result = {
+        .data = sv->data + sv->count - n,
+        .count = n
+    };
+
+    sv->count -= n;
+
+    return result;
+}
+
+bool sv_index_of(String_View sv, char c, size_t *index)
+{
+    size_t i = 0;
+    while (i < sv.count && sv.data[i] != c) {
+        i += 1;
+    }
+
+    if (i < sv.count) {
+        if (index) {
+            *index = i;
+        }
+        return true;
+    } else {
+        return false;
+    }
+}
+
+bool sv_try_chop_by_delim(String_View *sv, char delim, String_View *chunk)
+{
+    size_t i = 0;
+    while (i < sv->count && sv->data[i] != delim) {
+        i += 1;
+    }
+
+    String_View result = {
+        .count = i,
+        .data = sv->data,
+    };
+
+    if (i < sv->count) {
+        sv->count -= i + 1;
+        sv->data  += i + 1;
+        if (chunk) {
+            *chunk = result;
+        }
+        return true;
+    }
+
+    return false;
+}
+
+String_View sv_chop_by_delim(String_View *sv, char delim)
+{
+    size_t i = 0;
+    while (i < sv->count && sv->data[i] != delim) {
+        i += 1;
+    }
+
+    String_View result = {
+        .count = i,
+        .data = sv->data,
+    };
+
+    if (i < sv->count) {
+        sv->count -= i + 1;
+        sv->data  += i + 1;
+    } else {
+        sv->count -= i;
+        sv->data  += i;
+    }
+
+    return result;
+}
+
+bool sv_starts_with(String_View sv, String_View expected_prefix)
+{
+    if (expected_prefix.count <= sv.count) {
+        String_View actual_prefix = {
+            .data = sv.data,
+            .count = expected_prefix.count,
+        };
+
+        return sv_eq(expected_prefix, actual_prefix);
+    }
+
+    return false;
+}
+
+bool sv_ends_with(String_View sv, String_View expected_suffix)
+{
+    if (expected_suffix.count <= sv.count) {
+        String_View actual_suffix = {
+            .data = sv.data + sv.count - expected_suffix.count,
+            .count = expected_suffix.count
+        };
+
+        return sv_eq(expected_suffix, actual_suffix);
+    }
+
+    return false;
+}
+
+bool sv_eq(String_View a, String_View b)
+{
+    if (a.count != b.count) {
+        return false;
+    } else {
+        return memcmp(a.data, b.data, a.count) == 0;
+    }
+}
+
+uint64_t sv_to_u64(String_View sv)
+{
+    uint64_t result = 0;
+
+    for (size_t i = 0; i < sv.count && isdigit(sv.data[i]); ++i) {
+        result = result * 10 + (uint64_t) sv.data[i] - '0';
+    }
+
+    return result;
+}
+
+String_View sv_chop_left_while(String_View *sv, bool (*predicate)(char x))
+{
+    size_t i = 0;
+    while (i < sv->count && predicate(sv->data[i])) {
+        i += 1;
+    }
+    return sv_chop_left(sv, i);
+}
+
+#endif // SV_IMPLEMENTATION