// Scintilla source code edit control /** @file ContractionState.cxx ** Manages visibility of lines for folding and wrapping. **/ // Copyright 1998-2007 by Neil Hodgson // The License.txt file describes the conditions under which this software may be distributed. #include #include #include #include #include #include #include #include #include "Platform.h" #include "Position.h" #include "UniqueString.h" #include "SplitVector.h" #include "Partitioning.h" #include "RunStyles.h" #include "SparseVector.h" #include "ContractionState.h" using namespace Scintilla; namespace { template class ContractionState final : public IContractionState { // These contain 1 element for every document line. std::unique_ptr> visible; std::unique_ptr> expanded; std::unique_ptr> heights; std::unique_ptr> foldDisplayTexts; std::unique_ptr> displayLines; LINE linesInDocument; void EnsureData(); bool OneToOne() const noexcept { // True when each document line is exactly one display line so need for // complex data structures. return visible == nullptr; } void InsertLine(Sci::Line lineDoc); void DeleteLine(Sci::Line lineDoc); public: ContractionState() noexcept; // Deleted so ContractionState objects can not be copied. ContractionState(const ContractionState &) = delete; void operator=(const ContractionState &) = delete; ContractionState(ContractionState &&) = delete; void operator=(ContractionState &&) = delete; ~ContractionState() override; void Clear() noexcept override; Sci::Line LinesInDoc() const override; Sci::Line LinesDisplayed() const override; Sci::Line DisplayFromDoc(Sci::Line lineDoc) const override; Sci::Line DisplayLastFromDoc(Sci::Line lineDoc) const override; Sci::Line DocFromDisplay(Sci::Line lineDisplay) const override; void InsertLines(Sci::Line lineDoc, Sci::Line lineCount) override; void DeleteLines(Sci::Line lineDoc, Sci::Line lineCount) override; bool GetVisible(Sci::Line lineDoc) const override; bool SetVisible(Sci::Line lineDocStart, Sci::Line lineDocEnd, bool isVisible) override; bool HiddenLines() const override; const char *GetFoldDisplayText(Sci::Line lineDoc) const override; bool SetFoldDisplayText(Sci::Line lineDoc, const char *text) override; bool GetExpanded(Sci::Line lineDoc) const override; bool SetExpanded(Sci::Line lineDoc, bool isExpanded) override; Sci::Line ContractedNext(Sci::Line lineDocStart) const override; int GetHeight(Sci::Line lineDoc) const override; bool SetHeight(Sci::Line lineDoc, int height) override; void ShowAll() override; void Check() const; }; template ContractionState::ContractionState() noexcept : linesInDocument(1) { } template ContractionState::~ContractionState() { Clear(); } template void ContractionState::EnsureData() { if (OneToOne()) { visible = std::make_unique>(); expanded = std::make_unique>(); heights = std::make_unique>(); foldDisplayTexts = std::make_unique>(); displayLines = std::make_unique>(4); InsertLines(0, linesInDocument); } } template void ContractionState::InsertLine(Sci::Line lineDoc) { if (OneToOne()) { linesInDocument++; } else { const LINE lineDocCast = static_cast(lineDoc); visible->InsertSpace(lineDocCast, 1); visible->SetValueAt(lineDocCast, 1); expanded->InsertSpace(lineDocCast, 1); expanded->SetValueAt(lineDocCast, 1); heights->InsertSpace(lineDocCast, 1); heights->SetValueAt(lineDocCast, 1); foldDisplayTexts->InsertSpace(lineDocCast, 1); foldDisplayTexts->SetValueAt(lineDocCast, nullptr); const Sci::Line lineDisplay = DisplayFromDoc(lineDoc); displayLines->InsertPartition(lineDocCast, static_cast(lineDisplay)); displayLines->InsertText(lineDocCast, 1); } } template void ContractionState::DeleteLine(Sci::Line lineDoc) { if (OneToOne()) { linesInDocument--; } else { const LINE lineDocCast = static_cast(lineDoc); if (GetVisible(lineDoc)) { displayLines->InsertText(lineDocCast, -heights->ValueAt(lineDocCast)); } displayLines->RemovePartition(lineDocCast); visible->DeleteRange(lineDocCast, 1); expanded->DeleteRange(lineDocCast, 1); heights->DeleteRange(lineDocCast, 1); foldDisplayTexts->DeletePosition(lineDocCast); } } template void ContractionState::Clear() noexcept { visible.reset(); expanded.reset(); heights.reset(); foldDisplayTexts.reset(); displayLines.reset(); linesInDocument = 1; } template Sci::Line ContractionState::LinesInDoc() const { if (OneToOne()) { return linesInDocument; } else { return displayLines->Partitions() - 1; } } template Sci::Line ContractionState::LinesDisplayed() const { if (OneToOne()) { return linesInDocument; } else { return displayLines->PositionFromPartition(static_cast(LinesInDoc())); } } template Sci::Line ContractionState::DisplayFromDoc(Sci::Line lineDoc) const { if (OneToOne()) { return (lineDoc <= linesInDocument) ? lineDoc : linesInDocument; } else { if (lineDoc > displayLines->Partitions()) lineDoc = displayLines->Partitions(); return displayLines->PositionFromPartition(static_cast(lineDoc)); } } template Sci::Line ContractionState::DisplayLastFromDoc(Sci::Line lineDoc) const { return DisplayFromDoc(lineDoc) + GetHeight(lineDoc) - 1; } template Sci::Line ContractionState::DocFromDisplay(Sci::Line lineDisplay) const { if (OneToOne()) { return lineDisplay; } else { if (lineDisplay <= 0) { return 0; } if (lineDisplay > LinesDisplayed()) { return displayLines->PartitionFromPosition(static_cast(LinesDisplayed())); } const Sci::Line lineDoc = displayLines->PartitionFromPosition(static_cast(lineDisplay)); PLATFORM_ASSERT(GetVisible(lineDoc)); return lineDoc; } } template void ContractionState::InsertLines(Sci::Line lineDoc, Sci::Line lineCount) { if (OneToOne()) { linesInDocument += static_cast(lineCount); } else { for (Sci::Line l = 0; l < lineCount; l++) { InsertLine(lineDoc + l); } } Check(); } template void ContractionState::DeleteLines(Sci::Line lineDoc, Sci::Line lineCount) { if (OneToOne()) { linesInDocument -= static_cast(lineCount); } else { for (Sci::Line l = 0; l < lineCount; l++) { DeleteLine(lineDoc); } } Check(); } template bool ContractionState::GetVisible(Sci::Line lineDoc) const { if (OneToOne()) { return true; } else { if (lineDoc >= visible->Length()) return true; return visible->ValueAt(static_cast(lineDoc)) == 1; } } template bool ContractionState::SetVisible(Sci::Line lineDocStart, Sci::Line lineDocEnd, bool isVisible) { if (OneToOne() && isVisible) { return false; } else { EnsureData(); Sci::Line delta = 0; Check(); if ((lineDocStart <= lineDocEnd) && (lineDocStart >= 0) && (lineDocEnd < LinesInDoc())) { for (Sci::Line line = lineDocStart; line <= lineDocEnd; line++) { if (GetVisible(line) != isVisible) { const int heightLine = heights->ValueAt(static_cast(line)); const int difference = isVisible ? heightLine : -heightLine; visible->SetValueAt(static_cast(line), isVisible ? 1 : 0); displayLines->InsertText(static_cast(line), difference); delta += difference; } } } else { return false; } Check(); return delta != 0; } } template bool ContractionState::HiddenLines() const { if (OneToOne()) { return false; } else { return !visible->AllSameAs(1); } } template const char *ContractionState::GetFoldDisplayText(Sci::Line lineDoc) const { Check(); return foldDisplayTexts->ValueAt(lineDoc).get(); } template bool ContractionState::SetFoldDisplayText(Sci::Line lineDoc, const char *text) { EnsureData(); const char *foldText = foldDisplayTexts->ValueAt(lineDoc).get(); if (!foldText || !text || 0 != strcmp(text, foldText)) { UniqueString uns = IsNullOrEmpty(text) ? UniqueString() : UniqueStringCopy(text); foldDisplayTexts->SetValueAt(lineDoc, std::move(uns)); Check(); return true; } else { Check(); return false; } } template bool ContractionState::GetExpanded(Sci::Line lineDoc) const { if (OneToOne()) { return true; } else { Check(); return expanded->ValueAt(static_cast(lineDoc)) == 1; } } template bool ContractionState::SetExpanded(Sci::Line lineDoc, bool isExpanded) { if (OneToOne() && isExpanded) { return false; } else { EnsureData(); if (isExpanded != (expanded->ValueAt(static_cast(lineDoc)) == 1)) { expanded->SetValueAt(static_cast(lineDoc), isExpanded ? 1 : 0); Check(); return true; } else { Check(); return false; } } } template Sci::Line ContractionState::ContractedNext(Sci::Line lineDocStart) const { if (OneToOne()) { return -1; } else { Check(); if (!expanded->ValueAt(static_cast(lineDocStart))) { return lineDocStart; } else { const Sci::Line lineDocNextChange = expanded->EndRun(static_cast(lineDocStart)); if (lineDocNextChange < LinesInDoc()) return lineDocNextChange; else return -1; } } } template int ContractionState::GetHeight(Sci::Line lineDoc) const { if (OneToOne()) { return 1; } else { return heights->ValueAt(static_cast(lineDoc)); } } // Set the number of display lines needed for this line. // Return true if this is a change. template bool ContractionState::SetHeight(Sci::Line lineDoc, int height) { if (OneToOne() && (height == 1)) { return false; } else if (lineDoc < LinesInDoc()) { EnsureData(); if (GetHeight(lineDoc) != height) { if (GetVisible(lineDoc)) { displayLines->InsertText(static_cast(lineDoc), height - GetHeight(lineDoc)); } heights->SetValueAt(static_cast(lineDoc), height); Check(); return true; } else { Check(); return false; } } else { return false; } } template void ContractionState::ShowAll() { const LINE lines = static_cast(LinesInDoc()); Clear(); linesInDocument = lines; } // Debugging checks template void ContractionState::Check() const { #ifdef CHECK_CORRECTNESS for (Sci::Line vline = 0; vline < LinesDisplayed(); vline++) { const Sci::Line lineDoc = DocFromDisplay(vline); PLATFORM_ASSERT(GetVisible(lineDoc)); } for (Sci::Line lineDoc = 0; lineDoc < LinesInDoc(); lineDoc++) { const Sci::Line displayThis = DisplayFromDoc(lineDoc); const Sci::Line displayNext = DisplayFromDoc(lineDoc + 1); const Sci::Line height = displayNext - displayThis; PLATFORM_ASSERT(height >= 0); if (GetVisible(lineDoc)) { PLATFORM_ASSERT(GetHeight(lineDoc) == height); } else { PLATFORM_ASSERT(0 == height); } } #endif } } namespace Scintilla { std::unique_ptr ContractionStateCreate(bool largeDocument) { if (largeDocument) return std::make_unique>(); else return std::make_unique>(); } }