From 70f51642cdf8414cbcb5a183225d8941c8330d89 Mon Sep 17 00:00:00 2001 From: Matthew Date: Sat, 4 Oct 2025 16:33:14 +1000 Subject: [PATCH] more movement options --- src/editor/buffer.d | 302 ++++++++++++++++++++++++++++++++------------ 1 file changed, 221 insertions(+), 81 deletions(-) diff --git a/src/editor/buffer.d b/src/editor/buffer.d index c1ef764..3d5a026 100644 --- a/src/editor/buffer.d +++ b/src/editor/buffer.d @@ -3,6 +3,7 @@ import core.stdc.stdio : EOF; import parsing; import std.format : sformat; import std.math.algebraic : abs; +import editor; struct FlatBuffer { @@ -48,6 +49,41 @@ enum WalkDir Forward = +1, } +const bool[128] SYM_TOKENS = [ + '`': true, + '"': true, + '\'': true, + '[': true, + ']': true, + '~': true, + '!': true, + '@': true, + '#': true, + '$': true, + '%': true, + '^': true, + '&': true, + '*': true, + '(': true, + ')': true, + '-': true, + '_': true, + '=': true, + '+': true, + '|': true, + '}': true, + '{': true, + ',': true, + '.': true, + '?': true, + '>': true, + '<': true, + ':': true, + '/': true, + '\\': true, + ';': true, +]; + FlatBuffer CreateFlatBuffer(u8[] data) { @@ -94,10 +130,7 @@ CountLF(u8[] data) u64 lf_count = 0; for(u64 i = 0; i < data.length; i += 1) { - if(data.ptr[i] == '\n') - { - lf_count += 1; - } + lf_count += u64(data.ptr[i] == '\n'); } return lf_count; @@ -183,11 +216,7 @@ Insert(FlatBuffer* fb, u8[] insert, u64 length, u64 pos) for(u64 i = 0; i < length; i += 1) { fb.buf_pos += 1; - - if(insert.ptr[i] == '\n') - { - fb.lf_count += 1; - } + fb.lf_count += u64(insert.ptr[i] == '\n'); } if(length == 1) @@ -507,19 +536,167 @@ MoveToEmptyLine(bool up)(FlatBuffer* fb) } } +pragma(inline) bool +IsLetter(u8 ch) +{ + return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); +} + +pragma(inline) bool +IsNumber(u8 ch) +{ + return ch >= '0' && ch <= '9'; +} + +pragma(inline) bool +IsSymbol(u8 ch) +{ + return ch < 128 && SYM_TOKENS[ch]; +} + +enum ChType +{ + Letter, + Number, + Symbol, + Whitespace, +} + +bool +CheckChMatch(u8 ch, ChType start_type) +{ + return (start_type == ChType.Letter && !IsLetter(ch)) || + (start_type == ChType.Number && (!IsNumber(ch) && ch != '.')) || + (start_type == ChType.Symbol && !IsSymbol(ch)); +} + +ChType +GetChType(u8 ch) +{ + ChType type = ChType.Letter; + if(IsNumber(ch)) + { + type = ChType.Number; + } + else if(IsSymbol(ch)) + { + type = ChType.Symbol; + } + else if(CheckWhiteSpace(ch)) + { + type = ChType.Whitespace; + } + + return type; +} + +void +MoveToWordEdge(bool forward)(FlatBuffer* fb) +{ + u64 step = forward ? 1 : -1; + + if((forward && fb.buf_pos < fb.length-1) || (!forward && fb.buf_pos > 0)) + { + ChType type = GetChType(fb.data[fb.buf_pos]); + if(type != GetChType(fb.data[fb.buf_pos+step])) + { + fb.buf_pos += step; + } + else for(u64 i = fb.buf_pos+step; true; i += step) + { + static if(forward) + { + u8 next = i < fb.length-1 ? fb.data[i+1] : 0; + } + else + { + u8 next = i > 1 ? fb.data[i-1] : 0; + } + + static if(forward) if(i == fb.length-2) + { + fb.buf_pos = i; + fb.buf_pos += u64(GetChType(next) == type); + + break; + } + + static if(!forward) if(i == 1) + { + fb.buf_pos = i; + fb.buf_pos += GetChType(next) == type ? -1 : 0; + break; + } + + if(CheckChMatch(next, type)) + { + fb.buf_pos = i; + break; + } + } + + while(CheckWhiteSpace(fb.data[fb.buf_pos])) + { + bool exit = forward ? fb.buf_pos == fb.length-1 : fb.buf_pos == 0; + if(exit) break; + + fb.buf_pos += step; + } + } +} + +void +MoveToNextWord(bool forward)(FlatBuffer* fb) +{ + ChType type = GetChType(fb.data[fb.buf_pos]); + + bool hit_ws; + u64 step = forward ? 1 : -1; + for(u64 i = fb.buf_pos; true; i += step) with(ChType) + { + static if(forward) if(i == fb.length-1) + { + fb.buf_pos = i; + break; + } + + static if(!forward) if(i == 0) + { + fb.buf_pos = i; + break; + } + + u8 ch = fb.data[i]; + + if(CheckWhiteSpace(ch)) + { + hit_ws = true; + continue; + } + + if(hit_ws) + { + fb.buf_pos = i; + break; + } + + if(CheckChMatch(ch, type)) + { + fb.buf_pos = i; + break; + } + + } + + assert(fb.buf_pos >= 0 && fb.buf_pos <= fb.length); +} + bool Move(FlatBuffer* fb, Input key, Modifier md) { bool taken; - switch(key) with(Input) - { - case Home: MoveToSOL(fb); taken = true; break; - case End: MoveToEOL(fb); taken = true; break; - default: break; - } - - if(!taken && md & (MD.LeftShift | MD.RightShift)) + if(md & (MD.LeftShift | MD.RightShift)) { switch(key) with(Input) { @@ -533,16 +710,24 @@ Move(FlatBuffer* fb, Input key, Modifier md) } break; case Left: { - + if(fb.buf_pos > 0) + { + MoveToWordEdge!(false)(fb); + } } break; case Right: { - + if(fb.buf_pos < fb.length) + { + MoveToWordEdge!(true)(fb); + } } break; + case Home: fb.buf_pos = 0; taken = true; break; + case End: fb.buf_pos = fb.length; taken = true; break; default: break; } } - else if(!taken && md & (MD.LeftCtrl | MD.RightCtrl)) + else if(md & (MD.LeftCtrl | MD.RightCtrl)) { switch(key) with(Input) { @@ -551,20 +736,25 @@ Move(FlatBuffer* fb, Input key, Modifier md) } break; case Down: { - } break; case Left: { - + if(fb.buf_pos > 0) + { + MoveToNextWord!(false)(fb); + } } break; case Right: { - + if(fb.buf_pos < fb.length) + { + MoveToNextWord!(true)(fb); + } } break; default: break; } } - else if(!taken) + else { switch(key) with(Input) { @@ -594,6 +784,8 @@ Move(FlatBuffer* fb, Input key, Modifier md) taken = true; } } break; + case Home: MoveToSOL(fb); taken = true; break; + case End: MoveToEOL(fb); taken = true; break; default: break; } } @@ -607,20 +799,22 @@ Move(FlatBuffer* fb, Input key, Modifier md) void AdjustOffset(FlatBuffer* fb) { - with(fb) + if(fb.rows > 0) with(fb) { i64 screen_pos = CurrentLine(fb) - offset; - i64 start = 1; + i64 start = 0; i64 end = rows-2; if(offset > 0 && screen_pos < start) { - offset += screen_pos - start; + offset += screen_pos; } else if(screen_pos > end) { offset += screen_pos - end; } } + + assert(fb.offset <= fb.lf_count); } void @@ -708,58 +902,4 @@ unittest assert(buf.data[i] == ch, "FlatBuffer Replace failure: buffers do not match"); } } - - // FlatBuffer lines - { - /* - u8[] data = StringToU8("This is a line.\nThis is a second line,\nalong with a third line."); - FlatBuffer buf = CreateFlatBuffer(data); - - assert(buf.lf_count == 3, "FlatBuffer line count mismatch"); - - u8[] insert = StringToU8("Maybe, "); - Insert(&buf, insert, insert.length, Range(x: 0, y: 1)); - - string result = "This is a line.\nMaybe, This is a second line,\nalong with a third line."; - foreach(i, ch; result) - { - assert(buf.data[i] == ch, "FlatBuffer Insert pos failure: buffers do not match"); - } - - u8[][] lines = new u8[][](3, 1024); - GetLines(&buf, lines, 0, 3); - string[] result_arr = [ - "This is a line.", - "Maybe, This is a second line,", - "along with a third line.", - ]; - - foreach(i, res; result_arr) - { - foreach(j, ch; res) - { - assert(lines[i][j] == ch, "FlatBuffer GetLines failure: result strings do not match"); - } - } - - insert = StringToU8("replaced line.\nThis is the previous "); - Insert(&buf, insert, insert.length, Range(x: 10, y: 0)); - lines = new u8[][](4, 1024); - GetLines(&buf, lines, 0, 4); - result_arr = [ - "This is a replaced line.", - "This is the previous line.", - "Maybe, This is a second line,", - "along with a third line.", - ]; - - foreach(i, res; result_arr) - { - foreach(j, ch; res) - { - assert(lines[i][j] == ch, "FlatBuffer GetLines 2 failure: result strings do not match"); - } - } - */ - } }