From c826e556a12deb89e33a121102ae55cd40a0b907 Mon Sep 17 00:00:00 2001 From: Laury Date: Fri, 10 Jun 2022 16:15:31 +0200 Subject: [PATCH] [storage] New attempt to save cursor position --- apps/code/editor_controller.cpp | 8 +++++++- apps/code/script.cpp | 15 ++++++++++++++ apps/code/script.h | 2 ++ ion/include/ion/internal_storage.h | 6 ++++++ ion/include/ion/storage.h | 7 +++++++ ion/include/ion/unicode/utf8_helper.h | 4 ++++ ion/src/shared/internal_storage.cpp | 2 +- ion/src/shared/storage.cpp | 21 +++++++++++++++++++ ion/src/shared/unicode/utf8_helper.cpp | 28 ++++++++++++++++++++++++++ 9 files changed, 91 insertions(+), 2 deletions(-) diff --git a/apps/code/editor_controller.cpp b/apps/code/editor_controller.cpp index be09c9662..d286b92cf 100644 --- a/apps/code/editor_controller.cpp +++ b/apps/code/editor_controller.cpp @@ -65,13 +65,19 @@ void EditorController::viewWillAppear() { ViewController::viewWillAppear(); m_editorView.loadSyntaxHighlighter(); if(GlobalPreferences::sharedGlobalPreferences()->cursorSaving()) { - m_editorView.setCursorLocation(m_editorView.text() + strlen(m_editorView.text())); + int offset = m_script.cursorOffset(); + if (offset != -1) { + m_editorView.setCursorLocation(m_editorView.text() + offset); + } else { + m_editorView.setCursorLocation(m_editorView.text() + strlen(m_editorView.text())); + } } else { m_editorView.setCursorLocation(m_editorView.text() + strlen(m_editorView.text())); } } void EditorController::viewDidDisappear() { + m_script.setCursorOffset(m_editorView.cursorLocation() - m_script.content()); m_editorView.resetSelection(); m_menuController->scriptContentEditionDidFinish(); } diff --git a/apps/code/script.cpp b/apps/code/script.cpp index 4b39b3452..678f78da1 100644 --- a/apps/code/script.cpp +++ b/apps/code/script.cpp @@ -65,6 +65,21 @@ bool Script::nameCompliant(const char * name) { return false; } +uint16_t Script::cursorOffset() { + assert(!isNull()); + Ion::Storage::Metadata metadata = Ion::Storage::sharedStorage()->metadataForRecord(*this); + if (metadata.buffer != nullptr) { + assert(metadata.size == 2); + return *((uint16_t*) metadata.buffer); + } + + return -1; +} +void Script::setCursorOffset(uint16_t position) { + assert(!isNull()); + Ion::Storage::sharedStorage()->setMetadataForRecord(*this, { &position, sizeof(uint16_t) }); +} + uint8_t * StatusFromData(Script::Data d) { return const_cast(static_cast(d.buffer)); } diff --git a/apps/code/script.h b/apps/code/script.h index 6f7df9cda..ffa8038bc 100644 --- a/apps/code/script.h +++ b/apps/code/script.h @@ -50,6 +50,8 @@ public: void toggleAutoimportationStatus(); const char * content() const; size_t contentSize() { return value().size - k_statusSize; } + void setCursorOffset(uint16_t position); // -1 if no metadata + uint16_t cursorOffset(); /* Fetched status */ bool fetchedFromConsole() const; diff --git a/ion/include/ion/internal_storage.h b/ion/include/ion/internal_storage.h index 257d243ce..c40edb3f1 100644 --- a/ion/include/ion/internal_storage.h +++ b/ion/include/ion/internal_storage.h @@ -14,6 +14,11 @@ namespace Ion { class StorageDelegate; +/** + * Purpose the two storage classes : + * - The first (InternalStorage) is the base, it allows to create, modify and delete records + * - The second (Storage) is the visible part. It handle the trash system and the metadata records + */ class InternalStorage { public: typedef uint16_t record_size_t; @@ -114,6 +119,7 @@ public: // Used by Python OS module int numberOfRecords(); Record recordAtIndex(int index); + protected: InternalStorage(); /* Getters on address in buffer */ diff --git a/ion/include/ion/storage.h b/ion/include/ion/storage.h index 5385f4608..ea217ac52 100644 --- a/ion/include/ion/storage.h +++ b/ion/include/ion/storage.h @@ -37,9 +37,16 @@ public: Record recordAtIndex(int index); void destroyRecord(Record record); + // Trash void reinsertTrash(const char * extension); void emptyTrash(); + // Metadata + typedef Record::Data Metadata; + Metadata metadataForRecord(Record record); + Record::ErrorStatus setMetadataForRecord(Record record, Metadata metadata); + void removeMetadataForRecord(Record record); + private: Storage(): InternalStorage() {} diff --git a/ion/include/ion/unicode/utf8_helper.h b/ion/include/ion/unicode/utf8_helper.h index a3ce96df8..aa4b795cd 100644 --- a/ion/include/ion/unicode/utf8_helper.h +++ b/ion/include/ion/unicode/utf8_helper.h @@ -26,6 +26,10 @@ int CountOccurrences(const char * s, CodePoint c); * null terminating char otherwise. */ const char * CodePointSearch(const char * s, CodePoint c, const char * stoppingPosition = nullptr); +/* Returns the last occurrence of a code point in a string, the position of the + * null terminating char otherwise. */ +const char * LastCodePoint(const char * s, CodePoint c, const char * stoppingPosition = nullptr); + // Returns true if the text had the code point bool HasCodePoint(const char * s, CodePoint c, const char * stoppingPosition = nullptr); diff --git a/ion/src/shared/internal_storage.cpp b/ion/src/shared/internal_storage.cpp index 5fc4a90d1..1af851225 100644 --- a/ion/src/shared/internal_storage.cpp +++ b/ion/src/shared/internal_storage.cpp @@ -55,7 +55,7 @@ InternalStorage::Record::Record(const char * fullName) { m_fullNameCRC32 = 0; return; } - const char * dotChar = UTF8Helper::CodePointSearch(fullName, k_dotChar); + const char * dotChar = UTF8Helper::LastCodePoint(fullName, k_dotChar); // If no extension, return empty record if (*dotChar == 0 || *(dotChar+1) == 0) { m_fullNameCRC32 = 0; diff --git a/ion/src/shared/storage.cpp b/ion/src/shared/storage.cpp index 50a8a4c70..5a32b1147 100644 --- a/ion/src/shared/storage.cpp +++ b/ion/src/shared/storage.cpp @@ -61,6 +61,10 @@ bool Storage::hasRecord(Record r) { void Storage::destroyRecord(Record record) { emptyTrash(); + const Record metadataRecord = recordBaseNamedWithExtension(record.fullName(), "sys"); + if (!metadataRecord.isNull()) { + InternalStorage::destroyRecord(metadataRecord); + } m_trashRecord = record; } @@ -177,4 +181,21 @@ void Storage::emptyTrash() { } } +Storage::Metadata Storage::metadataForRecord(Record record) { + return recordBaseNamedWithExtension(record.fullName(), "sys").value(); +} + +Storage::Record::ErrorStatus Storage::setMetadataForRecord(Record record, Metadata data) { + Record metadataRecord = Record(record.fullName(), "sys"); + if (!hasRecord(metadataRecord)) { + return createRecordWithExtension(record.fullName(), "sys", data.buffer, data.size); + } else { + return metadataRecord.setValue(data); + } +} + +void Storage::removeMetadataForRecord(Record record) { + recordBaseNamedWithExtension(record.fullName(), "sys").destroy(); +} + } diff --git a/ion/src/shared/unicode/utf8_helper.cpp b/ion/src/shared/unicode/utf8_helper.cpp index 0481df941..45d841efe 100644 --- a/ion/src/shared/unicode/utf8_helper.cpp +++ b/ion/src/shared/unicode/utf8_helper.cpp @@ -54,6 +54,34 @@ const char * CodePointSearch(const char * s, CodePoint c, const char * stoppingP return currentPointer; } +const char * LastCodePoint(const char * s, CodePoint c, const char * stoppingPosition) { + if (UTF8Decoder::CharSizeOfCodePoint(c) == 1) { + const char * result = nullptr; + const char * position = s; + while (*position != 0 && (stoppingPosition == nullptr || position != stoppingPosition)) { + if (*position == c) { + result = position; + } + position++; + } + return result ? result : position; + } + UTF8Decoder decoder(s); + const char * currentPointer = s; + const char * result = nullptr; + CodePoint codePoint = decoder.nextCodePoint(); + const char * nextPointer = decoder.stringPosition(); + while (codePoint != UCodePointNull && (stoppingPosition == nullptr || currentPointer < stoppingPosition)) { + if (c == codePoint) { + result = currentPointer; + } + currentPointer = nextPointer; + codePoint = decoder.nextCodePoint(); + nextPointer = decoder.stringPosition(); + } + return result ? result : currentPointer; +} + bool HasCodePoint(const char * s, CodePoint c, const char * stoppingPosition) { assert(c != 0); const char * resultPosition = CodePointSearch(s, c, stoppingPosition);