Compare commits

...

11 Commits

Author SHA1 Message Date
FantasyPvP 00042ea092 done 2024-12-05 02:09:18 +00:00
FantasyPvP 3f35a00526 logging works ( but it's buggy 😭 ) 2024-12-05 01:42:50 +00:00
FantasyPvP ad8496ca68 started working on logging 2024-12-04 18:59:02 +00:00
FantasyPvP ad96bcc0c8 added documentation, almost done with code 2024-12-03 09:47:13 +00:00
FantasyPvP af08485d27 changed a comment 2024-11-10 00:42:59 +00:00
FantasyPvP e201014530 Merge remote-tracking branch 'refs/remotes/origin/main' 2024-11-10 00:42:32 +00:00
FantasyPvP 6573da5f43 added colours for line numbers, improved toolbar 2024-11-10 00:40:57 +00:00
Harry Irving 5018eac031 written an initial readme, removed some unnecessary files 2024-11-09 17:47:25 +00:00
FantasyPvP 380f3e1ff7 pretty much working, need to fix horizontal scrolling & file saving 2024-11-09 17:29:01 +00:00
FantasyPvP c4d7da083a Merge remote-tracking branch 'refs/remotes/origin/main' 2024-11-08 10:42:52 +00:00
FantasyPvP e7ecffa65a added loading files 2024-11-08 10:36:07 +00:00
18 changed files with 1304 additions and 468 deletions
+2
View File
@@ -1,2 +1,4 @@
*/target
*.out
*somefile*
*binary*
+2 -1
View File
@@ -13,6 +13,7 @@
"vector": "c",
"__bit_reference": "c",
"source_location": "c",
"stdbool.h": "c"
"stdbool.h": "c",
"dynstr.h": "c"
}
}
+20 -85
View File
@@ -1,93 +1,28 @@
# Computersystems Coursework Text Editor
## Usage:
## Getting started
### Compiling:
To make it easy for you to get started with GitLab, here's a list of recommended next steps.
for this program to compile you need the following C libraries on your system:
- ncurses
- Linux: usually preinstalled - otherwise installation depends on distro
- Mac
> brew install ncurses
- Windows:
- This may be more difficult to setup and requires additional research
Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
## Add your files
- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
compiling with GCC:
> gcc main.c editor.c dynstr.c -lncurses -o <binary-name>
### Running:
```bash
./<binary-name> open <$file> # opens the specified file in the editor
./<binary-name> new <$file> # creates a new file with <filename> as the name
./<binary-name> del <$file> # deletes the specified file
./<binary-name> copy <$file> <$newpath> # copies $file to the location $newpath
./<binary-name> move <$file> <$newpath> # moves $file to the location $newpath
./<binary-name> len <$file> # prints the length of $file
./<binary-name> log <$file> # prints the change history of $file
```
cd existing_repo
git remote add origin https://git.cs.bham.ac.uk/hxi486/computersystems-coursework-text-editor.git
git branch -M main
git push -uf origin main
```
## Integrate with your tools
- [ ] [Set up project integrations](https://git.cs.bham.ac.uk/hxi486/computersystems-coursework-text-editor/-/settings/integrations)
## Collaborate with your team
- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
## Test and Deploy
Use the built-in continuous integration in GitLab.
- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
***
# Editing this README
When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template.
## Suggestions for a good README
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
## Name
Choose a self-explaining name for your project.
## Description
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
## Badges
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
## Visuals
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
## Installation
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
## Usage
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
## Support
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
## Roadmap
If you have ideas for releases in the future, it is a good idea to list them in the README.
## Contributing
State if you are open to contributions and what your requirements are for accepting them.
For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
## Authors and acknowledgment
Show your appreciation to those who have contributed to the project.
## License
For open source projects, say how it is licensed.
## Project status
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
BIN
View File
Binary file not shown.
View File
+109 -68
View File
@@ -7,36 +7,35 @@
#include <stdbool.h>
#include <stdint.h>
typedef struct {
typedef struct String {
uint32_t size, capacity;
char* data;
} String_t;
String_t str_with_capacity(int capacity) {
String_t s;
/// allocate memory for 'capacity' chars
s.data = (char*)calloc(capacity, sizeof(char));
s.size = 0;
s.capacity = capacity;
return s;
int str_len(String_t* s) {
return (int)s->size;
}
String_t str_from_chars(const char* string) {
String_t s;
char* to_chars(String_t* s) {
// allocate buffer with null terminator
char* buffer = (char*)calloc(s->size + 1, sizeof(char));
for (int i = 0; i < s->size; i++) {
buffer[i] = s->data[i];
}
buffer[s->size] = '\0';
return buffer;
}
s.data = (char*)calloc(strlen(string), sizeof(char));
strcpy(s.data, string);
s.size = strlen(string);
s.capacity = strlen(string);
String_t str_with_capacity(int capacity) {
String_t s;
s.data = (char*)calloc(capacity, sizeof(char));
s.size = 0;
s.capacity = capacity;
return s;
}
String_t str_from_slice(const char* string, int len) {
String_t s;
s.data = (char*)calloc(len, sizeof(char));
strncpy(s.data, string, len);
s.size = len;
@@ -44,6 +43,10 @@ String_t str_from_slice(const char* string, int len) {
return s;
}
String_t str_from_chars(const char* string) {
return str_from_slice(string, strlen(string));
}
String_t str_new() {
return str_with_capacity(1);
}
@@ -54,14 +57,12 @@ int str_dealloc(String_t* self) {
}
int str_push(String_t* self, char c) {
// check size < capacity
if (self->size < self->capacity) {
self->data[self->size] = c;
self->size++;
return 0;
}
// reallocate to add capacity for an extra char
int newcap = self->capacity + sizeof(char);
self->data = (char*)realloc(self->data, newcap);
self->data[self->size] = c;
@@ -70,13 +71,66 @@ int str_push(String_t* self, char c) {
return 0;
}
int str_push_str(String_t* self, char* c) {
if (self->size + strlen(c) >= self->capacity) {
self->capacity = (self->size + strlen(c)) * sizeof(char);
self->data = (char*)realloc(self->data, self->capacity);
}
for (int i = 0; i < strlen(c); i++) {
self->data[self->size + i] = c[i];
}
self->size = self->capacity;
return 0;
}
String_t str_from_file(FILE* file) {
if (file == NULL) {
return str_new();
}
char buffer[64];
String_t string = str_new();
while (fgets(buffer, sizeof(buffer), file) != NULL) {
str_push_str(&string, buffer);
}
fclose(file);
return string;
}
int str_to_file(String_t* self, FILE* file) {
char* data = to_chars(self);
if (fprintf(file, "%s\n", data) == -1) {
free(data);
return -1;
}
free(data);
return 0;
}
String_t str_clone(String_t* self) {
String_t clone;
clone.size = self->size;
clone.capacity = self->capacity;
clone.data = (char*)calloc(clone.capacity, sizeof(char));
memcpy(clone.data, self->data, clone.capacity);
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) {
if (self->size == 0) {
return '\0';
}
self->size--;
char c = self->data[self->size];
return c;
return self->data[self->size];
}
int str_insert(String_t* self, int index, char c) {
@@ -84,6 +138,10 @@ int str_insert(String_t* self, int index, char c) {
return -1;
}
self->size++;
if (self->size > self->capacity) {
self->capacity = (self->size) * sizeof(char);
self->data = (char*)realloc(self->data, self->capacity);
}
for (int i = self->size - 1; i > index; i--) {
self->data[i] = self->data[i - 1];
}
@@ -102,22 +160,16 @@ int str_remove(String_t* self, int index) {
return 0;
}
/// splits a string into an array of strings based on a delimiter
String_t* str_split(String_t* self, int* res_len, char c) {
char* string = self->data;
String_t* elements = NULL;
// find the number of lines in the file
bool flag = false;
*res_len = 0;
for (int i = 0; i < strlen(string); i++) {
// Count number of splits
for (int i = 0; i < self->size; i++) {
if (string[i] == c) {
if (flag) {
(*res_len)++;
flag = false;
}
} else {
flag = true;
}
@@ -126,20 +178,17 @@ String_t* str_split(String_t* self, int* res_len, char c) {
(*res_len)++;
}
// allocate memory for an array of pointers to each line
elements = (String_t*)malloc((*res_len) * sizeof(String_t));
String_t* elements = (String_t*)malloc((*res_len) * sizeof(String_t));
int i = 0;
flag = false;
char* start = string;
char* end = string;
char* string_end = string + self->size;
while (*end != '\0') {
while (end < string_end) {
if (*end == c) {
if (flag) {
elements[i] = str_from_slice(start, end - start);
i++;
}
end++;
start = end;
flag = false;
@@ -147,12 +196,10 @@ String_t* str_split(String_t* self, int* res_len, char c) {
end++;
flag = true;
}
} if (flag) {
}
if (flag) {
elements[i] = str_from_slice(start, end - start);
}
// returns an array of String_t
return elements;
}
@@ -176,38 +223,32 @@ String_t* str_lines(String_t* self, int* numlines) {
String_t str_slice(String_t* s, int start, int end) {
String_t s2;
if (start < 0) start = 0;
if (end > s->size) end = s->size;
// if the range is invalid, we return an empty string
if (start >= end || start >= s->size) {
s2.size = 0;
s2.capacity = 1;
s2.data = (char*)calloc(1, sizeof(char));
return s2;
}
s2.size = end - start;
s2.capacity = s2.size;
s2.data = (char*)calloc(s2.size, sizeof(char));
// if the allocation fails we return a zero sized string
if (s2.data == NULL) {
s2.size = 0;
s2.capacity = 0;
return s2;
}
for (int i = 0; i < s2.size; i++) {
s2.data[i] = s->data[start + i];
}
return s2;
}
int str_len(String_t* s) {
return s->size;
}
char* to_chars(String_t* s) {
return s->data;
}
// int main() {
// String_t s = str_from_chars("hello\nworld\neeeee\notherline\n");
// str_remove(&s, 10);
// str_insert(&s, 10, 'h');
// printf("%s\n", to_chars(&s));
// int linen = 0;
// String_t* lines = str_lines(&s, &linen);
// for (int i=0; i < linen; i++) {
// printf("%s", to_chars(&lines[i]));
// }
// str_dealloc(&s);
// return 0;
// }
+167 -12
View File
@@ -1,40 +1,195 @@
#ifndef DYNSTR_H
#define DYNSTR_H
#include <stdio.h>
#include <stdint.h>
typedef struct {
int size;
int capacity;
/**
* Custom String type that stores an array of characters
*/
typedef struct String {
uint32_t size, capacity;
char* data;
} String_t;
/**
* Creates a new string with the specified initial capacity.
* The string will be empty but can hold up to capacity characters
* without requiring reallocation.
*
* @param capacity Initial capacity in bytes
* @return A new String_t structure with allocated memory
*/
String_t str_with_capacity(int capacity);
/**
* Creates a new string from a null-terminated character array.
*
* @param string Null-terminated source string
* @return A new String_t containing a copy of the input string
*/
String_t str_from_chars(char* string);
/**
* Creates a new string from a character array slice.
* Allocates new memory and copies the specified number of characters.
*
* @param string Source character array
* @param len Number of characters to copy
* @return A new String_t containing the copied characters
*/
String_t str_from_slice(char* string, int len);
/**
* Merges two strings into a new string.
* The resulting string contains s1's contents followed by s2's contents.
*
* @param s1 Pointer to the first String_t
* @param s2 Pointer to the second String_t
* @return A new String_t containing the merged contents
*/
String_t str_merge(String_t* s1, String_t* s2);
/**
* Creates a new string by reading from a file.
* Reads the file line by line and concatenates all lines.
*
* @param file Pointer to an open FILE
* @return A new String_t containing the file's contents
*/
String_t str_from_file(FILE* file);
/**
* Creates a new empty string with minimal capacity.
*
* @return A new empty String_t with capacity of 1 byte
*/
String_t str_new();
String_t* str_lines(String_t* self, int* numlines);
/**
* Returns a clone of the string.
* This allocates new memory that must be freed
* @param self Pointer to the String_t to 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.
* The slice includes characters from start index up to (but not including) end index.
*
* @param s Pointer to the source String_t
* @param start Starting index (inclusive)
* @param end Ending index (exclusive)
* @return A new String_t containing the specified slice
*/
String_t str_slice(String_t* s, int start, int end);
// String_t* str_split(String_t* self, int* res_len, char c);
/**
* Splits a string into lines based on newline characters.
* Convenience wrapper around str_split that uses '\n' as delimiter.
*
* @param self Pointer to the String_t to split
* @param numlines Pointer to store the number of resulting lines
* @return Array of String_t structures, one per line
*/
String_t* str_lines(String_t* self, int* numlines);
void str_push(String_t* s, char c);
/**
* Splits a string into an array of strings based on a delimiter.
* Each occurrence of the delimiter creates a new string.
*
* @param self Pointer to the String_t to split
* @param res_len Pointer to store the number of resulting strings
* @param c Delimiter character
* @return Array of String_t structures, must be freed by caller
*/
String_t* str_split(String_t* self, int* res_len, char c);
/**
* Removes and returns the last character in the string.
*
* @param self Pointer to the String_t to modify
* @return The removed character, or '\0' if string is empty
*/
char str_pop(String_t* s);
void str_insert(String_t* s, int index, char c);
void str_remove(String_t* s, int index);
int str_dealloc(String_t* s);
/**
* Returns a pointer to the underlying character array.
* Note: The returned pointer is still owned by the String_t structure.
*
* @param s Pointer to the String_t structure
* @return Pointer to the null-terminated character array
*/
char* to_chars(String_t* s);
/**
* Writes the string's contents to a file.
* Appends a newline character after writing the string.
*
* @param self Pointer to the String_t to write
* @param file Pointer to an open FILE
* @return 0 on success, -1 on failure
*/
int str_to_file(String_t* self, FILE* file);
/**
* Appends a null-terminated string to the end of the string.
* Automatically reallocates if more capacity is needed.
*
* @param self Pointer to the String_t to modify
* @param c Null-terminated string to append
* @return 0 on success
*/
int str_push_str(String_t* string, char* other);
/**
* Deallocates the memory used by a string.
* Must be called when the string is no longer needed to prevent memory leaks.
*
* @param self Pointer to the String_t to deallocate
* @return 0 on success
*/
int str_dealloc(String_t* s);
/**
* Returns the current length of the string.
*
* @param s Pointer to the String_t structure
* @return The number of characters in the string
*/
int str_len(String_t* s);
/**
* Inserts a character at the specified index.
* Shifts existing characters right to make room.
* Automatically reallocates if more capacity is needed.
*
* @param self Pointer to the String_t to modify
* @param index Position to insert the character
* @param c Character to insert
* @return 0 on success, -1 if index is out of bounds
*/
void str_insert(String_t* s, int index, char c);
/**
* Removes the character at the specified index.
* Shifts remaining characters left to fill the gap.
*
* @param self Pointer to the String_t to modify
* @param index Position of character to remove
* @return 0 on success, -1 if index is out of bounds
*/
void str_remove(String_t* s, int index);
/**
* Appends a single character to the end of the string.
* Automatically reallocates if more capacity is needed.
*
* @param self Pointer to the String_t to modify
* @param c Character to append
* @return 0 on success
*/
void str_push(String_t* s, char c);
#endif
+348 -34
View File
@@ -1,59 +1,163 @@
#include <stdbool.h>
#include <stdlib.h>
#include "dynstr.h"
#include "log.h"
#include "state.h"
#include <ncurses.h>
#define min(x, y) ((x) < (y) ? (x) : (y))
#define max(x, y) ((x) > (y) ? (x) : (y))
typedef struct {
uint32_t lines;
uint32_t capacity;
uint32_t buffer_line;
uint32_t buffer_col;
uint32_t y_offset;
uint32_t x_offset;
bool unsaved_changes;
bool editmode;
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;
void add_toolbar(Editor* self) {
int max_x, max_y;
getmaxyx(stdscr, max_y, max_x);
attron(COLOR_PAIR(2));
// add an entry for the current mode
move(max_y - 1, 0);
char mode[8];
char mode[9];
snprintf(mode, 9, "[%6s]", self->editmode ? "Insert" : "Normal");
addstr(mode);
// add an entry for the current line and col
char line_and_col[24];
snprintf(line_and_col, 24, " [ln: %d, col: %d]", self->buffer_line + 1, self->buffer_col + 1);
addstr(line_and_col);
attroff(COLOR_PAIR(2));
// add an entry for unsaved changes
attron(COLOR_PAIR(1));
char unsaved_changes[20];
snprintf(unsaved_changes, 20, " [%s]", self->unsaved_changes ? "Unsaved Changes!" : "No Changes Yet! ");
addstr(unsaved_changes);
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) {
move(0, 0);
clear();
// get the screen size so we can figure out where to place characters
int max_x, max_y;
getmaxyx(stdscr, max_y, max_x);
max_y--; // Reserve bottom line for toolbar
// Ensure y_offset is valid
if (self->buffer_line >= self->y_offset + max_y) {
self->y_offset = self->buffer_line - max_y + 1;
}
if (self->buffer_line < self->y_offset) {
self->y_offset = self->buffer_line;
}
// Display visible lines
for (size_t i = self->y_offset; i < self->lines && i < self->y_offset + max_y; i++) {
// for (size_t i = 0; i < self->lines; i++) {
move(i - self->y_offset, 0);
// adding the line number
char line_no[5];
snprintf(line_no, 5, "%-5d", i + 1);
// adding the line of text to the buffer
attron(COLOR_PAIR(1));
char line_no[6];
snprintf(line_no, 6, "%-4d]", i + 1);
addstr(line_no);
addch(' ');
addstr(to_chars(&self->buffer[i]));
addstr("\n");
attroff(COLOR_PAIR(1));
// Only try to display the line if it's within bounds
if (i < self->lines) {
// Calculate visible portion of line
int visible_start = self->x_offset;
int line_len = str_len(&self->buffer[i]);
int visible_end = min(line_len, self->x_offset + max_x - 5);
if (visible_start < line_len) {
String_t line_segment = str_slice(
&self->buffer[i],
visible_start,
visible_end
);
char* data = to_chars(&line_segment);
addstr(data);
str_dealloc(&line_segment);
free(data);
}
}
}
add_toolbar(self);
refresh_cursor(self);
}
move(self->buffer_line, self->buffer_col + 5);
}
void move_cursor_on_screen(Editor* editor, int x, int y) {
int newx = x + 5; // account for the line number
void move_cursor_on_screen(Editor* self, int x, int y) {
bool offset_changed = false;
int max_x, max_y;
move(y, newx);
getmaxyx(stdscr, max_y, max_x);
max_y--; // Reserve bottom line for toolbar
max_x -= 5; // Account for line number margin
// Adjust x_offset if cursor would be off screen
if (x + 5 >= max_x + self->x_offset) {
self->x_offset = x - max_x + 6;
offset_changed = true;
}
if (x < self->x_offset) {
self->x_offset = x;
offset_changed = true;
}
// Adjust y_offset if cursor would be off screen
if (y >= max_y + self->y_offset) {
self->y_offset = y - max_y + 1;
offset_changed = true;
}
if (y < self->y_offset) {
self->y_offset = y;
offset_changed = true;
}
if (offset_changed) {
refresh_buffer(self);
} else {
refresh_cursor(self);
}
}
void switch_mode(Editor* self) {
@@ -63,15 +167,18 @@ void switch_mode(Editor* self) {
void newline(Editor* self) {
move(self->buffer_line, 0);
char line_no[5];
snprintf(line_no, 5, "%-5d", self->buffer_line + 1);
// adding the line of text to the buffer
char line_no[6];
snprintf(line_no, 6, "%-5d", self->buffer_line + 1);
// add the line number
attron(COLOR_PAIR(1));
addstr(line_no);
addch(' ');
move(self->buffer_line, self->buffer_col + 5);
attroff(COLOR_PAIR(1));
move_cursor_on_screen(self, self->buffer_col, self->buffer_line);
}
Editor editor_from(String_t input_string) {
// instantiates a new editor object given an input string and a filename
Editor editor_from(String_t input_string, String_t filename) {
Editor e;
e.lines = 0;
e.buffer_line = 0;
@@ -79,24 +186,164 @@ Editor editor_from(String_t input_string) {
e.y_offset = 0;
e.x_offset = 0;
e.editmode = false;
e.unsaved_changes = false;
e.filename = filename;
int linenum = 0;
e.buffer = str_lines(&input_string, &linenum);
e.lines = (size_t)linenum;
e.original = str_clone_all(e.buffer, linenum);
e.lines = (uint32_t)linenum;
e.original_len = linenum;
e.capacity = e.lines;
e.diff = arr_init(e.lines);
str_dealloc(&input_string);
refresh_buffer(&e);
return e;
}
Editor new_editor() {
return editor_from(str_new());
// creates a new editor object from a filename with an emptry buffer
Editor new_editor(String_t filename) {
return editor_from(str_new(), filename);
}
// prints the changelog for the editor.
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];
// for each token in the state, we print the corresponding line if there is a change.
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);
}
// saves the current buffer to a file
int save_file(Editor* self) {
String_t log_string = str_new();
char log_message[64];
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
for (size_t i = 0; i < self->lines; i++) {
if (str_to_file(&self->buffer[i], file) == -1) {
free(name);
fprintf(stderr, "Error writing to file\n");
return -1;
}
}
// debug method
// arr_print(&self->diff);
print_diff(self);
free(name);
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);
self->original_len = self->lines;
snprintf(log_message, 64, "Edited file [%s]\n", to_chars(&self->filename));
str_push_str(&log_string, log_message);
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;
}
// move the cursor around the screen
// this uses extensive bounds checking to ensure that the cursor moves to the next line correctly
void move_cursor(Editor* self, int x, int y) {
if (y != 0
&& (int)(self->buffer_line) + y >= 0
&& self->buffer_line + y <= self->lines)
&& self->buffer_line + y < self->lines)
{
// move the cursor up or down
self->buffer_line += y;
@@ -110,37 +357,59 @@ void move_cursor(Editor* self, int x, int y) {
self->buffer_line -= 1;
self->buffer_col = str_len(&self->buffer[self->buffer_line]);
}
} else if (self->buffer_col + x > str_len(&self->buffer[self->buffer_line])) {
} else if (x != 0 && self->buffer_line < self->lines) {
int line_width = str_len(&self->buffer[self->buffer_line]);
if (self->buffer_col + x > line_width) {
// moving the cursor off the right hand side of the line
if (self->buffer_line < self->lines) {
if (self->buffer_line + 1 < self->lines) {
self->buffer_col = 0;
self->buffer_line += 1;
}
} else if (x != 0) {
} else {
// moving the cursor left or right in any other case
self->buffer_col += x;
}
}
move_cursor_on_screen(self, self->buffer_col, self->buffer_line);
}
void delchar(Editor* self) {
if (self->buffer_col == str_len(&self->buffer[self->buffer_line])) {
self->unsaved_changes = true;
if (self->buffer_col == str_len(&self->buffer[self->buffer_line])) {
if (self->buffer_line +1 == self->lines) {
return;
}
self->buffer[self->buffer_line] = str_merge(&self->buffer[self->buffer_line], &self->buffer[self->buffer_line + 1]);
// obtain string objects that reference the old lines.
String_t old_curr_line = self->buffer[self->buffer_line];
String_t old_next_line = self->buffer[self->buffer_line + 1];
// assign the new line
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
str_dealloc(&old_curr_line);
str_dealloc(&old_next_line);
for (size_t i = self->buffer_line + 2; i < self->lines; i++) {
self->buffer[i - 1] = self->buffer[i];
}
// replace the last line
self->buffer[self->lines - 1] = str_new();
self->lines--;
refresh_buffer(self);
} else {
// 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);
delch();
}
@@ -148,9 +417,23 @@ void delchar(Editor* self) {
}
void pressed_enter(Editor* self) {
self->unsaved_changes = true;
// allocate memory immediately since we know a new line is being added
self->lines++;
self->buffer = realloc(self->buffer, sizeof(String_t) * (self->lines));
while (self->lines > self->capacity) {
self->capacity++;
}
self->buffer = (String_t*)realloc(self->buffer, sizeof(String_t) * (self->capacity));
if (self->buffer_col == 0) {
arr_insert(&self->diff, self->buffer_line, ADDED);
} else if (self->buffer_col == str_len(&self->buffer[self->buffer_line])) {
arr_insert(&self->diff, self->buffer_line + 1, ADDED);
} else {
arr_insert(&self->diff, self->buffer_line + 1, ADDED);
}
if (self->buffer_line == self->lines -1) {
// if at end of file add an empty line
@@ -161,6 +444,8 @@ void pressed_enter(Editor* self) {
self->buffer[i] = self->buffer[i - 1];
}
String_t old_line = self->buffer[self->buffer_line];
self->buffer[self->buffer_line + 1] = str_slice(
&self->buffer[self->buffer_line],
self->buffer_col,
@@ -172,6 +457,8 @@ void pressed_enter(Editor* self) {
0,
self->buffer_col
);
str_dealloc(&old_line);
}
// refresh the screen with the new data
refresh_buffer(self);
@@ -180,16 +467,33 @@ void pressed_enter(Editor* self) {
// inserts a character at the cursor
void addchar(Editor* self, char c) {
self->unsaved_changes = true;
if (self->buffer_line == self->lines) {
// 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);
// reallocate self->buffer to be 1 larger
self->lines++;
self->buffer = realloc(self->buffer, sizeof(String_t) * (self->lines));
while (self->lines > self->capacity) {
self->capacity++;
}
self->buffer = (String_t*)realloc(self->buffer, sizeof(String_t) * (self->capacity));
// allocate the memory space for the new line and add it to the buffer
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);
@@ -198,7 +502,17 @@ void addchar(Editor* self, char c) {
move_cursor(self, 1, 0);
}
String_t* to_string(Editor* self) {
// closes the editor and ensures that all used resources are deallocated
void shutdown_editor(Editor* self) {
for (size_t i = 0; i < self->capacity; i++) {
str_dealloc(&self->buffer[i]);
}
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);
}
+81 -2
View File
@@ -1,24 +1,103 @@
#include "dynstr.h"
#include "state.h"
#include <stdint.h>
/**
* maintains the state of a text editor, including the text buffer,
* cursor position, and various editor settings.
*/
typedef struct {
uint32_t lines;
uint32_t capacity;
uint32_t buffer_line;
uint32_t buffer_col;
uint32_t y_offset;
uint32_t x_offset;
bool unsaved_changes;
bool editmode;
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 new_editor();
/**
* Creates a new editor instance from a filename
*
* @param filename The name of the file to open
* @return Editor A new editor instance initialized with the file's content
*/
Editor new_editor(String_t filename);
Editor editor_from(String_t input_string);
/**
* Creates a new editor instance from an input string
*
* @param input_string initial editor content
* @param filename name of the file being edited
* @return an instance of the editor
*/
Editor editor_from(String_t input_string, String_t filename);
/**
* Moves the cursor in the editor (relative to it's current position)
*
* @param self Pointer to the editor instance
* @param x Horizontal movement (negative for left, positive for right)
* @param y Vertical movement (negative for up, positive for down)
*/
void move_cursor(Editor* self, int x, int y);
/**
* Deletes the character before the cursor
*
* @param self Pointer to the editor instance
*/
void delchar(Editor* self);
/**
* Adds a character at the cursor position
*
* @param self Pointer to the editor instance
* @param c The character to add
*/
void addchar(Editor* self, char c);
/**
* Handles the enter key press, creating a new line
*
* @param self Pointer to the editor instance
*/
void pressed_enter(Editor* self);
/**
* Toggles between edit and command mode
*
* @param self Pointer to the editor instance
*/
void switch_mode(Editor* self);
/**
* Cleans up and frees resources used by the editor
*
* @param self Pointer to the editor instance
*/
void shutdown_editor(Editor* self);
/**
* Saves the current buffer content to the file
*
* @param self Pointer to the editor instance
* @return int Returns 0 on success, non-zero on failure
*/
int save_file(Editor* self);
/**
* Converts the editor's buffer content to a String_t type
*
* @param self Pointer to the editor instance
* @return String_t* Pointer to a string containing the buffer content
*/
String_t* to_string(Editor* self);
+18
View File
@@ -0,0 +1,18 @@
#include "dynstr.h"
int write_log(String_t* string) {
FILE* file = fopen("log.txt", "a");
int numlines;
String_t* lines = str_lines(string, &numlines);
for (int i = 0; i < numlines; i++) {
str_to_file(&lines[i], file);
}
fclose(file);
return 0;
}
+12
View File
@@ -0,0 +1,12 @@
#include "dynstr.h"
/**
* Writes the string to a file, appending a newline character
* This function automatically appends a newline character to the end of the string
*
* @param string the string to write
* @return 0 on success, -1 on error
* @note This function frees the string passed to it!
*/
int write_log(String_t* string);
+56 -1
View File
@@ -1 +1,56 @@
Fatal glibc error: malloc.c:2599 (sysmalloc): assertion failed: (old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)
ADDED 18 |
ADDED 19 |
ADDED 20 |
Edited file [somefile.c]
Old Length: [254] New Length: [254]
REMOVED 18 |
REMOVED 19 |
REMOVED 20 |
Edited file [somefile.c]
Old Length: [251] New Length: [251]
ADDED 42 |
ADDED 43 | fn main() -> idk {
ADDED 44 | print("")
ADDED 46 |
Edited file [somefile.c]
Old Length: [255] New Length: [255]
REMOVED 42 |
MODIFIED 43 | fn main() -> idk {
-> 42 |
MODIFIED 44 | print("")
-> 43 |
MODIFIED 45 | }
-> 44 | String_t str_from_chars(const char* string) {
Edited file [somefile.c]
Old Length: [252] New Length: [252]
Opened File [somefile.c]
ADDED 28 |
REMOVED 28 |
Edited file [somefile.c]
Old Length: [252] New Length: [252]
ADDED 42 |
REMOVED 42 |
Edited file [somefile.c]
Old Length: [252] New Length: [252]
ADDED 47 |
ADDED 48 |
Edited file [somefile.c]
Old Length: [254] New Length: [254]
MODIFIED 48 |
-> 48 | fn some() -> idk {
ADDED 49 | print(idk)
ADDED 50 | }
Edited file [somefile.c]
Old Length: [256] New Length: [256]
Opened File [somefile.c]
ADDED 28 |
REMOVED 28 |
Edited file [somefile.c]
Old Length: [256] New Length: [256]
ADDED 28 |
ADDED 29 |
ADDED 30 |
ADDED 31 |
Edited file [somefile.c]
Old Length: [260] New Length: [260]
Opened File [somefile.c]
+195 -33
View File
@@ -1,20 +1,23 @@
#include <ncurses.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "editor.h"
#include "log.h"
void help() {
printf("Usage:\n");
printf(" cmd open <path/to/file> // opens the specified file\n");
printf(" cmd rm <path/to/file> // deletes the specified file\n");
printf(" cmd new <path/to/file> // creates a new empty file at the specified path\n");
printf(" cmd mv <path/to/file> <new/path> // moves the specified file to the new path\n");
printf(" cmd cp <path/to/file> <new/path> // copies the specified file to the new path\n");
printf(" cmd len <path/to/file> // returns the length of the specified file\n");
printf(" cmd open <file/path> // opens the specified file\n");
printf(" cmd rm <file/path> // deletes the specified file\n");
printf(" cmd new <file/path> // creates a new empty file at the specified path\n");
printf(" cmd mv <old/path> <new/path> // moves the specified file to the new path\n");
printf(" cmd cp <old/path> <new/path> // copies the specified file to the new path\n");
printf(" cmd lenl <file/path> // returns the length of the specified file in lines\n");
printf(" cmd lenc <file/path> // returns the length of the specified file in chars\n");
printf(" cmd log // prints a list of all changes made to the file\n");
}
int open_editor() {
int open_editor(char* filename) {
initscr();
raw();
noecho();
@@ -22,33 +25,20 @@ int open_editor() {
int max_y, max_x;
getmaxyx(stdscr, max_y, max_x);
start_color();
use_default_colors();
FILE* file = fopen("somecode.c", "r");
if (file == NULL) {
endwin();
printf("Failed to open file\n");
return 1;
}
init_pair(1, COLOR_CYAN, -1);
init_pair(2, COLOR_MAGENTA, -1);
String_t text = str_new();
// read from file char by char
while (true) {
char line[1024];
if (fgets(line, 1024, file) == NULL) {
break;
}
String_t new_text = str_from_chars(line);
text = str_merge(&text, &new_text);
str_dealloc(&new_text);
}
fclose(file);
Editor editor = editor_from(text);
move(0, 5);
Editor editor;
if (strcmp(filename, "") == 0) {
editor = new_editor(str_from_chars(filename));
} else {
editor = editor_from(str_from_file(fopen(filename, "r")), str_from_chars(filename));
}
while (true) {
refresh();
int c = getch();
@@ -80,7 +70,6 @@ int open_editor() {
move_cursor(&editor, 1, 0);
break;
default:
fprintf(stderr, "%d\n", c);
addchar(&editor, c);
break;
}
@@ -88,11 +77,15 @@ int open_editor() {
switch (c) {
case 'q':
endwin();
shutdown_editor(&editor);
return 0;
case 'i':
switch_mode(&editor);
break;
case 'w':
editor.unsaved_changes = false;
save_file(&editor);
// TODO: write function to save the data to a file
break;
default:
@@ -102,8 +95,177 @@ int open_editor() {
}
}
int main() {
open_editor();
// Copies a file from source to destination
int copy_file(const char* source, const char* dest) {
FILE* src = fopen(source, "rb");
if (src == NULL) {
fprintf(stderr, "Error: Could not open source file %s\n", source);
return -1;
}
FILE* dst = fopen(dest, "wb");
if (dst == NULL) {
fclose(src);
fprintf(stderr, "Error: Could not create destination file %s\n", dest);
return -1;
}
char buffer[8192];
size_t bytes;
while ((bytes = fread(buffer, 1, sizeof(buffer), src)) > 0) {
if (fwrite(buffer, 1, bytes, dst) != bytes) {
fclose(src);
fclose(dst);
fprintf(stderr, "Error: Write failed to %s\n", dest);
return -1;
}
}
fclose(src);
fclose(dst);
return 0;
}
// Counts the number of characters in a file
int count_chars(const char* filename) {
FILE* file = fopen(filename, "r");
if (file == NULL) {
fprintf(stderr, "Error: Could not open file %s\n", filename);
return -1;
}
int count = 0;
int ch;
while ((ch = fgetc(file)) != EOF) {
count++;
}
fclose(file);
return count;
}
// Counts the number of lines in a file
int count_lines(const char* filename) {
FILE* file = fopen(filename, "r");
if (file == NULL) {
fprintf(stderr, "Error: Could not open file %s\n", filename);
return -1;
}
int count = 0;
int ch;
int last_char = '\n'; // Handle empty files as 0 lines
while ((ch = fgetc(file)) != EOF) {
if (ch == '\n') {
count++;
}
last_char = ch;
}
// Count last line if it doesn't end with newline
if (last_char != '\n' && last_char != EOF) {
count++;
}
fclose(file);
return count;
}
// Command types
typedef enum {
CMD_OPEN,
CMD_RM,
CMD_NEW,
CMD_MV,
CMD_CP,
CMD_LENL,
CMD_LENC,
CMD_LOG,
CMD_UNKNOWN
} CommandType;
// Convert string to command type
CommandType str_to_cmd(const char* cmd) {
if (strcmp(cmd, "open") == 0) return CMD_OPEN;
if (strcmp(cmd, "rm") == 0) return CMD_RM;
if (strcmp(cmd, "new") == 0) return CMD_NEW;
if (strcmp(cmd, "mv") == 0) return CMD_MV;
if (strcmp(cmd, "cp") == 0) return CMD_CP;
if (strcmp(cmd, "lenl") == 0) return CMD_LENL;
if (strcmp(cmd, "lenc") == 0) return CMD_LENC;
if (strcmp(cmd, "log") == 0) return CMD_LOG;
return CMD_UNKNOWN;
}
int main(int argc, char* argv[]) {
char log_fmt_string[128];
if (argc < 2) {
help();
return 1;
}
CommandType cmd = str_to_cmd(argv[1]);
if (argc == 3) {
switch (cmd) {
case CMD_OPEN:
sprintf(log_fmt_string, "Opened File [%s]", argv[2]);
open_editor(argv[2]);
break;
case CMD_RM:
remove(argv[2]);
sprintf(log_fmt_string, "Deleted File [%s]", argv[2]);
break;
case CMD_NEW:
sprintf(log_fmt_string, "Created File [%s]", argv[2]);
open_editor(argv[2]);
break;
case CMD_LENC:
printf("%d\n", count_chars(argv[2]));
break;
case CMD_LENL:
printf("%d\n", count_lines(argv[2]));
break;
default:
help();
}
}
else if (argc == 4) {
switch (cmd) {
case CMD_MV:
if (rename(argv[2], argv[3]) != 0) {
fprintf(stderr, "Error: Failed to move file %s to %s\n", argv[2], argv[3]);
} else {
sprintf(log_fmt_string, "Moved file [%s] To [%s]", argv[2], argv[3]);
}
break;
case CMD_CP:
if (copy_file(argv[2], argv[3]) != 0) {
fprintf(stderr, "Error: Failed to copy file %s to %s\n", argv[2], argv[3]);
sprintf(log_fmt_string, "Failed to copy file [%s] To [%s]", argv[2], argv[3]);
} else {
sprintf(log_fmt_string, "Copied file [%s] To [%s]", argv[2], argv[3]);
}
break;
default:
help();
}
}
else {
printf("invalid options\n");
help();
}
// log output of command
if (log_fmt_string[0] != '\0') {
String_t str = str_from_chars(log_fmt_string);
write_log(&str);
str_dealloc(&str);
}
return 0;
}
-213
View File
@@ -1,213 +0,0 @@
/// dynamic array class
/// written by: Harry Irving
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
typedef struct {
uint32_t size, capacity;
char* data;
} String_t;
String_t str_with_capacity(int capacity) {
String_t s;
/// allocate memory for 'capacity' chars
s.data = (char*)calloc(capacity, sizeof(char));
s.size = 0;
s.capacity = capacity;
return s;
}
String_t str_from_chars(const char* string) {
String_t s;
s.data = (char*)calloc(strlen(string), sizeof(char));
strcpy(s.data, string);
s.size = strlen(string);
s.capacity = strlen(string);
return s;
}
String_t str_from_slice(const char* string, int len) {
String_t s;
s.data = (char*)calloc(len, sizeof(char));
strncpy(s.data, string, len);
s.size = len;
s.capacity = len;
return s;
}
String_t str_new() {
return str_with_capacity(1);
}
int str_dealloc(String_t* self) {
free(self->data);
return 0;
}
int str_push(String_t* self, char c) {
// check size < capacity
if (self->size < self->capacity) {
self->data[self->size] = c;
self->size++;
return 0;
}
// reallocate to add capacity for an extra char
int newcap = self->capacity + sizeof(char);
self->data = (char*)realloc(self->data, newcap);
self->data[self->size] = c;
self->size++;
self->capacity = newcap;
return 0;
}
char str_pop(String_t* self) {
if (self->size == 0) {
return '\0';
}
self->size--;
char c = self->data[self->size];
return c;
}
int str_insert(String_t* self, int index, char c) {
if (index > self->size) {
return -1;
}
self->size++;
for (int i = self->size - 1; i > index; i--) {
self->data[i] = self->data[i - 1];
}
self->data[index] = c;
return 0;
}
int str_remove(String_t* self, int index) {
if (index >= self->size) {
return -1;
}
self->size--;
for (int i = index; i < self->size; i++) {
self->data[i] = self->data[i + 1];
}
return 0;
}
/// splits a string into an array of strings based on a delimiter
String_t* str_split(String_t* self, int* res_len, char c) {
char* string = self->data;
String_t* elements = NULL;
// find the number of lines in the file
bool flag = false;
*res_len = 0;
for (int i = 0; i < strlen(string); i++) {
if (string[i] == c) {
if (flag) {
(*res_len)++;
flag = false;
}
} else {
flag = true;
}
}
if (flag) {
(*res_len)++;
}
// allocate memory for an array of pointers to each line
elements = (String_t*)malloc((*res_len) * sizeof(String_t));
int i = 0;
flag = false;
char* start = string;
char* end = string;
while (*end != '\0') {
if (*end == c) {
if (flag) {
elements[i] = str_from_slice(start, end - start);
i++;
}
end++;
start = end;
flag = false;
} else {
end++;
flag = true;
}
} if (flag) {
elements[i] = str_from_slice(start, end - start);
}
// returns an array of String_t
return elements;
}
String_t str_merge(String_t* s1, String_t* s2) {
String_t s;
s.size = s1->size + s2->size;
s.capacity = s.size;
s.data = (char*)calloc(s.size, sizeof(char));
for (int i = 0; i < s1->size; i++) {
s.data[i] = s1->data[i];
}
for (int i = 0; i < s2->size; i++) {
s.data[s1->size + i] = s2->data[i];
}
return s;
}
String_t* str_lines(String_t* self, int* numlines) {
return str_split(self, numlines, '\n');
}
String_t str_slice(String_t* s, int start, int end) {
String_t s2;
s2.size = end - start;
s2.capacity = s2.size;
s2.data = (char*)calloc(s2.size, sizeof(char));
for (int i = 0; i < s2.size; i++) {
s2.data[i] = s->data[start + i];
}
return s2;
}
int str_len(String_t* s) {
return s->size;
}
char* to_chars(String_t* s) {
return s->data;
}
// int main() {
// String_t s = str_from_chars("hello\nworld\neeeee\notherline\n");
// str_remove(&s, 10);
// str_insert(&s, 10, 'h');
// printf("%s\n", to_chars(&s));
// int linen = 0;
// String_t* lines = str_lines(&s, &linen);
// for (int i=0; i < linen; i++) {
// printf("%s", to_chars(&lines[i]));
// }
// str_dealloc(&s);
// return 0;
// }
+161
View File
@@ -0,0 +1,161 @@
/// 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) {
// creates a new array to store the state
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;
}
// prints the state of the array into a file
// this method is for debug purposes!
// 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);
// }
// calculates an offset to use for indexing into the state relative to the buffer.
// this is because some elements might not be aligned due to needing to insert the "REMOVED" element in some cases.
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) {
// // every time a "REMOVED" element is found, we increase the index by 1
// index++;
// }
// count++;
// }
if (self->data[i] == REMOVED) {
// every time a "REMOVED" element is found, we increase the index by 1
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
View File
@@ -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
-4
View File
@@ -1,4 +0,0 @@
+3
View File
@@ -15,6 +15,9 @@ fn somefunc() {
let c2 = String::from("hello world");
}
// some comment
struct EditorData {
buffer: Vec<Vec<char>>, // outer vec is the line, inner is col;
file_line: i32,