Merge pull request #4029 from cousteaulecommandant/fix-readme-rst
[geany-mirror.git] / scintilla / src / ContractionState.cxx
blobb36e1ebd84a992a7b001a6847d6f7c4ddff967af
1 // Scintilla source code edit control
2 /** @file ContractionState.cxx
3 ** Manages visibility of lines for folding and wrapping.
4 **/
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.
8 #include <cstddef>
9 #include <cassert>
10 #include <cstring>
12 #include <stdexcept>
13 #include <string_view>
14 #include <vector>
15 #include <optional>
16 #include <algorithm>
17 #include <memory>
19 #include "Debugging.h"
21 #include "Position.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;
31 namespace {
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;
41 LINE linesInDocument;
43 void EnsureData();
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);
60 public:
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() {
100 if (OneToOne()) {
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) {
112 if (OneToOne()) {
113 linesInDocument++;
114 } else {
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) {
132 if (OneToOne()) {
133 linesInDocument--;
134 } else {
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 {
149 visible.reset();
150 expanded.reset();
151 heights.reset();
152 foldDisplayTexts.reset();
153 displayLines.reset();
154 linesInDocument = 1;
157 template <typename LINE>
158 Sci::Line ContractionState<LINE>::LinesInDoc() const noexcept {
159 if (OneToOne()) {
160 return linesInDocument;
161 } else {
162 return displayLines->Partitions() - 1;
166 template <typename LINE>
167 Sci::Line ContractionState<LINE>::LinesDisplayed() const noexcept {
168 if (OneToOne()) {
169 return linesInDocument;
170 } else {
171 return displayLines->PositionFromPartition(line_cast(LinesInDoc()));
175 template <typename LINE>
176 Sci::Line ContractionState<LINE>::DisplayFromDoc(Sci::Line lineDoc) const noexcept {
177 if (OneToOne()) {
178 return (lineDoc <= linesInDocument) ? lineDoc : linesInDocument;
179 } else {
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 {
193 if (OneToOne()) {
194 return lineDisplay;
195 } else {
196 if (lineDisplay < 0) {
197 return 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));
204 return lineDoc;
208 template <typename LINE>
209 void ContractionState<LINE>::InsertLines(Sci::Line lineDoc, Sci::Line lineCount) {
210 if (OneToOne()) {
211 linesInDocument += line_cast(lineCount);
212 } else {
213 for (Sci::Line l = 0; l < lineCount; l++) {
214 InsertLine(lineDoc + l);
217 Check();
220 template <typename LINE>
221 void ContractionState<LINE>::DeleteLines(Sci::Line lineDoc, Sci::Line lineCount) {
222 if (OneToOne()) {
223 linesInDocument -= line_cast(lineCount);
224 } else {
225 for (Sci::Line l = 0; l < lineCount; l++) {
226 DeleteLine(lineDoc);
229 Check();
232 template <typename LINE>
233 bool ContractionState<LINE>::GetVisible(Sci::Line lineDoc) const noexcept {
234 if (OneToOne()) {
235 return true;
236 } else {
237 if (lineDoc >= visible->Length())
238 return true;
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) {
246 return false;
247 } else {
248 EnsureData();
249 Check();
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) {
254 changed = true;
255 const int heightLine = heights->ValueAt(line_cast(line));
256 const int difference = isVisible ? heightLine : -heightLine;
257 displayLines->InsertText(line_cast(line), difference);
260 if (changed) {
261 visible->FillRange(line_cast(lineDocStart), isVisible ? 1 : 0,
262 line_cast(lineDocEnd - lineDocStart) + 1);
264 Check();
265 return changed;
266 } else {
267 return false;
272 template <typename LINE>
273 bool ContractionState<LINE>::HiddenLines() const noexcept {
274 if (OneToOne()) {
275 return false;
276 } else {
277 return !visible->AllSameAs(1);
281 template <typename LINE>
282 const char *ContractionState<LINE>::GetFoldDisplayText(Sci::Line lineDoc) const noexcept {
283 Check();
284 return foldDisplayTexts->ValueAt(lineDoc).get();
287 template <typename LINE>
288 bool ContractionState<LINE>::SetFoldDisplayText(Sci::Line lineDoc, const char *text) {
289 EnsureData();
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));
294 Check();
295 return true;
296 } else {
297 Check();
298 return false;
302 template <typename LINE>
303 bool ContractionState<LINE>::GetExpanded(Sci::Line lineDoc) const noexcept {
304 if (OneToOne()) {
305 return true;
306 } else {
307 Check();
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) {
315 return false;
316 } else {
317 EnsureData();
318 if (isExpanded != (expanded->ValueAt(line_cast(lineDoc)) == 1)) {
319 expanded->SetValueAt(line_cast(lineDoc), isExpanded ? 1 : 0);
320 Check();
321 return true;
322 } else {
323 Check();
324 return false;
329 template <typename LINE>
330 bool ContractionState<LINE>::ExpandAll() {
331 if (OneToOne()) {
332 return false;
333 } else {
334 const LINE lines = expanded->Length();
335 const bool changed = expanded->FillRange(0, 1, lines).changed;
336 Check();
337 return changed;
341 template <typename LINE>
342 Sci::Line ContractionState<LINE>::ContractedNext(Sci::Line lineDocStart) const noexcept {
343 if (OneToOne()) {
344 return -1;
345 } else {
346 Check();
347 if (!expanded->ValueAt(line_cast(lineDocStart))) {
348 return lineDocStart;
349 } else {
350 const Sci::Line lineDocNextChange = expanded->EndRun(line_cast(lineDocStart));
351 if (lineDocNextChange < LinesInDoc())
352 return lineDocNextChange;
353 else
354 return -1;
359 template <typename LINE>
360 int ContractionState<LINE>::GetHeight(Sci::Line lineDoc) const noexcept {
361 if (OneToOne()) {
362 return 1;
363 } else {
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)) {
373 return false;
374 } else if (lineDoc < LinesInDoc()) {
375 EnsureData();
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);
381 Check();
382 return true;
383 } else {
384 Check();
385 return false;
387 } else {
388 return false;
392 template <typename LINE>
393 void ContractionState<LINE>::ShowAll() noexcept {
394 const LINE lines = line_cast(LinesInDoc());
395 Clear();
396 linesInDocument = lines;
399 // Debugging checks
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);
415 } else {
416 PLATFORM_ASSERT(0 == height);
419 #endif
424 namespace Scintilla::Internal {
426 std::unique_ptr<IContractionState> ContractionStateCreate(bool largeDocument) {
427 if (largeDocument)
428 return std::make_unique<ContractionState<Sci::Line>>();
429 else
430 return std::make_unique<ContractionState<int>>();