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