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: `` ''") 0062 << i18n("French quotes: "< ">") 0063 << i18n("German quotes: "` "'") 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