File indexing completed on 2024-04-21 15:55:34

0001 /***********************************************************************************************
0002   Copyright (C) 2004-2012 by Holger Danielsson (holger.danielsson@versanet.de)
0003                 2008-2013 by Michel Ludwig (michel.ludwig@kdemail.net)
0004  ***********************************************************************************************/
0005 
0006 /***************************************************************************
0007  *                                                                         *
0008  *   This program is free software; you can redistribute it and/or modify  *
0009  *   it under the terms of the GNU General Public License as published by  *
0010  *   the Free Software Foundation; either version 2 of the License, or     *
0011  *   (at your option) any later version.                                   *
0012  *                                                                         *
0013  ***************************************************************************/
0014 
0015 #include "editorextension.h"
0016 
0017 #include <QFileInfo>
0018 #include <QClipboard>
0019 
0020 #include <QApplication>
0021 #include <KTextEditor/CodeCompletionInterface>
0022 #include <KTextEditor/Document>
0023 #include <KTextEditor/View>
0024 #include <KTextEditor/Range>
0025 #include <KTextEditor/Cursor>
0026 #include <KLocalizedString>
0027 
0028 
0029 #include "errorhandler.h"
0030 #include "codecompletion.h"
0031 #include "kile.h"
0032 #include "kileactions.h"
0033 #include "kileconfig.h"
0034 #include "kileextensions.h"
0035 #include "kileinfo.h"
0036 #include "kiletool_enums.h"
0037 #include "kileviewmanager.h"
0038 #include "quickpreview.h"
0039 #include "widgets/konsolewidget.h"
0040 
0041 /*
0042  * FIXME: The code in this file should be reworked completely. Once we've got a better parser
0043  *        most of the code in here should also be superfluous.
0044  */
0045 
0046 namespace KileDocument
0047 {
0048 
0049 EditorExtension::EditorExtension(KileInfo *info) : m_ki(info)
0050 {
0051     m_latexCommands = m_ki->latexCommands();
0052 
0053     // init regexp
0054     m_reg.setPattern("(\\\\(begin|end)\\s*\\{([A-Za-z]+\\*?)\\})|(\\\\\\[|\\\\\\])");
0055     //                1    2                 3                   4
0056     m_regexpEnter.setPattern("^(.*)((\\\\begin\\s*\\{([^\\{\\}]*)\\})|(\\\\\\[))");
0057     //                         1   23                 4               5
0058 
0059     // init double quotes
0060     m_quoteListI18N // this is shown in the configuration dialog
0061             << i18n("English quotes:   ``   &apos;&apos;")
0062             << i18n("French quotes:   &quot;&lt;   &quot;&gt;")
0063             << i18n("German quotes:   &quot;`   &quot;&apos;")
0064             << i18n("French quotes (long):   \\flqq   \\frqq")
0065             << i18n("German quotes (long):   \\glqq   \\grqq")
0066             << i18n("Icelandic quotes (v1):   \\ilqq   \\irqq")
0067             << i18n("Icelandic quotes (v2):   \\iflqq   \\ifrqq")
0068             << i18n("Czech quotes:   \\uv{}")
0069             << i18n("csquotes package:   \\enquote{}");
0070 
0071 
0072     m_quoteList
0073             << QPair<QString, QString>("``", "''")
0074             << QPair<QString, QString>("\"<", "\">")
0075             << QPair<QString, QString>("\"`", "\"'")
0076             << QPair<QString, QString>("\\flqq", "\\frqq")
0077             << QPair<QString, QString>("\\glqq", "\\grqq")
0078             << QPair<QString, QString>("\\ilqq", "\\irqq")
0079             << QPair<QString, QString>("\\iflqq", "\\ifrqq")
0080             << QPair<QString, QString>("\\uv{", "}")
0081             << QPair<QString, QString>("\\enquote{", "}");
0082 
0083     readConfig();
0084 }
0085 
0086 EditorExtension::~EditorExtension()
0087 {
0088 }
0089 
0090 //////////////////// read configuration ////////////////////
0091 
0092 void EditorExtension::readConfig()
0093 {
0094     // init insertion of double quotes
0095     initDoubleQuotes();
0096 
0097     // allow special chars?
0098     m_specialCharacters = KileConfig::insertSpecialCharacters();
0099 
0100     // calculate indent for autoindent of environments
0101     m_envAutoIndent.clear();
0102     if(KileConfig::envIndentation()) {
0103         if(KileConfig::envIndentSpaces()) {
0104             int num = KileConfig::envIndentNumSpaces();
0105             if(num < 1 || num > 9) {
0106                 num = 1;
0107             }
0108             m_envAutoIndent.fill(' ', num);
0109         }
0110         else {
0111             m_envAutoIndent = "\t";
0112         }
0113     }
0114 }
0115 
0116 void EditorExtension::insertTag(const KileAction::TagData& data, KTextEditor::View *view)
0117 {
0118     KTextEditor::Document *doc = view->document();
0119     if(!doc) {
0120         return;
0121     }
0122 
0123     //whether or not to wrap tag around selection
0124     bool wrap = !data.tagEnd.isNull() && view->selection();
0125 
0126     //%C before or after the selection
0127     bool before = data.tagBegin.count("%C");
0128     bool after = data.tagEnd.count("%C");
0129 
0130     //save current cursor position
0131     KTextEditor::Cursor cursor = view->cursorPosition();
0132     int para = cursor.line();
0133     int para_begin = para;
0134     int index = cursor.column();
0135     int index_begin = index;
0136     int para_end = 0;
0137     int index_cursor = index;
0138     int para_cursor = index;
0139     // offset for autoindentation of environments
0140     int dxIndentEnv = 0;
0141 
0142     // environment tag
0143     bool envtag = data.tagBegin.count("%E") || data.tagEnd.count("%E");
0144     QString whitespace = getWhiteSpace( doc->line(para).left(index) );
0145 
0146     //if there is a selection act as if cursor is at the beginning of selection
0147     if (wrap) {
0148         KTextEditor::Range selectionRange = view->selectionRange();
0149         index = selectionRange.start().column();
0150         para  = selectionRange.start().line();
0151         para_end = selectionRange.end().line();
0152     }
0153 
0154     QString ins = data.tagBegin;
0155     QString tagEnd = data.tagEnd;
0156 
0157     //start an atomic editing sequence
0158     KTextEditor::Document::EditingTransaction transaction(doc);
0159 
0160     //cut the selected text
0161     QString trailing;
0162     if(wrap) {
0163         QString sel = view->selectionText();
0164         view->removeSelectionText();
0165 
0166         // no autoindentation of environments, when text is selected
0167         if(envtag) {
0168             ins.remove("%E");
0169             tagEnd.remove("%E\n");
0170         }
0171 
0172         // strip one of two consecutive line ends
0173         int len = sel.length();
0174         if(tagEnd.at(0)=='\n' && len > 0 && sel.indexOf('\n',-1) == len - 1) {
0175             sel.truncate( len-1 );
0176         }
0177 
0178         // now add the selection
0179         ins += sel;
0180 
0181         // place the cursor behind this tag, if there is no other wish
0182         if(!before && !after) {
0183             trailing = "%C";
0184             after = true;
0185         }
0186     }
0187     else if(envtag) {
0188         ins.replace("%E",whitespace+m_envAutoIndent);
0189         tagEnd.replace("%E",whitespace+m_envAutoIndent);
0190         if(data.dy > 0) {
0191             dxIndentEnv = whitespace.length() + m_envAutoIndent.length();
0192         }
0193     }
0194 
0195     tagEnd.replace("\\end{",whitespace+"\\end{");
0196     ins += tagEnd + trailing;
0197 
0198     //do some replacements
0199     QFileInfo fi( doc->url().toLocalFile());
0200     ins.replace("%S", fi.completeBaseName());
0201     ins.replace("%B", s_bullet);
0202 
0203     //insert first part of tag at cursor position
0204     doc->insertText(KTextEditor::Cursor(para, index), ins);
0205 
0206     //move cursor to the new position
0207     if(before || after) {
0208         int n = data.tagBegin.count("\n")+ data.tagEnd.count("\n");
0209         if(wrap) {
0210             n += para_end > para ? para_end-para : para-para_end;
0211         }
0212         for (int line = para_begin; line <= para_begin+n; ++line) {
0213             if(doc->line(line).count("%C")) {
0214                 int i=doc->line(line).indexOf("%C");
0215                 para_cursor = line;
0216                 index_cursor = i;
0217                 doc->removeText(KTextEditor::Range(line, i, line, i+2));
0218                 break;
0219             }
0220             index_cursor=index;
0221             para_cursor=line;
0222         }
0223     }
0224     else {
0225         int py = para_begin, px = index_begin;
0226         if(wrap) { //act as if cursor was at beginning of selected text (which is the point where the tagBegin is inserted)
0227             py = para;
0228             px = index;
0229         }
0230         para_cursor = py+data.dy;
0231         index_cursor = px+data.dx+dxIndentEnv;
0232     }
0233 
0234     //end the atomic editing sequence
0235     transaction.finish();
0236 
0237     //set the cursor position (it is important that this is done outside of the atomic editing sequence)
0238     view->setCursorPosition(KTextEditor::Cursor(para_cursor, index_cursor));
0239 
0240     view->removeSelection();
0241 }
0242 
0243 //////////////////// goto environment tag (begin or end) ////////////////////
0244 
0245 // goto the next non-nested environment tag
0246 
0247 KTextEditor::View* EditorExtension::determineView(KTextEditor::View *view)
0248 {
0249     if (!view) {
0250         view = m_ki->viewManager()->currentTextView();
0251     }
0252 
0253     m_overwritemode = (!view) ? false : (view->viewMode() == KTextEditor::View::NormalModeOverwrite);
0254 
0255     return view;
0256 }
0257 
0258 void EditorExtension::gotoEnvironment(bool backwards, KTextEditor::View *view)
0259 {
0260     view = determineView(view);
0261     if(!view) {
0262         return;
0263     }
0264 
0265     uint row,col;
0266     EnvData env;
0267     bool found;
0268 
0269     // get current position
0270     KTextEditor::Document *doc = view->document();
0271     KTextEditor::Cursor cursor = view->cursorPosition();
0272     row = cursor.line();
0273     col = cursor.column();
0274 
0275 
0276     // start searching
0277     if(backwards) {
0278         found = findBeginEnvironment(doc,row,col,env);
0279         //KILE_DEBUG_MAIN << "   goto begin env:  " << env.row << "/" << env.col;
0280 
0281     }
0282     else {
0283         found = findEndEnvironment(doc,row,col,env);
0284         env.col += env.len;
0285     }
0286 
0287     if(found) {
0288         view->setCursorPosition(KTextEditor::Cursor(env.row, env.col));
0289     }
0290 }
0291 
0292 // match the opposite environment tag
0293 
0294 void EditorExtension::matchEnvironment(KTextEditor::View *view)
0295 {
0296     view = determineView(view);
0297     if(!view) {
0298         return;
0299     }
0300 
0301     uint row,col;
0302     EnvData env;
0303 
0304     // get current position
0305     KTextEditor::Document *doc = view->document();
0306     KTextEditor::Cursor cursor = view->cursorPosition();
0307     row = cursor.line();
0308     col = cursor.column();
0309 
0310     // we only start, when we are at an environment tag
0311     if(!isEnvironmentPosition(doc, row, col, env)) {
0312         return;
0313     }
0314 
0315     gotoEnvironment(env.tag != EnvBegin, view);
0316 }
0317 
0318 //////////////////// close opened environments  ////////////////////
0319 
0320 // search for the last opened environment and close it
0321 
0322 void EditorExtension::closeEnvironment(KTextEditor::View *view)
0323 {
0324     view = determineView(view);
0325     if(!view) {
0326         return;
0327     }
0328 
0329     int row, col, currentRow, currentCol;
0330     QString name;
0331 
0332     KTextEditor::Cursor cursor = view->cursorPosition();
0333     currentRow = cursor.line();
0334     currentCol = cursor.column();
0335 
0336     if(findOpenedEnvironment(row, col, name, view)) {
0337         if(name == "\\[") {
0338             view->document()->insertText(KTextEditor::Cursor(currentRow, currentCol), "\\]");
0339         }
0340         else {
0341             view->document()->insertText(KTextEditor::Cursor(currentRow, currentCol), "\\end{" + name + '}');
0342         }
0343 //      view->setCursorPosition(KTextEditor::Cursor(row + 1, 0));
0344     }
0345 }
0346 
0347 // close all opened environments
0348 
0349 void EditorExtension::closeAllEnvironments(KTextEditor::View *view)
0350 {
0351     view = determineView(view);
0352     if(!view) {
0353         return;
0354     }
0355 
0356     QStringList envlist = findOpenedEnvironmentList(view, true);
0357     if(envlist.count() == 0) {
0358         return;
0359     }
0360 
0361     int currentRow, currentCol, outputCol;
0362     KTextEditor::Document *doc = view->document();
0363     KTextEditor::Cursor cursor = view->cursorPosition();
0364     currentRow = cursor.line();
0365     currentCol = cursor.column();
0366 
0367     bool indent = !m_envAutoIndent.isEmpty();
0368     if(indent && currentCol > 0) {
0369         doc->insertText(KTextEditor::Cursor(currentRow, currentCol),"\n");
0370         currentRow++;
0371         currentCol = 0;
0372     }
0373 
0374     bool ok1,ok2;
0375     for(QStringList::Iterator it = envlist.begin(); it != envlist.end(); ++it) {
0376         QStringList entry = (*it).split(',');
0377         if(entry[0] == "document") {
0378             break;
0379         }
0380 
0381         int row = entry[1].toInt(&ok1);
0382         int col = entry[2].toInt(&ok2);
0383         if(!ok1 || !ok2) {
0384             continue;
0385         }
0386 
0387         outputCol = currentCol;
0388         if(indent) {
0389             QString whitespace = getWhiteSpace( doc->line(row).left(col) );
0390             doc->insertText(KTextEditor::Cursor(currentRow, outputCol), whitespace);
0391             outputCol += whitespace.length();
0392         }
0393         QString endtag = ( entry[0] == "\\[" ) ? "\\]\n" : "\\end{"+entry[0]+"}\n";
0394         doc->insertText(KTextEditor::Cursor(currentRow, outputCol), endtag);
0395         ++currentRow;
0396     }
0397 }
0398 
0399 //////////////////// mathgroup ////////////////////
0400 
0401 void EditorExtension::selectMathgroup(KTextEditor::View *view)
0402 {
0403     view = determineView(view);
0404     if(!view) {
0405         return;
0406     }
0407 
0408     KTextEditor::Range range = mathgroupRange(view);
0409     if(range.isValid()) {
0410         view->setSelection(range);
0411     }
0412 }
0413 
0414 void EditorExtension::deleteMathgroup(KTextEditor::View *view)
0415 {
0416     view = determineView(view);
0417     if(!view) {
0418         return;
0419     }
0420 
0421     KTextEditor::Range range = mathgroupRange(view);
0422     if(range.isValid()) {
0423         deleteRange(range,view);
0424     }
0425 }
0426 
0427 bool EditorExtension::hasMathgroup(KTextEditor::View *view)
0428 {
0429     // view will be checked in mathgroupRange()
0430     KTextEditor::Range range = mathgroupRange(view);
0431     return (range.isValid()) ? true : false;
0432 }
0433 
0434 QString EditorExtension::getMathgroupText(KTextEditor::View *view)
0435 {
0436     view = determineView(view);
0437     if(!view) {
0438         return QString();
0439     }
0440 
0441     KTextEditor::Range range = mathgroupRange(view);
0442     return (range.isValid()) ? view->document()->text(range) : QString();
0443 }
0444 
0445 QString EditorExtension::getMathgroupText(uint &row, uint &col, KTextEditor::View *view)
0446 {
0447     int row1, col1, row2, col2;
0448 
0449     view = determineView(view);
0450     if(view && getMathgroup(view, row1, col1, row2, col2)) {
0451         row = row1;
0452         col = col1;
0453         return view->document()->text(KTextEditor::Range(row1, col1, row2, col2));
0454     }
0455     else {
0456         return QString();
0457     }
0458 }
0459 
0460 KTextEditor::Range  EditorExtension::mathgroupRange(KTextEditor::View *view)
0461 {
0462     view = determineView(view);
0463     if(!view) {
0464         return KTextEditor::Range::invalid();
0465     }
0466 
0467     int row1, col1, row2, col2;
0468     if(getMathgroup(view, row1, col1, row2, col2)) {
0469         return KTextEditor::Range(row1, col1, row2, col2);
0470     }
0471     else {
0472         return KTextEditor::Range::invalid();
0473     }
0474 }
0475 
0476 bool EditorExtension::getMathgroup(KTextEditor::View *view, int &row1, int &col1, int &row2, int &col2)
0477 {
0478     int row, col, r, c;
0479     MathData begin, end;
0480 
0481     KTextEditor::Document *doc = view->document();
0482     KTextEditor::Cursor cursor = view->cursorPosition();
0483     row = cursor.line();
0484     col = cursor.column();
0485 
0486     QString textline = getTextLineReal(doc,row);
0487 
0488     // check for '\ensuremath{...}'
0489     QString currentWord;
0490     int x1, x2;
0491     if(getCurrentWord(doc, row, col, smTex, currentWord, x1, x2) && currentWord == "\\ensuremath") {
0492         view->setCursorPosition(KTextEditor::Cursor(row, x2));
0493     }
0494 
0495     BracketData open,close;
0496     if(getTexgroup(false, open, close, view)) {
0497         QString s = getTextLineReal(doc,open.row);
0498         if(open.col >= 11 && s.mid(open.col - 11, 11) == "\\ensuremath") {
0499             view->setCursorPosition(KTextEditor::Cursor(row, col));
0500             row1 = open.row;
0501             col1 = open.col-11;
0502             row2 = close.row;
0503             col2 = close.col;
0504             return true;
0505         }
0506     }
0507 
0508     // do we need to restore the cursor position
0509     view->setCursorPosition(KTextEditor::Cursor(row, col));
0510 
0511     // '$' is difficult, because it is used as opening and closing tag
0512     int mode = 0;
0513     if(textline[col] == '$') {
0514         mode = 1;
0515     }
0516     else if(col > 0 && textline[col - 1] == '$') {
0517         mode = 2;
0518     }
0519 
0520     if(mode > 0) {
0521         // first look, if this is a closing '$'
0522         r = row;
0523         c = (mode == 1) ? col : col - 1;
0524         if(decreaseCursorPosition(doc, r, c) && findOpenMathTag(doc, r, c, begin)) {
0525             if(begin.tag == mmMathDollar && (begin.numdollar & 1)) {
0526                 row1 = begin.row;
0527                 col1 = begin.col;
0528                 row2 = row;
0529                 col2 = (mode == 1) ? col + 1 : col;
0530                 return true;
0531             }
0532         }
0533 
0534         // perhaps an opening '$'
0535         r = row;
0536         c = (mode == 1) ? col+1 : col;
0537         if(findCloseMathTag(doc, r, c, end)) {
0538             if(end.tag == mmMathDollar) {
0539                 row1 = row;
0540                 col1 = ( mode == 1 ) ? col : col-1;
0541                 row2 = end.row;
0542                 col2 = end.col + end.len;
0543                 return true;
0544             }
0545         }
0546 
0547         // found no mathgroup with '$'
0548         return false;
0549     }
0550 
0551     // now let's search for all other math tags:
0552     // if a mathgroup tag starts in the current column, we save this
0553     // position and move the cursor one column to the right
0554     bool openingtag = isOpeningMathTagPosition(doc, row, col, begin);
0555     if(openingtag) {
0556         // try to find the corresponding closing tag at the right
0557         bool closetag = findCloseMathTag(doc, row, col + 1, end);
0558         if(closetag && checkMathtags(begin, end)) {
0559             row1 = begin.row;
0560             col1 = begin.col;
0561             row2 = end.row;
0562             col2 = end.col + end.len;
0563             return true;
0564         }
0565     }
0566 
0567     r = row;
0568     c = col;
0569     bool closingtag = isClosingMathTagPosition(doc, row, col, end);
0570     if(closingtag) {
0571         c = end.col;
0572         if(!decreaseCursorPosition(doc, r, c)) {
0573             return false;
0574         }
0575     }
0576 
0577     // now try to search to opening tag of the math group
0578     if(!findOpenMathTag(doc, r, c, begin)) {
0579         return false;
0580     }
0581 
0582     if(begin.tag == mmMathDollar && !(begin.numdollar & 1)) {
0583         //KILE_DEBUG_MAIN << "error: even number of '$' --> no math mode" ;
0584         return false;
0585     }
0586 
0587     // and now the closing tag
0588     if(!findCloseMathTag(doc, r, c, end)) {
0589         return false;
0590     }
0591 
0592     // both tags were found, but they must be of the same type
0593     if(checkMathtags(begin, end)) {
0594         row1 = begin.row;
0595         col1 = begin.col;
0596         row2 = end.row;
0597         col2 = end.col + end.len;
0598         return true;
0599     }
0600     else {
0601         return false;
0602     }
0603 }
0604 
0605 //////////////////// mathgroup tags ////////////////////
0606 
0607 bool EditorExtension::checkMathtags(const MathData &begin,const MathData &end)
0608 {
0609     // both tags were found, but they must be of the same type
0610     if(begin.tag != end.tag) {
0611         //KILE_DEBUG_MAIN << "error: opening and closing tag of mathmode don't match: " << begin.tag << " - " << end.tag;
0612         return false;
0613     }
0614 
0615     // and additionally: if it is a math env, both tags must have the same name
0616     if(begin.tag == mmDisplaymathEnv && begin.envname != end.envname) {
0617         //KILE_DEBUG_MAIN << "error: opening and closing env tags have different names: " << begin.envname << " - " << end.envname;
0618         return false;
0619     }
0620 
0621     return true;
0622 }
0623 
0624 bool EditorExtension::isOpeningMathTagPosition(KTextEditor::Document *doc, uint row, uint col, MathData &mathdata)
0625 {
0626     QString textline = getTextLineReal(doc,row);
0627 
0628     QRegExp reg("\\\\begin\\s*\\{([A-Za-z]+\\*?)\\}|\\\\\\[|\\\\\\(");
0629     if((int)col != reg.indexIn(textline, col)) {
0630         return false;
0631     }
0632 
0633     QChar id = reg.cap(0)[1];
0634     QString envname = reg.cap(1);
0635 
0636     mathdata.row = row;
0637     mathdata.col = col;
0638     mathdata.len = reg.cap(0).length();
0639 
0640     switch(id.unicode()) {
0641     case 'b':
0642         if(!(m_latexCommands->isMathEnv(envname) || envname=="math") || m_latexCommands->needsMathMode(envname)) {
0643             return false;
0644         }
0645         mathdata.tag = ( envname=="math" ) ? mmMathEnv : mmDisplaymathEnv;
0646         mathdata.envname = envname;
0647         break;
0648     case '[':
0649         mathdata.tag = mmDisplaymathParen;
0650         break;
0651     case '(':
0652         mathdata.tag = mmMathParen;
0653         break;
0654     }
0655 
0656     return true;
0657 }
0658 
0659 bool EditorExtension::isClosingMathTagPosition(KTextEditor::Document *doc, uint row, uint col,MathData &mathdata)
0660 {
0661     QString textline = doc->line(row);
0662 
0663     QRegExp reg("\\\\end\\s*\\{([A-Za-z]+\\*?)\\}|\\\\\\]|\\\\\\)");
0664     int pos = reg.lastIndexIn(textline, col);
0665     if(pos < 0 || (int)col > pos + reg.matchedLength()) {
0666         return false;
0667     }
0668 
0669     QChar id = reg.cap(0)[1];
0670     QString envname = reg.cap(1);
0671 
0672     mathdata.row = row;
0673     mathdata.col = pos;
0674     mathdata.len = reg.cap(0).length();
0675 
0676     switch(id.unicode()) {
0677     case 'e':
0678         if(!(m_latexCommands->isMathEnv(envname) || envname=="math") || m_latexCommands->needsMathMode(envname)) {
0679             return false;
0680         }
0681         mathdata.tag = ( envname=="math" ) ? mmMathEnv : mmDisplaymathEnv;
0682         mathdata.envname = envname;
0683         break;
0684     case ']':
0685         mathdata.tag = mmDisplaymathParen;
0686         break;
0687     case ')':
0688         mathdata.tag = mmMathParen;
0689         break;
0690     }
0691 
0692     return true;
0693 }
0694 
0695 bool EditorExtension::findOpenMathTag(KTextEditor::Document *doc, int row, int col, MathData &mathdata)
0696 {
0697     const QString regExpString = "\\$"
0698                                  "|\\\\begin\\s*\\{([A-Za-z]+\\*?)\\}"
0699                                  "|\\\\end\\s*\\{([A-Za-z]+\\*?)\\}"
0700                                  "|\\\\\\[|\\\\\\]"
0701                                  "|\\\\\\(|\\\\\\)";
0702 
0703     QRegExp reg(regExpString);
0704     int lastrow = -1, lastcol = -1;
0705     QString mathname;
0706 
0707     bool foundDollar = false;
0708     uint numDollar = 0;
0709 
0710     QString textline = getTextLineReal(doc, row);
0711     int column = col;
0712 
0713     bool continueSearch = true;
0714     while(continueSearch) {
0715         while((column = reg.lastIndexIn(textline, col)) != -1) {
0716             col = column;
0717 
0718             mathdata.row = row;
0719             mathdata.col = col;
0720             mathdata.len = reg.cap(0).length();
0721             mathname = reg.cap(0).left(2);
0722 
0723             // should be better called 'isValidChar()', because it checks for comments
0724             // and escaped chars like backslash and dollar in '\\' and '\$'
0725             if(mathname == "$") {
0726                 // count and continue search
0727                 ++numDollar;
0728 
0729                 // but remember the first dollar found backwards
0730                 if(!foundDollar) {
0731                     lastrow = row;
0732                     lastcol = col;
0733                     foundDollar = true;
0734                 }
0735             }
0736             else if(mathname=="\\[" || mathname=="\\(") {
0737                 // found start of mathmode
0738                 if(numDollar == 0) {
0739                     mathdata.tag = ( mathname == "\\[" ) ? mmDisplaymathParen : mmMathParen;
0740                     mathdata.numdollar = 0;
0741                     return true;
0742                 }
0743                 else {
0744                     //KILE_DEBUG_MAIN << "error: dollar not allowed in \\[ or \\( mode";
0745                     return false;
0746                 }
0747             }
0748             else if(mathname=="\\]" || mathname=="\\)") {
0749                 continueSearch = false;
0750                 break;
0751             }
0752             else  if(mathname=="\\b") {
0753                 // save name of environment
0754                 QString envname = reg.cap(1);
0755 
0756                 // if we found the opening tag of a math env
0757                 if(m_latexCommands->isMathEnv(envname) || envname=="math") {
0758                     if(numDollar > 0) {
0759                         //KILE_DEBUG_MAIN << "error: dollar not allowed in math env   numdollar=" << numDollar;
0760                         return false;
0761                     }
0762 
0763                     // if this is a math env with its own mathmode, we have found the starting position
0764                     if(envname == "math") {
0765                         mathdata.tag = mmMathEnv;
0766                         mathdata.envname = envname;
0767                         return true;
0768                     }
0769 
0770                     if(!m_latexCommands->needsMathMode(envname)) {
0771                         mathdata.tag = mmDisplaymathEnv;
0772                         mathdata.envname = envname;
0773                         return true;
0774                     }
0775                 }
0776                 // no math env, we found the opening tag of a normal env
0777                 else {
0778                     continueSearch = false;
0779                     break;
0780                 }
0781             }
0782             else if(mathname == "\\e") {
0783                 QString envname = reg.cap(2);
0784 
0785                 // if we found the closing tag of a math env
0786                 if(m_latexCommands->isMathEnv(envname) || envname == "math") {
0787                     // if this is a math env with its own mathmode
0788                     if(!m_latexCommands->needsMathMode(envname) || envname == "math") {
0789                         continueSearch = false;
0790                         break;
0791                     }
0792 
0793                     // if this is a math env which needs $..$
0794                     if(m_latexCommands->isMathModeEnv(envname)) {
0795                         if(numDollar >= 1) {
0796                             --numDollar;
0797                             continueSearch = false;
0798                             break;
0799                         }
0800                         // else continue search
0801                     }
0802                 }
0803                 // if we found the closing tag of a normal env
0804                 else {
0805                     continueSearch = false;
0806                     break;
0807                 }
0808             }
0809             else {
0810                 //KILE_DEBUG_MAIN << "error: unknown match";
0811                 return false;
0812             }
0813 
0814             // continue search one column left of the last match (if this is possible)
0815             if(col == 0) {
0816                 break;
0817             }
0818 
0819             --col;
0820         }
0821 
0822         if(row > 0) {
0823             textline = getTextLineReal(doc,--row);
0824             col = textline.length();
0825         }
0826         else if(column == -1) {
0827             break;
0828         }
0829     }
0830 
0831     // nothing else found, so math mode starts a the last dollar (the first one found backwards)
0832     mathdata.row = lastrow;
0833     mathdata.col = lastcol;
0834     mathdata.len = 1;
0835     mathdata.numdollar = numDollar;
0836 
0837     mathdata.tag = (numDollar > 0) ? mmMathDollar : mmNoMathMode;
0838 
0839     return true;
0840 }
0841 
0842 bool EditorExtension::findCloseMathTag(KTextEditor::Document *doc, int row, int col, MathData &mathdata)
0843 {
0844     const QString regExpString = "\\$"
0845                                  "|\\\\begin\\s*\\{([A-Za-z]+\\*?)\\}"
0846                                  "|\\\\end\\s*\\{([A-Za-z]+\\*?)\\}"
0847                                  "|\\\\\\[|\\\\\\]"
0848                                  "|\\\\\\(|\\\\\\)";
0849 
0850 // +       int rowFound, colFound;
0851 // +       QRegExp reg(regExpString);
0852 // +       reg.setCaseSensitivity(Qt::CaseInsensitive);
0853 // +       int lastMatch = 0;
0854 
0855     QRegExp reg(regExpString);
0856 
0857     KTextEditor::Range searchRange = KTextEditor::Range(KTextEditor::Cursor(row, col), doc->documentEnd());
0858 
0859     while(true) {
0860         QVector<KTextEditor::Range> foundRanges = doc->searchText(searchRange, regExpString, KTextEditor::Regex | KTextEditor::CaseInsensitive);
0861         if(foundRanges.isEmpty() || (foundRanges.size() == 1 && !foundRanges.first().isValid())) {
0862             break;
0863         }
0864 
0865         //KILE_DEBUG_MAIN << "number of ranges " << foundRanges.count();
0866         if(foundRanges.size() < 3) {
0867             break;
0868         }
0869 
0870         KTextEditor::Range range = foundRanges.first();
0871         //KILE_DEBUG_MAIN << "found math tag: " << doc->text(range);
0872         if(!range.isValid()) {
0873             break;
0874         }
0875 
0876         int rowFound = range.start().line();
0877         int colFound = range.start().column();
0878         QString textFound = doc->text(range);
0879 
0880         // should be better called 'isValidChar()', because it checks for comments
0881         // and escaped chars like backslash and dollar in '\\' and '\$'
0882         if(isValidBackslash(doc, rowFound, colFound)) {
0883             QString mathname = textFound.left(2);
0884 
0885             // always remember behind the last match
0886             mathdata.row = rowFound;
0887             mathdata.col = colFound;
0888             mathdata.len = textFound.length();
0889 
0890             if(mathname=="$") {
0891                 mathdata.tag = mmMathDollar;
0892                 return true;
0893             }
0894             else if(mathname=="\\]") {
0895                 mathdata.tag = mmDisplaymathParen;
0896                 return true;
0897             }
0898             else if(mathname=="\\)") {
0899                 mathdata.tag = mmMathParen;
0900                 return true;
0901             }
0902             else if(mathname=="\\[" || mathname=="\\(") {
0903                 //KILE_DEBUG_MAIN << "error: current mathgroup was not closed";
0904                 return false;
0905             }
0906             else if(mathname=="\\b") {
0907                 QString envname = doc->text(foundRanges[1]);
0908                 if(!(m_latexCommands->isMathEnv(envname) || envname=="math")) {
0909                     //KILE_DEBUG_MAIN << "error: only math env are allowed in mathmode (found begin tag)";
0910                     return false;
0911                 }
0912 
0913                 if(!m_latexCommands->needsMathMode(envname) || envname=="math") {
0914                     //KILE_DEBUG_MAIN << "error: mathenv with its own mathmode are not allowed in mathmode ";
0915                     return false;
0916                 }
0917 
0918                 // else continue search
0919             }
0920             else if(mathname == "\\e") {
0921                 QString envname = doc->text(foundRanges[2]);
0922                 if(!(m_latexCommands->isMathEnv(envname) || envname=="math")) {
0923                     //KILE_DEBUG_MAIN << "error: only math env are allowed in mathmode (found end tag)";
0924                     return false;
0925                 }
0926 
0927                 if(envname == "math") {
0928                     mathdata.tag = mmMathEnv;
0929                     mathdata.envname = envname;
0930                     return true;
0931                 }
0932 
0933                 if(!m_latexCommands->needsMathMode(envname)) {
0934                     mathdata.tag = mmDisplaymathEnv;
0935                     mathdata.envname = envname;
0936                     return true;
0937                 }
0938 
0939                 // else continue search
0940             }
0941         }
0942 
0943         // go ahead
0944         searchRange = KTextEditor::Range(foundRanges.first().end(), doc->documentEnd());
0945     }
0946 
0947     //KILE_DEBUG_MAIN << "not found anything";
0948     return false;
0949 }
0950 
0951 
0952 
0953 //////////////////// insert newlines inside an environment ////////////////////
0954 
0955 // intelligent newlines: look for the last opened environment
0956 // and decide what to insert
0957 // or continue the comment
0958 
0959 void EditorExtension::insertIntelligentNewline(KTextEditor::View *view)
0960 {
0961     KILE_DEBUG_MAIN << view;
0962 
0963     view = determineView(view);
0964 
0965     if(!view) {
0966         return;
0967     }
0968 
0969     KTextEditor::Document* doc = view->document();
0970 
0971     if(!doc) {
0972         return;
0973     }
0974 
0975     QString name;
0976     KTextEditor::Cursor cursor = view->cursorPosition();
0977     int row = cursor.line();
0978     int col = cursor.column();
0979 
0980     QString newLineAndIndentationString = '\n' + extractIndentationString(view, row);
0981 
0982     if(isCommentPosition(doc, row, col)) {
0983         KILE_DEBUG_MAIN << "found comment";
0984         view->insertText(newLineAndIndentationString + "% ");
0985         moveCursorToLastPositionInCurrentLine(view);
0986         return;
0987     }
0988     else if(findOpenedEnvironment(row, col, name, view)) {
0989         if(m_latexCommands->isListEnv(name)) {
0990             if ( name == "description" ) {
0991                 view->insertText(newLineAndIndentationString + "\\item[]");
0992             }
0993             else {
0994                 view->insertText(newLineAndIndentationString + "\\item ");
0995             }
0996             moveCursorToLastPositionInCurrentLine(view);
0997             return;
0998         }
0999         else if(m_latexCommands->isTabularEnv(name) || m_latexCommands->isMathEnv(name)) {
1000             view->insertText(newLineAndIndentationString + "\\\\");
1001             moveCursorToLastPositionInCurrentLine(view);
1002             return;
1003         }
1004     }
1005     // - no comment position
1006     // - found no opened environment
1007     // - unknown environment
1008     // - finish tabular or math environment
1009     view->insertText(newLineAndIndentationString);
1010     moveCursorToLastPositionInCurrentLine(view);
1011 }
1012 
1013 bool EditorExtension::findOpenedEnvironment(int &row, int &col, QString &envname, KTextEditor::View *view)
1014 {
1015     view = determineView(view);
1016     if(!view) {
1017         return false;
1018     }
1019 
1020     // get current cursor position
1021     KTextEditor::Document *doc = view->document();
1022     KTextEditor::Cursor cursor = view->cursorPosition();
1023     row = cursor.line();
1024     col = cursor.column();
1025 
1026     EnvData env;
1027     int startrow = row;
1028     int startcol = col;
1029 
1030     //KILE_DEBUG_MAIN << "   close - start ";
1031     // accept a starting place outside an environment
1032     bool env_position = isEnvironmentPosition(doc, row, col, env);
1033 
1034     // We can also accept a column, if we are on the left side of an environment.
1035     // But we should decrease the current cursor position for the search.
1036     if(env_position && env.cpos != EnvInside) {
1037         if(env.cpos == EnvLeft && !decreaseCursorPosition(doc, startrow, startcol)) {
1038             return false;
1039         }
1040         env_position = false;
1041     }
1042 
1043     if(!env_position && findEnvironmentTag(doc, startrow, startcol, env, true)) {
1044         //KILE_DEBUG_MAIN << "   close - found begin env at:  " << env.row << "/" << env.col << " " << env.name;
1045         row = env.row;
1046         col = env.col;
1047         envname = env.name;
1048         return true;
1049     }
1050     else {
1051         return false;
1052     }
1053 }
1054 
1055 QStringList EditorExtension::findOpenedEnvironmentList(KTextEditor::View *view, bool position)
1056 {
1057     QStringList envlist;
1058 
1059     view = determineView(view);
1060     if(view) {
1061         int currentRow, currentCol;
1062         KTextEditor::Document *doc = view->document();
1063         KTextEditor::Cursor cursor = view->cursorPosition();
1064         currentRow = cursor.line();
1065         currentCol = cursor.column();
1066 
1067 
1068         int row = currentRow;
1069         int col = currentCol;
1070         EnvData env;
1071 
1072         // check the starting position
1073         bool env_position = isEnvironmentPosition(doc, row, col, env);
1074         if(env_position) {
1075             // we are inside an environment tag: bad to complete
1076             if(env.cpos == EnvInside) {
1077                 return envlist;
1078             }
1079             // we are left of an environment tag: go one position to the left
1080             if(env.cpos == EnvLeft) {
1081                 if (!decreaseCursorPosition(doc, row, col)) {
1082                     return envlist;
1083                 }
1084             }
1085         }
1086 
1087         while (findEnvironmentTag(doc, row, col, env, true)) {
1088             row = env.row;
1089             col = env.col;
1090 
1091             if(position) {
1092                 envlist << env.name + QString(",%1,%2").arg(row).arg(col);
1093             }
1094             else {
1095                 envlist << env.name;
1096             }
1097 
1098             if(col == 0) {
1099                 if (!decreaseCursorPosition(doc, row, col)) {
1100                     break;
1101                 }
1102             }
1103             view->setCursorPosition(KTextEditor::Cursor(row, col));
1104         }
1105 
1106         // reset cursor original position
1107         view->setCursorPosition(KTextEditor::Cursor(currentRow, currentCol));
1108     }
1109 
1110     return envlist;
1111 }
1112 
1113 //////////////////// select an environment  ////////////////////
1114 
1115 void EditorExtension::selectEnvironment(bool inside, KTextEditor::View *view)
1116 {
1117     view = determineView(view);
1118     if(!view) {
1119         return;
1120     }
1121 
1122     if (!view->selection() || !expandSelectionEnvironment(inside,view)) {
1123         KTextEditor::Range range = environmentRange(inside,view);
1124         if(range.isValid()) {
1125             view->setSelection(range);
1126         }
1127     }
1128 }
1129 
1130 void EditorExtension::deleteEnvironment(bool inside, KTextEditor::View *view)
1131 {
1132     view = determineView(view);
1133     if(!view) {
1134         return;
1135     }
1136 
1137     KTextEditor::Range range = environmentRange(inside,view);
1138     if(range.isValid()) {
1139         deleteRange(range,view);
1140     }
1141 }
1142 
1143 void EditorExtension::deleteRange(KTextEditor::Range &range, KTextEditor::View *view)
1144 {
1145     view->removeSelection();
1146     view->document()->removeText(range);
1147     view->setCursorPosition(range.start());
1148 }
1149 
1150 // calculate start and end of an environment
1151 
1152 bool EditorExtension::getEnvironment(bool inside, EnvData &envbegin, EnvData &envend, KTextEditor::View *view)
1153 {
1154     view = determineView(view);
1155     if(!view) {
1156         return false;
1157     }
1158 
1159     int row, col;
1160 
1161     KTextEditor::Document *doc = view->document();
1162     KTextEditor::Cursor cursor = view->cursorPosition();
1163     row = cursor.line();
1164     col = cursor.column();
1165     if(!findBeginEnvironment(doc, row, col, envbegin)) {
1166         return false;
1167     }
1168     if(!findEndEnvironment(doc, row, col, envend)) {
1169         return false;
1170     }
1171 
1172     if(inside) {
1173         envbegin.col += envbegin.len;
1174     }
1175     else {
1176         envend.col += envend.len;
1177     }
1178 
1179     return true;
1180 }
1181 
1182 KTextEditor::Range EditorExtension::environmentRange(bool inside, KTextEditor::View *view)
1183 {
1184     // view will be checked in getEnvironment()
1185     EnvData envbegin, envend;
1186     return (getEnvironment(inside, envbegin, envend, view))
1187            ? KTextEditor::Range(envbegin.row, envbegin.col, envend.row, envend.col)
1188            : KTextEditor::Range::invalid();
1189 }
1190 
1191 QString EditorExtension::environmentText(bool inside, KTextEditor::View *view)
1192 {
1193     view = determineView(view);
1194     if(!view) {
1195         return QString();
1196     }
1197 
1198     KTextEditor::Range range = environmentRange(inside,view);
1199     return (range.isValid()) ? view->document()->text(range) : QString();
1200 }
1201 
1202 QString EditorExtension::environmentName(KTextEditor::View *view)
1203 {
1204     // view will be checked in getEnvironment()
1205     EnvData envbegin, envend;
1206     return (getEnvironment(false, envbegin, envend, view)) ? envbegin.name : QString();
1207 }
1208 
1209 // determine text, startrow and startcol of current environment
1210 
1211 QString EditorExtension::getEnvironmentText(int &row, int &col, QString &name, KTextEditor::View *view)
1212 {
1213     view = determineView(view);
1214     if(!view) {
1215         return QString();
1216     }
1217 
1218     EnvData envbegin, envend;
1219 
1220     if(getEnvironment(false, envbegin, envend, view) && envbegin.name != "document") {
1221         row = envbegin.row;
1222         col = envbegin.col;
1223         name = envbegin.name;
1224         return view->document()->text(KTextEditor::Range(envbegin.row, envbegin.col, envend.row, envend.col));
1225     }
1226     else {
1227         return QString();
1228     }
1229 }
1230 
1231 bool EditorExtension::hasEnvironment(KTextEditor::View *view)
1232 {
1233     view = determineView(view);
1234     if(!view) {
1235         return false;
1236     }
1237 
1238     EnvData envbegin,envend;
1239     return (getEnvironment(false, envbegin, envend, view) && envbegin.name != "document");
1240 }
1241 
1242 // when an environment is selected (inside or outside),
1243 // the selection is expanded to the surrounding environment
1244 
1245 bool EditorExtension::expandSelectionEnvironment(bool inside, KTextEditor::View *view)
1246 {
1247     KTextEditor::Document *doc = view->document();
1248     if (!view->selection()) {
1249         return false;
1250     }
1251 
1252     // get current position
1253     int row, col;
1254     KTextEditor::Cursor cursor = view->cursorPosition();
1255     row = cursor.line();
1256     col = cursor.column();
1257 
1258     // get current selection
1259     KTextEditor::Range selectionRange = view->selectionRange();
1260     int row1 = selectionRange.start().line();
1261     int col1 = selectionRange.start().column();
1262     int row2 = selectionRange.end().line();
1263     int col2 = selectionRange.end().column();
1264 
1265     // determine current environment outside
1266     EnvData oenvbegin,oenvend;
1267     if(!getEnvironment(false, oenvbegin, oenvend, view)) {
1268         return false;
1269     }
1270 
1271     bool newselection = false;
1272     // first look, if this environment is selected outside
1273     if(row1 == oenvbegin.row && col1 == oenvbegin.col && row2 == oenvend.row && col2 == oenvend.col) {
1274 
1275         if(!decreaseCursorPosition(doc, oenvbegin.row, oenvbegin.col) ) {
1276             return newselection;
1277         }
1278         view->setCursorPosition(KTextEditor::Cursor(oenvbegin.row, oenvbegin.col));
1279         // search the surrounding environment and select it
1280         if(getEnvironment(inside, oenvbegin, oenvend, view)) {
1281             view->setSelection(KTextEditor::Range(oenvbegin.row, oenvbegin.col, oenvend.row, oenvend.col));
1282             newselection = true;
1283 
1284         }
1285     }
1286     else {
1287         // then determine current environment inside
1288         EnvData ienvbegin, ienvend;
1289         getEnvironment(true, ienvbegin, ienvend, view);
1290         // and look, if this environment is selected inside
1291         if(row1 == ienvbegin.row && col1 == ienvbegin.col && row2 == ienvend.row && col2 == ienvend.col) {
1292             if(!decreaseCursorPosition(doc, oenvbegin.row, oenvbegin.col) ) {
1293                 return newselection;
1294             }
1295             view->setCursorPosition(KTextEditor::Cursor(oenvbegin.row, oenvbegin.col));
1296             // search the surrounding environment and select it
1297             if(getEnvironment(inside, ienvbegin, ienvend, view)) {
1298                 view->setSelection(KTextEditor::Range(ienvbegin.row, ienvbegin.col, ienvend.row, ienvend.col));
1299                 newselection = true;
1300             }
1301 
1302         }
1303     }
1304 
1305     // restore old cursor position
1306     view->setCursorPosition(KTextEditor::Cursor(row, col));
1307     return newselection;
1308 }
1309 
1310 //////////////////// search for \begin{env}  ////////////////////
1311 
1312 // Find the last \begin{env} tag. If the current cursor is over
1313 //  - \begin{env} tag: we will stop immediately
1314 //  - \end{env} tag: we will start before this tag
1315 
1316 bool EditorExtension::findBeginEnvironment(KTextEditor::Document *doc, int row, int col, EnvData &env)
1317 {
1318     // KILE_DEBUG_MAIN << "   find begin:  ";
1319     if(isEnvironmentPosition(doc, row, col, env)) {
1320         // already found position?
1321         //KILE_DEBUG_MAIN << "   found env at:  " << env.row << "/" << env.col << " " << env.name;
1322         if(env.tag == EnvBegin) {
1323             //KILE_DEBUG_MAIN << "   is begin env at:  " << env.row << "/" << env.col << " " << env.name;
1324             return true;
1325         }
1326 
1327         // go one position back
1328         //KILE_DEBUG_MAIN << "   is end env at:  " << env.row << "/" << env.col << " " << env.name;
1329         row = env.row;
1330         col = env.col;
1331         if(!decreaseCursorPosition(doc, row, col)) {
1332             return false;
1333         }
1334     }
1335 
1336     // looking back for last environment
1337     //KILE_DEBUG_MAIN << "   looking back from pos:  " << row << "/" << col << " " << env.name;
1338     return findEnvironmentTag(doc, row, col, env, true);
1339 }
1340 
1341 //////////////////// search for \end{env}  ////////////////////
1342 
1343 // Find the last \end{env} tag. If the current cursor is over
1344 //  - \end{env} tag: we will stop immediately
1345 //  - \begin{env} tag: we will start behind this tag
1346 
1347 bool EditorExtension::findEndEnvironment(KTextEditor::Document *doc, int row, int col, EnvData &env)
1348 {
1349     if(isEnvironmentPosition(doc, row, col, env)) {
1350         // already found position?
1351         if(env.tag == EnvEnd ) {
1352             return true;
1353         }
1354 
1355         // go one position forward
1356         row = env.row;
1357         col = env.col + 1;
1358     }
1359 
1360     // looking forward for the next environment
1361     return findEnvironmentTag(doc, row, col, env, false);
1362 }
1363 
1364 //////////////////// search for an environment tag ////////////////////
1365 // find the last/next non-nested environment tag
1366 bool EditorExtension::findEnvironmentTag(KTextEditor::Document *doc, int row, int col, EnvData &env, bool backwards)
1367 {
1368     unsigned int envcount = 0;
1369 
1370     KTextEditor::Range searchRange;
1371     if(backwards) {
1372         searchRange = KTextEditor::Range(KTextEditor::Cursor(0, 0), KTextEditor::Cursor(row, col));
1373     }
1374     else {
1375         searchRange = KTextEditor::Range(KTextEditor::Cursor(row, col), doc->documentEnd());
1376     }
1377 
1378     KTextEditor::SearchOptions searchOptions = (backwards) ? KTextEditor::Regex | KTextEditor::Backwards : KTextEditor::Regex;
1379 
1380     while(true) {
1381         QVector<KTextEditor::Range> foundRanges = doc->searchText(searchRange, m_reg.pattern(), searchOptions);
1382         if(foundRanges.isEmpty() || (foundRanges.size() == 1 && !foundRanges.first().isValid())) {
1383             break;
1384         }
1385 
1386         //KILE_DEBUG_MAIN << "number of ranges " << foundRanges.count();
1387 
1388         EnvTag wrong_env = (backwards) ? EnvEnd : EnvBegin;
1389 
1390         if(foundRanges.size() < 5) {
1391             break;
1392         }
1393 
1394         KTextEditor::Range range = foundRanges.first();
1395 
1396         if(!range.isValid()) {
1397             //KILE_DEBUG_MAIN << "invalid range found";
1398             break;
1399         }
1400         env.row = range.start().line();
1401         env.col = range.start().column();
1402         env.len = doc->text(range).length();
1403 
1404         if(isValidBackslash(doc, env.row, env.col)) {
1405             // index 0 is the fullmatch, 1 first cap and so on
1406             QString cap2 = (foundRanges[2].isValid() ? doc->text(foundRanges[2]) : "");
1407             QString cap3 = (foundRanges[3].isValid() ? doc->text(foundRanges[3]) : "");
1408             QString cap4 = (foundRanges[4].isValid() ? doc->text(foundRanges[4]) : "");
1409             EnvTag found_env = (cap2 == "begin" || cap4 == "\\[") ? EnvBegin : EnvEnd;
1410             if(found_env == wrong_env) {
1411                 ++envcount;
1412             }
1413             else {
1414                 if(envcount > 0) {
1415                     --envcount;
1416                 }
1417                 else {
1418                     if(found_env == EnvBegin) {
1419                         env.name = (cap2 == "begin") ? cap3 : "\\[";
1420                     }
1421                     else {
1422                         env.name = (cap2 == "end") ? cap3 : "\\]";
1423                     }
1424                     env.tag = found_env;
1425                     //KILE_DEBUG_MAIN << "found " << env.name;
1426                     return true;
1427                 }
1428             }
1429         }
1430 
1431         // finally, prepare the range for the next search
1432         if(backwards) {
1433             searchRange = KTextEditor::Range(KTextEditor::Cursor(0, 0), foundRanges.first().start());
1434         }
1435         else {
1436             searchRange = KTextEditor::Range(foundRanges.first().end(), doc->documentEnd());
1437         }
1438     }
1439 
1440     //KILE_DEBUG_MAIN << "not found anything";
1441     return false;
1442 }
1443 
1444 //////////////////// check for an environment position ////////////////////
1445 
1446 // Check if the current position belongs to an environment. The result is set
1447 // to the beginning backslash of the environment tag. The same algorithms as
1448 // matching brackets is used.
1449 //
1450 // insert mode:    if there is a full tag on the left, always take it
1451 //                 if not, look to the right
1452 // overwrite mode: always take the tag, which begins at the cursor position
1453 //
1454 // test it with {a}{{b}}{c}
1455 
1456 bool EditorExtension::isEnvironmentPosition(KTextEditor::Document *doc, int row, int col, EnvData &env)
1457 {
1458     // get real textline without comments, quoted characters and pairs of backslashes
1459     QString textline = getTextLineReal(doc, row);
1460 
1461     if(col >= textline.length()) {
1462         return false;
1463     }
1464 
1465     bool left = false;
1466 
1467     //KILE_DEBUG_MAIN << "col=" << col;
1468 
1469     // check if there is a match in this line from the current position to the left
1470     int startcol = (textline[col] == '\\') ? col - 1 : col;
1471     if(startcol >= 1) {
1472         //KILE_DEBUG_MAIN << "search to the left ";
1473         int pos = textline.lastIndexOf(m_reg, startcol);
1474         env.len = m_reg.matchedLength();
1475         if(pos != -1 && pos < col && col <= pos + env.len) {
1476             //KILE_DEBUG_MAIN << "search to the left: found";
1477             env.row = row;
1478             env.col = pos;
1479             QChar ch = textline.at(pos + 1);
1480             if(ch=='b' || ch=='e') {
1481                 env.tag = (ch == 'b') ? EnvBegin : EnvEnd;
1482                 env.name = m_reg.cap(3);
1483             }
1484             else {
1485                 env.tag = (ch == '[') ? EnvBegin : EnvEnd;
1486                 env.name = m_reg.cap(4);
1487             }
1488 
1489             if ( !m_overwritemode || (m_overwritemode && col<pos+env.len) ) {
1490                 // insert mode:    position is inside the tag or behind the tag, which also belongs to the tag
1491                 // overwrit emode: position is inside the tag) {
1492                 //KILE_DEBUG_MAIN << "search to the left: stop";
1493                 return true;
1494             }
1495             // overwritemode: position is behind the tag
1496             left = true;
1497             //KILE_DEBUG_MAIN << "search to the left: left=true, but also look to the right";
1498         }
1499     }
1500 
1501     // check if there is a match in this line from the current position to the right
1502     //KILE_DEBUG_MAIN << "search to the right " ;
1503     if (textline[col] == '\\' && col == textline.indexOf(m_reg, col)) {
1504         //KILE_DEBUG_MAIN << "search to the right: found";
1505         env.row = row;
1506         env.col = col;
1507         env.len = m_reg.matchedLength();
1508         QChar ch = textline.at(col+1);
1509         if(ch == 'b' || ch == 'e') { // found "\begin" or "\end"
1510             env.tag = ( ch == 'b' ) ? EnvBegin : EnvEnd;
1511             env.name = m_reg.cap(3);
1512         }
1513         else { // found "\[" or "\\]"
1514             env.tag = (ch == '[') ? EnvBegin : EnvEnd;
1515             env.name = m_reg.cap(4);
1516         }
1517         //KILE_DEBUG_MAIN << "search to the right: stop";
1518         return true;
1519     }
1520 
1521     return left;
1522 }
1523 
1524 //////////////////// check for a comment ////////////////////
1525 
1526 // check if the current position is within a comment
1527 
1528 bool EditorExtension::isCommentPosition(KTextEditor::Document *doc, int row, int col)
1529 {
1530     QString textline = doc->line(row);
1531 
1532     bool backslash = false;
1533     for(int i = 0; i < col; ++i) {
1534         if(textline[i] == '%') {
1535             if(!backslash) { // found a comment sign
1536                 return true;
1537             }
1538             else {
1539                 backslash = false;
1540             }
1541         }
1542         else if(textline[i] == '\\') { // count number of backslashes
1543             backslash = !backslash;
1544         }
1545         else {
1546             backslash = false;               // no backslash
1547         }
1548     }
1549 
1550     return false;
1551 }
1552 
1553 // check if the character at text[col] is a valid backslash:
1554 //  - there is no comment sign in this line before
1555 //  - there is not a odd number of backslashes directly before
1556 
1557 bool EditorExtension::isValidBackslash(KTextEditor::Document *doc, int row, int col)
1558 {
1559     QString textline = doc->line(row);
1560 
1561     bool backslash = false;
1562     for(int i = 0; i < col; ++i) {
1563         if(textline[i] == '%') {
1564             if(!backslash) {
1565                 return false;                 // found a comment sign
1566             }
1567             else {
1568                 backslash = false;
1569             }
1570         }
1571         else if(textline[i] == '\\') {  // count number of backslashes
1572             backslash = !backslash;
1573         }
1574         else {
1575             backslash = false;               // no backslash
1576         }
1577     }
1578 
1579     return !backslash;
1580 }
1581 
1582 //////////////////// goto next bullet ////////////////////
1583 
1584 void EditorExtension::gotoBullet(bool backwards, KTextEditor::View *view)
1585 {
1586     view = determineView(view);
1587     if(!view) {
1588         return;
1589     }
1590 
1591     // get current position
1592     KTextEditor::Document *doc = view->document();
1593 
1594     KTextEditor::Cursor cursor = view->cursorPosition();
1595 
1596     KTextEditor::SearchOptions searchOptions = (backwards) ? KTextEditor::Backwards : KTextEditor::Default;
1597 
1598     KTextEditor::Range searchRange;
1599     if(backwards) {
1600         searchRange = KTextEditor::Range(KTextEditor::Cursor(0, 0), cursor);
1601     }
1602     else {
1603         const KTextEditor::Cursor nextCursorPosition(cursor.line(), cursor.column() + 1);
1604         if((doc->characterAt(cursor) == s_bullet_char)                                      // we are already at a bullet
1605                 && view->selection()
1606                 && view->selectionRange() == KTextEditor::Range(cursor, nextCursorPosition)) { // which has been 'highlighted'
1607             cursor = nextCursorPosition; // search for the next bullet
1608         }
1609         searchRange = KTextEditor::Range(cursor, doc->documentEnd());
1610     }
1611 
1612     QVector<KTextEditor::Range> foundRanges = doc->searchText(searchRange, s_bullet, searchOptions);
1613     if(foundRanges.size() >= 1) {
1614         KTextEditor::Range range = foundRanges.first();
1615         if(range.isValid()) {
1616             int line = range.start().line();
1617             int column = range.start().column();
1618             view->setCursorPosition(KTextEditor::Cursor(line, column));
1619             view->setSelection(KTextEditor::Range(line, column, line, column + 1));
1620         }
1621     }
1622 }
1623 
1624 //////////////////// increase/decrease cursor position ////////////////////
1625 
1626 bool EditorExtension::moveCursorRight(KTextEditor::View *view)
1627 {
1628     return moveCursor(view, MoveCursorRight);
1629 }
1630 
1631 bool EditorExtension::moveCursorLeft(KTextEditor::View *view)
1632 {
1633     return moveCursor(view, MoveCursorLeft);
1634 }
1635 
1636 bool EditorExtension::moveCursorUp(KTextEditor::View *view)
1637 {
1638     return moveCursor(view, MoveCursorUp);
1639 }
1640 
1641 bool EditorExtension::moveCursorDown(KTextEditor::View *view)
1642 {
1643     return moveCursor(view, MoveCursorDown);
1644 }
1645 
1646 bool EditorExtension::moveCursor(KTextEditor::View *view, CursorMove direction)
1647 {
1648     view = determineView(view);
1649     if(!view) {
1650         return false;
1651     }
1652 
1653     KTextEditor::Document *doc = view->document();
1654 
1655     KTextEditor::Cursor cursor = view->cursorPosition();
1656     int row = cursor.line();
1657     int col = cursor.column();
1658 
1659     bool ok = false;
1660     switch (direction)  {
1661     case MoveCursorLeft:
1662         ok = decreaseCursorPosition(doc,row,col);
1663         break;
1664     case MoveCursorRight:
1665         ok = increaseCursorPosition(doc,row,col);
1666         break;
1667     case MoveCursorUp:
1668         if(row > 0) {
1669             row--;
1670             ok = true;
1671         }
1672         break;
1673     case MoveCursorDown:
1674         if(row < doc->lines() - 1) {
1675             row++;
1676             ok = true;
1677         }
1678         break;
1679     }
1680 
1681     if(ok) {
1682         return view->setCursorPosition(KTextEditor::Cursor(row,col));
1683     }
1684     else {
1685         return false;
1686     }
1687 }
1688 
1689 bool EditorExtension::increaseCursorPosition(KTextEditor::Document *doc, int &row, int &col)
1690 {
1691     bool ok = true;
1692 
1693     if(col < doc->lineLength(row) - 1) {
1694         ++col;
1695     }
1696     else if(row < doc->lines() - 1) {
1697         ++row;
1698         col = 0;
1699     }
1700     else {
1701         ok = false;
1702     }
1703 
1704     return ok;
1705 }
1706 
1707 bool EditorExtension::decreaseCursorPosition(KTextEditor::Document *doc, int &row, int &col)
1708 {
1709     bool ok = true;
1710 
1711     if(col > 0) {
1712         --col;
1713     }
1714     else if(row > 0) {
1715         --row;
1716         col = doc->lineLength(row);
1717     }
1718     else {
1719         ok = false;
1720     }
1721 
1722     return ok;
1723 }
1724 
1725 //////////////////// texgroups ////////////////////
1726 
1727 // goto the next non-nested bracket
1728 
1729 void EditorExtension::gotoTexgroup(bool backwards, KTextEditor::View *view)
1730 {
1731     view = determineView(view);
1732     if(!view) return;
1733 
1734     uint row,col;
1735     bool found;
1736     BracketData bracket;
1737 
1738     // get current position
1739     KTextEditor::Document *doc = view->document();
1740     KTextEditor::Cursor cursor = view->cursorPosition();
1741     row = cursor.line();
1742     col = cursor.column();
1743     m_overwritemode = (view->viewMode() == KTextEditor::View::NormalModeOverwrite);
1744 
1745     // start searching
1746     if(backwards) {
1747         found = findOpenBracket(doc, row, col, bracket);
1748     }
1749     else {
1750         found = findCloseBracket(doc, row, col, bracket);
1751         // go behind the bracket
1752         if(!m_overwritemode) {
1753             ++bracket.col;
1754         }
1755     }
1756 
1757     if(found) {
1758         view->setCursorPosition(KTextEditor::Cursor(bracket.row, bracket.col));
1759     }
1760 }
1761 
1762 // match the opposite bracket
1763 
1764 void EditorExtension::matchTexgroup(KTextEditor::View *view)
1765 {
1766     view = determineView(view);
1767     if(!view) {
1768         return;
1769     }
1770 
1771     int row, col;
1772     BracketData bracket;
1773 
1774     // get current position
1775     KTextEditor::Document *doc = view->document();
1776     KTextEditor::Cursor cursor = view->cursorPosition();
1777     row = cursor.line();
1778     col = cursor.column();
1779     m_overwritemode = (view->viewMode() == KTextEditor::View::NormalModeOverwrite);
1780 
1781     // this operation is only allowed at a bracket position
1782     if(!isBracketPosition(doc, row, col, bracket)) {
1783         return;
1784     }
1785 
1786     // start searching
1787     bool found = false;
1788     if(bracket.open) {
1789         found = findCloseBracketTag(doc, bracket.row, bracket.col + 1, bracket);
1790         // go behind the bracket
1791         if(!m_overwritemode) {
1792             ++bracket.col;
1793         }
1794     }
1795     else {
1796         if(!decreaseCursorPosition(doc, bracket.row, bracket.col)) {
1797             return;
1798         }
1799         found = findOpenBracketTag(doc, bracket.row, bracket.col, bracket);
1800     }
1801 
1802     if(found) {
1803         view->setCursorPosition(KTextEditor::Cursor(bracket.row, bracket.col));
1804     }
1805 }
1806 
1807 //////////////////// close an open texgroup  ////////////////////
1808 
1809 // search for the last opened texgroup and close it
1810 
1811 void EditorExtension::closeTexgroup(KTextEditor::View *view)
1812 {
1813     view = determineView(view);
1814     if(!view) {
1815         return;
1816     }
1817 
1818     int row, col;
1819     BracketData bracket;
1820 
1821     KTextEditor::Document *doc = view->document();
1822     KTextEditor::Cursor cursor = view->cursorPosition();
1823     row = cursor.line();
1824     col = cursor.column();
1825 
1826     int rowtemp = row;
1827     int coltemp = col;
1828     if(!decreaseCursorPosition(doc, rowtemp, coltemp)) {
1829         return;
1830     }
1831 
1832     if(findOpenBracketTag(doc, rowtemp, coltemp, bracket)) {
1833         doc->insertText(KTextEditor::Cursor(row, col), "}");
1834         view->setCursorPosition(KTextEditor::Cursor(row, col + 1));
1835     }
1836 }
1837 
1838 //////////////////// select a texgroup  ////////////////////
1839 
1840 void EditorExtension::selectTexgroup(bool inside, KTextEditor::View *view)
1841 {
1842     view = determineView(view);
1843     if(!view) {
1844         return;
1845     }
1846 
1847     KTextEditor::Range range = texgroupRange(inside,view);
1848     if(range.isValid()) {
1849         view->setSelection(range);
1850     }
1851 }
1852 
1853 void EditorExtension::deleteTexgroup(bool inside, KTextEditor::View *view)
1854 {
1855     view = determineView(view);
1856     if(!view) {
1857         return;
1858     }
1859 
1860     KTextEditor::Range range =texgroupRange(inside,view);
1861     if(range.isValid()) {
1862         deleteRange(range, view);
1863     }
1864 }
1865 
1866 // calculate start and end of a Texgroup
1867 
1868 KTextEditor::Range EditorExtension::texgroupRange(bool inside, KTextEditor::View *view)
1869 {
1870     view = determineView(view);
1871     if(!view) {
1872         return KTextEditor::Range::invalid();
1873     }
1874 
1875     BracketData open, close;
1876     if(getTexgroup(inside, open, close, view)) {
1877         return KTextEditor::Range(open.row, open.col, close.row, close.col);
1878     }
1879     else {
1880         return KTextEditor::Range::invalid();
1881     }
1882 }
1883 
1884 bool EditorExtension::hasTexgroup(KTextEditor::View *view)
1885 {
1886     // view will be checked in texgroupRange()
1887     KTextEditor::Range range = texgroupRange(true, view);
1888     return (range.isValid()) ? true : false;
1889 }
1890 
1891 QString EditorExtension::getTexgroupText(bool inside, KTextEditor::View *view)
1892 {
1893     view = determineView(view);
1894     if(!view) {
1895         return QString();
1896     }
1897 
1898     KTextEditor::Range range = texgroupRange(inside,view);
1899     return (range.isValid()) ? view->document()->text(range) : QString();
1900 }
1901 
1902 bool EditorExtension::getTexgroup(bool inside, BracketData &open, BracketData &close, KTextEditor::View *view)
1903 {
1904     view = determineView(view);
1905     if(!view) {
1906         return false;
1907     }
1908 
1909     int row, col;
1910 
1911     KTextEditor::Document *doc = view->document();
1912     KTextEditor::Cursor cursor = view->cursorPosition();
1913     row = cursor.line();
1914     col = cursor.column();
1915 
1916     if(!findOpenBracket(doc, row, col, open))  {
1917         //KILE_DEBUG_MAIN << "no open bracket";
1918         return false;
1919     }
1920     if(!findCloseBracket(doc, row, col, close)) {
1921         //KILE_DEBUG_MAIN << "no close bracket";
1922         return false;
1923     }
1924 
1925     if(inside) {
1926         ++open.col;
1927     }
1928     else {
1929         ++close.col;
1930     }
1931 
1932     return true;
1933 }
1934 
1935 //////////////////// search for a bracket position  ////////////////////
1936 
1937 // Find the last opening bracket. If the current cursor is over
1938 //  - '{': we will stop immediately
1939 //  - '}': we will start before this character
1940 
1941 bool EditorExtension::findOpenBracket(KTextEditor::Document *doc, int row, int col, BracketData &bracket)
1942 {
1943     if(isBracketPosition(doc, row, col, bracket)) {
1944         // already found position?
1945         if(bracket.open) {
1946             return true;
1947         }
1948 
1949         // go one position back
1950         row = bracket.row;
1951         col = bracket.col;
1952         if(!decreaseCursorPosition(doc, row, col)) {
1953             return false;
1954         }
1955     }
1956 
1957     // looking back for last bracket
1958     return findOpenBracketTag(doc, row, col, bracket);
1959 }
1960 
1961 // Find the last closing bracket. If the current cursor is over
1962 //  - '}': we will stop immediately
1963 //  - '{': we will start behind this character
1964 
1965 bool EditorExtension::findCloseBracket(KTextEditor::Document *doc, int row, int col, BracketData &bracket)
1966 {
1967     if (isBracketPosition(doc, row, col, bracket)) {
1968         // already found position?
1969         if(!bracket.open) {
1970             return true;
1971         }
1972 
1973         // go one position forward
1974         row = bracket.row;
1975         col = bracket.col + 1;
1976     }
1977 
1978     // looking forward for next bracket
1979     return findCloseBracketTag(doc, row, col, bracket);
1980 }
1981 
1982 /*
1983    Bracket matching uses the following algorithm (taken from Kate):
1984    1) If in overwrite mode, match the bracket currently underneath the cursor.
1985    2) Otherwise, if the character to the left of the cursor is an ending bracket,
1986       match it.
1987    3) Otherwise if the character to the right of the cursor is a
1988       starting bracket, match it.
1989    4) Otherwise, if the character to the left of the cursor is a
1990       starting bracket, match it.
1991    5) Otherwise, if the character to the right of the cursor is an
1992       ending bracket, match it.
1993    6) Otherwise, don't match anything.
1994 */
1995 
1996 bool EditorExtension::isBracketPosition(KTextEditor::Document *doc, int row, int col, BracketData &bracket)
1997 {
1998     // default results
1999     bracket.row = row;
2000     bracket.col = col;
2001 
2002     QString textline = getTextLineReal(doc, row);
2003     QChar right = textline[col];
2004     QChar left  = (col > 0) ? textline[col-1] : QChar(' ');
2005 
2006     if (m_overwritemode) {
2007         if(right == '{') {
2008             bracket.open = true;
2009         }
2010         else if(left == '}') {
2011             bracket.open = false;
2012         }
2013         else {
2014             return false;
2015         }
2016     }
2017     else if(left == '}') {
2018         bracket.open = false;
2019         --bracket.col;
2020     }
2021     else if(right == '{') {
2022         bracket.open = true;
2023     }
2024     else if(left == '{') {
2025         bracket.open = true;
2026         --bracket.col;
2027     }
2028     else if(right == '}') {
2029         bracket.open = false;
2030     }
2031     else {
2032         return false;
2033     }
2034 
2035     return true;
2036 }
2037 
2038 // find next non-nested closing bracket
2039 
2040 bool EditorExtension::findCloseBracketTag(KTextEditor::Document *doc, int row, int col, BracketData &bracket)
2041 {
2042     uint brackets = 0;
2043     for(int line = row; line < doc->lines(); ++line) {
2044         uint start = (line == row) ? col : 0;
2045         QString textline = getTextLineReal(doc,line);
2046         for(int i = start; i < textline.length(); ++i) {
2047             if(textline[i] == '{') {
2048                 ++brackets;
2049             }
2050             else if(textline[i] == '}') {
2051                 if(brackets > 0) {
2052                     --brackets;
2053                 }
2054                 else {
2055                     bracket.row = line;
2056                     bracket.col = i;
2057                     bracket.open = false;
2058                     return true;
2059                 }
2060             }
2061         }
2062     }
2063 
2064     return false;
2065 }
2066 
2067 // find next non-nested opening bracket
2068 
2069 bool EditorExtension::findOpenBracketTag(KTextEditor::Document *doc, int row, int col, BracketData &bracket)
2070 {
2071     uint brackets = 0;
2072     for(int line = row; line >= 0; --line) {
2073         QString textline = getTextLineReal(doc, line);
2074         int start = (line == row) ? col : textline.length() - 1;
2075         for (int i = start; i >= 0; --i) {
2076             //KILE_DEBUG_MAIN << "findOpenBracketTag: (" << line << "," << i << ") = " << textline[i].toLatin1();
2077             if(textline[i] == '{') {
2078                 if(brackets > 0) {
2079                     --brackets;
2080                 }
2081                 else {
2082                     bracket.row = line;
2083                     bracket.col = i;
2084                     bracket.open = true;
2085                     return true;
2086                 }
2087             }
2088             else if(textline[i] == '}') {
2089                 ++brackets;
2090             }
2091         }
2092     }
2093 
2094     //KILE_DEBUG_MAIN << "nothting found";
2095     return false;
2096 }
2097 
2098 //////////////////// get real text ////////////////////
2099 
2100 // get current textline and remove
2101 //  - all pairs of backslashes: '\\'
2102 //  - all quoted comment signs: '\%'
2103 //  - all quoted brackets: '\{' and '\}'
2104 //  - all comments
2105 // replace these characters with one, which never will be looked for
2106 
2107 QString EditorExtension::getTextLineReal(KTextEditor::Document *doc, int row)
2108 {
2109     QString textline = doc->line(row);
2110     int len = textline.length();
2111     if(len == 0) {
2112         return QString();
2113     }
2114 
2115     bool backslash = false;
2116     for(int i = 0; i < len; ++i) {
2117         if (textline[i]=='{' || textline[i]=='}' || textline[i]=='$') {
2118             if(backslash) {
2119                 textline[i-1] = '&';
2120                 textline[i] = '&';
2121             }
2122             backslash = false;
2123         }
2124         else if(textline[i] == '\\') {
2125             if(backslash) {
2126                 textline[i-1] = '&';
2127                 textline[i] = '&';
2128                 backslash = false;
2129             }
2130             else {
2131                 backslash = true;
2132             }
2133         }
2134         else if(textline[i]=='%') {
2135             if (backslash) {
2136                 textline[i-1] = '&';
2137                 textline[i] = '&';
2138             }
2139             else {
2140                 len = i;
2141                 break;
2142             }
2143             backslash = false;
2144         }
2145         else {
2146             backslash = false;
2147         }
2148     }
2149 
2150     // return real text
2151     return textline.left(len);
2152 }
2153 
2154 //////////////////// capture the current word ////////////////////
2155 
2156 // Capture the current word from the cursor position to the left and right.
2157 // The result depens on the given search mode;
2158 // - smTex       only letters, except backslash as first and star as last  character
2159 // - smLetter:   only letters
2160 // - smWord:     letters and digits
2161 // - smNospace:  everything except white space
2162 
2163 bool EditorExtension::getCurrentWord(KTextEditor::Document *doc, int row, int col, EditorExtension::SelectMode mode, QString &word, int &x1, int &x2)
2164 {
2165     // get real textline without comments, quoted characters and pairs of backslashes
2166     QString textline = getTextLineReal(doc, row);
2167     if (col > textline.length()) {
2168         return false;
2169     }
2170 
2171     QRegExp reg;
2172     QString pattern1, pattern2;
2173     switch(mode) {
2174     case smLetter:
2175         pattern1 = "[^a-zA-Z]+";
2176         pattern2 = "[a-zA-Z]+";
2177         break;
2178     case smWord:
2179         pattern1 = "[^a-zA-Z0-9]";
2180         pattern2 = "[a-zA-Z0-9]+";
2181         break;
2182     case smNospace:
2183         pattern1 = "\\s";
2184         pattern2 = "\\S+";
2185         break;
2186     default:
2187         pattern1 = "[^a-zA-Z]";
2188         pattern2 = "\\\\?[a-zA-Z]+\\*?";
2189         break;
2190     }
2191     x1 = x2 = col;
2192 
2193     int pos;
2194     // search to the left side
2195     if(col > 0) {
2196         reg.setPattern(pattern1);
2197         pos = textline.lastIndexOf(reg, col - 1);
2198         if(pos != -1) {        // found an illegal character
2199             x1 = pos + 1;
2200             if(mode == smTex) {
2201                 if(textline[pos] == '\\') {
2202                     x1 = pos;
2203                 }
2204                 col = x1;
2205             }
2206         }
2207         else {
2208             x1 = 0;               // pattern matches from beginning of line
2209         }
2210     }
2211 
2212     // search at the current position
2213     reg.setPattern(pattern2);
2214     pos = textline.indexOf(reg, col);
2215     if(pos != -1 && pos == col) {
2216         x2 = pos + reg.matchedLength();
2217     }
2218 
2219     // get all characters
2220     if(x1 != x2) {
2221         word = textline.mid(x1, x2 - x1);
2222         return true;
2223     }
2224     else {
2225         return false;
2226     }
2227 }
2228 
2229 KTextEditor::Range EditorExtension::wordRange(const KTextEditor::Cursor &cursor, bool latexCommand, KTextEditor::View *view)
2230 {
2231     view = determineView(view);
2232     if(!view) {
2233         return KTextEditor::Range::invalid();
2234     }
2235 
2236     int col1, col2;
2237     QString currentWord;
2238     EditorExtension::SelectMode mode = ( latexCommand ) ? EditorExtension::smTex : EditorExtension::smLetter;
2239     int line = cursor.line();
2240 
2241     return (getCurrentWord(view->document(), line, cursor.column(), mode, currentWord, col1, col2))
2242            ? KTextEditor::Range(line,col1,line,col2)
2243            : KTextEditor::Range::invalid();
2244 }
2245 
2246 QString EditorExtension::word(const KTextEditor::Cursor &cursor, bool latexCommand, KTextEditor::View *view)
2247 {
2248     KTextEditor::Range range = EditorExtension::wordRange(cursor,latexCommand,view);
2249     return ( range.isValid() ) ? view->document()->text(range) : QString();
2250 }
2251 
2252 //////////////////// paragraph ////////////////////
2253 
2254 void EditorExtension::selectParagraph(KTextEditor::View* view, bool wholeLines)
2255 {
2256     view = determineView(view);
2257     if(!view) {
2258         return;
2259     }
2260 
2261     KTextEditor::Range range = findCurrentParagraphRange(view, wholeLines);
2262     if ( range.isValid() ) {
2263         view->setSelection(range);
2264     }
2265 }
2266 
2267 void EditorExtension::deleteParagraph(KTextEditor::View *view)
2268 {
2269     view = determineView(view);
2270     if(!view) {
2271         return;
2272     }
2273     int startline, endline;
2274 
2275     if(findCurrentTexParagraph(startline, endline, view)) {
2276         KTextEditor::Document *doc = view->document();
2277         view->removeSelection();
2278         if(startline > 0) {
2279             --startline;
2280         }
2281         else if(endline < doc->lines() - 1) {
2282             ++endline;
2283         }
2284         doc->removeText(KTextEditor::Range(startline, 0, endline+1, 0));
2285         view->setCursorPosition(KTextEditor::Cursor(startline, 0));
2286     }
2287 }
2288 
2289 // get the range of the current paragraph
2290 KTextEditor::Range EditorExtension::findCurrentParagraphRange(KTextEditor::View* view, bool wholeLines)
2291 {
2292     view = determineView(view);
2293     if(!view) {
2294         return KTextEditor::Range::invalid();
2295     }
2296 
2297     int startline, endline, startcolumn, endcolumn;
2298 
2299     if (findCurrentTexParagraph(startline, startcolumn, endline, endcolumn, view)) {
2300         return wholeLines ?
2301                KTextEditor::Range(startline, 0, endline + 1, 0) :
2302                KTextEditor::Range(startline, startcolumn, endline, endcolumn);
2303     }
2304     else {
2305         return KTextEditor::Range::invalid();
2306     }
2307 }
2308 
2309 QString  EditorExtension::getParagraphText(KTextEditor::View *view)
2310 {
2311     view = determineView(view);
2312     if(!view) {
2313         return QString();
2314     }
2315 
2316     KTextEditor::Range range = findCurrentParagraphRange(view);
2317     return (range.isValid()) ? view->document()->text(range) : QString();
2318 }
2319 
2320 bool EditorExtension::findCurrentTexParagraph(int& startline, int& endline, KTextEditor::View* view)
2321 {
2322     int dummy;
2323     return findCurrentTexParagraph(startline, dummy, endline, dummy, view);
2324 }
2325 
2326 bool EditorExtension::findCurrentTexParagraph(int& startline, int& startcolumn, int& endline, int& endcolumn, KTextEditor::View* view)
2327 {
2328     view = determineView(view);
2329     if(!view) {
2330         return false;
2331     }
2332 
2333     int row;
2334 
2335     // get current position
2336     KTextEditor::Document *doc = view->document();
2337     KTextEditor::Cursor cursor = view->cursorPosition();
2338     row = cursor.line();
2339 
2340     // don't accept an empty line as part of a paragraph
2341     if(doc->line(row).trimmed().isEmpty()) {
2342         return false;
2343     }
2344 
2345     // settings default results
2346     startline = row;
2347     endline = row;
2348 
2349     // find the previous empty line
2350     for(int line = row - 1; line >= 0; --line) {
2351         if(doc->line(line).trimmed().isEmpty()) {
2352             break;
2353         }
2354         startline = line;
2355     }
2356 
2357     // it is guaranteed that 'startline.trimmed()' won't be empty
2358     startcolumn = 0;
2359     QString line = doc->line(startline);
2360     for(int i = 0; i < line.size(); ++i) {
2361         if(!line[i].isSpace()) {
2362             startcolumn = i;
2363             break;
2364         }
2365     }
2366 
2367     // find the next empty line
2368     for(int lineBelow = row + 1; lineBelow < doc->lines(); ++lineBelow) {
2369         if(doc->line(lineBelow).trimmed().isEmpty()) {
2370             break;
2371         }
2372         endline = lineBelow;
2373     }
2374 
2375     // it is guaranteed that 'endline.trimmed()' won't be empty
2376     line = doc->line(endline);
2377     endcolumn = line.size();
2378     for(int i = line.size() - 1; i >= 0; --i) {
2379         if(!line[i].isSpace()) {
2380             endcolumn = i+1;
2381             break;
2382         }
2383     }
2384 
2385     // settings result
2386     return true;
2387 }
2388 
2389 void EditorExtension::gotoNextParagraph(KTextEditor::View *view)
2390 {
2391     view = determineView(view);
2392     if(!view) {
2393         return;
2394     }
2395 
2396     bool found;
2397     KTextEditor::Document *doc = view->document();
2398 
2399     int endline = view->cursorPosition().line();
2400     if(doc->line(endline).trimmed().isEmpty()) {
2401         found = true;
2402     }
2403     else {
2404         int startline;
2405         found = findCurrentTexParagraph(startline, endline, view);
2406     }
2407 
2408     // we are in an empty line or in the last line of a paragraph
2409     if (found) {
2410         // find the next non empty line
2411         for(int line = endline + 1; line < doc->lines(); ++line) {
2412             if(!doc->line(line).trimmed().isEmpty()) {
2413                 view->setCursorPosition(KTextEditor::Cursor(line, 0));
2414                 return;
2415             }
2416         }
2417     }
2418 }
2419 
2420 void EditorExtension::gotoPrevParagraph(KTextEditor::View *view)
2421 {
2422     view = determineView(view);
2423     if(!view) {
2424         return;
2425     }
2426 
2427     bool found;
2428     int startline;
2429     KTextEditor::Document *doc = view->document();
2430 
2431     startline = view->cursorPosition().line();
2432     if(doc->line(startline).trimmed().isEmpty()) {
2433         startline++;
2434         found = true;
2435     }
2436     else {
2437         int endline;
2438         found = findCurrentTexParagraph(startline,endline,view);
2439     }
2440     // we are in an empty line or in the first line of a paragraph
2441     if(found) {
2442         // find the last line of the previous paragraph
2443         int foundline = -1;
2444         for (int line = startline - 1; line >= 0; --line) {
2445             if(!doc->line(line).trimmed().isEmpty()) {
2446                 break;
2447             }
2448             foundline = line;
2449         }
2450         if(foundline < 0) {
2451             return;
2452         }
2453 
2454         // and finally the first line of this paragraph
2455         int prevstartline = -1;
2456         for(int line = foundline - 1; line >= 0; --line) {
2457             if(doc->line(line).trimmed().isEmpty()) {
2458                 break;
2459             }
2460             prevstartline = line;
2461         }
2462 
2463         if(prevstartline >= 0) {
2464             view->setCursorPosition(KTextEditor::Cursor(prevstartline, 0));
2465         }
2466     }
2467 }
2468 
2469 int EditorExtension::prevNonEmptyLine(int line, KTextEditor::View *view)
2470 {
2471     view = determineView(view);
2472     if(!view) {
2473         return -1;
2474     }
2475 
2476     KTextEditor::Document *doc = view->document();
2477     for(int i = line - 1; i >= 0; --i) {
2478         if(!doc->line(i).trimmed().isEmpty()) {
2479             return i;
2480         }
2481     }
2482     return -1;
2483 }
2484 
2485 int EditorExtension::nextNonEmptyLine(int line, KTextEditor::View *view)
2486 {
2487     view = determineView(view);
2488     if(!view) {
2489         return -1;
2490     }
2491 
2492     KTextEditor::Document *doc = view->document();
2493     int lines = doc->lines();
2494     for(int i = line + 1; i < lines; ++i) {
2495         if(!doc->line(i).trimmed().isEmpty()) {
2496             return i;
2497         }
2498     }
2499     return -1;
2500 }
2501 
2502 //////////////////// one line of text////////////////////
2503 
2504 void EditorExtension::selectLine(KTextEditor::View *view)
2505 {
2506     view = determineView(view);
2507     if(!view) {
2508         return;
2509     }
2510 
2511     // get current position
2512     KTextEditor::Document *doc = view->document();
2513     KTextEditor::Cursor cursor = view->cursorPosition();
2514     int row = cursor.line();
2515 
2516     if(doc->lineLength(row) > 0) {
2517         view->setSelection(KTextEditor::Range(row, 0, row + 1, 0));
2518     }
2519 }
2520 
2521 void EditorExtension::selectLine(int line, KTextEditor::View *view)
2522 {
2523     view = determineView(view);
2524     if(!view) {
2525         return;
2526     }
2527 
2528     if(view->document()->lineLength(line) > 0) {
2529         view->setSelection(KTextEditor::Range(line, 0, line + 1, 0));
2530     }
2531 }
2532 
2533 void EditorExtension::selectLines(int from, int to, KTextEditor::View *view)
2534 {
2535     view = determineView(view);
2536     if(view && from <= to) {
2537         view->setSelection(KTextEditor::Range(from, 0, to + 1, 0));
2538     }
2539 }
2540 
2541 bool EditorExtension::replaceLine(int line, const QString &s, KTextEditor::View *view)
2542 {
2543     view = determineView(view);
2544     if(!view) {
2545         return false;
2546     }
2547 
2548     KTextEditor::Document *doc = view->document();
2549     KTextEditor::Document::EditingTransaction transaction(doc);
2550     doc->removeLine(line);
2551     bool result = doc->insertLine(line, s);
2552     return result;
2553 }
2554 
2555 void EditorExtension::deleteEndOfLine(KTextEditor::View *view)
2556 {
2557     view = determineView(view);
2558     if(!view) {
2559         return;
2560     }
2561 
2562     int row, col;
2563     KTextEditor::Cursor cursor = view->cursorPosition();
2564     row = cursor.line();
2565     col = cursor.column();
2566 
2567     KTextEditor::Document *doc = view->document();
2568     view->removeSelection();
2569     doc->removeText(KTextEditor::Range(row, col, row, doc->lineLength(row)));
2570 }
2571 
2572 //////////////////// LaTeX command ////////////////////
2573 
2574 void EditorExtension::selectWord(EditorExtension::SelectMode mode, KTextEditor::View *view)
2575 {
2576     view = determineView(view);
2577     if(!view) {
2578         return;
2579     }
2580 
2581     KTextEditor::Range range = wordRange(view->cursorPosition(),mode,view);
2582     if ( range.isValid() ) {
2583         view->setSelection(range);
2584     }
2585 }
2586 
2587 void EditorExtension::deleteWord(EditorExtension::SelectMode mode, KTextEditor::View *view)
2588 {
2589     view = determineView(view);
2590     if(!view) {
2591         return;
2592     }
2593 
2594     KTextEditor::Range range = wordRange(view->cursorPosition(),mode,view);
2595     if(range.isValid()) {
2596         deleteRange(range,view);
2597     }
2598 }
2599 
2600 void EditorExtension::nextBullet(KTextEditor::View* view)
2601 {
2602     gotoBullet(false, view);
2603 }
2604 
2605 void EditorExtension::prevBullet(KTextEditor::View* view)
2606 {
2607     gotoBullet(true, view);
2608 }
2609 
2610 void EditorExtension::insertBullet(KTextEditor::View* view)
2611 {
2612     view = determineView(view);
2613     if(!view) {
2614         return;
2615     }
2616 
2617     view->document()->insertText(view->cursorPosition(), s_bullet);
2618 }
2619 
2620 ///////////////////// Special Functions ///////////////
2621 /*
2622 void EditorExtension::insertNewLine(KTextEditor::View *view)
2623 {
2624     view = determineView(view);
2625     if(!view) {
2626         return;
2627     }
2628 
2629     int newLineNumber = view->cursorPosition().line() + 1;
2630     view->document()->insertLine(newLineNumber, QString());
2631 }
2632 */
2633 void EditorExtension::moveCursorToLastPositionInCurrentLine(KTextEditor::View *view)
2634 {
2635     view = determineView(view);
2636     if(!view) {
2637         return;
2638     }
2639 
2640     const KTextEditor::Cursor currentPosition = view->cursorPosition();
2641     view->setCursorPosition(KTextEditor::Cursor(currentPosition.line(),
2642                             view->document()->lineLength(currentPosition.line())));
2643 }
2644 
2645 void EditorExtension::keyReturn(KTextEditor::View *view)
2646 {
2647     view = determineView(view);
2648     if(!view) {
2649         return;
2650     }
2651 
2652     int newLineNumber = view->cursorPosition().line() + 1;
2653     view->document()->insertLine(newLineNumber, QString());
2654     view->setCursorPosition(KTextEditor::Cursor(newLineNumber, 0));
2655 }
2656 
2657 void EditorExtension::commentLaTeX(KTextEditor::Document* document, const KTextEditor::Range& range)
2658 {
2659     int startLine = range.start().line(), endLine = range.end().line();
2660     for(int i = startLine; i <= endLine; ++i) {
2661         document->insertText(KTextEditor::Cursor(i, 0), "% ");
2662     }
2663 }
2664 
2665 void EditorExtension::goToLine(int line, KTextEditor::View *view)
2666 {
2667     view = determineView(view);
2668     if(!view) {
2669         return;
2670     }
2671 
2672     KTextEditor::Cursor cursor(line, 0);
2673     view->setCursorPosition(cursor);
2674 }
2675 
2676 //////////////////// double quotes ////////////////////
2677 
2678 void EditorExtension::initDoubleQuotes()
2679 {
2680     m_dblQuotes = KileConfig::insertDoubleQuotes();
2681 
2682     int index = KileConfig::doubleQuotes();
2683     if(index < 0 || index >= m_quoteList.count()) {
2684         index = 0;
2685     }
2686 
2687     m_leftDblQuote = m_quoteList[index].first;
2688     m_rightDblQuote = m_quoteList[index].second;
2689     KILE_DEBUG_MAIN << "new quotes: " << m_dblQuotes << " left=" << m_leftDblQuote << " right=" << m_rightDblQuote<< Qt::endl;
2690 }
2691 
2692 bool EditorExtension::insertDoubleQuotes(KTextEditor::View *view)
2693 {
2694     // don't insert double quotes, if konsole has focus
2695     // return false, because if this is called from an event
2696     // handler, because this event has to be passed on
2697     if(m_ki->texKonsole()->hasFocus()) {
2698         return false;
2699     }
2700 
2701     // insert double quotes, normal mode or autocompletion mode
2702     // always return true for event handler
2703     view = determineView(view);
2704     if(!view) {
2705         return true;
2706     }
2707 
2708     // don't insert double quotes when in vimode
2709     // but not in a "text insert" mode (e.g normal mode)
2710     // return false so that the " can be handled normally
2711     KTextEditor::View::ViewMode viewMode = view->viewMode();
2712     if (view->viewInputMode() == KTextEditor::View::ViInputMode
2713         && viewMode != KTextEditor::View::ViModeInsert
2714         && viewMode != KTextEditor::View::ViModeReplace) {
2715             return false;
2716     }
2717 
2718     KTextEditor::Document *doc = view->document();
2719 
2720     if(!doc) {
2721         return false;
2722     }
2723 
2724     view->removeSelectionText();
2725 
2726     int row, col;
2727     KTextEditor::Cursor cursor = view->cursorPosition();
2728     row = cursor.line();
2729     col = cursor.column();
2730 
2731     // simply insert, if we are inside a verb command
2732     if(insideVerb(view) || insideVerbatim(view)) {
2733         return false;
2734     }
2735 
2736     // simply insert, if autoinsert mode is not active or the char bevor is \ (typically for \"a useful)
2737     if (!m_dblQuotes || (col > 0 && doc->text(KTextEditor::Range(row, col - 1, row, col)) == "\\")) {
2738         return false;
2739     }
2740 
2741     // insert with auto mode
2742     QString pattern1 = QRegExp::escape(m_leftDblQuote);
2743     if(m_leftDblQuote.at(m_leftDblQuote.length()-1).isLetter()) {
2744         pattern1 += "(\\b|(\\{\\}))";
2745     }
2746     QString pattern2 = QRegExp::escape(m_rightDblQuote);
2747     if(m_rightDblQuote.at(m_rightDblQuote.length()-1).isLetter()) {
2748         pattern2 += "(\\b|(\\{\\}))";
2749     }
2750 
2751     bool openFound = false;
2752     KTextEditor::Range searchRange = KTextEditor::Range(KTextEditor::Cursor(0, 0), KTextEditor::Cursor(row, col));
2753     QVector<KTextEditor::Range> foundRanges = doc->searchText(searchRange, '(' + pattern1 + ")|(" + pattern2 + ')',
2754             KTextEditor::Regex | KTextEditor::Backwards);
2755     // KTextEditor::Document#searchText always returns at least one range, even
2756     // if no occurrences have been found. Thus, we have to check if the range is valid.
2757     KTextEditor::Range range = foundRanges.first();
2758     if(range.isValid()) {
2759         int lineFound = range.start().line();
2760         int columnFound = range.start().column();
2761         openFound = (doc->line(lineFound).indexOf(m_leftDblQuote, columnFound) == columnFound);
2762     }
2763 
2764     QString textline = doc->line(row);
2765     //KILE_DEBUG_MAIN << "text=" << textline << " open=" << openfound;
2766     if(openFound) {
2767         // If we last inserted a language specific doublequote open,
2768         // we have to change it to a normal doublequote. If not we
2769         // insert a language specific doublequote close
2770         int startcol = col - m_leftDblQuote.length();
2771         //KILE_DEBUG_MAIN << "startcol=" << startcol << " col=" << col ;
2772         if (startcol>=0 && textline.indexOf(m_leftDblQuote, startcol) == startcol) {
2773             doc->removeText(KTextEditor::Range(row, startcol, row, startcol + m_leftDblQuote.length()));
2774             doc->insertText(KTextEditor::Cursor(row, startcol), "\"");
2775         }
2776         else {
2777             doc->insertText(KTextEditor::Cursor(row, col), m_rightDblQuote);
2778         }
2779     }
2780     else {
2781         // If we last inserted a language specific doublequote close,
2782         // we have to change it to a normal doublequote. If not we
2783         // insert a language specific doublequote open
2784         int startcol = col - m_rightDblQuote.length();
2785         //KILE_DEBUG_MAIN << "startcol=" << startcol << " col=" << col ;
2786         if (startcol >= 0 && textline.indexOf(m_rightDblQuote, startcol) == startcol) {
2787             doc->removeText(KTextEditor::Range(row, startcol, row, startcol + m_rightDblQuote.length()));
2788             doc->insertText(KTextEditor::Cursor(row,startcol), "\"");
2789         }
2790         else {
2791             doc->insertText(KTextEditor::Cursor(row, col), m_leftDblQuote);
2792         }
2793     }
2794     return true;
2795 }
2796 
2797 // Takes an unicode unsigned short and calls insertSpecialCharacter to
2798 // insert the proper LaTeX sequence and warn user of any dependencies.
2799 //FIXME: there should be one central place to convert unicode chars to LaTeX;
2800 //       also see 'LaTeXEventFilter::eventFilter'.
2801 bool EditorExtension::insertLatexFromUnicode(unsigned short rep, KTextEditor::View *view)
2802 {
2803     switch(rep)
2804     {   // Find the unicode decimal representation
2805     case 160:
2806         return insertSpecialCharacter("~", view);
2807     case 161:
2808         return insertSpecialCharacter("!`", view);
2809     case 162:
2810         return insertSpecialCharacter("\\textcent", view, "textcomp");
2811     case 163:
2812         return insertSpecialCharacter("\\pounds", view);
2813     case 164:
2814         return insertSpecialCharacter("\\textcurrency", view, "textcomp");
2815     case 165:
2816         return insertSpecialCharacter("\\textyen", view, "textcomp");
2817     case 166:
2818         return insertSpecialCharacter("\\textbrokenbar", view, "textcomp");
2819     case 167:
2820         return insertSpecialCharacter("\\S", view);
2821     case 168:
2822         return insertSpecialCharacter("\"", view);
2823     case 169:
2824         return insertSpecialCharacter("\\copyright", view);
2825     case 170:
2826         return insertSpecialCharacter("\\textordfeminine", view, "textcomp");
2827     case 171:
2828         return insertSpecialCharacter("\\guillemotleft", view);
2829     case 172:
2830         return insertSpecialCharacter("\\neg", view); // TODO: Check for math
2831     case 173:
2832         return insertSpecialCharacter("-", view); // TODO: Check for math
2833     case 174:
2834         return insertSpecialCharacter("\\textregistered", view, "textcomp");
2835     case 176:
2836         return insertSpecialCharacter("^\\circ", view); // TODO: Check for math
2837     case 177:
2838         return insertSpecialCharacter("\\pm", view); // TODO: Check for math
2839     case 178:
2840         return insertSpecialCharacter("^2", view); // TODO: Check for math
2841     case 179:
2842         return insertSpecialCharacter("^3", view); // TODO: Check for math
2843     case 180:
2844         return insertSpecialCharacter("'", view);
2845     case 181:
2846         return insertSpecialCharacter("\\mu", view); // TODO: Check for math
2847     case 182:
2848         return insertSpecialCharacter("\\P", view);
2849     case 185:
2850         return insertSpecialCharacter("^1", view); // TODO: Check for math
2851     case 186:
2852         return insertSpecialCharacter("\\textordmasculine", view, "textcomp");
2853     case 187:
2854         return insertSpecialCharacter("\\guillemotright", view);
2855     case 191:
2856         return insertSpecialCharacter("?`", view);
2857     case 192:
2858         return insertSpecialCharacter("\\`A", view);
2859     case 193:
2860         return insertSpecialCharacter("\\'A", view);
2861     case 194:
2862         return insertSpecialCharacter("\\^A", view);
2863     case 195:
2864         return insertSpecialCharacter("\\~A", view);
2865     case 196:
2866         return insertSpecialCharacter("\\\"A", view);
2867     case 197:
2868         return insertSpecialCharacter("\\AA", view);
2869     case 198:
2870         return insertSpecialCharacter("\\AE", view);
2871     case 199:
2872         return insertSpecialCharacter("\\c{C}", view);
2873     case 200:
2874         return insertSpecialCharacter("\\`E", view);
2875     case 201:
2876         return insertSpecialCharacter("\\'E", view);
2877     case 202:
2878         return insertSpecialCharacter("\\^E", view);
2879     case 203:
2880         return insertSpecialCharacter("\\\"E", view);
2881     case 204:
2882         return insertSpecialCharacter("\\`I", view);
2883     case 205:
2884         return insertSpecialCharacter("\\'I", view);
2885     case 206:
2886         return insertSpecialCharacter("\\^I", view);
2887     case 207:
2888         return insertSpecialCharacter("\\\"I", view);
2889     case 209:
2890         return insertSpecialCharacter("\\~N", view);
2891     case 210:
2892         return insertSpecialCharacter("\\`O", view);
2893     case 211:
2894         return insertSpecialCharacter("\\'O", view);
2895     case 212:
2896         return insertSpecialCharacter("\\^O", view);
2897     case 213:
2898         return insertSpecialCharacter("\\~O", view);
2899     case 214:
2900         return insertSpecialCharacter("\\\"O", view);
2901     case 215:
2902         return insertSpecialCharacter("\\times", view); //TODO: Check for math
2903     case 216:
2904         return insertSpecialCharacter("\\Oslash", view);
2905     case 217:
2906         return insertSpecialCharacter("\\`U", view);
2907     case 218:
2908         return insertSpecialCharacter("\\'U", view);
2909     case 219:
2910         return insertSpecialCharacter("\\^U", view);
2911     case 220:
2912         return insertSpecialCharacter("\\\"U", view);
2913     case 221:
2914         return insertSpecialCharacter("\\'Y", view);
2915     case 223:
2916         return insertSpecialCharacter("\\ss{}", view);
2917     case 224:
2918         return insertSpecialCharacter("\\`a", view);
2919     case 225:
2920         return insertSpecialCharacter("\\'a", view);
2921     case 226:
2922         return insertSpecialCharacter("\\^a", view);
2923     case 227:
2924         return insertSpecialCharacter("\\~a", view);
2925     case 228:
2926         return insertSpecialCharacter("\\\"a", view);
2927     case 229:
2928         return insertSpecialCharacter("\\aa", view);
2929     case 230:
2930         return insertSpecialCharacter("\\ae", view);
2931     case 231:
2932         return insertSpecialCharacter("\\c{c}", view);
2933     case 232:
2934         return insertSpecialCharacter("\\`e", view);
2935     case 233:
2936         return insertSpecialCharacter("\\'e", view);
2937     case 234:
2938         return insertSpecialCharacter("\\^e", view);
2939     case 235:
2940         return insertSpecialCharacter("\\\"e", view);
2941     case 236:
2942         return insertSpecialCharacter("\\`i", view);
2943     case 237:
2944         return insertSpecialCharacter("\\'i", view);
2945     case 238:
2946         return insertSpecialCharacter("\\^i", view);
2947     case 239:
2948         return insertSpecialCharacter("\\\"i", view);
2949     case 240:
2950         return insertSpecialCharacter("\\~o", view);
2951     case 241:
2952         return insertSpecialCharacter("\\~n", view);
2953     case 242:
2954         return insertSpecialCharacter("\\`o", view);
2955     case 243:
2956         return insertSpecialCharacter("\\'o", view);
2957     case 244:
2958         return insertSpecialCharacter("\\^o", view);
2959     case 245:
2960         return insertSpecialCharacter("\\~o", view);
2961     case 246:
2962         return insertSpecialCharacter("\\\"o", view);
2963     case 247:
2964         return insertSpecialCharacter("\\div", view);
2965     case 248:
2966         return insertSpecialCharacter("\\oslash", view);
2967     case 249:
2968         return insertSpecialCharacter("\\`u", view);
2969     case 250:
2970         return insertSpecialCharacter("\\'u", view);
2971     case 251:
2972         return insertSpecialCharacter("\\^u", view);
2973     case 252:
2974         return insertSpecialCharacter("\\\"u", view);
2975     case 253:
2976         return insertSpecialCharacter("\\'y", view);
2977     case 255:
2978         return insertSpecialCharacter("\\\"y", view);
2979     case 256:
2980         return insertSpecialCharacter("\\=A", view);
2981     case 257:
2982         return insertSpecialCharacter("\\=a", view);
2983     case 258:
2984         return insertSpecialCharacter("\\uA", view);
2985     case 259:
2986         return insertSpecialCharacter("\\ua", view);
2987     case 262:
2988         return insertSpecialCharacter("\\'C", view);
2989     case 263:
2990         return insertSpecialCharacter("\\'c", view);
2991     case 264:
2992         return insertSpecialCharacter("\\^C", view);
2993     case 265:
2994         return insertSpecialCharacter("\\^c", view);
2995     case 266:
2996         return insertSpecialCharacter("\\.C", view);
2997     case 267:
2998         return insertSpecialCharacter("\\.c", view);
2999     case 268:
3000         return insertSpecialCharacter("\\vC", view);
3001     case 269:
3002         return insertSpecialCharacter("\\vc", view);
3003     case 270:
3004         return insertSpecialCharacter("\\vD", view);
3005     case 271:
3006         return insertSpecialCharacter("\\vd", view);
3007     case 272:
3008         return insertSpecialCharacter("\\=D", view);
3009     case 273:
3010         return insertSpecialCharacter("\\=d", view);
3011     case 274:
3012         return insertSpecialCharacter("\\=E", view);
3013     case 275:
3014         return insertSpecialCharacter("\\=e", view);
3015     case 276:
3016         return insertSpecialCharacter("\\uE", view);
3017     case 277:
3018         return insertSpecialCharacter("\\ue", view);
3019     case 278:
3020         return insertSpecialCharacter("\\.E", view);
3021     case 279:
3022         return insertSpecialCharacter("\\.e", view);
3023     case 282:
3024         return insertSpecialCharacter("\\vE", view);
3025     case 283:
3026         return insertSpecialCharacter("\\ve", view);
3027     case 284:
3028         return insertSpecialCharacter("\\^G", view);
3029     case 285:
3030         return insertSpecialCharacter("\\^g", view);
3031     case 286:
3032         return insertSpecialCharacter("\\uG", view);
3033     case 287:
3034         return insertSpecialCharacter("\\ug", view);
3035     case 288:
3036         return insertSpecialCharacter("\\.G", view);
3037     case 289:
3038         return insertSpecialCharacter("\\.g", view);
3039     case 290:
3040         return insertSpecialCharacter("\\cG", view);
3041     case 291:
3042         return insertSpecialCharacter("\\'g", view);
3043     case 292:
3044         return insertSpecialCharacter("\\^H", view);
3045     case 293:
3046         return insertSpecialCharacter("\\^h", view);
3047     case 294:
3048         return insertSpecialCharacter("\\=H", view);
3049     case 295:
3050         return insertSpecialCharacter("\\=h", view);
3051     case 296:
3052         return insertSpecialCharacter("\\~I", view);
3053     case 297:
3054         return insertSpecialCharacter("\\~i", view);
3055     case 298:
3056         return insertSpecialCharacter("\\=I", view);
3057     case 299:
3058         return insertSpecialCharacter("\\=i", view);
3059     case 300:
3060         return insertSpecialCharacter("\\uI", view);
3061     case 301:
3062         return insertSpecialCharacter("\\ui", view);
3063     case 304:
3064         return insertSpecialCharacter("\\.I", view);
3065     case 305:
3066         return insertSpecialCharacter("\\i", view);
3067     case 308:
3068         return insertSpecialCharacter("\\^J", view);
3069     case 309:
3070         return insertSpecialCharacter("\\^j", view);
3071     case 310:
3072         return insertSpecialCharacter("\\cK", view);
3073     case 311:
3074         return insertSpecialCharacter("\\ck", view);
3075     case 313:
3076         return insertSpecialCharacter("\\'L", view);
3077     case 314:
3078         return insertSpecialCharacter("\\'l", view);
3079     case 315:
3080         return insertSpecialCharacter("\\cL", view);
3081     case 316:
3082         return insertSpecialCharacter("\\cl", view);
3083     case 317:
3084         return insertSpecialCharacter("\\vL", view);
3085     case 318:
3086         return insertSpecialCharacter("\\vl", view);
3087     case 323:
3088         return insertSpecialCharacter("\\'N", view);
3089     case 324:
3090         return insertSpecialCharacter("\\'n", view);
3091     case 325:
3092         return insertSpecialCharacter("\\cN", view);
3093     case 326:
3094         return insertSpecialCharacter("\\cn", view);
3095     case 327:
3096         return insertSpecialCharacter("\\vN", view);
3097     case 328:
3098         return insertSpecialCharacter("\\vn", view);
3099     case 332:
3100         return insertSpecialCharacter("\\=O", view);
3101     case 333:
3102         return insertSpecialCharacter("\\=o", view);
3103     case 334:
3104         return insertSpecialCharacter("\\uO", view);
3105     case 335:
3106         return insertSpecialCharacter("\\uo", view);
3107     case 336:
3108         return insertSpecialCharacter("\\HO", view);
3109     case 337:
3110         return insertSpecialCharacter("\\Ho", view);
3111     case 338:
3112         return insertSpecialCharacter("\\OE", view);
3113     case 339:
3114         return insertSpecialCharacter("\\oe", view);
3115     case 340:
3116         return insertSpecialCharacter("\\'R", view);
3117     case 341:
3118         return insertSpecialCharacter("\\'r", view);
3119     case 342:
3120         return insertSpecialCharacter("\\cR", view);
3121     case 343:
3122         return insertSpecialCharacter("\\cr", view);
3123     case 344:
3124         return insertSpecialCharacter("\\vR", view);
3125     case 345:
3126         return insertSpecialCharacter("\\vr", view);
3127     case 346:
3128         return insertSpecialCharacter("\\'S", view);
3129     case 347:
3130         return insertSpecialCharacter("\\'s", view);
3131     case 348:
3132         return insertSpecialCharacter("\\^S", view);
3133     case 349:
3134         return insertSpecialCharacter("\\^s", view);
3135     case 350:
3136         return insertSpecialCharacter("\\cS", view);
3137     case 351:
3138         return insertSpecialCharacter("\\cs", view);
3139     case 352:
3140         return insertSpecialCharacter("\\vS", view);
3141     case 353:
3142         return insertSpecialCharacter("\\vs", view);
3143     case 354:
3144         return insertSpecialCharacter("\\cT", view);
3145     case 355:
3146         return insertSpecialCharacter("\\ct", view);
3147     case 356:
3148         return insertSpecialCharacter("\\vT", view);
3149     case 357:
3150         return insertSpecialCharacter("\\vt", view);
3151     case 358:
3152         return insertSpecialCharacter("\\=T", view);
3153     case 359:
3154         return insertSpecialCharacter("\\=t", view);
3155     case 360:
3156         return insertSpecialCharacter("\\~U", view);
3157     case 361:
3158         return insertSpecialCharacter("\\~u", view);
3159     case 362:
3160         return insertSpecialCharacter("\\=U", view);
3161     case 363:
3162         return insertSpecialCharacter("\\=u", view);
3163     case 364:
3164         return insertSpecialCharacter("\\uU", view);
3165     case 365:
3166         return insertSpecialCharacter("\\uu", view);
3167     case 366:
3168         return insertSpecialCharacter("\\AU", view);
3169     case 367:
3170         return insertSpecialCharacter("\\Au", view);
3171     case 368:
3172         return insertSpecialCharacter("\\HU", view);
3173     case 369:
3174         return insertSpecialCharacter("\\Hu", view);
3175     case 370:
3176         return insertSpecialCharacter("\\cU", view);
3177     case 371:
3178         return insertSpecialCharacter("\\cu", view);
3179     case 372:
3180         return insertSpecialCharacter("\\^W", view);
3181     case 373:
3182         return insertSpecialCharacter("\\^w", view);
3183     case 374:
3184         return insertSpecialCharacter("\\^Y", view);
3185     case 375:
3186         return insertSpecialCharacter("\\^y", view);
3187     case 376:
3188         return insertSpecialCharacter("\\\"Y", view);
3189     case 377:
3190         return insertSpecialCharacter("\\'Z", view);
3191     case 378:
3192         return insertSpecialCharacter("\\'z", view);
3193     case 379:
3194         return insertSpecialCharacter("\\.Z", view);
3195     case 380:
3196         return insertSpecialCharacter("\\.z", view);
3197     case 381:
3198         return insertSpecialCharacter("\\vZ", view);
3199     case 382:
3200         return insertSpecialCharacter("\\vz", view);
3201     default:
3202         return false;
3203     }
3204 }
3205 
3206 // If allowed, inserts texString at current cursor postition. Startlingly similar to insertDoubleQuotes.
3207 bool EditorExtension::insertSpecialCharacter(const QString& texString, KTextEditor::View *view, const QString& dep)
3208 {
3209     // stop if special character replacement is disabled
3210     if (!m_specialCharacters) {
3211         return false;
3212     }
3213 
3214     // return false if konsole has focus
3215     if(m_ki->texKonsole()->hasFocus()) {
3216         return false;
3217     }
3218 
3219     // always return true for event handler
3220     view = determineView(view);
3221     if(!view) {
3222         return true;
3223     }
3224 
3225     KTextEditor::Document *doc = view->document();
3226 
3227     // Only change if we have a tex document
3228     if(!doc || !m_ki->extensions()->isTexFile(doc->url())) {
3229         return false;
3230     }
3231 
3232     // In case of replace
3233     view->removeSelectionText();
3234 
3235     int row, col;
3236     KTextEditor::Cursor cursor = view->cursorPosition();
3237     row = cursor.line();
3238     col = cursor.column();
3239 
3240     // insert texString
3241     doc->insertText(KTextEditor::Cursor(row, col), texString);
3242 
3243     KILE_DEBUG_MAIN << "Replacing with "<<texString;
3244 
3245     // Check dependency
3246     if (!dep.isEmpty()) {
3247         QStringList packagelist = m_ki->allPackages();
3248         if(!packagelist.contains(dep)) {
3249             m_ki->errorHandler()->printMessage(KileTool::Error, i18n("You have to include the package %1 to use %2.", dep, texString), i18n("Missing Package"));
3250             KILE_DEBUG_MAIN << "Need package "<< dep;
3251         }
3252     }
3253 
3254     return true;
3255 }
3256 
3257 //////////////////// insert tabulator ////////////////////
3258 
3259 void EditorExtension::insertIntelligentTabulator(KTextEditor::View *view)
3260 {
3261     view = determineView(view);
3262     if(!view) {
3263         return;
3264     }
3265 
3266     int row, col, currentRow, currentCol;
3267     QString envname,tab;
3268     QString prefix = " ";
3269 
3270     KTextEditor::Cursor cursor = view->cursorPosition();
3271     currentRow = cursor.line();
3272     currentCol = cursor.column();
3273     if(findOpenedEnvironment(row, col, envname, view)) {
3274         // look if this is an environment with tabs
3275         tab = m_latexCommands->getTabulator(envname);
3276 
3277         // try to align tabulator with textline above
3278         if(currentRow >= 1) {
3279             int tabpos = view->document()->line(currentRow - 1).indexOf('&', currentCol);
3280             if(tabpos >= 0) {
3281                 currentCol = tabpos;
3282                 prefix.clear();
3283             }
3284         }
3285     }
3286 
3287     if(tab.isEmpty()) {
3288         tab = '&';
3289     }
3290     tab = prefix + tab + ' ';
3291 
3292     view->document()->insertText(KTextEditor::Cursor(currentRow, currentCol), tab);
3293     view->setCursorPosition(KTextEditor::Cursor(currentRow, currentCol + tab.length()));
3294 }
3295 
3296 //////////////////// autocomplete environment ////////////////////
3297 
3298 // should we complete the current environment (call from LaTeXEventFilter)
3299 
3300 bool EditorExtension::eventInsertEnvironment(KTextEditor::View *view)
3301 {
3302     if(!view) {
3303         return false;
3304     }
3305 
3306     // don't complete environment, if we are
3307     // still working inside the completion box
3308     KTextEditor::CodeCompletionInterface *codeCompletionInterface
3309         = qobject_cast<KTextEditor::CodeCompletionInterface*>(view);
3310     if(codeCompletionInterface && codeCompletionInterface->isCompletionActive()) {
3311         return false;
3312     }
3313 
3314     int row = view->cursorPosition().line();
3315     int col = view->cursorPosition().column();
3316     QString line = view->document()->line(row).left(col);
3317     int lineLength = view->document()->line(row).length();
3318 
3319     int pos = m_regexpEnter.indexIn(line);
3320     if (pos != -1) {
3321         line = m_regexpEnter.cap(1);
3322         for(int i = 0; i < line.length(); ++i) {
3323             if(!line[i].isSpace()) {
3324                 line[i] = ' ';
3325             }
3326         }
3327 
3328         QString envname, endenv;
3329         if(m_regexpEnter.cap(2) == "\\[") {
3330             envname = m_regexpEnter.cap(2);
3331             endenv = "\\]";
3332         }
3333         else {
3334             envname = m_regexpEnter.cap(4);
3335             endenv = m_regexpEnter.cap(2).replace("\\begin","\\end");
3336         }
3337 
3338         if (col < lineLength) {
3339             //If the cursor was not at the end of the line when the user pressed enter, insert a newline after \end{env}
3340             endenv = endenv + '\n' + line;
3341         }
3342 
3343         if(shouldCompleteEnv(envname, view)) {
3344             QString item =  m_latexCommands->isListEnv(envname) ? "\\item " : QString();
3345             view->document()->insertText(KTextEditor::Cursor(row, col), '\n' + line + m_envAutoIndent + item + '\n' + line + endenv);
3346             view->setCursorPosition(KTextEditor::Cursor(row + 1, line.length() + m_envAutoIndent.length() + item.length()));
3347             return true;
3348         }
3349     }
3350     return false;
3351 }
3352 
3353 bool EditorExtension::shouldCompleteEnv(const QString &env, KTextEditor::View *view)
3354 {
3355     KILE_DEBUG_MAIN << "===EditorExtension::shouldCompleteEnv( " << env << " )===";
3356     QRegExp reTestBegin,reTestEnd;
3357     if(env == "\\[") {
3358         KILE_DEBUG_MAIN << "display style";
3359         reTestBegin.setPattern("(?:[^\\\\]|^)\\\\\\[");
3360         // the first part is a non-capturing bracket (?:...) and we check if we don't have a backslash in front,
3361         //  or that we are at the begin of the line
3362         reTestEnd.setPattern("(?:[^\\\\]|^)\\\\\\]");
3363     }
3364     else {
3365         reTestBegin.setPattern("(?:[^\\\\]|^)\\\\begin\\s*\\{" + QRegExp::escape(env) + "\\}");
3366         reTestEnd.setPattern("(?:[^\\\\]|^)\\\\end\\s*\\{" + QRegExp::escape(env) + "\\}");
3367     }
3368 
3369     int num = view->document()->lines();
3370     int numBeginsFound = 0;
3371     int numEndsFound = 0;
3372     KTextEditor::Cursor cursor = view->cursorPosition();
3373     int realLine = cursor.line();
3374 
3375     for(int i = realLine; i < num; ++i) {
3376         numBeginsFound += view->document()->line(i).count(reTestBegin);
3377         numEndsFound += view->document()->line(i).count(reTestEnd);
3378         KILE_DEBUG_MAIN << "line is " << i <<  " numBeginsFound = " << numBeginsFound <<  " , " << "numEndsFound = " << numEndsFound;
3379         if(numEndsFound >= numBeginsFound) {
3380             return false;
3381         }
3382         else if(numEndsFound == 0 && numBeginsFound > 1) {
3383             return true;
3384         }
3385         else if(numBeginsFound > 2 || numEndsFound > 1) {
3386             return true; // terminate the search
3387         }
3388     }
3389 
3390     return true;
3391 }
3392 
3393 QString EditorExtension::getWhiteSpace(const QString &s)
3394 {
3395     QString whitespace = s;
3396 
3397     for(int i = 0; i < whitespace.length(); ++i) {
3398         if(!whitespace[i].isSpace()) {
3399             whitespace[i] = ' ';
3400         }
3401     }
3402     return whitespace;
3403 }
3404 
3405 //////////////////// inside verbatim commands ////////////////////
3406 
3407 bool EditorExtension::insideVerbatim(KTextEditor::View *view)
3408 {
3409     int rowEnv, colEnv;
3410     QString nameEnv;
3411 
3412     if(findOpenedEnvironment(rowEnv, colEnv, nameEnv, view)) {
3413         if(m_latexCommands->isVerbatimEnv(nameEnv)) {
3414             return true;
3415         }
3416     }
3417 
3418     return false;
3419 }
3420 
3421 bool EditorExtension::insideVerb(KTextEditor::View *view)
3422 {
3423     view = determineView(view);
3424     if(!view) {
3425         return false;
3426     }
3427 
3428     // get current position
3429     int row, col;
3430     KTextEditor::Cursor cursor = view->cursorPosition();
3431     row = cursor.line();
3432     col = cursor.column();
3433 
3434     int startpos = 0;
3435     QString textline = getTextLineReal(view->document(),row);
3436     QRegExp reg("\\\\verb(\\*?)(.)");
3437     while(true) {
3438         int pos = textline.indexOf(reg,startpos);
3439         if(pos < 0 || col < pos + 6 + reg.cap(1).length()) {
3440             return false;
3441         }
3442         pos = textline.indexOf(reg.cap(2), pos + 6 + reg.cap(1).length());
3443         if(pos < 0 || col <= pos) {
3444             return true;
3445         }
3446 
3447         startpos = pos + 1;
3448     }
3449 }
3450 
3451 //////////////////// goto sectioning command ////////////////////
3452 
3453 void EditorExtension::gotoNextSectioning()
3454 {
3455     gotoSectioning(false);
3456 }
3457 
3458 void EditorExtension::gotoPrevSectioning()
3459 {
3460     gotoSectioning(true);
3461 }
3462 
3463 void EditorExtension::gotoSectioning(bool backwards, KTextEditor::View *view)
3464 {
3465     view = determineView(view);
3466     if(!view) {
3467         return;
3468     }
3469 
3470     int rowFound, colFound;
3471     if( view && view->document()->isModified() ) { // after saving, the document structure is the current one, so in this case we don't need to update it
3472         m_ki->viewManager()->updateStructure(true);
3473     }
3474     if(m_ki->structureWidget()->findSectioning(Q_NULLPTR,view->document(), view->cursorPosition().line(), view->cursorPosition().column(), backwards, false, rowFound, colFound)) {
3475         view->setCursorPosition(KTextEditor::Cursor(rowFound, colFound));
3476     }
3477 }
3478 
3479 //////////////////// sectioning popup ////////////////////
3480 
3481 void EditorExtension::sectioningCommand(KileWidget::StructureViewItem *item, int id)
3482 {
3483     KTextEditor::View *view = determineView(Q_NULLPTR);
3484     if(!view) {
3485         return;
3486     }
3487 
3488     if(!item) {
3489         return;
3490     }
3491     KTextEditor::Document *doc = view->document();
3492 
3493     // try to determine the whole secting
3494     // get the start auf the selected sectioning
3495     int row, col, row1, col1, row2, col2;
3496     row = row1 = item->startline() - 1;
3497     col = col1 = item->startcol() - 1;
3498 
3499     // FIXME tbraun make this more clever, introdoce in kiledocinfo a flag which can be easily queried for that, so that we don'
3500     // check, if the document was changed in the meantime
3501     QRegExp reg( "\\\\(part|chapter|section|subsection|subsubsection|paragraph|subparagraph)\\*?\\s*(\\{|\\[)" );
3502     QString textline = getTextLineReal(doc,row1);
3503     if(reg.indexIn(textline, col1) != col1) {
3504         m_ki->errorHandler()->clearMessages();
3505         m_ki->errorHandler()->printMessage(KileTool::Error,
3506                                            i18n("The document was modified and the structure view should be updated, before starting such an operation."),
3507                                            i18n("Structure View Error") );
3508         return;
3509     }
3510 
3511     // increase cursor position and find the following sectioning command
3512     if(!increaseCursorPosition(doc, row, col)) {
3513         return;
3514     }
3515     if (!m_ki->structureWidget()->findSectioning(item, doc, row, col, false, true, row2, col2)) {
3516         // or the end of the document
3517         // if there is a '\end{document} command, we have to exclude it
3518         if (!findEndOfDocument(doc, row, col, row2, col2)) {
3519             row2 = doc->lines() - 1;
3520             col2 = 0;
3521         }
3522     }
3523 
3524     // clear selection and make cursor position visible
3525     view->removeSelection();
3526     view->setCursorPosition(KTextEditor::Cursor(row1, col1));
3527 
3528     QString text;
3529     KTextEditor::Document::EditingTransaction transaction(doc);
3530     switch (id) {
3531     case KileWidget::StructureWidget::SectioningCut:
3532         QApplication::clipboard()->setText(doc->text(KTextEditor::Range(row1, col1, row2, col2)));  // copy -> clipboard
3533         doc->removeText(KTextEditor::Range(row1, col1, row2, col2));                                  // delete
3534         break;
3535     case KileWidget::StructureWidget::SectioningCopy:
3536         QApplication::clipboard()->setText(doc->text(KTextEditor::Range(row1, col1, row2, col2)));  // copy -> clipboard
3537         break;
3538     case KileWidget::StructureWidget::SectioningPaste:
3539         text = QApplication::clipboard()->text();                              // clipboard -> text
3540         if(!text.isEmpty()) {
3541             view->setCursorPosition(KTextEditor::Cursor(row2, col2));                             // insert
3542             view->insertText(text + '\n');
3543         }
3544         break;
3545     case KileWidget::StructureWidget::SectioningSelect:
3546         view->setSelection(KTextEditor::Range(row1, col1, row2, col2));                                // select
3547         break;
3548     case KileWidget::StructureWidget::SectioningDelete:
3549         doc->removeText(KTextEditor::Range(row1, col1, row2, col2));                                  // delete
3550         break;
3551     case KileWidget::StructureWidget::SectioningComment:
3552         commentLaTeX(doc, KTextEditor::Range(row1, col1, row2, col2));
3553         break;
3554     case KileWidget::StructureWidget::SectioningPreview:
3555         view->setSelection(KTextEditor::Range(row1, col1, row2, col2));                               // quick preview
3556         m_ki->quickPreview()->previewSelection(view, false);
3557         view->removeSelection();
3558         break;
3559     }
3560     transaction.finish();
3561 
3562     // update structure view, because it has changed
3563     if(id == KileWidget::StructureWidget::SectioningDelete || id == KileWidget::StructureWidget::SectioningComment) {
3564         m_ki->viewManager()->updateStructure(true);
3565     }
3566 
3567 }
3568 
3569 bool EditorExtension::findEndOfDocument(KTextEditor::Document *doc, int row, int col, int &rowFound, int &colFound)
3570 {
3571     KTextEditor::Range documentRange(KTextEditor::Cursor(row, col), doc->documentEnd());
3572     QVector<KTextEditor::Range> foundRanges = doc->searchText(documentRange, "\\end{document}");
3573 
3574     if(foundRanges.size() >= 1) {
3575         KTextEditor::Range range = foundRanges.first();
3576         if(range.isValid()) {
3577             rowFound = range.start().line();
3578             colFound = range.start().column();
3579             return true;
3580         }
3581     }
3582 
3583     return false;
3584 }
3585 
3586 QString EditorExtension::extractIndentationString(KTextEditor::View *view, int line)
3587 {
3588     KTextEditor::Document* doc = view->document();
3589 
3590     if(!doc) {
3591         return QString();
3592     }
3593 
3594     const QString lineString = doc->line(line);
3595     int lastWhiteSpaceCharIndex = -1;
3596 
3597     for(int i = 0; i < lineString.length(); ++i) {
3598         if(!lineString[i].isSpace()) {
3599             break;
3600         }
3601         ++lastWhiteSpaceCharIndex;
3602     }
3603     return lineString.left(lastWhiteSpaceCharIndex + 1);
3604 }
3605 
3606 }
3607