diff --git a/apps/code/python_text_area.cpp b/apps/code/python_text_area.cpp index 5d45bf203..1ac729dd1 100644 --- a/apps/code/python_text_area.cpp +++ b/apps/code/python_text_area.cpp @@ -256,6 +256,7 @@ void PythonTextArea::addAutocompletion() { const char * autocompletionLocation = const_cast(cursorLocation()); const char * textToInsert = nullptr; + int textToInsertLength = 0; CodePoint prevCodePoint = UTF8Helper::PreviousCodePoint(m_contentView.editedText(), autocompletionLocation); if (!UTF8Helper::CodePointIsEndOfWord(prevCodePoint) && UTF8Helper::CodePointIsEndOfWord(UTF8Helper::CodePointAtLocation(autocompletionLocation))) @@ -267,11 +268,11 @@ void PythonTextArea::addAutocompletion() { * builtins, then in the imported modules/scripts. */ VariableBoxController * varBox = m_contentView.pythonDelegate()->variableBoxController(); const char * beginningOfWord = m_contentView.textToAutocomplete(); - textToInsert = varBox->autocompletionForText(m_contentView.pythonDelegate()->menuController()->editedScriptIndex(), beginningOfWord); + textToInsert = varBox->autocompletionForText(m_contentView.pythonDelegate()->menuController()->editedScriptIndex(), beginningOfWord, &textToInsertLength); } // Try to insert the text (this might fail if the buffer is full) - if (textToInsert && m_contentView.insertTextAtLocation(textToInsert, const_cast(autocompletionLocation))) { + if (textToInsert && textToInsertLength > 0 && m_contentView.insertTextAtLocation(textToInsert, const_cast(autocompletionLocation), textToInsertLength)) { m_contentView.setAutocompleting(true); } } diff --git a/apps/code/variable_box_controller.cpp b/apps/code/variable_box_controller.cpp index ce569135e..da723264b 100644 --- a/apps/code/variable_box_controller.cpp +++ b/apps/code/variable_box_controller.cpp @@ -331,7 +331,7 @@ void VariableBoxController::loadFunctionsAndVariables(int scriptIndex, const cha #endif } -const char * VariableBoxController::autocompletionForText(int scriptIndex, const char * text) { +const char * VariableBoxController::autocompletionForText(int scriptIndex, const char * text, int * textToInsertLength) { // TODO LEA Accelerate loadFunctionsAndVariables(scriptIndex, text); const char * endOfText = UTF8Helper::EndOfWord(text); @@ -342,6 +342,7 @@ const char * VariableBoxController::autocompletionForText(int scriptIndex, const const char * currentName = node->name(); int currentNameLength = node->nameLength(); if ((currentNameLength < 0 || currentNameLength != textLength) && strncmp(text, currentName, textLength) == 0) { + *textToInsertLength = currentNameLength - textLength; return currentName + textLength; } } diff --git a/apps/code/variable_box_controller.h b/apps/code/variable_box_controller.h index 14925510c..aa436dbb5 100644 --- a/apps/code/variable_box_controller.h +++ b/apps/code/variable_box_controller.h @@ -27,7 +27,7 @@ public: /* VariableBoxController */ void loadFunctionsAndVariables(int scriptIndex, const char * textToAutocomplete); - const char * autocompletionForText(int scriptIndex, const char * text); + const char * autocompletionForText(int scriptIndex, const char * text, int * textToInsertLength); private: constexpr static int k_maxScriptObjectNameSize = 100; constexpr static int k_maxNumberOfDisplayedRows = 6; // 240/40 diff --git a/escher/include/escher/text_area.h b/escher/include/escher/text_area.h index 7d7e58716..41c177a41 100644 --- a/escher/include/escher/text_area.h +++ b/escher/include/escher/text_area.h @@ -117,7 +117,7 @@ protected: const char * editedText() const override { return m_text.text(); } size_t editedTextLength() const override { return m_text.textLength(); } const Text * getText() const { return &m_text; } - bool insertTextAtLocation(const char * text, char * location) override; + bool insertTextAtLocation(const char * text, char * location, int textLength = -1) override; void moveCursorGeo(int deltaX, int deltaY); bool removePreviousGlyph() override; bool removeEndOfLine() override; diff --git a/escher/include/escher/text_field.h b/escher/include/escher/text_field.h index af995328c..b7394f2b5 100644 --- a/escher/include/escher/text_field.h +++ b/escher/include/escher/text_field.h @@ -74,7 +74,7 @@ protected: /* If the text to be appended is too long to be added without overflowing the * buffer, nothing is done (not even adding few letters from the text to reach * the maximum buffer capacity) and false is returned. */ - bool insertTextAtLocation(const char * text, char * location) override; + bool insertTextAtLocation(const char * text, char * location, int textLength = -1) override; KDSize minimalSizeForOptimalDisplay() const override; bool removePreviousGlyph() override; bool removeEndOfLine() override; diff --git a/escher/include/escher/text_input.h b/escher/include/escher/text_input.h index 82bb60106..34c75dcd8 100644 --- a/escher/include/escher/text_input.h +++ b/escher/include/escher/text_input.h @@ -49,7 +49,7 @@ protected: // Virtual text get/add/remove virtual const char * text() const = 0; - virtual bool insertTextAtLocation(const char * text, char * location) = 0; + virtual bool insertTextAtLocation(const char * text, char * location, int textLength = -1) = 0; virtual bool removePreviousGlyph() = 0; virtual bool removeEndOfLine() = 0; diff --git a/escher/src/text_area.cpp b/escher/src/text_area.cpp index 11dfc4045..8d4239df8 100644 --- a/escher/src/text_area.cpp +++ b/escher/src/text_area.cpp @@ -458,26 +458,19 @@ void TextArea::ContentView::setText(char * textBuffer, size_t textBufferSize) { m_cursorLocation = text(); } -bool TextArea::ContentView::insertTextAtLocation(const char * text, char * location) { - int textSize = strlen(text); - if (m_text.textLength() + textSize >= m_text.bufferSize() || textSize == 0) { +bool TextArea::ContentView::insertTextAtLocation(const char * text, char * location, int textLength) { + int textLen = textLength < 0 ? strlen(text) : textLength; + assert(textLen < 0 || textLen <= strlen(text)); + if (m_text.textLength() + textLen >= m_text.bufferSize() || textLen == 0) { return false; } - bool lineBreak = false; - // Scan for \n and 0 - const char * nullLocation = UTF8Helper::PerformAtCodePoints( - text, '\n', - [](int codePointOffset, void * lineBreak, int context1, int context2) { - *((bool *)lineBreak) = true; - }, - [](int c1, void * c2, int c3, int c4) { }, - &lineBreak, 0); + // Scan for \n + bool lineBreak = UTF8Helper::HasCodePoint(text, '\n', text + textLen); - assert(UTF8Helper::CodePointIs(nullLocation, 0)); - m_text.insertText(text, nullLocation - text, location); + m_text.insertText(text, textLen, location); // Replace System parentheses (used to keep layout tree structure) by normal parentheses - Poincare::SerializationHelper::ReplaceSystemParenthesesByUserParentheses(location, nullLocation - text); + Poincare::SerializationHelper::ReplaceSystemParenthesesByUserParentheses(location, textLen); reloadRectFromPosition(location, lineBreak); return true; } diff --git a/escher/src/text_field.cpp b/escher/src/text_field.cpp index bbabf8756..ebd9bd48a 100644 --- a/escher/src/text_field.cpp +++ b/escher/src/text_field.cpp @@ -119,10 +119,10 @@ void TextField::ContentView::reinitDraftTextBuffer() { setCursorLocation(s_draftTextBuffer); } -bool TextField::ContentView::insertTextAtLocation(const char * text, char * location) { +bool TextField::ContentView::insertTextAtLocation(const char * text, char * location, int textLen) { assert(m_isEditing); - int textLength = strlen(text); + int textLength = textLen < 0 ? strlen(text) : textLen; if (m_currentDraftTextLength + textLength >= m_draftTextBufferSize || textLength == 0) { return false; } @@ -130,12 +130,12 @@ bool TextField::ContentView::insertTextAtLocation(const char * text, char * loca memmove(location + textLength, location, (s_draftTextBuffer + m_currentDraftTextLength + 1) - location); // Caution! One byte will be overridden by the null-terminating char of strlcpy - char * overridenByteLocation = location + textLength; + size_t copySize = std::min(textLength, (s_draftTextBuffer + m_draftTextBufferSize) - location); + char * overridenByteLocation = location + copySize; char overridenByte = *overridenByteLocation; - strlcpy(location, text, (s_draftTextBuffer + m_draftTextBufferSize) - location); - assert(overridenByteLocation < s_draftTextBuffer + m_draftTextBufferSize); + strlcpy(location, text, copySize); *overridenByteLocation = overridenByte; - m_currentDraftTextLength += textLength; + m_currentDraftTextLength += copySize; reloadRectFromPosition(m_horizontalAlignment == 0.0f ? location : s_draftTextBuffer); return true; diff --git a/ion/include/ion/unicode/utf8_helper.h b/ion/include/ion/unicode/utf8_helper.h index 38878989c..17ff9385f 100644 --- a/ion/include/ion/unicode/utf8_helper.h +++ b/ion/include/ion/unicode/utf8_helper.h @@ -11,10 +11,10 @@ int CountOccurrences(const char * s, CodePoint c); /* Returns the first occurence of a code point in a string, the position of the * null terminating char otherwise. */ -const char * CodePointSearch(const char * s, CodePoint c); +const char * CodePointSearch(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); +bool HasCodePoint(const char * s, CodePoint c, const char * stoppingPosition = nullptr); /* Returns the first occurence of a code point that is not c in a string, * stopping at the null-terminating char or the start of string. */ diff --git a/ion/src/shared/unicode/utf8_helper.cpp b/ion/src/shared/unicode/utf8_helper.cpp index 6f2d3e41e..d15c897c6 100644 --- a/ion/src/shared/unicode/utf8_helper.cpp +++ b/ion/src/shared/unicode/utf8_helper.cpp @@ -33,10 +33,10 @@ int CountOccurrences(const char * s, CodePoint c) { return count; } -const char * CodePointSearch(const char * s, CodePoint c) { +const char * CodePointSearch(const char * s, CodePoint c, const char * stoppingPosition) { if (UTF8Decoder::CharSizeOfCodePoint(c) == 1) { const char * result = s; - while (*result != 0 && *result != c) { + while (*result != 0 && *result != c && (stoppingPosition == nullptr || result != stoppingPosition)) { result++; } return result; @@ -45,7 +45,7 @@ const char * CodePointSearch(const char * s, CodePoint c) { const char * currentPointer = s; CodePoint codePoint = decoder.nextCodePoint(); const char * nextPointer = decoder.stringPosition(); - while (codePoint != UCodePointNull && codePoint != c) { + while (codePoint != UCodePointNull && codePoint != c && (stoppingPosition == nullptr || currentPointer < stoppingPosition)) { currentPointer = nextPointer; codePoint = decoder.nextCodePoint(); nextPointer = decoder.stringPosition(); @@ -53,9 +53,10 @@ const char * CodePointSearch(const char * s, CodePoint c) { return currentPointer; } -bool HasCodePoint(const char * s, CodePoint c) { +bool HasCodePoint(const char * s, CodePoint c, const char * stoppingPosition) { assert(c != 0); - return *CodePointSearch(s, c) != 0; + const char * resultPosition = CodePointSearch(s, c, stoppingPosition); + return *resultPosition != 0 && (stoppingPosition == nullptr || resultPosition < stoppingPosition); } const char * NotCodePointSearch(const char * s, CodePoint c, bool goingLeft, const char * initialPosition) {