2
2
3
3
namespace LastDragon_ru \LaraASP \Documentator \Editor ;
4
4
5
+ use LastDragon_ru \LaraASP \Documentator \Editor \Mutators \Extractor ;
6
+ use LastDragon_ru \LaraASP \Documentator \Editor \Mutators \Mutator ;
5
7
use LastDragon_ru \LaraASP \Documentator \Utils \Text ;
6
8
use Override ;
7
9
use Stringable ;
8
10
9
- use function array_merge ;
10
- use function array_push ;
11
- use function array_reverse ;
12
- use function array_slice ;
13
- use function array_splice ;
14
- use function array_values ;
15
- use function count ;
16
11
use function implode ;
17
12
use function is_string ;
18
- use function mb_rtrim ;
19
- use function mb_substr ;
20
- use function usort ;
21
-
22
- use const PHP_INT_MAX ;
23
13
24
14
readonly class Editor implements Stringable {
25
15
/**
26
16
* @var list<string>
27
17
*/
28
- protected array $ lines ;
18
+ protected array $ lines ;
19
+ protected Mutator $ mutator ;
20
+ protected Extractor $ extractor ;
29
21
30
22
/**
31
23
* @param list<string>|string $content
@@ -35,7 +27,9 @@ final public function __construct(
35
27
protected int $ startLine = 0 ,
36
28
protected string $ endOfLine = "\n" ,
37
29
) {
38
- $ this ->lines = is_string ($ content ) ? Text::getLines ($ content ) : $ content ;
30
+ $ this ->lines = is_string ($ content ) ? Text::getLines ($ content ) : $ content ;
31
+ $ this ->mutator = new Mutator ();
32
+ $ this ->extractor = new Extractor ();
39
33
}
40
34
41
35
#[Override]
@@ -44,207 +38,26 @@ public function __toString(): string {
44
38
}
45
39
46
40
/**
47
- * @param iterable<array-key, Coordinate> $location
48
- */
49
- public function getText (iterable $ location ): ?string {
50
- // Select
51
- $ selected = null ;
52
-
53
- foreach ($ location as $ coordinate ) {
54
- $ number = $ coordinate ->line - $ this ->startLine ;
55
-
56
- if (isset ($ this ->lines [$ number ])) {
57
- $ selected [] = mb_substr ($ this ->lines [$ number ], $ coordinate ->offset , $ coordinate ->length );
58
- } else {
59
- $ selected = null ;
60
- break ;
61
- }
62
- }
63
-
64
- if ($ selected === null ) {
65
- return null ;
66
- }
67
-
68
- // Return
69
- return implode ($ this ->endOfLine , $ selected );
70
- }
71
-
72
- /**
73
- * @param iterable<array-key, array{iterable<array-key, Coordinate>, ?string}> $changes
41
+ * @param iterable<mixed, iterable<mixed, Coordinate>> $locations
74
42
*
75
43
* @return new<static>
76
44
*/
77
- public function mutate (iterable $ changes ): static {
78
- // Modify
79
- $ lines = $ this ->lines ;
80
- $ changes = $ this ->prepare ($ changes );
81
- $ changes = $ this ->removeOverlaps ($ changes );
82
- $ changes = $ this ->expand ($ changes );
83
-
84
- foreach ($ changes as [$ coordinate , $ text ]) {
85
- // Append?
86
- if ($ coordinate ->line === PHP_INT_MAX ) {
87
- array_push ($ lines , ...$ text );
88
- continue ;
89
- }
90
-
91
- // Change
92
- $ number = $ coordinate ->line - $ this ->startLine ;
93
- $ line = $ lines [$ number ] ?? '' ;
94
- $ count = count ($ text );
95
- $ prefix = mb_substr ($ line , 0 , $ coordinate ->offset );
96
- $ suffix = $ coordinate ->length !== null
97
- ? mb_substr ($ line , $ coordinate ->offset + $ coordinate ->length )
98
- : '' ;
99
- $ padding = mb_substr ($ line , 0 , $ coordinate ->padding );
100
-
101
- if ($ count > 1 ) {
102
- $ insert = [];
103
-
104
- for ($ t = 0 ; $ t < $ count ; $ t ++) {
105
- $ insert [] = match (true ) {
106
- $ t === 0 => mb_rtrim ($ prefix .$ text [$ t ]),
107
- $ t === $ count - 1 => mb_rtrim ($ padding .$ text [$ t ].$ suffix ),
108
- default => mb_rtrim ($ padding .$ text [$ t ]),
109
- };
110
- }
111
-
112
- array_splice ($ lines , $ number , 1 , $ insert );
113
- } elseif ($ count === 1 ) {
114
- $ lines [$ number ] = mb_rtrim ($ prefix .$ text [0 ].$ suffix );
115
- } elseif (($ prefix !== '' && $ prefix !== $ padding ) || $ suffix !== '' ) {
116
- $ lines [$ number ] = mb_rtrim ($ prefix .$ suffix );
117
- } else {
118
- unset($ lines [$ number ]);
119
- }
120
- }
121
-
122
- // Return
123
- return new static (array_values ($ lines ), $ this ->startLine , $ this ->endOfLine );
124
- }
125
-
126
- /**
127
- * @param iterable<array-key, array{iterable<array-key, Coordinate>, ?string}> $changes
128
- *
129
- * @return list<array{list<Coordinate>, ?string}>
130
- */
131
- protected function prepare (iterable $ changes ): array {
132
- $ prepared = [];
133
-
134
- foreach ($ changes as [$ location , $ text ]) {
135
- $ coordinates = [];
136
-
137
- foreach ($ location as $ coordinate ) {
138
- $ coordinates [] = $ coordinate ;
139
- }
140
-
141
- if ($ coordinates !== []) {
142
- $ prepared [] = [$ coordinates , $ text ];
143
- }
144
- }
145
-
146
- return array_reverse ($ prepared );
147
- }
148
-
149
- /**
150
- * @param array<int, array{list<Coordinate>, ?string}> $changes
151
- *
152
- * @return list<array{Coordinate, list<string>}>
153
- */
154
- protected function expand (array $ changes ): array {
155
- $ expanded = [];
156
- $ append = [];
157
- $ sort = static function (Coordinate $ a , Coordinate $ b ): int {
158
- $ result = $ a ->line <=> $ b ->line ;
159
- $ result = $ result === 0
160
- ? $ a ->offset <=> $ b ->offset
161
- : $ result ;
162
-
163
- return $ result ;
164
- };
165
-
166
- foreach ($ changes as [$ coordinates , $ text ]) {
167
- $ text = match (true ) {
168
- $ text === null => [],
169
- $ text === '' => ['' ],
170
- default => Text::getLines ($ text ),
171
- };
45
+ public function extract (iterable $ locations ): static {
46
+ $ extracted = ($ this ->extractor )($ this ->lines , $ locations , $ this ->startLine );
47
+ $ editor = new static ($ extracted , $ this ->startLine , $ this ->endOfLine );
172
48
173
- usort ($ coordinates , $ sort );
174
-
175
- for ($ i = 0 , $ c = count ($ coordinates ); $ i < $ c ; $ i ++) {
176
- $ line = $ i === $ c - 1 ? array_slice ($ text , $ i ) : (array ) ($ text [$ i ] ?? null );
177
-
178
- if ($ coordinates [$ i ]->line === PHP_INT_MAX ) {
179
- $ append [] = [$ coordinates [$ i ], $ line ];
180
- } else {
181
- $ expanded [] = [$ coordinates [$ i ], $ line ];
182
- }
183
- }
184
- }
185
-
186
- usort ($ expanded , static fn ($ a , $ b ) => -$ sort ($ a [0 ], $ b [0 ]));
187
-
188
- return array_merge ($ expanded , array_reverse ($ append ));
49
+ return $ editor ;
189
50
}
190
51
191
52
/**
192
- * @param list< array{list< Coordinate>, ?string}> $changes
53
+ * @param iterable<mixed, array{iterable<mixed, Coordinate>, ?string}> $changes
193
54
*
194
- * @return array<int, array{list<Coordinate>, ?string}>
195
- */
196
- protected function removeOverlaps (array $ changes ): array {
197
- $ used = [];
198
-
199
- foreach ($ changes as $ key => [$ coordinates ]) {
200
- $ lines = [];
201
-
202
- foreach ($ coordinates as $ coordinate ) {
203
- $ lines [$ coordinate ->line ][] = $ coordinate ;
204
-
205
- if ($ this ->isOverlapped ($ used , $ coordinate )) {
206
- $ lines = [];
207
- break ;
208
- }
209
- }
210
-
211
- if ($ lines !== []) {
212
- foreach ($ lines as $ line => $ coords ) {
213
- $ used [$ line ] = array_merge ($ used [$ line ] ?? [], $ coords );
214
- }
215
- } else {
216
- unset($ changes [$ key ]);
217
- }
218
- }
219
-
220
- // Return
221
- return $ changes ;
222
- }
223
-
224
- /**
225
- * @param array<int, array<int, Coordinate>> $coordinates
55
+ * @return new<static>
226
56
*/
227
- private function isOverlapped (array $ coordinates , Coordinate $ coordinate ): bool {
228
- // Append?
229
- if ($ coordinate ->line === PHP_INT_MAX ) {
230
- return false ;
231
- }
232
-
233
- // Check
234
- $ overlapped = false ;
235
-
236
- foreach ($ coordinates [$ coordinate ->line ] ?? [] as $ c ) {
237
- $ aStart = $ c ->offset ;
238
- $ aEnd = $ aStart + ($ c ->length ?? PHP_INT_MAX ) - 1 ;
239
- $ bStart = $ coordinate ->offset ;
240
- $ bEnd = $ bStart + ($ coordinate ->length ?? PHP_INT_MAX ) - 1 ;
241
- $ overlapped = !($ bEnd < $ aStart || $ bStart > $ aEnd );
242
-
243
- if ($ overlapped ) {
244
- break ;
245
- }
246
- }
57
+ public function mutate (iterable $ changes ): static {
58
+ $ mutated = ($ this ->mutator )($ this ->lines , $ changes , $ this ->startLine );
59
+ $ editor = new static ($ mutated , $ this ->startLine , $ this ->endOfLine );
247
60
248
- return $ overlapped ;
61
+ return $ editor ;
249
62
}
250
63
}
0 commit comments