[code] Cleaned Script and ScriptStore classes, created accordion.h

A Script object now contains its AutoImport marker, its name and its
content. The ScripStore methods have better names and the optimization
is cleaner.

Change-Id: I1b21af2d23f1c9a34f984309512b0c01b2f1c320
This commit is contained in:
Léa Saviot
2017-10-25 18:02:01 +02:00
committed by Romain Goyet
parent a14cfd0aea
commit fbb5c70339
15 changed files with 498 additions and 419 deletions

View File

@@ -18,7 +18,7 @@ const Image * App::Descriptor::icon() {
}
App::Snapshot::Snapshot() {
m_scriptStore.addFactorialScript();
m_scriptStore.addNewScript(ScriptStore::DefaultScript::Factorial);
}
App * App::Snapshot::unpack(Container * container) {
@@ -26,7 +26,7 @@ App * App::Snapshot::unpack(Container * container) {
}
void App::Snapshot::reset() {
m_scriptStore.deleteAll();
m_scriptStore.deleteAllScripts();
}
App::Descriptor * App::Snapshot::descriptor() {

View File

@@ -9,7 +9,7 @@ EditorController::EditorController() :
}
void EditorController::setScript(Script script){
m_view.setText(script.editableContent(), script.bufferSize());
m_view.setText(script.editableContent(), script.contentBufferSize());
}
bool EditorController::handleEvent(Ion::Events::Event event) {

View File

@@ -10,6 +10,8 @@ class EditorController : public ViewController {
public:
EditorController();
void setScript(Script script);
/* ViewController */
View * view() override { return &m_view; }
bool handleEvent(Ion::Events::Event event) override;
void didBecomeFirstResponder() override;

View File

@@ -89,7 +89,7 @@ void MenuController::renameScriptAtIndex(int i) {
}
void MenuController::deleteScriptAtIndex(int i) {
m_scriptStore->deleteScript(i);
m_scriptStore->deleteScriptAtIndex(i);
m_selectableTableView.reloadData();
}
@@ -150,7 +150,7 @@ int MenuController::typeAtLocation(int i, int j) {
void MenuController::willDisplayCellForIndex(HighlightCell * cell, int index) {
if (index < m_scriptStore->numberOfScripts()) {
EvenOddEditableTextCell * myCell = static_cast<EvenOddEditableTextCell *>(cell);
myCell->editableTextCell()->textField()->setText(m_scriptStore->nameOfScript(index));
myCell->editableTextCell()->textField()->setText(m_scriptStore->scriptAtIndex(index).name());
myCell->setEven(index%2 == 0);
} else {
assert(index == m_scriptStore->numberOfScripts());
@@ -169,7 +169,7 @@ bool MenuController::textFieldDidReceiveEvent(TextField * textField, Ion::Events
}
bool MenuController::textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) {
if (m_scriptStore->renameScript(m_selectableTableView.selectedRow(), text)) {
if (m_scriptStore->renameScriptAtIndex(m_selectableTableView.selectedRow(), text)) {
int currentRow = m_selectableTableView.selectedRow();
if (event == Ion::Events::Down && currentRow < numberOfRows() - 1) {
m_selectableTableView.selectCellAtLocation(m_selectableTableView.selectedColumn(), currentRow + 1);

View File

@@ -2,21 +2,35 @@
namespace Code {
Script::Script(char * textBuffer, size_t sizeOfBuffer) :
m_bufferSize(sizeOfBuffer),
m_textBuffer(textBuffer)
Script::Script(const char * marker, const char * name, size_t nameBufferSize, const char * content, size_t contentBufferSize) :
m_marker(marker),
m_name(name),
m_nameBufferSize(nameBufferSize),
m_content(content),
m_contentBufferSize(contentBufferSize)
{
}
const char * Script::readOnlyContent() const {
return m_textBuffer;
bool Script::isNull() const {
if (m_marker == nullptr) {
assert(m_name == nullptr);
assert(m_nameBufferSize == 0);
assert(m_content == nullptr);
assert(m_contentBufferSize == 0);
return true;
}
return false;
}
char * Script::editableContent() {
return m_textBuffer;
}
size_t Script::bufferSize() const {
return m_bufferSize;
bool Script::autoimport() const {
assert(!isNull());
assert(m_marker != nullptr);
if (m_marker[0] == AutoImportationMarker) {
return true;
}
assert (m_marker[0] == NoAutoImportationMarker);
return false;
}
}

View File

@@ -7,13 +7,62 @@ namespace Code {
class Script {
public:
Script(char * textBuffer = nullptr, size_t sizeOfBuffer = 0);
const char * readOnlyContent() const;
char * editableContent();
size_t bufferSize() const;
Script(const char * marker = nullptr, const char * name = nullptr, size_t nameBufferSize = 0, const char * content = nullptr, size_t contentBufferSize = 0);
bool isNull() const;
bool autoimport() const;
const char * name() const {
assert(!isNull());
assert(m_name != nullptr);
return m_name;
}
char * editableName() {
assert(!isNull());
assert(m_name != nullptr);
return const_cast<char *>(m_name);
}
/* nameBufferSize() might not be equal to strlen(name()): There might be free
* space (chars equal to ScriptStore::FreeSpaceMarker), that is used to edit
* the script name. */
size_t nameBufferSize() const {
assert(!isNull());
return m_nameBufferSize;
}
const char * content() const {
assert(!isNull());
assert(m_content != nullptr);
return m_content;
}
char * editableContent() {
assert(!isNull());
assert(m_content != nullptr);
return const_cast<char *>(m_content);
}
/* contentBufferSize() might not be equal to strlen(name()): There might be
* free space (chars equal to ScriptStore::FreeSpaceMarker), that is used to
* edit the script content. */
size_t contentBufferSize() const {
assert(!isNull());
return m_contentBufferSize;
}
static constexpr int NumberOfStringsPerScript = 3;
static constexpr char AutoImportationMarker = 2;
static constexpr char NoAutoImportationMarker = 3;
static constexpr char DefaultAutoImportationMarker = AutoImportationMarker;
/* We made sure that these chars are not used in ion/include/ion/charset.h,
* nor used in the ScriptStore. */
private:
size_t m_bufferSize;
char * m_textBuffer;
const char * m_marker;
const char * m_name;
size_t m_nameBufferSize;
const char * m_content;
size_t m_contentBufferSize;
};
}

View File

@@ -19,7 +19,7 @@ ScriptParameterController::ScriptParameterController(Responder * parentResponder
}
void ScriptParameterController::setScript(int i){
m_editorController.setScript(m_scriptStore->editableScript(i));
m_editorController.setScript(m_scriptStore->scriptAtIndex(i, ScriptStore::EditableZone::Content));
m_currentScriptIndex = i;
}

View File

@@ -7,355 +7,138 @@ namespace Code {
constexpr char ScriptStore::k_defaultScriptName[];
ScriptStore::ScriptStore() :
m_numberOfScripts(0),
m_lastEditedStringPosition(0)
m_accordion(m_scriptData, k_scriptDataSize)
{
for (int i = 0; i<k_historySize; i ++) {
m_history[i] = FreeSpaceMarker;
}
}
Script ScriptStore::editableScript(int i) {
assert(i >= 0 && i < numberOfScripts());
cleanAndMoveFreeSpaceAfterScriptContent(i);
int beginningOfScriptContent = indexOfScriptContent(i);
const Script ScriptStore::scriptAtIndex(int index, EditableZone zone) {
assert(index >= 0 && index < numberOfScripts());
size_t nameBufferSize = 0;
size_t contentBufferSize = 0;
int accordionIndex;
// Compute the size of the script, including the free space of m_history
int sizeOfEditableScript = 0;
for (int j=beginningOfScriptContent; j<k_historySize-1; j++) {
if (m_history[j] == FreeSpaceMarker && m_history[j+1] != FreeSpaceMarker) {
// Move the Free Space at the end of the correct string.
switch (zone) {
case EditableZone::None:
break;
case EditableZone::Name:
accordionIndex = accordionIndexOfNameOfScriptAtIndex(index);
nameBufferSize = m_accordion.sizeOfEditableBufferAtIndex(accordionIndex);
break;
case EditableZone::Content:
accordionIndex = accordionIndexOfContentOfScriptAtIndex(index);
contentBufferSize = m_accordion.sizeOfEditableBufferAtIndex(accordionIndex);
break;
}
sizeOfEditableScript++;
}
return Script(&m_history[beginningOfScriptContent], sizeOfEditableScript);
// Compute the positions and lengths of the Script Marker, Name and Content.
const char * marker = m_accordion.bufferAtIndex(accordionIndexOfMarkersOfScriptAtIndex(index));
const char * name = m_accordion.bufferAtIndex(accordionIndexOfNameOfScriptAtIndex(index));
if (nameBufferSize == 0) {
nameBufferSize = strlen(name);
}
const char * content = m_accordion.bufferAtIndex(accordionIndexOfContentOfScriptAtIndex(index));
if (contentBufferSize == 0) {
contentBufferSize = strlen(content);
}
return Script(marker, name, nameBufferSize, content, contentBufferSize);
}
Script ScriptStore::script(int i) {
assert(i >= 0 && i < numberOfScripts());
cleanFreeSpace();
int beginningOfScriptContent = indexOfScriptContent(i);
return Script(&m_history[beginningOfScriptContent], strlen(&m_history[beginningOfScriptContent]) + 1);
}
Script ScriptStore::script(const char * name) {
cleanFreeSpace();
for (int i=0; i<numberOfScripts(); i++) {
const char * nameScripti = nameOfScript(i);
if (strcmp(nameScripti, name) == 0) {
return script(i);
const Script ScriptStore::scriptNamed(const char * name) {
for (int i = 0; i < numberOfScripts(); i++) {
int accordionIndex = accordionIndexOfNameOfScriptAtIndex(i);
const char * currentScriptName = m_accordion.bufferAtIndex(accordionIndex);
if (strcmp(currentScriptName, name) == 0) {
return scriptAtIndex(i);
}
}
return Script();
}
char * ScriptStore::nameOfScript(int i) {
cleanFreeSpace();
return &m_history[indexOfScriptName(i)];
int ScriptStore::numberOfScripts() {
return (m_accordion.numberOfBuffers())/Script::NumberOfStringsPerScript;
}
char * ScriptStore::editableNameOfScript(int i) {
assert (i >= 0 && i < numberOfScripts());
cleanAndMoveFreeSpaceAfterScriptName(i);
return &m_history[indexOfScriptName(i)];
}
int ScriptStore::sizeOfEditableNameOfScript(int i) {
// Compute the size of the name of the script, including the free space of m_history
int sizeOfEditableNameScript = 0;
for (int j=indexOfScriptName(i); j<k_historySize-1; j++) {
if (m_history[j] == FreeSpaceMarker && m_history[j+1] != FreeSpaceMarker) {
bool ScriptStore::addNewScript(DefaultScript defaultScript) {
const char autoImportationString[2] = {Script::DefaultAutoImportationMarker, 0};
if (!m_accordion.appendBuffer(autoImportationString)) {
return false;
}
const char * name = nullptr;
switch (defaultScript) {
case DefaultScript::Empty:
name = k_defaultScriptName;
break;
case DefaultScript::Mandelbrot:
name = "mandelbrot.py";
break;
case DefaultScript::Factorial:
name = "factorial.py";
break;
}
sizeOfEditableNameScript++;
}
return sizeOfEditableNameScript;
}
int ScriptStore::numberOfScripts() const {
return m_numberOfScripts;
}
bool ScriptStore::addNewScript() {
int sizeFreeSpace = sizeOfFreeSpace();
if (sizeFreeSpace < 4) {
if (!m_accordion.appendBuffer(name)) {
// Delete the Auto Importation Marker
m_accordion.deleteLastBuffer();
return false;
}
cleanAndMoveFreeSpaceAfterScriptContent(numberOfScripts()-1);
int beginningNewScript = indexOfFirstFreeSpaceMarker();
m_history[beginningNewScript] = AutoImportationMarker;
if (copyName(beginningNewScript + 1)) {
if (copyEmptyScriptOnFreeSpace()) {
m_numberOfScripts++;
m_lastEditedStringPosition = indexOfScriptContent(numberOfScripts()-1);
return true;
}
bool didCopy = false;
switch (defaultScript) {
case DefaultScript::Empty:
didCopy = copyEmptyScriptOnFreeSpace();
break;
case DefaultScript::Mandelbrot:
didCopy = copyMandelbrotScriptOnFreeSpace();
break;
case DefaultScript::Factorial:
didCopy = copyFactorialScriptOnFreeSpace();
break;
}
for (int i = beginningNewScript; i<k_historySize; i++) {
m_history[i] = FreeSpaceMarker;
}
return false;
}
bool ScriptStore::addFactorialScript() {
int sizeFreeSpace = sizeOfFreeSpace();
if (sizeFreeSpace < 4) {
return false;
}
cleanAndMoveFreeSpaceAfterScriptContent(numberOfScripts()-1);
int beginningNewScript = indexOfFirstFreeSpaceMarker();
m_history[beginningNewScript] = AutoImportationMarker;
if (copyName(beginningNewScript + 1, "factorial.py")) {
if (copyFactorialScriptOnFreeSpace()) {
m_numberOfScripts++;
m_lastEditedStringPosition = indexOfScriptContent(numberOfScripts()-1);
return true;
}
}
for (int i = beginningNewScript; i<k_historySize; i++) {
m_history[i] = FreeSpaceMarker;
}
return false;
}
bool ScriptStore::addMandelbrotScript() {
int sizeFreeSpace = sizeOfFreeSpace();
if (sizeFreeSpace < 5) {
return false;
}
cleanAndMoveFreeSpaceAfterScriptContent(numberOfScripts()-1);
int beginningNewScript = indexOfFirstFreeSpaceMarker();
m_history[beginningNewScript] = AutoImportationMarker;
if (copyName(beginningNewScript + 1, "mandelbrot.py")) {
if (copyMandelbrotScriptOnFreeSpace()) {
m_numberOfScripts++;
m_lastEditedStringPosition = indexOfScriptContent(numberOfScripts()-1);
return true;
}
}
for (int i = beginningNewScript; i<k_historySize; i++) {
m_history[i] = FreeSpaceMarker;
}
return false;
}
bool ScriptStore::renameScript(int i, const char * newName) {
assert (i >= 0 && i < numberOfScripts());
cleanAndMoveFreeSpaceAfterScriptName(i);
if (strlen(newName) <= sizeOfEditableNameOfScript(i)) {
copyName(indexOfScriptName(i), newName);
if (didCopy) {
return true;
}
// Delete the Auto Importation Marker and the Name Of the Script
m_accordion.deleteLastBuffer();
m_accordion.deleteLastBuffer();
return false;
}
void ScriptStore::deleteScript(int i) {
assert (i >= 0 && i < numberOfScripts());
cleanAndMoveFreeSpaceAfterScriptContent(i);
int indexOfCharToDelete = indexOfScript(i);
while (m_history[indexOfCharToDelete] != FreeSpaceMarker && indexOfCharToDelete < k_historySize) {
m_history[indexOfCharToDelete] = FreeSpaceMarker;
indexOfCharToDelete++;
}
m_numberOfScripts--;
bool ScriptStore::renameScriptAtIndex(int index, const char * newName) {
assert (index >= 0 && index < numberOfScripts());
int accordionIndex = accordionIndexOfNameOfScriptAtIndex(index);
return m_accordion.replaceBufferAtIndex(accordionIndex, newName);
}
void ScriptStore::deleteAll() {
for (int i = 0; i<k_historySize; i++){
m_history[i] = FreeSpaceMarker;
}
m_numberOfScripts = 0;
void ScriptStore::deleteScriptAtIndex(int index) {
assert (index >= 0 && index < numberOfScripts());
int accordionIndex = accordionIndexOfContentOfScriptAtIndex(index);
// We delete in reverse order because we want the indexes to stay true.
m_accordion.deleteBufferAtIndex(accordionIndex);
m_accordion.deleteBufferAtIndex(accordionIndex-1);
m_accordion.deleteBufferAtIndex(accordionIndex-2);
}
void ScriptStore::deleteAllScripts() {
m_accordion.deleteAll();
}
const char * ScriptStore::contentOfScript(const char * name) {
int filenameIndex = indexBeginningFilename(name);
const char * filename = &name[filenameIndex];
return script(filename).readOnlyContent();
Script script = scriptNamed(name);
if (script.isNull()) {
return nullptr;
}
return script.content();
}
bool ScriptStore::copyName(int position, const char * name) {
if (name) {
int len = strlen(name);
if (len > sizeOfFreeSpace() - 1) { // We keep at keast one free char.
return false;
}
memcpy(&m_history[position], name, len+1);
return true;
} else {
int len = strlen(k_defaultScriptName);
if (len > sizeOfFreeSpace() - 1) { // We keep at keast one free char.
return false;
}
memcpy(&m_history[position], k_defaultScriptName, len+1);
return true;
}
int ScriptStore::accordionIndexOfMarkersOfScriptAtIndex(int index) const {
return index * Script::NumberOfStringsPerScript;
}
int ScriptStore::indexOfScript(int i) const {
assert (i >= 0 && i < numberOfScripts());
int currentScriptNumber = 0;
int beginningOfScript = 0;
while (m_history[beginningOfScript] == FreeSpaceMarker && beginningOfScript < k_historySize) {
beginningOfScript++;
}
if (i == 0) {
return beginningOfScript;
}
bool goingThroughName = true;
for (int j=beginningOfScript; j<k_historySize; j++) {
if (m_history[j] == 0) {
if (goingThroughName) {
goingThroughName = false;
} else {
goingThroughName = true;
currentScriptNumber++;
if (currentScriptNumber == i) {
j++;
while (m_history[j] == FreeSpaceMarker && j < beginningOfScript) {
j++;
}
return j;
}
}
}
}
assert(false);
return 0;
int ScriptStore::accordionIndexOfNameOfScriptAtIndex(int index) const {
return index * Script::NumberOfStringsPerScript + 1;
}
int ScriptStore::indexOfScriptName(int i) const {
assert (i >= 0 && i < numberOfScripts());
return indexOfScript(i)+1;
}
int ScriptStore::indexOfScriptContent(int i) const {
assert (i >= 0 && i < numberOfScripts());
int indexOfScriptContent = indexOfScriptName(i);
while (m_history[indexOfScriptContent] != 0 && indexOfScriptContent<k_historySize) {
indexOfScriptContent++;
}
indexOfScriptContent++;
return indexOfScriptContent;
}
int ScriptStore::lastIndexOfScript(int i) const {
assert (i >= 0 && i < numberOfScripts());
int indexOfPrgm = indexOfScriptContent(i);
int lastIndexOfScript = indexOfPrgm + strlen(&m_history[indexOfPrgm]);
return lastIndexOfScript;
}
int ScriptStore::indexOfFirstFreeSpaceMarker() const {
for (int i=0; i<k_historySize; i++) {
if (m_history[i] == FreeSpaceMarker) {
return i;
}
}
assert(false);
return 0;
}
int ScriptStore::sizeOfFreeSpace() const {
int sizeOfFreeSpace = 0;
for (int i=0; i<k_historySize; i++) {
if (m_history[i] == FreeSpaceMarker) {
sizeOfFreeSpace++;
} else {
if (sizeOfFreeSpace > 0) {
return sizeOfFreeSpace;
}
}
}
return sizeOfFreeSpace;
}
void ScriptStore::cleanFreeSpace() {
if (m_history[m_lastEditedStringPosition] == FreeSpaceMarker
|| m_history[m_lastEditedStringPosition] == AutoImportationMarker
|| m_history[m_lastEditedStringPosition] == NoAutoImportationMarker)
{
return;
}
int indexOfCharToChangeIntoFreeSpaceMarker = m_lastEditedStringPosition
+ strlen (&m_history[m_lastEditedStringPosition]) + 1;
while (m_history[indexOfCharToChangeIntoFreeSpaceMarker] != FreeSpaceMarker
&& indexOfCharToChangeIntoFreeSpaceMarker<k_historySize)
{
m_history[indexOfCharToChangeIntoFreeSpaceMarker] = FreeSpaceMarker;
indexOfCharToChangeIntoFreeSpaceMarker ++;
}
}
void ScriptStore::moveFreeSpaceAfterScriptContent(int i) {
assert (i >= 0 && i < numberOfScripts());
int indexOfFreeSpace = indexOfFirstFreeSpaceMarker();
int newFreeSpacePosition = lastIndexOfScript(i) + 1;
if (indexOfFreeSpace < newFreeSpacePosition) {
newFreeSpacePosition -= sizeOfFreeSpace();
}
moveFreeSpaceAtPosition(newFreeSpacePosition);
}
void ScriptStore::moveFreeSpaceAfterScriptName(int i) {
assert (i >= 0 && i < numberOfScripts());
int newFreeSpacePosition = indexOfScriptName(i);
while (m_history[newFreeSpacePosition] != 0 && newFreeSpacePosition < k_historySize) {
newFreeSpacePosition++;
}
newFreeSpacePosition++;
moveFreeSpaceAtPosition(newFreeSpacePosition);
}
void ScriptStore::moveFreeSpaceAtPosition(int i) {
assert (i >= 0 && i < k_historySize);
int indexOfFreeSpace = indexOfFirstFreeSpaceMarker();
if (indexOfFreeSpace != i){
// First, move the chars that would be overriden by the free space.
int freeSpaceSize = sizeOfFreeSpace();
int len, src, dst;
// The indexes depend on the relative positions of the free space and the
// new destination.
if (indexOfFreeSpace > i) {
len = indexOfFreeSpace - i;
src = i;
dst = i + freeSpaceSize;
} else {
src = indexOfFreeSpace + freeSpaceSize;
len = i + freeSpaceSize - src;
dst = indexOfFreeSpace;
}
memmove(&m_history[dst], &m_history[src], len);
// Then move the free space.
for (int j = i ; j<i+freeSpaceSize; j++) {
m_history[j] = FreeSpaceMarker;
}
}
m_lastEditedStringPosition = i-1;
while (m_lastEditedStringPosition > 0
&& m_history[m_lastEditedStringPosition-1] != 0
&& m_history[m_lastEditedStringPosition-1] != AutoImportationMarker
&& m_history[m_lastEditedStringPosition-1] != NoAutoImportationMarker)
{
m_lastEditedStringPosition--;
}
}
void ScriptStore::cleanAndMoveFreeSpaceAfterScriptContent(int i) {
if (i >= 0 && i<numberOfScripts()){
cleanFreeSpace();
moveFreeSpaceAfterScriptContent(i);
}
}
void ScriptStore::cleanAndMoveFreeSpaceAfterScriptName(int i) {
if (i >= 0 && i<numberOfScripts()){
cleanFreeSpace();
moveFreeSpaceAfterScriptName(i);
}
int ScriptStore::accordionIndexOfContentOfScriptAtIndex(int index) const {
return index * Script::NumberOfStringsPerScript + 2;
}
bool ScriptStore::copyMandelbrotScriptOnFreeSpace() {
@@ -383,47 +166,21 @@ for x in range(320):
# Draw a pixel colored in 'col' at position (x,y)
kandinsky.set_pixel(x,y,col))";
int len = strlen(script);
if (len + 1 > sizeOfFreeSpace() - 1) { // We keep at keast one free char.
return false;
}
memcpy(&m_history[indexOfFirstFreeSpaceMarker()], script, len+1);
return true;
return m_accordion.appendBuffer(script);
}
bool ScriptStore::copyFactorialScriptOnFreeSpace() {
const char script[] = R"(def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n-1)
)";
return n * factorial(n-1))";
int len = strlen(script);
if (len + 1 > sizeOfFreeSpace() - 1) { // We keep at keast one free char.
return false;
}
memcpy(&m_history[indexOfFirstFreeSpaceMarker()], script, len+1);
return true;
return m_accordion.appendBuffer(script);
}
bool ScriptStore::copyEmptyScriptOnFreeSpace() {
const char script[] = "\0";
int len = 1;
if (len > sizeOfFreeSpace() - 1) { // We keep at keast one free char.
return false;
}
memcpy(&m_history[indexOfFirstFreeSpaceMarker()], script, len);
return true;
}
int ScriptStore::indexBeginningFilename(const char * path) {
int beginningFilename = strlen(path)-1;
while (beginningFilename > 0 && path[beginningFilename - 1] != '\\') {
beginningFilename--;
}
return beginningFilename;
return m_accordion.appendBuffer(script);
}
}

View File

@@ -2,74 +2,49 @@
#define CODE_SCRIPT_STORE_H
#include "script.h"
#include <escher/accordion.h>
#include <python/port/port.h>
namespace Code {
class ScriptStore : public MicroPython::ScriptProvider {
public:
enum class EditableZone {
None,
Name,
Content
};
enum class DefaultScript {
Empty,
Mandelbrot,
Factorial
};
ScriptStore();
Script editableScript(int i);
/* editableScript moves the free space of m_history at the end of the
* ith script. It returns a Script object that points to the beginning of the
* wanted script and has a length taking into account both the script and the
* free space. */
Script script(int i);
/* script returns a Script object that points to the beginning of the
* ith script and has the length of the script. */
Script script(const char * name);
/* script(const char * name) looks for a script that has the right name and
* returns it. If there is no such script, it returns an empty Script. */
char * nameOfScript(int i);
char * editableNameOfScript(int i);
int sizeOfEditableNameOfScript(int i);
int numberOfScripts() const;
bool addNewScript();
bool addMandelbrotScript();
bool addFactorialScript();
bool renameScript(int i, const char * newName);
void deleteScript(int i);
void deleteAll();
const Script scriptAtIndex(int index, EditableZone zone = EditableZone::None);
const Script scriptNamed(const char * name);
int numberOfScripts();
bool addNewScript(DefaultScript defaultScript = DefaultScript::Empty);
bool renameScriptAtIndex(int index, const char * newName);
void deleteScriptAtIndex(int index);
void deleteAllScripts();
/* MicroPython::ScriptProvider */
const char * contentOfScript(const char * name) override;
private:
static constexpr char FreeSpaceMarker = 0x01;
static constexpr char AutoImportationMarker = 0x02;
static constexpr char NoAutoImportationMarker = 0x03;
/* We made sure that these chars are not used in ion/include/ion/charset.h */
static constexpr int k_historySize = 1024;
static constexpr char k_defaultScriptName[] = ".py";
bool copyName(int position, const char * name = nullptr);
int indexOfScript(int i) const;
int indexOfScriptName(int i) const;
int indexOfScriptContent(int i) const;
int lastIndexOfScript(int i) const;
int indexOfFirstFreeSpaceMarker() const;
int sizeOfFreeSpace() const;
void cleanFreeSpace();
void moveFreeSpaceAfterScriptContent(int i);
void moveFreeSpaceAfterScriptName(int i);
void moveFreeSpaceAtPosition(int i);
void cleanAndMoveFreeSpaceAfterScriptContent(int i);
void cleanAndMoveFreeSpaceAfterScriptName(int i);
static constexpr size_t k_scriptDataSize = 1024;
int accordionIndexOfScriptAtIndex(int index) const;
int accordionIndexOfMarkersOfScriptAtIndex(int index) const;
int accordionIndexOfNameOfScriptAtIndex(int index) const;
int accordionIndexOfContentOfScriptAtIndex(int index) const;
bool copyMandelbrotScriptOnFreeSpace();
bool copyFactorialScriptOnFreeSpace();
bool copyEmptyScriptOnFreeSpace();
int indexBeginningFilename(const char * path);
char m_history[k_historySize];
/* The m_history variable sequentially stores scripts as text buffers.
* Each script is stored as follow:
* - First, a char that says whether the script should be automatically
* imported in the console.
* - Then, the name of the script.
* - Finally, the content of the script.
* The free bytes of m_history contain the FreeSpaceMarker. By construction,
* there is always at least one free byte, and the free space is always
* continuous. */
int m_numberOfScripts;
int m_lastEditedStringPosition;
char m_scriptData[k_scriptDataSize];
Accordion m_accordion;
};
}

View File

@@ -1,6 +1,7 @@
SFLAGS += -Iescher/include
objs += $(addprefix escher/src/,\
accordion.o\
alternate_empty_view_controller.o\
app.o\
buffer_text_view.o\

View File

@@ -1,6 +1,7 @@
#ifndef ESCHER_H
#define ESCHER_H
#include <escher/accordion.h>
#include <escher/alternate_empty_view_controller.h>
#include <escher/alternate_empty_view_delegate.h>
#include <escher/app.h>

View File

@@ -0,0 +1,51 @@
#ifndef ESCHER_ACCORDION_H
#define ESCHER_ACCORDION_H
/* Accordion sequentially stores null-terminated char buffers. It moves the free
* space at the end of a buffer if it will be edited. */
class Accordion {
public:
Accordion(char * buffer, int bufferSize);
int numberOfBuffers();
const char * bufferAtIndex(int index);
char * editableBufferAtIndex(int index);
int sizeOfEditableBufferAtIndex(int index);
/* sizeOfEditableBufferAtIndex appends the free space at the end of the buffer
* and returns the length of the buffer plus the free space, minus one free
* space char that we never edit in order to keep track of the position of the
* last edited buffer. */
bool appendBuffer(const char * buffer);
bool replaceBufferAtIndex(int index, const char * buffer);
void deleteBufferAtIndex(int index);
void deleteLastBuffer();
void deleteAll();
private:
static constexpr char k_freeSpaceMarker = 0x01;
int startOfBufferAtIndex(int index);
int endOfBufferAtIndex(int index);
int startOfFreeSpace();
int freeSpaceSize();
void cleanFreeSpace();
/* When a buffer is edited, there is garbage after the first null char of the
* buffer. cleanFreeSpace() declares the space after this null char as free,
* by marking it with the FreeSpaceMarker. Because we always keep at least one
* Free Space char out of the editable zone, cleanFreeSpace() just needs to
* put FreeSpaceMarkers after the first null char of the last edited Buffer,
* until the first FreeSpaceMarker char.
* WARNING: We have to call cleanFreeSpace() before any operation on m_history,
* otherwise m_history might contain garbage chars. */
void moveFreeSpaceAtPosition(int i);
void moveFreeSpaceAtEndOfBufferAtIndex(int index);
void moveFreeSpaceAtEndOfHistory();
int m_historySize;
char * m_history;
/* The m_history variable sequentially stores null-terminated char buffers.
* It also contains free space, which is marked with the FreeSpaceMarker. By
* construction, there is always at least one free byte, and the free space is
* always continuous. */
int m_numberOfBuffers;
int m_startOfLastEditedBuffer;
};
#endif

View File

@@ -7,7 +7,7 @@ class TextAreaDelegate {
public:
virtual bool textAreaShouldFinishEditing(TextArea * textArea, Ion::Events::Event event) = 0;
virtual bool textAreaDidReceiveEvent(TextArea * textArea, Ion::Events::Event event) = 0;
virtual Toolbox * toolboxForTextArea(TextArea * textFied) = 0;
virtual Toolbox * toolboxForTextArea(TextArea * textArea) = 0;
};
#endif

228
escher/src/accordion.cpp Normal file
View File

@@ -0,0 +1,228 @@
#include <escher/accordion.h>
#include <assert.h>
#include <string.h>
Accordion::Accordion(char * buffer, int bufferSize) :
m_historySize(bufferSize),
m_history(buffer),
m_numberOfBuffers(0),
m_startOfLastEditedBuffer(0)
{
for (int i = 0; i < m_historySize; i ++) {
m_history[i] = k_freeSpaceMarker;
}
}
int Accordion::numberOfBuffers() {
return m_numberOfBuffers;
}
const char * Accordion::bufferAtIndex(int index) {
assert(index >= 0 && index < numberOfBuffers());
cleanFreeSpace();
int startOfBuffer = startOfBufferAtIndex(index);
return &m_history[startOfBuffer];
}
char * Accordion::editableBufferAtIndex(int index) {
assert(index >= 0 && index < numberOfBuffers());
cleanFreeSpace();
moveFreeSpaceAtEndOfBufferAtIndex(index);
int startOfBuffer = startOfBufferAtIndex(index);
return &m_history[startOfBuffer];
}
int Accordion::sizeOfEditableBufferAtIndex(int index) {
assert(index >= 0 && index < numberOfBuffers());
cleanFreeSpace();
moveFreeSpaceAtEndOfBufferAtIndex(index);
int length = 0;
for (int i = startOfBufferAtIndex(index); i < m_historySize-1; i++) {
if (m_history[i] == k_freeSpaceMarker && m_history[i+1] != k_freeSpaceMarker) {
break;
}
length++;
// We do not count one Free Space Marker, in order to always have at a
// Free Space of sizeat least one.
}
return length;
}
bool Accordion::appendBuffer(const char * buffer) {
cleanFreeSpace();
moveFreeSpaceAtEndOfHistory();
int len = strlen(buffer);
if (len + 1 > freeSpaceSize() - 1) { // We keep at keast one Free char.
return false;
}
m_startOfLastEditedBuffer = startOfFreeSpace();
memcpy(&m_history[m_startOfLastEditedBuffer], buffer, len+1);
m_numberOfBuffers++;
return true;
}
bool Accordion::replaceBufferAtIndex(int index, const char * buffer) {
assert(index >= 0 && index < numberOfBuffers());
cleanFreeSpace();
int len = strlen(buffer);
if (len < sizeOfEditableBufferAtIndex(index)) {
int startOfOldBuffer = startOfBufferAtIndex(index);
memcpy(&m_history[startOfOldBuffer], buffer, len+1);
m_startOfLastEditedBuffer = startOfOldBuffer;
return true;
}
return false;
}
void Accordion::deleteBufferAtIndex(int index) {
assert(index >= 0 && index < numberOfBuffers());
cleanFreeSpace();
moveFreeSpaceAtEndOfBufferAtIndex(index);
int i = startOfBufferAtIndex(index);
while (i < m_historySize && m_history[i] != k_freeSpaceMarker) {
m_history[i] = k_freeSpaceMarker;
i++;
}
m_numberOfBuffers--;
}
void Accordion::deleteLastBuffer() {
cleanFreeSpace();
if (m_numberOfBuffers > 0) {
deleteBufferAtIndex(m_numberOfBuffers-1);
}
}
void Accordion::deleteAll() {
cleanFreeSpace();
for (int i = 0; i < m_historySize; i++){
m_history[i] = k_freeSpaceMarker;
}
m_numberOfBuffers = 0;
}
int Accordion::startOfBufferAtIndex(int index) {
assert(index >= 0 && index < numberOfBuffers());
cleanFreeSpace();
int bufferCount = 0;
int startOfBuffer = 0;
while (m_history[startOfBuffer] == k_freeSpaceMarker && startOfBuffer < m_historySize) {
startOfBuffer++;
}
for (int i = startOfBuffer; i < m_historySize; i++) {
if (bufferCount == index) {
while (m_history[i] == k_freeSpaceMarker && i < m_historySize) {
i++;
}
return i;
}
if (m_history[i] == 0) {
bufferCount++;
}
}
assert(false);
return 0;
}
int Accordion::endOfBufferAtIndex(int index) {
assert(index >= 0 && index < numberOfBuffers());
cleanFreeSpace();
int startOfBuffer = startOfBufferAtIndex(index);
for (int i = startOfBuffer; i < m_historySize; i++) {
if (m_history[i] == 0) {
return i;
}
}
assert(false);
return 0;
}
int Accordion::startOfFreeSpace() {
cleanFreeSpace();
for (int i = 0; i < m_historySize; i++) {
if (m_history[i] == k_freeSpaceMarker) {
return i;
}
}
assert(false);
return 0;
}
int Accordion::freeSpaceSize() {
cleanFreeSpace();
int sizeOfFreeSpace = 0;
int freeSpaceStart = startOfFreeSpace();
for (int i = freeSpaceStart; i < m_historySize; i++) {
if (m_history[i] == k_freeSpaceMarker) {
sizeOfFreeSpace++;
} else {
return sizeOfFreeSpace;
}
}
return sizeOfFreeSpace;
}
void Accordion::cleanFreeSpace() {
if (m_history[m_startOfLastEditedBuffer] == k_freeSpaceMarker) {
return;
}
int indexOfCharToChangeIntoFreeSpaceMarker = m_startOfLastEditedBuffer
+ strlen(&m_history[m_startOfLastEditedBuffer]) + 1;
while (m_history[indexOfCharToChangeIntoFreeSpaceMarker] != k_freeSpaceMarker
&& indexOfCharToChangeIntoFreeSpaceMarker < m_historySize)
{
m_history[indexOfCharToChangeIntoFreeSpaceMarker] = k_freeSpaceMarker;
indexOfCharToChangeIntoFreeSpaceMarker ++;
}
}
void Accordion::moveFreeSpaceAtPosition(int i) {
assert(i >= 0 && i <= m_historySize);
cleanFreeSpace();
int freeSpaceStart = startOfFreeSpace();
if (freeSpaceStart != i){
// First, move the chars that would be overriden by the free space.
// The indexes depend on the relative positions of the free space and the
// new destination.
int sizeFreeSpace = freeSpaceSize();
int len, src, dst, newFreeSpaceStart;
if (freeSpaceStart > i) {
len = freeSpaceStart - i;
src = i;
dst = i + sizeFreeSpace;
newFreeSpaceStart = i;
} else {
src = freeSpaceStart + sizeFreeSpace;
len = i - src;
dst = freeSpaceStart;
newFreeSpaceStart = i-sizeFreeSpace;
}
memmove(&m_history[dst], &m_history[src], len);
// Then move the free space.
for (int j = newFreeSpaceStart ; j < newFreeSpaceStart+sizeFreeSpace; j++) {
m_history[j] = k_freeSpaceMarker;
}
}
m_startOfLastEditedBuffer = i-1;
while (m_startOfLastEditedBuffer > 0 && m_history[m_startOfLastEditedBuffer-1] != 0 ) {
m_startOfLastEditedBuffer--;
}
}
void Accordion::moveFreeSpaceAtEndOfBufferAtIndex(int index) {
assert(index >= 0 && index < numberOfBuffers());
cleanFreeSpace();
int endOfBuffer = endOfBufferAtIndex(index);
moveFreeSpaceAtPosition(endOfBuffer+1);
}
void Accordion::moveFreeSpaceAtEndOfHistory() {
cleanFreeSpace();
if (m_numberOfBuffers > 0) {
moveFreeSpaceAtEndOfBufferAtIndex(m_numberOfBuffers-1);
}
}

View File

@@ -323,6 +323,7 @@ TextArea::TextArea(Responder * parentResponder, char * textBuffer,
void TextArea::setText(char * textBuffer, size_t textBufferSize) {
m_contentView.setText(textBuffer, textBufferSize);
m_contentView.moveCursorGeo(0, 0);
}
bool TextArea::TextArea::handleEvent(Ion::Events::Event event) {