Refactor the folder structure which only had 2 'real' modules, now it's more real, no need for splitting into folders beside the external one
This commit is contained in:
252
src/plugin_word_complete.cpp
Normal file
252
src/plugin_word_complete.cpp
Normal file
@@ -0,0 +1,252 @@
|
||||
// MAAAAAAAAAAAAN I DONT LIKE THIS CODE, BUT HOPE IT WORKS
|
||||
|
||||
// @todo: potentially bad that we are not checking against end! lexer->at[0] == 0 check is not enough
|
||||
struct Lexer2 {
|
||||
char16_t *at;
|
||||
};
|
||||
|
||||
Lexer2 BeginLexing(String16 data) {
|
||||
Lexer2 result = {data.data};
|
||||
return result;
|
||||
}
|
||||
|
||||
String16 Next(Lexer2 *lexer) {
|
||||
while (lexer->at[0] != 0 && IsNonWord(lexer->at[0])) {
|
||||
lexer->at += 1;
|
||||
}
|
||||
String16 result = {lexer->at, 0};
|
||||
while (lexer->at[0] != 0 && IsWord(lexer->at[0])) {
|
||||
lexer->at += 1;
|
||||
result.len += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
struct StringAndDistance {
|
||||
String16 string;
|
||||
Int distance;
|
||||
};
|
||||
|
||||
Int FindValueIndex(Array<StringAndDistance> *arr, String16 string) {
|
||||
for (int64_t i = 0; i < arr->len; i += 1) {
|
||||
if (arr->data[i].string == string) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
inline bool MergeSortCompare(StringAndDistance *EntryA, StringAndDistance *EntryB) {
|
||||
bool result = EntryA->distance > EntryB->distance;
|
||||
return result;
|
||||
}
|
||||
|
||||
struct {
|
||||
BlockArena arena;
|
||||
String16 prefix_string;
|
||||
String16 last_string;
|
||||
mco_coro *co;
|
||||
Buffer *buffer;
|
||||
View *view;
|
||||
Int original_caret_pos;
|
||||
Int direction;
|
||||
} CWS;
|
||||
|
||||
void CWSLexIdentifiers(Array<StringAndDistance> *out_idents, Buffer *buffer) {
|
||||
Array<StringAndDistance> idents = {CWS.arena};
|
||||
String16 string = GetString(buffer);
|
||||
Lexer2 lexer = BeginLexing(string.data);
|
||||
for (;;) {
|
||||
String16 token = Next(&lexer);
|
||||
if (token.len <= 0) {
|
||||
break;
|
||||
}
|
||||
if (StartsWith(token, CWS.prefix_string) && token != CWS.prefix_string) {
|
||||
Int pos = token.data - buffer->str;
|
||||
// Here we are computing distance based on position from currently open
|
||||
// buffer but should be fine. We are sorting then putting into the array
|
||||
// and it doesn't displace the original ones so it's fine
|
||||
Int distance = Absolute(CWS.original_caret_pos - pos);
|
||||
int64_t value_index = FindValueIndex(&idents, token);
|
||||
if (value_index != -1) {
|
||||
if (idents[value_index].distance > distance) {
|
||||
idents[value_index].distance = distance;
|
||||
}
|
||||
} else {
|
||||
Add(&idents, {Copy16(CWS.arena, token), distance});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (idents.len > 1) {
|
||||
Array<StringAndDistance> temp = TightCopy(CWS.arena, idents);
|
||||
MergeSort(idents.len, idents.data, temp.data);
|
||||
}
|
||||
For (idents) {
|
||||
if (FindValueIndex(out_idents, it.string) == -1) Add(out_idents, it);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
void CWSGetNextBuffer(mco_coro *co) {
|
||||
// Would be nice to iterate through 64 last active buffers
|
||||
// - Then all buffers
|
||||
Add(&CWS.visited_buffers, CWS.buffer->id);
|
||||
mco_result res = mco_push(co, &CWS.buffer->id, sizeof(CWS.buffer->id));
|
||||
Assert(res == MCO_SUCCESS);
|
||||
mco_yield(co);
|
||||
|
||||
For (IterateInReverse(&Buffers)) {
|
||||
Add(&buffers, it->id);
|
||||
}
|
||||
|
||||
ForItem (window, Windows) {
|
||||
if (!window->visible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
View *view = GetView(window->active_view);
|
||||
Buffer *buffer = GetBuffer(view->active_buffer);
|
||||
if (Contains(CWS.visited_buffers, buffer->id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Add(&CWS.visited_buffers, buffer->id);
|
||||
mco_result res = mco_push(co, &buffer->id, sizeof(buffer->id));
|
||||
Assert(res == MCO_SUCCESS);
|
||||
mco_yield(co);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void WordComplete(mco_coro *co) {
|
||||
Array<BufferID> buffers = {CWS.arena};
|
||||
Add(&buffers, CWS.buffer->id);
|
||||
For (IterateInReverse(&Buffers)) {
|
||||
Add(&buffers, it->id);
|
||||
}
|
||||
|
||||
Int buffer_i = 0;
|
||||
Array<StringAndDistance> idents = {CWS.arena};
|
||||
Add(&idents, {CWS.prefix_string, 0});
|
||||
for (Int i = 1;;) {
|
||||
if (i >= idents.len) {
|
||||
while (buffer_i < buffers.len) {
|
||||
Buffer *buffer = GetBuffer(buffers[buffer_i++]);
|
||||
CWSLexIdentifiers(&idents, buffer);
|
||||
if (i < idents.len) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i >= idents.len) {
|
||||
goto yield;
|
||||
}
|
||||
}
|
||||
CWS.last_string = Copy16(CWS.arena, idents[i].string);
|
||||
SelectRange(CWS.view, EncloseWord(CWS.buffer, CWS.original_caret_pos));
|
||||
Replace(CWS.view, CWS.last_string);
|
||||
yield:;
|
||||
mco_yield(co);
|
||||
if (CWS.direction == -1 && i > 0 && i == idents.len) {
|
||||
// special case for when we are at the end of the list and we want to go back
|
||||
// to make it sure we don't need to click twice to flip
|
||||
i -= 1;
|
||||
}
|
||||
i += CWS.direction;
|
||||
i = Clamp(i, (Int)0, idents.len);
|
||||
}
|
||||
}
|
||||
|
||||
void WordComplete(View *view, Int pos) {
|
||||
Buffer *buffer = GetBuffer(view->active_buffer);
|
||||
Range prefix_string_range = {GetWordStart(buffer, pos), pos};
|
||||
String16 prefix = GetString(buffer, prefix_string_range);
|
||||
if (prefix == u"") {
|
||||
return;
|
||||
}
|
||||
|
||||
bool continue_with_previous = CWS.co && CWS.last_string == prefix && CWS.buffer == buffer && CWS.view == view;
|
||||
if (!continue_with_previous) {
|
||||
if (CWS.co) {
|
||||
mco_result res = mco_destroy(CWS.co);
|
||||
Assert(res == MCO_SUCCESS);
|
||||
CWS.co = NULL;
|
||||
}
|
||||
Release(&CWS.arena);
|
||||
MemoryZero(&CWS, sizeof(CWS));
|
||||
|
||||
// CWS.visited_buffers.allocator = CWS.arena;
|
||||
CWS.buffer = buffer;
|
||||
CWS.view = view;
|
||||
CWS.original_caret_pos = pos;
|
||||
CWS.prefix_string = Copy16(CWS.arena, prefix);
|
||||
|
||||
mco_desc desc = mco_desc_init(WordComplete, 0);
|
||||
mco_result res = mco_create(&CWS.co, &desc);
|
||||
Assert(res == MCO_SUCCESS);
|
||||
}
|
||||
CWS.direction = 1;
|
||||
mco_result res = mco_resume(CWS.co);
|
||||
Assert(res == MCO_SUCCESS);
|
||||
|
||||
if (mco_status(CWS.co) == MCO_DEAD) {
|
||||
res = mco_destroy(CWS.co);
|
||||
Assert(res == MCO_SUCCESS);
|
||||
CWS.co = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CompletePrevWord(View *view, Int pos) {
|
||||
Buffer *buffer = GetBuffer(view->active_buffer);
|
||||
Range prefix_string_range = {GetWordStart(buffer, pos), pos};
|
||||
String16 prefix = GetString(buffer, prefix_string_range);
|
||||
bool continue_with_previous = CWS.co && CWS.last_string == prefix && CWS.buffer == buffer;
|
||||
if (!continue_with_previous) {
|
||||
return;
|
||||
}
|
||||
CWS.direction = -1;
|
||||
mco_result res = mco_resume(CWS.co);
|
||||
Assert(res == MCO_SUCCESS);
|
||||
if (mco_status(CWS.co) == MCO_DEAD) {
|
||||
res = mco_destroy(CWS.co);
|
||||
Assert(res == MCO_SUCCESS);
|
||||
CWS.co = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void CMD_WordComplete() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
bool ok = active.view->carets.len == 1 && GetSize(active.view->carets[0].range) == 0;
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
WordComplete(active.view, active.view->carets[0].range.min);
|
||||
} RegisterCommand(CMD_WordComplete, "ctrl-space", "Completes the current word");
|
||||
|
||||
void CMD_CompletePrevWord() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
bool ok = active.view->carets.len == 1 && GetSize(active.view->carets[0].range) == 0;
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
CompletePrevWord(active.view, active.view->carets[0].range.min);
|
||||
} RegisterCommand(CMD_CompletePrevWord, "ctrl-shift-space", "If already completing a word, iterate to previous word");
|
||||
|
||||
void CMD_CompletePrevWordOrDedent() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
if (active.view->carets.len == 1 && GetSize(active.view->carets[0].range) == 0) {
|
||||
CMD_CompletePrevWord();
|
||||
} else {
|
||||
IndentSelectedLines(active.view, true);
|
||||
}
|
||||
} RegisterCommand(CMD_CompletePrevWordOrDedent, "", "Completes the current word or it indents it, when single caret with no selection it goes for word complete");
|
||||
|
||||
void CMD_WordCompleteOrIndent() {
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
if (active.view->carets.len == 1 && GetSize(active.view->carets[0].range) == 0) {
|
||||
CMD_WordComplete();
|
||||
} else {
|
||||
IndentSelectedLines(active.view);
|
||||
}
|
||||
} RegisterCommand(CMD_WordCompleteOrIndent, "", "Completes the current word or it indents it, when single caret with no selection it goes for word complete");
|
||||
Reference in New Issue
Block a user