logging works ( but it's buggy 😭 )
This commit is contained in:
+270
@@ -0,0 +1,270 @@
|
|||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
REMOVED
|
||||||
|
REMOVED
|
||||||
|
REMOVED
|
||||||
|
REMOVED
|
||||||
|
REMOVED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
ADDED
|
||||||
|
ADDED
|
||||||
|
ADDED
|
||||||
|
ADDED
|
||||||
|
ADDED
|
||||||
|
ADDED
|
||||||
|
ADDED
|
||||||
|
ADDED
|
||||||
|
ADDED
|
||||||
|
ADDED
|
||||||
|
REMOVED
|
||||||
|
REMOVED
|
||||||
|
REMOVED
|
||||||
|
REMOVED
|
||||||
|
REMOVED
|
||||||
|
REMOVED
|
||||||
|
REMOVED
|
||||||
|
REMOVED
|
||||||
|
REMOVED
|
||||||
|
REMOVED
|
||||||
|
REMOVED
|
||||||
|
REMOVED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
|
UNMODIFIED
|
||||||
+11
-4
@@ -96,7 +96,6 @@ String_t str_from_file(FILE* file) {
|
|||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int str_to_file(String_t* self, FILE* file) {
|
int str_to_file(String_t* self, FILE* file) {
|
||||||
char* data = to_chars(self);
|
char* data = to_chars(self);
|
||||||
|
|
||||||
@@ -108,16 +107,24 @@ int str_to_file(String_t* self, FILE* file) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
String_t str_clone(String_t* self) {
|
String_t str_clone(String_t* self) {
|
||||||
String_t clone;
|
String_t clone;
|
||||||
clone.size = self->size;
|
clone.size = self->size;
|
||||||
clone.capacity = self->capacity;
|
clone.capacity = self->capacity;
|
||||||
clone.data = (char*)malloc(clone.capacity);
|
clone.data = (char*)calloc(clone.capacity, sizeof(char));
|
||||||
memcpy(clone.data, self->data, clone.capacity);
|
memcpy(clone.data, self->data, clone.capacity);
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String_t* str_clone_all(String_t* strings, int len) {
|
||||||
|
String_t* clones = (String_t*)calloc(len, sizeof(String_t));
|
||||||
|
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
clones[i] = str_clone(&strings[i]);
|
||||||
|
}
|
||||||
|
return clones;
|
||||||
|
}
|
||||||
|
|
||||||
char str_pop(String_t* self) {
|
char str_pop(String_t* self) {
|
||||||
if (self->size == 0) {
|
if (self->size == 0) {
|
||||||
return '\0';
|
return '\0';
|
||||||
@@ -244,4 +251,4 @@ String_t str_slice(String_t* s, int start, int end) {
|
|||||||
s2.data[i] = s->data[start + i];
|
s2.data[i] = s->data[start + i];
|
||||||
}
|
}
|
||||||
return s2;
|
return s2;
|
||||||
}
|
}
|
||||||
+3
-1
@@ -70,7 +70,9 @@ String_t str_new();
|
|||||||
* This allocates new memory that must be freed
|
* This allocates new memory that must be freed
|
||||||
* @param self Pointer to the String_t to clone
|
* @param self Pointer to the String_t to clone
|
||||||
*/
|
*/
|
||||||
String_t str_clone();
|
String_t str_clone(String_t* self);
|
||||||
|
|
||||||
|
String_t* str_clone_all(String_t* strings, int len);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new string containing a slice of an existing string.
|
* Creates a new string containing a slice of an existing string.
|
||||||
|
|||||||
+191
-30
@@ -1,6 +1,8 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "dynstr.h"
|
#include "dynstr.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "state.h"
|
||||||
#include <ncurses.h>
|
#include <ncurses.h>
|
||||||
|
|
||||||
#define min(x, y) ((x) < (y) ? (x) : (y))
|
#define min(x, y) ((x) < (y) ? (x) : (y))
|
||||||
@@ -15,9 +17,13 @@ typedef struct {
|
|||||||
uint32_t x_offset;
|
uint32_t x_offset;
|
||||||
bool unsaved_changes;
|
bool unsaved_changes;
|
||||||
bool editmode;
|
bool editmode;
|
||||||
String_t* buffer;
|
|
||||||
String_t* original;
|
|
||||||
String_t filename;
|
String_t filename;
|
||||||
|
String_t* buffer;
|
||||||
|
|
||||||
|
// variables needed for the changelog functionality
|
||||||
|
int original_len; // length of the original buffer
|
||||||
|
String_t* original; // original buffer
|
||||||
|
StateArray diff; // changelog
|
||||||
} Editor;
|
} Editor;
|
||||||
|
|
||||||
void add_toolbar(Editor* self) {
|
void add_toolbar(Editor* self) {
|
||||||
@@ -46,6 +52,23 @@ void add_toolbar(Editor* self) {
|
|||||||
attroff(COLOR_PAIR(1));
|
attroff(COLOR_PAIR(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void refresh_cursor(Editor* self) {
|
||||||
|
|
||||||
|
int max_x,max_y;
|
||||||
|
getmaxyx(stdscr, max_y, max_x);
|
||||||
|
|
||||||
|
// Clamp cursor position to screen bounds
|
||||||
|
int cursor_y = min(
|
||||||
|
max(self->buffer_line - self->y_offset, 0),
|
||||||
|
max_y - 1
|
||||||
|
);
|
||||||
|
int cursor_x = min(
|
||||||
|
max(self->buffer_col - self->x_offset + 5, 5),
|
||||||
|
max_x - 1
|
||||||
|
);
|
||||||
|
|
||||||
|
move(cursor_y, cursor_x);
|
||||||
|
}
|
||||||
|
|
||||||
void refresh_buffer(Editor* self) {
|
void refresh_buffer(Editor* self) {
|
||||||
move(0, 0);
|
move(0, 0);
|
||||||
@@ -99,41 +122,42 @@ void refresh_buffer(Editor* self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
add_toolbar(self);
|
add_toolbar(self);
|
||||||
|
refresh_cursor(self);
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure cursor position is valid
|
|
||||||
int cursor_y = self->buffer_line - self->y_offset;
|
|
||||||
int cursor_x = self->buffer_col - self->x_offset + 5;
|
|
||||||
|
|
||||||
// Clamp cursor position to screen bounds
|
|
||||||
cursor_y = min(max(cursor_y, 0), max_y - 1);
|
|
||||||
cursor_x = min(max(cursor_x, 5), max_x - 1);
|
|
||||||
|
|
||||||
move(cursor_y, cursor_x);
|
|
||||||
}
|
|
||||||
|
|
||||||
void move_cursor_on_screen(Editor* editor, int x, int y) {
|
void move_cursor_on_screen(Editor* self, int x, int y) {
|
||||||
|
bool offset_changed = false;
|
||||||
int max_x, max_y;
|
int max_x, max_y;
|
||||||
|
|
||||||
getmaxyx(stdscr, max_y, max_x);
|
getmaxyx(stdscr, max_y, max_x);
|
||||||
max_y--; // Reserve bottom line for toolbar
|
max_y--; // Reserve bottom line for toolbar
|
||||||
max_x -= 5; // Account for line number margin
|
max_x -= 5; // Account for line number margin
|
||||||
|
|
||||||
// Adjust x_offset if cursor would be off screen
|
// Adjust x_offset if cursor would be off screen
|
||||||
if (x + 5 >= max_x + editor->x_offset) {
|
if (x + 5 >= max_x + self->x_offset) {
|
||||||
editor->x_offset = x - max_x + 6;
|
self->x_offset = x - max_x + 6;
|
||||||
|
offset_changed = true;
|
||||||
}
|
}
|
||||||
if (x < editor->x_offset) {
|
if (x < self->x_offset) {
|
||||||
editor->x_offset = x;
|
self->x_offset = x;
|
||||||
|
offset_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjust y_offset if cursor would be off screen
|
// Adjust y_offset if cursor would be off screen
|
||||||
if (y >= max_y + editor->y_offset) {
|
if (y >= max_y + self->y_offset) {
|
||||||
editor->y_offset = y - max_y + 1;
|
self->y_offset = y - max_y + 1;
|
||||||
|
offset_changed = true;
|
||||||
}
|
}
|
||||||
if (y < editor->y_offset) {
|
if (y < self->y_offset) {
|
||||||
editor->y_offset = y;
|
self->y_offset = y;
|
||||||
|
offset_changed = true;
|
||||||
|
}
|
||||||
|
if (offset_changed) {
|
||||||
|
refresh_buffer(self);
|
||||||
|
} else {
|
||||||
|
refresh_cursor(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh_buffer(editor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void switch_mode(Editor* self) {
|
void switch_mode(Editor* self) {
|
||||||
@@ -144,7 +168,7 @@ void switch_mode(Editor* self) {
|
|||||||
void newline(Editor* self) {
|
void newline(Editor* self) {
|
||||||
move(self->buffer_line, 0);
|
move(self->buffer_line, 0);
|
||||||
char line_no[6];
|
char line_no[6];
|
||||||
snprintf(line_no, 6, "%-6d", self->buffer_line + 1);
|
snprintf(line_no, 6, "%-5d", self->buffer_line + 1);
|
||||||
|
|
||||||
// add the line number
|
// add the line number
|
||||||
attron(COLOR_PAIR(1));
|
attron(COLOR_PAIR(1));
|
||||||
@@ -166,9 +190,13 @@ Editor editor_from(String_t input_string, String_t filename) {
|
|||||||
|
|
||||||
int linenum = 0;
|
int linenum = 0;
|
||||||
e.buffer = str_lines(&input_string, &linenum);
|
e.buffer = str_lines(&input_string, &linenum);
|
||||||
|
e.original = str_clone_all(e.buffer, linenum);
|
||||||
e.lines = (uint32_t)linenum;
|
e.lines = (uint32_t)linenum;
|
||||||
|
e.original_len = linenum;
|
||||||
e.capacity = e.lines;
|
e.capacity = e.lines;
|
||||||
|
|
||||||
|
e.diff = arr_init(e.lines);
|
||||||
|
|
||||||
str_dealloc(&input_string);
|
str_dealloc(&input_string);
|
||||||
|
|
||||||
refresh_buffer(&e);
|
refresh_buffer(&e);
|
||||||
@@ -179,22 +207,129 @@ Editor new_editor(String_t filename) {
|
|||||||
return editor_from(str_new(), filename);
|
return editor_from(str_new(), filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int print_diff(Editor* self) {
|
||||||
|
|
||||||
|
char buffer[512];
|
||||||
|
String_t log_string = str_new();
|
||||||
|
int original_offset;
|
||||||
|
int modified_offset;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < self->diff.size; i++) {
|
||||||
|
LineState state = self->diff.data[i];
|
||||||
|
switch (state) {
|
||||||
|
// case UNMODIFIED:
|
||||||
|
// original_offset = arr_original_offset(&self->diff, i);
|
||||||
|
// snprintf(
|
||||||
|
// buffer,
|
||||||
|
// 512,
|
||||||
|
// "UNMODIFIED %s \n",
|
||||||
|
// to_chars(&self->buffer[original_offset + i])
|
||||||
|
// );
|
||||||
|
// str_push_str(&log_string, buffer);
|
||||||
|
// break;
|
||||||
|
|
||||||
|
case MODIFIED:
|
||||||
|
original_offset = arr_original_offset(&self->diff, i);
|
||||||
|
modified_offset = arr_modified_offset(&self->diff, i);
|
||||||
|
snprintf(
|
||||||
|
buffer,
|
||||||
|
512,
|
||||||
|
"MODIFIED %-6d | %s\n -> %-6d | %s \n",
|
||||||
|
original_offset + i + 1,
|
||||||
|
to_chars(&self->original[original_offset + i]),
|
||||||
|
modified_offset + i + 1,
|
||||||
|
to_chars(&self->buffer[modified_offset + i])
|
||||||
|
);
|
||||||
|
str_push_str(&log_string, buffer);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case REMOVED:
|
||||||
|
original_offset = arr_original_offset(&self->diff, i);
|
||||||
|
snprintf(
|
||||||
|
buffer,
|
||||||
|
512,
|
||||||
|
"REMOVED %-6d | %s \n",
|
||||||
|
original_offset + i + 1,
|
||||||
|
to_chars(&self->original[original_offset + i])
|
||||||
|
);
|
||||||
|
str_push_str(&log_string, buffer);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ADDED:
|
||||||
|
modified_offset = arr_modified_offset(&self->diff, i);
|
||||||
|
snprintf(
|
||||||
|
buffer,
|
||||||
|
512,
|
||||||
|
"ADDED %-6d | %s \n",
|
||||||
|
modified_offset + i + 1,
|
||||||
|
to_chars(&self->buffer[modified_offset + i])
|
||||||
|
);
|
||||||
|
str_push_str(&log_string, buffer);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write_log(&log_string) == -1) {
|
||||||
|
fprintf(stderr, "Error writing log\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
str_dealloc(&log_string);
|
||||||
|
}
|
||||||
|
|
||||||
int save_file(Editor* self) {
|
int save_file(Editor* self) {
|
||||||
|
String_t log_string = str_new();
|
||||||
|
char log_message[64];
|
||||||
char* name = to_chars(&self->filename);
|
char* name = to_chars(&self->filename);
|
||||||
|
FILE* file = fopen(name, "w");
|
||||||
|
|
||||||
|
if (file == NULL) {
|
||||||
|
fprintf(stderr, "Error: Could not open file %s\n", name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
// writes the content line by line to the file
|
// writes the content line by line to the file
|
||||||
FILE* file = fopen(name, "w");
|
|
||||||
for (size_t i = 0; i < self->lines; i++) {
|
for (size_t i = 0; i < self->lines; i++) {
|
||||||
if (str_to_file(&self->buffer[i], file) == -1) {
|
if (str_to_file(&self->buffer[i], file) == -1) {
|
||||||
|
|
||||||
free(name);
|
free(name);
|
||||||
fprintf(stderr, "Error writing to file\n");
|
fprintf(stderr, "Error writing to file\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
arr_print(&self->diff);
|
||||||
|
print_diff(self);
|
||||||
|
|
||||||
free(name);
|
free(name);
|
||||||
fclose(file);
|
fclose(file);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < self->original_len; i++) {
|
||||||
|
str_dealloc(&self->original[i]);
|
||||||
|
}
|
||||||
|
free(self->original);
|
||||||
|
self->original = str_clone_all(self->buffer, self->lines);
|
||||||
|
|
||||||
|
snprintf(log_message, 64, "Edited file [%s]\n", to_chars(&self->filename));
|
||||||
|
str_push_str(&log_string, log_message);
|
||||||
|
|
||||||
|
self->original_len = self->lines;
|
||||||
|
|
||||||
|
snprintf(log_message, 64, "Old Length: [%d] New Length: [%d]\n", self->original_len, self->lines);
|
||||||
|
str_push_str(&log_string, log_message);
|
||||||
|
|
||||||
|
// log messages
|
||||||
|
if (write_log(&log_string) == -1) {
|
||||||
|
fprintf(stderr, "Error writing log\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("wrote log");
|
||||||
|
str_dealloc(&log_string);
|
||||||
|
|
||||||
|
arr_dealloc(&self->diff);
|
||||||
|
self->diff = arr_init(self->lines);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,7 +370,6 @@ void delchar(Editor* self) {
|
|||||||
self->unsaved_changes = true;
|
self->unsaved_changes = true;
|
||||||
|
|
||||||
if (self->buffer_col == str_len(&self->buffer[self->buffer_line])) {
|
if (self->buffer_col == str_len(&self->buffer[self->buffer_line])) {
|
||||||
|
|
||||||
if (self->buffer_line +1 == self->lines) {
|
if (self->buffer_line +1 == self->lines) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -245,7 +379,13 @@ void delchar(Editor* self) {
|
|||||||
String_t old_next_line = self->buffer[self->buffer_line + 1];
|
String_t old_next_line = self->buffer[self->buffer_line + 1];
|
||||||
|
|
||||||
// assign the new line
|
// assign the new line
|
||||||
self->buffer[self->buffer_line] = str_merge(&self->buffer[self->buffer_line], &self->buffer[self->buffer_line + 1]);
|
self->buffer[self->buffer_line] = str_merge(&old_curr_line, &old_next_line);
|
||||||
|
|
||||||
|
if (arr_get(&self->diff, self->buffer_line + 1) == ADDED) {
|
||||||
|
arr_remove(&self->diff, self->buffer_line + 1);
|
||||||
|
} else {
|
||||||
|
arr_set(&self->diff, self->buffer_line + 1, REMOVED);
|
||||||
|
}
|
||||||
|
|
||||||
// deallocate the memory for the old lines
|
// deallocate the memory for the old lines
|
||||||
str_dealloc(&old_curr_line);
|
str_dealloc(&old_curr_line);
|
||||||
@@ -261,6 +401,8 @@ void delchar(Editor* self) {
|
|||||||
refresh_buffer(self);
|
refresh_buffer(self);
|
||||||
} else {
|
} else {
|
||||||
// removes the character from the current line
|
// removes the character from the current line
|
||||||
|
arr_set(&self->diff, self->buffer_line, MODIFIED);
|
||||||
|
|
||||||
str_remove(&self->buffer[self->buffer_line], self->buffer_col);
|
str_remove(&self->buffer[self->buffer_line], self->buffer_col);
|
||||||
delch();
|
delch();
|
||||||
}
|
}
|
||||||
@@ -278,6 +420,9 @@ void pressed_enter(Editor* self) {
|
|||||||
}
|
}
|
||||||
self->buffer = (String_t*)realloc(self->buffer, sizeof(String_t) * (self->capacity));
|
self->buffer = (String_t*)realloc(self->buffer, sizeof(String_t) * (self->capacity));
|
||||||
|
|
||||||
|
// logs the change of adding a new line
|
||||||
|
arr_insert(&self->diff, self->buffer_line + 1, ADDED);
|
||||||
|
|
||||||
if (self->buffer_line == self->lines -1) {
|
if (self->buffer_line == self->lines -1) {
|
||||||
// if at end of file add an empty line
|
// if at end of file add an empty line
|
||||||
self->buffer[self->lines - 1] = str_new();
|
self->buffer[self->lines - 1] = str_new();
|
||||||
@@ -314,6 +459,12 @@ void addchar(Editor* self, char c) {
|
|||||||
|
|
||||||
if (self->buffer_line == self->lines) {
|
if (self->buffer_line == self->lines) {
|
||||||
// if we are at the end of the file then we need to add a new line
|
// if we are at the end of the file then we need to add a new line
|
||||||
|
if (arr_get(&self->diff, self->buffer_line) == REMOVED) {
|
||||||
|
arr_set(&self->diff, self->buffer_line, MODIFIED);
|
||||||
|
} else {
|
||||||
|
arr_insert(&self->diff, self->buffer_line, ADDED);
|
||||||
|
}
|
||||||
|
|
||||||
newline(self);
|
newline(self);
|
||||||
|
|
||||||
// reallocate self->buffer to be 1 larger
|
// reallocate self->buffer to be 1 larger
|
||||||
@@ -327,6 +478,10 @@ void addchar(Editor* self, char c) {
|
|||||||
|
|
||||||
// allocate the memory space for the new line and add it to the buffer
|
// allocate the memory space for the new line and add it to the buffer
|
||||||
self->buffer[self->buffer_line] = str_new();
|
self->buffer[self->buffer_line] = str_new();
|
||||||
|
} else {
|
||||||
|
if (arr_get(&self->diff, self->buffer_line) == UNMODIFIED) {
|
||||||
|
arr_set(&self->diff, self->buffer_line, MODIFIED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
insch(c);
|
insch(c);
|
||||||
@@ -339,6 +494,12 @@ void shutdown_editor(Editor* self) {
|
|||||||
for (size_t i = 0; i < self->capacity; i++) {
|
for (size_t i = 0; i < self->capacity; i++) {
|
||||||
str_dealloc(&self->buffer[i]);
|
str_dealloc(&self->buffer[i]);
|
||||||
}
|
}
|
||||||
str_dealloc(&self->filename);
|
|
||||||
free(self->buffer);
|
free(self->buffer);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < self->original_len; i++) {
|
||||||
|
str_dealloc(&self->original[i]);
|
||||||
|
}
|
||||||
|
free(self->original);
|
||||||
|
|
||||||
|
str_dealloc(&self->filename);
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-3
@@ -1,4 +1,5 @@
|
|||||||
#include "dynstr.h"
|
#include "dynstr.h"
|
||||||
|
#include "state.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -6,7 +7,7 @@
|
|||||||
* cursor position, and various editor settings.
|
* cursor position, and various editor settings.
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t lines;
|
uint32_t lines;
|
||||||
uint32_t capacity;
|
uint32_t capacity;
|
||||||
uint32_t buffer_line;
|
uint32_t buffer_line;
|
||||||
uint32_t buffer_col;
|
uint32_t buffer_col;
|
||||||
@@ -14,9 +15,13 @@ typedef struct {
|
|||||||
uint32_t x_offset;
|
uint32_t x_offset;
|
||||||
bool unsaved_changes;
|
bool unsaved_changes;
|
||||||
bool editmode;
|
bool editmode;
|
||||||
String_t* buffer;
|
|
||||||
String_t* original;
|
|
||||||
String_t filename;
|
String_t filename;
|
||||||
|
String_t* buffer;
|
||||||
|
|
||||||
|
// variables needed for the changelog functionality
|
||||||
|
int original_len; // length of the original buffer
|
||||||
|
String_t* original; // original buffer
|
||||||
|
StateArray diff; // changelog
|
||||||
} Editor;
|
} Editor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+7
-3
@@ -1,14 +1,18 @@
|
|||||||
#include "dynstr.h"
|
#include "dynstr.h"
|
||||||
|
|
||||||
|
|
||||||
int write_log(String_t* string, int lines) {
|
int write_log(String_t* string) {
|
||||||
|
|
||||||
FILE* file = fopen("log.txt", "a");
|
FILE* file = fopen("log.txt", "a");
|
||||||
|
int numlines;
|
||||||
|
String_t* lines = str_lines(string, &numlines);
|
||||||
|
|
||||||
for (int i = 0; i < lines; i++) {
|
for (int i = 0; i < numlines; i++) {
|
||||||
str_to_file(&string[i], file);
|
str_to_file(&lines[i], file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -8,5 +8,5 @@
|
|||||||
* @return 0 on success, -1 on error
|
* @return 0 on success, -1 on error
|
||||||
* @note This function frees the string passed to it!
|
* @note This function frees the string passed to it!
|
||||||
*/
|
*/
|
||||||
int write_log(String_t* string, int lines);
|
int write_log(String_t* string);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
REMOVED 16 |
|
||||||
|
REMOVED 17 |
|
||||||
|
REMOVED 18 | modifie
|
||||||
|
REMOVED 19 |
|
||||||
|
REMOVED 20 |
|
||||||
|
ADDED 35 | } AND modified a line.
|
||||||
|
ADDED 36 |
|
||||||
|
ADDED 37 |
|
||||||
|
ADDED 38 | added some lines
|
||||||
|
ADDED 39 |
|
||||||
|
ADDED 40 |
|
||||||
|
ADDED 41 |
|
||||||
|
ADDED 42 |
|
||||||
|
ADDED 43 |
|
||||||
|
ADDED 44 |
|
||||||
|
REMOVED 40 | } AND modified a line.
|
||||||
|
REMOVED 41 |
|
||||||
|
REMOVED 42 |
|
||||||
|
REMOVED 43 | added some lines
|
||||||
|
REMOVED 44 |
|
||||||
|
REMOVED 45 |
|
||||||
|
REMOVED 46 |
|
||||||
|
REMOVED 47 |
|
||||||
|
REMOVED 48 |
|
||||||
|
REMOVED 49 |
|
||||||
|
REMOVED 50 |
|
||||||
|
REMOVED 51 |
|
||||||
+3
-2
@@ -211,7 +211,7 @@ int main(int argc, char* argv[]) {
|
|||||||
if (argc == 3) {
|
if (argc == 3) {
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case CMD_OPEN:
|
case CMD_OPEN:
|
||||||
sprintf(log_fmt_string, "Edited File [%s]", argv[2]);
|
sprintf(log_fmt_string, "Opened File [%s]", argv[2]);
|
||||||
open_editor(argv[2]);
|
open_editor(argv[2]);
|
||||||
break;
|
break;
|
||||||
case CMD_RM:
|
case CMD_RM:
|
||||||
@@ -263,7 +263,8 @@ int main(int argc, char* argv[]) {
|
|||||||
// log output of command
|
// log output of command
|
||||||
if (log_fmt_string[0] != '\0') {
|
if (log_fmt_string[0] != '\0') {
|
||||||
String_t str = str_from_chars(log_fmt_string);
|
String_t str = str_from_chars(log_fmt_string);
|
||||||
write_log(&str, 1);
|
write_log(&str);
|
||||||
|
str_dealloc(&str);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+150
@@ -0,0 +1,150 @@
|
|||||||
|
/// dynamic array class
|
||||||
|
/// written by: Harry Irving
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef enum LineState {
|
||||||
|
UNMODIFIED,
|
||||||
|
MODIFIED,
|
||||||
|
ADDED,
|
||||||
|
REMOVED,
|
||||||
|
} LineState;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t size; // Current number of elements
|
||||||
|
uint32_t capacity; // Total capacity allocated
|
||||||
|
LineState* data; // Array of LineState values
|
||||||
|
} StateArray;
|
||||||
|
|
||||||
|
StateArray arr_init(int capacity) {
|
||||||
|
StateArray arr;
|
||||||
|
arr.size = capacity;
|
||||||
|
arr.capacity = capacity;
|
||||||
|
arr.data = (LineState*)calloc(capacity, sizeof(LineState));
|
||||||
|
// Initialize all elements to UNMODIFIED
|
||||||
|
for (int i = 0; i < capacity; i++) {
|
||||||
|
arr.data[i] = UNMODIFIED;
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void arr_print(StateArray* self) {
|
||||||
|
FILE* log_file = fopen("diff.txt", "w");
|
||||||
|
if (log_file == NULL) {
|
||||||
|
fprintf(stderr, "Error: Could not open log file\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < self->size; i++) {
|
||||||
|
switch (self->data[i]) {
|
||||||
|
case UNMODIFIED:
|
||||||
|
fwrite("UNMODIFIED\n", sizeof(char), strlen("UNMODIFIED\n"), log_file);
|
||||||
|
break;
|
||||||
|
case MODIFIED:
|
||||||
|
fwrite("MODIFIED\n", sizeof(char), strlen("MODIFIED\n"), log_file);
|
||||||
|
break;
|
||||||
|
case ADDED:
|
||||||
|
fwrite("ADDED\n", sizeof(char), strlen("ADDED\n"), log_file);
|
||||||
|
break;
|
||||||
|
case REMOVED:
|
||||||
|
fwrite("REMOVED\n", sizeof(char), strlen("REMOVED\n"), log_file);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(log_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
int arr_added_or_removed_before(StateArray* self, int index) {
|
||||||
|
int count = 0;
|
||||||
|
for (int i = 0; i < index; i++) {
|
||||||
|
if (self->data[i] == ADDED || self->data[i] == REMOVED) {
|
||||||
|
if (self->data[i] == REMOVED) {
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void arr_set(StateArray* self, int index, LineState state) {
|
||||||
|
int offset = arr_added_or_removed_before(self, index);
|
||||||
|
self->data[index] = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
LineState arr_remove(StateArray* self, int index) {
|
||||||
|
int offset = arr_added_or_removed_before(self, index);
|
||||||
|
|
||||||
|
if (index >= self->size) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LineState removed = self->data[index];
|
||||||
|
|
||||||
|
// Shift remaining elements left
|
||||||
|
for (int i = index; i < self->size - 1; i++) {
|
||||||
|
self->data[i] = self->data[i + 1];
|
||||||
|
}
|
||||||
|
self->size--;
|
||||||
|
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
int arr_insert(StateArray* self, int index, LineState state) {
|
||||||
|
int offset = arr_added_or_removed_before(self, index);
|
||||||
|
|
||||||
|
if (index > self->size) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
self->size++;
|
||||||
|
|
||||||
|
// Grow array if needed
|
||||||
|
if (self->size > self->capacity) {
|
||||||
|
self->capacity = (self->size) * sizeof(LineState);
|
||||||
|
self->data = (LineState*)realloc(self->data, self->capacity * sizeof(LineState));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift elements right to make room
|
||||||
|
for (int i = self->size - 1; i > index; i--) {
|
||||||
|
self->data[i] = self->data[i - 1];
|
||||||
|
}
|
||||||
|
self->data[index] = state;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LineState arr_get(StateArray* self, int index) {
|
||||||
|
int offset = arr_added_or_removed_before(self, index);
|
||||||
|
return self->data[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
int arr_original_offset(StateArray* self, int index) {
|
||||||
|
int offset = 0;
|
||||||
|
for (int i = 0; i < index; i++) {
|
||||||
|
if (self->data[i] == ADDED) {
|
||||||
|
offset--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
int arr_modified_offset(StateArray* self, int index) {
|
||||||
|
int offset = 0;
|
||||||
|
for (int i = 0; i < index; i++) {
|
||||||
|
if (self->data[i] == REMOVED) {
|
||||||
|
offset--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
int arr_dealloc(StateArray* self) {
|
||||||
|
free(self->data);
|
||||||
|
self->data = NULL;
|
||||||
|
self->size = 0;
|
||||||
|
self->capacity = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
+115
@@ -0,0 +1,115 @@
|
|||||||
|
#ifndef STATEARR_H
|
||||||
|
#define STATEARR_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef enum LineState {
|
||||||
|
UNMODIFIED,
|
||||||
|
MODIFIED,
|
||||||
|
ADDED,
|
||||||
|
REMOVED,
|
||||||
|
} LineState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dynamic array structure for storing LineState values
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint32_t size; // Current number of elements
|
||||||
|
uint32_t capacity; // Total capacity allocated
|
||||||
|
LineState* data; // Array of LineState values
|
||||||
|
} StateArray;
|
||||||
|
|
||||||
|
void arr_print(StateArray* self);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new StateArray with the specified initial capacity
|
||||||
|
*
|
||||||
|
* All elements will have a default value of UNMODIFIED
|
||||||
|
*
|
||||||
|
* @param capacity Initial capacity in number of elements
|
||||||
|
* @return A new StateArray structure with allocated memory
|
||||||
|
*/
|
||||||
|
StateArray arr_init(int capacity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a LineState value at the specified index
|
||||||
|
* Does not perform bounds checking
|
||||||
|
*
|
||||||
|
* @param self Pointer to the StateArray to modify
|
||||||
|
* @param index Position to set the value
|
||||||
|
* @param state The LineState value to set
|
||||||
|
*/
|
||||||
|
void arr_set(StateArray* self, int index, LineState state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a LineState value at the specified index
|
||||||
|
* Shifts remaining elements left to fill the gap
|
||||||
|
*
|
||||||
|
* @param self Pointer to the StateArray to modify
|
||||||
|
* @param index Position of value to remove
|
||||||
|
* @return The LineState value that was removed
|
||||||
|
*/
|
||||||
|
LineState arr_remove(StateArray* self, int index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts a LineState value at the specified index
|
||||||
|
* Shifts existing elements right to make room
|
||||||
|
* Automatically reallocates if more capacity is needed
|
||||||
|
*
|
||||||
|
* @param self Pointer to the StateArray to modify
|
||||||
|
* @param index Position to insert the value
|
||||||
|
* @param state The LineState value to insert
|
||||||
|
* @return 0 on success, -1 if index is out of bounds
|
||||||
|
*/
|
||||||
|
int arr_insert(StateArray* self, int index, LineState state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the LineState value at the specified index
|
||||||
|
* Does not perform bounds checking
|
||||||
|
*
|
||||||
|
* @param self Pointer to the StateArray to read from
|
||||||
|
* @param index Position to get the value from
|
||||||
|
* @return The LineState value at the specified index
|
||||||
|
*/
|
||||||
|
LineState arr_get(StateArray* self, int index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deallocates memory used by the StateArray
|
||||||
|
* Must be called when the array is no longer needed
|
||||||
|
*
|
||||||
|
* @param self Pointer to the StateArray to deallocate
|
||||||
|
* @return 0 on success
|
||||||
|
*/
|
||||||
|
int arr_dealloc(StateArray* self);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the offset needed to map from state array index to original buffer index
|
||||||
|
* by counting ADDED lines before the given index
|
||||||
|
*
|
||||||
|
* @param self Pointer to the StateArray
|
||||||
|
* @param index The index in the state array
|
||||||
|
* @return The negative offset (number of ADDED lines before index)
|
||||||
|
*/
|
||||||
|
int arr_original_offset(StateArray* self, int index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the offset needed to map from state array index to modified buffer index
|
||||||
|
* by counting REMOVED lines before the given index
|
||||||
|
*
|
||||||
|
* @param self Pointer to the StateArray
|
||||||
|
* @param index The index in the state array
|
||||||
|
* @return The negative offset (number of REMOVED lines before index)
|
||||||
|
*/
|
||||||
|
int arr_modified_offset(StateArray* self, int index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counts the number of lines that have been either added or removed
|
||||||
|
* Does not count lines that have been modified
|
||||||
|
*
|
||||||
|
* @param self Pointer to the StateArray to analyze
|
||||||
|
* @param index The index to count up to.
|
||||||
|
* @return The count of lines marked as either ADDED or REMOVED
|
||||||
|
*/
|
||||||
|
int arr_added_or_removed_before(StateArray* self, int index);
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user