• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • kate
 

kate

  • kate
  • part
kateautoindent.cpp
1/* This file is part of the KDE libraries
2 Copyright (C) 2003 Jesse Yurkovich <yurkjes@iit.edu>
3 Copyright (C) 2004 >Anders Lund <anders@alweb.dk> (KateVarIndent class)
4 Copyright (C) 2005 Dominik Haumann <dhdev@gmx.de> (basic support for config page)
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License version 2 as published by the Free Software Foundation.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19*/
20
21#include "kateautoindent.h"
22#include "kateautoindent.moc"
23
24#include "kateconfig.h"
25#include "katehighlight.h"
26#include "katefactory.h"
27#include "katejscript.h"
28#include "kateview.h"
29
30#include <tdelocale.h>
31#include <kdebug.h>
32#include <tdepopupmenu.h>
33
34#include <cctype>
35
36//BEGIN KateAutoIndent
37
38KateAutoIndent *KateAutoIndent::createIndenter (KateDocument *doc, uint mode)
39{
40 if (mode == KateDocumentConfig::imNormal)
41 return new KateNormalIndent (doc);
42 else if (mode == KateDocumentConfig::imCStyle)
43 return new KateCSmartIndent (doc);
44 else if (mode == KateDocumentConfig::imPythonStyle)
45 return new KatePythonIndent (doc);
46 else if (mode == KateDocumentConfig::imXmlStyle)
47 return new KateXmlIndent (doc);
48 else if (mode == KateDocumentConfig::imCSAndS)
49 return new KateCSAndSIndent (doc);
50 else if ( mode == KateDocumentConfig::imVarIndent )
51 return new KateVarIndent ( doc );
52// else if ( mode == KateDocumentConfig::imScriptIndent)
53// return new KateScriptIndent ( doc );
54
55 return new KateAutoIndent (doc);
56}
57
58TQStringList KateAutoIndent::listModes ()
59{
60 TQStringList l;
61
62 l << modeDescription(KateDocumentConfig::imNone);
63 l << modeDescription(KateDocumentConfig::imNormal);
64 l << modeDescription(KateDocumentConfig::imCStyle);
65 l << modeDescription(KateDocumentConfig::imPythonStyle);
66 l << modeDescription(KateDocumentConfig::imXmlStyle);
67 l << modeDescription(KateDocumentConfig::imCSAndS);
68 l << modeDescription(KateDocumentConfig::imVarIndent);
69// l << modeDescription(KateDocumentConfig::imScriptIndent);
70
71 return l;
72}
73
74TQString KateAutoIndent::modeName (uint mode)
75{
76 if (mode == KateDocumentConfig::imNormal)
77 return TQString ("normal");
78 else if (mode == KateDocumentConfig::imCStyle)
79 return TQString ("cstyle");
80 else if (mode == KateDocumentConfig::imPythonStyle)
81 return TQString ("python");
82 else if (mode == KateDocumentConfig::imXmlStyle)
83 return TQString ("xml");
84 else if (mode == KateDocumentConfig::imCSAndS)
85 return TQString ("csands");
86 else if ( mode == KateDocumentConfig::imVarIndent )
87 return TQString( "varindent" );
88// else if ( mode == KateDocumentConfig::imScriptIndent )
89// return TQString( "scriptindent" );
90
91 return TQString ("none");
92}
93
94TQString KateAutoIndent::modeDescription (uint mode)
95{
96 if (mode == KateDocumentConfig::imNormal)
97 return i18n ("Normal");
98 else if (mode == KateDocumentConfig::imCStyle)
99 return i18n ("C Style");
100 else if (mode == KateDocumentConfig::imPythonStyle)
101 return i18n ("Python Style");
102 else if (mode == KateDocumentConfig::imXmlStyle)
103 return i18n ("XML Style");
104 else if (mode == KateDocumentConfig::imCSAndS)
105 return i18n ("S&S C Style");
106 else if ( mode == KateDocumentConfig::imVarIndent )
107 return i18n("Variable Based Indenter");
108// else if ( mode == KateDocumentConfig::imScriptIndent )
109// return i18n("JavaScript Indenter");
110
111 return i18n ("None");
112}
113
114uint KateAutoIndent::modeNumber (const TQString &name)
115{
116 if (modeName(KateDocumentConfig::imNormal) == name)
117 return KateDocumentConfig::imNormal;
118 else if (modeName(KateDocumentConfig::imCStyle) == name)
119 return KateDocumentConfig::imCStyle;
120 else if (modeName(KateDocumentConfig::imPythonStyle) == name)
121 return KateDocumentConfig::imPythonStyle;
122 else if (modeName(KateDocumentConfig::imXmlStyle) == name)
123 return KateDocumentConfig::imXmlStyle;
124 else if (modeName(KateDocumentConfig::imCSAndS) == name)
125 return KateDocumentConfig::imCSAndS;
126 else if ( modeName( KateDocumentConfig::imVarIndent ) == name )
127 return KateDocumentConfig::imVarIndent;
128// else if ( modeName( KateDocumentConfig::imScriptIndent ) == name )
129// return KateDocumentConfig::imScriptIndent;
130
131 return KateDocumentConfig::imNone;
132}
133
134bool KateAutoIndent::hasConfigPage (uint mode)
135{
136// if ( mode == KateDocumentConfig::imScriptIndent )
137// return true;
138
139 return false;
140}
141
142IndenterConfigPage* KateAutoIndent::configPage(TQWidget *parent, uint mode)
143{
144// if ( mode == KateDocumentConfig::imScriptIndent )
145// return new ScriptIndentConfigPage(parent, "script_indent_config_page");
146
147 return 0;
148}
149
150KateAutoIndent::KateAutoIndent (KateDocument *_doc)
151: TQObject(), doc(_doc)
152{
153}
154KateAutoIndent::~KateAutoIndent ()
155{
156}
157
158//END KateAutoIndent
159
160//BEGIN KateViewIndentAction
161KateViewIndentationAction::KateViewIndentationAction(KateDocument *_doc, const TQString& text, TQObject* parent, const char* name)
162 : TDEActionMenu (text, parent, name), doc(_doc)
163{
164 connect(popupMenu(),TQT_SIGNAL(aboutToShow()),this,TQT_SLOT(slotAboutToShow()));
165}
166
167void KateViewIndentationAction::slotAboutToShow()
168{
169 TQStringList modes = KateAutoIndent::listModes ();
170
171 popupMenu()->clear ();
172 for (uint z=0; z<modes.size(); ++z)
173 popupMenu()->insertItem ( '&' + KateAutoIndent::modeDescription(z).replace('&', "&&"), this, TQT_SLOT(setMode(int)), 0, z);
174
175 popupMenu()->setItemChecked (doc->config()->indentationMode(), true);
176}
177
178void KateViewIndentationAction::setMode (int mode)
179{
180 doc->config()->setIndentationMode((uint)mode);
181}
182//END KateViewIndentationAction
183
184//BEGIN KateNormalIndent
185
186KateNormalIndent::KateNormalIndent (KateDocument *_doc)
187 : KateAutoIndent (_doc)
188{
189 // if highlighting changes, update attributes
190 connect(_doc, TQT_SIGNAL(hlChanged()), this, TQT_SLOT(updateConfig()));
191}
192
193KateNormalIndent::~KateNormalIndent ()
194{
195}
196
197void KateNormalIndent::updateConfig ()
198{
199 KateDocumentConfig *config = doc->config();
200
201 useSpaces = config->configFlags() & KateDocument::cfSpaceIndent || config->configFlags() & KateDocumentConfig::cfReplaceTabsDyn;
202 mixedIndent = useSpaces && config->configFlags() & KateDocumentConfig::cfMixedIndent;
203 keepProfile = config->configFlags() & KateDocument::cfKeepIndentProfile;
204 tabWidth = config->tabWidth();
205 indentWidth = useSpaces? config->indentationWidth() : tabWidth;
206
207 commentAttrib = 255;
208 doxyCommentAttrib = 255;
209 regionAttrib = 255;
210 symbolAttrib = 255;
211 alertAttrib = 255;
212 tagAttrib = 255;
213 wordAttrib = 255;
214 keywordAttrib = 255;
215 normalAttrib = 255;
216 extensionAttrib = 255;
217 preprocessorAttrib = 255;
218 stringAttrib = 255;
219 charAttrib = 255;
220
221 KateHlItemDataList items;
222 doc->highlight()->getKateHlItemDataListCopy (0, items);
223
224 for (uint i=0; i<items.count(); i++)
225 {
226 TQString name = items.at(i)->name;
227 if (name.find("Comment") != -1 && commentAttrib == 255)
228 {
229 commentAttrib = i;
230 }
231 else if (name.find("Region Marker") != -1 && regionAttrib == 255)
232 {
233 regionAttrib = i;
234 }
235 else if (name.find("Symbol") != -1 && symbolAttrib == 255)
236 {
237 symbolAttrib = i;
238 }
239 else if (name.find("Alert") != -1)
240 {
241 alertAttrib = i;
242 }
243 else if (name.find("Comment") != -1 && commentAttrib != 255 && doxyCommentAttrib == 255)
244 {
245 doxyCommentAttrib = i;
246 }
247 else if (name.find("Tags") != -1 && tagAttrib == 255)
248 {
249 tagAttrib = i;
250 }
251 else if (name.find("Word") != -1 && wordAttrib == 255)
252 {
253 wordAttrib = i;
254 }
255 else if (name.find("Keyword") != -1 && keywordAttrib == 255)
256 {
257 keywordAttrib = i;
258 }
259 else if (name.find("Normal") != -1 && normalAttrib == 255)
260 {
261 normalAttrib = i;
262 }
263 else if (name.find("Extensions") != -1 && extensionAttrib == 255)
264 {
265 extensionAttrib = i;
266 }
267 else if (name.find("Preprocessor") != -1 && preprocessorAttrib == 255)
268 {
269 preprocessorAttrib = i;
270 }
271 else if (name.find("String") != -1 && stringAttrib == 255)
272 {
273 stringAttrib = i;
274 }
275 else if (name.find("Char") != -1 && charAttrib == 255)
276 {
277 charAttrib = i;
278 }
279 }
280}
281
282bool KateNormalIndent::isBalanced (KateDocCursor &begin, const KateDocCursor &end, TQChar open, TQChar close, uint &pos) const
283{
284 int parenOpen = 0;
285 bool atLeastOne = false;
286 bool getNext = false;
287
288 pos = doc->plainKateTextLine(begin.line())->firstChar();
289
290 // Iterate one-by-one finding opening and closing chars
291 // Assume that open and close are 'Symbol' characters
292 while (begin < end)
293 {
294 TQChar c = begin.currentChar();
295 if (begin.currentAttrib() == symbolAttrib)
296 {
297 if (c == open)
298 {
299 if (!atLeastOne)
300 {
301 atLeastOne = true;
302 getNext = true;
303 pos = measureIndent(begin) + 1;
304 }
305 parenOpen++;
306 }
307 else if (c == close)
308 {
309 parenOpen--;
310 }
311 }
312 else if (getNext && !c.isSpace())
313 {
314 getNext = false;
315 pos = measureIndent(begin);
316 }
317
318 if (atLeastOne && parenOpen <= 0)
319 return true;
320
321 if (!begin.moveForward(1))
322 break;
323 }
324
325 return (atLeastOne) ? false : true;
326}
327
328bool KateNormalIndent::skipBlanks (KateDocCursor &cur, KateDocCursor &max, bool newline) const
329{
330 int curLine = cur.line();
331 if (newline)
332 cur.moveForward(1);
333
334 if (cur >= max)
335 return false;
336
337 do
338 {
339 uchar attrib = cur.currentAttrib();
340 const TQString hlFile = doc->highlight()->hlKeyForAttrib( attrib );
341
342 if (attrib != commentAttrib && attrib != regionAttrib && attrib != alertAttrib && attrib != preprocessorAttrib && !hlFile.endsWith("doxygen.xml"))
343 {
344 TQChar c = cur.currentChar();
345 if (!c.isNull() && !c.isSpace())
346 break;
347 }
348
349 if (!cur.moveForward(1))
350 {
351 // not able to move forward, so set cur to max
352 cur = max;
353 break;
354 }
355 // Make sure col is 0 if we spill into next line i.e. count the '\n' as a character
356 if (curLine != cur.line())
357 {
358 if (!newline)
359 break;
360 curLine = cur.line();
361 cur.setCol(0);
362 }
363 } while (cur < max);
364
365 if (cur > max)
366 cur = max;
367 return true;
368}
369
370uint KateNormalIndent::measureIndent (KateDocCursor &cur) const
371{
372 // We cannot short-cut by checking for useSpaces because there may be
373 // tabs in the line despite this setting.
374
375 return doc->plainKateTextLine(cur.line())->cursorX(cur.col(), tabWidth);
376}
377
378TQString KateNormalIndent::tabString(uint pos) const
379{
380 TQString s;
381 pos = kMin (pos, 80U); // sanity check for large values of pos
382
383 if (!useSpaces || mixedIndent)
384 {
385 while (pos >= tabWidth)
386 {
387 s += '\t';
388 pos -= tabWidth;
389 }
390 }
391 while (pos > 0)
392 {
393 s += ' ';
394 pos--;
395 }
396 return s;
397}
398
399void KateNormalIndent::processNewline (KateDocCursor &begin, bool /*needContinue*/)
400{
401 int line = begin.line() - 1;
402 int pos = begin.col();
403
404 while ((line > 0) && (pos < 0)) // search a not empty text line
405 pos = doc->plainKateTextLine(--line)->firstChar();
406
407 if (pos > 0)
408 {
409 TQString filler = doc->text(line, 0, line, pos);
410 doc->insertText(begin.line(), 0, filler);
411 begin.setCol(filler.length());
412 }
413 else
414 begin.setCol(0);
415}
416
417//END
418
419//BEGIN KateCSmartIndent
420
421KateCSmartIndent::KateCSmartIndent (KateDocument *doc)
422: KateNormalIndent (doc),
423 allowSemi (false),
424 processingBlock (false)
425{
426 kdDebug(13030)<<"CREATING KATECSMART INTDETER"<<endl;
427}
428
429KateCSmartIndent::~KateCSmartIndent ()
430{
431
432}
433
434void KateCSmartIndent::processLine (KateDocCursor &line)
435{
436 kdDebug(13030)<<"PROCESSING LINE "<<line.line()<<endl;
437 KateTextLine::Ptr textLine = doc->plainKateTextLine(line.line());
438
439 int firstChar = textLine->firstChar();
440 // Empty line is worthless ... but only when doing more than 1 line
441 if (firstChar == -1 && processingBlock)
442 return;
443
444 uint indent = 0;
445
446 // TODO Here we do not check for beginning and ending comments ...
447 TQChar first = textLine->getChar(firstChar);
448 TQChar last = textLine->getChar(textLine->lastChar());
449
450 if (first == '}')
451 {
452 indent = findOpeningBrace(line);
453 }
454 else if (first == ')')
455 {
456 indent = findOpeningParen(line);
457 }
458 else if (first == '{')
459 {
460 // If this is the first brace, we keep the indent at 0
461 KateDocCursor temp(line.line(), firstChar, doc);
462 if (!firstOpeningBrace(temp))
463 indent = calcIndent(temp, false);
464 }
465 else if (first == ':')
466 {
467 // Initialization lists (handle c++ and c#)
468 int pos = findOpeningBrace(line);
469 if (pos == 0)
470 indent = indentWidth;
471 else
472 indent = pos + (indentWidth * 2);
473 }
474 else if (last == ':')
475 {
476 if (textLine->stringAtPos (firstChar, "case") ||
477 textLine->stringAtPos (firstChar, "default") ||
478 textLine->stringAtPos (firstChar, "public") ||
479 textLine->stringAtPos (firstChar, "private") ||
480 textLine->stringAtPos (firstChar, "protected") ||
481 textLine->stringAtPos (firstChar, "signals") ||
482 textLine->stringAtPos (firstChar, "Q_SIGNALS") ||
483 textLine->stringAtPos (firstChar, "Q_SLOTS") ||
484 textLine->stringAtPos (firstChar, "slots"))
485 {
486 indent = findOpeningBrace(line) + indentWidth;
487 }
488 }
489 else if (first == '*')
490 {
491 if (last == '/')
492 {
493 int lineEnd = textLine->lastChar();
494 if (lineEnd > 0 && textLine->getChar(lineEnd - 1) == '*')
495 {
496 indent = findOpeningComment(line);
497 if (textLine->attribute(firstChar) == doxyCommentAttrib)
498 indent++;
499 }
500 else
501 return;
502 }
503 else
504 {
505 KateDocCursor temp = line;
506 if (textLine->attribute(firstChar) == doxyCommentAttrib)
507 indent = calcIndent(temp, false) + 1;
508 else
509 indent = calcIndent(temp, true);
510 }
511 }
512 else if (first == '#')
513 {
514 // c# regions
515 if (textLine->stringAtPos (firstChar, "#region") ||
516 textLine->stringAtPos (firstChar, "#endregion"))
517 {
518 KateDocCursor temp = line;
519 indent = calcIndent(temp, true);
520 }
521 }
522 else
523 {
524 // Everything else ...
525 if (first == '/' && last != '/')
526 return;
527
528 KateDocCursor temp = line;
529 indent = calcIndent(temp, true);
530 if (indent == 0)
531 {
532 KateNormalIndent::processNewline(line, true);
533 return;
534 }
535 }
536
537 // Slightly faster if we don't indent what we don't have to
538 if (indent != measureIndent(line) || first == '}' || first == '{' || first == '#')
539 {
540 doc->removeText(line.line(), 0, line.line(), firstChar);
541 TQString filler = tabString(indent);
542 if (indent > 0) doc->insertText(line.line(), 0, filler);
543 if (!processingBlock) line.setCol(filler.length());
544 }
545}
546
547void KateCSmartIndent::processSection (const KateDocCursor &begin, const KateDocCursor &end)
548{
549 kdDebug(13030)<<"PROCESS SECTION"<<endl;
550 KateDocCursor cur = begin;
551 TQTime t;
552 t.start();
553
554 processingBlock = (end.line() - cur.line() > 0) ? true : false;
555
556 while (cur.line() <= end.line())
557 {
558 processLine (cur);
559 if (!cur.gotoNextLine())
560 break;
561 }
562
563 processingBlock = false;
564 kdDebug(13030) << "+++ total: " << t.elapsed() << endl;
565}
566
567bool KateCSmartIndent::handleDoxygen (KateDocCursor &begin)
568{
569 // Factor out the rather involved Doxygen stuff here ...
570 int line = begin.line();
571 int first = -1;
572 while ((line > 0) && (first < 0))
573 first = doc->plainKateTextLine(--line)->firstChar();
574
575 if (first >= 0)
576 {
577 KateTextLine::Ptr textLine = doc->plainKateTextLine(line);
578 bool insideDoxygen = false;
579 bool justAfterDoxygen = false;
580 if (textLine->attribute(first) == doxyCommentAttrib || textLine->attribute(textLine->lastChar()) == doxyCommentAttrib)
581 {
582 const int last = textLine->lastChar();
583 if (last <= 0 || !(justAfterDoxygen = textLine->stringAtPos(last-1, "*/")))
584 insideDoxygen = true;
585 if (justAfterDoxygen)
586 justAfterDoxygen &= textLine->string().find("/**") < 0;
587 while (textLine->attribute(first) != doxyCommentAttrib && first <= textLine->lastChar())
588 first++;
589 if (textLine->stringAtPos(first, "//"))
590 return false;
591 }
592
593 // Align the *'s and then go ahead and insert one too ...
594 if (insideDoxygen)
595 {
596 textLine = doc->plainKateTextLine(begin.line());
597 first = textLine->firstChar();
598 int indent = findOpeningComment(begin);
599 TQString filler = tabString (indent);
600
601 bool doxygenAutoInsert = doc->config()->configFlags() & KateDocumentConfig::cfDoxygenAutoTyping;
602
603 if ( doxygenAutoInsert &&
604 ((first < 0) || (!textLine->stringAtPos(first, "*/") && !textLine->stringAtPos(first, "*"))))
605 {
606 filler = filler + " * ";
607 }
608
609 doc->removeText (begin.line(), 0, begin.line(), first);
610 doc->insertText (begin.line(), 0, filler);
611 begin.setCol(filler.length());
612
613 return true;
614 }
615 // Align position with beginning of doxygen comment. Otherwise the
616 // indentation is one too much.
617 else if (justAfterDoxygen)
618 {
619 textLine = doc->plainKateTextLine(begin.line());
620 first = textLine->firstChar();
621 int indent = findOpeningComment(begin);
622 TQString filler = tabString (indent);
623
624 doc->removeText (begin.line(), 0, begin.line(), first);
625 doc->insertText (begin.line(), 0, filler);
626 begin.setCol(filler.length());
627
628 return true;
629 }
630 }
631
632 return false;
633}
634
635void KateCSmartIndent::processNewline (KateDocCursor &begin, bool needContinue)
636{
637 if (!handleDoxygen (begin))
638 {
639 KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line());
640 bool inMiddle = textLine->firstChar() > -1;
641
642 int indent = calcIndent (begin, needContinue);
643
644 if (indent > 0 || inMiddle)
645 {
646 TQString filler = tabString (indent);
647 doc->insertText (begin.line(), 0, filler);
648 begin.setCol(filler.length());
649
650 // Handles cases where user hits enter at the beginning or middle of text
651 if (inMiddle)
652 {
653 processLine(begin);
654 begin.setCol(textLine->firstChar());
655 }
656 }
657 else
658 {
659 KateNormalIndent::processNewline (begin, needContinue);
660 }
661
662 if (begin.col() < 0)
663 begin.setCol(0);
664 }
665}
666
676static inline bool isColonImmune(const KateNormalIndent &indenter,
677 uchar attr1, uchar attr2,
678 TQChar prev1, TQChar prev2)
679{
680 return attr1 == indenter.preprocessorAttrib
681 // FIXME: no way to discriminate against multiline comment and single
682 // line comment. Therefore, using prev? is futile.
683 || attr1 == indenter.commentAttrib /*&& prev2 != '*' && prev1 != '/'*/
684 || attr1 == indenter.doxyCommentAttrib
685 || attr1 == indenter.stringAttrib && (attr2 != indenter.stringAttrib
686 || (prev1 != '"' || prev2 == '\\' && attr2 == indenter.charAttrib))
687 || prev1 == '\'' && attr1 != indenter.charAttrib;
688}
689
696static inline bool colonPermitsReindent(const KateNormalIndent &indenter,
697 const KateTextLine::Ptr &line,
698 int curCol
699 )
700{
701 const TQString txt = line->string(0,curCol);
702 // do we have any significant preceding colon?
703 for (int pos = 0; (pos = txt.find(':', pos)) >= 0; pos++) {
704 if (line->attribute(pos) == indenter.symbolAttrib)
705 // yes, it has already contributed to this line's indentation, don't
706 // indent again
707 return false;
708 }
709
710 // otherwise, check whether this colon is not within an influence
711 // immune attribute range
712 return !isColonImmune(indenter, line->attribute(curCol - 1),
713 line->attribute(curCol - 2),
714 txt[curCol - 1], txt[curCol - 2]);
715}
716
717void KateCSmartIndent::processChar(TQChar c)
718{
719 // You may be curious about 'n' among the triggers:
720 // It is used to discriminate C#'s #region/#endregion which are indented
721 // against normal preprocessing statements which aren't indented.
722 static const TQString triggers("}{)/:#n");
723 static const TQString firstTriggers("}{)/:#");
724 static const TQString lastTriggers(":n");
725 if (triggers.find(c) < 0)
726 return;
727
728 KateView *view = doc->activeView();
729 int curCol = view->cursorColumnReal() - 1;
730 KateDocCursor begin(view->cursorLine(), 0, doc);
731
732 KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line());
733 const TQChar curChar = textLine->getChar(curCol);
734 const int first = textLine->firstChar();
735 const TQChar firstChar = textLine->getChar(first);
736
737#if 0 // nice try
738 // Only indent on symbols or preprocessing directives -- never on
739 // anything else
740 kdDebug() << "curChar " << curChar << " curCol " << curCol << " textlen " << textLine->length() << " a " << textLine->attribute( curCol ) << " sym " << symbolAttrib << " pp " << preprocessorAttrib << endl;
741 if (!(((curChar == '#' || curChar == 'n')
742 && textLine->attribute( curCol ) == preprocessorAttrib)
743 || textLine->attribute( curCol ) == symbolAttrib)
744 )
745 return;
746 kdDebug() << "curChar " << curChar << endl;
747#endif
748
749 if (c == 'n')
750 {
751 if (firstChar != '#' || textLine->string(curCol-5, 5) != TQString::fromLatin1("regio"))
752 return;
753 }
754
755 if ( c == '/' )
756 {
757 // dominik: if line is "* /", change it to "*/"
758 if ( textLine->attribute( begin.col() ) == doxyCommentAttrib )
759 {
760 // if the first char exists and is a '*', and the next non-space-char
761 // is already the just typed '/', concatenate it to "*/".
762 if ( first != -1
763 && firstChar == '*'
764 && textLine->nextNonSpaceChar( first+1 ) == view->cursorColumnReal()-1 )
765 doc->removeText( view->cursorLine(), first+1, view->cursorLine(), view->cursorColumnReal()-1);
766 }
767
768 // ls: never have comments change the indentation.
769 return;
770 }
771
772 // ls: only reindent line if the user actually expects it
773 // I. e. take action on single braces on line or last colon, but inhibit
774 // any reindentation if any of those characters appear amidst some section
775 // of the line
776 const TQChar lastChar = textLine->getChar(textLine->lastChar());
777 int pos;
778 if (((c == firstChar && firstTriggers.find(firstChar) >= 0)
779 || (c == lastChar && lastTriggers.find(lastChar) >= 0))
780 && (c != ':' || colonPermitsReindent(*this, textLine, curCol)))
781 processLine(begin);
782}
783
784
785uint KateCSmartIndent::calcIndent(KateDocCursor &begin, bool needContinue)
786{
787 KateTextLine::Ptr textLine;
788 KateDocCursor cur = begin;
789
790 uint anchorIndent = 0;
791 int anchorPos = 0;
792 int parenCount = 0; // Possibly in a multiline for stmt. Used to skip ';' ...
793 bool found = false;
794 bool isSpecial = false;
795 bool potentialAnchorSeen = false;
796 bool isArg = false; // ...arg,<newline>
797 bool parenthesizedArg = false; // ...(arg,<newline>
798
799 //kdDebug(13030) << "calcIndent begin line:" << begin.line() << " col:" << begin.col() << endl;
800
801 // Find Indent Anchor Point
802 while (cur.gotoPreviousLine())
803 {
804 isSpecial = found = false;
805 textLine = doc->plainKateTextLine(cur.line());
806
807 // Skip comments and handle cases like if (...) { stmt;
808 int pos = textLine->lastChar();
809 int openCount = 0;
810 int otherAnchor = -1;
811 do
812 {
813 if (textLine->attribute(pos) == symbolAttrib)
814 {
815 TQChar tc = textLine->getChar (pos);
816 if ((tc == ';' || tc == ':' || tc == ',') && otherAnchor == -1 && parenCount <= 0) {
817 otherAnchor = pos, potentialAnchorSeen = true;
818 isArg = tc == ',';
819 } else if (tc == ')')
820 parenCount++;
821 else if (tc == '(')
822 parenCount--, parenthesizedArg = isArg, potentialAnchorSeen = true;
823 else if (tc == '}')
824 openCount--;
825 else if (tc == '{')
826 {
827 openCount++, potentialAnchorSeen = true;
828 if (openCount == 1)
829 break;
830 }
831 }
832 } while (--pos >= textLine->firstChar());
833
834 if (openCount != 0 || otherAnchor != -1)
835 {
836 found = true;
837 TQChar c;
838 if (openCount > 0)
839 c = '{';
840 else if (openCount < 0)
841 c = '}';
842 else if (otherAnchor >= 0)
843 c = textLine->getChar (otherAnchor);
844
845 int specialIndent = 0;
846 if (c == ':' && needContinue)
847 {
848 TQChar ch;
849 specialIndent = textLine->firstChar();
850 if (textLine->stringAtPos(specialIndent, "case"))
851 ch = textLine->getChar(specialIndent + 4);
852 else if (textLine->stringAtPos(specialIndent, "default"))
853 ch = textLine->getChar(specialIndent + 7);
854 else if (textLine->stringAtPos(specialIndent, "public"))
855 ch = textLine->getChar(specialIndent + 6);
856 else if (textLine->stringAtPos(specialIndent, "private"))
857 ch = textLine->getChar(specialIndent + 7);
858 else if (textLine->stringAtPos(specialIndent, "protected"))
859 ch = textLine->getChar(specialIndent + 9);
860 else if (textLine->stringAtPos(specialIndent, "signals"))
861 ch = textLine->getChar(specialIndent + 7);
862 else if (textLine->stringAtPos(specialIndent, "Q_SIGNALS"))
863 ch = textLine->getChar(specialIndent + 9);
864 else if (textLine->stringAtPos(specialIndent, "slots"))
865 ch = textLine->getChar(specialIndent + 5);
866 else if (textLine->stringAtPos(specialIndent, "Q_SLOTS"))
867 ch = textLine->getChar(specialIndent + 7);
868
869 if (ch.isNull() || (!ch.isSpace() && ch != '(' && ch != ':'))
870 continue;
871
872 KateDocCursor lineBegin = cur;
873 lineBegin.setCol(specialIndent);
874 specialIndent = measureIndent(lineBegin);
875 isSpecial = true;
876 }
877
878 // Move forward past blank lines
879 KateDocCursor skip = cur;
880 skip.setCol(textLine->lastChar());
881 bool result = skipBlanks(skip, begin, true);
882
883 anchorPos = skip.col();
884 anchorIndent = measureIndent(skip);
885
886 //kdDebug(13030) << "calcIndent anchorPos:" << anchorPos << " anchorIndent:" << anchorIndent << " at line:" << skip.line() << endl;
887
888 // Accept if it's before requested position or if it was special
889 if (result && skip < begin)
890 {
891 cur = skip;
892 break;
893 }
894 else if (isSpecial)
895 {
896 anchorIndent = specialIndent;
897 break;
898 }
899
900 // Are these on a line by themselves? (i.e. both last and first char)
901 if ((c == '{' || c == '}') && textLine->getChar(textLine->firstChar()) == c)
902 {
903 cur.setCol(anchorPos = textLine->firstChar());
904 anchorIndent = measureIndent (cur);
905 break;
906 }
907 }
908 }
909
910 // treat beginning of document as anchor position
911 if (cur.line() == 0 && cur.col() == 0 && potentialAnchorSeen)
912 found = true;
913
914 if (!found)
915 return 0;
916
917 uint continueIndent = (needContinue) ? calcContinue (cur, begin) : 0;
918 //kdDebug(13030) << "calcIndent continueIndent:" << continueIndent << endl;
919
920 // Move forward from anchor and determine last known reference character
921 // Braces take precedance over others ...
922 textLine = doc->plainKateTextLine(cur.line());
923 TQChar lastChar = textLine->getChar (anchorPos);
924 int lastLine = cur.line();
925 if (lastChar == '#' || lastChar == '[')
926 {
927 // Never continue if # or [ is encountered at this point here
928 // A fail-safe really... most likely an #include, #region, or a c# attribute
929 continueIndent = 0;
930 }
931
932 int openCount = 0;
933 while (cur.validPosition() && cur < begin)
934 {
935 if (!skipBlanks(cur, begin, true))
936 return isArg && !parenthesizedArg ? begin.col() : 0;
937
938 TQChar tc = cur.currentChar();
939 //kdDebug(13030) << " cur.line:" << cur.line() << " cur.col:" << cur.col() << " currentChar '" << tc << "' " << textLine->attribute(cur.col()) << endl;
940 if (cur == begin || tc.isNull())
941 break;
942
943 if (!tc.isSpace() && cur < begin)
944 {
945 uchar attrib = cur.currentAttrib();
946 if (tc == '{' && attrib == symbolAttrib)
947 openCount++;
948 else if (tc == '}' && attrib == symbolAttrib)
949 openCount--;
950
951 lastChar = tc;
952 lastLine = cur.line();
953 }
954 }
955 if (openCount > 0) // Open braces override
956 lastChar = '{';
957
958 uint indent = 0;
959 //kdDebug(13030) << "calcIndent lastChar '" << lastChar << "'" << endl;
960
961 if (lastChar == '{' || (lastChar == ':' && isSpecial && needContinue))
962 {
963 indent = anchorIndent + indentWidth;
964 }
965 else if (lastChar == '}')
966 {
967 indent = anchorIndent;
968 }
969 else if (lastChar == ';')
970 {
971 indent = anchorIndent + ((allowSemi && needContinue) ? continueIndent : 0);
972 }
973 else if (lastChar == ',' || lastChar == '(')
974 {
975 textLine = doc->plainKateTextLine(lastLine);
976 KateDocCursor start(lastLine, textLine->firstChar(), doc);
977 KateDocCursor finish(lastLine, textLine->lastChar() + 1, doc);
978 uint pos = 0;
979
980 if (isBalanced(start, finish, TQChar('('), TQChar(')'), pos) && false)
981 indent = anchorIndent;
982 else
983 {
984 // TODO: Config option. If we're below 48, go ahead and line them up
985 indent = ((pos < 48) ? pos : anchorIndent + (indentWidth * 2));
986 }
987 }
988 else if (!lastChar.isNull())
989 {
990 if (anchorIndent != 0)
991 indent = anchorIndent + continueIndent;
992 else
993 indent = continueIndent;
994 }
995
996 return indent;
997}
998
999uint KateCSmartIndent::calcContinue(KateDocCursor &start, KateDocCursor &end)
1000{
1001 KateDocCursor cur = start;
1002
1003 bool needsBalanced = true;
1004 bool isFor = false;
1005 allowSemi = false;
1006
1007 KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
1008
1009 // Handle cases such as } while (s ... by skipping the leading symbol
1010 if (textLine->attribute(cur.col()) == symbolAttrib)
1011 {
1012 cur.moveForward(1);
1013 skipBlanks(cur, end, false);
1014 }
1015
1016 if (textLine->getChar(cur.col()) == '}')
1017 {
1018 skipBlanks(cur, end, true);
1019 if (cur.line() != start.line())
1020 textLine = doc->plainKateTextLine(cur.line());
1021
1022 if (textLine->stringAtPos(cur.col(), "else"))
1023 cur.setCol(cur.col() + 4);
1024 else
1025 return indentWidth * 2;
1026
1027 needsBalanced = false;
1028 }
1029 else if (textLine->stringAtPos(cur.col(), "else"))
1030 {
1031 cur.setCol(cur.col() + 4);
1032 needsBalanced = false;
1033 int next = textLine->nextNonSpaceChar(cur.col());
1034 if (next >= 0 && textLine->stringAtPos(next, "if"))
1035 {
1036 cur.setCol(next + 2);
1037 needsBalanced = true;
1038 }
1039 }
1040 else if (textLine->stringAtPos(cur.col(), "if"))
1041 {
1042 cur.setCol(cur.col() + 2);
1043 }
1044 else if (textLine->stringAtPos(cur.col(), "do"))
1045 {
1046 cur.setCol(cur.col() + 2);
1047 needsBalanced = false;
1048 }
1049 else if (textLine->stringAtPos(cur.col(), "for"))
1050 {
1051 cur.setCol(cur.col() + 3);
1052 isFor = true;
1053 }
1054 else if (textLine->stringAtPos(cur.col(), "while"))
1055 {
1056 cur.setCol(cur.col() + 5);
1057 }
1058 else if (textLine->stringAtPos(cur.col(), "switch"))
1059 {
1060 cur.setCol(cur.col() + 6);
1061 }
1062 else if (textLine->stringAtPos(cur.col(), "using"))
1063 {
1064 cur.setCol(cur.col() + 5);
1065 }
1066 else
1067 {
1068 return indentWidth * 2;
1069 }
1070
1071 uint openPos = 0;
1072 if (needsBalanced && !isBalanced (cur, end, TQChar('('), TQChar(')'), openPos))
1073 {
1074 allowSemi = isFor;
1075 if (openPos > 0)
1076 return (openPos - textLine->firstChar());
1077 else
1078 return indentWidth * 2;
1079 }
1080
1081 // Check if this statement ends a line now
1082 skipBlanks(cur, end, false);
1083 if (cur == end)
1084 return indentWidth;
1085
1086 if (skipBlanks(cur, end, true))
1087 {
1088 if (cur == end)
1089 return indentWidth;
1090 else
1091 return indentWidth + calcContinue(cur, end);
1092 }
1093
1094 return 0;
1095}
1096
1097uint KateCSmartIndent::findOpeningBrace(KateDocCursor &start)
1098{
1099 KateDocCursor cur = start;
1100 int count = 1;
1101
1102 // Move backwards 1 by 1 and find the opening brace
1103 // Return the indent of that line
1104 while (cur.moveBackward(1))
1105 {
1106 if (cur.currentAttrib() == symbolAttrib)
1107 {
1108 TQChar ch = cur.currentChar();
1109 if (ch == '{')
1110 count--;
1111 else if (ch == '}')
1112 count++;
1113
1114 if (count == 0)
1115 {
1116 KateDocCursor temp(cur.line(), doc->plainKateTextLine(cur.line())->firstChar(), doc);
1117 return measureIndent(temp);
1118 }
1119 }
1120 }
1121
1122 return 0;
1123}
1124
1125bool KateCSmartIndent::firstOpeningBrace(KateDocCursor &start)
1126{
1127 KateDocCursor cur = start;
1128
1129 // Are we the first opening brace at this level?
1130 while(cur.moveBackward(1))
1131 {
1132 if (cur.currentAttrib() == symbolAttrib)
1133 {
1134 TQChar ch = cur.currentChar();
1135 if (ch == '{')
1136 return false;
1137 else if (ch == '}' && cur.col() == 0)
1138 break;
1139 }
1140 }
1141
1142 return true;
1143}
1144
1145uint KateCSmartIndent::findOpeningParen(KateDocCursor &start)
1146{
1147 KateDocCursor cur = start;
1148 int count = 1;
1149
1150 // Move backwards 1 by 1 and find the opening (
1151 // Return the indent of that line
1152 while (cur.moveBackward(1))
1153 {
1154 if (cur.currentAttrib() == symbolAttrib)
1155 {
1156 TQChar ch = cur.currentChar();
1157 if (ch == '(')
1158 count--;
1159 else if (ch == ')')
1160 count++;
1161
1162 if (count == 0)
1163 return measureIndent(cur);
1164 }
1165 }
1166
1167 return 0;
1168}
1169
1170uint KateCSmartIndent::findOpeningComment(KateDocCursor &start)
1171{
1172 KateDocCursor cur = start;
1173
1174 // Find the line with the opening /* and return the proper indent
1175 do
1176 {
1177 KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
1178
1179 int pos = textLine->string().find("/*", false);
1180 if (pos >= 0)
1181 {
1182 KateDocCursor temp(cur.line(), pos, doc);
1183 return measureIndent(temp);
1184 }
1185
1186 } while (cur.gotoPreviousLine());
1187
1188 return 0;
1189}
1190
1191//END
1192
1193//BEGIN KatePythonIndent
1194
1195TQRegExp KatePythonIndent::endWithColon = TQRegExp( "^[^#]*:\\s*(#.*)?$" );
1196TQRegExp KatePythonIndent::stopStmt = TQRegExp( "^\\s*(break|continue|raise|return|pass)\\b.*" );
1197TQRegExp KatePythonIndent::blockBegin = TQRegExp( "^\\s*(class|def|if|elif|else|for|while|try)\\b.*" );
1198
1199KatePythonIndent::KatePythonIndent (KateDocument *doc)
1200: KateNormalIndent (doc)
1201{
1202}
1203KatePythonIndent::~KatePythonIndent ()
1204{
1205}
1206
1207void KatePythonIndent::processNewline (KateDocCursor &begin, bool /*newline*/)
1208{
1209 int prevLine = begin.line() - 1;
1210 int prevPos = begin.col();
1211
1212 while ((prevLine > 0) && (prevPos < 0)) // search a not empty text line
1213 prevPos = doc->plainKateTextLine(--prevLine)->firstChar();
1214
1215 int prevBlock = prevLine;
1216 int prevBlockPos = prevPos;
1217 int extraIndent = calcExtra (prevBlock, prevBlockPos, begin);
1218
1219 int indent = doc->plainKateTextLine(prevBlock)->cursorX(prevBlockPos, tabWidth);
1220 if (extraIndent == 0)
1221 {
1222 if (!stopStmt.exactMatch(doc->plainKateTextLine(prevLine)->string()))
1223 {
1224 if (endWithColon.exactMatch(doc->plainKateTextLine(prevLine)->string()))
1225 indent += indentWidth;
1226 else
1227 indent = doc->plainKateTextLine(prevLine)->cursorX(prevPos, tabWidth);
1228 }
1229 }
1230 else
1231 indent += extraIndent;
1232
1233 if (indent > 0)
1234 {
1235 TQString filler = tabString (indent);
1236 doc->insertText (begin.line(), 0, filler);
1237 begin.setCol(filler.length());
1238 }
1239 else
1240 begin.setCol(0);
1241}
1242
1243int KatePythonIndent::calcExtra (int &prevBlock, int &pos, KateDocCursor &end)
1244{
1245 int nestLevel = 0;
1246 bool levelFound = false;
1247 while ((prevBlock > 0))
1248 {
1249 if (blockBegin.exactMatch(doc->plainKateTextLine(prevBlock)->string()))
1250 {
1251 if ((!levelFound && nestLevel == 0) || (levelFound && nestLevel - 1 <= 0))
1252 {
1253 pos = doc->plainKateTextLine(prevBlock)->firstChar();
1254 break;
1255 }
1256
1257 nestLevel --;
1258 }
1259 else if (stopStmt.exactMatch(doc->plainKateTextLine(prevBlock)->string()))
1260 {
1261 nestLevel ++;
1262 levelFound = true;
1263 }
1264
1265 --prevBlock;
1266 }
1267
1268 KateDocCursor cur (prevBlock, pos, doc);
1269 TQChar c;
1270 int extraIndent = 0;
1271 while (cur.line() < end.line())
1272 {
1273 c = cur.currentChar();
1274
1275 if (c == '(')
1276 extraIndent += indentWidth;
1277 else if (c == ')')
1278 extraIndent -= indentWidth;
1279 else if (c == ':')
1280 break;
1281 else if (c == '\'' || c == '"' )
1282 traverseString( c, cur, end );
1283
1284 if (c.isNull() || c == '#')
1285 cur.gotoNextLine();
1286 else
1287 cur.moveForward(1);
1288 }
1289
1290 return extraIndent;
1291}
1292
1293void KatePythonIndent::traverseString( const TQChar &stringChar, KateDocCursor &cur, KateDocCursor &end )
1294{
1295 TQChar c;
1296 bool escape = false;
1297
1298 cur.moveForward(1);
1299 c = cur.currentChar();
1300 while ( ( c != stringChar || escape ) && cur.line() < end.line() )
1301 {
1302 if ( escape )
1303 escape = false;
1304 else if ( c == '\\' )
1305 escape = !escape;
1306
1307 cur.moveForward(1);
1308 c = cur.currentChar();
1309 }
1310}
1311
1312//END
1313
1314//BEGIN KateXmlIndent
1315
1316/* Explanation
1317
1318The XML indenter simply inherits the indentation of the previous line,
1319with the first line starting at 0 (of course!). For each element that
1320is opened on the previous line, the indentation is increased by one
1321level; for each element that is closed, it is decreased by one.
1322
1323We also have a special case of opening an element on one line and then
1324entering attributes on the following lines, in which case we would like
1325to see the following layout:
1326<elem attr="..."
1327 blah="..." />
1328
1329<x><a href="..."
1330 title="..." />
1331</x>
1332
1333This is accomplished by checking for lines that contain an unclosed open
1334tag.
1335
1336*/
1337
1338const TQRegExp KateXmlIndent::startsWithCloseTag("^[ \t]*</");
1339const TQRegExp KateXmlIndent::unclosedDoctype("<!DOCTYPE[^>]*$");
1340
1341KateXmlIndent::KateXmlIndent (KateDocument *doc)
1342: KateNormalIndent (doc)
1343{
1344}
1345
1346KateXmlIndent::~KateXmlIndent ()
1347{
1348}
1349
1350void KateXmlIndent::processNewline (KateDocCursor &begin, bool /*newline*/)
1351{
1352 begin.setCol(processLine(begin.line()));
1353}
1354
1355void KateXmlIndent::processChar (TQChar c)
1356{
1357 if(c != '/') return;
1358
1359 // only alter lines that start with a close element
1360 KateView *view = doc->activeView();
1361 TQString text = doc->plainKateTextLine(view->cursorLine())->string();
1362 if(text.find(startsWithCloseTag) == -1) return;
1363
1364 // process it
1365 processLine(view->cursorLine());
1366}
1367
1368void KateXmlIndent::processLine (KateDocCursor &line)
1369{
1370 processLine (line.line());
1371}
1372
1373void KateXmlIndent::processSection (const KateDocCursor &start, const KateDocCursor &end)
1374{
1375 KateDocCursor cur (start);
1376 int endLine = end.line();
1377
1378 do {
1379 processLine(cur.line());
1380 if(!cur.gotoNextLine()) break;
1381 } while(cur.line() < endLine);
1382}
1383
1384void KateXmlIndent::getLineInfo (uint line, uint &prevIndent, int &numTags,
1385 uint &attrCol, bool &unclosedTag)
1386{
1387 prevIndent = 0;
1388 int firstChar;
1389 KateTextLine::Ptr prevLine = 0;
1390
1391 // get the indentation of the first non-empty line
1392 while(true) {
1393 prevLine = doc->plainKateTextLine(line);
1394 if( (firstChar = prevLine->firstChar()) < 0) {
1395 if(!line--) return;
1396 continue;
1397 }
1398 break;
1399 }
1400 prevIndent = prevLine->cursorX(prevLine->firstChar(), tabWidth);
1401 TQString text = prevLine->string();
1402
1403 // special case:
1404 // <a>
1405 // </a> <!-- indentation *already* decreased -->
1406 // requires that we discount the </a> from the number of closed tags
1407 if(text.find(startsWithCloseTag) != -1) ++numTags;
1408
1409 // count the number of open and close tags
1410 int lastCh = 0;
1411 uint pos, len = text.length();
1412 bool seenOpen = false;
1413 for(pos = 0; pos < len; ++pos) {
1414 int ch = text.at(pos).unicode();
1415 switch(ch) {
1416 case '<':
1417 seenOpen = true;
1418 unclosedTag = true;
1419 attrCol = pos;
1420 ++numTags;
1421 break;
1422
1423 // don't indent because of DOCTYPE, comment, CDATA, etc.
1424 case '!':
1425 if(lastCh == '<') --numTags;
1426 break;
1427
1428 // don't indent because of xml decl or PI
1429 case '?':
1430 if(lastCh == '<') --numTags;
1431 break;
1432
1433 case '>':
1434 if(!seenOpen) {
1435 // we are on a line like the second one here:
1436 // <element attr="val"
1437 // other="val">
1438 // so we need to set prevIndent to the indent of the first line
1439 //
1440 // however, we need to special case "<!DOCTYPE" because
1441 // it's not an open tag
1442
1443 prevIndent = 0;
1444
1445 for(uint backLine = line; backLine; ) {
1446 // find first line with an open tag
1447 KateTextLine::Ptr x = doc->plainKateTextLine(--backLine);
1448 if(x->string().find('<') == -1) continue;
1449
1450 // recalculate the indent
1451 if(x->string().find(unclosedDoctype) != -1) --numTags;
1452 getLineInfo(backLine, prevIndent, numTags, attrCol, unclosedTag);
1453 break;
1454 }
1455 }
1456 if(lastCh == '/') --numTags;
1457 unclosedTag = false;
1458 break;
1459
1460 case '/':
1461 if(lastCh == '<') numTags -= 2; // correct for '<', above
1462 break;
1463 }
1464 lastCh = ch;
1465 }
1466
1467 if(unclosedTag) {
1468 // find the start of the next attribute, so we can align with it
1469 do {
1470 lastCh = text.at(++attrCol).unicode();
1471 }while(lastCh && lastCh != ' ' && lastCh != '\t');
1472
1473 while(lastCh == ' ' || lastCh == '\t') {
1474 lastCh = text.at(++attrCol).unicode();
1475 }
1476
1477 attrCol = prevLine->cursorX(attrCol, tabWidth);
1478 }
1479}
1480
1481uint KateXmlIndent::processLine (uint line)
1482{
1483 KateTextLine::Ptr kateLine = doc->plainKateTextLine(line);
1484 if(!kateLine) return 0; // sanity check
1485
1486 // get details from previous line
1487 uint prevIndent = 0, attrCol = 0;
1488 int numTags = 0;
1489 bool unclosedTag = false; // for aligning attributes
1490
1491 if(line) {
1492 getLineInfo(line - 1, prevIndent, numTags, attrCol, unclosedTag);
1493 }
1494
1495 // compute new indent
1496 int indent = 0;
1497 if(unclosedTag) indent = attrCol;
1498 else indent = prevIndent + numTags * indentWidth;
1499 if(indent < 0) indent = 0;
1500
1501 // unindent lines that start with a close tag
1502 if(kateLine->string().find(startsWithCloseTag) != -1) {
1503 indent -= indentWidth;
1504 }
1505 if(indent < 0) indent = 0;
1506
1507 // apply new indent
1508 doc->removeText(line, 0, line, kateLine->firstChar());
1509 TQString filler = tabString(indent);
1510 doc->insertText(line, 0, filler);
1511
1512 return filler.length();
1513}
1514
1515//END
1516
1517//BEGIN KateCSAndSIndent
1518
1519KateCSAndSIndent::KateCSAndSIndent (KateDocument *doc)
1520: KateNormalIndent (doc)
1521{
1522}
1523
1524void KateCSAndSIndent::updateIndentString()
1525{
1526 if( useSpaces )
1527 indentString.fill( ' ', indentWidth );
1528 else
1529 indentString = '\t';
1530}
1531
1532KateCSAndSIndent::~KateCSAndSIndent ()
1533{
1534}
1535
1536void KateCSAndSIndent::processLine (KateDocCursor &line)
1537{
1538 KateTextLine::Ptr textLine = doc->plainKateTextLine(line.line());
1539
1540 if (!textLine)
1541 return;
1542
1543 updateIndentString();
1544
1545 const int oldCol = line.col();
1546 TQString whitespace = calcIndent(line);
1547 // strip off existing whitespace
1548 int oldIndent = textLine->firstChar();
1549 if ( oldIndent < 0 )
1550 oldIndent = doc->lineLength( line.line() );
1551 if( oldIndent > 0 )
1552 doc->removeText(line.line(), 0, line.line(), oldIndent);
1553 // add correct amount
1554 doc->insertText(line.line(), 0, whitespace);
1555
1556 // try to preserve the cursor position in the line
1557 if ( int(oldCol + whitespace.length()) >= oldIndent )
1558 line.setCol( oldCol + whitespace.length() - oldIndent );
1559 else
1560 line.setCol( 0 );
1561}
1562
1563void KateCSAndSIndent::processSection (const KateDocCursor &begin, const KateDocCursor &end)
1564{
1565 TQTime t; t.start();
1566 for( KateDocCursor cur = begin; cur.line() <= end.line(); )
1567 {
1568 processLine (cur);
1569 if (!cur.gotoNextLine())
1570 break;
1571 }
1572 kdDebug(13030) << "+++ total: " << t.elapsed() << endl;
1573}
1574
1580static TQString initialWhitespace(const KateTextLine::Ptr &line, int chars, bool convert = true)
1581{
1582 TQString text = line->string(0, chars);
1583 if( (int)text.length() < chars )
1584 {
1585 TQString filler; filler.fill(' ',chars - text.length());
1586 text += filler;
1587 }
1588 for( uint n = 0; n < text.length(); ++n )
1589 {
1590 if( text[n] != '\t' && text[n] != ' ' )
1591 {
1592 if( !convert )
1593 return text.left( n );
1594 text[n] = ' ';
1595 }
1596 }
1597 return text;
1598}
1599
1600TQString KateCSAndSIndent::findOpeningCommentIndentation(const KateDocCursor &start)
1601{
1602 KateDocCursor cur = start;
1603
1604 // Find the line with the opening /* and return the indentation of it
1605 do
1606 {
1607 KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
1608
1609 int pos = textLine->string().findRev("/*");
1610 // FIXME: /* inside /* is possible. This screws up in that case...
1611 if (pos >= 0)
1612 return initialWhitespace(textLine, pos);
1613 } while (cur.gotoPreviousLine());
1614
1615 // should never happen.
1616 kdWarning( 13030 ) << " in a comment, but can't find the start of it" << endl;
1617 return TQString::null;
1618}
1619
1620bool KateCSAndSIndent::handleDoxygen (KateDocCursor &begin)
1621{
1622 // Look backwards for a nonempty line
1623 int line = begin.line();
1624 int first = -1;
1625 while ((line > 0) && (first < 0))
1626 first = doc->plainKateTextLine(--line)->firstChar();
1627
1628 // no earlier nonempty line
1629 if (first < 0)
1630 return false;
1631
1632 KateTextLine::Ptr textLine = doc->plainKateTextLine(line);
1633
1634 // if the line doesn't end with a doxygen comment (that's not closed)
1635 // and doesn't start with a doxygen comment (that's not closed), we don't care.
1636 // note that we do need to check the start of the line, or lines ending with, say, @brief aren't
1637 // recognised.
1638 if ( !(textLine->attribute(textLine->lastChar()) == doxyCommentAttrib && !textLine->endingWith("*/")) &&
1639 !(textLine->attribute(textLine->firstChar()) == doxyCommentAttrib && !textLine->string().contains("*/")) )
1640 return false;
1641
1642 // our line is inside a doxygen comment. align the *'s and then maybe insert one too ...
1643 textLine = doc->plainKateTextLine(begin.line());
1644 first = textLine->firstChar();
1645 TQString indent = findOpeningCommentIndentation(begin);
1646
1647 bool doxygenAutoInsert = doc->config()->configFlags() & KateDocumentConfig::cfDoxygenAutoTyping;
1648
1649 // starts with *: indent one space more to line up *s
1650 if ( first >= 0 && textLine->stringAtPos(first, "*") )
1651 indent = indent + " ";
1652 // does not start with *: insert one if user wants that
1653 else if ( doxygenAutoInsert )
1654 indent = indent + " * ";
1655 // user doesn't want * inserted automatically: put in spaces?
1656 //else
1657 // indent = indent + " ";
1658
1659 doc->removeText (begin.line(), 0, begin.line(), first);
1660 doc->insertText (begin.line(), 0, indent);
1661 begin.setCol(indent.length());
1662
1663 return true;
1664}
1665
1672void KateCSAndSIndent::processNewline (KateDocCursor &begin, bool /*needContinue*/)
1673{
1674 // in a comment, add a * doxygen-style.
1675 if( handleDoxygen(begin) )
1676 return;
1677
1678 // TODO: if the user presses enter in the middle of a label, maybe the first half of the
1679 // label should be indented?
1680
1681 // where the cursor actually is...
1682 int cursorPos = doc->plainKateTextLine( begin.line() )->firstChar();
1683 if ( cursorPos < 0 )
1684 cursorPos = doc->lineLength( begin.line() );
1685 begin.setCol( cursorPos );
1686
1687 processLine( begin );
1688}
1689
1694bool KateCSAndSIndent::startsWithLabel( int line )
1695{
1696 // Get the current line.
1697 KateTextLine::Ptr indentLine = doc->plainKateTextLine(line);
1698 const int indentFirst = indentLine->firstChar();
1699
1700 // Not entirely sure what this check does.
1701 int attrib = indentLine->attribute(indentFirst);
1702 if (attrib != 0 && attrib != keywordAttrib && attrib != normalAttrib && attrib != extensionAttrib)
1703 return false;
1704
1705 // Get the line text.
1706 const TQString lineContents = indentLine->string();
1707 const int indentLast = indentLine->lastChar();
1708 bool whitespaceFound = false;
1709 for ( int n = indentFirst; n <= indentLast; ++n )
1710 {
1711 // Get the character as latin1. Can't use TQChar::isLetterOrNumber()
1712 // as that includes non 0-9 numbers.
1713 char c = lineContents[n].latin1();
1714 if ( c == ':' )
1715 {
1716 // See if the next character is ':' - if so, skip to the character after it.
1717 if ( n < lineContents.length() - 1 )
1718 {
1719 if ( lineContents[n+1].latin1() == ':' )
1720 {
1721 n += 2;
1722 continue;
1723 }
1724 }
1725 // Right this is the relevent ':'.
1726 if ( n == indentFirst)
1727 {
1728 // Just a line with a : on it.
1729 return false;
1730 }
1731 // It is a label of some kind!
1732 return true;
1733 }
1734 if (isspace(c))
1735 {
1736 if (!whitespaceFound)
1737 {
1738 if (lineContents.mid(indentFirst, n - indentFirst) == "case")
1739 return true;
1740 else if (lineContents.mid(indentFirst, n - indentFirst) == "class")
1741 return false;
1742 whitespaceFound = true;
1743 }
1744 }
1745 // All other characters don't indent.
1746 else if ( !isalnum(c) && c != '_' )
1747 {
1748 return false;
1749 }
1750 }
1751 return false;
1752}
1753
1754template<class T> T min(T a, T b) { return (a < b) ? a : b; }
1755
1756int KateCSAndSIndent::lastNonCommentChar( const KateDocCursor &line )
1757{
1758 KateTextLine::Ptr textLine = doc->plainKateTextLine( line.line() );
1759 TQString str = textLine->string();
1760
1761 // find a possible start-of-comment
1762 int p = -2; // so the first find starts at position 0
1763 do p = str.find( "//", p + 2 );
1764 while ( p >= 0 && textLine->attribute(p) != commentAttrib && textLine->attribute(p) != doxyCommentAttrib );
1765
1766 // no // found? use whole string
1767 if ( p < 0 )
1768 p = str.length();
1769
1770 // ignore trailing blanks. p starts one-past-the-end.
1771 while( p > 0 && str[p-1].isSpace() ) --p;
1772 return p - 1;
1773}
1774
1775bool KateCSAndSIndent::inForStatement( int line )
1776{
1777 // does this line end in a for ( ...
1778 // with no closing ) ?
1779 int parens = 0, semicolons = 0;
1780 for ( ; line >= 0; --line )
1781 {
1782 KateTextLine::Ptr textLine = doc->plainKateTextLine(line);
1783 const int first = textLine->firstChar();
1784 const int last = textLine->lastChar();
1785
1786 // look backwards for a symbol: (){};
1787 // match ()s, {...; and }...; => not in a for
1788 // ; ; ; => not in a for
1789 // ( ; and ( ; ; => a for
1790 for ( int curr = last; curr >= first; --curr )
1791 {
1792 if ( textLine->attribute(curr) != symbolAttrib )
1793 continue;
1794
1795 switch( textLine->getChar(curr) )
1796 {
1797 case ';':
1798 if( ++semicolons > 2 )
1799 return false;
1800 break;
1801 case '{': case '}':
1802 return false;
1803 case ')':
1804 ++parens;
1805 break;
1806 case '(':
1807 if( --parens < 0 )
1808 return true;
1809 break;
1810 }
1811 }
1812 }
1813 // no useful symbols before the ;?
1814 // not in a for then
1815 return false;
1816}
1817
1818
1819// is the start of the line containing 'begin' in a statement?
1820bool KateCSAndSIndent::inStatement( const KateDocCursor &begin )
1821{
1822 // if the current line starts with an open brace, it's not a continuation.
1823 // this happens after a function definition (which is treated as a continuation).
1824 KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line());
1825 const int first = textLine->firstChar();
1826 // note that if we're being called from processChar the attribute has not yet been calculated
1827 // should be reasonably safe to assume that unattributed {s are symbols; if the { is in a comment
1828 // we don't want to touch it anyway.
1829 const int attrib = textLine->attribute(first);
1830 if( first >= 0 && (attrib == 0 || attrib == symbolAttrib) && textLine->getChar(first) == '{' )
1831 return false;
1832
1833 int line;
1834 for ( line = begin.line() - 1; line >= 0; --line )
1835 {
1836 textLine = doc->plainKateTextLine(line);
1837 const int first = textLine->firstChar();
1838 if ( first == -1 )
1839 continue;
1840
1841 // starts with #: in a comment, don't care
1842 // outside a comment: preprocessor, don't care
1843 if ( textLine->getChar( first ) == '#' )
1844 continue;
1845 KateDocCursor currLine = begin;
1846 currLine.setLine( line );
1847 const int last = lastNonCommentChar( currLine );
1848 if ( last < first )
1849 continue;
1850
1851 // HACK: if we see a comment, assume boldly that this isn't a continuation.
1852 // detecting comments (using attributes) is HARD, since they may have
1853 // embedded alerts, or doxygen stuff, or just about anything. this is
1854 // wrong, and needs fixing. note that only multi-line comments and
1855 // single-line comments continued with \ are affected.
1856 const int attrib = textLine->attribute(last);
1857 if ( attrib == commentAttrib || attrib == doxyCommentAttrib )
1858 return false;
1859
1860 char c = textLine->getChar(last);
1861
1862 // brace => not a continuation.
1863 if ( attrib == symbolAttrib && c == '{' || c == '}' )
1864 return false;
1865
1866 // ; => not a continuation, unless in a for (;;)
1867 if ( attrib == symbolAttrib && c == ';' )
1868 return inForStatement( line );
1869
1870 // found something interesting. maybe it's a label?
1871 if ( attrib == symbolAttrib && c == ':' )
1872 {
1873 // the : above isn't necessarily the : in the label, eg in
1874 // case 'x': a = b ? c :
1875 // this will say no continuation incorrectly. but continued statements
1876 // starting on a line with a label at the start is Bad Style (tm).
1877 if( startsWithLabel( line ) )
1878 {
1879 // either starts with a label or a continuation. if the current line
1880 // starts in a continuation, we're still in one. if not, this was
1881 // a label, so we're not in one now. so continue to the next line
1882 // upwards.
1883 continue;
1884 }
1885 }
1886
1887 // any other character => in a continuation
1888 return true;
1889 }
1890 // no non-comment text found before here - not a continuation.
1891 return false;
1892}
1893
1894TQString KateCSAndSIndent::continuationIndent( const KateDocCursor &begin )
1895{
1896 if( !inStatement( begin ) )
1897 return TQString::null;
1898 return indentString;
1899}
1900
1904TQString KateCSAndSIndent::calcIndent (const KateDocCursor &begin)
1905{
1906 KateTextLine::Ptr currLine = doc->plainKateTextLine(begin.line());
1907 int currLineFirst = currLine->firstChar();
1908
1909 // if the line starts inside a comment, no change of indentation.
1910 // FIXME: this unnecessarily copies the current indentation over itself.
1911 // FIXME: on newline, this should copy from the previous line.
1912 if ( currLineFirst >= 0 &&
1913 (currLine->attribute(currLineFirst) == commentAttrib ||
1914 currLine->attribute(currLineFirst) == doxyCommentAttrib) )
1915 return currLine->string( 0, currLineFirst );
1916
1917 // if the line starts with # (but isn't a c# region thingy), no indentation at all.
1918 if( currLineFirst >= 0 && currLine->getChar(currLineFirst) == '#' )
1919 {
1920 if( !currLine->stringAtPos( currLineFirst+1, TQString::fromLatin1("region") ) &&
1921 !currLine->stringAtPos( currLineFirst+1, TQString::fromLatin1("endregion") ) )
1922 return TQString::null;
1923 }
1924
1925 /* Strategy:
1926 * Look for an open bracket or brace, or a keyword opening a new scope, whichever comes latest.
1927 * Found a brace: indent one tab in.
1928 * Found a bracket: indent to the first non-white after it.
1929 * Found a keyword: indent one tab in. for try, catch and switch, if newline is set, also add
1930 * an open brace, a newline, and indent two tabs in.
1931 */
1932 KateDocCursor cur = begin;
1933 int pos, openBraceCount = 0, openParenCount = 0;
1934 bool lookingForScopeKeywords = true;
1935 const char * const scopeKeywords[] = { "for", "do", "while", "if", "else" };
1936 const char * const blockScopeKeywords[] = { "try", "catch", "switch" };
1937
1938 while (cur.gotoPreviousLine())
1939 {
1940 KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
1941 const int lastChar = textLine->lastChar();
1942 const int firstChar = textLine->firstChar();
1943
1944 // look through line backwards for interesting characters
1945 for( pos = lastChar; pos >= firstChar; --pos )
1946 {
1947 if (textLine->attribute(pos) == symbolAttrib)
1948 {
1949 char tc = textLine->getChar (pos);
1950 switch( tc )
1951 {
1952 case '(': case '[':
1953 if( ++openParenCount > 0 )
1954 return calcIndentInBracket( begin, cur, pos );
1955 break;
1956 case ')': case ']': openParenCount--; break;
1957 case '{':
1958 if( ++openBraceCount > 0 )
1959 return calcIndentInBrace( begin, cur, pos );
1960 break;
1961 case '}': openBraceCount--; lookingForScopeKeywords = false; break;
1962 case ';':
1963 if( openParenCount == 0 )
1964 lookingForScopeKeywords = false;
1965 break;
1966 }
1967 }
1968
1969 // if we've not had a close brace or a semicolon yet, and we're at the same parenthesis level
1970 // as the cursor, and we're at the start of a scope keyword, indent from it.
1971 if ( lookingForScopeKeywords && openParenCount == 0 &&
1972 textLine->attribute(pos) == keywordAttrib &&
1973 (pos == 0 || textLine->attribute(pos-1) != keywordAttrib ) )
1974 {
1975 #define ARRLEN( array ) ( sizeof(array)/sizeof(array[0]) )
1976 for( uint n = 0; n < ARRLEN(scopeKeywords); ++n )
1977 if( textLine->stringAtPos(pos, TQString::fromLatin1(scopeKeywords[n]) ) )
1978 return calcIndentAfterKeyword( begin, cur, pos, false );
1979 for( uint n = 0; n < ARRLEN(blockScopeKeywords); ++n )
1980 if( textLine->stringAtPos(pos, TQString::fromLatin1(blockScopeKeywords[n]) ) )
1981 return calcIndentAfterKeyword( begin, cur, pos, true );
1982 #undef ARRLEN
1983 }
1984 }
1985 }
1986
1987 // no active { in file.
1988 return TQString::null;
1989}
1990
1991TQString KateCSAndSIndent::calcIndentInBracket(const KateDocCursor &indentCursor, const KateDocCursor &bracketCursor, int bracketPos)
1992{
1993 KateTextLine::Ptr indentLine = doc->plainKateTextLine(indentCursor.line());
1994 KateTextLine::Ptr bracketLine = doc->plainKateTextLine(bracketCursor.line());
1995
1996 // FIXME: hard-coded max indent to bracket width - use a kate variable
1997 // FIXME: expand tabs first...
1998 if ( bracketPos > 48 )
1999 {
2000 // how far to indent? we could look back for a brace or keyword, 2 from that.
2001 // as it is, we just indent one more than the line with the ( on it.
2002 // the potential problem with this is when
2003 // you have code ( which does <-- continuation + start of func call
2004 // something like this ); <-- extra indentation for func call
2005 // then again (
2006 // it works better than (
2007 // the other method for (
2008 // cases like this )));
2009 // consequently, i think this method wins.
2010 return indentString + initialWhitespace( bracketLine, bracketLine->firstChar() );
2011 }
2012
2013 const int indentLineFirst = indentLine->firstChar();
2014
2015 int indentTo;
2016 const int attrib = indentLine->attribute(indentLineFirst);
2017 if( indentLineFirst >= 0 && (attrib == 0 || attrib == symbolAttrib) &&
2018 ( indentLine->getChar(indentLineFirst) == ')' || indentLine->getChar(indentLineFirst) == ']' ) )
2019 {
2020 // If the line starts with a close bracket, line it up
2021 indentTo = bracketPos;
2022 }
2023 else
2024 {
2025 // Otherwise, line up with the text after the open bracket
2026 indentTo = bracketLine->nextNonSpaceChar( bracketPos + 1 );
2027 if( indentTo == -1 )
2028 indentTo = bracketPos + 2;
2029 }
2030 return initialWhitespace( bracketLine, indentTo );
2031}
2032
2033TQString KateCSAndSIndent::calcIndentAfterKeyword(const KateDocCursor &indentCursor, const KateDocCursor &keywordCursor, int keywordPos, bool blockKeyword)
2034{
2035 KateTextLine::Ptr keywordLine = doc->plainKateTextLine(keywordCursor.line());
2036 KateTextLine::Ptr indentLine = doc->plainKateTextLine(indentCursor.line());
2037
2038 TQString whitespaceToKeyword = initialWhitespace( keywordLine, keywordPos, false );
2039 if( blockKeyword ) {
2040 // FIXME: we could add the open brace and subsequent newline here since they're definitely needed.
2041 }
2042
2043 // If the line starts with an open brace, don't indent...
2044 int first = indentLine->firstChar();
2045 // if we're being called from processChar attribute won't be set
2046 const int attrib = indentLine->attribute(first);
2047 if( first >= 0 && (attrib == 0 || attrib == symbolAttrib) && indentLine->getChar(first) == '{' )
2048 return whitespaceToKeyword;
2049
2050 // don't check for a continuation. rules are simple here:
2051 // if we're in a non-compound statement after a scope keyword, we indent all lines
2052 // once. so:
2053 // if ( some stuff
2054 // goes here )
2055 // apples, and <-- continuation here is ignored. but this is Bad Style (tm) anyway.
2056 // oranges too;
2057 return indentString + whitespaceToKeyword;
2058}
2059
2060TQString KateCSAndSIndent::calcIndentInBrace(const KateDocCursor &indentCursor, const KateDocCursor &braceCursor, int bracePos)
2061{
2062 KateTextLine::Ptr braceLine = doc->plainKateTextLine(braceCursor.line());
2063 const int braceFirst = braceLine->firstChar();
2064
2065 TQString whitespaceToOpenBrace = initialWhitespace( braceLine, bracePos, false );
2066
2067 // if the open brace is the start of a namespace, don't indent...
2068 // FIXME: this is an extremely poor heuristic. it looks on the line with
2069 // the { and the line before to see if they start with a keyword
2070 // beginning 'namespace'. that's 99% of usage, I'd guess.
2071 {
2072 if( braceFirst >= 0 && braceLine->attribute(braceFirst) == keywordAttrib &&
2073 braceLine->stringAtPos( braceFirst, TQString::fromLatin1( "namespace" ) ) )
2074 return continuationIndent(indentCursor) + whitespaceToOpenBrace;
2075
2076 if( braceCursor.line() > 0 )
2077 {
2078 KateTextLine::Ptr prevLine = doc->plainKateTextLine(braceCursor.line() - 1);
2079 int firstPrev = prevLine->firstChar();
2080 if( firstPrev >= 0 && prevLine->attribute(firstPrev) == keywordAttrib &&
2081 prevLine->stringAtPos( firstPrev, TQString::fromLatin1( "namespace" ) ) )
2082 return continuationIndent(indentCursor) + whitespaceToOpenBrace;
2083 }
2084 }
2085
2086 KateTextLine::Ptr indentLine = doc->plainKateTextLine(indentCursor.line());
2087 const int indentFirst = indentLine->firstChar();
2088
2089 // if the line starts with a close brace, don't indent...
2090 if( indentFirst >= 0 && indentLine->getChar(indentFirst) == '}' )
2091 return whitespaceToOpenBrace;
2092
2093 // if : is the first character (and not followed by another :), this is the start
2094 // of an initialization list, or a continuation of a ?:. either way, indent twice.
2095 if ( indentFirst >= 0 && indentLine->attribute(indentFirst) == symbolAttrib &&
2096 indentLine->getChar(indentFirst) == ':' && indentLine->getChar(indentFirst+1) != ':' )
2097 {
2098 return indentString + indentString + whitespaceToOpenBrace;
2099 }
2100
2101 const bool continuation = inStatement(indentCursor);
2102 // if the current line starts with a label, don't indent...
2103 if( !continuation && startsWithLabel( indentCursor.line() ) )
2104 return whitespaceToOpenBrace;
2105
2106 // the normal case: indent once for the brace, again if it's a continuation
2107 TQString continuationIndent = continuation ? indentString : TQString::null;
2108 return indentString + continuationIndent + whitespaceToOpenBrace;
2109}
2110
2111void KateCSAndSIndent::processChar(TQChar c)
2112{
2113 // 'n' trigger is for c# regions.
2114 static const TQString triggers("}{)]/:;#n");
2115 if (triggers.find(c) == -1)
2116 return;
2117
2118 // for historic reasons, processChar doesn't get a cursor
2119 // to work on. so fabricate one.
2120 KateView *view = doc->activeView();
2121 KateDocCursor begin(view->cursorLine(), 0, doc);
2122
2123 KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line());
2124 if ( c == 'n' )
2125 {
2126 int first = textLine->firstChar();
2127 if( first < 0 || textLine->getChar(first) != '#' )
2128 return;
2129 }
2130
2131 if ( textLine->attribute( begin.col() ) == doxyCommentAttrib )
2132 {
2133 // dominik: if line is "* /", change it to "*/"
2134 if ( c == '/' )
2135 {
2136 int first = textLine->firstChar();
2137 // if the first char exists and is a '*', and the next non-space-char
2138 // is already the just typed '/', concatenate it to "*/".
2139 if ( first != -1
2140 && textLine->getChar( first ) == '*'
2141 && textLine->nextNonSpaceChar( first+1 ) == view->cursorColumnReal()-1 )
2142 doc->removeText( view->cursorLine(), first+1, view->cursorLine(), view->cursorColumnReal()-1);
2143 }
2144
2145 // anders: don't change the indent of doxygen lines here.
2146 return;
2147 }
2148
2149 processLine(begin);
2150}
2151
2152//END
2153
2154//BEGIN KateVarIndent
2155class KateVarIndentPrivate {
2156 public:
2157 TQRegExp reIndentAfter, reIndent, reUnindent;
2158 TQString triggers;
2159 uint couples;
2160 uchar coupleAttrib;
2161};
2162
2163KateVarIndent::KateVarIndent( KateDocument *doc )
2164: KateNormalIndent( doc )
2165{
2166 d = new KateVarIndentPrivate;
2167 d->reIndentAfter = TQRegExp( doc->variable( "var-indent-indent-after" ) );
2168 d->reIndent = TQRegExp( doc->variable( "var-indent-indent" ) );
2169 d->reUnindent = TQRegExp( doc->variable( "var-indent-unindent" ) );
2170 d->triggers = doc->variable( "var-indent-triggerchars" );
2171 d->coupleAttrib = 0;
2172
2173 slotVariableChanged( "var-indent-couple-attribute", doc->variable( "var-indent-couple-attribute" ) );
2174 slotVariableChanged( "var-indent-handle-couples", doc->variable( "var-indent-handle-couples" ) );
2175
2176 // update if a setting is changed
2177 connect( doc, TQT_SIGNAL(variableChanged( const TQString&, const TQString&) ),
2178 this, TQT_SLOT(slotVariableChanged( const TQString&, const TQString& )) );
2179}
2180
2181KateVarIndent::~KateVarIndent()
2182{
2183 delete d;
2184}
2185
2186void KateVarIndent::processNewline ( KateDocCursor &begin, bool /*needContinue*/ )
2187{
2188 // process the line left, as well as the one entered
2189 KateDocCursor left( begin.line()-1, 0, doc );
2190 processLine( left );
2191 processLine( begin );
2192}
2193
2194void KateVarIndent::processChar ( TQChar c )
2195{
2196 // process line if the c is in our list, and we are not in comment text
2197 if ( d->triggers.contains( c ) )
2198 {
2199 KateTextLine::Ptr ln = doc->plainKateTextLine( doc->activeView()->cursorLine() );
2200 if ( ln->attribute( doc->activeView()->cursorColumn()-1 ) == commentAttrib )
2201 return;
2202
2203 KateView *view = doc->activeView();
2204 KateDocCursor begin( view->cursorLine(), 0, doc );
2205 kdDebug(13030)<<"variable indenter: process char '"<<c<<", line "<<begin.line()<<endl;
2206 processLine( begin );
2207 }
2208}
2209
2210void KateVarIndent::processLine ( KateDocCursor &line )
2211{
2212 TQString indent; // store the indent string here
2213
2214 // find the first line with content that is not starting with comment text,
2215 // and take the position from that
2216 int ln = line.line();
2217 int pos = -1;
2218 KateTextLine::Ptr ktl = doc->plainKateTextLine( ln );
2219 if ( ! ktl ) return; // no line!?
2220
2221 // skip blank lines, except for the cursor line
2222 KateView *v = doc->activeView();
2223 if ( (ktl->firstChar() < 0) && (!v || (int)v->cursorLine() != ln ) )
2224 return;
2225
2226 int fc;
2227 if ( ln > 0 )
2228 do
2229 {
2230
2231 ktl = doc->plainKateTextLine( --ln );
2232 fc = ktl->firstChar();
2233 if ( ktl->attribute( fc ) != commentAttrib )
2234 pos = fc;
2235 }
2236 while ( (ln > 0) && (pos < 0) ); // search a not empty text line
2237
2238 if ( pos < 0 )
2239 pos = 0;
2240 else
2241 pos = ktl->cursorX( pos, tabWidth );
2242
2243 int adjustment = 0;
2244
2245 // try 'couples' for an opening on the above line first. since we only adjust by 1 unit,
2246 // we only need 1 match.
2247 if ( d->couples & Parens && coupleBalance( ln, '(', ')' ) > 0 )
2248 adjustment++;
2249 else if ( d->couples & Braces && coupleBalance( ln, '{', '}' ) > 0 )
2250 adjustment++;
2251 else if ( d->couples & Brackets && coupleBalance( ln, '[', ']' ) > 0 )
2252 adjustment++;
2253
2254 // Try 'couples' for a closing on this line first. since we only adjust by 1 unit,
2255 // we only need 1 match. For unindenting, we look for a closing character
2256 // *at the beginning of the line*
2257 // NOTE Assume that a closing brace with the configured attribute on the start
2258 // of the line is closing.
2259 // When acting on processChar, the character isn't highlighted. So I could
2260 // either not check, assuming that the first char *is* meant to close, or do a
2261 // match test if the attrib is 0. How ever, doing that is
2262 // a potentially huge job, if the match is several hundred lines away.
2263 // Currently, the check is done.
2264 {
2265 KateTextLine::Ptr tl = doc->plainKateTextLine( line.line() );
2266 int i = tl->firstChar();
2267 if ( i > -1 )
2268 {
2269 TQChar ch = tl->getChar( i );
2270 uchar at = tl->attribute( i );
2271 kdDebug(13030)<<"attrib is "<<at<<endl;
2272 if ( d->couples & Parens && ch == ')'
2273 && ( at == d->coupleAttrib
2274 || (! at && hasRelevantOpening( KateDocCursor( line.line(), i, doc ) ))
2275 )
2276 )
2277 adjustment--;
2278 else if ( d->couples & Braces && ch == '}'
2279 && ( at == d->coupleAttrib
2280 || (! at && hasRelevantOpening( KateDocCursor( line.line(), i, doc ) ))
2281 )
2282 )
2283 adjustment--;
2284 else if ( d->couples & Brackets && ch == ']'
2285 && ( at == d->coupleAttrib
2286 || (! at && hasRelevantOpening( KateDocCursor( line.line(), i, doc ) ))
2287 )
2288 )
2289 adjustment--;
2290 }
2291 }
2292#define ISCOMMENTATTR(attr) (attr==commentAttrib||attr==doxyCommentAttrib)
2293#define ISCOMMENT (ISCOMMENTATTR(ktl->attribute(ktl->firstChar()))||ISCOMMENTATTR(ktl->attribute(matchpos)))
2294 // check if we should indent, unless the line starts with comment text,
2295 // or the match is in comment text
2296 kdDebug(13030)<<"variable indenter: starting indent: "<<pos<<endl;
2297 // check if the above line indicates that we shuld add indentation
2298 int matchpos = 0;
2299 if ( ktl && ! d->reIndentAfter.isEmpty()
2300 && (matchpos = d->reIndentAfter.search( doc->textLine( ln ) )) > -1
2301 && ! ISCOMMENT )
2302 adjustment++;
2303
2304 // else, check if this line should indent unless ...
2305 ktl = doc->plainKateTextLine( line.line() );
2306 if ( ! d->reIndent.isEmpty()
2307 && (matchpos = d->reIndent.search( doc->textLine( line.line() ) )) > -1
2308 && ! ISCOMMENT )
2309 adjustment++;
2310
2311 // else, check if the current line indicates if we should remove indentation unless ...
2312 if ( ! d->reUnindent.isEmpty()
2313 && (matchpos = d->reUnindent.search( doc->textLine( line.line() ) )) > -1
2314 && ! ISCOMMENT )
2315 adjustment--;
2316
2317 kdDebug(13030)<<"variable indenter: adjusting by "<<adjustment<<" units"<<endl;
2318
2319 if ( adjustment > 0 )
2320 pos += indentWidth;
2321 else if ( adjustment < 0 )
2322 pos -= indentWidth;
2323
2324 ln = line.line();
2325 fc = doc->plainKateTextLine( ln )->firstChar();
2326
2327 // dont change if there is no change.
2328 // ### should I actually compare the strings?
2329 // FIXME for some odd reason, the document gets marked as changed
2330 // even if we don't change it !?
2331 if ( fc == pos )
2332 return;
2333
2334 if ( fc > 0 )
2335 doc->removeText (ln, 0, ln, fc );
2336
2337 if ( pos > 0 )
2338 indent = tabString( pos );
2339
2340 if ( pos > 0 )
2341 doc->insertText (ln, 0, indent);
2342
2343 // try to restore cursor ?
2344 line.setCol( pos );
2345}
2346
2347void KateVarIndent::processSection (const KateDocCursor &begin, const KateDocCursor &end)
2348{
2349 KateDocCursor cur = begin;
2350 while (cur.line() <= end.line())
2351 {
2352 processLine (cur);
2353 if (!cur.gotoNextLine())
2354 break;
2355 }
2356}
2357
2358void KateVarIndent::slotVariableChanged( const TQString &var, const TQString &val )
2359{
2360 if ( ! var.startsWith("var-indent") )
2361 return;
2362
2363 if ( var == "var-indent-indent-after" )
2364 d->reIndentAfter.setPattern( val );
2365 else if ( var == "var-indent-indent" )
2366 d->reIndent.setPattern( val );
2367 else if ( var == "var-indent-unindent" )
2368 d->reUnindent.setPattern( val );
2369 else if ( var == "var-indent-triggerchars" )
2370 d->triggers = val;
2371 else if ( var == "var-indent-handle-couples" )
2372 {
2373 d->couples = 0;
2374 TQStringList l = TQStringList::split( " ", val );
2375 if ( l.contains("parens") ) d->couples |= Parens;
2376 if ( l.contains("braces") ) d->couples |= Braces;
2377 if ( l.contains("brackets") ) d->couples |= Brackets;
2378 }
2379 else if ( var == "var-indent-couple-attribute" )
2380 {
2381 //read a named attribute of the config.
2382 KateHlItemDataList items;
2383 doc->highlight()->getKateHlItemDataListCopy (0, items);
2384
2385 for (uint i=0; i<items.count(); i++)
2386 {
2387 if ( items.at(i)->name.section( ':', 1 ) == val )
2388 {
2389 d->coupleAttrib = i;
2390 break;
2391 }
2392 }
2393 }
2394}
2395
2396int KateVarIndent::coupleBalance ( int line, const TQChar &open, const TQChar &close ) const
2397{
2398 int r = 0;
2399
2400 KateTextLine::Ptr ln = doc->plainKateTextLine( line );
2401 if ( ! ln || ! ln->length() ) return 0;
2402
2403 for ( uint z=0; z < ln->length(); z++ )
2404 {
2405 TQChar c = ln->getChar( z );
2406 if ( ln->attribute(z) == d->coupleAttrib )
2407 {
2408 kdDebug(13030)<<z<<", "<<c<<endl;
2409 if (c == open)
2410 r++;
2411 else if (c == close)
2412 r--;
2413 }
2414 }
2415 return r;
2416}
2417
2418bool KateVarIndent::hasRelevantOpening( const KateDocCursor &end ) const
2419{
2420 KateDocCursor cur = end;
2421 int count = 1;
2422
2423 TQChar close = cur.currentChar();
2424 TQChar opener;
2425 if ( close == '}' ) opener = '{';
2426 else if ( close = ')' ) opener = '(';
2427 else if (close = ']' ) opener = '[';
2428 else return false;
2429
2430 //Move backwards 1 by 1 and find the opening partner
2431 while (cur.moveBackward(1))
2432 {
2433 if (cur.currentAttrib() == d->coupleAttrib)
2434 {
2435 TQChar ch = cur.currentChar();
2436 if (ch == opener)
2437 count--;
2438 else if (ch == close)
2439 count++;
2440
2441 if (count == 0)
2442 return true;
2443 }
2444 }
2445
2446 return false;
2447}
2448
2449
2450//END KateVarIndent
2451
2452//BEGIN KateScriptIndent
2453KateScriptIndent::KateScriptIndent( KateDocument *doc )
2454 : KateNormalIndent( doc )
2455{
2456 m_script=KateFactory::self()->indentScript ("script-indent-c1-test");
2457}
2458
2459KateScriptIndent::~KateScriptIndent()
2460{
2461}
2462
2463void KateScriptIndent::processNewline( KateDocCursor &begin, bool needContinue )
2464{
2465 kdDebug(13030) << "processNewline" << endl;
2466 KateView *view = doc->activeView();
2467
2468 if (view)
2469 {
2470 TQString errorMsg;
2471
2472 TQTime t;
2473 t.start();
2474 kdDebug(13030)<<"calling m_script.processChar"<<endl;
2475 if( !m_script.processNewline( view, begin, needContinue , errorMsg ) )
2476 {
2477 kdDebug(13030) << "Error in script-indent: " << errorMsg << endl;
2478 }
2479 kdDebug(13030) << "ScriptIndent::TIME in ms: " << t.elapsed() << endl;
2480 }
2481}
2482
2483void KateScriptIndent::processChar( TQChar c )
2484{
2485 kdDebug(13030) << "processChar" << endl;
2486 KateView *view = doc->activeView();
2487
2488 if (view)
2489 {
2490 TQString errorMsg;
2491
2492 TQTime t;
2493 t.start();
2494 kdDebug(13030)<<"calling m_script.processChar"<<endl;
2495 if( !m_script.processChar( view, c , errorMsg ) )
2496 {
2497 kdDebug(13030) << "Error in script-indent: " << errorMsg << endl;
2498 }
2499 kdDebug(13030) << "ScriptIndent::TIME in ms: " << t.elapsed() << endl;
2500 }
2501}
2502
2503void KateScriptIndent::processLine (KateDocCursor &line)
2504{
2505 kdDebug(13030) << "processLine" << endl;
2506 KateView *view = doc->activeView();
2507
2508 if (view)
2509 {
2510 TQString errorMsg;
2511
2512 TQTime t;
2513 t.start();
2514 kdDebug(13030)<<"calling m_script.processLine"<<endl;
2515 if( !m_script.processLine( view, line , errorMsg ) )
2516 {
2517 kdDebug(13030) << "Error in script-indent: " << errorMsg << endl;
2518 }
2519 kdDebug(13030) << "ScriptIndent::TIME in ms: " << t.elapsed() << endl;
2520 }
2521}
2522//END KateScriptIndent
2523
2524//BEGIN ScriptIndentConfigPage, THIS IS ONLY A TEST! :)
2525#include <tqlabel.h>
2526ScriptIndentConfigPage::ScriptIndentConfigPage ( TQWidget *parent, const char *name )
2527 : IndenterConfigPage(parent, name)
2528{
2529 TQLabel* hello = new TQLabel("Hello world! Dummy for testing purpose.", this);
2530 hello->show();
2531}
2532
2533ScriptIndentConfigPage::~ScriptIndentConfigPage ()
2534{
2535}
2536
2537void ScriptIndentConfigPage::apply ()
2538{
2539 kdDebug(13030) << "ScriptIndentConfigPagE::apply() was called, save config options now!" << endl;
2540}
2541//END ScriptIndentConfigPage
IndenterConfigPage
This widget will be embedded into a modal dialog when clicking the "Configure..." button in the inden...
Definition: kateautoindent.h:45
KateAutoIndent
Provides Auto-Indent functionality for katepart.
Definition: kateautoindent.h:71
KateAutoIndent::hasConfigPage
static bool hasConfigPage(uint mode)
Config page support.
Definition: kateautoindent.cpp:134
KateAutoIndent::modeName
static TQString modeName(uint mode)
Return the mode name given the mode.
Definition: kateautoindent.cpp:74
KateAutoIndent::listModes
static TQStringList listModes()
List all possible modes by name.
Definition: kateautoindent.cpp:58
KateAutoIndent::~KateAutoIndent
virtual ~KateAutoIndent()
Virtual Destructor for the baseclass.
Definition: kateautoindent.cpp:154
KateAutoIndent::configPage
static IndenterConfigPage * configPage(TQWidget *parent, uint mode)
Support for a config page.
Definition: kateautoindent.cpp:142
KateAutoIndent::modeDescription
static TQString modeDescription(uint mode)
Return the mode description.
Definition: kateautoindent.cpp:94
KateAutoIndent::KateAutoIndent
KateAutoIndent(KateDocument *doc)
Constructor.
Definition: kateautoindent.cpp:150
KateAutoIndent::modeNumber
virtual uint modeNumber() const
Mode index of this mode.
Definition: kateautoindent.h:185
KateAutoIndent::createIndenter
static KateAutoIndent * createIndenter(KateDocument *doc, uint mode)
Static methods to create and list indention modes.
Definition: kateautoindent.cpp:38
KateDocCursor
Cursor class with a pointer to its document.
Definition: katecursor.h:93
KateNormalIndent
Provides Auto-Indent functionality for katepart.
Definition: kateautoindent.h:218
KateNormalIndent::measureIndent
uint measureIndent(KateDocCursor &cur) const
Measures the indention of the current textline marked by cur.
Definition: kateautoindent.cpp:370
KateNormalIndent::keepProfile
bool keepProfile
Always try to honor the leading whitespace of lines already in the file.
Definition: kateautoindent.h:345
KateNormalIndent::skipBlanks
bool skipBlanks(KateDocCursor &cur, KateDocCursor &max, bool newline) const
Skip all whitespace starting at cur and ending at max.
Definition: kateautoindent.cpp:328
KateNormalIndent::updateConfig
virtual void updateConfig()
Update indenter's configuration (indention width, attributes etc.)
Definition: kateautoindent.cpp:197
KateNormalIndent::indentWidth
uint indentWidth
The number of characters used when tabs are replaced by spaces.
Definition: kateautoindent.h:324
KateNormalIndent::isBalanced
bool isBalanced(KateDocCursor &begin, const KateDocCursor &end, TQChar open, TQChar close, uint &pos) const
Determines if the characters open and close are balanced between begin and end Fills in pos with the ...
Definition: kateautoindent.cpp:282
KateNormalIndent::processNewline
virtual void processNewline(KateDocCursor &cur, bool needContinue)
Called every time a newline character is inserted in the document.
Definition: kateautoindent.cpp:399
KateNormalIndent::useSpaces
bool useSpaces
Should we use spaces or tabs to indent.
Definition: kateautoindent.h:343
KateNormalIndent::mixedIndent
bool mixedIndent
Optimize indent by mixing spaces and tabs, ala emacs.
Definition: kateautoindent.h:344
KateNormalIndent::KateNormalIndent
KateNormalIndent(KateDocument *doc)
Constructor.
Definition: kateautoindent.cpp:186
KateNormalIndent::tabWidth
uint tabWidth
The number of characters simulated for a tab.
Definition: kateautoindent.h:323
KateNormalIndent::tabString
TQString tabString(uint length) const
Produces a string with the proper indentation characters for its length.
Definition: kateautoindent.cpp:378
KateNormalIndent::~KateNormalIndent
virtual ~KateNormalIndent()
Virtual Destructor for the baseclass.
Definition: kateautoindent.cpp:193
KateVarIndent
This indenter uses document variables to determine when to add/remove indents.
Definition: kateautoindent.h:493
KateVarIndent::processSection
virtual void processSection(const KateDocCursor &begin, const KateDocCursor &end)
Processes a section of text, indenting each line in between.
Definition: kateautoindent.cpp:2347
KateVarIndent::processChar
virtual void processChar(TQChar c)
Called every time a character is inserted into the document.
Definition: kateautoindent.cpp:2194
KateVarIndent::processLine
virtual void processLine(KateDocCursor &line)
Aligns/indents the given line to the proper indent position.
Definition: kateautoindent.cpp:2210
KateVarIndent::processNewline
virtual void processNewline(KateDocCursor &cur, bool needContinue)
Called every time a newline character is inserted in the document.
Definition: kateautoindent.cpp:2186
TDEActionMenu
TDESharedPtr
kdWarning
kdbgstream kdWarning(int area=0)
endl
kndbgstream & endl(kndbgstream &s)
kdDebug
kdbgstream kdDebug(int area=0)
TDEStdAccel::next
const TDEShortcut & next()
TDEStdAccel::name
TQString name(StdAccel id)
TDEStdAccel::end
const TDEShortcut & end()
TDEStdAccel::close
const TDEShortcut & close()
TDEStdAccel::replace
const TDEShortcut & replace()
tdelocale.h

kate

Skip menu "kate"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kate

Skip menu "kate"
  • arts
  • dcop
  • dnssd
  • interfaces
  •   kspeech
  •     interface
  •     library
  •   tdetexteditor
  • kate
  • kded
  • kdoctools
  • kimgio
  • kjs
  • libtdemid
  • libtdescreensaver
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdeioslave
  •   http
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for kate by doxygen 1.9.4
This website is maintained by Timothy Pearson.