1 // Scintilla source code edit control
2 /** @file ContractionState.cxx
3 ** Manages visibility of lines for folding and wrapping.
5 // Copyright 1998-2007 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
13 #include <string_view>
19 #include "Debugging.h"
22 #include "UniqueString.h"
23 #include "SplitVector.h"
24 #include "Partitioning.h"
25 #include "RunStyles.h"
26 #include "SparseVector.h"
27 #include "ContractionState.h"
29 using namespace Scintilla::Internal
;
33 template <typename LINE
>
34 class ContractionState final
: public IContractionState
{
35 // These contain 1 element for every document line.
36 std::unique_ptr
<RunStyles
<LINE
, char>> visible
;
37 std::unique_ptr
<RunStyles
<LINE
, char>> expanded
;
38 std::unique_ptr
<RunStyles
<LINE
, int>> heights
;
39 std::unique_ptr
<SparseVector
<UniqueString
>> foldDisplayTexts
;
40 std::unique_ptr
<Partitioning
<LINE
>> displayLines
;
45 bool OneToOne() const noexcept
{
46 // True when each document line is exactly one display line so need for
47 // complex data structures.
48 return visible
== nullptr;
51 void InsertLine(Sci::Line lineDoc
);
52 void DeleteLine(Sci::Line lineDoc
);
54 // line_cast(): cast Sci::Line to either 32-bit or 64-bit value
55 // This avoids warnings from Visual C++ Code Analysis and shortens code
56 static constexpr LINE
line_cast(Sci::Line line
) noexcept
{
57 return static_cast<LINE
>(line
);
61 ContractionState() noexcept
;
63 void Clear() noexcept override
;
65 Sci::Line
LinesInDoc() const noexcept override
;
66 Sci::Line
LinesDisplayed() const noexcept override
;
67 Sci::Line
DisplayFromDoc(Sci::Line lineDoc
) const noexcept override
;
68 Sci::Line
DisplayLastFromDoc(Sci::Line lineDoc
) const noexcept override
;
69 Sci::Line
DocFromDisplay(Sci::Line lineDisplay
) const noexcept override
;
71 void InsertLines(Sci::Line lineDoc
, Sci::Line lineCount
) override
;
72 void DeleteLines(Sci::Line lineDoc
, Sci::Line lineCount
) override
;
74 bool GetVisible(Sci::Line lineDoc
) const noexcept override
;
75 bool SetVisible(Sci::Line lineDocStart
, Sci::Line lineDocEnd
, bool isVisible
) override
;
76 bool HiddenLines() const noexcept override
;
78 const char *GetFoldDisplayText(Sci::Line lineDoc
) const noexcept override
;
79 bool SetFoldDisplayText(Sci::Line lineDoc
, const char *text
) override
;
81 bool GetExpanded(Sci::Line lineDoc
) const noexcept override
;
82 bool SetExpanded(Sci::Line lineDoc
, bool isExpanded
) override
;
83 bool ExpandAll() override
;
84 Sci::Line
ContractedNext(Sci::Line lineDocStart
) const noexcept override
;
86 int GetHeight(Sci::Line lineDoc
) const noexcept override
;
87 bool SetHeight(Sci::Line lineDoc
, int height
) override
;
89 void ShowAll() noexcept override
;
91 void Check() const noexcept
;
94 template <typename LINE
>
95 ContractionState
<LINE
>::ContractionState() noexcept
: linesInDocument(1) {
98 template <typename LINE
>
99 void ContractionState
<LINE
>::EnsureData() {
101 visible
= std::make_unique
<RunStyles
<LINE
, char>>();
102 expanded
= std::make_unique
<RunStyles
<LINE
, char>>();
103 heights
= std::make_unique
<RunStyles
<LINE
, int>>();
104 foldDisplayTexts
= std::make_unique
<SparseVector
<UniqueString
>>();
105 displayLines
= std::make_unique
<Partitioning
<LINE
>>(4);
106 InsertLines(0, linesInDocument
);
110 template <typename LINE
>
111 void ContractionState
<LINE
>::InsertLine(Sci::Line lineDoc
) {
115 const LINE lineDocCast
= line_cast(lineDoc
);
116 visible
->InsertSpace(lineDocCast
, 1);
117 visible
->SetValueAt(lineDocCast
, 1);
118 expanded
->InsertSpace(lineDocCast
, 1);
119 expanded
->SetValueAt(lineDocCast
, 1);
120 heights
->InsertSpace(lineDocCast
, 1);
121 heights
->SetValueAt(lineDocCast
, 1);
122 foldDisplayTexts
->InsertSpace(lineDocCast
, 1);
123 foldDisplayTexts
->SetValueAt(lineDocCast
, nullptr);
124 const Sci::Line lineDisplay
= DisplayFromDoc(lineDoc
);
125 displayLines
->InsertPartition(lineDocCast
, line_cast(lineDisplay
));
126 displayLines
->InsertText(lineDocCast
, 1);
130 template <typename LINE
>
131 void ContractionState
<LINE
>::DeleteLine(Sci::Line lineDoc
) {
135 const LINE lineDocCast
= line_cast(lineDoc
);
136 if (GetVisible(lineDoc
)) {
137 displayLines
->InsertText(lineDocCast
, -heights
->ValueAt(lineDocCast
));
139 displayLines
->RemovePartition(lineDocCast
);
140 visible
->DeleteRange(lineDocCast
, 1);
141 expanded
->DeleteRange(lineDocCast
, 1);
142 heights
->DeleteRange(lineDocCast
, 1);
143 foldDisplayTexts
->DeletePosition(lineDocCast
);
147 template <typename LINE
>
148 void ContractionState
<LINE
>::Clear() noexcept
{
152 foldDisplayTexts
.reset();
153 displayLines
.reset();
157 template <typename LINE
>
158 Sci::Line ContractionState
<LINE
>::LinesInDoc() const noexcept
{
160 return linesInDocument
;
162 return displayLines
->Partitions() - 1;
166 template <typename LINE
>
167 Sci::Line ContractionState
<LINE
>::LinesDisplayed() const noexcept
{
169 return linesInDocument
;
171 return displayLines
->PositionFromPartition(line_cast(LinesInDoc()));
175 template <typename LINE
>
176 Sci::Line ContractionState
<LINE
>::DisplayFromDoc(Sci::Line lineDoc
) const noexcept
{
178 return (lineDoc
<= linesInDocument
) ? lineDoc
: linesInDocument
;
180 if (lineDoc
> displayLines
->Partitions())
181 lineDoc
= displayLines
->Partitions();
182 return displayLines
->PositionFromPartition(line_cast(lineDoc
));
186 template <typename LINE
>
187 Sci::Line ContractionState
<LINE
>::DisplayLastFromDoc(Sci::Line lineDoc
) const noexcept
{
188 return DisplayFromDoc(lineDoc
) + GetHeight(lineDoc
) - 1;
191 template <typename LINE
>
192 Sci::Line ContractionState
<LINE
>::DocFromDisplay(Sci::Line lineDisplay
) const noexcept
{
196 if (lineDisplay
< 0) {
199 if (lineDisplay
> LinesDisplayed()) {
200 return displayLines
->PartitionFromPosition(line_cast(LinesDisplayed()));
202 const Sci::Line lineDoc
= displayLines
->PartitionFromPosition(line_cast(lineDisplay
));
203 PLATFORM_ASSERT(GetVisible(lineDoc
));
208 template <typename LINE
>
209 void ContractionState
<LINE
>::InsertLines(Sci::Line lineDoc
, Sci::Line lineCount
) {
211 linesInDocument
+= line_cast(lineCount
);
213 for (Sci::Line l
= 0; l
< lineCount
; l
++) {
214 InsertLine(lineDoc
+ l
);
220 template <typename LINE
>
221 void ContractionState
<LINE
>::DeleteLines(Sci::Line lineDoc
, Sci::Line lineCount
) {
223 linesInDocument
-= line_cast(lineCount
);
225 for (Sci::Line l
= 0; l
< lineCount
; l
++) {
232 template <typename LINE
>
233 bool ContractionState
<LINE
>::GetVisible(Sci::Line lineDoc
) const noexcept
{
237 if (lineDoc
>= visible
->Length())
239 return visible
->ValueAt(line_cast(lineDoc
)) == 1;
243 template <typename LINE
>
244 bool ContractionState
<LINE
>::SetVisible(Sci::Line lineDocStart
, Sci::Line lineDocEnd
, bool isVisible
) {
245 if (OneToOne() && isVisible
) {
250 if ((lineDocStart
<= lineDocEnd
) && (lineDocStart
>= 0) && (lineDocEnd
< LinesInDoc())) {
251 bool changed
= false;
252 for (Sci::Line line
= lineDocStart
; line
<= lineDocEnd
; line
++) {
253 if (GetVisible(line
) != isVisible
) {
255 const int heightLine
= heights
->ValueAt(line_cast(line
));
256 const int difference
= isVisible
? heightLine
: -heightLine
;
257 displayLines
->InsertText(line_cast(line
), difference
);
261 visible
->FillRange(line_cast(lineDocStart
), isVisible
? 1 : 0,
262 line_cast(lineDocEnd
- lineDocStart
) + 1);
272 template <typename LINE
>
273 bool ContractionState
<LINE
>::HiddenLines() const noexcept
{
277 return !visible
->AllSameAs(1);
281 template <typename LINE
>
282 const char *ContractionState
<LINE
>::GetFoldDisplayText(Sci::Line lineDoc
) const noexcept
{
284 return foldDisplayTexts
->ValueAt(lineDoc
).get();
287 template <typename LINE
>
288 bool ContractionState
<LINE
>::SetFoldDisplayText(Sci::Line lineDoc
, const char *text
) {
290 const char *foldText
= foldDisplayTexts
->ValueAt(lineDoc
).get();
291 if (!foldText
|| !text
|| 0 != strcmp(text
, foldText
)) {
292 UniqueString uns
= IsNullOrEmpty(text
) ? UniqueString() : UniqueStringCopy(text
);
293 foldDisplayTexts
->SetValueAt(lineDoc
, std::move(uns
));
302 template <typename LINE
>
303 bool ContractionState
<LINE
>::GetExpanded(Sci::Line lineDoc
) const noexcept
{
308 return expanded
->ValueAt(line_cast(lineDoc
)) == 1;
312 template <typename LINE
>
313 bool ContractionState
<LINE
>::SetExpanded(Sci::Line lineDoc
, bool isExpanded
) {
314 if (OneToOne() && isExpanded
) {
318 if (isExpanded
!= (expanded
->ValueAt(line_cast(lineDoc
)) == 1)) {
319 expanded
->SetValueAt(line_cast(lineDoc
), isExpanded
? 1 : 0);
329 template <typename LINE
>
330 bool ContractionState
<LINE
>::ExpandAll() {
334 const LINE lines
= expanded
->Length();
335 const bool changed
= expanded
->FillRange(0, 1, lines
).changed
;
341 template <typename LINE
>
342 Sci::Line ContractionState
<LINE
>::ContractedNext(Sci::Line lineDocStart
) const noexcept
{
347 if (!expanded
->ValueAt(line_cast(lineDocStart
))) {
350 const Sci::Line lineDocNextChange
= expanded
->EndRun(line_cast(lineDocStart
));
351 if (lineDocNextChange
< LinesInDoc())
352 return lineDocNextChange
;
359 template <typename LINE
>
360 int ContractionState
<LINE
>::GetHeight(Sci::Line lineDoc
) const noexcept
{
364 return heights
->ValueAt(line_cast(lineDoc
));
368 // Set the number of display lines needed for this line.
369 // Return true if this is a change.
370 template <typename LINE
>
371 bool ContractionState
<LINE
>::SetHeight(Sci::Line lineDoc
, int height
) {
372 if (OneToOne() && (height
== 1)) {
374 } else if (lineDoc
< LinesInDoc()) {
376 if (GetHeight(lineDoc
) != height
) {
377 if (GetVisible(lineDoc
)) {
378 displayLines
->InsertText(line_cast(lineDoc
), height
- GetHeight(lineDoc
));
380 heights
->SetValueAt(line_cast(lineDoc
), height
);
392 template <typename LINE
>
393 void ContractionState
<LINE
>::ShowAll() noexcept
{
394 const LINE lines
= line_cast(LinesInDoc());
396 linesInDocument
= lines
;
401 template <typename LINE
>
402 void ContractionState
<LINE
>::Check() const noexcept
{
403 #ifdef CHECK_CORRECTNESS
404 for (Sci::Line vline
= 0; vline
< LinesDisplayed(); vline
++) {
405 const Sci::Line lineDoc
= DocFromDisplay(vline
);
406 PLATFORM_ASSERT(GetVisible(lineDoc
));
408 for (Sci::Line lineDoc
= 0; lineDoc
< LinesInDoc(); lineDoc
++) {
409 const Sci::Line displayThis
= DisplayFromDoc(lineDoc
);
410 const Sci::Line displayNext
= DisplayFromDoc(lineDoc
+ 1);
411 const Sci::Line height
= displayNext
- displayThis
;
412 PLATFORM_ASSERT(height
>= 0);
413 if (GetVisible(lineDoc
)) {
414 PLATFORM_ASSERT(GetHeight(lineDoc
) == height
);
416 PLATFORM_ASSERT(0 == height
);
424 namespace Scintilla::Internal
{
426 std::unique_ptr
<IContractionState
> ContractionStateCreate(bool largeDocument
) {
428 return std::make_unique
<ContractionState
<Sci::Line
>>();
430 return std::make_unique
<ContractionState
<int>>();