Compare commits

...

3 Commits

Author SHA1 Message Date
9900b83871 Minor fixes 2025-12-09 23:18:06 +00:00
cacda354c2 Remove repeated c submodule entry 2025-12-09 23:15:12 +00:00
6598bb941b Add other tree-sitter languages 2025-12-09 23:12:32 +00:00
8 changed files with 130 additions and 55 deletions

52
.gitmodules vendored
View File

@@ -1,16 +1,56 @@
[submodule "libs/libgrapheme"]
path = libs/libgrapheme
url = https://git.suckless.org/libgrapheme.git
ignore = dirty
; tree-sitter
[submodule "libs/tree-sitter"] [submodule "libs/tree-sitter"]
path = libs/tree-sitter path = libs/tree-sitter
url = https://github.com/tree-sitter/tree-sitter.git url = https://github.com/tree-sitter/tree-sitter.git
ignore = dirty ignore = dirty
; Tree-sitter languages
[submodule "libs/tree-sitter-ruby"]
path = libs/tree-sitter-ruby
url = https://github.com/tree-sitter/tree-sitter-ruby.git
ignore = dirty
[submodule "libs/tree-sitter-c"] [submodule "libs/tree-sitter-c"]
path = libs/tree-sitter-c path = libs/tree-sitter-c
url = https://github.com/tree-sitter/tree-sitter-c.git url = https://github.com/tree-sitter/tree-sitter-c.git
ignore = dirty ignore = dirty
[submodule "libs/tree-sitter-ruby"] [submodule "libs/tree-sitter-cpp"]
path = libs/tree-sitter-ruby path = libs/tree-sitter-cpp
url = https://github.com/tree-sitter/tree-sitter-ruby.git url = https://github.com/tree-sitter/tree-sitter-cpp.git
ignore = dirty ignore = dirty
[submodule "libs/libgrapheme"] [submodule "libs/tree-sitter-css"]
path = libs/libgrapheme path = libs/tree-sitter-css
url = https://git.suckless.org/libgrapheme.git url = https://github.com/tree-sitter/tree-sitter-css.git
ignore = dirty
[submodule "libs/tree-sitter-html"]
path = libs/tree-sitter-html
url = https://github.com/tree-sitter/tree-sitter-html.git
ignore = dirty
[submodule "libs/tree-sitter-javascript"]
path = libs/tree-sitter-javascript
url = https://github.com/tree-sitter/tree-sitter-javascript.git
ignore = dirty
[submodule "libs/tree-sitter-json"]
path = libs/tree-sitter-json
url = https://github.com/tree-sitter/tree-sitter-json.git
ignore = dirty
[submodule "libs/tree-sitter-python"]
path = libs/tree-sitter-python
url = https://github.com/tree-sitter/tree-sitter-python.git
ignore = dirty
[submodule "libs/tree-sitter-haskell"]
path = libs/tree-sitter-haskell
url = https://github.com/tree-sitter/tree-sitter-haskell.git
ignore = dirty
[submodule "libs/tree-sitter-go"]
path = libs/tree-sitter-go
url = https://github.com/tree-sitter/tree-sitter-go.git
ignore = dirty
[submodule "libs/tree-sitter-bash"]
path = libs/tree-sitter-bash
url = https://github.com/tree-sitter/tree-sitter-bash.git
ignore = dirty ignore = dirty

View File

@@ -29,7 +29,6 @@ LIBS := \
libs/tree-sitter-ruby/libtree-sitter-ruby.a \ libs/tree-sitter-ruby/libtree-sitter-ruby.a \
-lpcre2-8 -lpcre2-8
SRC := $(wildcard $(SRC_DIR)/*.cc) SRC := $(wildcard $(SRC_DIR)/*.cc)
OBJ_DEBUG := $(patsubst $(SRC_DIR)/%.cc,$(OBJ_DIR)/debug/%.o,$(SRC)) OBJ_DEBUG := $(patsubst $(SRC_DIR)/%.cc,$(OBJ_DIR)/debug/%.o,$(SRC))
OBJ_RELEASE := $(patsubst $(SRC_DIR)/%.cc,$(OBJ_DIR)/release/%.o,$(SRC)) OBJ_RELEASE := $(patsubst $(SRC_DIR)/%.cc,$(OBJ_DIR)/release/%.o,$(SRC))
@@ -53,7 +52,6 @@ $(TARGET_RELEASE): $(OBJ_RELEASE) $(UNICODE_OBJ_RELEASE)
mkdir -p $(BIN_DIR) mkdir -p $(BIN_DIR)
$(CXX_RELEASE) $(CFLAGS_RELEASE) -o $@ $^ $(LIBS) $(CXX_RELEASE) $(CFLAGS_RELEASE) -o $@ $^ $(LIBS)
# Pattern rules for object files + dependency generation
$(OBJ_DIR)/debug/%.o: $(SRC_DIR)/%.cc $(OBJ_DIR)/debug/%.o: $(SRC_DIR)/%.cc
mkdir -p $(dir $@) mkdir -p $(dir $@)
$(CXX_DEBUG) $(CFLAGS_DEBUG) -MMD -MP -c $< -o $@ $(CXX_DEBUG) $(CFLAGS_DEBUG) -MMD -MP -c $< -o $@
@@ -70,7 +68,6 @@ $(OBJ_DIR)/release/unicode_width/%.o: libs/unicode_width/%.c
mkdir -p $(dir $@) mkdir -p $(dir $@)
$(CXX_RELEASE) $(CFLAGS_RELEASE) -MMD -MP -c $< -o $@ $(CXX_RELEASE) $(CFLAGS_RELEASE) -MMD -MP -c $< -o $@
# Include deps if they exist
DEP_DEBUG += $(UNICODE_OBJ_DEBUG:.o=.d) DEP_DEBUG += $(UNICODE_OBJ_DEBUG:.o=.d)
DEP_RELEASE += $(UNICODE_OBJ_RELEASE:.o=.d) DEP_RELEASE += $(UNICODE_OBJ_RELEASE:.o=.d)

View File

@@ -3,3 +3,8 @@ Copyright 2025 Syed Daanish
# Crib # Crib
A TUI IDE. A TUI IDE.
# TODO
- [ ] VERY HIGH PRIORITY: fix bug when moving upwards into wrapped line (cuses random line amongst the wrapped lines to be visually focused while in reality the top is selected), has be somewhere in `ensure_scroll` function.
- [ ] Add support for wide characters with wrapping.

View File

@@ -142,17 +142,17 @@
((identifier) @keyword.modifier ((identifier) @keyword.modifier
(#match? @keyword.modifier "^(private|protected|public)$" )) (#match? @keyword.modifier "^(private|protected|public)$" ))
;; #fbb152 #000000 0 0 0 1 ;; #fbb152 #000000 0 0 0 3
(program (program
(call (call
(identifier) @keyword.import) (identifier) @keyword.import)
(#match? @keyword.import "^(require|require_relative|load)$")) (#match? @keyword.import "^(require|require_relative|load)$"))
;; #fbb152 #000000 0 0 0 2 ;; #fbb152 #000000 0 0 0 4
((identifier) @constant.builtin ((identifier) @constant.builtin
(#match? @constant.builtin "^(__callee__|__dir__|__id__|__method__|__send__|__ENCODING__|__FILE__|__LINE__)$" )) (#match? @constant.builtin "^(__callee__|__dir__|__id__|__method__|__send__|__ENCODING__|__FILE__|__LINE__)$" ))
;; #aad84c #000000 0 0 0 1 ;; #aad84c #000000 0 0 0 3
((identifier) @function.builtin ((identifier) @function.builtin
(#match? @function.builtin "^(attr_reader|attr_writer|attr_accessor|module_function)$" )) (#match? @function.builtin "^(attr_reader|attr_writer|attr_accessor|module_function)$" ))

View File

@@ -31,31 +31,6 @@ struct Spans {
Queue<std::pair<uint32_t, int64_t>> edits; Queue<std::pair<uint32_t, int64_t>> edits;
bool mid_parse = false; bool mid_parse = false;
std::shared_mutex mtx; std::shared_mutex mtx;
void apply(uint32_t x, int64_t y) {
std::unique_lock lock(mtx);
auto it = std::lower_bound(
spans.begin(), spans.end(), Span{.start = x, .end = 0, .hl = nullptr},
[](auto &a, auto &b) { return a.start < b.start; });
while (it != spans.begin()) {
auto prev = std::prev(it);
if (prev->end <= x)
break;
it = prev;
}
while (it != spans.end()) {
if (it->start < x && it->end > x) {
it->end += y;
} else if (it->start > x) {
it->start += y;
it->end += y;
}
if (it->end <= it->start)
it = spans.erase(it);
else
++it;
}
}
}; };
struct SpanCursor { struct SpanCursor {
@@ -145,5 +120,6 @@ void cursor_down(Editor *editor, uint32_t number);
void cursor_left(Editor *editor, uint32_t number); void cursor_left(Editor *editor, uint32_t number);
void cursor_right(Editor *editor, uint32_t number); void cursor_right(Editor *editor, uint32_t number);
void ensure_scroll(Editor *editor); void ensure_scroll(Editor *editor);
void apply_edit(std::vector<Span> &spans, uint32_t x, int64_t y);
#endif #endif

View File

@@ -40,19 +40,20 @@ Editor *new_editor(const char *filename, Coord position, Coord size) {
editor->size = size; editor->size = size;
editor->tree = nullptr; editor->tree = nullptr;
editor->cursor = {0, 0}; editor->cursor = {0, 0};
editor->cursor_preffered = UINT32_MAX;
editor->selection_active = false; editor->selection_active = false;
editor->selection = {0, 0}; editor->selection = {0, 0};
editor->scroll = {0, 0}; editor->scroll = {0, 0};
editor->root = load(str, len, optimal_chunk_size(len)); editor->root = load(str, len, optimal_chunk_size(len));
free(str);
editor->folded.resize(editor->root->line_count + 2); editor->folded.resize(editor->root->line_count + 2);
std::string query = get_exe_dir() + "/../grammar/ruby.scm"; std::string query = get_exe_dir() + "/../grammar/ruby.scm";
if (!(len > (1024 * 1024))) { if (len < (1024 * 64)) {
editor->parser = ts_parser_new(); editor->parser = ts_parser_new();
editor->language = tree_sitter_ruby(); editor->language = tree_sitter_ruby();
ts_parser_set_language(editor->parser, editor->language); ts_parser_set_language(editor->parser, editor->language);
editor->query = load_query(query.c_str(), editor); editor->query = load_query(query.c_str(), editor);
} }
free(str);
return editor; return editor;
} }
@@ -183,8 +184,10 @@ void cursor_down(Editor *editor, uint32_t number) {
char *line_content = next_line(it); char *line_content = next_line(it);
if (line_content == nullptr) if (line_content == nullptr)
return; return;
uint32_t visual_col = if (editor->cursor_preffered == UINT32_MAX)
editor->cursor_preffered =
get_visual_col_from_bytes(line_content, editor->cursor.col); get_visual_col_from_bytes(line_content, editor->cursor.col);
uint32_t visual_col = editor->cursor_preffered;
do { do {
free(line_content); free(line_content);
line_content = next_line(it); line_content = next_line(it);
@@ -212,8 +215,10 @@ void cursor_up(Editor *editor, uint32_t number) {
free(it); free(it);
return; return;
} }
uint32_t visual_col = if (editor->cursor_preffered == UINT32_MAX)
editor->cursor_preffered =
get_visual_col_from_bytes(line_content, editor->cursor.col); get_visual_col_from_bytes(line_content, editor->cursor.col);
uint32_t visual_col = editor->cursor_preffered;
free(line_content); free(line_content);
while (number > 0 && editor->cursor.row > 0) { while (number > 0 && editor->cursor.row > 0) {
editor->cursor.row--; editor->cursor.row--;
@@ -275,6 +280,19 @@ void cursor_right(Editor *editor, uint32_t number) {
} }
number--; number--;
} }
LineIterator *it2 = begin_l_iter(editor->root, editor->cursor.row);
char *cur_line = next_line(it2);
free(it2);
if (cur_line) {
uint32_t len2 = strlen(cur_line);
if (len2 > 0 && cur_line[len2 - 1] == '\n')
cur_line[--len2] = '\0';
editor->cursor_preffered =
get_visual_col_from_bytes(cur_line, editor->cursor.col);
free(cur_line);
} else {
editor->cursor_preffered = UINT32_MAX;
}
if (line) if (line)
free(line); free(line);
} }
@@ -328,6 +346,19 @@ void cursor_left(Editor *editor, uint32_t number) {
} }
number--; number--;
} }
LineIterator *it2 = begin_l_iter(editor->root, editor->cursor.row);
char *cur_line = next_line(it2);
free(it2);
if (cur_line) {
uint32_t len2 = strlen(cur_line);
if (len2 > 0 && cur_line[len2 - 1] == '\n')
cur_line[--len2] = '\0';
editor->cursor_preffered =
get_visual_col_from_bytes(cur_line, editor->cursor.col);
free(cur_line);
} else {
editor->cursor_preffered = UINT32_MAX;
}
if (line) if (line)
free(line); free(line);
} }
@@ -376,6 +407,29 @@ void update_render_fold_marker(uint32_t row, uint32_t cols) {
update(row, i, " ", 0xc6c6c6, 0, 0); update(row, i, " ", 0xc6c6c6, 0, 0);
} }
void apply_edit(std::vector<Span> &spans, uint32_t x, int64_t y) {
Span key{.start = x, .end = 0, .hl = nullptr};
auto it = std::lower_bound(
spans.begin(), spans.end(), key,
[](const Span &a, const Span &b) { return a.start < b.start; });
size_t idx = std::distance(spans.begin(), it);
while (idx > 0 && spans.at(idx - 1).end > x)
--idx;
for (size_t i = idx; i < spans.size();) {
Span &s = spans.at(i);
if (s.start < x && s.end > x) {
s.end += y;
} else if (s.start > x) {
s.start += y;
s.end += y;
}
if (s.end <= s.start)
spans.erase(spans.begin() + i);
else
++i;
}
}
void render_editor(Editor *editor) { void render_editor(Editor *editor) {
uint32_t screen_rows = editor->size.row; uint32_t screen_rows = editor->size.row;
uint32_t screen_cols = editor->size.col; uint32_t screen_cols = editor->size.col;

View File

@@ -75,10 +75,10 @@ void handle_editor_event(Editor *editor, KeyEvent event) {
}; };
editor->edit_queue.push(edit); editor->edit_queue.push(edit);
} }
editor->spans.apply(pos, 1); cursor_right(editor, 1);
apply_edit(editor->spans.spans, pos, 1);
if (editor->spans.mid_parse) if (editor->spans.mid_parse)
editor->spans.edits.push({pos, 1}); editor->spans.edits.push({pos, 1});
cursor_right(editor, 1);
} }
if (event.key_type == KEY_CHAR && event.c == '\t') { if (event.key_type == KEY_CHAR && event.c == '\t') {
std::shared_lock lock_1(editor->knot_mtx); std::shared_lock lock_1(editor->knot_mtx);
@@ -99,10 +99,11 @@ void handle_editor_event(Editor *editor, KeyEvent event) {
}; };
editor->edit_queue.push(edit); editor->edit_queue.push(edit);
} }
editor->spans.apply(pos, 2); cursor_right(editor, 2);
std::unique_lock lock_3(editor->spans.mtx);
apply_edit(editor->spans.spans, pos, 2);
if (editor->spans.mid_parse) if (editor->spans.mid_parse)
editor->spans.edits.push({pos, 2}); editor->spans.edits.push({pos, 2});
cursor_right(editor, 2);
} }
if (event.key_type == KEY_CHAR && (event.c == '\n' || event.c == '\r')) { if (event.key_type == KEY_CHAR && (event.c == '\n' || event.c == '\r')) {
std::shared_lock lock_1(editor->knot_mtx); std::shared_lock lock_1(editor->knot_mtx);
@@ -120,14 +121,15 @@ void handle_editor_event(Editor *editor, KeyEvent event) {
.new_end_byte = pos + 1, .new_end_byte = pos + 1,
.start_point = {editor->cursor.row, editor->cursor.col}, .start_point = {editor->cursor.row, editor->cursor.col},
.old_end_point = {editor->cursor.row, editor->cursor.col}, .old_end_point = {editor->cursor.row, editor->cursor.col},
.new_end_point = {editor->cursor.row, editor->cursor.col + 1}, .new_end_point = {editor->cursor.row + 1, 0},
}; };
editor->edit_queue.push(edit); editor->edit_queue.push(edit);
} }
editor->spans.apply(pos + 1, 1); cursor_right(editor, 1);
std::unique_lock lock_3(editor->spans.mtx);
apply_edit(editor->spans.spans, pos + 1, 1);
if (editor->spans.mid_parse) if (editor->spans.mid_parse)
editor->spans.edits.push({pos + 1, 1}); editor->spans.edits.push({pos + 1, 1});
cursor_right(editor, 1);
} }
if (event.key_type == KEY_CHAR && event.c == 0x7F) { if (event.key_type == KEY_CHAR && event.c == 0x7F) {
std::shared_lock lock_1(editor->knot_mtx); std::shared_lock lock_1(editor->knot_mtx);
@@ -152,7 +154,8 @@ void handle_editor_event(Editor *editor, KeyEvent event) {
}; };
editor->edit_queue.push(edit); editor->edit_queue.push(edit);
} }
editor->spans.apply(start, start - pos); std::unique_lock lock_3(editor->spans.mtx);
apply_edit(editor->spans.spans, start, start - pos);
if (editor->spans.mid_parse) if (editor->spans.mid_parse)
editor->spans.edits.push({start, start - pos}); editor->spans.edits.push({start, start - pos});
} }

View File

@@ -94,7 +94,7 @@ static inline bool ts_predicate(TSQuery *query, const TSQueryMatch &match,
ts_query_predicates_for_pattern(query, match.pattern_index, &step_count); ts_query_predicates_for_pattern(query, match.pattern_index, &step_count);
if (!steps || step_count != 4) if (!steps || step_count != 4)
return true; return true;
if (source->char_count >= (64 * 1024)) if (source->char_count >= (16 * 1024))
return false; return false;
std::string command; std::string command;
std::string regex_txt; std::string regex_txt;
@@ -233,11 +233,11 @@ void ts_collect_spans(Editor *editor) {
if (!running) if (!running)
return; return;
std::sort(new_spans.begin(), new_spans.end()); std::sort(new_spans.begin(), new_spans.end());
std::pair<uint32_t, int64_t> span_edit;
while (editor->spans.edits.pop(span_edit))
apply_edit(new_spans, span_edit.first, span_edit.second);
std::unique_lock span_mtx(editor->spans.mtx); std::unique_lock span_mtx(editor->spans.mtx);
editor->spans.mid_parse = false; editor->spans.mid_parse = false;
editor->spans.spans.swap(new_spans); editor->spans.spans.swap(new_spans);
span_mtx.unlock(); span_mtx.unlock();
std::pair<uint32_t, int64_t> span_edit;
while (editor->spans.edits.pop(span_edit))
editor->spans.apply(span_edit.first, span_edit.second);
} }