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 | // remember all ranges modified | 154 | // remember all ranges modified, optimize for the standard case of a few ranges | ||
153 | QSet<TextRange *> changedRanges; | 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… | |||||
154 | foreach (TextCursor *cursor, m_cursors) { | 156 | foreach (TextCursor *cursor, m_cursors) { | ||
155 | // skip cursors on lines in front of the wrapped one! | 157 | // skip cursors on lines in front of the wrapped one! | ||
156 | if (cursor->lineInBlock() < line) { | 158 | if (cursor->lineInBlock() < line) { | ||
157 | continue; | 159 | continue; | ||
158 | } | 160 | } | ||
159 | 161 | | |||
160 | // either this is simple, line behind the wrapped one | 162 | // either this is simple, line behind the wrapped one | ||
161 | if (cursor->lineInBlock() > line) { | 163 | if (cursor->lineInBlock() > line) { | ||
Show All 16 Lines | 169 | else { | |||
178 | cursor->m_line++; | 180 | cursor->m_line++; | ||
179 | 181 | | |||
180 | // patch column | 182 | // patch column | ||
181 | cursor->m_column -= position.column(); | 183 | cursor->m_column -= position.column(); | ||
182 | } | 184 | } | ||
183 | 185 | | |||
184 | // remember range, if any | 186 | // remember range, if any | ||
185 | if (cursor->kateRange()) { | 187 | if (cursor->kateRange()) { | ||
186 | changedRanges.insert(cursor->kateRange()); | 188 | cursor->kateRange()->setValidityCheckRequired(); | ||
189 | changedRanges.push_back(cursor->kateRange()); | ||||
187 | } | 190 | } | ||
188 | } | 191 | } | ||
189 | 192 | | |||
190 | // check validity of all ranges, might invalidate them... | 193 | // we might need to invalidate ranges or notify about their changes | ||
191 | foreach (TextRange *range, changedRanges) { | 194 | for (TextRange *range : changedRanges) { | ||
195 | if (range->isValidityCheckRequired()) { | ||||
192 | range->checkValidity(); | 196 | range->checkValidity(); | ||
193 | } | 197 | } | ||
194 | } | 198 | } | ||
199 | } | ||||
195 | 200 | | |||
196 | void TextBlock::unwrapLine(int line, TextBlock *previousBlock, int fixStartLinesStartIndex) | 201 | void TextBlock::unwrapLine(int line, TextBlock *previousBlock, int fixStartLinesStartIndex) | ||
197 | { | 202 | { | ||
198 | // calc internal line | 203 | // calc internal line | ||
199 | line = line - startLine(); | 204 | line = line - startLine(); | ||
200 | 205 | | |||
201 | // two possiblities: either first line of this block or later line | 206 | // two possiblities: either first line of this block or later line | ||
202 | if (line == 0) { | 207 | if (line == 0) { | ||
Show All 37 Lines | |||||
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 | // remember all ranges modified | 253 | // remember all ranges modified, optimize for the standard case of a few ranges | ||
249 | QSet<TextRange *> changedRanges; | 254 | QVarLengthArray<TextRange *, 32> changedRanges; | ||
250 | foreach (TextCursor *cursor, m_cursors) { | 255 | foreach (TextCursor *cursor, m_cursors) { | ||
251 | // this is the unwrapped line | 256 | // this is the unwrapped line | ||
252 | if (cursor->lineInBlock() == 0) { | 257 | if (cursor->lineInBlock() == 0) { | ||
253 | // patch column | 258 | // patch column | ||
254 | cursor->m_column += oldSizeOfPreviousLine; | 259 | cursor->m_column += oldSizeOfPreviousLine; | ||
255 | 260 | | |||
256 | // remember range, if any | 261 | // remember range, if any | ||
257 | if (cursor->kateRange()) { | 262 | if (cursor->kateRange()) { | ||
258 | changedRanges.insert(cursor->kateRange()); | 263 | cursor->kateRange()->setValidityCheckRequired(); | ||
264 | changedRanges.push_back(cursor->kateRange()); | ||||
259 | } | 265 | } | ||
260 | } | 266 | } | ||
261 | } | 267 | } | ||
262 | 268 | | |||
263 | // move cursors of the moved line from previous block to this block now | 269 | // move cursors of the moved line from previous block to this block now | ||
264 | QSet<TextCursor *> newPreviousCursors; | 270 | QSet<TextCursor *> newPreviousCursors; | ||
265 | foreach (TextCursor *cursor, previousBlock->m_cursors) { | 271 | foreach (TextCursor *cursor, previousBlock->m_cursors) { | ||
266 | if (cursor->lineInBlock() == lastLineOfPreviousBlock) { | 272 | if (cursor->lineInBlock() == lastLineOfPreviousBlock) { | ||
267 | cursor->m_line = 0; | 273 | cursor->m_line = 0; | ||
268 | cursor->m_block = this; | 274 | cursor->m_block = this; | ||
269 | m_cursors.insert(cursor); | 275 | m_cursors.insert(cursor); | ||
270 | 276 | | |||
271 | // remember range, if any | 277 | // remember range, if any | ||
272 | if (cursor->kateRange()) { | 278 | if (cursor->kateRange()) { | ||
273 | changedRanges.insert(cursor->kateRange()); | 279 | cursor->kateRange()->setValidityCheckRequired(); | ||
280 | changedRanges.push_back(cursor->kateRange()); | ||||
274 | } | 281 | } | ||
275 | } else { | 282 | } else { | ||
276 | newPreviousCursors.insert(cursor); | 283 | newPreviousCursors.insert(cursor); | ||
277 | } | 284 | } | ||
278 | } | 285 | } | ||
279 | previousBlock->m_cursors = newPreviousCursors; | 286 | previousBlock->m_cursors = newPreviousCursors; | ||
280 | 287 | | |||
281 | // fixup the ranges that might be effected, because they moved from last line to this block | 288 | // fixup the ranges that might be effected, because they moved from last line to this block | ||
282 | foreach (TextRange *range, changedRanges) { | 289 | // we might need to invalidate ranges or notify about their changes | ||
290 | for (TextRange *range : changedRanges) { | ||||
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 | if (range->isValidityCheckRequired()) { | ||||
283 | // update both blocks | 292 | // update both blocks | ||
284 | updateRange(range); | 293 | updateRange(range); | ||
285 | previousBlock->updateRange(range); | 294 | previousBlock->updateRange(range); | ||
286 | } | | |||
287 | 295 | | |||
288 | // check validity of all ranges, might invalidate them... | 296 | // afterwards check validity | ||
289 | foreach (TextRange *range, changedRanges) { | | |||
290 | range->checkValidity(); | 297 | range->checkValidity(); | ||
291 | } | 298 | } | ||
299 | } | ||||
292 | 300 | | |||
293 | // be done | 301 | // be done | ||
294 | return; | 302 | return; | ||
295 | } | 303 | } | ||
296 | 304 | | |||
297 | // easy: just move text to previous line and remove current one | 305 | // easy: just move text to previous line and remove current one | ||
298 | const int oldSizeOfPreviousLine = m_lines.at(line - 1)->length(); | 306 | const int oldSizeOfPreviousLine = m_lines.at(line - 1)->length(); | ||
299 | const int sizeOfCurrentLine = m_lines.at(line)->length(); | 307 | const int sizeOfCurrentLine = m_lines.at(line)->length(); | ||
Show All 27 Lines | |||||
327 | */ | 335 | */ | ||
328 | 336 | | |||
329 | // no cursors in this block, no work to do.. | 337 | // no cursors in this block, no work to do.. | ||
330 | if (m_cursors.empty()) { | 338 | if (m_cursors.empty()) { | ||
331 | return; | 339 | return; | ||
332 | } | 340 | } | ||
333 | 341 | | |||
334 | // move all cursors because of the unwrapped line | 342 | // move all cursors because of the unwrapped line | ||
335 | // remember all ranges modified | 343 | // remember all ranges modified, optimize for the standard case of a few ranges | ||
336 | QSet<TextRange *> changedRanges; | 344 | QVarLengthArray<TextRange *, 32> changedRanges; | ||
337 | foreach (TextCursor *cursor, m_cursors) { | 345 | foreach (TextCursor *cursor, m_cursors) { | ||
338 | // skip cursors in lines in front of removed one | 346 | // skip cursors in lines in front of removed one | ||
339 | if (cursor->lineInBlock() < line) { | 347 | if (cursor->lineInBlock() < line) { | ||
340 | continue; | 348 | continue; | ||
341 | } | 349 | } | ||
342 | 350 | | |||
343 | // this is the unwrapped line | 351 | // this is the unwrapped line | ||
344 | if (cursor->lineInBlock() == line) { | 352 | if (cursor->lineInBlock() == line) { | ||
345 | // patch column | 353 | // patch column | ||
346 | cursor->m_column += oldSizeOfPreviousLine; | 354 | cursor->m_column += oldSizeOfPreviousLine; | ||
347 | } | 355 | } | ||
348 | 356 | | |||
349 | // patch line of cursor | 357 | // patch line of cursor | ||
350 | cursor->m_line--; | 358 | cursor->m_line--; | ||
351 | 359 | | |||
352 | // remember range, if any | 360 | // remember range, if any | ||
353 | if (cursor->kateRange()) { | 361 | if (cursor->kateRange()) { | ||
354 | changedRanges.insert(cursor->kateRange()); | 362 | cursor->kateRange()->setValidityCheckRequired(); | ||
363 | changedRanges.push_back(cursor->kateRange()); | ||||
355 | } | 364 | } | ||
356 | } | 365 | } | ||
357 | 366 | | |||
358 | // check validity of all ranges, might invalidate them... | 367 | // we might need to invalidate ranges or notify about their changes | ||
359 | foreach (TextRange *range, changedRanges) { | 368 | for (TextRange *range : changedRanges) { | ||
369 | if (range->isValidityCheckRequired()) { | ||||
360 | range->checkValidity(); | 370 | range->checkValidity(); | ||
361 | } | 371 | } | ||
362 | } | 372 | } | ||
373 | } | ||||
363 | 374 | | |||
364 | void TextBlock::insertText(const KTextEditor::Cursor &position, const QString &text) | 375 | void TextBlock::insertText(const KTextEditor::Cursor &position, const QString &text) | ||
365 | { | 376 | { | ||
366 | // calc internal line | 377 | // calc internal line | ||
367 | int line = position.line() - startLine(); | 378 | int line = position.line() - startLine(); | ||
368 | 379 | | |||
369 | // get text | 380 | // get text | ||
370 | QString &textOfLine = m_lines.at(line)->textReadWrite(); | 381 | QString &textOfLine = m_lines.at(line)->textReadWrite(); | ||
Show All 17 Lines | |||||
388 | */ | 399 | */ | ||
389 | 400 | | |||
390 | // no cursors in this block, no work to do.. | 401 | // no cursors in this block, no work to do.. | ||
391 | if (m_cursors.empty()) { | 402 | if (m_cursors.empty()) { | ||
392 | return; | 403 | return; | ||
393 | } | 404 | } | ||
394 | 405 | | |||
395 | // move all cursors on the line which has the text inserted | 406 | // move all cursors on the line which has the text inserted | ||
396 | // remember all ranges modified | 407 | // remember all ranges modified, optimize for the standard case of a few ranges | ||
397 | QSet<TextRange *> changedRanges; | 408 | QVarLengthArray<TextRange *, 32> changedRanges; | ||
398 | foreach (TextCursor *cursor, m_cursors) { | 409 | foreach (TextCursor *cursor, m_cursors) { | ||
399 | // skip cursors not on this line! | 410 | // skip cursors not on this line! | ||
400 | if (cursor->lineInBlock() != line) { | 411 | if (cursor->lineInBlock() != line) { | ||
401 | continue; | 412 | continue; | ||
402 | } | 413 | } | ||
403 | 414 | | |||
404 | // skip cursors with too small column | 415 | // skip cursors with too small column | ||
405 | if (cursor->column() <= position.column()) { | 416 | if (cursor->column() <= position.column()) { | ||
406 | if (cursor->column() < position.column() || !cursor->m_moveOnInsert) { | 417 | if (cursor->column() < position.column() || !cursor->m_moveOnInsert) { | ||
407 | continue; | 418 | continue; | ||
408 | } | 419 | } | ||
409 | } | 420 | } | ||
410 | 421 | | |||
411 | // patch column of cursor | 422 | // patch column of cursor | ||
412 | if (cursor->m_column <= oldLength) { | 423 | if (cursor->m_column <= oldLength) { | ||
413 | cursor->m_column += text.size(); | 424 | cursor->m_column += text.size(); | ||
414 | } | 425 | } | ||
415 | 426 | | |||
416 | // special handling if cursor behind the real line, e.g. non-wrapping cursor in block selection mode | 427 | // special handling if cursor behind the real line, e.g. non-wrapping cursor in block selection mode | ||
417 | else if (cursor->m_column < textOfLine.size()) { | 428 | else if (cursor->m_column < textOfLine.size()) { | ||
418 | cursor->m_column = textOfLine.size(); | 429 | cursor->m_column = textOfLine.size(); | ||
419 | } | 430 | } | ||
420 | 431 | | |||
421 | // remember range, if any | 432 | // we only need to trigger checkValidity later if the range has feedback or might be invalidated | ||
422 | if (cursor->kateRange()) { | 433 | if (cursor->kateRange() && (cursor->kateRange()->feedback() || cursor->kateRange()->start().line() == cursor->kateRange()->end().line())) { | ||
423 | changedRanges.insert(cursor->kateRange()); | 434 | cursor->kateRange()->setValidityCheckRequired(); | ||
435 | changedRanges.push_back(cursor->kateRange()); | ||||
424 | } | 436 | } | ||
425 | } | 437 | } | ||
426 | 438 | | |||
427 | // check validity of all ranges, might invalidate them... | 439 | // we might need to invalidate ranges or notify about their changes | ||
428 | foreach (TextRange *range, changedRanges) { | 440 | for (TextRange *range : changedRanges) { | ||
441 | if (range->isValidityCheckRequired()) { | ||||
429 | range->checkValidity(); | 442 | range->checkValidity(); | ||
430 | } | 443 | } | ||
431 | } | 444 | } | ||
445 | } | ||||
432 | 446 | | |||
433 | void TextBlock::removeText(const KTextEditor::Range &range, QString &removedText) | 447 | void TextBlock::removeText(const KTextEditor::Range &range, QString &removedText) | ||
434 | { | 448 | { | ||
435 | // calc internal line | 449 | // calc internal line | ||
436 | int line = range.start().line() - startLine(); | 450 | int line = range.start().line() - startLine(); | ||
437 | 451 | | |||
438 | // get text | 452 | // get text | ||
439 | QString &textOfLine = m_lines.at(line)->textReadWrite(); | 453 | QString &textOfLine = m_lines.at(line)->textReadWrite(); | ||
Show All 22 Lines | |||||
462 | */ | 476 | */ | ||
463 | 477 | | |||
464 | // no cursors in this block, no work to do.. | 478 | // no cursors in this block, no work to do.. | ||
465 | if (m_cursors.empty()) { | 479 | if (m_cursors.empty()) { | ||
466 | return; | 480 | return; | ||
467 | } | 481 | } | ||
468 | 482 | | |||
469 | // move all cursors on the line which has the text removed | 483 | // move all cursors on the line which has the text removed | ||
470 | // remember all ranges modified | 484 | // remember all ranges modified, optimize for the standard case of a few ranges | ||
471 | QSet<TextRange *> changedRanges; | 485 | QVarLengthArray<TextRange *, 32> changedRanges; | ||
472 | foreach (TextCursor *cursor, m_cursors) { | 486 | foreach (TextCursor *cursor, m_cursors) { | ||
473 | // skip cursors not on this line! | 487 | // skip cursors not on this line! | ||
474 | if (cursor->lineInBlock() != line) { | 488 | if (cursor->lineInBlock() != line) { | ||
475 | continue; | 489 | continue; | ||
476 | } | 490 | } | ||
477 | 491 | | |||
478 | // skip cursors with too small column | 492 | // skip cursors with too small column | ||
479 | if (cursor->column() <= range.start().column()) { | 493 | if (cursor->column() <= range.start().column()) { | ||
480 | continue; | 494 | continue; | ||
481 | } | 495 | } | ||
482 | 496 | | |||
483 | // patch column of cursor | 497 | // patch column of cursor | ||
484 | if (cursor->column() <= range.end().column()) { | 498 | if (cursor->column() <= range.end().column()) { | ||
485 | cursor->m_column = range.start().column(); | 499 | cursor->m_column = range.start().column(); | ||
486 | } else { | 500 | } else { | ||
487 | cursor->m_column -= (range.end().column() - range.start().column()); | 501 | cursor->m_column -= (range.end().column() - range.start().column()); | ||
488 | } | 502 | } | ||
489 | 503 | | |||
490 | // remember range, if any | 504 | // we only need to trigger checkValidity later if the range has feedback or might be invalidated | ||
491 | if (cursor->kateRange()) { | 505 | if (cursor->kateRange() && (cursor->kateRange()->feedback() || cursor->kateRange()->start().line() == cursor->kateRange()->end().line())) { | ||
492 | changedRanges.insert(cursor->kateRange()); | 506 | cursor->kateRange()->setValidityCheckRequired(); | ||
507 | changedRanges.push_back(cursor->kateRange()); | ||||
493 | } | 508 | } | ||
494 | } | 509 | } | ||
495 | 510 | | |||
496 | // check validity of all ranges, might invalidate them... | 511 | // we might need to invalidate ranges or notify about their changes | ||
497 | foreach (TextRange *range, changedRanges) { | 512 | for (TextRange *range : changedRanges) { | ||
513 | if (range->isValidityCheckRequired()) { | ||||
498 | range->checkValidity(); | 514 | range->checkValidity(); | ||
499 | } | 515 | } | ||
500 | } | 516 | } | ||
517 | } | ||||
501 | 518 | | |||
502 | void TextBlock::debugPrint(int blockIndex) const | 519 | void TextBlock::debugPrint(int blockIndex) const | ||
503 | { | 520 | { | ||
504 | // print all blocks | 521 | // print all blocks | ||
505 | for (int i = 0; i < m_lines.size(); ++i) | 522 | for (int i = 0; i < m_lines.size(); ++i) | ||
506 | printf("%4d - %4d : %4d : '%s'\n", blockIndex, startLine() + i | 523 | printf("%4d - %4d : %4d : '%s'\n", blockIndex, startLine() + i | ||
507 | , m_lines.at(i)->text().size(), qPrintable(m_lines.at(i)->text())); | 524 | , m_lines.at(i)->text().size(), qPrintable(m_lines.at(i)->text())); | ||
508 | } | 525 | } | ||
▲ Show 20 Lines • Show All 217 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.