Switch to c++

This commit is contained in:
2025-12-07 17:22:12 +00:00
parent 2927df7710
commit 7acf35c8ea
55 changed files with 3506 additions and 304 deletions

View File

111
include/editor.h Normal file
View File

@@ -0,0 +1,111 @@
#pragma once
#include "../libs/tree-sitter/lib/include/tree_sitter/api.h"
#include "./rope.h"
#include "./ui.h"
#include "./utils.h"
#include <cstdint>
#include <map>
#include <shared_mutex>
struct Highlight {
uint32_t fg;
uint32_t bg;
uint32_t flags;
uint8_t priority;
};
struct Span {
uint32_t start;
uint32_t end;
Highlight *hl;
bool operator<(const Span &other) const { return start < other.start; }
};
struct SpanCursor {
const std::vector<Span> &spans;
size_t index = 0;
std::vector<Span *> active;
SpanCursor(const std::vector<Span> &s) : spans(s) {}
Highlight *get_highlight(uint32_t byte_offset) {
for (int i = active.size() - 1; i >= 0; i--)
if (active[i]->end <= byte_offset)
active.erase(active.begin() + i);
while (index < spans.size() && spans[index].start <= byte_offset) {
if (spans[index].end > byte_offset)
active.push_back(const_cast<Span *>(&spans[index]));
index++;
}
Highlight *best = nullptr;
int max_prio = -1;
for (auto *s : active) {
if (s->hl->priority > max_prio) {
max_prio = s->hl->priority;
best = s->hl;
}
}
return best;
}
void sync(uint32_t byte_offset) {
active.clear();
size_t left = 0, right = spans.size();
while (left < right) {
size_t mid = (left + right) / 2;
if (spans[mid].start <= byte_offset)
left = mid + 1;
else
right = mid;
}
index = left;
while (left > 0) {
left--;
if (spans[left].end > byte_offset)
active.push_back(const_cast<Span *>(&spans[left]));
else if (byte_offset - spans[left].end > 1000)
break;
}
}
};
struct Editor {
const char *filename; // Filename of the editor
Knot *root; // A rope
std::shared_mutex knot_mtx; // A mutex
std::shared_mutex span_mtx; // A mutex
Coord cursor; // position of the cursor
uint32_t cursor_preffered; // preffered visual column
Coord selection; // position of the selection
bool selection_active; // true if there is a selection
Coord position; // Position of the editor
Coord size; // Size of the editor
Coord scroll; // Position of the scroll
TSTree *tree; // Tree-sitter tree
TSParser *parser; // Tree-sitter parser
TSQuery *query; // Tree-sitter query
const TSLanguage *language; // Tree-sitter language
Queue<TSInputEdit> edit_queue; // Tree-sitter edit queue
std::vector<Highlight> query_map; // Tree-sitter query map
int *folded; // folded lines indexed by line number - cached form of
// folded_node
std::vector<Span> spans;
std::map<uint32_t, bool> folded_node; // maps content hash to fold state
// - built by tree-sitter
};
typedef struct TSLoad {
Editor *editor;
char *prev = nullptr;
} TSLoad;
Editor *new_editor(const char *filename, Coord position, Coord size);
void free_editor(Editor *editor);
void render_editor(Editor *editor);
void fold(Editor *editor, uint32_t start_line, uint32_t end_line);
void scroll_up(Editor *editor, uint32_t lines);
void scroll_down(Editor *editor, uint32_t lines);
void cursor_up(Editor *editor, uint32_t number);
void cursor_down(Editor *editor, uint32_t number);
void cursor_left(Editor *editor, uint32_t number);
void cursor_right(Editor *editor, uint32_t number);
void ensure_scroll(Editor *editor);

157
include/rope.h Normal file
View File

@@ -0,0 +1,157 @@
#ifndef ROPE_HPP
#define ROPE_HPP
#include <cstdint>
#include <vector>
#define MIN_CHUNK_SIZE 64 // 64 Bytes
#define MAX_CHUNK_SIZE 1024 * 8 // 8192 Bytes (8 KiB)
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define DEPTH(n) ((n) ? (n)->depth : 0)
#define PCRE2_CODE_UNIT_WIDTH 8
#define PCRE_WORKSPACE_SIZE 512
// Rope node definition
typedef struct Knot {
Knot *left;
Knot *right;
uint8_t depth;
uint32_t chunk_size;
uint32_t line_count;
uint32_t char_count;
char data[];
} Knot;
typedef struct LineIterator {
Knot *node;
uint8_t top;
uint32_t offset;
uint32_t line;
Knot *stack[64];
} LineIterator;
typedef struct LeafIterator {
Knot *node;
uint8_t top;
uint32_t offset;
Knot *stack[64];
} LeafIterator;
typedef struct ByteIterator {
LeafIterator *it;
uint32_t offset_l;
uint32_t offset_g;
uint32_t char_count;
char *data;
} ByteIterator;
// Rope operations
// Takes lengt of string to be converted
// to rope and returns a suitable chunk size
// but rope should work with any positive chunk size
uint32_t optimal_chunk_size(uint64_t length);
// Takes a string (no need for null termination) and returns a rope
// len is the length of the string, and chunk size is the size of each chunk
// load does not free or consume the string.
// and the str can be freed after load has been run.
Knot *load(char *str, uint32_t len, uint32_t chunk_size);
// Balances the rope and returns the root
// n is no longer valid / do not free
// As rope is balanced by other functions
// this is not to be used directly
Knot *balance(Knot *n);
// Concatenates two ropes and returns the joined root
// Balances the ropes too, if needed
// left and right are no longer valid / do not free
// ! left and right should have the same chunk size !
Knot *concat(Knot *left, Knot *right);
// Used to insert text into the rope
// node (the rope being inserted into) is no longer valid after call
// instead use return value as the new node
// offset is the position of the insertion relative to the start of the rope
// str is the string to be inserted (no need for null termination)
// len is the length of the string
Knot *insert(Knot *node, uint32_t offset, char *str, uint32_t len);
// Similar to insert but for deletion
// node (the rope being deleted from) is no longer valid after call
// instead use return value as the new node
// offset is the position of the deletion relative to the start of the rope
// len is the length of the deletion
Knot *erase(Knot *node, uint32_t offset, uint32_t len);
// Used to read a string from the rope
// root is the rope to be read from
// offset is the position of the read relative to the start of the rope
// len is the length of the read
// returns a null terminated string, should be freed by the caller
char *read(Knot *root, uint32_t offset, uint32_t len);
// Used to split the rope into left and right ropes
// node is the rope to be split (it is no longer valid after call / do not free)
// offset is the position of the split relative to the start of the rope
// left and right are pointers set to the root of that side of the split
void split(Knot *node, uint32_t offset, Knot **left, Knot **right);
// Used to convert a byte offset to a line number that contains that byte
uint32_t byte_to_line(Knot *node, uint32_t offset);
// Used to convert a line number to a byte offset (start of the line)
// also sets out_len to the length of the line
uint32_t line_to_byte(Knot *node, uint32_t line, uint32_t *out_len);
// Used to start a line iterator from the start_line number
// root is the root of the rope
// returned iterator must be freed after iteration is done
LineIterator *begin_l_iter(Knot *root, uint32_t start_line);
// Each subsequent call returns the next line as a null terminated string
// `it` is the iterator returned from begin_l_iter
// After getting the necessary lines free the iterator (no need to go upto the
// end) returns null if there are no more lines All return strings `must` be
// freed by the caller
char *next_line(LineIterator *it);
// Used to start an iterator over leaf data
// root is the root of the rope
// the caller must free the iterator after use
LeafIterator *begin_k_iter(Knot *root);
// Returns the next leaf data as a null terminated string
// `it` is the iterator returned from begin_k_iter
// ! Strings returned must never be freed by the caller !
// to mutate the string a copy must be made
char *next_leaf(LeafIterator *it);
// Used to start an iterator over byte data (one byte at a time)
// Uses leaf iterator internally
// root is the root of the rope, the caller must free the iterator after use
ByteIterator *begin_b_iter(Knot *root);
// Returns the next byte from the iterator
// Returns '\0' if there are no more bytes left
// `it` is the iterator returned from begin_b_iter
char next_byte(ByteIterator *it);
// Used to search for a pattern in the rope
// Pattern is a null terminated string representing a regular expression (DFA
// compliant) I.e some forms of backtracking etc. are not supported
// root is the root of the rope to be searched
// Returns a vector of pairs of start and length offsets (in bytes)
std::vector<std::pair<size_t, size_t>> search_rope(Knot *root,
const char *pattern);
// Helper function to free the rope
// root is the root of the rope
// the root is no longer valid after call
// This must be called only once when the rope is no longer needed
void free_rope(Knot *root);
#endif // ROPE_HPP

7
include/ts.h Normal file
View File

@@ -0,0 +1,7 @@
#pragma once
#include "./editor.h"
#define HEX(s) (static_cast<uint32_t>(std::stoul(s, nullptr, 16)))
TSQuery *load_query(const char *query_path, Editor *editor);
void ts_collect_spans(Editor *editor);

106
include/ui.h Normal file
View File

@@ -0,0 +1,106 @@
#pragma once
#include <atomic>
#include <cstdint>
#include <mutex>
#include <string.h>
#include <string>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
#include <vector>
#define KEY_CHAR 0
#define KEY_SPECIAL 1
#define KEY_MOUSE 2
#define KEY_NONE 3
#define KEY_UP 0
#define KEY_DOWN 1
#define KEY_LEFT 2
#define KEY_RIGHT 3
#define KEY_DELETE 4
#define KEY_ESC '\x1b'
#define PRESS 0
#define RELEASE 1
#define DRAG 2
#define SCROLL 3
#define LEFT_BTN 0
#define MIDDLE_BTN 1
#define RIGHT_BTN 2
#define SCROLL_BTN 3
#define NONE_BTN 4
#define SCROLL_UP 0
#define SCROLL_DOWN 1
#define SCROLL_LEFT 2
#define SCROLL_RIGHT 3
#define ALT 1
#define CNTRL 2
#define CNTRL_ALT 3
#define SHIFT 4
const char VS16_BYTE_A = '\xEF';
const char VS16_BYTE_B = '\xB8';
const char VS16_BYTE_C = '\x8F';
enum CellFlags : uint8_t {
CF_NONE = 0,
CF_ITALIC = 1 << 0,
CF_BOLD = 1 << 1,
CF_UNDERLINE = 1 << 2,
};
struct ScreenCell {
std::string utf8 = std::string(""); // empty => no content
uint32_t fg = 0;
uint32_t bg = 0;
uint8_t flags = CF_NONE;
};
struct Coord {
uint32_t row;
uint32_t col;
};
struct KeyEvent {
uint8_t key_type;
char c;
uint8_t special_key;
uint8_t special_modifier;
uint8_t mouse_x;
uint8_t mouse_y;
uint8_t mouse_button;
uint8_t mouse_state;
uint8_t mouse_direction;
uint8_t mouse_modifier;
};
extern uint32_t rows, cols;
extern std::vector<ScreenCell> screen; // size rows*cols
extern std::vector<ScreenCell> old_screen;
extern std::mutex screen_mutex;
extern std::atomic<bool> running;
void get_terminal_size();
void die(const char *s);
void enable_raw_mode();
void disable_raw_mode();
Coord start_screen();
void end_screen();
void update(uint32_t row, uint32_t col, const char *utf8, uint32_t fg,
uint32_t bg, uint8_t flags);
void set_cursor(int row, int col, int show_cursor_param);
void render();
Coord get_size();
int read_input(char *buf, size_t buflen);
KeyEvent read_key();
int display_width(const char *str);

32
include/utils.h Normal file
View File

@@ -0,0 +1,32 @@
#include <mutex>
#include <queue>
#include <string>
template <typename T> struct Queue {
std::queue<T> q;
std::mutex m;
void push(T val) {
std::lock_guard<std::mutex> lock(m);
q.push(val);
}
bool pop(T &val) {
std::lock_guard<std::mutex> lock(m);
if (q.empty())
return false;
val = q.front();
q.pop();
return true;
}
bool empty() {
std::lock_guard<std::mutex> lock(m);
return q.empty();
}
};
uint32_t grapheme_strlen(const char *s);
uint32_t get_visual_col_from_bytes(const char *line, uint32_t byte_limit);
uint32_t get_bytes_from_visual_col(const char *line,
uint32_t target_visual_col);
void log(const char *fmt, ...);
std::string get_exe_dir();