Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
LineBasedPartition.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
6#include "LineBasedPartition.h"
7
8using namespace Stroika::Foundation;
9
10using namespace Stroika::Frameworks;
11using namespace Stroika::Frameworks::Led;
12
13/*
14 ********************************************************************************
15 ****************************** LineBasedPartition ******************************
16 ********************************************************************************
17 */
18LineBasedPartition::LineBasedPartition (TextStore& textStore)
19 : inherited{textStore}
20{
21 FinalConstruct ();
22}
23
24LineBasedPartition::LineBasedPartition (TextStore& textStore, SpecialHackToDisableInit /*hack*/)
25 : inherited{textStore}
26{
27}
28
29void LineBasedPartition::FinalConstruct ()
30{
31 inherited::FinalConstruct ();
32 PartitionMarker* pm = GetFirstPartitionMarker ();
33 // Perform any splits of the first created PM into lines (in case text buffer starts with text in it)
34 for (size_t i = GetTextStore ().GetLength (); i > 0; --i) {
35 Led_tChar c;
36 GetTextStore ().CopyOut (i - 1, 1, &c);
37 if (c == '\n') {
38 Split (pm, i);
39 }
40 }
41}
42
43/*
44@METHOD: LineBasedPartition::UpdatePartitions
45@DESCRIPTION: <p>Hook notification that the given partition was updated. See if any newlines were added
46 or deleted, and update the partitions as appropriate. Do not call directly.</p>
47 <p>Call @'LineBasedPartition::CheckForSplits' for each character to see if any splits are needed,
48 and call @'LineBasedPartition::NeedToCoalesce' to see if the given PM needs coalescing because of the update.</p>
49*/
50void LineBasedPartition::UpdatePartitions (PartitionMarker* pm, const UpdateInfo& updateInfo) noexcept
51{
52 RequireNotNull (pm);
53
54 if (updateInfo.fTextModified) {
55 /*
56 * First check if we had any newlines inserted into our middle. If so, then
57 * we must split ourselves. Then check if we retain our trailing newline (or
58 * are the last partition element) and if not - coalece with the next element.
59 *
60 * When checking for newlines inserted - we iterate backwards because when
61 * we do splits - we lose the ability to process the rest of the insertion, and
62 * there could be multiple newlines inserted.
63 */
64 size_t startOfInsert = max (updateInfo.fReplaceFrom, pm->GetStart ());
65 size_t endOfInsert = min (updateInfo.GetResultingRHS (), pm->GetEnd ());
66 for (size_t i = endOfInsert; i > startOfInsert; --i) {
67 CheckForSplits (pm, updateInfo, i);
68 }
69
70 // See if after insertion of that text this PM needs to be coalesed with the next
71 bool coalesce = NeedToCoalesce (pm);
72 if (coalesce) {
73 Coalece (pm); // 'pm' is DELETED BY THIS SO DO NOTHING to it AFTERWARDS!!!
74 }
75 }
76}
77
78/*
79@METHOD: LineBasedPartition::CheckForSplits
80@ACCESS: protected
81@DESCRIPTION: <p>Called by @'LineBasedPartition::UpdatePartitions' () to check if the given inserted text
82 requires any partition elements to be split. 'i' is the position at which we may want
83 to do a split (e.g. right after the newline character).</p>
84*/
85void LineBasedPartition::CheckForSplits (PartitionMarker* pm, const UpdateInfo& updateInfo, size_t i) noexcept
86{
87 Require (updateInfo.fTextModified); //so there is something in the fTextInserted area
88 if (updateInfo.fTextInserted[i - updateInfo.fReplaceFrom - 1] == '\n') {
89 Split (pm, i);
90 }
91}
92
93/*
94@METHOD: LineBasedPartition::NeedToCoalesce
95@ACCESS: protected
96@DESCRIPTION: <p>Called by @'LineBasedPartition::UpdatePartitions' () to check if the given inserted text requires this PM to be coalesed with its
97 following one.</p>
98*/
99bool LineBasedPartition::NeedToCoalesce (PartitionMarker* pm) noexcept
100{
101 // If after inserting a bunch of characters, and deleting some too, my
102 // last character is no longer a newline - better Coalece...
103 bool coalesce = false;
104 if (pm->GetLength () == 0) {
105 coalesce = true;
106 }
107 else {
108 if (pm->GetNext () != nullptr) {
109 size_t end = pm->GetEnd ();
110 Led_tChar endChar = '\0';
111 // Look at the character just BEFORE (rather than after) the end
112 CopyOut (end - 1, 1, &endChar);
113 if (endChar != '\n') {
114 coalesce = true;
115 }
116 }
117 }
118 return coalesce;
119}
120
121#if qStroika_Foundation_Debug_AssertionsChecked
122/*
123@METHOD: LineBasedPartition::Invariant_
124@DESCRIPTION: <p>Check internal consitency of data structures. Don't call this directly. Call Invariant instead.
125 And only call at quiesent times; not in the midst of some update where data structures might not be fully consistent.</p>
126*/
127void LineBasedPartition::Invariant_ () const
128{
129 inherited::Invariant_ ();
130 for (PartitionMarker* cur = GetFirstPartitionMarker (); cur != nullptr; cur = cur->GetNext ()) {
131 AssertNotNull (cur);
132 size_t start = cur->GetStart ();
133 size_t end = cur->GetEnd ();
134 size_t len = end - start;
135
136 if (end > GetEnd ()) {
137 --len; // Last partition extends past end of text
138 }
139 Memory::StackBuffer<Led_tChar> buf{Memory::eUninitialized, len};
140 CopyOut (start, len, buf.data ());
141 for (size_t i = 1; i < len; ++i) {
142 Assert (buf[i - 1] != '\n');
143 }
144 if (cur->GetNext () != nullptr) { // All but the last partition must be NL terminated...
145 Assert (buf[len - 1] == '\n');
146 }
147 }
148}
149#endif
#define AssertNotNull(p)
Definition Assertions.h:333
#define RequireNotNull(p)
Definition Assertions.h:347
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...