Public Access
The editor held the whole note in memory and stopped at 16 KB. It now keeps a window of the file around the cursor, and the rest on the card as a list of pieces (notes::NoteDocument). Memory with a note open is what it was. Up to 64 KB a save rewrites the file, as before. Above, the five-second save appends what changed to <note>.edit, and the file is rewritten on leaving the note, with a progress bar. After a power cut, opening the note picks the edit up where it was saved; a rewrite cut short is finished or dropped, never half applied. Also: Ctrl with Fn+Up/Down go to the start and end of the note; the consoles' `key` command takes ctrl-, alt- and shift-; the Storage App's `e` no longer refuses a big file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
92 lines
4.0 KiB
C++
92 lines
4.0 KiB
C++
#pragma once
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace roro::notes {
|
|
|
|
// The text of a note while it's edited (F1, Q144, Q145): UTF-8 held in memory, a cursor, and the
|
|
// part of it on screen. Up to 16 KB: a longer note is edited through NoteDocument (note_document.h),
|
|
// which keeps this as its window on the file. Lines wrap at spaces, `cols` characters wide; a line owns the space or
|
|
// the newline it ends with, so every byte of the text belongs to exactly one line. No index of
|
|
// lines is kept (a note of newlines alone would need twice its size): where a line starts is
|
|
// worked out from the start of its paragraph, which is never far.
|
|
class NoteText {
|
|
public:
|
|
static constexpr size_t kMaxBytes = 16 * 1024;
|
|
|
|
// Room for a full note is reserved once, so typing never has to find a bigger block of memory:
|
|
// on the device a failed allocation is the end. The second form takes over a string the
|
|
// caller filled (and reserved): a note is never in memory twice.
|
|
NoteText(int cols, int rows);
|
|
NoteText(int cols, int rows, std::string&& text);
|
|
|
|
// Copied into the buffer already held. CRLF becomes LF (Q148). False, and nothing changes, if
|
|
// it's over kMaxBytes.
|
|
bool setText(const std::string& text);
|
|
const std::string& text() const { return text_; }
|
|
size_t cursor() const { return cursor_; }
|
|
size_t size() const { return text_.size(); }
|
|
uint32_t revision() const { return revision_; } // changes with every edit: is it saved?
|
|
|
|
// For a window on a longer text (issue #47): the caller refills the buffer, then says where
|
|
// the cursor is in it and which row of the screen it should be on. CRLF becomes LF as in
|
|
// setText; returns how many CRs went. The revision doesn't change: nothing was edited.
|
|
std::string& buffer() { return text_; }
|
|
size_t refilled(size_t cursor, int row);
|
|
size_t startOfLine(size_t pos) const { return lineOf(pos); }
|
|
size_t top() const { return top_; }
|
|
|
|
bool insert(uint32_t codePoint); // false: the note is full
|
|
bool insertText(const std::string& s); // all of it or nothing
|
|
void backspace();
|
|
void left();
|
|
void right();
|
|
void up();
|
|
void down();
|
|
void pageUp();
|
|
void pageDown();
|
|
void lineStart();
|
|
void lineEnd();
|
|
void toStart();
|
|
void toEnd();
|
|
|
|
// The screen, kept around the cursor: its rows (tabs as spaces, the ending newline left out),
|
|
// and where the cursor is on it, in rows and characters.
|
|
std::vector<std::string> rows();
|
|
int cursorRow();
|
|
int cursorCol();
|
|
int percent() const { return text_.empty() ? 0 : static_cast<int>(top_ * 100 / text_.size()); }
|
|
|
|
std::string firstLine() const; // without its newline, for the title and the file's name
|
|
|
|
private:
|
|
size_t nextLine(size_t start) const; // where the line after the one at `start` starts
|
|
size_t lineOf(size_t pos) const; // the start of the line `pos` is on
|
|
size_t lastSpot(size_t start) const; // the last place the cursor can be on that line
|
|
size_t atColumn(size_t start, int col) const;
|
|
int columnOf(size_t start, size_t pos) const;
|
|
bool hasLineAfter(size_t start) const;
|
|
void moved(bool keepGoal = false);
|
|
void follow(); // scrolls so the cursor is on screen
|
|
size_t dropCarriageReturns(size_t* follow = nullptr); // how many; `follow` is a place in the text, kept on its character
|
|
|
|
int cols_, rows_;
|
|
std::string text_;
|
|
size_t cursor_ = 0, top_ = 0;
|
|
int goal_ = -1; // the column Up and Down aim for, across short lines
|
|
uint32_t revision_ = 0;
|
|
};
|
|
|
|
// The file a new note is saved as (Q142): from its first line, "Shopping list!" -> "shopping-list",
|
|
// or "note-<stamp>" when that gives nothing. No extension, no folder.
|
|
std::string nameFromFirstLine(const std::string& firstLine, const std::string& stamp);
|
|
|
|
// A row's title in the Notes list, from the first bytes of a file: its first line that isn't
|
|
// blank, cut to `maxChars`; "" if there's none.
|
|
std::string titleFrom(const std::string& head, size_t maxChars);
|
|
|
|
} // namespace roro::notes
|