Implement a lot of position moving functions
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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?
|
||||
|
||||
Reference in New Issue
Block a user