Implement a lot of position moving functions

This commit is contained in:
Krzosa Karol
2024-08-03 12:58:53 +02:00
parent 22899c2523
commit 7655ece178
7 changed files with 228 additions and 98 deletions

View File

@@ -18,6 +18,16 @@ bool IsSymbol(wchar_t w) {
return result;
}
bool IsNonWord(wchar_t w) {
bool result = IsSymbol(w) || IsWhitespace(w);
return result;
}
bool IsWord(wchar_t w) {
bool result = IsSymbol(w) || IsWhitespace(w);
return !result;
}
bool IsAlphabetic(wchar_t a) {
bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z');
return result;

View File

@@ -27,91 +27,6 @@ void OffsetAllLinesForward(Buffer *buffer, Int line, Int *_offset) {
}
}
Int LastLine(Buffer &buffer) {
Int result = buffer.line_starts.len - 1;
return result;
}
const Int LAST_LINE = INT64_MAX;
Range GetLineRange(Buffer &buffer, Int line, Int *end_of_buffer = NULL) {
Range result = {buffer.line_starts[line], buffer.len};
if (line + 1 < buffer.line_starts.len) {
result.max = buffer.line_starts[line + 1];
} else if (end_of_buffer) {
*end_of_buffer = 1;
}
return result;
}
String16 GetLineString(Buffer &buffer, Int line, Int *end_of_buffer = NULL) {
Range range = GetLineRange(buffer, line, end_of_buffer);
String16 string = GetString(buffer, range);
return string;
}
Range GetLineRangeWithoutNL(Buffer &buffer, Int line) {
Int end_of_buffer = 0;
Range line_range = GetLineRange(buffer, line, &end_of_buffer);
line_range.max = line_range.max - 1 + end_of_buffer;
return line_range;
}
String16 GetLineStringWithoutNL(Buffer &buffer, Int line) {
Range range = GetLineRangeWithoutNL(buffer, line);
String16 string = GetString(buffer, range);
return string;
}
Int PosToLine(Buffer &buffer, Int pos) {
Add(&buffer.line_starts, buffer.len + 1);
// binary search
Int low = 0;
Int high = buffer.line_starts.len - 2;
Int result = 0;
while (low <= high) {
Int mid = low + (high - low) / 2;
Range range = {buffer.line_starts[mid], buffer.line_starts[mid + 1]};
if (pos >= range.min && pos < range.max) {
result = mid;
break;
}
if (range.min < pos) {
low = mid + 1;
} else {
high = mid - 1;
}
}
Pop(&buffer.line_starts);
return result;
}
XY PosToXY(Buffer &buffer, Int pos) {
Int line = PosToLine(buffer, pos);
Range line_range = GetLineRange(buffer, line);
Int col = pos - line_range.min;
XY result = {col, line};
return result;
}
Int XYToPos(Buffer &buffer, XY xy) {
xy.line = Clamp(xy.line, (Int)0, buffer.line_starts.len - 1);
Range line_range = GetLineRange(buffer, xy.line);
Int pos = Clamp(xy.col + line_range.min, line_range.min, line_range.max);
return pos;
}
Int XYToPosWithoutNL(Buffer &buffer, XY xy) {
xy.line = Clamp(xy.line, (Int)0, buffer.line_starts.len - 1);
Int end_of_buffer = 0;
Range line_range = GetLineRange(buffer, xy.line, &end_of_buffer);
Int pos = Clamp(xy.col + line_range.min, line_range.min, line_range.max - 1 + end_of_buffer);
return pos;
}
void UpdateLines(Buffer *buffer, Range range, String16 string) {
ProfileFunction();
Array<Int> &ls = buffer->line_starts;
@@ -204,4 +119,16 @@ void Appendf(Buffer *buffer, const char *fmt, ...) {
STRING_FORMAT(scratch, fmt, string);
String16 string16 = ToString16(scratch, string);
ReplaceText(buffer, GetEndAsRange(*buffer), string16);
}
}
String16 GetLineString(Buffer &buffer, Int line, Int *end_of_buffer = NULL) {
Range range = GetLineRange(buffer, line, end_of_buffer);
String16 string = GetString(buffer, range);
return string;
}
String16 GetLineStringWithoutNL(Buffer &buffer, Int line) {
Range range = GetLineRangeWithoutNL(buffer, line);
String16 string = GetString(buffer, range);
return string;
}

View File

@@ -142,4 +142,183 @@ Range operator-(Range a, Int value) {
Range operator-=(Range &range, Int value) {
range = range - value;
return range;
}
Int LastLine(Buffer &buffer) {
Int result = buffer.line_starts.len - 1;
return result;
}
const Int LAST_LINE = INT64_MAX;
Range GetLineRange(Buffer &buffer, Int line, Int *end_of_buffer = NULL) {
Range result = {buffer.line_starts[line], buffer.len};
if (line + 1 < buffer.line_starts.len) {
result.max = buffer.line_starts[line + 1];
} else if (end_of_buffer) {
*end_of_buffer = 1;
}
return result;
}
Range GetLineRangeWithoutNL(Buffer &buffer, Int line) {
Int end_of_buffer = 0;
Range line_range = GetLineRange(buffer, line, &end_of_buffer);
line_range.max = line_range.max - 1 + end_of_buffer;
return line_range;
}
Int PosToLine(Buffer &buffer, Int pos) {
Add(&buffer.line_starts, buffer.len + 1);
// binary search
Int low = 0;
Int high = buffer.line_starts.len - 2;
Int result = 0;
while (low <= high) {
Int mid = low + (high - low) / 2;
Range range = {buffer.line_starts[mid], buffer.line_starts[mid + 1]};
if (pos >= range.min && pos < range.max) {
result = mid;
break;
}
if (range.min < pos) {
low = mid + 1;
} else {
high = mid - 1;
}
}
Pop(&buffer.line_starts);
return result;
}
XY PosToXY(Buffer &buffer, Int pos) {
Int line = PosToLine(buffer, pos);
Range line_range = GetLineRange(buffer, line);
Int col = pos - line_range.min;
XY result = {col, line};
return result;
}
Int XYToPos(Buffer &buffer, XY xy) {
xy.line = Clamp(xy.line, (Int)0, buffer.line_starts.len - 1);
Range line_range = GetLineRange(buffer, xy.line);
Int pos = Clamp(xy.col + line_range.min, line_range.min, line_range.max);
return pos;
}
Int XYToPosWithoutNL(Buffer &buffer, XY xy) {
xy.line = Clamp(xy.line, (Int)0, buffer.line_starts.len - 1);
Int end_of_buffer = 0;
Range line_range = GetLineRange(buffer, xy.line, &end_of_buffer);
Int pos = Clamp(xy.col + line_range.min, line_range.min, line_range.max - 1 + end_of_buffer);
return pos;
}
Int GetWordStart(Buffer *buffer, Int pos) {
pos = Clamp(pos, (Int)0, buffer->len);
for (Int i = pos - 1; i >= 0; i -= 1) {
if (IsNonWord(buffer->str[i])) break;
pos = i;
}
return pos;
}
Int GetWordEnd(Buffer *buffer, Int pos) {
pos = Clamp(pos, (Int)0, buffer->len);
for (Int i = pos; i < buffer->len; i += 1) {
pos = i;
if (IsNonWord(buffer->str[i])) break;
}
return pos;
}
Int GetNextWordEnd(Buffer *buffer, Int pos) {
pos = Clamp(pos, (Int)0, buffer->len);
wchar_t prev = 0;
for (Int i = pos; i < buffer->len; i += 1) {
pos = i;
if ((prev && prev != buffer->str[i]) || IsWord(buffer->str[i])) {
break;
}
prev = buffer->str[i];
}
Int result = GetWordEnd(buffer, pos);
return result;
}
Int GetPrevWordStart(Buffer *buffer, Int pos) {
pos = Clamp(pos, (Int)0, buffer->len);
wchar_t prev = 0;
for (Int i = pos - 1; i >= 0; i -= 1) {
if ((prev && prev != buffer->str[i]) || IsWord(buffer->str[i])) {
break;
}
pos = i;
prev = buffer->str[i];
}
Int result = GetWordStart(buffer, pos);
return result;
}
Int GetLineStart(Buffer *buffer, Int pos) {
pos = Clamp(pos, (Int)0, buffer->len);
Int line = PosToLine(*buffer, pos);
Range range = GetLineRangeWithoutNL(*buffer, line);
return range.min;
}
Int GetLineEnd(Buffer *buffer, Int pos) {
pos = Clamp(pos, (Int)0, buffer->len);
Int line = PosToLine(*buffer, pos);
Range range = GetLineRangeWithoutNL(*buffer, line);
return range.max;
}
Int GetBufferEnd(Buffer *buffer) {
return buffer->len;
}
Int GetBufferStart(Buffer *buffer) {
return 0;
}
Int GetNextEmptyLineStart(Buffer *buffer, Int pos) {
Int result = pos;
Int next_line = PosToLine(*buffer, pos) + 1;
for (Int line = next_line; line < buffer->line_starts.len; line += 1) {
Range line_range = GetLineRangeWithoutNL(*buffer, line);
result = line_range.min;
bool whitespace_line = true;
for (Int i = line_range.min; i < line_range.max; i += 1) {
if (!IsWhitespace(buffer->str[i])) {
whitespace_line = false;
break;
}
}
if (whitespace_line) break;
}
return result;
}
Int GetPrevEmptyLineStart(Buffer *buffer, Int pos) {
Int result = pos;
Int next_line = PosToLine(*buffer, pos) - 1;
for (Int line = next_line; line >= 0; line -= 1) {
Range line_range = GetLineRangeWithoutNL(*buffer, line);
result = line_range.min;
bool whitespace_line = true;
for (Int i = line_range.min; i < line_range.max; i += 1) {
if (!IsWhitespace(buffer->str[i])) {
whitespace_line = false;
break;
}
}
if (whitespace_line) break;
}
return result;
}

View File

@@ -79,8 +79,8 @@ void UndoEdit(Buffer *buffer, Array<Caret> *carets) {
void ApplyEdits(Buffer *buffer, Array<Edit> &edits) {
ProfileFunction();
Assert(buffer->debug_edit_phase == 1);
buffer->debug_edit_phase += 1;
Assert(buffer->edit_phase == 1);
buffer->edit_phase += 1;
SaveHistoryBeforeApplyEdits(buffer, &buffer->undo_stack, edits);
_ApplyEdits(buffer, edits);
}
@@ -94,19 +94,25 @@ void ClearRedoStack(Buffer *buffer) {
buffer->redo_stack.len = 0;
}
// @note: !!
// We can invoke this before caret altering commands to save caret history
// and then call some editing command to edit which is not going to save carets
// @todo: this needs to be actually tested though!!!
void BeforeEdit(Buffer *buffer, Array<Caret> &carets) {
Assert(buffer->debug_edit_phase == 0);
buffer->debug_edit_phase += 1;
Assert(carets.len);
SaveHistoryBeforeMergeCursor(buffer, &buffer->undo_stack, carets);
ClearRedoStack(buffer);
Assert(buffer->edit_phase == 0 || buffer->edit_phase == 1);
if (buffer->edit_phase == 0) {
buffer->edit_phase += 1;
Assert(carets.len);
SaveHistoryBeforeMergeCursor(buffer, &buffer->undo_stack, carets);
ClearRedoStack(buffer);
}
}
bool KILL_SELECTION = true;
void AfterEdit(Buffer *buffer, Array<Edit> *edits, Array<Caret> *carets, bool kill_selection = true) {
ProfileFunction();
Assert(buffer->debug_edit_phase == 2);
buffer->debug_edit_phase -= 2;
Assert(buffer->edit_phase == 2);
buffer->edit_phase -= 2;
#if BUFFER_DEBUG
if (buffer->no_history == false) {

View File

@@ -513,7 +513,13 @@ void WindowCommand(Event event, Window *window, View *view) {
}
if (Ctrl(SDLK_W)) {
Command_TrimTrailingWhitespace(view, false);
Int pos = view->carets[0].range.min;
view->carets[0] = MakeCaret(GetPrevWordStart(buffer, pos));
}
if (Ctrl(SDLK_R)) {
Int pos = view->carets[0].range.min;
view->carets[0] = MakeCaret(GetWordStart(buffer, pos));
}
if (CtrlShift(SDLK_K)) {

View File

@@ -35,7 +35,7 @@ struct Buffer {
Array<HistoryEntry> undo_stack;
Array<HistoryEntry> redo_stack;
int debug_edit_phase;
int edit_phase;
bool no_history;
bool dirty;
};

View File

@@ -4,9 +4,11 @@
- don't trim lines with cursor or selection on it
- fix history of ctrl + enter
- maybe my mouse selection is wrong? seems like on double click lite and sublime start to enclosing words!!
- click 3 times to select line
- we could rewrite kill lines with simpler commands - extend selection to encompass lines->replace
- improve cursor movement, it's too clunky, too much stopping
- better enclosures
- kill selected lines
- should be able click on title bar of windows which disappear on losing focus
- search backwards
- load selected string or auto enclosed word when midclick?, ctrl + click, ctrl + e?