]> git.lyx.org Git - lyx.git/blob - src/DocIterator.cpp
Account for old versions of Pygments
[lyx.git] / src / DocIterator.cpp
1 /**
2  * \file DocIterator.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author André Pönitz
7  * \author Alfredo Braunstein
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12
13 #include <config.h>
14
15 #include "DocIterator.h"
16
17 #include "Buffer.h"
18 #include "InsetList.h"
19 #include "Paragraph.h"
20 #include "LyXRC.h"
21 #include "Text.h"
22
23 #include "mathed/MathData.h"
24 #include "mathed/InsetMath.h"
25 #include "mathed/InsetMathHull.h"
26
27 #include "insets/InsetTabular.h"
28
29 #include "support/convert.h"
30 #include "support/debug.h"
31 #include "support/ExceptionMessage.h"
32 #include "support/gettext.h"
33 #include "support/lassert.h"
34 #include "support/lstrings.h"
35
36 #include <ostream>
37
38 using namespace std;
39 using namespace lyx::support;
40
41 namespace lyx {
42
43
44 DocIterator::DocIterator()
45         : boundary_(false), inset_(0), buffer_(0)
46 {}
47
48
49 // We could be able to get rid of this if only every BufferView were
50 // associated to a buffer on construction.
51 DocIterator::DocIterator(Buffer * buf)
52         : boundary_(false), inset_(0), buffer_(buf)
53 {}
54
55
56 DocIterator::DocIterator(Buffer * buf, Inset * inset)
57         : boundary_(false), inset_(inset), buffer_(buf)
58 {}
59
60
61 DocIterator doc_iterator_begin(const Buffer * buf0, const Inset * inset0)
62 {
63         Buffer * buf = const_cast<Buffer *>(buf0);      
64         Inset * inset = const_cast<Inset *>(inset0);
65         DocIterator dit(buf, inset ? inset : &buf->inset());
66         dit.forwardPos();
67         return dit;
68 }
69
70
71 DocIterator doc_iterator_end(const Buffer * buf0, const Inset * inset0)
72 {
73         Buffer * buf = const_cast<Buffer *>(buf0);      
74         Inset * inset = const_cast<Inset *>(inset0);
75         return DocIterator(buf, inset ? inset : &buf->inset());
76 }
77
78
79 DocIterator DocIterator::clone(Buffer * buffer) const
80 {
81         LASSERT(buffer->isClone(), return DocIterator());
82         Inset * inset = &buffer->inset();
83         DocIterator dit(buffer);
84         size_t const n = slices_.size();
85         for (size_t i = 0 ; i != n; ++i) {
86                 LBUFERR(inset);
87                 dit.push_back(slices_[i]);
88                 dit.top().inset_ = inset;
89                 if (i + 1 != n)
90                         inset = dit.nextInset();
91         }
92         return dit;
93 }
94
95
96 bool DocIterator::inRegexped() const
97 {
98         InsetMath * im = inset().asInsetMath();
99         if (!im) 
100                 return false;
101         InsetMathHull * hull = im->asHullInset();
102         return hull && hull->getType() == hullRegexp;
103 }
104
105
106 LyXErr & operator<<(LyXErr & os, DocIterator const & it)
107 {
108         os.stream() << it;
109         return os;
110 }
111
112
113 Inset * DocIterator::nextInset() const
114 {
115         LASSERT(!empty(), return 0);
116         if (pos() == lastpos())
117                 return 0;
118         if (pos() > lastpos()) {
119                 LYXERR0("Should not happen, but it does: pos() = "
120                         << pos() << ", lastpos() = " << lastpos());
121                 return 0;
122         }
123         if (inMathed())
124                 return nextAtom().nucleus();
125         return paragraph().getInset(pos());
126 }
127
128
129 Inset * DocIterator::prevInset() const
130 {
131         LASSERT(!empty(), return 0);
132         if (pos() == 0)
133                 return 0;
134         if (inMathed()) {
135                 if (cell().empty())
136                         // FIXME: this should not happen but it does.
137                         // See bug 3189
138                         // http://www.lyx.org/trac/ticket/3189
139                         return 0;
140                 else
141                         return prevAtom().nucleus();
142         }
143         return paragraph().getInset(pos() - 1);
144 }
145
146
147 Inset * DocIterator::realInset() const
148 {
149         LASSERT(inTexted(), return 0);
150         // if we are in a tabular, we need the cell
151         if (inset().lyxCode() == TABULAR_CODE) {
152                 InsetTabular * tabular = inset().asInsetTabular();
153                 return tabular->cell(idx()).get();
154         }
155         return &inset();
156 }
157
158
159 MathAtom & DocIterator::prevAtom() const
160 {
161         LASSERT(!empty(), /**/);
162         LASSERT(pos() > 0, /**/);
163         return cell()[pos() - 1];
164 }
165
166
167 MathAtom & DocIterator::nextAtom() const
168 {
169         LASSERT(!empty(), /**/);
170         //lyxerr << "lastpos: " << lastpos() << " next atom:\n" << *this << endl;
171         LASSERT(pos() < lastpos(), /**/);
172         return cell()[pos()];
173 }
174
175
176 Text * DocIterator::text() const
177 {
178         LASSERT(!empty(), return 0);
179         return top().text();
180 }
181
182
183 Paragraph & DocIterator::paragraph() const
184 {
185         if (!inTexted()) {
186                 LYXERR0(*this);
187                 LBUFERR(false);
188         }
189         return top().paragraph();
190 }
191
192
193 Paragraph & DocIterator::innerParagraph() const
194 {
195         LBUFERR(!empty());
196         return innerTextSlice().paragraph();
197 }
198
199
200 FontSpan DocIterator::locateWord(word_location const loc) const
201 {
202         FontSpan f = FontSpan();
203
204         if (!top().text()->empty()) {
205                 f.first = pos();
206                 top().paragraph().locateWord(f.first, f.last, loc);
207         }
208         return f;
209 }
210
211
212 CursorSlice const & DocIterator::innerTextSlice() const
213 {
214         LBUFERR(!empty());
215         // go up until first non-0 text is hit
216         // (innermost text is 0 in mathed)
217         for (int i = depth() - 1; i >= 0; --i)
218                 if (slices_[i].text())
219                         return slices_[i];
220
221         // This case is in principle not possible. We _must_
222         // be inside a Text.
223         LBUFERR(false);
224         // Squash warning
225         static const CursorSlice c;
226         return c;
227 }
228
229
230 docstring DocIterator::paragraphGotoArgument() const
231 {
232         CursorSlice const & s = innerTextSlice();
233         return convert<docstring>(s.paragraph().id()) + ' ' +
234                 convert<docstring>(s.pos());
235 }
236
237
238 DocIterator DocIterator::getInnerText() const
239 {
240         DocIterator texted = *this;
241         while (!texted.inTexted())
242                 texted.pop_back();
243         return texted;
244 }
245
246
247 pit_type DocIterator::lastpit() const
248 {
249         return inMathed() ? 0 : text()->paragraphs().size() - 1;
250 }
251
252
253 pos_type DocIterator::lastpos() const
254 {
255         return inMathed() ? cell().size() : paragraph().size();
256 }
257
258
259 DocIterator::idx_type DocIterator::lastidx() const
260 {
261         return top().lastidx();
262 }
263
264
265 size_t DocIterator::nargs() const
266 {
267         // assume 1x1 grid for main text
268         return top().nargs();
269 }
270
271
272 size_t DocIterator::ncols() const
273 {
274         // assume 1x1 grid for main text
275         return top().ncols();
276 }
277
278
279 size_t DocIterator::nrows() const
280 {
281         // assume 1x1 grid for main text
282         return top().nrows();
283 }
284
285
286 DocIterator::row_type DocIterator::row() const
287 {
288         return top().row();
289 }
290
291
292 DocIterator::col_type DocIterator::col() const
293 {
294         return top().col();
295 }
296
297
298 MathData & DocIterator::cell() const
299 {
300 //      LASSERT(inMathed(), /**/);
301         return top().cell();
302 }
303
304
305 Text * DocIterator::innerText() const
306 {
307         LASSERT(!empty(), return 0);
308         return innerTextSlice().text();
309 }
310
311
312 Inset * DocIterator::innerInsetOfType(int code) const
313 {
314         for (int i = depth() - 1; i >= 0; --i)
315                 if (slices_[i].inset_->lyxCode() == code)
316                         return slices_[i].inset_;
317         return 0;
318 }
319
320
321 bool DocIterator::posBackward()
322 {
323         if (pos() == 0)
324                 return false;
325         --pos();
326         return true;
327 }
328
329
330 bool DocIterator::posForward()
331 {
332         if (pos() == lastpos())
333                 return false;
334         ++pos();
335         return true;
336 }
337
338
339 // This duplicates code above, but is in the critical path.
340 // So please think twice before adding stuff
341 void DocIterator::forwardPos()
342 {
343         // this dog bites his tail
344         if (empty()) {
345                 push_back(CursorSlice(*inset_));
346                 return;
347         }
348
349         CursorSlice & tip = top();
350         //lyxerr << "XXX\n" << *this << endl;
351
352         // not at cell/paragraph end?
353         if (tip.pos() != tip.lastpos()) {
354                 // move into an inset to the right if possible
355                 Inset * n = 0;
356                 if (inMathed())
357                         n = (tip.cell().begin() + tip.pos())->nucleus();
358                 else
359                         n = paragraph().getInset(tip.pos());
360                 if (n && n->isActive()) {
361                         //lyxerr << "... descend" << endl;
362                         push_back(CursorSlice(*n));
363                         return;
364                 }
365         }
366
367         // jump to the next cell/paragraph if possible
368         if (!tip.at_end()) {
369                 tip.forwardPos();
370                 return;
371         }
372
373         // otherwise leave inset and jump over inset as a whole
374         pop_back();
375         // 'tip' is invalid now...
376         if (!empty())
377                 ++top().pos();
378 }
379
380
381 void DocIterator::forwardPosIgnoreCollapsed()
382 {
383         Inset * const nextinset = nextInset();
384         // FIXME: the check for asInsetMath() shouldn't be necessary
385         // but math insets do not return a sensible editable() state yet.
386         if (nextinset && !nextinset->asInsetMath()
387             && !nextinset->editable()) {
388                 ++top().pos();
389                 return;
390         }
391         forwardPos();
392 }
393
394
395 void DocIterator::forwardPar()
396 {
397         forwardPos();
398
399         while (!empty() && (!inTexted() || pos() != 0)) {
400                 if (inTexted()) {
401                         pos_type const lastp = lastpos();
402                         Paragraph const & par = paragraph();
403                         pos_type & pos = top().pos();
404                         if (par.insetList().empty())
405                                 pos = lastp;
406                         else
407                                 while (pos < lastp && !par.isInset(pos))
408                                         ++pos;
409                 }
410                 forwardPos();
411         }
412 }
413
414
415 void DocIterator::forwardChar()
416 {
417         forwardPos();
418         while (!empty() && pos() == lastpos())
419                 forwardPos();
420 }
421
422
423 void DocIterator::forwardInset()
424 {
425         forwardPos();
426
427         while (!empty() && !nextInset()) {
428                 if (inTexted()) {
429                         pos_type const lastp = lastpos();
430                         Paragraph const & par = paragraph();
431                         pos_type & pos = top().pos();
432                         while (pos < lastp && !par.isInset(pos))
433                                 ++pos;
434                         if (pos < lastp)
435                                 break;
436                 }
437                 forwardPos();
438         }
439 }
440
441
442 void DocIterator::backwardChar()
443 {
444         backwardPos();
445         while (!empty() && pos() == lastpos())
446                 backwardPos();
447 }
448
449
450 void DocIterator::backwardPos()
451 {
452         //this dog bites his tail
453         if (empty()) {
454                 push_back(CursorSlice(*inset_));
455                 top().idx() = lastidx();
456                 top().pit() = lastpit();
457                 top().pos() = lastpos();
458                 return;
459         }
460
461         // at inset beginning?
462         if (top().at_begin()) {
463                 pop_back();
464                 return;
465         }
466
467         top().backwardPos();
468
469         // entered another cell/paragraph from the right?
470         if (top().pos() == top().lastpos())
471                 return;
472
473         // move into an inset to the left if possible
474         Inset * n = 0;
475         if (inMathed())
476                 n = (top().cell().begin() + top().pos())->nucleus();
477         else
478                 n = paragraph().getInset(top().pos());
479         if (n && n->isActive()) {
480                 push_back(CursorSlice(*n));
481                 top().idx() = lastidx();
482                 top().pit() = lastpit();
483                 top().pos() = lastpos();
484         }
485 }
486
487
488 #if 0
489 // works, but currently not needed
490 void DocIterator::backwardInset()
491 {
492         backwardPos();
493
494         while (!empty() && !nextInset()) {
495                 if (inTexted()) {
496                         pos_type const lastp = lastpos();
497                         Paragraph const & par = paragraph();
498                         pos_type & pos = top().pos();
499                         while (pos > 0 && (pos == lastp || !par.isInset(pos)))
500                                 --pos;
501                         if (pos > 0)
502                                 break;
503                 }
504                 backwardPos();
505         }
506 }
507 #endif
508
509
510 bool DocIterator::hasPart(DocIterator const & it) const
511 {
512         // it can't be a part if it is larger
513         if (it.depth() > depth())
514                 return false;
515
516         // as inset adresses are the 'last' level
517         return &it.top().inset() == &slices_[it.depth() - 1].inset();
518 }
519
520
521 bool DocIterator::allowSpellCheck() const
522 {
523         /// spell check is disabled if the iterator position
524         /// is inside of an inset which disables the spell checker
525         size_t const n = depth();
526         for (size_t i = 0; i < n; ++i) {
527                 if (!slices_[i].inset_->allowSpellCheck())
528                         return false;
529         }
530         return true;
531 }
532
533
534 void DocIterator::updateInsets(Inset * inset)
535 {
536         // this function re-creates the cache of inset pointers.
537         //lyxerr << "converting:\n" << *this << endl;
538         DocIterator dit = *this;
539         size_t const n = slices_.size();
540         slices_.resize(0);
541         for (size_t i = 0 ; i < n; ++i) {
542                 LBUFERR(inset);
543                 push_back(dit[i]);
544                 top().inset_ = inset;
545                 if (i + 1 != n)
546                         inset = nextInset();
547         }
548         //lyxerr << "converted:\n" << *this << endl;
549 }
550
551
552 bool DocIterator::fixIfBroken()
553 {
554         if (empty())
555                 return false;
556
557         // Go through the slice stack from the bottom. 
558         // Check that all coordinates (idx, pit, pos) are correct and
559         // that the inset is the one which is claimed to be there
560         Inset * inset = &slices_[0].inset();
561         size_t i = 0;
562         size_t n = slices_.size();
563         for (; i != n; ++i) {
564                 CursorSlice & cs = slices_[i];
565                 if (&cs.inset() != inset || cs.nargs() == 0) {
566                         // the whole slice is wrong, chop off this as well
567                         --i;
568                         LYXERR(Debug::DEBUG, "fixIfBroken(): inset changed");
569                         break;
570                 } else if (cs.idx() > cs.lastidx()) {
571                         cs.idx() = cs.lastidx();
572                         cs.pit() = cs.lastpit();
573                         cs.pos() = cs.lastpos();
574                         LYXERR(Debug::DEBUG, "fixIfBroken(): idx fixed");
575                         break;
576                 } else if (cs.pit() > cs.lastpit()) {
577                         cs.pit() = cs.lastpit();
578                         cs.pos() = cs.lastpos();
579                         LYXERR(Debug::DEBUG, "fixIfBroken(): pit fixed");
580                         break;
581                 } else if (cs.pos() > cs.lastpos()) {
582                         cs.pos() = cs.lastpos();
583                         LYXERR(Debug::DEBUG, "fixIfBroken(): pos fixed");
584                         break;
585                 } else if (i != n - 1 && cs.pos() != cs.lastpos()) {
586                         // get inset which is supposed to be in the next slice
587                         if (cs.inset().inMathed())
588                                 inset = (cs.cell().begin() + cs.pos())->nucleus();
589                         else if (Inset * csInset = cs.paragraph().getInset(cs.pos()))
590                                 inset = csInset;
591                         else {
592                                 // there are slices left, so there must be another inset
593                                 break;
594                         }
595                 }
596         }
597
598         // Did we make it through the whole slice stack? Otherwise there
599         // was a problem at slice i, and we have to chop off above
600         if (i < n) {
601                 LYXERR(Debug::DEBUG, "fixIfBroken(): cursor chopped at " << i);
602                 resize(i + 1);
603                 return true;
604         } else
605                 return false;
606 }
607
608
609 void DocIterator::sanitize()
610 {
611         // keep a copy of the slices
612         vector<CursorSlice> const sl = slices_;
613         slices_.clear();
614         if (buffer_)
615                 inset_ = &buffer_->inset();
616         Inset * inset = inset_;
617         // re-add the slices one by one, and adjust the inset pointer.
618         for (size_t i = 0, n = sl.size(); i != n; ++i) {
619                 if (inset == 0) {
620                         // FIXME
621                         LYXERR0(" Should not happen, but does e.g. after "
622                                 "C-n C-l C-z S-C-z\n"
623                                 << " or when a Buffer has been concurrently edited by two views"
624                                 << '\n' << "dit: " << *this << '\n'
625                                 << " lastpos: " << slices_[i].lastpos());
626                         fixIfBroken();
627                         break;
628                 }
629                 if (!inset->isActive()) {
630                         LYXERR0("Inset found on cursor stack is not active.");
631                         fixIfBroken();
632                         break;
633                 }
634                 push_back(sl[i]);
635                 top().inset_ = inset;
636                 if (fixIfBroken())
637                         break;
638                 if (i + 1 != n)
639                         inset = nextInset();
640         }
641 }
642
643
644 int DocIterator::find(MathData const & cell) const
645 {
646         for (size_t l = 0; l != slices_.size(); ++l) {
647                 if (slices_[l].asInsetMath() && &slices_[l].cell() == &cell)
648                         return l;
649         }
650         return -1;
651 }
652
653
654 int DocIterator::find(Inset const * inset) const 
655 {
656         for (size_t l = 0; l != slices_.size(); ++l) {
657                 if (&slices_[l].inset() == inset)
658                         return l;
659         }
660         return -1;
661 }
662
663
664 void DocIterator::cutOff(int above, vector<CursorSlice> & cut)
665 {
666         cut = vector<CursorSlice>(slices_.begin() + above + 1, slices_.end());
667         slices_.resize(above + 1);
668 }
669
670
671 void DocIterator::cutOff(int above)
672 {
673         slices_.resize(above + 1);
674 }
675
676
677 void DocIterator::append(vector<CursorSlice> const & x) 
678 {
679         slices_.insert(slices_.end(), x.begin(), x.end());
680 }
681
682
683 void DocIterator::append(DocIterator::idx_type idx, pos_type pos) 
684 {
685         slices_.push_back(CursorSlice());
686         top().idx() = idx;
687         top().pos() = pos;
688 }
689
690
691 ostream & operator<<(ostream & os, DocIterator const & dit)
692 {
693         for (size_t i = 0, n = dit.depth(); i != n; ++i)
694                 os << " " << dit[i] << "\n";
695         return os;
696 }
697
698
699 ///////////////////////////////////////////////////////
700
701 StableDocIterator::StableDocIterator(DocIterator const & dit) :
702         data_(dit.internalData())
703 {
704         for (size_t i = 0, n = data_.size(); i != n; ++i)
705                 data_[i].inset_ = 0;
706 }
707
708
709 DocIterator StableDocIterator::asDocIterator(Buffer * buf) const
710 {
711         DocIterator dit(buf);
712         dit.slices_ = data_;
713         dit.sanitize();
714         return dit;
715 }
716
717
718 ostream & operator<<(ostream & os, StableDocIterator const & dit)
719 {
720         for (size_t i = 0, n = dit.data_.size(); i != n; ++i)
721                 os << " " << dit.data_[i] << "\n";
722         return os;
723 }
724
725
726 bool operator==(StableDocIterator const & dit1, StableDocIterator const & dit2)
727 {
728         return dit1.data_ == dit2.data_;
729 }
730
731
732 } // namespace lyx