1 /***********************************************************************************************
2 Copyright (C) 2004-2012 by Holger Danielsson (holger.danielsson@versanet.de)
3 2008-2013 by Michel Ludwig (michel.ludwig@kdemail.net)
4 ***********************************************************************************************/
5
6 /***************************************************************************
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 ***************************************************************************/
14
15 #include "editorextension.h"
16
17 #include <QFileInfo>
18 #include <QClipboard>
19
20 #include <QApplication>
21 #include <KTextEditor/CodeCompletionInterface>
22 #include <KTextEditor/Document>
23 #include <KTextEditor/View>
24 #include <KTextEditor/Range>
25 #include <KTextEditor/Cursor>
26 #include <KLocalizedString>
27
28
29 #include "errorhandler.h"
30 #include "codecompletion.h"
31 #include "kile.h"
32 #include "kileactions.h"
33 #include "kileconfig.h"
34 #include "kileextensions.h"
35 #include "kileinfo.h"
36 #include "kiletool_enums.h"
37 #include "kileviewmanager.h"
38 #include "quickpreview.h"
39 #include "widgets/konsolewidget.h"
40
41 /*
42 * FIXME: The code in this file should be reworked completely. Once we've got a better parser
43 * most of the code in here should also be superfluous.
44 */
45
46 namespace KileDocument
47 {
48
EditorExtension(KileInfo * info)49 EditorExtension::EditorExtension(KileInfo *info) : m_ki(info)
50 {
51 m_latexCommands = m_ki->latexCommands();
52
53 // init regexp
54 m_reg.setPattern("(\\\\(begin|end)\\s*\\{([A-Za-z]+\\*?)\\})|(\\\\\\[|\\\\\\])");
55 // 1 2 3 4
56 m_regexpEnter.setPattern("^(.*)((\\\\begin\\s*\\{([^\\{\\}]*)\\})|(\\\\\\[))");
57 // 1 23 4 5
58
59 // init double quotes
60 m_quoteListI18N // this is shown in the configuration dialog
61 << i18n("English quotes: `` ''")
62 << i18n("French quotes: "< ">")
63 << i18n("German quotes: "` "'")
64 << i18n("French quotes (long): \\flqq \\frqq")
65 << i18n("German quotes (long): \\glqq \\grqq")
66 << i18n("Icelandic quotes (v1): \\ilqq \\irqq")
67 << i18n("Icelandic quotes (v2): \\iflqq \\ifrqq")
68 << i18n("Czech quotes: \\uv{}")
69 << i18n("csquotes package: \\enquote{}");
70
71
72 m_quoteList
73 << QPair<QString, QString>("``", "''")
74 << QPair<QString, QString>("\"<", "\">")
75 << QPair<QString, QString>("\"`", "\"'")
76 << QPair<QString, QString>("\\flqq", "\\frqq")
77 << QPair<QString, QString>("\\glqq", "\\grqq")
78 << QPair<QString, QString>("\\ilqq", "\\irqq")
79 << QPair<QString, QString>("\\iflqq", "\\ifrqq")
80 << QPair<QString, QString>("\\uv{", "}")
81 << QPair<QString, QString>("\\enquote{", "}");
82
83 readConfig();
84 }
85
~EditorExtension()86 EditorExtension::~EditorExtension()
87 {
88 }
89
90 //////////////////// read configuration ////////////////////
91
readConfig()92 void EditorExtension::readConfig()
93 {
94 // init insertion of double quotes
95 initDoubleQuotes();
96
97 // allow special chars?
98 m_specialCharacters = KileConfig::insertSpecialCharacters();
99
100 // calculate indent for autoindent of environments
101 m_envAutoIndent.clear();
102 if(KileConfig::envIndentation()) {
103 if(KileConfig::envIndentSpaces()) {
104 int num = KileConfig::envIndentNumSpaces();
105 if(num < 1 || num > 9) {
106 num = 1;
107 }
108 m_envAutoIndent.fill(' ', num);
109 }
110 else {
111 m_envAutoIndent = "\t";
112 }
113 }
114 }
115
insertTag(const KileAction::TagData & data,KTextEditor::View * view)116 void EditorExtension::insertTag(const KileAction::TagData& data, KTextEditor::View *view)
117 {
118 KTextEditor::Document *doc = view->document();
119 if(!doc) {
120 return;
121 }
122
123 //whether or not to wrap tag around selection
124 bool wrap = !data.tagEnd.isNull() && view->selection();
125
126 //%C before or after the selection
127 bool before = data.tagBegin.count("%C");
128 bool after = data.tagEnd.count("%C");
129
130 //save current cursor position
131 KTextEditor::Cursor cursor = view->cursorPosition();
132 int para = cursor.line();
133 int para_begin = para;
134 int index = cursor.column();
135 int index_begin = index;
136 int para_end = 0;
137 int index_cursor = index;
138 int para_cursor = index;
139 // offset for autoindentation of environments
140 int dxIndentEnv = 0;
141
142 // environment tag
143 bool envtag = data.tagBegin.count("%E") || data.tagEnd.count("%E");
144 QString whitespace = getWhiteSpace( doc->line(para).left(index) );
145
146 //if there is a selection act as if cursor is at the beginning of selection
147 if (wrap) {
148 KTextEditor::Range selectionRange = view->selectionRange();
149 index = selectionRange.start().column();
150 para = selectionRange.start().line();
151 para_end = selectionRange.end().line();
152 }
153
154 QString ins = data.tagBegin;
155 QString tagEnd = data.tagEnd;
156
157 //start an atomic editing sequence
158 KTextEditor::Document::EditingTransaction transaction(doc);
159
160 //cut the selected text
161 QString trailing;
162 if(wrap) {
163 QString sel = view->selectionText();
164 view->removeSelectionText();
165
166 // no autoindentation of environments, when text is selected
167 if(envtag) {
168 ins.remove("%E");
169 tagEnd.remove("%E\n");
170 }
171
172 // strip one of two consecutive line ends
173 int len = sel.length();
174 if(tagEnd.at(0)=='\n' && len > 0 && sel.indexOf('\n',-1) == len - 1) {
175 sel.truncate( len-1 );
176 }
177
178 // now add the selection
179 ins += sel;
180
181 // place the cursor behind this tag, if there is no other wish
182 if(!before && !after) {
183 trailing = "%C";
184 after = true;
185 }
186 }
187 else if(envtag) {
188 ins.replace("%E",whitespace+m_envAutoIndent);
189 tagEnd.replace("%E",whitespace+m_envAutoIndent);
190 if(data.dy > 0) {
191 dxIndentEnv = whitespace.length() + m_envAutoIndent.length();
192 }
193 }
194
195 tagEnd.replace("\\end{",whitespace+"\\end{");
196 ins += tagEnd + trailing;
197
198 //do some replacements
199 QFileInfo fi( doc->url().toLocalFile());
200 ins.replace("%S", fi.completeBaseName());
201 ins.replace("%B", s_bullet);
202
203 //insert first part of tag at cursor position
204 doc->insertText(KTextEditor::Cursor(para, index), ins);
205
206 //move cursor to the new position
207 if(before || after) {
208 int n = data.tagBegin.count("\n")+ data.tagEnd.count("\n");
209 if(wrap) {
210 n += para_end > para ? para_end-para : para-para_end;
211 }
212 for (int line = para_begin; line <= para_begin+n; ++line) {
213 if(doc->line(line).count("%C")) {
214 int i=doc->line(line).indexOf("%C");
215 para_cursor = line;
216 index_cursor = i;
217 doc->removeText(KTextEditor::Range(line, i, line, i+2));
218 break;
219 }
220 index_cursor=index;
221 para_cursor=line;
222 }
223 }
224 else {
225 int py = para_begin, px = index_begin;
226 if(wrap) { //act as if cursor was at beginning of selected text (which is the point where the tagBegin is inserted)
227 py = para;
228 px = index;
229 }
230 para_cursor = py+data.dy;
231 index_cursor = px+data.dx+dxIndentEnv;
232 }
233
234 //end the atomic editing sequence
235 transaction.finish();
236
237 //set the cursor position (it is important that this is done outside of the atomic editing sequence)
238 view->setCursorPosition(KTextEditor::Cursor(para_cursor, index_cursor));
239
240 view->removeSelection();
241 }
242
243 //////////////////// goto environment tag (begin or end) ////////////////////
244
245 // goto the next non-nested environment tag
246
determineView(KTextEditor::View * view)247 KTextEditor::View* EditorExtension::determineView(KTextEditor::View *view)
248 {
249 if (!view) {
250 view = m_ki->viewManager()->currentTextView();
251 }
252
253 m_overwritemode = (!view) ? false : (view->viewMode() == KTextEditor::View::NormalModeOverwrite);
254
255 return view;
256 }
257
gotoEnvironment(bool backwards,KTextEditor::View * view)258 void EditorExtension::gotoEnvironment(bool backwards, KTextEditor::View *view)
259 {
260 view = determineView(view);
261 if(!view) {
262 return;
263 }
264
265 uint row,col;
266 EnvData env;
267 bool found;
268
269 // get current position
270 KTextEditor::Document *doc = view->document();
271 KTextEditor::Cursor cursor = view->cursorPosition();
272 row = cursor.line();
273 col = cursor.column();
274
275
276 // start searching
277 if(backwards) {
278 found = findBeginEnvironment(doc,row,col,env);
279 //KILE_DEBUG_MAIN << " goto begin env: " << env.row << "/" << env.col;
280
281 }
282 else {
283 found = findEndEnvironment(doc,row,col,env);
284 env.col += env.len;
285 }
286
287 if(found) {
288 view->setCursorPosition(KTextEditor::Cursor(env.row, env.col));
289 }
290 }
291
292 // match the opposite environment tag
293
matchEnvironment(KTextEditor::View * view)294 void EditorExtension::matchEnvironment(KTextEditor::View *view)
295 {
296 view = determineView(view);
297 if(!view) {
298 return;
299 }
300
301 uint row,col;
302 EnvData env;
303
304 // get current position
305 KTextEditor::Document *doc = view->document();
306 KTextEditor::Cursor cursor = view->cursorPosition();
307 row = cursor.line();
308 col = cursor.column();
309
310 // we only start, when we are at an environment tag
311 if(!isEnvironmentPosition(doc, row, col, env)) {
312 return;
313 }
314
315 gotoEnvironment(env.tag != EnvBegin, view);
316 }
317
318 //////////////////// close opened environments ////////////////////
319
320 // search for the last opened environment and close it
321
closeEnvironment(KTextEditor::View * view)322 void EditorExtension::closeEnvironment(KTextEditor::View *view)
323 {
324 view = determineView(view);
325 if(!view) {
326 return;
327 }
328
329 int row, col, currentRow, currentCol;
330 QString name;
331
332 KTextEditor::Cursor cursor = view->cursorPosition();
333 currentRow = cursor.line();
334 currentCol = cursor.column();
335
336 if(findOpenedEnvironment(row, col, name, view)) {
337 if(name == "\\[") {
338 view->document()->insertText(KTextEditor::Cursor(currentRow, currentCol), "\\]");
339 }
340 else {
341 view->document()->insertText(KTextEditor::Cursor(currentRow, currentCol), "\\end{" + name + '}');
342 }
343 // view->setCursorPosition(KTextEditor::Cursor(row + 1, 0));
344 }
345 }
346
347 // close all opened environments
348
closeAllEnvironments(KTextEditor::View * view)349 void EditorExtension::closeAllEnvironments(KTextEditor::View *view)
350 {
351 view = determineView(view);
352 if(!view) {
353 return;
354 }
355
356 QStringList envlist = findOpenedEnvironmentList(view, true);
357 if(envlist.count() == 0) {
358 return;
359 }
360
361 int currentRow, currentCol, outputCol;
362 KTextEditor::Document *doc = view->document();
363 KTextEditor::Cursor cursor = view->cursorPosition();
364 currentRow = cursor.line();
365 currentCol = cursor.column();
366
367 bool indent = !m_envAutoIndent.isEmpty();
368 if(indent && currentCol > 0) {
369 doc->insertText(KTextEditor::Cursor(currentRow, currentCol),"\n");
370 currentRow++;
371 currentCol = 0;
372 }
373
374 bool ok1,ok2;
375 for(QStringList::Iterator it = envlist.begin(); it != envlist.end(); ++it) {
376 QStringList entry = (*it).split(',');
377 if(entry[0] == "document") {
378 break;
379 }
380
381 int row = entry[1].toInt(&ok1);
382 int col = entry[2].toInt(&ok2);
383 if(!ok1 || !ok2) {
384 continue;
385 }
386
387 outputCol = currentCol;
388 if(indent) {
389 QString whitespace = getWhiteSpace( doc->line(row).left(col) );
390 doc->insertText(KTextEditor::Cursor(currentRow, outputCol), whitespace);
391 outputCol += whitespace.length();
392 }
393 QString endtag = ( entry[0] == "\\[" ) ? "\\]\n" : "\\end{"+entry[0]+"}\n";
394 doc->insertText(KTextEditor::Cursor(currentRow, outputCol), endtag);
395 ++currentRow;
396 }
397 }
398
399 //////////////////// mathgroup ////////////////////
400
selectMathgroup(KTextEditor::View * view)401 void EditorExtension::selectMathgroup(KTextEditor::View *view)
402 {
403 view = determineView(view);
404 if(!view) {
405 return;
406 }
407
408 KTextEditor::Range range = mathgroupRange(view);
409 if(range.isValid()) {
410 view->setSelection(range);
411 }
412 }
413
deleteMathgroup(KTextEditor::View * view)414 void EditorExtension::deleteMathgroup(KTextEditor::View *view)
415 {
416 view = determineView(view);
417 if(!view) {
418 return;
419 }
420
421 KTextEditor::Range range = mathgroupRange(view);
422 if(range.isValid()) {
423 deleteRange(range,view);
424 }
425 }
426
hasMathgroup(KTextEditor::View * view)427 bool EditorExtension::hasMathgroup(KTextEditor::View *view)
428 {
429 // view will be checked in mathgroupRange()
430 KTextEditor::Range range = mathgroupRange(view);
431 return (range.isValid()) ? true : false;
432 }
433
getMathgroupText(KTextEditor::View * view)434 QString EditorExtension::getMathgroupText(KTextEditor::View *view)
435 {
436 view = determineView(view);
437 if(!view) {
438 return QString();
439 }
440
441 KTextEditor::Range range = mathgroupRange(view);
442 return (range.isValid()) ? view->document()->text(range) : QString();
443 }
444
getMathgroupText(uint & row,uint & col,KTextEditor::View * view)445 QString EditorExtension::getMathgroupText(uint &row, uint &col, KTextEditor::View *view)
446 {
447 int row1, col1, row2, col2;
448
449 view = determineView(view);
450 if(view && getMathgroup(view, row1, col1, row2, col2)) {
451 row = row1;
452 col = col1;
453 return view->document()->text(KTextEditor::Range(row1, col1, row2, col2));
454 }
455 else {
456 return QString();
457 }
458 }
459
mathgroupRange(KTextEditor::View * view)460 KTextEditor::Range EditorExtension::mathgroupRange(KTextEditor::View *view)
461 {
462 view = determineView(view);
463 if(!view) {
464 return KTextEditor::Range::invalid();
465 }
466
467 int row1, col1, row2, col2;
468 if(getMathgroup(view, row1, col1, row2, col2)) {
469 return KTextEditor::Range(row1, col1, row2, col2);
470 }
471 else {
472 return KTextEditor::Range::invalid();
473 }
474 }
475
getMathgroup(KTextEditor::View * view,int & row1,int & col1,int & row2,int & col2)476 bool EditorExtension::getMathgroup(KTextEditor::View *view, int &row1, int &col1, int &row2, int &col2)
477 {
478 int row, col, r, c;
479 MathData begin, end;
480
481 KTextEditor::Document *doc = view->document();
482 KTextEditor::Cursor cursor = view->cursorPosition();
483 row = cursor.line();
484 col = cursor.column();
485
486 QString textline = getTextLineReal(doc,row);
487
488 // check for '\ensuremath{...}'
489 QString word;
490 int x1, x2;
491 if(getCurrentWord(doc, row, col, smTex, word, x1, x2) && word == "\\ensuremath") {
492 view->setCursorPosition(KTextEditor::Cursor(row, x2));
493 }
494
495 BracketData open,close;
496 if(getTexgroup(false, open, close, view)) {
497 QString s = getTextLineReal(doc,open.row);
498 if(open.col >= 11 && s.mid(open.col - 11, 11) == "\\ensuremath") {
499 view->setCursorPosition(KTextEditor::Cursor(row, col));
500 row1 = open.row;
501 col1 = open.col-11;
502 row2 = close.row;
503 col2 = close.col;
504 return true;
505 }
506 }
507
508 // do we need to restore the cursor position
509 view->setCursorPosition(KTextEditor::Cursor(row, col));
510
511 // '$' is difficult, because it is used as opening and closing tag
512 int mode = 0;
513 if(textline[col] == '$') {
514 mode = 1;
515 }
516 else if(col > 0 && textline[col - 1] == '$') {
517 mode = 2;
518 }
519
520 if(mode > 0) {
521 // first look, if this is a closing '$'
522 r = row;
523 c = (mode == 1) ? col : col - 1;
524 if(decreaseCursorPosition(doc, r, c) && findOpenMathTag(doc, r, c, begin)) {
525 if(begin.tag == mmMathDollar && (begin.numdollar & 1)) {
526 row1 = begin.row;
527 col1 = begin.col;
528 row2 = row;
529 col2 = (mode == 1) ? col + 1 : col;
530 return true;
531 }
532 }
533
534 // perhaps an opening '$'
535 r = row;
536 c = (mode == 1) ? col+1 : col;
537 if(findCloseMathTag(doc, r, c, end)) {
538 if(end.tag == mmMathDollar) {
539 row1 = row;
540 col1 = ( mode == 1 ) ? col : col-1;
541 row2 = end.row;
542 col2 = end.col + end.len;
543 return true;
544 }
545 }
546
547 // found no mathgroup with '$'
548 return false;
549 }
550
551 // now let's search for all other math tags:
552 // if a mathgroup tag starts in the current column, we save this
553 // position and move the cursor one column to the right
554 bool openingtag = isOpeningMathTagPosition(doc, row, col, begin);
555 if(openingtag) {
556 // try to find the corresponding closing tag at the right
557 bool closetag = findCloseMathTag(doc, row, col + 1, end);
558 if(closetag && checkMathtags(begin, end)) {
559 row1 = begin.row;
560 col1 = begin.col;
561 row2 = end.row;
562 col2 = end.col + end.len;
563 return true;
564 }
565 }
566
567 r = row;
568 c = col;
569 bool closingtag = isClosingMathTagPosition(doc, row, col, end);
570 if(closingtag) {
571 c = end.col;
572 if(!decreaseCursorPosition(doc, r, c)) {
573 return false;
574 }
575 }
576
577 // now try to search to opening tag of the math group
578 if(!findOpenMathTag(doc, r, c, begin)) {
579 return false;
580 }
581
582 if(begin.tag == mmMathDollar && !(begin.numdollar & 1)) {
583 //KILE_DEBUG_MAIN << "error: even number of '$' --> no math mode" ;
584 return false;
585 }
586
587 // and now the closing tag
588 if(!findCloseMathTag(doc, r, c, end)) {
589 return false;
590 }
591
592 // both tags were found, but they must be of the same type
593 if(checkMathtags(begin, end)) {
594 row1 = begin.row;
595 col1 = begin.col;
596 row2 = end.row;
597 col2 = end.col + end.len;
598 return true;
599 }
600 else {
601 return false;
602 }
603 }
604
605 //////////////////// mathgroup tags ////////////////////
606
checkMathtags(const MathData & begin,const MathData & end)607 bool EditorExtension::checkMathtags(const MathData &begin,const MathData &end)
608 {
609 // both tags were found, but they must be of the same type
610 if(begin.tag != end.tag) {
611 //KILE_DEBUG_MAIN << "error: opening and closing tag of mathmode don't match: " << begin.tag << " - " << end.tag;
612 return false;
613 }
614
615 // and additionally: if it is a math env, both tags must have the same name
616 if(begin.tag == mmDisplaymathEnv && begin.envname != end.envname) {
617 //KILE_DEBUG_MAIN << "error: opening and closing env tags have different names: " << begin.envname << " - " << end.envname;
618 return false;
619 }
620
621 return true;
622 }
623
isOpeningMathTagPosition(KTextEditor::Document * doc,uint row,uint col,MathData & mathdata)624 bool EditorExtension::isOpeningMathTagPosition(KTextEditor::Document *doc, uint row, uint col, MathData &mathdata)
625 {
626 QString textline = getTextLineReal(doc,row);
627
628 QRegExp reg("\\\\begin\\s*\\{([A-Za-z]+\\*?)\\}|\\\\\\[|\\\\\\(");
629 if((int)col != reg.indexIn(textline, col)) {
630 return false;
631 }
632
633 QChar id = reg.cap(0)[1];
634 QString envname = reg.cap(1);
635
636 mathdata.row = row;
637 mathdata.col = col;
638 mathdata.len = reg.cap(0).length();
639
640 switch(id.unicode()) {
641 case 'b':
642 if(!(m_latexCommands->isMathEnv(envname) || envname=="math") || m_latexCommands->needsMathMode(envname)) {
643 return false;
644 }
645 mathdata.tag = ( envname=="math" ) ? mmMathEnv : mmDisplaymathEnv;
646 mathdata.envname = envname;
647 break;
648 case '[':
649 mathdata.tag = mmDisplaymathParen;
650 break;
651 case '(':
652 mathdata.tag = mmMathParen;
653 break;
654 }
655
656 return true;
657 }
658
isClosingMathTagPosition(KTextEditor::Document * doc,uint row,uint col,MathData & mathdata)659 bool EditorExtension::isClosingMathTagPosition(KTextEditor::Document *doc, uint row, uint col,MathData &mathdata)
660 {
661 QString textline = doc->line(row);
662
663 QRegExp reg("\\\\end\\s*\\{([A-Za-z]+\\*?)\\}|\\\\\\]|\\\\\\)");
664 int pos = reg.lastIndexIn(textline, col);
665 if(pos < 0 || (int)col > pos + reg.matchedLength()) {
666 return false;
667 }
668
669 QChar id = reg.cap(0)[1];
670 QString envname = reg.cap(1);
671
672 mathdata.row = row;
673 mathdata.col = pos;
674 mathdata.len = reg.cap(0).length();
675
676 switch(id.unicode()) {
677 case 'e':
678 if(!(m_latexCommands->isMathEnv(envname) || envname=="math") || m_latexCommands->needsMathMode(envname)) {
679 return false;
680 }
681 mathdata.tag = ( envname=="math" ) ? mmMathEnv : mmDisplaymathEnv;
682 mathdata.envname = envname;
683 break;
684 case ']':
685 mathdata.tag = mmDisplaymathParen;
686 break;
687 case ')':
688 mathdata.tag = mmMathParen;
689 break;
690 }
691
692 return true;
693 }
694
findOpenMathTag(KTextEditor::Document * doc,int row,int col,MathData & mathdata)695 bool EditorExtension::findOpenMathTag(KTextEditor::Document *doc, int row, int col, MathData &mathdata)
696 {
697 const QString regExpString = "\\$"
698 "|\\\\begin\\s*\\{([A-Za-z]+\\*?)\\}"
699 "|\\\\end\\s*\\{([A-Za-z]+\\*?)\\}"
700 "|\\\\\\[|\\\\\\]"
701 "|\\\\\\(|\\\\\\)";
702
703 QRegExp reg(regExpString);
704 int lastrow = -1, lastcol = -1;
705 QString mathname;
706
707 bool foundDollar = false;
708 uint numDollar = 0;
709
710 QString textline = getTextLineReal(doc, row);
711 int column = col;
712
713 bool continueSearch = true;
714 while(continueSearch) {
715 while((column = reg.lastIndexIn(textline, col)) != -1) {
716 col = column;
717
718 mathdata.row = row;
719 mathdata.col = col;
720 mathdata.len = reg.cap(0).length();
721 mathname = reg.cap(0).left(2);
722
723 // should be better called 'isValidChar()', because it checks for comments
724 // and escaped chars like backslash and dollar in '\\' and '\$'
725 if(mathname == "$") {
726 // count and continue search
727 ++numDollar;
728
729 // but remember the first dollar found backwards
730 if(!foundDollar) {
731 lastrow = row;
732 lastcol = col;
733 foundDollar = true;
734 }
735 }
736 else if(mathname=="\\[" || mathname=="\\(") {
737 // found start of mathmode
738 if(numDollar == 0) {
739 mathdata.tag = ( mathname == "\\[" ) ? mmDisplaymathParen : mmMathParen;
740 mathdata.numdollar = 0;
741 return true;
742 }
743 else {
744 //KILE_DEBUG_MAIN << "error: dollar not allowed in \\[ or \\( mode";
745 return false;
746 }
747 }
748 else if(mathname=="\\]" || mathname=="\\)") {
749 continueSearch = false;
750 break;
751 }
752 else if(mathname=="\\b") {
753 // save name of environment
754 QString envname = reg.cap(1);
755
756 // if we found the opening tag of a math env
757 if(m_latexCommands->isMathEnv(envname) || envname=="math") {
758 if(numDollar > 0) {
759 //KILE_DEBUG_MAIN << "error: dollar not allowed in math env numdollar=" << numDollar;
760 return false;
761 }
762
763 // if this is a math env with its own mathmode, we have found the starting position
764 if(envname == "math") {
765 mathdata.tag = mmMathEnv;
766 mathdata.envname = envname;
767 return true;
768 }
769
770 if(!m_latexCommands->needsMathMode(envname)) {
771 mathdata.tag = mmDisplaymathEnv;
772 mathdata.envname = envname;
773 return true;
774 }
775 }
776 // no math env, we found the opening tag of a normal env
777 else {
778 continueSearch = false;
779 break;
780 }
781 }
782 else if(mathname == "\\e") {
783 QString envname = reg.cap(2);
784
785 // if we found the closing tag of a math env
786 if(m_latexCommands->isMathEnv(envname) || envname == "math") {
787 // if this is a math env with its own mathmode
788 if(!m_latexCommands->needsMathMode(envname) || envname == "math") {
789 continueSearch = false;
790 break;
791 }
792
793 // if this is a math env which needs $..$
794 if(m_latexCommands->isMathModeEnv(envname)) {
795 if(numDollar >= 1) {
796 --numDollar;
797 continueSearch = false;
798 break;
799 }
800 // else continue search
801 }
802 }
803 // if we found the closing tag of a normal env
804 else {
805 continueSearch = false;
806 break;
807 }
808 }
809 else {
810 //KILE_DEBUG_MAIN << "error: unknown match";
811 return false;
812 }
813
814 // continue search one column left of the last match (if this is possible)
815 if(col == 0) {
816 break;
817 }
818
819 --col;
820 }
821
822 if(row > 0) {
823 textline = getTextLineReal(doc,--row);
824 col = textline.length();
825 }
826 else if(column == -1) {
827 continueSearch = false;
828 break;
829 }
830 }
831
832 // nothing else found, so math mode starts a the last dollar (the first one found backwards)
833 mathdata.row = lastrow;
834 mathdata.col = lastcol;
835 mathdata.len = 1;
836 mathdata.numdollar = numDollar;
837
838 mathdata.tag = (numDollar > 0) ? mmMathDollar : mmNoMathMode;
839
840 return true;
841 }
842
findCloseMathTag(KTextEditor::Document * doc,int row,int col,MathData & mathdata)843 bool EditorExtension::findCloseMathTag(KTextEditor::Document *doc, int row, int col, MathData &mathdata)
844 {
845 const QString regExpString = "\\$"
846 "|\\\\begin\\s*\\{([A-Za-z]+\\*?)\\}"
847 "|\\\\end\\s*\\{([A-Za-z]+\\*?)\\}"
848 "|\\\\\\[|\\\\\\]"
849 "|\\\\\\(|\\\\\\)";
850
851 // + int rowFound, colFound;
852 // + QRegExp reg(regExpString);
853 // + reg.setCaseSensitivity(Qt::CaseInsensitive);
854 // + int lastMatch = 0;
855
856 int rowFound, colFound;
857 QRegExp reg(regExpString);
858
859 KTextEditor::Range searchRange = KTextEditor::Range(KTextEditor::Cursor(row, col), doc->documentEnd());
860
861 while(true) {
862 QVector<KTextEditor::Range> foundRanges = doc->searchText(searchRange, regExpString, KTextEditor::Regex | KTextEditor::CaseInsensitive);
863 if(foundRanges.isEmpty() || (foundRanges.size() == 1 && !foundRanges.first().isValid())) {
864 break;
865 }
866
867 //KILE_DEBUG_MAIN << "number of ranges " << foundRanges.count();
868 if(foundRanges.size() < 3) {
869 break;
870 }
871
872 KTextEditor::Range range = foundRanges.first();
873 //KILE_DEBUG_MAIN << "found math tag: " << doc->text(range);
874 if(!range.isValid()) {
875 break;
876 }
877
878 rowFound = range.start().line();
879 colFound = range.start().column();
880 QString textFound = doc->text(range);
881
882 // should be better called 'isValidChar()', because it checks for comments
883 // and escaped chars like backslash and dollar in '\\' and '\$'
884 if(isValidBackslash(doc, rowFound, colFound)) {
885 QString mathname = textFound.left(2);
886
887 // always remember behind the last match
888 mathdata.row = rowFound;
889 mathdata.col = colFound;
890 mathdata.len = textFound.length();
891
892 if(mathname=="$") {
893 mathdata.tag = mmMathDollar;
894 return true;
895 }
896 else if(mathname=="\\]") {
897 mathdata.tag = mmDisplaymathParen;
898 return true;
899 }
900 else if(mathname=="\\)") {
901 mathdata.tag = mmMathParen;
902 return true;
903 }
904 else if(mathname=="\\[" || mathname=="\\(") {
905 //KILE_DEBUG_MAIN << "error: current mathgroup was not closed";
906 return false;
907 }
908 else if(mathname=="\\b") {
909 QString envname = doc->text(foundRanges[1]);
910 if(!(m_latexCommands->isMathEnv(envname) || envname=="math")) {
911 //KILE_DEBUG_MAIN << "error: only math env are allowed in mathmode (found begin tag)";
912 return false;
913 }
914
915 if(!m_latexCommands->needsMathMode(envname) || envname=="math") {
916 //KILE_DEBUG_MAIN << "error: mathenv with its own mathmode are not allowed in mathmode ";
917 return false;
918 }
919
920 // else continue search
921 }
922 else if(mathname == "\\e") {
923 QString envname = doc->text(foundRanges[2]);
924 if(!(m_latexCommands->isMathEnv(envname) || envname=="math")) {
925 //KILE_DEBUG_MAIN << "error: only math env are allowed in mathmode (found end tag)";
926 return false;
927 }
928
929 if(envname == "math") {
930 mathdata.tag = mmMathEnv;
931 mathdata.envname = envname;
932 return true;
933 }
934
935 if(!m_latexCommands->needsMathMode(envname)) {
936 mathdata.tag = mmDisplaymathEnv;
937 mathdata.envname = envname;
938 return true;
939 }
940
941 // else continue search
942 }
943 }
944
945 // go ahead
946 searchRange = KTextEditor::Range(foundRanges.first().end(), doc->documentEnd());
947 }
948
949 //KILE_DEBUG_MAIN << "not found anything";
950 return false;
951 }
952
953
954
955 //////////////////// insert newlines inside an environment ////////////////////
956
957 // intelligent newlines: look for the last opened environment
958 // and decide what to insert
959 // or continue the comment
960
insertIntelligentNewline(KTextEditor::View * view)961 void EditorExtension::insertIntelligentNewline(KTextEditor::View *view)
962 {
963 KILE_DEBUG_MAIN << view;
964
965 view = determineView(view);
966
967 if(!view) {
968 return;
969 }
970
971 KTextEditor::Document* doc = view->document();
972
973 if(!doc) {
974 return;
975 }
976
977 QString name;
978 KTextEditor::Cursor cursor = view->cursorPosition();
979 int row = cursor.line();
980 int col = cursor.column();
981
982 QString newLineAndIndentationString = '\n' + extractIndentationString(view, row);
983
984 if(isCommentPosition(doc, row, col)) {
985 KILE_DEBUG_MAIN << "found comment";
986 view->insertText(newLineAndIndentationString + "% ");
987 moveCursorToLastPositionInCurrentLine(view);
988 return;
989 }
990 else if(findOpenedEnvironment(row, col, name, view)) {
991 if(m_latexCommands->isListEnv(name)) {
992 if ( name == "description" ) {
993 view->insertText(newLineAndIndentationString + "\\item[]");
994 }
995 else {
996 view->insertText(newLineAndIndentationString + "\\item ");
997 }
998 moveCursorToLastPositionInCurrentLine(view);
999 return;
1000 }
1001 else if(m_latexCommands->isTabularEnv(name) || m_latexCommands->isMathEnv(name)) {
1002 view->insertText(newLineAndIndentationString + "\\\\");
1003 moveCursorToLastPositionInCurrentLine(view);
1004 return;
1005 }
1006 }
1007 // - no comment position
1008 // - found no opened environment
1009 // - unknown environment
1010 // - finish tabular or math environment
1011 view->insertText(newLineAndIndentationString);
1012 moveCursorToLastPositionInCurrentLine(view);
1013 }
1014
findOpenedEnvironment(int & row,int & col,QString & envname,KTextEditor::View * view)1015 bool EditorExtension::findOpenedEnvironment(int &row, int &col, QString &envname, KTextEditor::View *view)
1016 {
1017 view = determineView(view);
1018 if(!view) {
1019 return false;
1020 }
1021
1022 // get current cursor position
1023 KTextEditor::Document *doc = view->document();
1024 KTextEditor::Cursor cursor = view->cursorPosition();
1025 row = cursor.line();
1026 col = cursor.column();
1027
1028 EnvData env;
1029 int startrow = row;
1030 int startcol = col;
1031
1032 //KILE_DEBUG_MAIN << " close - start ";
1033 // accept a starting place outside an environment
1034 bool env_position = isEnvironmentPosition(doc, row, col, env);
1035
1036 // We can also accept a column, if we are on the left side of an environment.
1037 // But we should decrease the current cursor position for the search.
1038 if(env_position && env.cpos != EnvInside) {
1039 if(env.cpos == EnvLeft && !decreaseCursorPosition(doc, startrow, startcol)) {
1040 return false;
1041 }
1042 env_position = false;
1043 }
1044
1045 if(!env_position && findEnvironmentTag(doc, startrow, startcol, env, true)) {
1046 //KILE_DEBUG_MAIN << " close - found begin env at: " << env.row << "/" << env.col << " " << env.name;
1047 row = env.row;
1048 col = env.col;
1049 envname = env.name;
1050 return true;
1051 }
1052 else {
1053 return false;
1054 }
1055 }
1056
findOpenedEnvironmentList(KTextEditor::View * view,bool position)1057 QStringList EditorExtension::findOpenedEnvironmentList(KTextEditor::View *view, bool position)
1058 {
1059 QStringList envlist;
1060
1061 view = determineView(view);
1062 if(view) {
1063 int currentRow, currentCol;
1064 KTextEditor::Document *doc = view->document();
1065 KTextEditor::Cursor cursor = view->cursorPosition();
1066 currentRow = cursor.line();
1067 currentCol = cursor.column();
1068
1069
1070 int row = currentRow;
1071 int col = currentCol;
1072 EnvData env;
1073
1074 // check the starting position
1075 bool env_position = isEnvironmentPosition(doc, row, col, env);
1076 if(env_position) {
1077 // we are inside an environment tag: bad to complete
1078 if(env.cpos == EnvInside) {
1079 return envlist;
1080 }
1081 // we are left of an environment tag: go one position to the left
1082 if(env.cpos == EnvLeft) {
1083 if (!decreaseCursorPosition(doc, row, col)) {
1084 return envlist;
1085 }
1086 }
1087 }
1088
1089 while (findEnvironmentTag(doc, row, col, env, true)) {
1090 row = env.row;
1091 col = env.col;
1092
1093 if(position) {
1094 envlist << env.name + QString(",%1,%2").arg(row).arg(col);
1095 }
1096 else {
1097 envlist << env.name;
1098 }
1099
1100 if(col == 0) {
1101 if (!decreaseCursorPosition(doc, row, col)) {
1102 break;
1103 }
1104 }
1105 view->setCursorPosition(KTextEditor::Cursor(row, col));
1106 }
1107
1108 // reset cursor original position
1109 view->setCursorPosition(KTextEditor::Cursor(currentRow, currentCol));
1110 }
1111
1112 return envlist;
1113 }
1114
1115 //////////////////// select an environment ////////////////////
1116
selectEnvironment(bool inside,KTextEditor::View * view)1117 void EditorExtension::selectEnvironment(bool inside, KTextEditor::View *view)
1118 {
1119 view = determineView(view);
1120 if(!view) {
1121 return;
1122 }
1123
1124 if (!view->selection() || !expandSelectionEnvironment(inside,view)) {
1125 KTextEditor::Range range = environmentRange(inside,view);
1126 if(range.isValid()) {
1127 view->setSelection(range);
1128 }
1129 }
1130 }
1131
deleteEnvironment(bool inside,KTextEditor::View * view)1132 void EditorExtension::deleteEnvironment(bool inside, KTextEditor::View *view)
1133 {
1134 view = determineView(view);
1135 if(!view) {
1136 return;
1137 }
1138
1139 KTextEditor::Range range = environmentRange(inside,view);
1140 if(range.isValid()) {
1141 deleteRange(range,view);
1142 }
1143 }
1144
deleteRange(KTextEditor::Range & range,KTextEditor::View * view)1145 void EditorExtension::deleteRange(KTextEditor::Range &range, KTextEditor::View *view)
1146 {
1147 view->removeSelection();
1148 view->document()->removeText(range);
1149 view->setCursorPosition(range.start());
1150 }
1151
1152 // calculate start and end of an environment
1153
getEnvironment(bool inside,EnvData & envbegin,EnvData & envend,KTextEditor::View * view)1154 bool EditorExtension::getEnvironment(bool inside, EnvData &envbegin, EnvData &envend, KTextEditor::View *view)
1155 {
1156 view = determineView(view);
1157 if(!view) {
1158 return false;
1159 }
1160
1161 int row, col;
1162
1163 KTextEditor::Document *doc = view->document();
1164 KTextEditor::Cursor cursor = view->cursorPosition();
1165 row = cursor.line();
1166 col = cursor.column();
1167 if(!findBeginEnvironment(doc, row, col, envbegin)) {
1168 return false;
1169 }
1170 if(!findEndEnvironment(doc, row, col, envend)) {
1171 return false;
1172 }
1173
1174 if(inside) {
1175 envbegin.col += envbegin.len;
1176 }
1177 else {
1178 envend.col += envend.len;
1179 }
1180
1181 return true;
1182 }
1183
environmentRange(bool inside,KTextEditor::View * view)1184 KTextEditor::Range EditorExtension::environmentRange(bool inside, KTextEditor::View *view)
1185 {
1186 // view will be checked in getEnvironment()
1187 EnvData envbegin, envend;
1188 return (getEnvironment(inside, envbegin, envend, view))
1189 ? KTextEditor::Range(envbegin.row, envbegin.col, envend.row, envend.col)
1190 : KTextEditor::Range::invalid();
1191 }
1192
environmentText(bool inside,KTextEditor::View * view)1193 QString EditorExtension::environmentText(bool inside, KTextEditor::View *view)
1194 {
1195 view = determineView(view);
1196 if(!view) {
1197 return QString();
1198 }
1199
1200 KTextEditor::Range range = environmentRange(inside,view);
1201 return (range.isValid()) ? view->document()->text(range) : QString();
1202 }
1203
environmentName(KTextEditor::View * view)1204 QString EditorExtension::environmentName(KTextEditor::View *view)
1205 {
1206 // view will be checked in getEnvironment()
1207 EnvData envbegin, envend;
1208 return (getEnvironment(false, envbegin, envend, view)) ? envbegin.name : QString();
1209 }
1210
1211 // determine text, startrow and startcol of current environment
1212
getEnvironmentText(int & row,int & col,QString & name,KTextEditor::View * view)1213 QString EditorExtension::getEnvironmentText(int &row, int &col, QString &name, KTextEditor::View *view)
1214 {
1215 view = determineView(view);
1216 if(!view) {
1217 return QString();
1218 }
1219
1220 EnvData envbegin, envend;
1221
1222 if(getEnvironment(false, envbegin, envend, view) && envbegin.name != "document") {
1223 row = envbegin.row;
1224 col = envbegin.col;
1225 name = envbegin.name;
1226 return view->document()->text(KTextEditor::Range(envbegin.row, envbegin.col, envend.row, envend.col));
1227 }
1228 else {
1229 return QString();
1230 }
1231 }
1232
hasEnvironment(KTextEditor::View * view)1233 bool EditorExtension::hasEnvironment(KTextEditor::View *view)
1234 {
1235 view = determineView(view);
1236 if(!view) {
1237 return false;
1238 }
1239
1240 EnvData envbegin,envend;
1241 return (getEnvironment(false, envbegin, envend, view) && envbegin.name != "document");
1242 }
1243
1244 // when an environment is selected (inside or outside),
1245 // the selection is expanded to the surrounding environment
1246
expandSelectionEnvironment(bool inside,KTextEditor::View * view)1247 bool EditorExtension::expandSelectionEnvironment(bool inside, KTextEditor::View *view)
1248 {
1249 KTextEditor::Document *doc = view->document();
1250 if (!view->selection()) {
1251 return false;
1252 }
1253
1254 // get current position
1255 int row, col;
1256 KTextEditor::Cursor cursor = view->cursorPosition();
1257 row = cursor.line();
1258 col = cursor.column();
1259
1260 // get current selection
1261 KTextEditor::Range selectionRange = view->selectionRange();
1262 int row1 = selectionRange.start().line();
1263 int col1 = selectionRange.start().column();
1264 int row2 = selectionRange.end().line();
1265 int col2 = selectionRange.end().column();
1266
1267 // determine current environment outside
1268 EnvData oenvbegin,oenvend;
1269 if(!getEnvironment(false, oenvbegin, oenvend, view)) {
1270 return false;
1271 }
1272
1273 bool newselection = false;
1274 // first look, if this environment is selected outside
1275 if(row1 == oenvbegin.row && col1 == oenvbegin.col && row2 == oenvend.row && col2 == oenvend.col) {
1276
1277 if(!decreaseCursorPosition(doc, oenvbegin.row, oenvbegin.col) ) {
1278 return newselection;
1279 }
1280 view->setCursorPosition(KTextEditor::Cursor(oenvbegin.row, oenvbegin.col));
1281 // search the surrounding environment and select it
1282 if(getEnvironment(inside, oenvbegin, oenvend, view)) {
1283 view->setSelection(KTextEditor::Range(oenvbegin.row, oenvbegin.col, oenvend.row, oenvend.col));
1284 newselection = true;
1285
1286 }
1287 }
1288 else {
1289 // then determine current environment inside
1290 EnvData ienvbegin, ienvend;
1291 getEnvironment(true, ienvbegin, ienvend, view);
1292 // and look, if this environment is selected inside
1293 if(row1 == ienvbegin.row && col1 == ienvbegin.col && row2 == ienvend.row && col2 == ienvend.col) {
1294 if(!decreaseCursorPosition(doc, oenvbegin.row, oenvbegin.col) ) {
1295 return newselection;
1296 }
1297 view->setCursorPosition(KTextEditor::Cursor(oenvbegin.row, oenvbegin.col));
1298 // search the surrounding environment and select it
1299 if(getEnvironment(inside, ienvbegin, ienvend, view)) {
1300 view->setSelection(KTextEditor::Range(ienvbegin.row, ienvbegin.col, ienvend.row, ienvend.col));
1301 newselection = true;
1302 }
1303
1304 }
1305 }
1306
1307 // restore old cursor position
1308 view->setCursorPosition(KTextEditor::Cursor(row, col));
1309 return newselection;
1310 }
1311
1312 //////////////////// search for \begin{env} ////////////////////
1313
1314 // Find the last \begin{env} tag. If the current cursor is over
1315 // - \begin{env} tag: we will stop immediately
1316 // - \end{env} tag: we will start before this tag
1317
findBeginEnvironment(KTextEditor::Document * doc,int row,int col,EnvData & env)1318 bool EditorExtension::findBeginEnvironment(KTextEditor::Document *doc, int row, int col, EnvData &env)
1319 {
1320 // KILE_DEBUG_MAIN << " find begin: ";
1321 if(isEnvironmentPosition(doc, row, col, env)) {
1322 // already found position?
1323 //KILE_DEBUG_MAIN << " found env at: " << env.row << "/" << env.col << " " << env.name;
1324 if(env.tag == EnvBegin) {
1325 //KILE_DEBUG_MAIN << " is begin env at: " << env.row << "/" << env.col << " " << env.name;
1326 return true;
1327 }
1328
1329 // go one position back
1330 //KILE_DEBUG_MAIN << " is end env at: " << env.row << "/" << env.col << " " << env.name;
1331 row = env.row;
1332 col = env.col;
1333 if(!decreaseCursorPosition(doc, row, col)) {
1334 return false;
1335 }
1336 }
1337
1338 // looking back for last environment
1339 //KILE_DEBUG_MAIN << " looking back from pos: " << row << "/" << col << " " << env.name;
1340 return findEnvironmentTag(doc, row, col, env, true);
1341 }
1342
1343 //////////////////// search for \end{env} ////////////////////
1344
1345 // Find the last \end{env} tag. If the current cursor is over
1346 // - \end{env} tag: we will stop immediately
1347 // - \begin{env} tag: we will start behind this tag
1348
findEndEnvironment(KTextEditor::Document * doc,int row,int col,EnvData & env)1349 bool EditorExtension::findEndEnvironment(KTextEditor::Document *doc, int row, int col, EnvData &env)
1350 {
1351 if(isEnvironmentPosition(doc, row, col, env)) {
1352 // already found position?
1353 if(env.tag == EnvEnd ) {
1354 return true;
1355 }
1356
1357 // go one position forward
1358 row = env.row;
1359 col = env.col + 1;
1360 }
1361
1362 // looking forward for the next environment
1363 return findEnvironmentTag(doc, row, col, env, false);
1364 }
1365
1366 //////////////////// search for an environment tag ////////////////////
1367 // find the last/next non-nested environment tag
findEnvironmentTag(KTextEditor::Document * doc,int row,int col,EnvData & env,bool backwards)1368 bool EditorExtension::findEnvironmentTag(KTextEditor::Document *doc, int row, int col, EnvData &env, bool backwards)
1369 {
1370 unsigned int envcount = 0;
1371
1372 KTextEditor::Range searchRange;
1373 if(backwards) {
1374 searchRange = KTextEditor::Range(KTextEditor::Cursor(0, 0), KTextEditor::Cursor(row, col));
1375 }
1376 else {
1377 searchRange = KTextEditor::Range(KTextEditor::Cursor(row, col), doc->documentEnd());
1378 }
1379
1380 KTextEditor::SearchOptions searchOptions = (backwards) ? KTextEditor::Regex | KTextEditor::Backwards : KTextEditor::Regex;
1381
1382 while(true) {
1383 QVector<KTextEditor::Range> foundRanges = doc->searchText(searchRange, m_reg.pattern(), searchOptions);
1384 if(foundRanges.isEmpty() || (foundRanges.size() == 1 && !foundRanges.first().isValid())) {
1385 break;
1386 }
1387
1388 //KILE_DEBUG_MAIN << "number of ranges " << foundRanges.count();
1389
1390 EnvTag wrong_env = (backwards) ? EnvEnd : EnvBegin;
1391
1392 if(foundRanges.size() < 5) {
1393 break;
1394 }
1395
1396 KTextEditor::Range range = foundRanges.first();
1397
1398 if(!range.isValid()) {
1399 //KILE_DEBUG_MAIN << "invalid range found";
1400 break;
1401 }
1402 env.row = range.start().line();
1403 env.col = range.start().column();
1404 env.len = doc->text(range).length();
1405
1406 if(isValidBackslash(doc, env.row, env.col)) {
1407 // index 0 is the fullmatch, 1 first cap and so on
1408 QString cap2 = (foundRanges[2].isValid() ? doc->text(foundRanges[2]) : "");
1409 QString cap3 = (foundRanges[3].isValid() ? doc->text(foundRanges[3]) : "");
1410 QString cap4 = (foundRanges[4].isValid() ? doc->text(foundRanges[4]) : "");
1411 EnvTag found_env = (cap2 == "begin" || cap4 == "\\[") ? EnvBegin : EnvEnd;
1412 if(found_env == wrong_env) {
1413 ++envcount;
1414 }
1415 else {
1416 if(envcount > 0) {
1417 --envcount;
1418 }
1419 else {
1420 if(found_env == EnvBegin) {
1421 env.name = (cap2 == "begin") ? cap3 : "\\[";
1422 }
1423 else {
1424 env.name = (cap2 == "end") ? cap3 : "\\]";
1425 }
1426 env.tag = found_env;
1427 //KILE_DEBUG_MAIN << "found " << env.name;
1428 return true;
1429 }
1430 }
1431 }
1432
1433 // finally, prepare the range for the next search
1434 if(backwards) {
1435 searchRange = KTextEditor::Range(KTextEditor::Cursor(0, 0), foundRanges.first().start());
1436 }
1437 else {
1438 searchRange = KTextEditor::Range(foundRanges.first().end(), doc->documentEnd());
1439 }
1440 }
1441
1442 //KILE_DEBUG_MAIN << "not found anything";
1443 return false;
1444 }
1445
1446 //////////////////// check for an environment position ////////////////////
1447
1448 // Check if the current position belongs to an environment. The result is set
1449 // to the beginning backslash of the environment tag. The same algorithms as
1450 // matching brackets is used.
1451 //
1452 // insert mode: if there is a full tag on the left, always take it
1453 // if not, look to the right
1454 // overwrite mode: always take the tag, which begins at the cursor position
1455 //
1456 // test it with {a}{{b}}{c}
1457
isEnvironmentPosition(KTextEditor::Document * doc,int row,int col,EnvData & env)1458 bool EditorExtension::isEnvironmentPosition(KTextEditor::Document *doc, int row, int col, EnvData &env)
1459 {
1460 // get real textline without comments, quoted characters and pairs of backslashes
1461 QString textline = getTextLineReal(doc, row);
1462
1463 if(col > textline.length()) {
1464 return false;
1465 }
1466
1467 bool left = false;
1468
1469 //KILE_DEBUG_MAIN << "col=" << col;
1470
1471 // check if there is a match in this line from the current position to the left
1472 int startcol = (textline[col] == '\\') ? col - 1 : col;
1473 if(startcol >= 1) {
1474 //KILE_DEBUG_MAIN << "search to the left ";
1475 int pos = textline.lastIndexOf(m_reg, startcol);
1476 env.len = m_reg.matchedLength();
1477 if(pos != -1 && pos < col && col <= pos + env.len) {
1478 //KILE_DEBUG_MAIN << "search to the left: found";
1479 env.row = row;
1480 env.col = pos;
1481 QChar ch = textline.at(pos + 1);
1482 if(ch=='b' || ch=='e') {
1483 env.tag = (ch == 'b') ? EnvBegin : EnvEnd;
1484 env.name = m_reg.cap(3);
1485 }
1486 else {
1487 env.tag = (ch == '[') ? EnvBegin : EnvEnd;
1488 env.name = m_reg.cap(4);
1489 }
1490
1491 if ( !m_overwritemode || (m_overwritemode && col<pos+env.len) ) {
1492 // insert mode: position is inside the tag or behind the tag, which also belongs to the tag
1493 // overwrit emode: position is inside the tag) {
1494 //KILE_DEBUG_MAIN << "search to the left: stop";
1495 return true;
1496 }
1497 // overwritemode: position is behind the tag
1498 left = true;
1499 //KILE_DEBUG_MAIN << "search to the left: left=true, but also look to the right";
1500 }
1501 }
1502
1503 // check if there is a match in this line from the current position to the right
1504 //KILE_DEBUG_MAIN << "search to the right " ;
1505 if (textline[col] == '\\' && col == textline.indexOf(m_reg, col)) {
1506 //KILE_DEBUG_MAIN << "search to the right: found";
1507 env.row = row;
1508 env.col = col;
1509 env.len = m_reg.matchedLength();
1510 QChar ch = textline.at(col+1);
1511 if(ch == 'b' || ch == 'e') { // found "\begin" or "\end"
1512 env.tag = ( ch == 'b' ) ? EnvBegin : EnvEnd;
1513 env.name = m_reg.cap(3);
1514 }
1515 else { // found "\[" or "\\]"
1516 env.tag = (ch == '[') ? EnvBegin : EnvEnd;
1517 env.name = m_reg.cap(4);
1518 }
1519 //KILE_DEBUG_MAIN << "search to the right: stop";
1520 return true;
1521 }
1522
1523 return left;
1524 }
1525
1526 //////////////////// check for a comment ////////////////////
1527
1528 // check if the current position is within a comment
1529
isCommentPosition(KTextEditor::Document * doc,int row,int col)1530 bool EditorExtension::isCommentPosition(KTextEditor::Document *doc, int row, int col)
1531 {
1532 QString textline = doc->line(row);
1533
1534 bool backslash = false;
1535 for(int i = 0; i < col; ++i) {
1536 if(textline[i] == '%') {
1537 if(!backslash) { // found a comment sign
1538 return true;
1539 }
1540 else {
1541 backslash = false;
1542 }
1543 }
1544 else if(textline[i] == '\\') { // count number of backslashes
1545 backslash = !backslash;
1546 }
1547 else {
1548 backslash = false; // no backslash
1549 }
1550 }
1551
1552 return false;
1553 }
1554
1555 // check if the character at text[col] is a valid backslash:
1556 // - there is no comment sign in this line before
1557 // - there is not a odd number of backslashes directly before
1558
isValidBackslash(KTextEditor::Document * doc,int row,int col)1559 bool EditorExtension::isValidBackslash(KTextEditor::Document *doc, int row, int col)
1560 {
1561 QString textline = doc->line(row);
1562
1563 bool backslash = false;
1564 for(int i = 0; i < col; ++i) {
1565 if(textline[i] == '%') {
1566 if(!backslash) {
1567 return false; // found a comment sign
1568 }
1569 else {
1570 backslash = false;
1571 }
1572 }
1573 else if(textline[i] == '\\') { // count number of backslashes
1574 backslash = !backslash;
1575 }
1576 else {
1577 backslash = false; // no backslash
1578 }
1579 }
1580
1581 return !backslash;
1582 }
1583
1584 //////////////////// goto next bullet ////////////////////
1585
gotoBullet(bool backwards,KTextEditor::View * view)1586 void EditorExtension::gotoBullet(bool backwards, KTextEditor::View *view)
1587 {
1588 view = determineView(view);
1589 if(!view) {
1590 return;
1591 }
1592
1593 // get current position
1594 KTextEditor::Document *doc = view->document();
1595
1596 KTextEditor::Cursor cursor = view->cursorPosition();
1597
1598 KTextEditor::SearchOptions searchOptions = (backwards) ? KTextEditor::Backwards : KTextEditor::Default;
1599
1600 KTextEditor::Range searchRange;
1601 if(backwards) {
1602 searchRange = KTextEditor::Range(KTextEditor::Cursor(0, 0), cursor);
1603 }
1604 else {
1605 const KTextEditor::Cursor nextCursorPosition(cursor.line(), cursor.column() + 1);
1606 if((doc->characterAt(cursor) == s_bullet_char) // we are already at a bullet
1607 && view->selection()
1608 && view->selectionRange() == KTextEditor::Range(cursor, nextCursorPosition)) { // which has been 'highlighted'
1609 cursor = nextCursorPosition; // search for the next bullet
1610 }
1611 searchRange = KTextEditor::Range(cursor, doc->documentEnd());
1612 }
1613
1614 QVector<KTextEditor::Range> foundRanges = doc->searchText(searchRange, s_bullet, searchOptions);
1615 if(foundRanges.size() >= 1) {
1616 KTextEditor::Range range = foundRanges.first();
1617 if(range.isValid()) {
1618 int line = range.start().line();
1619 int column = range.start().column();
1620 view->setCursorPosition(KTextEditor::Cursor(line, column));
1621 view->setSelection(KTextEditor::Range(line, column, line, column + 1));
1622 }
1623 }
1624 }
1625
1626 //////////////////// increase/decrease cursor position ////////////////////
1627
moveCursorRight(KTextEditor::View * view)1628 bool EditorExtension::moveCursorRight(KTextEditor::View *view)
1629 {
1630 return moveCursor(view, MoveCursorRight);
1631 }
1632
moveCursorLeft(KTextEditor::View * view)1633 bool EditorExtension::moveCursorLeft(KTextEditor::View *view)
1634 {
1635 return moveCursor(view, MoveCursorLeft);
1636 }
1637
moveCursorUp(KTextEditor::View * view)1638 bool EditorExtension::moveCursorUp(KTextEditor::View *view)
1639 {
1640 return moveCursor(view, MoveCursorUp);
1641 }
1642
moveCursorDown(KTextEditor::View * view)1643 bool EditorExtension::moveCursorDown(KTextEditor::View *view)
1644 {
1645 return moveCursor(view, MoveCursorDown);
1646 }
1647
moveCursor(KTextEditor::View * view,CursorMove direction)1648 bool EditorExtension::moveCursor(KTextEditor::View *view, CursorMove direction)
1649 {
1650 view = determineView(view);
1651 if(!view) {
1652 return false;
1653 }
1654
1655 KTextEditor::Document *doc = view->document();
1656
1657 KTextEditor::Cursor cursor = view->cursorPosition();
1658 int row = cursor.line();
1659 int col = cursor.column();
1660
1661 bool ok = false;
1662 switch (direction) {
1663 case MoveCursorLeft:
1664 ok = decreaseCursorPosition(doc,row,col);
1665 break;
1666 case MoveCursorRight:
1667 ok = increaseCursorPosition(doc,row,col);
1668 break;
1669 case MoveCursorUp:
1670 if(row > 0) {
1671 row--;
1672 ok = true;
1673 }
1674 break;
1675 case MoveCursorDown:
1676 if(row < doc->lines() - 1) {
1677 row++;
1678 ok = true;
1679 }
1680 break;
1681 }
1682
1683 if(ok) {
1684 return view->setCursorPosition(KTextEditor::Cursor(row,col));
1685 }
1686 else {
1687 return false;
1688 }
1689 }
1690
increaseCursorPosition(KTextEditor::Document * doc,int & row,int & col)1691 bool EditorExtension::increaseCursorPosition(KTextEditor::Document *doc, int &row, int &col)
1692 {
1693 bool ok = true;
1694
1695 if(col < doc->lineLength(row) - 1) {
1696 ++col;
1697 }
1698 else if(row < doc->lines() - 1) {
1699 ++row;
1700 col = 0;
1701 }
1702 else {
1703 ok = false;
1704 }
1705
1706 return ok;
1707 }
1708
decreaseCursorPosition(KTextEditor::Document * doc,int & row,int & col)1709 bool EditorExtension::decreaseCursorPosition(KTextEditor::Document *doc, int &row, int &col)
1710 {
1711 bool ok = true;
1712
1713 if(col > 0) {
1714 --col;
1715 }
1716 else if(row > 0) {
1717 --row;
1718 col = doc->lineLength(row);
1719 }
1720 else {
1721 ok = false;
1722 }
1723
1724 return ok;
1725 }
1726
1727 //////////////////// texgroups ////////////////////
1728
1729 // goto the next non-nested bracket
1730
gotoTexgroup(bool backwards,KTextEditor::View * view)1731 void EditorExtension::gotoTexgroup(bool backwards, KTextEditor::View *view)
1732 {
1733 view = determineView(view);
1734 if(!view) return;
1735
1736 uint row,col;
1737 bool found;
1738 BracketData bracket;
1739
1740 // get current position
1741 KTextEditor::Document *doc = view->document();
1742 KTextEditor::Cursor cursor = view->cursorPosition();
1743 row = cursor.line();
1744 col = cursor.column();
1745 m_overwritemode = (view->viewMode() == KTextEditor::View::NormalModeOverwrite);
1746
1747 // start searching
1748 if(backwards) {
1749 found = findOpenBracket(doc, row, col, bracket);
1750 }
1751 else {
1752 found = findCloseBracket(doc, row, col, bracket);
1753 // go behind the bracket
1754 if(!m_overwritemode) {
1755 ++bracket.col;
1756 }
1757 }
1758
1759 if(found) {
1760 view->setCursorPosition(KTextEditor::Cursor(bracket.row, bracket.col));
1761 }
1762 }
1763
1764 // match the opposite bracket
1765
matchTexgroup(KTextEditor::View * view)1766 void EditorExtension::matchTexgroup(KTextEditor::View *view)
1767 {
1768 view = determineView(view);
1769 if(!view) {
1770 return;
1771 }
1772
1773 int row, col;
1774 BracketData bracket;
1775
1776 // get current position
1777 KTextEditor::Document *doc = view->document();
1778 KTextEditor::Cursor cursor = view->cursorPosition();
1779 row = cursor.line();
1780 col = cursor.column();
1781 m_overwritemode = (view->viewMode() == KTextEditor::View::NormalModeOverwrite);
1782
1783 // this operation is only allowed at a bracket position
1784 if(!isBracketPosition(doc, row, col, bracket)) {
1785 return;
1786 }
1787
1788 // start searching
1789 bool found = false;
1790 if(bracket.open) {
1791 found = findCloseBracketTag(doc, bracket.row, bracket.col + 1, bracket);
1792 // go behind the bracket
1793 if(!m_overwritemode) {
1794 ++bracket.col;
1795 }
1796 }
1797 else {
1798 if(!decreaseCursorPosition(doc, bracket.row, bracket.col)) {
1799 return;
1800 }
1801 found = findOpenBracketTag(doc, bracket.row, bracket.col, bracket);
1802 }
1803
1804 if(found) {
1805 view->setCursorPosition(KTextEditor::Cursor(bracket.row, bracket.col));
1806 }
1807 }
1808
1809 //////////////////// close an open texgroup ////////////////////
1810
1811 // search for the last opened texgroup and close it
1812
closeTexgroup(KTextEditor::View * view)1813 void EditorExtension::closeTexgroup(KTextEditor::View *view)
1814 {
1815 view = determineView(view);
1816 if(!view) {
1817 return;
1818 }
1819
1820 int row, col;
1821 BracketData bracket;
1822
1823 KTextEditor::Document *doc = view->document();
1824 KTextEditor::Cursor cursor = view->cursorPosition();
1825 row = cursor.line();
1826 col = cursor.column();
1827
1828 int rowtemp = row;
1829 int coltemp = col;
1830 if(!decreaseCursorPosition(doc, rowtemp, coltemp)) {
1831 return;
1832 }
1833
1834 if(findOpenBracketTag(doc, rowtemp, coltemp, bracket)) {
1835 doc->insertText(KTextEditor::Cursor(row, col), "}");
1836 view->setCursorPosition(KTextEditor::Cursor(row, col + 1));
1837 }
1838 }
1839
1840 //////////////////// select a texgroup ////////////////////
1841
selectTexgroup(bool inside,KTextEditor::View * view)1842 void EditorExtension::selectTexgroup(bool inside, KTextEditor::View *view)
1843 {
1844 view = determineView(view);
1845 if(!view) {
1846 return;
1847 }
1848
1849 KTextEditor::Range range = texgroupRange(inside,view);
1850 if(range.isValid()) {
1851 view->setSelection(range);
1852 }
1853 }
1854
deleteTexgroup(bool inside,KTextEditor::View * view)1855 void EditorExtension::deleteTexgroup(bool inside, KTextEditor::View *view)
1856 {
1857 view = determineView(view);
1858 if(!view) {
1859 return;
1860 }
1861
1862 KTextEditor::Range range =texgroupRange(inside,view);
1863 if(range.isValid()) {
1864 deleteRange(range, view);
1865 }
1866 }
1867
1868 // calculate start and end of a Texgroup
1869
texgroupRange(bool inside,KTextEditor::View * view)1870 KTextEditor::Range EditorExtension::texgroupRange(bool inside, KTextEditor::View *view)
1871 {
1872 view = determineView(view);
1873 if(!view) {
1874 return KTextEditor::Range::invalid();
1875 }
1876
1877 BracketData open, close;
1878 if(getTexgroup(inside, open, close, view)) {
1879 return KTextEditor::Range(open.row, open.col, close.row, close.col);
1880 }
1881 else {
1882 return KTextEditor::Range::invalid();
1883 }
1884 }
1885
hasTexgroup(KTextEditor::View * view)1886 bool EditorExtension::hasTexgroup(KTextEditor::View *view)
1887 {
1888 // view will be checked in texgroupRange()
1889 KTextEditor::Range range = texgroupRange(true, view);
1890 return (range.isValid()) ? true : false;
1891 }
1892
getTexgroupText(bool inside,KTextEditor::View * view)1893 QString EditorExtension::getTexgroupText(bool inside, KTextEditor::View *view)
1894 {
1895 view = determineView(view);
1896 if(!view) {
1897 return QString();
1898 }
1899
1900 KTextEditor::Range range = texgroupRange(inside,view);
1901 return (range.isValid()) ? view->document()->text(range) : QString();
1902 }
1903
getTexgroup(bool inside,BracketData & open,BracketData & close,KTextEditor::View * view)1904 bool EditorExtension::getTexgroup(bool inside, BracketData &open, BracketData &close, KTextEditor::View *view)
1905 {
1906 view = determineView(view);
1907 if(!view) {
1908 return false;
1909 }
1910
1911 int row, col;
1912
1913 KTextEditor::Document *doc = view->document();
1914 KTextEditor::Cursor cursor = view->cursorPosition();
1915 row = cursor.line();
1916 col = cursor.column();
1917
1918 if(!findOpenBracket(doc, row, col, open)) {
1919 //KILE_DEBUG_MAIN << "no open bracket";
1920 return false;
1921 }
1922 if(!findCloseBracket(doc, row, col, close)) {
1923 //KILE_DEBUG_MAIN << "no close bracket";
1924 return false;
1925 }
1926
1927 if(inside) {
1928 ++open.col;
1929 }
1930 else {
1931 ++close.col;
1932 }
1933
1934 return true;
1935 }
1936
1937 //////////////////// search for a bracket position ////////////////////
1938
1939 // Find the last opening bracket. If the current cursor is over
1940 // - '{': we will stop immediately
1941 // - '}': we will start before this character
1942
findOpenBracket(KTextEditor::Document * doc,int row,int col,BracketData & bracket)1943 bool EditorExtension::findOpenBracket(KTextEditor::Document *doc, int row, int col, BracketData &bracket)
1944 {
1945 if(isBracketPosition(doc, row, col, bracket)) {
1946 // already found position?
1947 if(bracket.open) {
1948 return true;
1949 }
1950
1951 // go one position back
1952 row = bracket.row;
1953 col = bracket.col;
1954 if(!decreaseCursorPosition(doc, row, col)) {
1955 return false;
1956 }
1957 }
1958
1959 // looking back for last bracket
1960 return findOpenBracketTag(doc, row, col, bracket);
1961 }
1962
1963 // Find the last closing bracket. If the current cursor is over
1964 // - '}': we will stop immediately
1965 // - '{': we will start behind this character
1966
findCloseBracket(KTextEditor::Document * doc,int row,int col,BracketData & bracket)1967 bool EditorExtension::findCloseBracket(KTextEditor::Document *doc, int row, int col, BracketData &bracket)
1968 {
1969 if (isBracketPosition(doc, row, col, bracket)) {
1970 // already found position?
1971 if(!bracket.open) {
1972 return true;
1973 }
1974
1975 // go one position forward
1976 row = bracket.row;
1977 col = bracket.col + 1;
1978 }
1979
1980 // looking forward for next bracket
1981 return findCloseBracketTag(doc, row, col, bracket);
1982 }
1983
1984 /*
1985 Bracket matching uses the following algorithm (taken from Kate):
1986 1) If in overwrite mode, match the bracket currently underneath the cursor.
1987 2) Otherwise, if the character to the left of the cursor is an ending bracket,
1988 match it.
1989 3) Otherwise if the character to the right of the cursor is a
1990 starting bracket, match it.
1991 4) Otherwise, if the character to the left of the cursor is a
1992 starting bracket, match it.
1993 5) Otherwise, if the character to the right of the cursor is an
1994 ending bracket, match it.
1995 6) Otherwise, don't match anything.
1996 */
1997
isBracketPosition(KTextEditor::Document * doc,int row,int col,BracketData & bracket)1998 bool EditorExtension::isBracketPosition(KTextEditor::Document *doc, int row, int col, BracketData &bracket)
1999 {
2000 // default results
2001 bracket.row = row;
2002 bracket.col = col;
2003
2004 QString textline = getTextLineReal(doc, row);
2005 QChar right = textline[col];
2006 QChar left = (col > 0) ? textline[col-1] : QChar(' ');
2007
2008 if (m_overwritemode) {
2009 if(right == '{') {
2010 bracket.open = true;
2011 }
2012 else if(left == '}') {
2013 bracket.open = false;
2014 }
2015 else {
2016 return false;
2017 }
2018 }
2019 else if(left == '}') {
2020 bracket.open = false;
2021 --bracket.col;
2022 }
2023 else if(right == '{') {
2024 bracket.open = true;
2025 }
2026 else if(left == '{') {
2027 bracket.open = true;
2028 --bracket.col;
2029 }
2030 else if(right == '}') {
2031 bracket.open = false;
2032 }
2033 else {
2034 return false;
2035 }
2036
2037 return true;
2038 }
2039
2040 // find next non-nested closing bracket
2041
findCloseBracketTag(KTextEditor::Document * doc,int row,int col,BracketData & bracket)2042 bool EditorExtension::findCloseBracketTag(KTextEditor::Document *doc, int row, int col, BracketData &bracket)
2043 {
2044 uint brackets = 0;
2045 for(int line = row; line < doc->lines(); ++line) {
2046 uint start = (line == row) ? col : 0;
2047 QString textline = getTextLineReal(doc,line);
2048 for(int i = start; i < textline.length(); ++i) {
2049 if(textline[i] == '{') {
2050 ++brackets;
2051 }
2052 else if(textline[i] == '}') {
2053 if(brackets > 0) {
2054 --brackets;
2055 }
2056 else {
2057 bracket.row = line;
2058 bracket.col = i;
2059 bracket.open = false;
2060 return true;
2061 }
2062 }
2063 }
2064 }
2065
2066 return false;
2067 }
2068
2069 // find next non-nested opening bracket
2070
findOpenBracketTag(KTextEditor::Document * doc,int row,int col,BracketData & bracket)2071 bool EditorExtension::findOpenBracketTag(KTextEditor::Document *doc, int row, int col, BracketData &bracket)
2072 {
2073 uint brackets = 0;
2074 for(int line = row; line >= 0; --line) {
2075 QString textline = getTextLineReal(doc, line);
2076 int start = (line == row) ? col : textline.length() - 1;
2077 for (int i = start; i >= 0; --i) {
2078 //KILE_DEBUG_MAIN << "findOpenBracketTag: (" << line << "," << i << ") = " << textline[i].toLatin1();
2079 if(textline[i] == '{') {
2080 if(brackets > 0) {
2081 --brackets;
2082 }
2083 else {
2084 bracket.row = line;
2085 bracket.col = i;
2086 bracket.open = true;
2087 return true;
2088 }
2089 }
2090 else if(textline[i] == '}') {
2091 ++brackets;
2092 }
2093 }
2094 }
2095
2096 //KILE_DEBUG_MAIN << "nothting found";
2097 return false;
2098 }
2099
2100 //////////////////// get real text ////////////////////
2101
2102 // get current textline and remove
2103 // - all pairs of backslashes: '\\'
2104 // - all quoted comment signs: '\%'
2105 // - all quoted brackets: '\{' and '\}'
2106 // - all comments
2107 // replace these characters with one, which never will be looked for
2108
getTextLineReal(KTextEditor::Document * doc,int row)2109 QString EditorExtension::getTextLineReal(KTextEditor::Document *doc, int row)
2110 {
2111 QString textline = doc->line(row);
2112 int len = textline.length();
2113 if(len == 0) {
2114 return QString();
2115 }
2116
2117 bool backslash = false;
2118 for(int i = 0; i < len; ++i) {
2119 if (textline[i]=='{' || textline[i]=='}' || textline[i]=='$') {
2120 if(backslash) {
2121 textline[i-1] = '&';
2122 textline[i] = '&';
2123 }
2124 backslash = false;
2125 }
2126 else if(textline[i] == '\\') {
2127 if(backslash) {
2128 textline[i-1] = '&';
2129 textline[i] = '&';
2130 backslash = false;
2131 }
2132 else {
2133 backslash = true;
2134 }
2135 }
2136 else if(textline[i]=='%') {
2137 if (backslash) {
2138 textline[i-1] = '&';
2139 textline[i] = '&';
2140 }
2141 else {
2142 len = i;
2143 break;
2144 }
2145 backslash = false;
2146 }
2147 else {
2148 backslash = false;
2149 }
2150 }
2151
2152 // return real text
2153 return textline.left(len);
2154 }
2155
2156 //////////////////// capture the current word ////////////////////
2157
2158 // Capture the current word from the cursor position to the left and right.
2159 // The result depens on the given search mode;
2160 // - smTex only letters, except backslash as first and star as last character
2161 // - smLetter: only letters
2162 // - smWord: letters and digits
2163 // - smNospace: everything except white space
2164
getCurrentWord(KTextEditor::Document * doc,int row,int col,EditorExtension::SelectMode mode,QString & word,int & x1,int & x2)2165 bool EditorExtension::getCurrentWord(KTextEditor::Document *doc, int row, int col, EditorExtension::SelectMode mode, QString &word, int &x1, int &x2)
2166 {
2167 // get real textline without comments, quoted characters and pairs of backslashes
2168 QString textline = getTextLineReal(doc, row);
2169 if (col > textline.length()) {
2170 return false;
2171 }
2172
2173 QRegExp reg;
2174 QString pattern1, pattern2;
2175 switch(mode) {
2176 case smLetter:
2177 pattern1 = "[^a-zA-Z]+";
2178 pattern2 = "[a-zA-Z]+";
2179 break;
2180 case smWord:
2181 pattern1 = "[^a-zA-Z0-9]";
2182 pattern2 = "[a-zA-Z0-9]+";
2183 break;
2184 case smNospace:
2185 pattern1 = "\\s";
2186 pattern2 = "\\S+";
2187 break;
2188 default:
2189 pattern1 = "[^a-zA-Z]";
2190 pattern2 = "\\\\?[a-zA-Z]+\\*?";
2191 break;
2192 }
2193 x1 = x2 = col;
2194
2195 int pos;
2196 // search to the left side
2197 if(col > 0) {
2198 reg.setPattern(pattern1);
2199 pos = textline.lastIndexOf(reg, col - 1);
2200 if(pos != -1) { // found an illegal character
2201 x1 = pos + 1;
2202 if(mode == smTex) {
2203 if(textline[pos] == '\\') {
2204 x1 = pos;
2205 }
2206 col = x1;
2207 }
2208 }
2209 else {
2210 x1 = 0; // pattern matches from beginning of line
2211 }
2212 }
2213
2214 // search at the current position
2215 reg.setPattern(pattern2);
2216 pos = textline.indexOf(reg, col);
2217 if(pos != -1 && pos == col) {
2218 x2 = pos + reg.matchedLength();
2219 }
2220
2221 // get all characters
2222 if(x1 != x2) {
2223 word = textline.mid(x1, x2 - x1);
2224 return true;
2225 }
2226 else {
2227 return false;
2228 }
2229 }
2230
wordRange(const KTextEditor::Cursor & cursor,bool latexCommand,KTextEditor::View * view)2231 KTextEditor::Range EditorExtension::wordRange(const KTextEditor::Cursor &cursor, bool latexCommand, KTextEditor::View *view)
2232 {
2233 view = determineView(view);
2234 if(!view) {
2235 return KTextEditor::Range::invalid();
2236 }
2237
2238 int col1, col2;
2239 QString word;
2240 EditorExtension::SelectMode mode = ( latexCommand ) ? EditorExtension::smTex : EditorExtension::smLetter;
2241 int line = cursor.line();
2242
2243 return (getCurrentWord(view->document(), line, cursor.column(), mode, word, col1, col2))
2244 ? KTextEditor::Range(line,col1,line,col2)
2245 : KTextEditor::Range::invalid();
2246 }
2247
word(const KTextEditor::Cursor & cursor,bool latexCommand,KTextEditor::View * view)2248 QString EditorExtension::word(const KTextEditor::Cursor &cursor, bool latexCommand, KTextEditor::View *view)
2249 {
2250 KTextEditor::Range range = EditorExtension::wordRange(cursor,latexCommand,view);
2251 return ( range.isValid() ) ? view->document()->text(range) : QString();
2252 }
2253
2254 //////////////////// paragraph ////////////////////
2255
selectParagraph(KTextEditor::View * view,bool wholeLines)2256 void EditorExtension::selectParagraph(KTextEditor::View* view, bool wholeLines)
2257 {
2258 view = determineView(view);
2259 if(!view) {
2260 return;
2261 }
2262
2263 KTextEditor::Range range = findCurrentParagraphRange(view, wholeLines);
2264 if ( range.isValid() ) {
2265 view->setSelection(range);
2266 }
2267 }
2268
deleteParagraph(KTextEditor::View * view)2269 void EditorExtension::deleteParagraph(KTextEditor::View *view)
2270 {
2271 view = determineView(view);
2272 if(!view) {
2273 return;
2274 }
2275 int startline, endline;
2276
2277 if(findCurrentTexParagraph(startline, endline, view)) {
2278 KTextEditor::Document *doc = view->document();
2279 view->removeSelection();
2280 if(startline > 0) {
2281 --startline;
2282 }
2283 else if(endline < doc->lines() - 1) {
2284 ++endline;
2285 }
2286 doc->removeText(KTextEditor::Range(startline, 0, endline+1, 0));
2287 view->setCursorPosition(KTextEditor::Cursor(startline, 0));
2288 }
2289 }
2290
2291 // get the range of the current paragraph
findCurrentParagraphRange(KTextEditor::View * view,bool wholeLines)2292 KTextEditor::Range EditorExtension::findCurrentParagraphRange(KTextEditor::View* view, bool wholeLines)
2293 {
2294 view = determineView(view);
2295 if(!view) {
2296 return KTextEditor::Range::invalid();
2297 }
2298
2299 int startline, endline, startcolumn, endcolumn;
2300
2301 if (findCurrentTexParagraph(startline, startcolumn, endline, endcolumn, view)) {
2302 return wholeLines ?
2303 KTextEditor::Range(startline, 0, endline + 1, 0) :
2304 KTextEditor::Range(startline, startcolumn, endline, endcolumn);
2305 }
2306 else {
2307 return KTextEditor::Range::invalid();
2308 }
2309 }
2310
getParagraphText(KTextEditor::View * view)2311 QString EditorExtension::getParagraphText(KTextEditor::View *view)
2312 {
2313 view = determineView(view);
2314 if(!view) {
2315 return QString();
2316 }
2317
2318 KTextEditor::Range range = findCurrentParagraphRange(view);
2319 return (range.isValid()) ? view->document()->text(range) : QString();
2320 }
2321
findCurrentTexParagraph(int & startline,int & endline,KTextEditor::View * view)2322 bool EditorExtension::findCurrentTexParagraph(int& startline, int& endline, KTextEditor::View* view)
2323 {
2324 int dummy;
2325 return findCurrentTexParagraph(startline, dummy, endline, dummy, view);
2326 }
2327
findCurrentTexParagraph(int & startline,int & startcolumn,int & endline,int & endcolumn,KTextEditor::View * view)2328 bool EditorExtension::findCurrentTexParagraph(int& startline, int& startcolumn, int& endline, int& endcolumn, KTextEditor::View* view)
2329 {
2330 view = determineView(view);
2331 if(!view) {
2332 return false;
2333 }
2334
2335 int row;
2336
2337 // get current position
2338 KTextEditor::Document *doc = view->document();
2339 KTextEditor::Cursor cursor = view->cursorPosition();
2340 row = cursor.line();
2341
2342 // don't accept an empty line as part of a paragraph
2343 if(doc->line(row).trimmed().isEmpty()) {
2344 return false;
2345 }
2346
2347 // settings default results
2348 startline = row;
2349 endline = row;
2350
2351 // find the previous empty line
2352 for(int line = row - 1; line >= 0; --line) {
2353 if(doc->line(line).trimmed().isEmpty()) {
2354 break;
2355 }
2356 startline = line;
2357 }
2358
2359 // it is guaranteed that 'startline.trimmed()' won't be empty
2360 startcolumn = 0;
2361 QString line = doc->line(startline);
2362 for(int i = 0; i < line.size(); ++i) {
2363 if(!line[i].isSpace()) {
2364 startcolumn = i;
2365 break;
2366 }
2367 }
2368
2369 // find the next empty line
2370 for(int line = row + 1; line < doc->lines(); ++line) {
2371 if(doc->line(line).trimmed().isEmpty()) {
2372 break;
2373 }
2374 endline = line;
2375 }
2376
2377 // it is guaranteed that 'endline.trimmed()' won't be empty
2378 line = doc->line(endline);
2379 endcolumn = line.size();
2380 for(int i = line.size() - 1; i >= 0; --i) {
2381 if(!line[i].isSpace()) {
2382 endcolumn = i+1;
2383 break;
2384 }
2385 }
2386
2387 // settings result
2388 return true;
2389 }
2390
gotoNextParagraph(KTextEditor::View * view)2391 void EditorExtension::gotoNextParagraph(KTextEditor::View *view)
2392 {
2393 view = determineView(view);
2394 if(!view) {
2395 return;
2396 }
2397
2398 bool found;
2399 int startline, endline;
2400 KTextEditor::Document *doc = view->document();
2401
2402 endline = view->cursorPosition().line();
2403 if(doc->line(endline).trimmed().isEmpty()) {
2404 found = true;
2405 }
2406 else {
2407 found = findCurrentTexParagraph(startline, endline, view);
2408 }
2409
2410 // we are in an empty line or in the last line of a paragraph
2411 if (found) {
2412 // find the next non empty line
2413 for(int line = endline + 1; line < doc->lines(); ++line) {
2414 if(!doc->line(line).trimmed().isEmpty()) {
2415 view->setCursorPosition(KTextEditor::Cursor(line, 0));
2416 return;
2417 }
2418 }
2419 }
2420 }
2421
gotoPrevParagraph(KTextEditor::View * view)2422 void EditorExtension::gotoPrevParagraph(KTextEditor::View *view)
2423 {
2424 view = determineView(view);
2425 if(!view) {
2426 return;
2427 }
2428
2429 bool found;
2430 int startline,endline;
2431 KTextEditor::Document *doc = view->document();
2432
2433 startline = view->cursorPosition().line();
2434 if(doc->line(startline).trimmed().isEmpty()) {
2435 startline++;
2436 found = true;
2437 }
2438 else {
2439 found = findCurrentTexParagraph(startline,endline,view);
2440 }
2441 // we are in an empty line or in the first line of a paragraph
2442 if(found) {
2443 // find the last line of the previous paragraph
2444 int foundline = -1;
2445 for (int line = startline - 1; line >= 0; --line) {
2446 if(!doc->line(line).trimmed().isEmpty()) {
2447 break;
2448 }
2449 foundline = line;
2450 }
2451 if(foundline < 0) {
2452 return;
2453 }
2454
2455 // and finally the first line of this paragraph
2456 int prevstartline = -1;
2457 for(int line = foundline - 1; line >= 0; --line) {
2458 if(doc->line(line).trimmed().isEmpty()) {
2459 break;
2460 }
2461 prevstartline = line;
2462 }
2463
2464 if(prevstartline >= 0) {
2465 view->setCursorPosition(KTextEditor::Cursor(prevstartline, 0));
2466 }
2467 }
2468 }
2469
prevNonEmptyLine(int line,KTextEditor::View * view)2470 int EditorExtension::prevNonEmptyLine(int line, KTextEditor::View *view)
2471 {
2472 view = determineView(view);
2473 if(!view) {
2474 return -1;
2475 }
2476
2477 KTextEditor::Document *doc = view->document();
2478 for(int i = line - 1; i >= 0; --i) {
2479 if(!doc->line(i).trimmed().isEmpty()) {
2480 return i;
2481 }
2482 }
2483 return -1;
2484 }
2485
nextNonEmptyLine(int line,KTextEditor::View * view)2486 int EditorExtension::nextNonEmptyLine(int line, KTextEditor::View *view)
2487 {
2488 view = determineView(view);
2489 if(!view) {
2490 return -1;
2491 }
2492
2493 KTextEditor::Document *doc = view->document();
2494 int lines = doc->lines();
2495 for(int i = line + 1; i < lines; ++i) {
2496 if(!doc->line(i).trimmed().isEmpty()) {
2497 return i;
2498 }
2499 }
2500 return -1;
2501 }
2502
2503 //////////////////// one line of text////////////////////
2504
selectLine(KTextEditor::View * view)2505 void EditorExtension::selectLine(KTextEditor::View *view)
2506 {
2507 view = determineView(view);
2508 if(!view) {
2509 return;
2510 }
2511
2512 // get current position
2513 int row;
2514 QString word;
2515 KTextEditor::Document *doc = view->document();
2516 KTextEditor::Cursor cursor = view->cursorPosition();
2517 row = cursor.line();
2518
2519 if(doc->lineLength(row) > 0) {
2520 view->setSelection(KTextEditor::Range(row, 0, row + 1, 0));
2521 }
2522 }
2523
selectLine(int line,KTextEditor::View * view)2524 void EditorExtension::selectLine(int line, KTextEditor::View *view)
2525 {
2526 view = determineView(view);
2527 if(!view) {
2528 return;
2529 }
2530
2531 if(view->document()->lineLength(line) > 0) {
2532 view->setSelection(KTextEditor::Range(line, 0, line + 1, 0));
2533 }
2534 }
2535
selectLines(int from,int to,KTextEditor::View * view)2536 void EditorExtension::selectLines(int from, int to, KTextEditor::View *view)
2537 {
2538 view = determineView(view);
2539 if(view && from <= to) {
2540 view->setSelection(KTextEditor::Range(from, 0, to + 1, 0));
2541 }
2542 }
2543
replaceLine(int line,const QString & s,KTextEditor::View * view)2544 bool EditorExtension::replaceLine(int line, const QString &s, KTextEditor::View *view)
2545 {
2546 view = determineView(view);
2547 if(!view) {
2548 return false;
2549 }
2550
2551 KTextEditor::Document *doc = view->document();
2552 KTextEditor::Document::EditingTransaction transaction(doc);
2553 doc->removeLine(line);
2554 bool result = doc->insertLine(line, s);
2555 return result;
2556 }
2557
deleteEndOfLine(KTextEditor::View * view)2558 void EditorExtension::deleteEndOfLine(KTextEditor::View *view)
2559 {
2560 view = determineView(view);
2561 if(!view) {
2562 return;
2563 }
2564
2565 int row, col;
2566 KTextEditor::Cursor cursor = view->cursorPosition();
2567 row = cursor.line();
2568 col = cursor.column();
2569
2570 KTextEditor::Document *doc = view->document();
2571 view->removeSelection();
2572 doc->removeText(KTextEditor::Range(row, col, row, doc->lineLength(row)));
2573 }
2574
2575 //////////////////// LaTeX command ////////////////////
2576
selectWord(EditorExtension::SelectMode mode,KTextEditor::View * view)2577 void EditorExtension::selectWord(EditorExtension::SelectMode mode, KTextEditor::View *view)
2578 {
2579 view = determineView(view);
2580 if(!view) {
2581 return;
2582 }
2583
2584 KTextEditor::Range range = wordRange(view->cursorPosition(),mode,view);
2585 if ( range.isValid() ) {
2586 view->setSelection(range);
2587 }
2588 }
2589
deleteWord(EditorExtension::SelectMode mode,KTextEditor::View * view)2590 void EditorExtension::deleteWord(EditorExtension::SelectMode mode, KTextEditor::View *view)
2591 {
2592 view = determineView(view);
2593 if(!view) {
2594 return;
2595 }
2596
2597 KTextEditor::Range range = wordRange(view->cursorPosition(),mode,view);
2598 if(range.isValid()) {
2599 deleteRange(range,view);
2600 }
2601 }
2602
nextBullet(KTextEditor::View * view)2603 void EditorExtension::nextBullet(KTextEditor::View* view)
2604 {
2605 gotoBullet(false, view);
2606 }
2607
prevBullet(KTextEditor::View * view)2608 void EditorExtension::prevBullet(KTextEditor::View* view)
2609 {
2610 gotoBullet(true, view);
2611 }
2612
insertBullet(KTextEditor::View * view)2613 void EditorExtension::insertBullet(KTextEditor::View* view)
2614 {
2615 view = determineView(view);
2616 if(!view) {
2617 return;
2618 }
2619
2620 view->document()->insertText(view->cursorPosition(), s_bullet);
2621 }
2622
2623 ///////////////////// Special Functions ///////////////
2624 /*
2625 void EditorExtension::insertNewLine(KTextEditor::View *view)
2626 {
2627 view = determineView(view);
2628 if(!view) {
2629 return;
2630 }
2631
2632 int newLineNumber = view->cursorPosition().line() + 1;
2633 view->document()->insertLine(newLineNumber, QString());
2634 }
2635 */
moveCursorToLastPositionInCurrentLine(KTextEditor::View * view)2636 void EditorExtension::moveCursorToLastPositionInCurrentLine(KTextEditor::View *view)
2637 {
2638 view = determineView(view);
2639 if(!view) {
2640 return;
2641 }
2642
2643 const KTextEditor::Cursor currentPosition = view->cursorPosition();
2644 view->setCursorPosition(KTextEditor::Cursor(currentPosition.line(),
2645 view->document()->lineLength(currentPosition.line())));
2646 }
2647
keyReturn(KTextEditor::View * view)2648 void EditorExtension::keyReturn(KTextEditor::View *view)
2649 {
2650 view = determineView(view);
2651 if(!view) {
2652 return;
2653 }
2654
2655 int newLineNumber = view->cursorPosition().line() + 1;
2656 view->document()->insertLine(newLineNumber, QString());
2657 view->setCursorPosition(KTextEditor::Cursor(newLineNumber, 0));
2658 }
2659
commentLaTeX(KTextEditor::Document * document,const KTextEditor::Range & range)2660 void EditorExtension::commentLaTeX(KTextEditor::Document* document, const KTextEditor::Range& range)
2661 {
2662 int startLine = range.start().line(), endLine = range.end().line();
2663 for(int i = startLine; i <= endLine; ++i) {
2664 document->insertText(KTextEditor::Cursor(i, 0), "% ");
2665 }
2666 }
2667
goToLine(int line,KTextEditor::View * view)2668 void EditorExtension::goToLine(int line, KTextEditor::View *view)
2669 {
2670 view = determineView(view);
2671 if(!view) {
2672 return;
2673 }
2674
2675 KTextEditor::Cursor cursor(line, 0);
2676 view->setCursorPosition(cursor);
2677 }
2678
2679 //////////////////// double quotes ////////////////////
2680
initDoubleQuotes()2681 void EditorExtension::initDoubleQuotes()
2682 {
2683 m_dblQuotes = KileConfig::insertDoubleQuotes();
2684
2685 int index = KileConfig::doubleQuotes();
2686 if(index < 0 || index >= m_quoteList.count()) {
2687 index = 0;
2688 }
2689
2690 m_leftDblQuote = m_quoteList[index].first;
2691 m_rightDblQuote = m_quoteList[index].second;
2692 KILE_DEBUG_MAIN << "new quotes: " << m_dblQuotes << " left=" << m_leftDblQuote << " right=" << m_rightDblQuote<< endl;
2693 }
2694
insertDoubleQuotes(KTextEditor::View * view)2695 bool EditorExtension::insertDoubleQuotes(KTextEditor::View *view)
2696 {
2697 // don't insert double quotes, if konsole has focus
2698 // return false, because if this is called from an event
2699 // handler, because this event has to be passed on
2700 if(m_ki->texKonsole()->hasFocus()) {
2701 return false;
2702 }
2703
2704 // insert double quotes, normal mode or autocompletion mode
2705 // always return true for event handler
2706 view = determineView(view);
2707 if(!view) {
2708 return true;
2709 }
2710
2711 KTextEditor::Document *doc = view->document();
2712
2713 if(!doc) {
2714 return false;
2715 }
2716
2717 view->removeSelectionText();
2718
2719 int row, col;
2720 KTextEditor::Cursor cursor = view->cursorPosition();
2721 row = cursor.line();
2722 col = cursor.column();
2723
2724 // simply insert, if we are inside a verb command
2725 if(insideVerb(view) || insideVerbatim(view)) {
2726 return false;
2727 }
2728
2729 // simply insert, if autoinsert mode is not active or the char bevor is \ (typically for \"a useful)
2730 if (!m_dblQuotes || (col > 0 && doc->text(KTextEditor::Range(row, col - 1, row, col)) == "\\")) {
2731 return false;
2732 }
2733
2734 // insert with auto mode
2735 QString pattern1 = QRegExp::escape(m_leftDblQuote);
2736 if(m_leftDblQuote.at(m_leftDblQuote.length()-1).isLetter()) {
2737 pattern1 += "(\\b|(\\{\\}))";
2738 }
2739 QString pattern2 = QRegExp::escape(m_rightDblQuote);
2740 if(m_rightDblQuote.at(m_rightDblQuote.length()-1).isLetter()) {
2741 pattern2 += "(\\b|(\\{\\}))";
2742 }
2743
2744 bool openFound = false;
2745 KTextEditor::Range searchRange = KTextEditor::Range(KTextEditor::Cursor(0, 0), KTextEditor::Cursor(row, col));
2746 QVector<KTextEditor::Range> foundRanges = doc->searchText(searchRange, '(' + pattern1 + ")|(" + pattern2 + ')',
2747 KTextEditor::Regex | KTextEditor::Backwards);
2748 // KTextEditor::Document#searchText always returns at least one range, even
2749 // if no occurrences have been found. Thus, we have to check if the range is valid.
2750 KTextEditor::Range range = foundRanges.first();
2751 if(range.isValid()) {
2752 int lineFound = range.start().line();
2753 int columnFound = range.start().column();
2754 openFound = (doc->line(lineFound).indexOf(m_leftDblQuote, columnFound) == columnFound);
2755 }
2756
2757 QString textline = doc->line(row);
2758 //KILE_DEBUG_MAIN << "text=" << textline << " open=" << openfound;
2759 if(openFound) {
2760 // If we last inserted a language specific doublequote open,
2761 // we have to change it to a normal doublequote. If not we
2762 // insert a language specific doublequote close
2763 int startcol = col - m_leftDblQuote.length();
2764 //KILE_DEBUG_MAIN << "startcol=" << startcol << " col=" << col ;
2765 if (startcol>=0 && textline.indexOf(m_leftDblQuote, startcol) == startcol) {
2766 doc->removeText(KTextEditor::Range(row, startcol, row, startcol + m_leftDblQuote.length()));
2767 doc->insertText(KTextEditor::Cursor(row, startcol), "\"");
2768 }
2769 else {
2770 doc->insertText(KTextEditor::Cursor(row, col), m_rightDblQuote);
2771 }
2772 }
2773 else {
2774 // If we last inserted a language specific doublequote close,
2775 // we have to change it to a normal doublequote. If not we
2776 // insert a language specific doublequote open
2777 int startcol = col - m_rightDblQuote.length();
2778 //KILE_DEBUG_MAIN << "startcol=" << startcol << " col=" << col ;
2779 if (startcol >= 0 && textline.indexOf(m_rightDblQuote, startcol) == startcol) {
2780 doc->removeText(KTextEditor::Range(row, startcol, row, startcol + m_rightDblQuote.length()));
2781 doc->insertText(KTextEditor::Cursor(row,startcol), "\"");
2782 }
2783 else {
2784 doc->insertText(KTextEditor::Cursor(row, col), m_leftDblQuote);
2785 }
2786 }
2787 return true;
2788 }
2789
2790 // Takes an unicode unsigned short and calls insertSpecialCharacter to
2791 // insert the proper LaTeX sequence and warn user of any dependencies.
2792 //FIXME: there should be one central place to convert unicode chars to LaTeX;
2793 // also see 'LaTeXEventFilter::eventFilter'.
insertLatexFromUnicode(unsigned short rep,KTextEditor::View * view)2794 bool EditorExtension::insertLatexFromUnicode(unsigned short rep, KTextEditor::View *view)
2795 {
2796 switch(rep)
2797 { // Find the unicode decimal representation
2798 case 160:
2799 return insertSpecialCharacter("~", view);
2800 case 161:
2801 return insertSpecialCharacter("!`", view);
2802 case 162:
2803 return insertSpecialCharacter("\\textcent", view, "textcomp");
2804 case 163:
2805 return insertSpecialCharacter("\\pounds", view);
2806 case 164:
2807 return insertSpecialCharacter("\\textcurrency", view, "textcomp");
2808 case 165:
2809 return insertSpecialCharacter("\\textyen", view, "textcomp");
2810 case 166:
2811 return insertSpecialCharacter("\\textbrokenbar", view, "textcomp");
2812 case 167:
2813 return insertSpecialCharacter("\\S", view);
2814 case 168:
2815 return insertSpecialCharacter("\"", view);
2816 case 169:
2817 return insertSpecialCharacter("\\copyright", view);
2818 case 170:
2819 return insertSpecialCharacter("\\textordfeminine", view, "textcomp");
2820 case 171:
2821 return insertSpecialCharacter("\\guillemotleft", view);
2822 case 172:
2823 return insertSpecialCharacter("\\neg", view); // TODO: Check for math
2824 case 173:
2825 return insertSpecialCharacter("-", view); // TODO: Check for math
2826 case 174:
2827 return insertSpecialCharacter("\\textregistered", view, "textcomp");
2828 case 176:
2829 return insertSpecialCharacter("^\\circ", view); // TODO: Check for math
2830 case 177:
2831 return insertSpecialCharacter("\\pm", view); // TODO: Check for math
2832 case 178:
2833 return insertSpecialCharacter("^2", view); // TODO: Check for math
2834 case 179:
2835 return insertSpecialCharacter("^3", view); // TODO: Check for math
2836 case 180:
2837 return insertSpecialCharacter("'", view);
2838 case 181:
2839 return insertSpecialCharacter("\\mu", view); // TODO: Check for math
2840 case 182:
2841 return insertSpecialCharacter("\\P", view);
2842 case 185:
2843 return insertSpecialCharacter("^1", view); // TODO: Check for math
2844 case 186:
2845 return insertSpecialCharacter("\\textordmasculine", view, "textcomp");
2846 case 187:
2847 return insertSpecialCharacter("\\guillemotright", view);
2848 case 191:
2849 return insertSpecialCharacter("?`", view);
2850 case 192:
2851 return insertSpecialCharacter("\\`A", view);
2852 case 193:
2853 return insertSpecialCharacter("\\'A", view);
2854 case 194:
2855 return insertSpecialCharacter("\\^A", view);
2856 case 195:
2857 return insertSpecialCharacter("\\~A", view);
2858 case 196:
2859 return insertSpecialCharacter("\\\"A", view);
2860 case 197:
2861 return insertSpecialCharacter("\\AA", view);
2862 case 198:
2863 return insertSpecialCharacter("\\AE", view);
2864 case 199:
2865 return insertSpecialCharacter("\\c{C}", view);
2866 case 200:
2867 return insertSpecialCharacter("\\`E", view);
2868 case 201:
2869 return insertSpecialCharacter("\\'E", view);
2870 case 202:
2871 return insertSpecialCharacter("\\^E", view);
2872 case 203:
2873 return insertSpecialCharacter("\\\"E", view);
2874 case 204:
2875 return insertSpecialCharacter("\\`I", view);
2876 case 205:
2877 return insertSpecialCharacter("\\'I", view);
2878 case 206:
2879 return insertSpecialCharacter("\\^I", view);
2880 case 207:
2881 return insertSpecialCharacter("\\\"I", view);
2882 case 209:
2883 return insertSpecialCharacter("\\~N", view);
2884 case 210:
2885 return insertSpecialCharacter("\\`O", view);
2886 case 211:
2887 return insertSpecialCharacter("\\'O", view);
2888 case 212:
2889 return insertSpecialCharacter("\\^O", view);
2890 case 213:
2891 return insertSpecialCharacter("\\~O", view);
2892 case 214:
2893 return insertSpecialCharacter("\\\"O", view);
2894 case 215:
2895 return insertSpecialCharacter("\\times", view); //TODO: Check for math
2896 case 216:
2897 return insertSpecialCharacter("\\Oslash", view);
2898 case 217:
2899 return insertSpecialCharacter("\\`U", view);
2900 case 218:
2901 return insertSpecialCharacter("\\'U", view);
2902 case 219:
2903 return insertSpecialCharacter("\\^U", view);
2904 case 220:
2905 return insertSpecialCharacter("\\\"U", view);
2906 case 221:
2907 return insertSpecialCharacter("\\'Y", view);
2908 case 223:
2909 return insertSpecialCharacter("\\ss{}", view);
2910 case 224:
2911 return insertSpecialCharacter("\\`a", view);
2912 case 225:
2913 return insertSpecialCharacter("\\'a", view);
2914 case 226:
2915 return insertSpecialCharacter("\\^a", view);
2916 case 227:
2917 return insertSpecialCharacter("\\~a", view);
2918 case 228:
2919 return insertSpecialCharacter("\\\"a", view);
2920 case 229:
2921 return insertSpecialCharacter("\\aa", view);
2922 case 230:
2923 return insertSpecialCharacter("\\ae", view);
2924 case 231:
2925 return insertSpecialCharacter("\\c{c}", view);
2926 case 232:
2927 return insertSpecialCharacter("\\`e", view);
2928 case 233:
2929 return insertSpecialCharacter("\\'e", view);
2930 case 234:
2931 return insertSpecialCharacter("\\^e", view);
2932 case 235:
2933 return insertSpecialCharacter("\\\"e", view);
2934 case 236:
2935 return insertSpecialCharacter("\\`i", view);
2936 case 237:
2937 return insertSpecialCharacter("\\'i", view);
2938 case 238:
2939 return insertSpecialCharacter("\\^i", view);
2940 case 239:
2941 return insertSpecialCharacter("\\\"i", view);
2942 case 240:
2943 return insertSpecialCharacter("\\~o", view);
2944 case 241:
2945 return insertSpecialCharacter("\\~n", view);
2946 case 242:
2947 return insertSpecialCharacter("\\`o", view);
2948 case 243:
2949 return insertSpecialCharacter("\\'o", view);
2950 case 244:
2951 return insertSpecialCharacter("\\^o", view);
2952 case 245:
2953 return insertSpecialCharacter("\\~o", view);
2954 case 246:
2955 return insertSpecialCharacter("\\\"o", view);
2956 case 247:
2957 return insertSpecialCharacter("\\div", view);
2958 case 248:
2959 return insertSpecialCharacter("\\oslash", view);
2960 case 249:
2961 return insertSpecialCharacter("\\`u", view);
2962 case 250:
2963 return insertSpecialCharacter("\\'u", view);
2964 case 251:
2965 return insertSpecialCharacter("\\^u", view);
2966 case 252:
2967 return insertSpecialCharacter("\\\"u", view);
2968 case 253:
2969 return insertSpecialCharacter("\\'y", view);
2970 case 255:
2971 return insertSpecialCharacter("\\\"y", view);
2972 case 256:
2973 return insertSpecialCharacter("\\=A", view);
2974 case 257:
2975 return insertSpecialCharacter("\\=a", view);
2976 case 258:
2977 return insertSpecialCharacter("\\uA", view);
2978 case 259:
2979 return insertSpecialCharacter("\\ua", view);
2980 case 262:
2981 return insertSpecialCharacter("\\'C", view);
2982 case 263:
2983 return insertSpecialCharacter("\\'c", view);
2984 case 264:
2985 return insertSpecialCharacter("\\^C", view);
2986 case 265:
2987 return insertSpecialCharacter("\\^c", view);
2988 case 266:
2989 return insertSpecialCharacter("\\.C", view);
2990 case 267:
2991 return insertSpecialCharacter("\\.c", view);
2992 case 268:
2993 return insertSpecialCharacter("\\vC", view);
2994 case 269:
2995 return insertSpecialCharacter("\\vc", view);
2996 case 270:
2997 return insertSpecialCharacter("\\vD", view);
2998 case 271:
2999 return insertSpecialCharacter("\\vd", view);
3000 case 272:
3001 return insertSpecialCharacter("\\=D", view);
3002 case 273:
3003 return insertSpecialCharacter("\\=d", view);
3004 case 274:
3005 return insertSpecialCharacter("\\=E", view);
3006 case 275:
3007 return insertSpecialCharacter("\\=e", view);
3008 case 276:
3009 return insertSpecialCharacter("\\uE", view);
3010 case 277:
3011 return insertSpecialCharacter("\\ue", view);
3012 case 278:
3013 return insertSpecialCharacter("\\.E", view);
3014 case 279:
3015 return insertSpecialCharacter("\\.e", view);
3016 case 282:
3017 return insertSpecialCharacter("\\vE", view);
3018 case 283:
3019 return insertSpecialCharacter("\\ve", view);
3020 case 284:
3021 return insertSpecialCharacter("\\^G", view);
3022 case 285:
3023 return insertSpecialCharacter("\\^g", view);
3024 case 286:
3025 return insertSpecialCharacter("\\uG", view);
3026 case 287:
3027 return insertSpecialCharacter("\\ug", view);
3028 case 288:
3029 return insertSpecialCharacter("\\.G", view);
3030 case 289:
3031 return insertSpecialCharacter("\\.g", view);
3032 case 290:
3033 return insertSpecialCharacter("\\cG", view);
3034 case 291:
3035 return insertSpecialCharacter("\\'g", view);
3036 case 292:
3037 return insertSpecialCharacter("\\^H", view);
3038 case 293:
3039 return insertSpecialCharacter("\\^h", view);
3040 case 294:
3041 return insertSpecialCharacter("\\=H", view);
3042 case 295:
3043 return insertSpecialCharacter("\\=h", view);
3044 case 296:
3045 return insertSpecialCharacter("\\~I", view);
3046 case 297:
3047 return insertSpecialCharacter("\\~i", view);
3048 case 298:
3049 return insertSpecialCharacter("\\=I", view);
3050 case 299:
3051 return insertSpecialCharacter("\\=i", view);
3052 case 300:
3053 return insertSpecialCharacter("\\uI", view);
3054 case 301:
3055 return insertSpecialCharacter("\\ui", view);
3056 case 304:
3057 return insertSpecialCharacter("\\.I", view);
3058 case 305:
3059 return insertSpecialCharacter("\\i", view);
3060 case 308:
3061 return insertSpecialCharacter("\\^J", view);
3062 case 309:
3063 return insertSpecialCharacter("\\^j", view);
3064 case 310:
3065 return insertSpecialCharacter("\\cK", view);
3066 case 311:
3067 return insertSpecialCharacter("\\ck", view);
3068 case 313:
3069 return insertSpecialCharacter("\\'L", view);
3070 case 314:
3071 return insertSpecialCharacter("\\'l", view);
3072 case 315:
3073 return insertSpecialCharacter("\\cL", view);
3074 case 316:
3075 return insertSpecialCharacter("\\cl", view);
3076 case 317:
3077 return insertSpecialCharacter("\\vL", view);
3078 case 318:
3079 return insertSpecialCharacter("\\vl", view);
3080 case 323:
3081 return insertSpecialCharacter("\\'N", view);
3082 case 324:
3083 return insertSpecialCharacter("\\'n", view);
3084 case 325:
3085 return insertSpecialCharacter("\\cN", view);
3086 case 326:
3087 return insertSpecialCharacter("\\cn", view);
3088 case 327:
3089 return insertSpecialCharacter("\\vN", view);
3090 case 328:
3091 return insertSpecialCharacter("\\vn", view);
3092 case 332:
3093 return insertSpecialCharacter("\\=O", view);
3094 case 333:
3095 return insertSpecialCharacter("\\=o", view);
3096 case 334:
3097 return insertSpecialCharacter("\\uO", view);
3098 case 335:
3099 return insertSpecialCharacter("\\uo", view);
3100 case 336:
3101 return insertSpecialCharacter("\\HO", view);
3102 case 337:
3103 return insertSpecialCharacter("\\Ho", view);
3104 case 338:
3105 return insertSpecialCharacter("\\OE", view);
3106 case 339:
3107 return insertSpecialCharacter("\\oe", view);
3108 case 340:
3109 return insertSpecialCharacter("\\'R", view);
3110 case 341:
3111 return insertSpecialCharacter("\\'r", view);
3112 case 342:
3113 return insertSpecialCharacter("\\cR", view);
3114 case 343:
3115 return insertSpecialCharacter("\\cr", view);
3116 case 344:
3117 return insertSpecialCharacter("\\vR", view);
3118 case 345:
3119 return insertSpecialCharacter("\\vr", view);
3120 case 346:
3121 return insertSpecialCharacter("\\'S", view);
3122 case 347:
3123 return insertSpecialCharacter("\\'s", view);
3124 case 348:
3125 return insertSpecialCharacter("\\^S", view);
3126 case 349:
3127 return insertSpecialCharacter("\\^s", view);
3128 case 350:
3129 return insertSpecialCharacter("\\cS", view);
3130 case 351:
3131 return insertSpecialCharacter("\\cs", view);
3132 case 352:
3133 return insertSpecialCharacter("\\vS", view);
3134 case 353:
3135 return insertSpecialCharacter("\\vs", view);
3136 case 354:
3137 return insertSpecialCharacter("\\cT", view);
3138 case 355:
3139 return insertSpecialCharacter("\\ct", view);
3140 case 356:
3141 return insertSpecialCharacter("\\vT", view);
3142 case 357:
3143 return insertSpecialCharacter("\\vt", view);
3144 case 358:
3145 return insertSpecialCharacter("\\=T", view);
3146 case 359:
3147 return insertSpecialCharacter("\\=t", view);
3148 case 360:
3149 return insertSpecialCharacter("\\~U", view);
3150 case 361:
3151 return insertSpecialCharacter("\\~u", view);
3152 case 362:
3153 return insertSpecialCharacter("\\=U", view);
3154 case 363:
3155 return insertSpecialCharacter("\\=u", view);
3156 case 364:
3157 return insertSpecialCharacter("\\uU", view);
3158 case 365:
3159 return insertSpecialCharacter("\\uu", view);
3160 case 366:
3161 return insertSpecialCharacter("\\AU", view);
3162 case 367:
3163 return insertSpecialCharacter("\\Au", view);
3164 case 368:
3165 return insertSpecialCharacter("\\HU", view);
3166 case 369:
3167 return insertSpecialCharacter("\\Hu", view);
3168 case 370:
3169 return insertSpecialCharacter("\\cU", view);
3170 case 371:
3171 return insertSpecialCharacter("\\cu", view);
3172 case 372:
3173 return insertSpecialCharacter("\\^W", view);
3174 case 373:
3175 return insertSpecialCharacter("\\^w", view);
3176 case 374:
3177 return insertSpecialCharacter("\\^Y", view);
3178 case 375:
3179 return insertSpecialCharacter("\\^y", view);
3180 case 376:
3181 return insertSpecialCharacter("\\\"Y", view);
3182 case 377:
3183 return insertSpecialCharacter("\\'Z", view);
3184 case 378:
3185 return insertSpecialCharacter("\\'z", view);
3186 case 379:
3187 return insertSpecialCharacter("\\.Z", view);
3188 case 380:
3189 return insertSpecialCharacter("\\.z", view);
3190 case 381:
3191 return insertSpecialCharacter("\\vZ", view);
3192 case 382:
3193 return insertSpecialCharacter("\\vz", view);
3194 default:
3195 return false;
3196 }
3197 }
3198
3199 // If allowed, inserts texString at current cursor postition. Startlingly similar to insertDoubleQuotes.
insertSpecialCharacter(const QString & texString,KTextEditor::View * view,const QString & dep)3200 bool EditorExtension::insertSpecialCharacter(const QString& texString, KTextEditor::View *view, const QString& dep)
3201 {
3202 // stop if special character replacement is disabled
3203 if (!m_specialCharacters) {
3204 return false;
3205 }
3206
3207 // return false if konsole has focus
3208 if(m_ki->texKonsole()->hasFocus()) {
3209 return false;
3210 }
3211
3212 // always return true for event handler
3213 view = determineView(view);
3214 if(!view) {
3215 return true;
3216 }
3217
3218 KTextEditor::Document *doc = view->document();
3219
3220 // Only change if we have a tex document
3221 if(!doc || !m_ki->extensions()->isTexFile(doc->url())) {
3222 return false;
3223 }
3224
3225 // In case of replace
3226 view->removeSelectionText();
3227
3228 int row, col;
3229 KTextEditor::Cursor cursor = view->cursorPosition();
3230 row = cursor.line();
3231 col = cursor.column();
3232
3233 // insert texString
3234 doc->insertText(KTextEditor::Cursor(row, col), texString);
3235
3236 KILE_DEBUG_MAIN << "Replacing with "<<texString;
3237
3238 // Check dependency
3239 if (!dep.isEmpty()) {
3240 QStringList packagelist = m_ki->allPackages();
3241 if(!packagelist.contains(dep)) {
3242 m_ki->errorHandler()->printMessage(KileTool::Error, i18n("You have to include the package %1 to use %2.", dep, texString), i18n("Missing Package"));
3243 KILE_DEBUG_MAIN << "Need package "<< dep;
3244 }
3245 }
3246
3247 return true;
3248 }
3249
3250 //////////////////// insert tabulator ////////////////////
3251
insertIntelligentTabulator(KTextEditor::View * view)3252 void EditorExtension::insertIntelligentTabulator(KTextEditor::View *view)
3253 {
3254 view = determineView(view);
3255 if(!view) {
3256 return;
3257 }
3258
3259 int row, col, currentRow, currentCol;
3260 QString envname,tab;
3261 QString prefix = " ";
3262
3263 KTextEditor::Cursor cursor = view->cursorPosition();
3264 currentRow = cursor.line();
3265 currentCol = cursor.column();
3266 if(findOpenedEnvironment(row, col, envname, view)) {
3267 // look if this is an environment with tabs
3268 tab = m_latexCommands->getTabulator(envname);
3269
3270 // try to align tabulator with textline above
3271 if(currentRow >= 1) {
3272 int tabpos = view->document()->line(currentRow - 1).indexOf('&', currentCol);
3273 if(tabpos >= 0) {
3274 currentCol = tabpos;
3275 prefix.clear();
3276 }
3277 }
3278 }
3279
3280 if(tab.isEmpty()) {
3281 tab = '&';
3282 }
3283 tab = prefix + tab + ' ';
3284
3285 view->document()->insertText(KTextEditor::Cursor(currentRow, currentCol), tab);
3286 view->setCursorPosition(KTextEditor::Cursor(currentRow, currentCol + tab.length()));
3287 }
3288
3289 //////////////////// autocomplete environment ////////////////////
3290
3291 // should we complete the current environment (call from LaTeXEventFilter)
3292
eventInsertEnvironment(KTextEditor::View * view)3293 bool EditorExtension::eventInsertEnvironment(KTextEditor::View *view)
3294 {
3295 if(!view) {
3296 return false;
3297 }
3298
3299 // don't complete environment, if we are
3300 // still working inside the completion box
3301 KTextEditor::CodeCompletionInterface *codeCompletionInterface
3302 = qobject_cast<KTextEditor::CodeCompletionInterface*>(view);
3303 if(codeCompletionInterface && codeCompletionInterface->isCompletionActive()) {
3304 return false;
3305 }
3306
3307 int row = view->cursorPosition().line();
3308 int col = view->cursorPositionVirtual().column();
3309 QString line = view->document()->line(row).left(col);
3310
3311 int pos = m_regexpEnter.indexIn(line);
3312 if (pos != -1) {
3313 line = m_regexpEnter.cap(1);
3314 for(int i = 0; i < line.length(); ++i) {
3315 if(!line[i].isSpace()) {
3316 line[i] = ' ';
3317 }
3318 }
3319
3320 QString envname, endenv;
3321 if(m_regexpEnter.cap(2) == "\\[") {
3322 envname = m_regexpEnter.cap(2);
3323 endenv = "\\]\n";
3324 }
3325 else {
3326 envname = m_regexpEnter.cap(4);
3327 endenv = m_regexpEnter.cap(2).replace("\\begin","\\end") + '\n';
3328 }
3329
3330 if(shouldCompleteEnv(envname, view)) {
3331 QString item = m_latexCommands->isListEnv(envname) ? "\\item " : QString();
3332 view->document()->insertText(KTextEditor::Cursor(row, col), '\n' + line + m_envAutoIndent + item + '\n' + line + endenv);
3333 view->setCursorPosition(KTextEditor::Cursor(row + 1, line.length() + m_envAutoIndent.length() + item.length()));
3334 return true;
3335 }
3336 }
3337 return false;
3338 }
3339
shouldCompleteEnv(const QString & env,KTextEditor::View * view)3340 bool EditorExtension::shouldCompleteEnv(const QString &env, KTextEditor::View *view)
3341 {
3342 KILE_DEBUG_MAIN << "===EditorExtension::shouldCompleteEnv( " << env << " )===";
3343 QRegExp reTestBegin,reTestEnd;
3344 if(env == "\\[") {
3345 KILE_DEBUG_MAIN << "display style";
3346 reTestBegin.setPattern("(?:[^\\\\]|^)\\\\\\[");
3347 // the first part is a non-capturing bracket (?:...) and we check if we don't have a backslash in front,
3348 // or that we are at the begin of the line
3349 reTestEnd.setPattern("(?:[^\\\\]|^)\\\\\\]");
3350 }
3351 else {
3352 reTestBegin.setPattern("(?:[^\\\\]|^)\\\\begin\\s*\\{" + QRegExp::escape(env) + "\\}");
3353 reTestEnd.setPattern("(?:[^\\\\]|^)\\\\end\\s*\\{" + QRegExp::escape(env) + "\\}");
3354 }
3355
3356 int num = view->document()->lines();
3357 int numBeginsFound = 0;
3358 int numEndsFound = 0;
3359 KTextEditor::Cursor cursor = view->cursorPosition();
3360 int realLine = cursor.line();
3361
3362 for(int i = realLine; i < num; ++i) {
3363 numBeginsFound += view->document()->line(i).count(reTestBegin);
3364 numEndsFound += view->document()->line(i).count(reTestEnd);
3365 KILE_DEBUG_MAIN << "line is " << i << " numBeginsFound = " << numBeginsFound << " , " << "numEndsFound = " << numEndsFound;
3366 if(numEndsFound >= numBeginsFound) {
3367 return false;
3368 }
3369 else if(numEndsFound == 0 && numBeginsFound > 1) {
3370 return true;
3371 }
3372 else if(numBeginsFound > 2 || numEndsFound > 1) {
3373 return true; // terminate the search
3374 }
3375 }
3376
3377 return true;
3378 }
3379
getWhiteSpace(const QString & s)3380 QString EditorExtension::getWhiteSpace(const QString &s)
3381 {
3382 QString whitespace = s;
3383
3384 for(int i = 0; i < whitespace.length(); ++i) {
3385 if(!whitespace[i].isSpace()) {
3386 whitespace[i] = ' ';
3387 }
3388 }
3389 return whitespace;
3390 }
3391
3392 //////////////////// inside verbatim commands ////////////////////
3393
insideVerbatim(KTextEditor::View * view)3394 bool EditorExtension::insideVerbatim(KTextEditor::View *view)
3395 {
3396 int rowEnv, colEnv;
3397 QString nameEnv;
3398
3399 if(findOpenedEnvironment(rowEnv, colEnv, nameEnv, view)) {
3400 if(m_latexCommands->isVerbatimEnv(nameEnv)) {
3401 return true;
3402 }
3403 }
3404
3405 return false;
3406 }
3407
insideVerb(KTextEditor::View * view)3408 bool EditorExtension::insideVerb(KTextEditor::View *view)
3409 {
3410 view = determineView(view);
3411 if(!view) {
3412 return false;
3413 }
3414
3415 // get current position
3416 int row, col;
3417 KTextEditor::Cursor cursor = view->cursorPosition();
3418 row = cursor.line();
3419 col = cursor.column();
3420
3421 int startpos = 0;
3422 QString textline = getTextLineReal(view->document(),row);
3423 QRegExp reg("\\\\verb(\\*?)(.)");
3424 while(true) {
3425 int pos = textline.indexOf(reg,startpos);
3426 if(pos < 0 || col < pos + 6 + reg.cap(1).length()) {
3427 return false;
3428 }
3429 pos = textline.indexOf(reg.cap(2), pos + 6 + reg.cap(1).length());
3430 if(pos < 0 || col <= pos) {
3431 return true;
3432 }
3433
3434 startpos = pos + 1;
3435 }
3436 }
3437
3438 //////////////////// goto sectioning command ////////////////////
3439
gotoNextSectioning()3440 void EditorExtension::gotoNextSectioning()
3441 {
3442 gotoSectioning(false);
3443 }
3444
gotoPrevSectioning()3445 void EditorExtension::gotoPrevSectioning()
3446 {
3447 gotoSectioning(true);
3448 }
3449
gotoSectioning(bool backwards,KTextEditor::View * view)3450 void EditorExtension::gotoSectioning(bool backwards, KTextEditor::View *view)
3451 {
3452 view = determineView(view);
3453 if(!view) {
3454 return;
3455 }
3456
3457 int rowFound, colFound;
3458 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
3459 m_ki->viewManager()->updateStructure(true);
3460 }
3461 if(m_ki->structureWidget()->findSectioning(Q_NULLPTR,view->document(), view->cursorPosition().line(), view->cursorPosition().column(), backwards, false, rowFound, colFound)) {
3462 view->setCursorPosition(KTextEditor::Cursor(rowFound, colFound));
3463 }
3464 }
3465
3466 //////////////////// sectioning popup ////////////////////
3467
sectioningCommand(KileWidget::StructureViewItem * item,int id)3468 void EditorExtension::sectioningCommand(KileWidget::StructureViewItem *item, int id)
3469 {
3470 KTextEditor::View *view = determineView(Q_NULLPTR);
3471 if(!view) {
3472 return;
3473 }
3474
3475 if(!item) {
3476 return;
3477 }
3478 KTextEditor::Document *doc = view->document();
3479
3480 // try to determine the whole secting
3481 // get the start auf the selected sectioning
3482 int row, col, row1, col1, row2, col2;
3483 row = row1 = item->startline() - 1;
3484 col = col1 = item->startcol() - 1;
3485
3486 // FIXME tbraun make this more clever, introdoce in kiledocinfo a flag which can be easily queried for that, so that we don'
3487 // check, if the document was changed in the meantime
3488 QRegExp reg( "\\\\(part|chapter|section|subsection|subsubsection|paragraph|subparagraph)\\*?\\s*(\\{|\\[)" );
3489 QString textline = getTextLineReal(doc,row1);
3490 if(reg.indexIn(textline, col1) != col1) {
3491 m_ki->errorHandler()->clearMessages();
3492 m_ki->errorHandler()->printMessage(KileTool::Error,
3493 i18n("The document was modified and the structure view should be updated, before starting such an operation."),
3494 i18n("Structure View Error") );
3495 return;
3496 }
3497
3498 // increase cursor position and find the following sectioning command
3499 if(!increaseCursorPosition(doc, row, col)) {
3500 return;
3501 }
3502 if (!m_ki->structureWidget()->findSectioning(item, doc, row, col, false, true, row2, col2)) {
3503 // or the end of the document
3504 // if there is a '\end{document} command, we have to exclude it
3505 if (!findEndOfDocument(doc, row, col, row2, col2)) {
3506 row2 = doc->lines() - 1;
3507 col2 = 0;
3508 }
3509 }
3510
3511 // clear selection and make cursor position visible
3512 view->removeSelection();
3513 view->setCursorPosition(KTextEditor::Cursor(row1, col1));
3514
3515 QString text;
3516 KTextEditor::Document::EditingTransaction transaction(doc);
3517 switch (id) {
3518 case KileWidget::StructureWidget::SectioningCut:
3519 QApplication::clipboard()->setText(doc->text(KTextEditor::Range(row1, col1, row2, col2))); // copy -> clipboard
3520 doc->removeText(KTextEditor::Range(row1, col1, row2, col2)); // delete
3521 break;
3522 case KileWidget::StructureWidget::SectioningCopy:
3523 QApplication::clipboard()->setText(doc->text(KTextEditor::Range(row1, col1, row2, col2))); // copy -> clipboard
3524 break;
3525 case KileWidget::StructureWidget::SectioningPaste:
3526 text = QApplication::clipboard()->text(); // clipboard -> text
3527 if(!text.isEmpty()) {
3528 view->setCursorPosition(KTextEditor::Cursor(row2, col2)); // insert
3529 view->insertText(text + '\n');
3530 }
3531 break;
3532 case KileWidget::StructureWidget::SectioningSelect:
3533 view->setSelection(KTextEditor::Range(row1, col1, row2, col2)); // select
3534 break;
3535 case KileWidget::StructureWidget::SectioningDelete:
3536 doc->removeText(KTextEditor::Range(row1, col1, row2, col2)); // delete
3537 break;
3538 case KileWidget::StructureWidget::SectioningComment:
3539 commentLaTeX(doc, KTextEditor::Range(row1, col1, row2, col2));
3540 break;
3541 case KileWidget::StructureWidget::SectioningPreview:
3542 view->setSelection(KTextEditor::Range(row1, col1, row2, col2)); // quick preview
3543 m_ki->quickPreview()->previewSelection(view, false);
3544 view->removeSelection();
3545 break;
3546 }
3547 transaction.finish();
3548
3549 // update structure view, because it has changed
3550 if(id == KileWidget::StructureWidget::SectioningDelete || id == KileWidget::StructureWidget::SectioningComment) {
3551 m_ki->viewManager()->updateStructure(true);
3552 }
3553
3554 }
3555
findEndOfDocument(KTextEditor::Document * doc,int row,int col,int & rowFound,int & colFound)3556 bool EditorExtension::findEndOfDocument(KTextEditor::Document *doc, int row, int col, int &rowFound, int &colFound)
3557 {
3558 KTextEditor::Range documentRange(KTextEditor::Cursor(row, col), doc->documentEnd());
3559 QVector<KTextEditor::Range> foundRanges = doc->searchText(documentRange, "\\end{document}");
3560
3561 if(foundRanges.size() >= 1) {
3562 KTextEditor::Range range = foundRanges.first();
3563 if(range.isValid()) {
3564 rowFound = range.start().line();
3565 colFound = range.start().column();
3566 return true;
3567 }
3568 }
3569
3570 return false;
3571 }
3572
extractIndentationString(KTextEditor::View * view,int line)3573 QString EditorExtension::extractIndentationString(KTextEditor::View *view, int line)
3574 {
3575 KTextEditor::Document* doc = view->document();
3576
3577 if(!doc) {
3578 return QString();
3579 }
3580
3581 const QString lineString = doc->line(line);
3582 int lastWhiteSpaceCharIndex = -1;
3583
3584 for(int i = 0; i < lineString.length(); ++i) {
3585 if(!lineString[i].isSpace()) {
3586 break;
3587 }
3588 ++lastWhiteSpaceCharIndex;
3589 }
3590 return lineString.left(lastWhiteSpaceCharIndex + 1);
3591 }
3592
3593 }
3594
3595