Changeset View
Changeset View
Standalone View
Standalone View
src/buffer/katetextblock.cpp
Show All 15 Lines | |||||
16 | * along with this library; see the file COPYING.LIB. If not, write to | 16 | * along with this library; see the file COPYING.LIB. If not, write to | ||
17 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | 17 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||
18 | * Boston, MA 02110-1301, USA. | 18 | * Boston, MA 02110-1301, USA. | ||
19 | */ | 19 | */ | ||
20 | 20 | | |||
21 | #include "katetextblock.h" | 21 | #include "katetextblock.h" | ||
22 | #include "katetextbuffer.h" | 22 | #include "katetextbuffer.h" | ||
23 | 23 | | |||
24 | #include <QVarLengthArray> | ||||
25 | | ||||
24 | namespace Kate | 26 | namespace Kate | ||
25 | { | 27 | { | ||
26 | 28 | | |||
27 | TextBlock::TextBlock(TextBuffer *buffer, int startLine) | 29 | TextBlock::TextBlock(TextBuffer *buffer, int startLine) | ||
28 | : m_buffer(buffer) | 30 | : m_buffer(buffer) | ||
29 | , m_startLine(startLine) | 31 | , m_startLine(startLine) | ||
30 | { | 32 | { | ||
31 | // reserve the block size | 33 | // reserve the block size | ||
▲ Show 20 Lines • Show All 112 Lines • ▼ Show 20 Line(s) | 94 | { | |||
144 | // no cursors will leave or join this block | 146 | // no cursors will leave or join this block | ||
145 | 147 | | |||
146 | // no cursors in this block, no work to do.. | 148 | // no cursors in this block, no work to do.. | ||
147 | if (m_cursors.empty()) { | 149 | if (m_cursors.empty()) { | ||
148 | return; | 150 | return; | ||
149 | } | 151 | } | ||
150 | 152 | | |||
151 | // move all cursors on the line which has the text inserted | 153 | // move all cursors on the line which has the text inserted | ||
152 | QSet<TextRange *> changedRanges; | 154 | // remember all ranges modified, optimize for the standard case of a few ranges | ||
155 | QVarLengthArray<TextRange *, 32> changedRanges; | ||||
anthonyfieroni: You can use unordered_set, so you don't need m_isCheckValidityRequired any more. It can be… | |||||
Actually no, that will be again a lot more expensive. cullmann: Actually no, that will be again a lot more expensive.
With the vector, you get one allocation… | |||||
153 | for (TextCursor *cursor : qAsConst(m_cursors)) { | 156 | for (TextCursor *cursor : qAsConst(m_cursors)) { | ||
154 | // skip cursors on lines in front of the wrapped one! | 157 | // skip cursors on lines in front of the wrapped one! | ||
155 | if (cursor->lineInBlock() < line) { | 158 | if (cursor->lineInBlock() < line) { | ||
156 | continue; | 159 | continue; | ||
157 | } | 160 | } | ||
158 | 161 | | |||
159 | // either this is simple, line behind the wrapped one | 162 | // either this is simple, line behind the wrapped one | ||
160 | if (cursor->lineInBlock() > line) { | 163 | if (cursor->lineInBlock() > line) { | ||
Show All 14 Lines | 169 | else { | |||
175 | 178 | | |||
176 | // patch line of cursor | 179 | // patch line of cursor | ||
177 | cursor->m_line++; | 180 | cursor->m_line++; | ||
178 | 181 | | |||
179 | // patch column | 182 | // patch column | ||
180 | cursor->m_column -= position.column(); | 183 | cursor->m_column -= position.column(); | ||
181 | } | 184 | } | ||
182 | 185 | | |||
183 | // remember range, if any | 186 | // remember range, if any, avoid double insert | ||
184 | if (cursor->kateRange()) { | 187 | auto range = cursor->kateRange(); | ||
185 | changedRanges.insert(cursor->kateRange()); | 188 | if (range && !range->isValidityCheckRequired()) { | ||
189 | range->setValidityCheckRequired(); | ||||
190 | changedRanges.push_back(range); | ||||
186 | } | 191 | } | ||
187 | } | 192 | } | ||
188 | 193 | | |||
189 | // we might need to invalidate ranges or notify about their changes | 194 | // we might need to invalidate ranges or notify about their changes | ||
190 | // checkValidity might trigger delete of the range! | 195 | // checkValidity might trigger delete of the range! | ||
191 | for (TextRange *range : qAsConst(changedRanges)) { | 196 | for (TextRange *range : qAsConst(changedRanges)) { | ||
192 | range->checkValidity(); | 197 | range->checkValidity(); | ||
193 | } | 198 | } | ||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Line(s) | 207 | if (line == 0) { | |||
240 | */ | 245 | */ | ||
241 | 246 | | |||
242 | // no cursors in this block and the previous one, no work to do.. | 247 | // no cursors in this block and the previous one, no work to do.. | ||
243 | if (m_cursors.empty() && previousBlock->m_cursors.empty()) { | 248 | if (m_cursors.empty() && previousBlock->m_cursors.empty()) { | ||
244 | return; | 249 | return; | ||
245 | } | 250 | } | ||
246 | 251 | | |||
247 | // move all cursors because of the unwrapped line | 252 | // move all cursors because of the unwrapped line | ||
248 | QSet<TextRange *> changedRanges; | 253 | // remember all ranges modified, optimize for the standard case of a few ranges | ||
254 | QVarLengthArray<TextRange *, 32> changedRanges; | ||||
249 | for (TextCursor *cursor : qAsConst(m_cursors)) { | 255 | for (TextCursor *cursor : qAsConst(m_cursors)) { | ||
250 | // this is the unwrapped line | 256 | // this is the unwrapped line | ||
251 | if (cursor->lineInBlock() == 0) { | 257 | if (cursor->lineInBlock() == 0) { | ||
252 | // patch column | 258 | // patch column | ||
253 | cursor->m_column += oldSizeOfPreviousLine; | 259 | cursor->m_column += oldSizeOfPreviousLine; | ||
254 | 260 | | |||
255 | // remember range, if any | 261 | // remember range, if any, avoid double insert | ||
256 | if (cursor->kateRange()) { | 262 | auto range = cursor->kateRange(); | ||
257 | changedRanges.insert(cursor->kateRange()); | 263 | if (range && !range->isValidityCheckRequired()) { | ||
264 | range->setValidityCheckRequired(); | ||||
265 | changedRanges.push_back(range); | ||||
258 | } | 266 | } | ||
259 | } | 267 | } | ||
260 | } | 268 | } | ||
261 | 269 | | |||
262 | // move cursors of the moved line from previous block to this block now | 270 | // move cursors of the moved line from previous block to this block now | ||
263 | QSet<TextCursor *> newPreviousCursors; | 271 | QSet<TextCursor *> newPreviousCursors; | ||
264 | for (TextCursor *cursor : qAsConst(previousBlock->m_cursors)) { | 272 | for (TextCursor *cursor : qAsConst(previousBlock->m_cursors)) { | ||
265 | if (cursor->lineInBlock() == lastLineOfPreviousBlock) { | 273 | if (cursor->lineInBlock() == lastLineOfPreviousBlock) { | ||
266 | cursor->m_line = 0; | 274 | cursor->m_line = 0; | ||
267 | cursor->m_block = this; | 275 | cursor->m_block = this; | ||
268 | m_cursors.insert(cursor); | 276 | m_cursors.insert(cursor); | ||
269 | 277 | | |||
270 | // remember range, if any | 278 | // remember range, if any, avoid double insert | ||
271 | if (cursor->kateRange()) { | 279 | auto range = cursor->kateRange(); | ||
272 | changedRanges.insert(cursor->kateRange()); | 280 | if (range && !range->isValidityCheckRequired()) { | ||
281 | range->setValidityCheckRequired(); | ||||
282 | changedRanges.push_back(range); | ||||
273 | } | 283 | } | ||
274 | } else { | 284 | } else { | ||
275 | newPreviousCursors.insert(cursor); | 285 | newPreviousCursors.insert(cursor); | ||
276 | } | 286 | } | ||
277 | } | 287 | } | ||
278 | previousBlock->m_cursors = newPreviousCursors; | 288 | previousBlock->m_cursors = newPreviousCursors; | ||
279 | 289 | | |||
280 | // fixup the ranges that might be effected, because they moved from last line to this block | 290 | // fixup the ranges that might be effected, because they moved from last line to this block | ||
281 | // we might need to invalidate ranges or notify about their changes | 291 | // we might need to invalidate ranges or notify about their changes | ||
282 | // checkValidity might trigger delete of the range! | 292 | // checkValidity might trigger delete of the range! | ||
283 | for (TextRange *range : qAsConst(changedRanges)) { | 293 | for (TextRange *range : qAsConst(changedRanges)) { | ||
284 | // update both blocks | 294 | // update both blocks | ||
285 | updateRange(range); | 295 | updateRange(range); | ||
286 | previousBlock->updateRange(range); | 296 | previousBlock->updateRange(range); | ||
287 | 297 | | |||
288 | // afterwards check validity, might delete this range! | 298 | // afterwards check validity, might delete this range! | ||
289 | range->checkValidity(); | 299 | range->checkValidity(); | ||
290 | } | 300 | } | ||
This loop now crash in the unit test ./bin/bug313759 )It works again, if I use a QSet, at that one location. cullmann: This loop now crash in the unit test
./bin/bug313759
=)
It works again, if I use a QSet, at… | |||||
Ok, doesn't work due to the issue that we might delete the range via notifier in checkValidity() :P cullmann: Ok, doesn't work due to the issue that we might delete the range via notifier in checkValidity… | |||||
Noob question: Would QPointer help? if (range && range->isValidityCheckRequired()) { loh.tar: Noob question: Would QPointer help?
if (range && range->isValidityCheckRequired()) { | |||||
As it is no QObject, no ;=) Actually, I think all workaround might be even more expensive than the QSet. cullmann: As it is no QObject, no ;=) Actually, I think all workaround might be even more expensive than… | |||||
291 | 301 | | |||
292 | // be done | 302 | // be done | ||
293 | return; | 303 | return; | ||
294 | } | 304 | } | ||
295 | 305 | | |||
296 | // easy: just move text to previous line and remove current one | 306 | // easy: just move text to previous line and remove current one | ||
297 | const int oldSizeOfPreviousLine = m_lines.at(line - 1)->length(); | 307 | const int oldSizeOfPreviousLine = m_lines.at(line - 1)->length(); | ||
298 | const int sizeOfCurrentLine = m_lines.at(line)->length(); | 308 | const int sizeOfCurrentLine = m_lines.at(line)->length(); | ||
Show All 27 Lines | |||||
326 | */ | 336 | */ | ||
327 | 337 | | |||
328 | // no cursors in this block, no work to do.. | 338 | // no cursors in this block, no work to do.. | ||
329 | if (m_cursors.empty()) { | 339 | if (m_cursors.empty()) { | ||
330 | return; | 340 | return; | ||
331 | } | 341 | } | ||
332 | 342 | | |||
333 | // move all cursors because of the unwrapped line | 343 | // move all cursors because of the unwrapped line | ||
334 | QSet<TextRange *> changedRanges; | 344 | // remember all ranges modified, optimize for the standard case of a few ranges | ||
345 | QVarLengthArray<TextRange *, 32> changedRanges; | ||||
335 | for (TextCursor *cursor : qAsConst(m_cursors)) { | 346 | for (TextCursor *cursor : qAsConst(m_cursors)) { | ||
336 | // skip cursors in lines in front of removed one | 347 | // skip cursors in lines in front of removed one | ||
337 | if (cursor->lineInBlock() < line) { | 348 | if (cursor->lineInBlock() < line) { | ||
338 | continue; | 349 | continue; | ||
339 | } | 350 | } | ||
340 | 351 | | |||
341 | // this is the unwrapped line | 352 | // this is the unwrapped line | ||
342 | if (cursor->lineInBlock() == line) { | 353 | if (cursor->lineInBlock() == line) { | ||
343 | // patch column | 354 | // patch column | ||
344 | cursor->m_column += oldSizeOfPreviousLine; | 355 | cursor->m_column += oldSizeOfPreviousLine; | ||
345 | } | 356 | } | ||
346 | 357 | | |||
347 | // patch line of cursor | 358 | // patch line of cursor | ||
348 | cursor->m_line--; | 359 | cursor->m_line--; | ||
349 | 360 | | |||
350 | // remember range, if any | 361 | // remember range, if any, avoid double insert | ||
351 | if (cursor->kateRange()) { | 362 | auto range = cursor->kateRange(); | ||
352 | changedRanges.insert(cursor->kateRange()); | 363 | if (range && !range->isValidityCheckRequired()) { | ||
364 | range->setValidityCheckRequired(); | ||||
365 | changedRanges.push_back(range); | ||||
353 | } | 366 | } | ||
354 | } | 367 | } | ||
355 | 368 | | |||
356 | // we might need to invalidate ranges or notify about their changes | 369 | // we might need to invalidate ranges or notify about their changes | ||
357 | // checkValidity might trigger delete of the range! | 370 | // checkValidity might trigger delete of the range! | ||
358 | for (TextRange *range : qAsConst(changedRanges)) { | 371 | for (TextRange *range : qAsConst(changedRanges)) { | ||
359 | range->checkValidity(); | 372 | range->checkValidity(); | ||
360 | } | 373 | } | ||
Show All 26 Lines | 377 | { | |||
387 | */ | 400 | */ | ||
388 | 401 | | |||
389 | // no cursors in this block, no work to do.. | 402 | // no cursors in this block, no work to do.. | ||
390 | if (m_cursors.empty()) { | 403 | if (m_cursors.empty()) { | ||
391 | return; | 404 | return; | ||
392 | } | 405 | } | ||
393 | 406 | | |||
394 | // move all cursors on the line which has the text inserted | 407 | // move all cursors on the line which has the text inserted | ||
395 | QSet<TextRange *> changedRanges; | 408 | // remember all ranges modified, optimize for the standard case of a few ranges | ||
409 | QVarLengthArray<TextRange *, 32> changedRanges; | ||||
396 | for (TextCursor *cursor : qAsConst(m_cursors)) { | 410 | for (TextCursor *cursor : qAsConst(m_cursors)) { | ||
397 | // skip cursors not on this line! | 411 | // skip cursors not on this line! | ||
398 | if (cursor->lineInBlock() != line) { | 412 | if (cursor->lineInBlock() != line) { | ||
399 | continue; | 413 | continue; | ||
400 | } | 414 | } | ||
401 | 415 | | |||
402 | // skip cursors with too small column | 416 | // skip cursors with too small column | ||
403 | if (cursor->column() <= position.column()) { | 417 | if (cursor->column() <= position.column()) { | ||
404 | if (cursor->column() < position.column() || !cursor->m_moveOnInsert) { | 418 | if (cursor->column() < position.column() || !cursor->m_moveOnInsert) { | ||
405 | continue; | 419 | continue; | ||
406 | } | 420 | } | ||
407 | } | 421 | } | ||
408 | 422 | | |||
409 | // patch column of cursor | 423 | // patch column of cursor | ||
410 | if (cursor->m_column <= oldLength) { | 424 | if (cursor->m_column <= oldLength) { | ||
411 | cursor->m_column += text.size(); | 425 | cursor->m_column += text.size(); | ||
412 | } | 426 | } | ||
413 | 427 | | |||
414 | // special handling if cursor behind the real line, e.g. non-wrapping cursor in block selection mode | 428 | // special handling if cursor behind the real line, e.g. non-wrapping cursor in block selection mode | ||
415 | else if (cursor->m_column < textOfLine.size()) { | 429 | else if (cursor->m_column < textOfLine.size()) { | ||
416 | cursor->m_column = textOfLine.size(); | 430 | cursor->m_column = textOfLine.size(); | ||
417 | } | 431 | } | ||
418 | 432 | | |||
433 | // remember range, if any, avoid double insert | ||||
419 | // we only need to trigger checkValidity later if the range has feedback or might be invalidated | 434 | // we only need to trigger checkValidity later if the range has feedback or might be invalidated | ||
420 | if (cursor->kateRange() && (cursor->kateRange()->feedback() || cursor->kateRange()->start().line() == cursor->kateRange()->end().line())) { | 435 | auto range = cursor->kateRange(); | ||
421 | changedRanges.insert(cursor->kateRange()); | 436 | if (range && !range->isValidityCheckRequired() && (range->feedback() || range->start().line() == range->end().line())) { | ||
437 | range->setValidityCheckRequired(); | ||||
438 | changedRanges.push_back(range); | ||||
422 | } | 439 | } | ||
423 | } | 440 | } | ||
424 | 441 | | |||
425 | // we might need to invalidate ranges or notify about their changes | 442 | // we might need to invalidate ranges or notify about their changes | ||
426 | // checkValidity might trigger delete of the range! | 443 | // checkValidity might trigger delete of the range! | ||
427 | for (TextRange *range : qAsConst(changedRanges)) { | 444 | for (TextRange *range : qAsConst(changedRanges)) { | ||
428 | range->checkValidity(); | 445 | range->checkValidity(); | ||
429 | } | 446 | } | ||
Show All 31 Lines | 450 | { | |||
461 | */ | 478 | */ | ||
462 | 479 | | |||
463 | // no cursors in this block, no work to do.. | 480 | // no cursors in this block, no work to do.. | ||
464 | if (m_cursors.empty()) { | 481 | if (m_cursors.empty()) { | ||
465 | return; | 482 | return; | ||
466 | } | 483 | } | ||
467 | 484 | | |||
468 | // move all cursors on the line which has the text removed | 485 | // move all cursors on the line which has the text removed | ||
469 | QSet<TextRange *> changedRanges; | 486 | // remember all ranges modified, optimize for the standard case of a few ranges | ||
487 | QVarLengthArray<TextRange *, 32> changedRanges; | ||||
470 | for (TextCursor *cursor : qAsConst(m_cursors)) { | 488 | for (TextCursor *cursor : qAsConst(m_cursors)) { | ||
471 | // skip cursors not on this line! | 489 | // skip cursors not on this line! | ||
472 | if (cursor->lineInBlock() != line) { | 490 | if (cursor->lineInBlock() != line) { | ||
473 | continue; | 491 | continue; | ||
474 | } | 492 | } | ||
475 | 493 | | |||
476 | // skip cursors with too small column | 494 | // skip cursors with too small column | ||
477 | if (cursor->column() <= range.start().column()) { | 495 | if (cursor->column() <= range.start().column()) { | ||
478 | continue; | 496 | continue; | ||
479 | } | 497 | } | ||
480 | 498 | | |||
481 | // patch column of cursor | 499 | // patch column of cursor | ||
482 | if (cursor->column() <= range.end().column()) { | 500 | if (cursor->column() <= range.end().column()) { | ||
483 | cursor->m_column = range.start().column(); | 501 | cursor->m_column = range.start().column(); | ||
484 | } else { | 502 | } else { | ||
485 | cursor->m_column -= (range.end().column() - range.start().column()); | 503 | cursor->m_column -= (range.end().column() - range.start().column()); | ||
486 | } | 504 | } | ||
487 | 505 | | |||
506 | // remember range, if any, avoid double insert | ||||
488 | // we only need to trigger checkValidity later if the range has feedback or might be invalidated | 507 | // we only need to trigger checkValidity later if the range has feedback or might be invalidated | ||
489 | if (cursor->kateRange() && (cursor->kateRange()->feedback() || cursor->kateRange()->start().line() == cursor->kateRange()->end().line())) { | 508 | auto range = cursor->kateRange(); | ||
490 | changedRanges.insert(cursor->kateRange()); | 509 | if (range && !range->isValidityCheckRequired() && (range->feedback() || range->start().line() == range->end().line())) { | ||
510 | range->setValidityCheckRequired(); | ||||
511 | changedRanges.push_back(range); | ||||
491 | } | 512 | } | ||
492 | } | 513 | } | ||
493 | 514 | | |||
494 | // we might need to invalidate ranges or notify about their changes | 515 | // we might need to invalidate ranges or notify about their changes | ||
495 | // checkValidity might trigger delete of the range! | 516 | // checkValidity might trigger delete of the range! | ||
496 | for (TextRange *range : qAsConst(changedRanges)) { | 517 | for (TextRange *range : qAsConst(changedRanges)) { | ||
497 | range->checkValidity(); | 518 | range->checkValidity(); | ||
498 | } | 519 | } | ||
▲ Show 20 Lines • Show All 227 Lines • Show Last 20 Lines |
You can use unordered_set, so you don't need m_isCheckValidityRequired any more. It can be applied to other places as well.