1
0
mirror of synced 2025-01-09 13:11:38 +01:00

feat: Added multiline search/replace and fixed various crashes. (#1911)

### Problem description
Previous implementation ignored everything after the first newline.
Other changes include:
- Added isEmpty() function that checks if editor has no content.
- replaced asserts with default behavior to avoid unneeded crashes.
- fixed off by one error in DeleteRange()
- some crashes occurred because readily available corrective actions
were not being taken. For example start/end being -1 in DeleteRange().
- At the heart of search/replace is the ability to translate from
indices into the text string to line/column coordinates used for
everything. To this end a function (StringIndexToCoordinates) was added
to do the translation taking utf-8 chars into account. This made the
recently added Utf8BytesToChars function unneeded, so it was removed.
- Removed commented out code that is not useful anymore. Also removed
tooltip code which is also unused.
- Removed unused parameter wrapAround to FindNext().
This commit is contained in:
paxcut 2024-11-29 01:37:05 -07:00 committed by GitHub
parent 05c25b6aff
commit 6d2761f141
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 87 additions and 134 deletions

View File

@ -211,6 +211,10 @@ public:
void Render(const char* aTitle, const ImVec2& aSize = ImVec2(), bool aBorder = false); void Render(const char* aTitle, const ImVec2& aSize = ImVec2(), bool aBorder = false);
void SetText(const std::string& aText); void SetText(const std::string& aText);
std::string GetText() const; std::string GetText() const;
bool isEmpty() const {
auto text = GetText();
return text.empty() || text == "\n";
}
void SetTextLines(const std::vector<std::string>& aLines); void SetTextLines(const std::vector<std::string>& aLines);
std::vector<std::string> GetTextLines() const; std::vector<std::string> GetTextLines() const;
@ -314,7 +318,7 @@ public:
FindReplaceHandler(); FindReplaceHandler();
typedef std::vector<EditorState> Matches; typedef std::vector<EditorState> Matches;
Matches &GetMatches() { return mMatches; } Matches &GetMatches() { return mMatches; }
bool FindNext(TextEditor *editor,bool wrapAround); bool FindNext(TextEditor *editor);
unsigned FindMatch(TextEditor *editor,bool isNex); unsigned FindMatch(TextEditor *editor,bool isNex);
bool Replace(TextEditor *editor,bool right); bool Replace(TextEditor *editor,bool right);
bool ReplaceAll(TextEditor *editor); bool ReplaceAll(TextEditor *editor);
@ -428,10 +432,10 @@ private:
Coordinates FindWordStart(const Coordinates& aFrom) const; Coordinates FindWordStart(const Coordinates& aFrom) const;
Coordinates FindWordEnd(const Coordinates& aFrom) const; Coordinates FindWordEnd(const Coordinates& aFrom) const;
Coordinates FindNextWord(const Coordinates& aFrom) const; Coordinates FindNextWord(const Coordinates& aFrom) const;
Coordinates StringIndexToCoordinates(int aIndex, const std::string &str) const;
int GetCharacterIndex(const Coordinates& aCoordinates) const; int GetCharacterIndex(const Coordinates& aCoordinates) const;
int GetCharacterColumn(int aLine, int aIndex) const; int GetCharacterColumn(int aLine, int aIndex) const;
int GetLineCharacterCount(int aLine) const; int GetLineCharacterCount(int aLine) const;
int Utf8BytesToChars(const Coordinates &aCoordinates) const;
int Utf8CharsToBytes(const Coordinates &aCoordinates) const; int Utf8CharsToBytes(const Coordinates &aCoordinates) const;
unsigned long long GetLineByteCount(int aLine) const; unsigned long long GetLineByteCount(int aLine) const;
int GetStringCharacterCount(std::string str) const; int GetStringCharacterCount(std::string str) const;

View File

@ -123,7 +123,7 @@ TextEditor::Coordinates TextEditor::SanitizeCoordinates(const Coordinates &aValu
auto line = aValue.mLine; auto line = aValue.mLine;
auto column = aValue.mColumn; auto column = aValue.mColumn;
if (line >= (int)mLines.size()) { if (line >= (int)mLines.size()) {
if (mLines.empty()) { if (isEmpty()) {
line = 0; line = 0;
column = 0; column = 0;
} else { } else {
@ -132,7 +132,7 @@ TextEditor::Coordinates TextEditor::SanitizeCoordinates(const Coordinates &aValu
} }
return Coordinates(line, column); return Coordinates(line, column);
} else { } else {
column = mLines.empty() ? 0 : std::min(column, GetLineMaxColumn(line)); column = isEmpty() ? 0 : std::min(column, GetLineMaxColumn(line));
return Coordinates(line, column); return Coordinates(line, column);
} }
} }
@ -213,6 +213,8 @@ void TextEditor::DeleteRange(const Coordinates &aStart, const Coordinates &aEnd)
auto start = GetCharacterIndex(aStart); auto start = GetCharacterIndex(aStart);
auto end = GetCharacterIndex(aEnd); auto end = GetCharacterIndex(aEnd);
if (start == -1 || end == -1)
return;
if (aStart.mLine == aEnd.mLine) { if (aStart.mLine == aEnd.mLine) {
auto &line = mLines[aStart.mLine]; auto &line = mLines[aStart.mLine];
@ -232,7 +234,7 @@ void TextEditor::DeleteRange(const Coordinates &aStart, const Coordinates &aEnd)
firstLine.insert(firstLine.end(), lastLine.begin(), lastLine.end()); firstLine.insert(firstLine.end(), lastLine.begin(), lastLine.end());
if (aStart.mLine < aEnd.mLine) if (aStart.mLine < aEnd.mLine)
RemoveLine(aStart.mLine + 1, aEnd.mLine + 1); RemoveLine(aStart.mLine + 1, aEnd.mLine);
} }
mTextChanged = true; mTextChanged = true;
@ -242,7 +244,10 @@ int TextEditor::InsertTextAt(Coordinates & /* inout */ aWhere, const char *aValu
int cindex = GetCharacterIndex(aWhere); int cindex = GetCharacterIndex(aWhere);
int totalLines = 0; int totalLines = 0;
while (*aValue != '\0') { while (*aValue != '\0') {
assert(!mLines.empty()); if (mLines.empty()) {
mLines.push_back(Line());
mTextChanged = true;
}
if (*aValue == '\r') { if (*aValue == '\r') {
// skip // skip
@ -422,7 +427,7 @@ TextEditor::Coordinates TextEditor::FindNextWord(const Coordinates &aFrom) const
bool isword = false; bool isword = false;
bool skip = false; bool skip = false;
if (cindex < (int)mLines[at.mLine].size()) { if (cindex < (int)mLines[at.mLine].size()) {
auto &line = mLines[at.mLine]; const auto &line = mLines[at.mLine];
isword = isalnum(line[cindex].mChar); isword = isalnum(line[cindex].mChar);
skip = isword; skip = isword;
} }
@ -455,26 +460,12 @@ TextEditor::Coordinates TextEditor::FindNextWord(const Coordinates &aFrom) const
return at; return at;
} }
int TextEditor::Utf8BytesToChars(const Coordinates &aCoordinates) const {
if (aCoordinates.mLine >= mLines.size())
return -1;
auto &line = mLines[aCoordinates.mLine];
int c = 0;
int i = 0;
while (i < aCoordinates.mColumn) {
i += UTF8CharLength(line[i].mChar);
if (line[i].mChar == '\t')
c = (c / mTabSize) * mTabSize + mTabSize;
else
++c;
}
return c;
}
int TextEditor::Utf8CharsToBytes(const Coordinates &aCoordinates) const { int TextEditor::Utf8CharsToBytes(const Coordinates &aCoordinates) const {
if (aCoordinates.mLine >= mLines.size()) if (aCoordinates.mLine >= mLines.size())
return -1; return -1;
auto &line = mLines[aCoordinates.mLine]; auto &line = mLines[aCoordinates.mLine];
if (line.empty())
return 0;
int c = 0; int c = 0;
int i = 0; int i = 0;
while (i < line.size() && c < aCoordinates.mColumn) { while (i < line.size() && c < aCoordinates.mColumn) {
@ -487,6 +478,18 @@ int TextEditor::Utf8CharsToBytes(const Coordinates &aCoordinates) const {
return i; return i;
} }
TextEditor::Coordinates TextEditor::StringIndexToCoordinates(int aIndex, const std::string &input ) const {
if (aIndex < 0 || aIndex > (int)input.size())
return Coordinates(0, 0);
std::string str = input.substr(0, aIndex);
auto line = std::count(str.begin(),str.end(),'\n');
auto index = str.find_last_of('\n');
str = str.substr(index+1);
auto col = GetStringCharacterCount(str);
return Coordinates(line, col);
}
int TextEditor::GetCharacterIndex(const Coordinates &aCoordinates) const { int TextEditor::GetCharacterIndex(const Coordinates &aCoordinates) const {
if (aCoordinates.mLine >= mLines.size()) if (aCoordinates.mLine >= mLines.size())
return -1; return -1;
@ -580,7 +583,6 @@ bool TextEditor::IsOnWordBoundary(const Coordinates &aAt) const {
void TextEditor::RemoveLine(int aStart, int aEnd) { void TextEditor::RemoveLine(int aStart, int aEnd) {
assert(!mReadOnly); assert(!mReadOnly);
assert(aEnd >= aStart); assert(aEnd >= aStart);
assert(mLines.size() > (size_t)(aEnd - aStart));
ErrorMarkers etmp; ErrorMarkers etmp;
for (auto &i : mErrorMarkers) { for (auto &i : mErrorMarkers) {
@ -601,12 +603,12 @@ void TextEditor::RemoveLine(int aStart, int aEnd) {
btmp.insert(breakpoint); btmp.insert(breakpoint);
} }
} }
if (mBreakPointsChanged)
mBreakpoints = std::move(btmp); mBreakpoints = std::move(btmp);
if (aStart == 0 && aEnd == (int32_t)mLines.size() - 1) // use clamp to ensure valid results instead of assert.
mLines.erase(mLines.begin() + aStart, mLines.end()); auto start = std::clamp(aStart, 0, (int)mLines.size()-1);
else auto end = std::clamp(aEnd, 0, (int)mLines.size());
mLines.erase(mLines.begin() + aStart, mLines.begin() + aEnd + 1); mLines.erase(mLines.begin() + aStart, mLines.begin() + aEnd + 1);
mTextChanged = true; mTextChanged = true;
} }
@ -642,7 +644,16 @@ void TextEditor::RemoveLine(int aIndex) {
} }
TextEditor::Line &TextEditor::InsertLine(int aIndex) { TextEditor::Line &TextEditor::InsertLine(int aIndex) {
auto &result = *mLines.insert(mLines.begin() + aIndex, Line());
if (isEmpty())
return *mLines.insert(mLines.begin(), Line());
if (aIndex == mLines.size())
return *mLines.insert(mLines.end(), Line());
auto newLine = Line();
TextEditor::Line &result = *mLines.insert(mLines.begin() + aIndex, newLine);
ErrorMarkers etmp; ErrorMarkers etmp;
for (auto &i : mErrorMarkers) for (auto &i : mErrorMarkers)
@ -717,22 +728,10 @@ void TextEditor::HandleKeyboardInputs() {
auto ctrl = io.KeyCtrl; auto ctrl = io.KeyCtrl;
auto alt = io.KeyAlt; auto alt = io.KeyAlt;
auto shift = io.KeyShift; auto shift = io.KeyShift;
/* auto left = ImGui::IsKeyPressed(ImGuiKey_LeftArrow);
auto right = ImGui::IsKeyPressed(ImGuiKey_RightArrow);
auto up = ImGui::IsKeyPressed(ImGuiKey_UpArrow);
auto down = ImGui::IsKeyPressed(ImGuiKey_DownArrow);
auto home = io.ConfigMacOSXBehaviors ? io.KeySuper && left : ImGui::IsKeyPressed(ImGuiKey_Home);
auto end = io.ConfigMacOSXBehaviors ? io.KeySuper && right : ImGui::IsKeyPressed(ImGuiKey_End);
auto top = io.ConfigMacOSXBehaviors ? io.KeySuper && up : ctrl && ImGui::IsKeyPressed(ImGuiKey_Home);
auto bottom = io.ConfigMacOSXBehaviors ? io.KeySuper && down : ctrl && ImGui::IsKeyPressed(ImGuiKey_End);
auto pageUp = io.ConfigMacOSXBehaviors ? ctrl && up : ImGui::IsKeyPressed(ImGuiKey_PageUp);
auto pageDown = io.ConfigMacOSXBehaviors ? ctrl && down : ImGui::IsKeyPressed(ImGuiKey_PageDown);
*/
if (ImGui::IsWindowFocused()) { if (ImGui::IsWindowFocused()) {
if (ImGui::IsWindowHovered()) if (ImGui::IsWindowHovered())
ImGui::SetMouseCursor(ImGuiMouseCursor_TextInput); ImGui::SetMouseCursor(ImGuiMouseCursor_TextInput);
// ImGui::CaptureKeyboardFromApp(true);
io.WantCaptureKeyboard = true; io.WantCaptureKeyboard = true;
io.WantTextInput = true; io.WantTextInput = true;
@ -865,7 +864,7 @@ void TextEditor::Render() {
auto scrollX = ImGui::GetScrollX(); auto scrollX = ImGui::GetScrollX();
auto scrollY = ImGui::GetScrollY(); auto scrollY = ImGui::GetScrollY();
auto lineNo = (int)(std::floor(scrollY / mCharAdvance.y));// + linesAdded); auto lineNo = (int)(std::floor(scrollY / mCharAdvance.y));
auto globalLineMax = (int)mLines.size(); auto globalLineMax = (int)mLines.size();
auto lineMax = std::max(0, std::min((int)mLines.size() - 1, lineNo + (int)std::ceil((scrollY + contentSize.y) / mCharAdvance.y))); auto lineMax = std::max(0, std::min((int)mLines.size() - 1, lineNo + (int)std::ceil((scrollY + contentSize.y) / mCharAdvance.y)));
@ -878,7 +877,7 @@ void TextEditor::Render() {
buf[0] = '\0'; buf[0] = '\0';
mTextStart = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf, nullptr, nullptr).x + mLeftMargin; mTextStart = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf, nullptr, nullptr).x + mLeftMargin;
if (!mLines.empty()) { if (!isEmpty()) {
float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ", nullptr, nullptr).x; float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ", nullptr, nullptr).x;
while (lineNo <= lineMax) { while (lineNo <= lineMax) {
@ -1048,25 +1047,6 @@ void TextEditor::Render() {
if (lineNo < mLines.size() && ImGui::GetScrollMaxX() > 0.0f) if (lineNo < mLines.size() && ImGui::GetScrollMaxX() > 0.0f)
longest = std::max(mTextStart + TextDistanceToLineStart(Coordinates(lineNo, GetLineMaxColumn(lineNo))), longest); longest = std::max(mTextStart + TextDistanceToLineStart(Coordinates(lineNo, GetLineMaxColumn(lineNo))), longest);
// Draw a tooltip on known identifiers/preprocessor symbols
if (ImGui::IsMousePosValid()) {
auto id = GetWordAt(ScreenPosToCoordinates(ImGui::GetMousePos()));
if (!id.empty()) {
auto it = mLanguageDefinition.mIdentifiers.find(id);
if (it != mLanguageDefinition.mIdentifiers.end() && !it->second.mDeclaration.empty()) {
ImGui::BeginTooltip();
ImGui::TextUnformatted(it->second.mDeclaration.c_str());
ImGui::EndTooltip();
} else {
auto pi = mLanguageDefinition.mPreprocIdentifiers.find(id);
if (pi != mLanguageDefinition.mPreprocIdentifiers.end() && !pi->second.mDeclaration.empty()) {
ImGui::BeginTooltip();
ImGui::TextUnformatted(pi->second.mDeclaration.c_str());
ImGui::EndTooltip();
}
}
}
}
} }
ImGui::Dummy(ImVec2((longest + 2), mLines.size() * mCharAdvance.y)); ImGui::Dummy(ImVec2((longest + 2), mLines.size() * mCharAdvance.y));
@ -1098,8 +1078,7 @@ void TextEditor::Render() {
auto popupStack = g.OpenPopupStack; auto popupStack = g.OpenPopupStack;
if (popupStack.Size > 0) { if (popupStack.Size > 0) {
for (int n = 0; n < popupStack.Size; n++){ for (int n = 0; n < popupStack.Size; n++){
auto window = popupStack[n].Window; if (auto window = popupStack[n].Window; window != nullptr) {
if (window != nullptr) {
if (window->Size.x == mFindReplaceHandler.GetFindWindowSize().x && if (window->Size.x == mFindReplaceHandler.GetFindWindowSize().x &&
window->Size.y == mFindReplaceHandler.GetFindWindowSize().y && window->Size.y == mFindReplaceHandler.GetFindWindowSize().y &&
window->Pos.x == mFindReplaceHandler.GetFindWindowPos().x && window->Pos.x == mFindReplaceHandler.GetFindWindowPos().x &&
@ -1112,11 +1091,9 @@ void TextEditor::Render() {
mTopMargin = 0; mTopMargin = 0;
} }
static float linesAdded = 0;
static float pixelsAdded = 0;
static float savedScrollY = 0;
static float shiftedScrollY = 0;
if (mTopMargin != oldTopMargin) { if (mTopMargin != oldTopMargin) {
static float savedScrollY = 0;
if (oldTopMargin == 0) if (oldTopMargin == 0)
savedScrollY = ImGui::GetScrollY(); savedScrollY = ImGui::GetScrollY();
auto window = ImGui::GetCurrentWindow(); auto window = ImGui::GetCurrentWindow();
@ -1124,6 +1101,7 @@ void TextEditor::Render() {
if (maxScroll > 0) { if (maxScroll > 0) {
float lineCount; float lineCount;
float pixelCount; float pixelCount;
static float linesAdded = 0;
if (mTopMargin > oldTopMargin) { if (mTopMargin > oldTopMargin) {
pixelCount = mTopMargin - oldTopMargin; pixelCount = mTopMargin - oldTopMargin;
lineCount = pixelCount / mCharAdvance.y; lineCount = pixelCount / mCharAdvance.y;
@ -1148,6 +1126,7 @@ void TextEditor::Render() {
else else
mLines.erase(mLines.begin() + mLines.size() - 1); mLines.erase(mLines.begin() + mLines.size() - 1);
} }
static float pixelsAdded = 0;
if (mTopMargin > oldTopMargin) { if (mTopMargin > oldTopMargin) {
linesAdded += lineCount; linesAdded += lineCount;
pixelsAdded += pixelCount; pixelsAdded += pixelCount;
@ -1159,6 +1138,7 @@ void TextEditor::Render() {
pixelsAdded = 0; pixelsAdded = 0;
} }
if (oldScrollY + pixelCount < maxScroll) { if (oldScrollY + pixelCount < maxScroll) {
static float shiftedScrollY = 0;
if (mTopMargin > oldTopMargin) if (mTopMargin > oldTopMargin)
shiftedScrollY = oldScrollY + pixelCount; shiftedScrollY = oldScrollY + pixelCount;
else if (mTopMargin > 0) else if (mTopMargin > 0)
@ -1211,15 +1191,15 @@ void TextEditor::Render(const char *aTitle, const ImVec2 &aSize, bool aBorder) {
} }
void TextEditor::SetText(const std::string &aText) { void TextEditor::SetText(const std::string &aText) {
mLines.clear(); mLines.resize(1);
mLines.emplace_back(Line()); mLines[0].clear();
for (auto chr : aText) { for (auto chr : aText) {
if (chr == '\r') { if (chr == '\r') {
// ignore the carriage return character // ignore the carriage return character
} else if (chr == '\n') } else if (chr == '\n')
mLines.emplace_back(Line()); mLines.push_back(Line());
else { else {
mLines.back().emplace_back(Glyph(chr, PaletteIndex::Default)); mLines.back().push_back(Glyph(chr, PaletteIndex::Default));
} }
} }
@ -1235,7 +1215,7 @@ void TextEditor::SetText(const std::string &aText) {
void TextEditor::SetTextLines(const std::vector<std::string> &aLines) { void TextEditor::SetTextLines(const std::vector<std::string> &aLines) {
mLines.clear(); mLines.clear();
if (aLines.empty()) { if (isEmpty()) {
mLines.emplace_back(Line()); mLines.emplace_back(Line());
} else { } else {
mLines.resize(aLines.size()); mLines.resize(aLines.size());
@ -1281,7 +1261,7 @@ void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) {
if (end.mColumn == 0 && end.mLine > 0) if (end.mColumn == 0 && end.mLine > 0)
--end.mLine; --end.mLine;
if (end.mLine >= (int)mLines.size()) if (end.mLine >= (int)mLines.size())
end.mLine = mLines.empty() ? 0 : (int)mLines.size() - 1; end.mLine = isEmpty() ? 0 : (int)mLines.size() - 1;
end.mColumn = GetLineMaxColumn(end.mLine); end.mColumn = GetLineMaxColumn(end.mLine);
u.mRemovedStart = start; u.mRemovedStart = start;
@ -1350,6 +1330,10 @@ void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) {
auto coord = GetActualCursorCoordinates(); auto coord = GetActualCursorCoordinates();
u.mAddedStart = coord; u.mAddedStart = coord;
if (mLines.empty())
mLines.push_back(Line());
if (aChar == '\n') { if (aChar == '\n') {
InsertLine(coord.mLine + 1); InsertLine(coord.mLine + 1);
auto &line = mLines[coord.mLine]; auto &line = mLines[coord.mLine];
@ -1613,7 +1597,7 @@ void TextEditor::MoveLeft(int aAmount, bool aSelect, bool aWordMode) {
auto oldPos = mState.mCursorPosition; auto oldPos = mState.mCursorPosition;
ResetCursorBlinkTime(); ResetCursorBlinkTime();
if (mLines.empty() || oldPos.mLine >= mLines.size()) if (isEmpty() || oldPos.mLine >= mLines.size())
return; return;
mState.mCursorPosition = GetActualCursorCoordinates(); mState.mCursorPosition = GetActualCursorCoordinates();
@ -1670,7 +1654,7 @@ void TextEditor::MoveRight(int aAmount, bool aSelect, bool aWordMode) {
ResetCursorBlinkTime(); ResetCursorBlinkTime();
auto oldPos = mState.mCursorPosition; auto oldPos = mState.mCursorPosition;
if (mLines.empty() || oldPos.mLine >= mLines.size()) if (isEmpty() || oldPos.mLine >= mLines.size())
return; return;
mState.mCursorPosition = GetActualCursorCoordinates(); mState.mCursorPosition = GetActualCursorCoordinates();
@ -1796,7 +1780,7 @@ void TextEditor::Delete() {
ResetCursorBlinkTime(); ResetCursorBlinkTime();
assert(!mReadOnly); assert(!mReadOnly);
if (mLines.empty()) if (isEmpty())
return; return;
UndoRecord u; UndoRecord u;
@ -1853,7 +1837,7 @@ void TextEditor::Backspace() {
ResetCursorBlinkTime(); ResetCursorBlinkTime();
assert(!mReadOnly); assert(!mReadOnly);
if (mLines.empty()) if (isEmpty())
return; return;
UndoRecord u; UndoRecord u;
@ -1932,14 +1916,14 @@ void TextEditor::SelectAll() {
} }
bool TextEditor::HasSelection() const { bool TextEditor::HasSelection() const {
return mState.mSelectionEnd > mState.mSelectionStart; return !isEmpty() && mState.mSelectionEnd > mState.mSelectionStart;
} }
void TextEditor::Copy() { void TextEditor::Copy() {
if (HasSelection()) { if (HasSelection()) {
ImGui::SetClipboardText(GetSelectedText().c_str()); ImGui::SetClipboardText(GetSelectedText().c_str());
} else { } else {
if (!mLines.empty()) { if (!isEmpty()) {
std::string str; std::string str;
auto &line = mLines[GetActualCursorCoordinates().mLine]; auto &line = mLines[GetActualCursorCoordinates().mLine];
for (auto &g : line) for (auto &g : line)
@ -2165,13 +2149,13 @@ std::string make_wholeWord(const std::string &s) {
} }
// Performs actual search to fill mMatches // Performs actual search to fill mMatches
bool TextEditor::FindReplaceHandler::FindNext(TextEditor *editor, bool wrapAround) { bool TextEditor::FindReplaceHandler::FindNext(TextEditor *editor) {
Coordinates curPos; Coordinates curPos;
curPos.mLine = mMatches.empty() ? editor->mState.mCursorPosition.mLine : mMatches.back().mCursorPosition.mLine; curPos.mLine = mMatches.empty() ? editor->mState.mCursorPosition.mLine : mMatches.back().mCursorPosition.mLine;
curPos.mColumn = mMatches.empty() ? editor->mState.mCursorPosition.mColumn : editor->Utf8CharsToBytes( curPos.mColumn = mMatches.empty() ? editor->mState.mCursorPosition.mColumn : editor->Utf8CharsToBytes(
mMatches.back().mCursorPosition); mMatches.back().mCursorPosition);
unsigned long selectionLength = editor->GetStringCharacterCount(mFindWord); unsigned long matchLength = editor->GetStringCharacterCount(mFindWord);
size_t byteIndex = 0; size_t byteIndex = 0;
for (size_t ln = 0; ln < curPos.mLine; ln++) for (size_t ln = 0; ln < curPos.mLine; ln++)
@ -2218,67 +2202,32 @@ bool TextEditor::FindReplaceHandler::FindNext(TextEditor *editor, bool wrapAroun
if(firstLoc > byteIndex) { if(firstLoc > byteIndex) {
pos = firstLoc; pos = firstLoc;
selectionLength = firstLength; matchLength = firstLength;
} else { } else {
while (iter != end) { while (iter != end) {
iter++; iter++;
if (((pos = iter->position()) > byteIndex) && ((selectionLength = iter->length()) > 0)) if (((pos = iter->position()) > byteIndex) && ((matchLength = iter->length()) > 0))
break; break;
} }
} }
if (iter == end && !wrapAround) if (iter == end)
return false; return false;
textLoc = pos; textLoc = pos;
if (wrapAround) {
if (iter == end)
selectionLength = firstLength;
}
} else { } else {
// non regex search // non regex search
textLoc = textSrc.find(wordLower, byteIndex); textLoc = textSrc.find(wordLower, byteIndex);
if (textLoc == std::string::npos) { if (textLoc == std::string::npos)
if (wrapAround) return false;
textLoc = textSrc.find(wordLower, 0);
else
return false;
}
} }
if (textLoc != std::string::npos) { if (textLoc == std::string::npos)
curPos.mLine = curPos.mColumn = 0;
byteIndex = 0;
for (size_t ln = 0; ln < editor->mLines.size(); ln++) {
auto byteCount = editor->GetLineByteCount(ln) + 1;
if (byteIndex + byteCount > textLoc) {
curPos.mLine = ln;
curPos.mColumn = textLoc - byteIndex;
auto &line = editor->mLines[curPos.mLine];
int lineSize = line.size();
for (int i = 0; i < std::min(lineSize,curPos.mColumn); i++) {
if (line[i].mChar == '\t')
curPos.mColumn += (editor->mTabSize - 1);
}
break;
} else {// just keep adding
byteIndex += byteCount;
}
}
} else
return false; return false;
Coordinates selStart, selEnd;
selStart.mLine = curPos.mLine;
selStart.mColumn = editor->Utf8BytesToChars(curPos);
selEnd = selStart;
selEnd.mColumn += selectionLength;
TextEditor::EditorState state; TextEditor::EditorState state;
state.mSelectionStart = selStart; state.mSelectionStart = editor->StringIndexToCoordinates(textLoc,textSrc);
state.mSelectionEnd = selEnd; state.mSelectionEnd = editor->StringIndexToCoordinates(textLoc + matchLength,textSrc);
state.mCursorPosition = selEnd; state.mCursorPosition = state.mSelectionEnd;
mMatches.push_back(state); mMatches.push_back(state);
return true; return true;
} }
@ -2305,7 +2254,7 @@ void TextEditor::FindReplaceHandler::FindAllMatches(TextEditor *editor,std::stri
Coordinates begin = Coordinates(0,0); Coordinates begin = Coordinates(0,0);
editor->mState.mCursorPosition = begin; editor->mState.mCursorPosition = begin;
if (!FindNext(editor,false)) { if (!FindNext(editor)) {
editor->mState = saveState; editor->mState = saveState;
editor->EnsureCursorVisible(); editor->EnsureCursorVisible();
return; return;
@ -2313,7 +2262,7 @@ void TextEditor::FindReplaceHandler::FindAllMatches(TextEditor *editor,std::stri
TextEditor::EditorState state = mMatches.back(); TextEditor::EditorState state = mMatches.back();
while( state.mCursorPosition < startingPos) { while( state.mCursorPosition < startingPos) {
if (!FindNext(editor,false)) { if (!FindNext(editor)) {
editor->mState = saveState; editor->mState = saveState;
editor->EnsureCursorVisible(); editor->EnsureCursorVisible();
return; return;
@ -2321,7 +2270,7 @@ void TextEditor::FindReplaceHandler::FindAllMatches(TextEditor *editor,std::stri
state = mMatches.back(); state = mMatches.back();
} }
while (FindNext(editor,false)); while (FindNext(editor));
editor->mState = saveState; editor->mState = saveState;
editor->EnsureCursorVisible(); editor->EnsureCursorVisible();
@ -2538,7 +2487,7 @@ void TextEditor::Colorize(int aFromLine, int aLines) {
} }
void TextEditor::ColorizeRange(int aFromLine, int aToLine) { void TextEditor::ColorizeRange(int aFromLine, int aToLine) {
if (mLines.empty() || aFromLine >= aToLine) if (isEmpty() || aFromLine >= aToLine)
return; return;
std::string buffer; std::string buffer;
@ -2628,7 +2577,7 @@ void TextEditor::ColorizeRange(int aFromLine, int aToLine) {
} }
void TextEditor::ColorizeInternal() { void TextEditor::ColorizeInternal() {
if (mLines.empty() || !mColorizerEnabled) if (isEmpty() || !mColorizerEnabled)
return; return;
if (mCheckComments) { if (mCheckComments) {