]> gitweb.ps.run Git - iftint/blobdiff - main2.c
object editing, press space to edit existing primitive node
[iftint] / main2.c
diff --git a/main2.c b/main2.c
index d58a5bcfdbfb65ae0d6ca75e7d2c93d971eece1c..3dcbf8448b9ac57a6760cf51b6fc67875e946c74 100644 (file)
--- a/main2.c
+++ b/main2.c
-#include <stdio.h>\r
-\r
-#ifdef _WIN32\r
-#include <conio.h>\r
-#else\r
-#include <termios.h>\r
-#include <unistd.h>\r
-#include <stdio.h>\r
-\r
-/* reads from keypress, doesn't echo */\r
-int getch(void)\r
-{\r
-    struct termios oldattr, newattr;\r
-    int ch;\r
-    tcgetattr( STDIN_FILENO, &oldattr );\r
-    newattr = oldattr;\r
-    newattr.c_lflag &= ~( ICANON | ECHO );\r
-    tcsetattr( STDIN_FILENO, TCSANOW, &newattr );\r
-    ch = getchar();\r
-    tcsetattr( STDIN_FILENO, TCSANOW, &oldattr );\r
-    return ch;\r
-}\r
-\r
-/* reads from keypress, echoes */\r
-int getche(void)\r
-{\r
-    struct termios oldattr, newattr;\r
-    int ch;\r
-    tcgetattr( STDIN_FILENO, &oldattr );\r
-    newattr = oldattr;\r
-    newattr.c_lflag &= ~( ICANON );\r
-    tcsetattr( STDIN_FILENO, TCSANOW, &newattr );\r
-    ch = getchar();\r
-    tcsetattr( STDIN_FILENO, TCSANOW, &oldattr );\r
-    return ch;\r
-}\r
-#endif\r
-\r
-#define ASCII_ESC 27\r
-\r
-int main() {\r
-    int c;\r
-    printf("Hallo\n");\r
-    c = getch();\r
-    printf("%c[2J", ASCII_ESC);\r
-    printf("%c[H", ASCII_ESC);\r
-    printf("c: %c\n", c);\r
-\r
-    c = getch();\r
-\r
-    return 0;\r
-}
\ No newline at end of file
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+/* TODO
+- whitelist input on GetStr/GetInt
+*/
+
+// Memory
+
+#define NEW(TYPE) ((TYPE *)calloc(1, sizeof(TYPE)))
+#define NEWARR(TYPE, NUM) ((TYPE *)calloc(NUM, sizeof(TYPE)))
+
+
+// Util
+
+bool
+charInString(char c, const char * str) {
+    for (int i = 0; i < strlen(str); i++)
+        if (c == str[i])
+            return true;
+    return false;
+}
+
+bool
+isNewline(char c) {
+    return c == '\n' || c == '\r';
+}
+bool
+isBackspace(char c) {
+    return c == 8 || c == 127;
+}
+
+
+// Key defines
+
+#define KEY_CTRL_C 3
+#define KEY_BACKSPACE1 8
+#define KEY_BACKSPACE2 127
+
+
+// getch
+
+#ifdef _WIN32
+#include <windows.h>
+#include <conio.h>
+#else
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <unistd.h>
+#include <stdio.h>
+
+/* reads from keypress, doesn't echo */
+int getch(void)
+{
+    struct termios oldattr, newattr;
+    int ch;
+    tcgetattr( STDIN_FILENO, &oldattr );
+    newattr = oldattr;
+    newattr.c_lflag &= ~( ICANON | ECHO ); // no ECHO for echo(?)
+    tcsetattr( STDIN_FILENO, TCSANOW, &newattr );
+    ch = getchar();
+    tcsetattr( STDIN_FILENO, TCSANOW, &oldattr );
+    return ch;
+}
+
+/* ungets keypress */
+void ungetch(int ch)
+{
+    struct termios oldattr, newattr;
+    tcgetattr( STDIN_FILENO, &oldattr );
+    newattr = oldattr;
+    newattr.c_lflag &= ~( ICANON | ECHO );
+    tcsetattr( STDIN_FILENO, TCSANOW, &newattr );
+    ungetc(ch, stdin);
+    tcsetattr( STDIN_FILENO, TCSANOW, &oldattr );
+}
+#endif
+
+int
+peekch() {
+    int c = getch();
+    //ungetc(c, stdin);
+    ungetch(c);
+    return c;
+}
+
+
+// VT100
+
+#define ASCII_ESC 27
+
+void vt100Escape(const char * str, ...) {
+    va_list args;
+    va_start(args, str);
+
+    printf("%c", ASCII_ESC);
+    vprintf(str, args);
+}
+
+void vt100ClearScreen() { vt100Escape("[2J"); }
+void vt100CursorHome() { vt100Escape("[H"); }
+void vt100CursorPos(int v, int h) { vt100Escape("[%d;%dH", v, h); }
+void vt100SaveCursor() { vt100Escape("7"); }
+void vt100RestoreCursor() { vt100Escape("8"); }
+// void vt100GetCursor(int * v, int * h) {
+//     *v = *h = 0;
+//     printf("\033[6n");
+//     getch(); getch();
+//     int c;
+//     while ((c = getch()) != ';')
+//         *v = (10*(*v)+(c-'0'));
+//     while ((c = getch()) != 'R')
+//         *h = (10*(*h)+(c-'0'));
+// }
+void vt100GetScreenSize(int * v, int * h) {
+#ifdef _WIN32
+    CONSOLE_SCREEN_BUFFER_INFO csbi;
+    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
+    *h = csbi.srWindow.Right - csbi.srWindow.Left + 1;
+    *v = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
+#else
+    struct winsize w;
+    ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
+    *h = w.ws_row;
+    *v = w.ws_col;
+#endif
+}
+void vt100EnableAlternateBuffer() { vt100Escape("[?1049h"); }
+void vt100DisableAlternateBuffer() { vt100Escape("[?1049l"); }
+
+
+// JSON
+
+typedef enum {
+    JSONNodeKind_Nul,
+    JSONNodeKind_Int,
+    JSONNodeKind_Str,
+    JSONNodeKind_Obj,
+    JSONNodeKind_Arr,
+    JSONNodeKind_COUNT
+} JSONNodeKind;
+
+struct JSONNode;
+typedef struct JSONNode {
+    JSONNodeKind kind;
+    size_t data;
+    struct JSONNode * parent;
+    struct JSONNode * firstChild;
+    int childCount;
+    struct JSONNode * prev;
+    struct JSONNode * next;
+} JSONNode;
+
+JSONNode *
+JSONNodeNew(JSONNodeKind kind) {
+    JSONNode * result = NEW(JSONNode);
+    result->kind = kind;
+    return result;
+}
+
+JSONNode *
+JSONNodePush(JSONNode * this, JSONNode * that) {
+    if (this->firstChild == NULL) {
+        this->firstChild = that;
+    }
+    else {
+        JSONNode * lastNode = this->firstChild;
+        while (lastNode->next != NULL)
+            lastNode = lastNode->next;
+        lastNode->next = that;
+        that->prev = lastNode;
+    }
+    this->childCount++;
+    that->parent = this;
+    
+    return that;
+}
+
+JSONNode *
+JSONNodeRemove(JSONNode * node) {
+    if (node->prev == NULL) { // first child
+        node->parent->firstChild = node->next;
+        if (node->next != NULL)
+            node->next->prev = NULL;
+        return node->parent;
+    }
+    else { // second child
+        node->prev->next = node->next;
+        if (node->next != NULL)
+            node->next->prev = node->prev;
+        return node->prev;
+    }
+}
+
+bool
+JSONNodeEditable(JSONNode * node) {
+    if (node == NULL) return false;
+    else if (node->kind == JSONNodeKind_Int) return true;
+    else if (node->kind == JSONNodeKind_Str) return true;
+    return false;
+}
+
+void
+Indent(int indent) {
+    for (int i = 0; i < indent; i++)
+        printf("  ");
+}
+
+void
+JSONNodePrint(JSONNode * node, JSONNode * currNode) {
+    if (node == NULL)
+        return;
+    
+    static int indent;
+    if (node->parent == NULL)
+        indent = 0;
+
+    if (currNode == node) {
+        vt100SaveCursor();
+    }
+
+    switch (node->kind) {
+    case JSONNodeKind_Nul: {
+        printf("null");
+        break;
+    }
+    case JSONNodeKind_Int: {
+        char * str = (char *)node->data;
+        printf("%s", (str == NULL || strlen(str) == 0) ? "0" : str);
+        break;
+    }
+    case JSONNodeKind_Str: {
+        char * str = (char *)node->data;
+        printf("\"%s\"", str == NULL ? "" : str);
+        break;
+    }
+    case JSONNodeKind_Obj: {
+        printf("{\n");
+        JSONNode * ptr = node->firstChild;
+        indent++;
+        while (ptr != NULL) {
+            Indent(indent);
+            JSONNodePrint(ptr, currNode);
+            printf(": ");
+            JSONNodePrint(ptr->firstChild, currNode);
+            if (ptr->next != NULL)
+                printf(",");
+            ptr = ptr->next;
+            printf("\n");
+        }
+        indent--;
+        Indent(indent);
+        printf("}");
+        break;
+    }
+    case JSONNodeKind_Arr: {
+        printf("[\n");
+        JSONNode * ptr = node->firstChild;
+        indent++;
+        while (ptr != NULL) {
+            Indent(indent);
+            JSONNodePrint(ptr, currNode);
+            if (ptr->next != NULL)
+                printf(",");
+            printf("\n");
+            ptr = ptr->next;
+        }
+        indent--;
+        Indent(indent);
+        printf("]");
+        break;
+    }
+    }
+}
+
+
+// Input
+
+JSONNode * g_DrawNode = NULL;
+JSONNode * g_CurrNode = NULL;
+
+void
+Draw(void) {
+    vt100ClearScreen();
+    vt100CursorHome();
+    
+    if (g_DrawNode != NULL) {
+        JSONNodePrint(g_DrawNode, g_CurrNode);
+        vt100RestoreCursor();
+    }
+}
+
+int
+GetChar() {
+    Draw();
+    int c = getch();
+    return c;
+}
+
+int
+PeekChar() {
+    Draw();
+    int c = peekch();
+    return c;
+}
+
+typedef bool(*CharPredicateFunc)(char, int);
+
+bool predStr(char c, int i) { return c >= 'a' && c <= 'z'; }
+bool predInt(char c, int i) { return c >= '0' && c <= '9'; }
+
+JSONNode *
+GetNode() {
+    JSONNode * result = NULL;
+    JSONNode * node = NULL;
+    int c;
+    int strLen = 0;
+
+    typedef enum {
+        InputState_Initial,
+        InputState_Primitive,
+        InputState_Object,
+        InputState_KeyValue,
+        InputState_Array,
+    } InputState;
+    InputState inputState;
+
+    bool editingNode = false;
+
+    while (true) {
+        c = GetChar();
+
+        if (c == KEY_CTRL_C)
+            break;
+
+        if (isNewline(c)) {
+            if (node == NULL || node->parent == NULL)
+                break;
+
+            if (node->parent->kind == JSONNodeKind_Obj) // editing obj key
+                g_CurrNode = node = node->firstChild;
+            else if (node->parent->kind == JSONNodeKind_Str) // editing obj value
+                g_CurrNode = node = node->parent->parent;
+            else
+                g_CurrNode = node = node->parent;
+            
+            editingNode = JSONNodeEditable(node);
+            
+            continue;
+        }
+
+        if (JSONNodeEditable(node) && editingNode) {
+            if (node->data == (size_t)NULL) {
+                node->data = (size_t)NEWARR(char, 16);
+                strLen = 0;
+            }
+            char * str = (char *)node->data;
+
+            if (isBackspace(c)) {
+                str[strLen-1] = '\0';
+                strLen--;
+            }
+            else if (strLen < 16 - 1) {
+                str[strLen] = c;
+                str[strLen+1] = '\0';
+                strLen++;
+            }
+        }
+        else {
+            JSONNode * old = node;
+
+            /**/ if (c == 'i') { node = JSONNodeNew(JSONNodeKind_Int); editingNode = true; }
+            else if (c == 's') { node = JSONNodeNew(JSONNodeKind_Str); editingNode = true; }
+            else if (c == 'o') { node = JSONNodeNew(JSONNodeKind_Obj); }
+            else if (c == 'a') { node = JSONNodeNew(JSONNodeKind_Arr); }
+            else if (c == 'h') { if (node->prev != NULL) g_CurrNode = node = node->prev; continue; }
+            else if (c == 'l') { if (node->next != NULL) g_CurrNode = node = node->next; continue; }
+            else if (c == 'k') { if (node->parent != NULL) g_CurrNode = node = node->parent; continue; }
+            else if (c == 'j') { if (node->firstChild != NULL) g_CurrNode = node = node->firstChild; continue; }
+            else if (c == ' ') { if (JSONNodeEditable(node)) editingNode = true; continue; }
+            else if (isBackspace(c) && node != NULL) { g_CurrNode = node = JSONNodeRemove(node); continue; }
+            else { continue; }
+
+            if (old != NULL) {
+                // new node was added to an object
+                if (old->kind == JSONNodeKind_Obj) {
+                    JSONNode * keyNode = JSONNodeNew(JSONNodeKind_Str);
+                    JSONNodePush(keyNode, node);
+                    node = keyNode;
+                    editingNode = true;
+                }
+                JSONNodePush(old, node);
+            }
+            else {
+                g_DrawNode = result = node;
+            }
+
+            g_CurrNode = node;
+        }
+    }
+
+    return result;
+}
+
+
+
+
+int main() {
+    vt100EnableAlternateBuffer();
+
+    JSONNode * n = GetNode();
+    
+    vt100DisableAlternateBuffer();
+
+    JSONNodePrint(n, NULL);
+
+    // JSONFree(n);
+
+    return 0;
+}