Handle end-of-file newline semantics and improve scroll alignment logic.
This commit is contained in:
19
.idea/workspace.xml
generated
19
.idea/workspace.xml
generated
@@ -33,11 +33,9 @@
|
|||||||
</configurations>
|
</configurations>
|
||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="e1fe3ab0-3650-4fca-8664-a247d5dfa457" name="Changes" comment="Add undo/redo infrastructure and buffer management additions.">
|
<list default="true" id="e1fe3ab0-3650-4fca-8664-a247d5dfa457" name="Changes" comment="Refactor `Buffer` to use `Line` abstraction and improve handling of row operations. This uses either a GapBuffer or PieceTable depending on the compilation.">
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/Buffer.cc" beforeDir="false" afterPath="$PROJECT_DIR$/Buffer.cc" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/Buffer.cc" beforeDir="false" afterPath="$PROJECT_DIR$/Buffer.cc" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/Buffer.h" beforeDir="false" afterPath="$PROJECT_DIR$/Buffer.h" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/Command.cc" beforeDir="false" afterPath="$PROJECT_DIR$/Command.cc" afterDir="false" />
|
|
||||||
</list>
|
</list>
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
@@ -144,7 +142,7 @@
|
|||||||
<option name="number" value="Default" />
|
<option name="number" value="Default" />
|
||||||
<option name="presentableId" value="Default" />
|
<option name="presentableId" value="Default" />
|
||||||
<updated>1764457173148</updated>
|
<updated>1764457173148</updated>
|
||||||
<workItem from="1764457174208" duration="27302000" />
|
<workItem from="1764457174208" duration="27972000" />
|
||||||
</task>
|
</task>
|
||||||
<task id="LOCAL-00001" summary="Add undo/redo infrastructure and buffer management additions.">
|
<task id="LOCAL-00001" summary="Add undo/redo infrastructure and buffer management additions.">
|
||||||
<option name="closed" value="true" />
|
<option name="closed" value="true" />
|
||||||
@@ -154,7 +152,15 @@
|
|||||||
<option name="project" value="LOCAL" />
|
<option name="project" value="LOCAL" />
|
||||||
<updated>1764485311566</updated>
|
<updated>1764485311566</updated>
|
||||||
</task>
|
</task>
|
||||||
<option name="localTasksCounter" value="2" />
|
<task id="LOCAL-00002" summary="Refactor `Buffer` to use `Line` abstraction and improve handling of row operations. This uses either a GapBuffer or PieceTable depending on the compilation.">
|
||||||
|
<option name="closed" value="true" />
|
||||||
|
<created>1764486011231</created>
|
||||||
|
<option name="number" value="00002" />
|
||||||
|
<option name="presentableId" value="LOCAL-00002" />
|
||||||
|
<option name="project" value="LOCAL" />
|
||||||
|
<updated>1764486011231</updated>
|
||||||
|
</task>
|
||||||
|
<option name="localTasksCounter" value="3" />
|
||||||
<servers />
|
<servers />
|
||||||
</component>
|
</component>
|
||||||
<component name="TypeScriptGeneratedFilesManager">
|
<component name="TypeScriptGeneratedFilesManager">
|
||||||
@@ -168,7 +174,8 @@
|
|||||||
<component name="VcsManagerConfiguration">
|
<component name="VcsManagerConfiguration">
|
||||||
<MESSAGE value="Refactoring" />
|
<MESSAGE value="Refactoring" />
|
||||||
<MESSAGE value="Add undo/redo infrastructure and buffer management additions." />
|
<MESSAGE value="Add undo/redo infrastructure and buffer management additions." />
|
||||||
<option name="LAST_COMMIT_MESSAGE" value="Add undo/redo infrastructure and buffer management additions." />
|
<MESSAGE value="Refactor `Buffer` to use `Line` abstraction and improve handling of row operations. This uses either a GapBuffer or PieceTable depending on the compilation." />
|
||||||
|
<option name="LAST_COMMIT_MESSAGE" value="Refactor `Buffer` to use `Line` abstraction and improve handling of row operations. This uses either a GapBuffer or PieceTable depending on the compilation." />
|
||||||
</component>
|
</component>
|
||||||
<component name="XSLT-Support.FileAssociations.UIState">
|
<component name="XSLT-Support.FileAssociations.UIState">
|
||||||
<expand />
|
<expand />
|
||||||
|
|||||||
35
Buffer.cc
35
Buffer.cc
@@ -42,13 +42,29 @@ Buffer::OpenFromFile(const std::string &path, std::string &err)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Detect if file ends with a newline so we can preserve a final empty line
|
||||||
|
// in our in-memory representation (mg-style semantics).
|
||||||
|
bool ends_with_nl = false;
|
||||||
|
{
|
||||||
|
in.seekg(0, std::ios::end);
|
||||||
|
std::streamoff sz = in.tellg();
|
||||||
|
if (sz > 0) {
|
||||||
|
in.seekg(-1, std::ios::end);
|
||||||
|
char last = 0;
|
||||||
|
in.read(&last, 1);
|
||||||
|
ends_with_nl = (last == '\n');
|
||||||
|
} else {
|
||||||
|
in.clear();
|
||||||
|
}
|
||||||
|
// Rewind to start for line-by-line read
|
||||||
|
in.clear();
|
||||||
|
in.seekg(0, std::ios::beg);
|
||||||
|
}
|
||||||
|
|
||||||
rows_.clear();
|
rows_.clear();
|
||||||
std::string line;
|
std::string line;
|
||||||
while (std::getline(in, line)) {
|
while (std::getline(in, line)) {
|
||||||
// std::getline strips the '\n', keep raw line
|
// std::getline strips the '\n', keep raw line content only
|
||||||
if (!line.empty() && !in.good()) {
|
|
||||||
// fallthrough
|
|
||||||
}
|
|
||||||
// Handle potential Windows CRLF: strip trailing '\r'
|
// Handle potential Windows CRLF: strip trailing '\r'
|
||||||
if (!line.empty() && line.back() == '\r') {
|
if (!line.empty() && line.back() == '\r') {
|
||||||
line.pop_back();
|
line.pop_back();
|
||||||
@@ -56,8 +72,15 @@ Buffer::OpenFromFile(const std::string &path, std::string &err)
|
|||||||
rows_.emplace_back(line);
|
rows_.emplace_back(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If file ends with a trailing newline, getline will have produced an empty
|
// If the file ended with a newline and we didn't already get an
|
||||||
// last line already. If the file is empty and no lines were read, keep rows_ empty.
|
// empty final row from getline (e.g., when the last textual line
|
||||||
|
// had content followed by '\n'), append an empty row to represent
|
||||||
|
// the cursor position past the last newline.
|
||||||
|
if (ends_with_nl) {
|
||||||
|
if (rows_.empty() || !rows_.back().empty()) {
|
||||||
|
rows_.emplace_back(std::string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
nrows_ = rows_.size();
|
nrows_ = rows_.size();
|
||||||
filename_ = path;
|
filename_ = path;
|
||||||
|
|||||||
@@ -83,7 +83,8 @@ GUIRenderer::Draw(Editor &ed)
|
|||||||
long last_row = first_row + vis_rows - 1;
|
long last_row = first_row + vis_rows - 1;
|
||||||
|
|
||||||
// A) If user scrolled (scroll_y changed), and cursor outside, move cursor to nearest visible row
|
// A) If user scrolled (scroll_y changed), and cursor outside, move cursor to nearest visible row
|
||||||
if (prev_scroll_y >= 0.0f && scroll_y != prev_scroll_y) {
|
// Skip this when we just forced a scroll alignment this frame (programmatic change).
|
||||||
|
if (!forced_scroll && prev_scroll_y >= 0.0f && scroll_y != prev_scroll_y) {
|
||||||
long cyr = static_cast<long>(cy);
|
long cyr = static_cast<long>(cy);
|
||||||
if (cyr < first_row || cyr > last_row) {
|
if (cyr < first_row || cyr > last_row) {
|
||||||
long new_row = (cyr < first_row) ? first_row : last_row;
|
long new_row = (cyr < first_row) ? first_row : last_row;
|
||||||
|
|||||||
Reference in New Issue
Block a user