• 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(),TQ_SIGNAL(aboutToShow()),this,TQ_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, TQ_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, TQ_SIGNAL(hlChanged()), this, TQ_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, "slots"))
483 {
484 indent = findOpeningBrace(line) + indentWidth;
485 }
486 }
487 else if (first == '*')
488 {
489 if (last == '/')
490 {
491 int lineEnd = textLine->lastChar();
492 if (lineEnd > 0 && textLine->getChar(lineEnd - 1) == '*')
493 {
494 indent = findOpeningComment(line);
495 if (textLine->attribute(firstChar) == doxyCommentAttrib)
496 indent++;
497 }
498 else
499 return;
500 }
501 else
502 {
503 KateDocCursor temp = line;
504 if (textLine->attribute(firstChar) == doxyCommentAttrib)
505 indent = calcIndent(temp, false) + 1;
506 else
507 indent = calcIndent(temp, true);
508 }
509 }
510 else if (first == '#')
511 {
512 // c# regions
513 if (textLine->stringAtPos (firstChar, "#region") ||
514 textLine->stringAtPos (firstChar, "#endregion"))
515 {
516 KateDocCursor temp = line;
517 indent = calcIndent(temp, true);
518 }
519 }
520 else
521 {
522 // Everything else ...
523 if (first == '/' && last != '/')
524 return;
525
526 KateDocCursor temp = line;
527 indent = calcIndent(temp, true);
528 if (indent == 0)
529 {
530 KateNormalIndent::processNewline(line, true);
531 return;
532 }
533 }
534
535 // Slightly faster if we don't indent what we don't have to
536 if (indent != measureIndent(line) || first == '}' || first == '{' || first == '#')
537 {
538 doc->removeText(line.line(), 0, line.line(), firstChar);
539 TQString filler = tabString(indent);
540 if (indent > 0) doc->insertText(line.line(), 0, filler);
541 if (!processingBlock) line.setCol(filler.length());
542 }
543}
544
545void KateCSmartIndent::processSection (const KateDocCursor &begin, const KateDocCursor &end)
546{
547 kdDebug(13030)<<"PROCESS SECTION"<<endl;
548 KateDocCursor cur = begin;
549 TQTime t;
550 t.start();
551
552 processingBlock = (end.line() - cur.line() > 0) ? true : false;
553
554 while (cur.line() <= end.line())
555 {
556 processLine (cur);
557 if (!cur.gotoNextLine())
558 break;
559 }
560
561 processingBlock = false;
562 kdDebug(13030) << "+++ total: " << t.elapsed() << endl;
563}
564
565bool KateCSmartIndent::handleDoxygen (KateDocCursor &begin)
566{
567 // Factor out the rather involved Doxygen stuff here ...
568 int line = begin.line();
569 int first = -1;
570 while ((line > 0) && (first < 0))
571 first = doc->plainKateTextLine(--line)->firstChar();
572
573 if (first >= 0)
574 {
575 KateTextLine::Ptr textLine = doc->plainKateTextLine(line);
576 bool insideDoxygen = false;
577 bool justAfterDoxygen = false;
578 if (textLine->attribute(first) == doxyCommentAttrib || textLine->attribute(textLine->lastChar()) == doxyCommentAttrib)
579 {
580 const int last = textLine->lastChar();
581 if (last <= 0 || !(justAfterDoxygen = textLine->stringAtPos(last-1, "*/")))
582 insideDoxygen = true;
583 if (justAfterDoxygen)
584 justAfterDoxygen &= textLine->string().find("/**") < 0;
585 while (textLine->attribute(first) != doxyCommentAttrib && first <= textLine->lastChar())
586 first++;
587 if (textLine->stringAtPos(first, "//"))
588 return false;
589 }
590
591 // Align the *'s and then go ahead and insert one too ...
592 if (insideDoxygen)
593 {
594 textLine = doc->plainKateTextLine(begin.line());
595 first = textLine->firstChar();
596 int indent = findOpeningComment(begin);
597 TQString filler = tabString (indent);
598
599 bool doxygenAutoInsert = doc->config()->configFlags() & KateDocumentConfig::cfDoxygenAutoTyping;
600
601 if ( doxygenAutoInsert &&
602 ((first < 0) || (!textLine->stringAtPos(first, "*/") && !textLine->stringAtPos(first, "*"))))
603 {
604 filler = filler + " * ";
605 }
606
607 doc->removeText (begin.line(), 0, begin.line(), first);
608 doc->insertText (begin.line(), 0, filler);
609 begin.setCol(filler.length());
610
611 return true;
612 }
613 // Align position with beginning of doxygen comment. Otherwise the
614 // indentation is one too much.
615 else if (justAfterDoxygen)
616 {
617 textLine = doc->plainKateTextLine(begin.line());
618 first = textLine->firstChar();
619 int indent = findOpeningComment(begin);
620 TQString filler = tabString (indent);
621
622 doc->removeText (begin.line(), 0, begin.line(), first);
623 doc->insertText (begin.line(), 0, filler);
624 begin.setCol(filler.length());
625
626 return true;
627 }
628 }
629
630 return false;
631}
632
633void KateCSmartIndent::processNewline (KateDocCursor &begin, bool needContinue)
634{
635 if (!handleDoxygen (begin))
636 {
637 KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line());
638 bool inMiddle = textLine->firstChar() > -1;
639
640 int indent = calcIndent (begin, needContinue);
641
642 if (indent > 0 || inMiddle)
643 {
644 TQString filler = tabString (indent);
645 doc->insertText (begin.line(), 0, filler);
646 begin.setCol(filler.length());
647
648 // Handles cases where user hits enter at the beginning or middle of text
649 if (inMiddle)
650 {
651 processLine(begin);
652 begin.setCol(textLine->firstChar());
653 }
654 }
655 else
656 {
657 KateNormalIndent::processNewline (begin, needContinue);
658 }
659
660 if (begin.col() < 0)
661 begin.setCol(0);
662 }
663}
664
674static inline bool isColonImmune(const KateNormalIndent &indenter,
675 uchar attr1, uchar attr2,
676 TQChar prev1, TQChar prev2)
677{
678 return attr1 == indenter.preprocessorAttrib
679 // FIXME: no way to discriminate against multiline comment and single
680 // line comment. Therefore, using prev? is futile.
681 || attr1 == indenter.commentAttrib /*&& prev2 != '*' && prev1 != '/'*/
682 || attr1 == indenter.doxyCommentAttrib
683 || attr1 == indenter.stringAttrib && (attr2 != indenter.stringAttrib
684 || (prev1 != '"' || prev2 == '\\' && attr2 == indenter.charAttrib))
685 || prev1 == '\'' && attr1 != indenter.charAttrib;
686}
687
694static inline bool colonPermitsReindent(const KateNormalIndent &indenter,
695 const KateTextLine::Ptr &line,
696 int curCol
697 )
698{
699 const TQString txt = line->string(0,curCol);
700 // do we have any significant preceding colon?
701 for (int pos = 0; (pos = txt.find(':', pos)) >= 0; pos++) {
702 if (line->attribute(pos) == indenter.symbolAttrib)
703 // yes, it has already contributed to this line's indentation, don't
704 // indent again
705 return false;
706 }
707
708 // otherwise, check whether this colon is not within an influence
709 // immune attribute range
710 return !isColonImmune(indenter, line->attribute(curCol - 1),
711 line->attribute(curCol - 2),
712 txt[curCol - 1], txt[curCol - 2]);
713}
714
715void KateCSmartIndent::processChar(TQChar c)
716{
717 // You may be curious about 'n' among the triggers:
718 // It is used to discriminate C#'s #region/#endregion which are indented
719 // against normal preprocessing statements which aren't indented.
720 static const TQString triggers("}{)/:#n");
721 static const TQString firstTriggers("}{)/:#");
722 static const TQString lastTriggers(":n");
723 if (triggers.find(c) < 0)
724 return;
725
726 KateView *view = doc->activeView();
727 int curCol = view->cursorColumnReal() - 1;
728 KateDocCursor begin(view->cursorLine(), 0, doc);
729
730 KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line());
731 const TQChar curChar = textLine->getChar(curCol);
732 const int first = textLine->firstChar();
733 const TQChar firstChar = textLine->getChar(first);
734
735#if 0 // nice try
736 // Only indent on symbols or preprocessing directives -- never on
737 // anything else
738 kdDebug() << "curChar " << curChar << " curCol " << curCol << " textlen " << textLine->length() << " a " << textLine->attribute( curCol ) << " sym " << symbolAttrib << " pp " << preprocessorAttrib << endl;
739 if (!(((curChar == '#' || curChar == 'n')
740 && textLine->attribute( curCol ) == preprocessorAttrib)
741 || textLine->attribute( curCol ) == symbolAttrib)
742 )
743 return;
744 kdDebug() << "curChar " << curChar << endl;
745#endif
746
747 if (c == 'n')
748 {
749 if (firstChar != '#' || textLine->string(curCol-5, 5) != TQString::fromLatin1("regio"))
750 return;
751 }
752
753 if ( c == '/' )
754 {
755 // dominik: if line is "* /", change it to "*/"
756 if ( textLine->attribute( begin.col() ) == doxyCommentAttrib )
757 {
758 // if the first char exists and is a '*', and the next non-space-char
759 // is already the just typed '/', concatenate it to "*/".
760 if ( first != -1
761 && firstChar == '*'
762 && textLine->nextNonSpaceChar( first+1 ) == view->cursorColumnReal()-1 )
763 doc->removeText( view->cursorLine(), first+1, view->cursorLine(), view->cursorColumnReal()-1);
764 }
765
766 // ls: never have comments change the indentation.
767 return;
768 }
769
770 // ls: only reindent line if the user actually expects it
771 // I. e. take action on single braces on line or last colon, but inhibit
772 // any reindentation if any of those characters appear amidst some section
773 // of the line
774 const TQChar lastChar = textLine->getChar(textLine->lastChar());
775 int pos;
776 if (((c == firstChar && firstTriggers.find(firstChar) >= 0)
777 || (c == lastChar && lastTriggers.find(lastChar) >= 0))
778 && (c != ':' || colonPermitsReindent(*this, textLine, curCol)))
779 processLine(begin);
780}
781
782
783uint KateCSmartIndent::calcIndent(KateDocCursor &begin, bool needContinue)
784{
785 KateTextLine::Ptr textLine;
786 KateDocCursor cur = begin;
787
788 uint anchorIndent = 0;
789 int anchorPos = 0;
790 int parenCount = 0; // Possibly in a multiline for stmt. Used to skip ';' ...
791 bool found = false;
792 bool isSpecial = false;
793 bool potentialAnchorSeen = false;
794 bool isArg = false; // ...arg,<newline>
795 bool parenthesizedArg = false; // ...(arg,<newline>
796
797 //kdDebug(13030) << "calcIndent begin line:" << begin.line() << " col:" << begin.col() << endl;
798
799 // Find Indent Anchor Point
800 while (cur.gotoPreviousLine())
801 {
802 isSpecial = found = false;
803 textLine = doc->plainKateTextLine(cur.line());
804
805 // Skip comments and handle cases like if (...) { stmt;
806 int pos = textLine->lastChar();
807 int openCount = 0;
808 int otherAnchor = -1;
809 do
810 {
811 if (textLine->attribute(pos) == symbolAttrib)
812 {
813 TQChar tc = textLine->getChar (pos);
814 if ((tc == ';' || tc == ':' || tc == ',') && otherAnchor == -1 && parenCount <= 0) {
815 otherAnchor = pos, potentialAnchorSeen = true;
816 isArg = tc == ',';
817 } else if (tc == ')')
818 parenCount++;
819 else if (tc == '(')
820 parenCount--, parenthesizedArg = isArg, potentialAnchorSeen = true;
821 else if (tc == '}')
822 openCount--;
823 else if (tc == '{')
824 {
825 openCount++, potentialAnchorSeen = true;
826 if (openCount == 1)
827 break;
828 }
829 }
830 } while (--pos >= textLine->firstChar());
831
832 if (openCount != 0 || otherAnchor != -1)
833 {
834 found = true;
835 TQChar c;
836 if (openCount > 0)
837 c = '{';
838 else if (openCount < 0)
839 c = '}';
840 else if (otherAnchor >= 0)
841 c = textLine->getChar (otherAnchor);
842
843 int specialIndent = 0;
844 if (c == ':' && needContinue)
845 {
846 TQChar ch;
847 specialIndent = textLine->firstChar();
848 if (textLine->stringAtPos(specialIndent, "case"))
849 ch = textLine->getChar(specialIndent + 4);
850 else if (textLine->stringAtPos(specialIndent, "default"))
851 ch = textLine->getChar(specialIndent + 7);
852 else if (textLine->stringAtPos(specialIndent, "public"))
853 ch = textLine->getChar(specialIndent + 6);
854 else if (textLine->stringAtPos(specialIndent, "private"))
855 ch = textLine->getChar(specialIndent + 7);
856 else if (textLine->stringAtPos(specialIndent, "protected"))
857 ch = textLine->getChar(specialIndent + 9);
858 else if (textLine->stringAtPos(specialIndent, "signals"))
859 ch = textLine->getChar(specialIndent + 7);
860 else if (textLine->stringAtPos(specialIndent, "slots"))
861 ch = textLine->getChar(specialIndent + 5);
862
863 if (ch.isNull() || (!ch.isSpace() && ch != '(' && ch != ':'))
864 continue;
865
866 KateDocCursor lineBegin = cur;
867 lineBegin.setCol(specialIndent);
868 specialIndent = measureIndent(lineBegin);
869 isSpecial = true;
870 }
871
872 // Move forward past blank lines
873 KateDocCursor skip = cur;
874 skip.setCol(textLine->lastChar());
875 bool result = skipBlanks(skip, begin, true);
876
877 anchorPos = skip.col();
878 anchorIndent = measureIndent(skip);
879
880 //kdDebug(13030) << "calcIndent anchorPos:" << anchorPos << " anchorIndent:" << anchorIndent << " at line:" << skip.line() << endl;
881
882 // Accept if it's before requested position or if it was special
883 if (result && skip < begin)
884 {
885 cur = skip;
886 break;
887 }
888 else if (isSpecial)
889 {
890 anchorIndent = specialIndent;
891 break;
892 }
893
894 // Are these on a line by themselves? (i.e. both last and first char)
895 if ((c == '{' || c == '}') && textLine->getChar(textLine->firstChar()) == c)
896 {
897 cur.setCol(anchorPos = textLine->firstChar());
898 anchorIndent = measureIndent (cur);
899 break;
900 }
901 }
902 }
903
904 // treat beginning of document as anchor position
905 if (cur.line() == 0 && cur.col() == 0 && potentialAnchorSeen)
906 found = true;
907
908 if (!found)
909 return 0;
910
911 uint continueIndent = (needContinue) ? calcContinue (cur, begin) : 0;
912 //kdDebug(13030) << "calcIndent continueIndent:" << continueIndent << endl;
913
914 // Move forward from anchor and determine last known reference character
915 // Braces take precedance over others ...
916 textLine = doc->plainKateTextLine(cur.line());
917 TQChar lastChar = textLine->getChar (anchorPos);
918 int lastLine = cur.line();
919 if (lastChar == '#' || lastChar == '[')
920 {
921 // Never continue if # or [ is encountered at this point here
922 // A fail-safe really... most likely an #include, #region, or a c# attribute
923 continueIndent = 0;
924 }
925
926 int openCount = 0;
927 while (cur.validPosition() && cur < begin)
928 {
929 if (!skipBlanks(cur, begin, true))
930 return isArg && !parenthesizedArg ? begin.col() : 0;
931
932 TQChar tc = cur.currentChar();
933 //kdDebug(13030) << " cur.line:" << cur.line() << " cur.col:" << cur.col() << " currentChar '" << tc << "' " << textLine->attribute(cur.col()) << endl;
934 if (cur == begin || tc.isNull())
935 break;
936
937 if (!tc.isSpace() && cur < begin)
938 {
939 uchar attrib = cur.currentAttrib();
940 if (tc == '{' && attrib == symbolAttrib)
941 openCount++;
942 else if (tc == '}' && attrib == symbolAttrib)
943 openCount--;
944
945 lastChar = tc;
946 lastLine = cur.line();
947 }
948 }
949 if (openCount > 0) // Open braces override
950 lastChar = '{';
951
952 uint indent = 0;
953 //kdDebug(13030) << "calcIndent lastChar '" << lastChar << "'" << endl;
954
955 if (lastChar == '{' || (lastChar == ':' && isSpecial && needContinue))
956 {
957 indent = anchorIndent + indentWidth;
958 }
959 else if (lastChar == '}')
960 {
961 indent = anchorIndent;
962 }
963 else if (lastChar == ';')
964 {
965 indent = anchorIndent + ((allowSemi && needContinue) ? continueIndent : 0);
966 }
967 else if (lastChar == ',' || lastChar == '(')
968 {
969 textLine = doc->plainKateTextLine(lastLine);
970 KateDocCursor start(lastLine, textLine->firstChar(), doc);
971 KateDocCursor finish(lastLine, textLine->lastChar() + 1, doc);
972 uint pos = 0;
973
974 if (isBalanced(start, finish, TQChar('('), TQChar(')'), pos) && false)
975 indent = anchorIndent;
976 else
977 {
978 // TODO: Config option. If we're below 48, go ahead and line them up
979 indent = ((pos < 48) ? pos : anchorIndent + (indentWidth * 2));
980 }
981 }
982 else if (!lastChar.isNull())
983 {
984 if (anchorIndent != 0)
985 indent = anchorIndent + continueIndent;
986 else
987 indent = continueIndent;
988 }
989
990 return indent;
991}
992
993uint KateCSmartIndent::calcContinue(KateDocCursor &start, KateDocCursor &end)
994{
995 KateDocCursor cur = start;
996
997 bool needsBalanced = true;
998 bool isFor = false;
999 allowSemi = false;
1000
1001 KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
1002
1003 // Handle cases such as } while (s ... by skipping the leading symbol
1004 if (textLine->attribute(cur.col()) == symbolAttrib)
1005 {
1006 cur.moveForward(1);
1007 skipBlanks(cur, end, false);
1008 }
1009
1010 if (textLine->getChar(cur.col()) == '}')
1011 {
1012 skipBlanks(cur, end, true);
1013 if (cur.line() != start.line())
1014 textLine = doc->plainKateTextLine(cur.line());
1015
1016 if (textLine->stringAtPos(cur.col(), "else"))
1017 cur.setCol(cur.col() + 4);
1018 else
1019 return indentWidth * 2;
1020
1021 needsBalanced = false;
1022 }
1023 else if (textLine->stringAtPos(cur.col(), "else"))
1024 {
1025 cur.setCol(cur.col() + 4);
1026 needsBalanced = false;
1027 int next = textLine->nextNonSpaceChar(cur.col());
1028 if (next >= 0 && textLine->stringAtPos(next, "if"))
1029 {
1030 cur.setCol(next + 2);
1031 needsBalanced = true;
1032 }
1033 }
1034 else if (textLine->stringAtPos(cur.col(), "if"))
1035 {
1036 cur.setCol(cur.col() + 2);
1037 }
1038 else if (textLine->stringAtPos(cur.col(), "do"))
1039 {
1040 cur.setCol(cur.col() + 2);
1041 needsBalanced = false;
1042 }
1043 else if (textLine->stringAtPos(cur.col(), "for"))
1044 {
1045 cur.setCol(cur.col() + 3);
1046 isFor = true;
1047 }
1048 else if (textLine->stringAtPos(cur.col(), "while"))
1049 {
1050 cur.setCol(cur.col() + 5);
1051 }
1052 else if (textLine->stringAtPos(cur.col(), "switch"))
1053 {
1054 cur.setCol(cur.col() + 6);
1055 }
1056 else if (textLine->stringAtPos(cur.col(), "using"))
1057 {
1058 cur.setCol(cur.col() + 5);
1059 }
1060 else
1061 {
1062 return indentWidth * 2;
1063 }
1064
1065 uint openPos = 0;
1066 if (needsBalanced && !isBalanced (cur, end, TQChar('('), TQChar(')'), openPos))
1067 {
1068 allowSemi = isFor;
1069 if (openPos > 0)
1070 return (openPos - textLine->firstChar());
1071 else
1072 return indentWidth * 2;
1073 }
1074
1075 // Check if this statement ends a line now
1076 skipBlanks(cur, end, false);
1077 if (cur == end)
1078 return indentWidth;
1079
1080 if (skipBlanks(cur, end, true))
1081 {
1082 if (cur == end)
1083 return indentWidth;
1084 else
1085 return indentWidth + calcContinue(cur, end);
1086 }
1087
1088 return 0;
1089}
1090
1091uint KateCSmartIndent::findOpeningBrace(KateDocCursor &start)
1092{
1093 KateDocCursor cur = start;
1094 int count = 1;
1095
1096 // Move backwards 1 by 1 and find the opening brace
1097 // Return the indent of that line
1098 while (cur.moveBackward(1))
1099 {
1100 if (cur.currentAttrib() == symbolAttrib)
1101 {
1102 TQChar ch = cur.currentChar();
1103 if (ch == '{')
1104 count--;
1105 else if (ch == '}')
1106 count++;
1107
1108 if (count == 0)
1109 {
1110 KateDocCursor temp(cur.line(), doc->plainKateTextLine(cur.line())->firstChar(), doc);
1111 return measureIndent(temp);
1112 }
1113 }
1114 }
1115
1116 return 0;
1117}
1118
1119bool KateCSmartIndent::firstOpeningBrace(KateDocCursor &start)
1120{
1121 KateDocCursor cur = start;
1122
1123 // Are we the first opening brace at this level?
1124 while(cur.moveBackward(1))
1125 {
1126 if (cur.currentAttrib() == symbolAttrib)
1127 {
1128 TQChar ch = cur.currentChar();
1129 if (ch == '{')
1130 return false;
1131 else if (ch == '}' && cur.col() == 0)
1132 break;
1133 }
1134 }
1135
1136 return true;
1137}
1138
1139uint KateCSmartIndent::findOpeningParen(KateDocCursor &start)
1140{
1141 KateDocCursor cur = start;
1142 int count = 1;
1143
1144 // Move backwards 1 by 1 and find the opening (
1145 // Return the indent of that line
1146 while (cur.moveBackward(1))
1147 {
1148 if (cur.currentAttrib() == symbolAttrib)
1149 {
1150 TQChar ch = cur.currentChar();
1151 if (ch == '(')
1152 count--;
1153 else if (ch == ')')
1154 count++;
1155
1156 if (count == 0)
1157 return measureIndent(cur);
1158 }
1159 }
1160
1161 return 0;
1162}
1163
1164uint KateCSmartIndent::findOpeningComment(KateDocCursor &start)
1165{
1166 KateDocCursor cur = start;
1167
1168 // Find the line with the opening /* and return the proper indent
1169 do
1170 {
1171 KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
1172
1173 int pos = textLine->string().find("/*", false);
1174 if (pos >= 0)
1175 {
1176 KateDocCursor temp(cur.line(), pos, doc);
1177 return measureIndent(temp);
1178 }
1179
1180 } while (cur.gotoPreviousLine());
1181
1182 return 0;
1183}
1184
1185//END
1186
1187//BEGIN KatePythonIndent
1188
1189TQRegExp KatePythonIndent::endWithColon = TQRegExp( "^[^#]*:\\s*(#.*)?$" );
1190TQRegExp KatePythonIndent::stopStmt = TQRegExp( "^\\s*(break|continue|raise|return|pass)\\b.*" );
1191TQRegExp KatePythonIndent::blockBegin = TQRegExp( "^\\s*(class|def|if|elif|else|for|while|try)\\b.*" );
1192
1193KatePythonIndent::KatePythonIndent (KateDocument *doc)
1194: KateNormalIndent (doc)
1195{
1196}
1197KatePythonIndent::~KatePythonIndent ()
1198{
1199}
1200
1201void KatePythonIndent::processNewline (KateDocCursor &begin, bool /*newline*/)
1202{
1203 int prevLine = begin.line() - 1;
1204 int prevPos = begin.col();
1205
1206 while ((prevLine > 0) && (prevPos < 0)) // search a not empty text line
1207 prevPos = doc->plainKateTextLine(--prevLine)->firstChar();
1208
1209 int prevBlock = prevLine;
1210 int prevBlockPos = prevPos;
1211 int extraIndent = calcExtra (prevBlock, prevBlockPos, begin);
1212
1213 int indent = doc->plainKateTextLine(prevBlock)->cursorX(prevBlockPos, tabWidth);
1214 if (extraIndent == 0)
1215 {
1216 if (!stopStmt.exactMatch(doc->plainKateTextLine(prevLine)->string()))
1217 {
1218 if (endWithColon.exactMatch(doc->plainKateTextLine(prevLine)->string()))
1219 indent += indentWidth;
1220 else
1221 indent = doc->plainKateTextLine(prevLine)->cursorX(prevPos, tabWidth);
1222 }
1223 }
1224 else
1225 indent += extraIndent;
1226
1227 if (indent > 0)
1228 {
1229 TQString filler = tabString (indent);
1230 doc->insertText (begin.line(), 0, filler);
1231 begin.setCol(filler.length());
1232 }
1233 else
1234 begin.setCol(0);
1235}
1236
1237int KatePythonIndent::calcExtra (int &prevBlock, int &pos, KateDocCursor &end)
1238{
1239 int nestLevel = 0;
1240 bool levelFound = false;
1241 while ((prevBlock > 0))
1242 {
1243 if (blockBegin.exactMatch(doc->plainKateTextLine(prevBlock)->string()))
1244 {
1245 if ((!levelFound && nestLevel == 0) || (levelFound && nestLevel - 1 <= 0))
1246 {
1247 pos = doc->plainKateTextLine(prevBlock)->firstChar();
1248 break;
1249 }
1250
1251 nestLevel --;
1252 }
1253 else if (stopStmt.exactMatch(doc->plainKateTextLine(prevBlock)->string()))
1254 {
1255 nestLevel ++;
1256 levelFound = true;
1257 }
1258
1259 --prevBlock;
1260 }
1261
1262 KateDocCursor cur (prevBlock, pos, doc);
1263 TQChar c;
1264 int extraIndent = 0;
1265 while (cur.line() < end.line())
1266 {
1267 c = cur.currentChar();
1268
1269 if (c == '(')
1270 extraIndent += indentWidth;
1271 else if (c == ')')
1272 extraIndent -= indentWidth;
1273 else if (c == ':')
1274 break;
1275 else if (c == '\'' || c == '"' )
1276 traverseString( c, cur, end );
1277
1278 if (c.isNull() || c == '#')
1279 cur.gotoNextLine();
1280 else
1281 cur.moveForward(1);
1282 }
1283
1284 return extraIndent;
1285}
1286
1287void KatePythonIndent::traverseString( const TQChar &stringChar, KateDocCursor &cur, KateDocCursor &end )
1288{
1289 TQChar c;
1290 bool escape = false;
1291
1292 cur.moveForward(1);
1293 c = cur.currentChar();
1294 while ( ( c != stringChar || escape ) && cur.line() < end.line() )
1295 {
1296 if ( escape )
1297 escape = false;
1298 else if ( c == '\\' )
1299 escape = !escape;
1300
1301 cur.moveForward(1);
1302 c = cur.currentChar();
1303 }
1304}
1305
1306//END
1307
1308//BEGIN KateXmlIndent
1309
1310/* Explanation
1311
1312The XML indenter simply inherits the indentation of the previous line,
1313with the first line starting at 0 (of course!). For each element that
1314is opened on the previous line, the indentation is increased by one
1315level; for each element that is closed, it is decreased by one.
1316
1317We also have a special case of opening an element on one line and then
1318entering attributes on the following lines, in which case we would like
1319to see the following layout:
1320<elem attr="..."
1321 blah="..." />
1322
1323<x><a href="..."
1324 title="..." />
1325</x>
1326
1327This is accomplished by checking for lines that contain an unclosed open
1328tag.
1329
1330*/
1331
1332const TQRegExp KateXmlIndent::startsWithCloseTag("^[ \t]*</");
1333const TQRegExp KateXmlIndent::unclosedDoctype("<!DOCTYPE[^>]*$");
1334
1335KateXmlIndent::KateXmlIndent (KateDocument *doc)
1336: KateNormalIndent (doc)
1337{
1338}
1339
1340KateXmlIndent::~KateXmlIndent ()
1341{
1342}
1343
1344void KateXmlIndent::processNewline (KateDocCursor &begin, bool /*newline*/)
1345{
1346 begin.setCol(processLine(begin.line()));
1347}
1348
1349void KateXmlIndent::processChar (TQChar c)
1350{
1351 if(c != '/') return;
1352
1353 // only alter lines that start with a close element
1354 KateView *view = doc->activeView();
1355 TQString text = doc->plainKateTextLine(view->cursorLine())->string();
1356 if(text.find(startsWithCloseTag) == -1) return;
1357
1358 // process it
1359 processLine(view->cursorLine());
1360}
1361
1362void KateXmlIndent::processLine (KateDocCursor &line)
1363{
1364 processLine (line.line());
1365}
1366
1367void KateXmlIndent::processSection (const KateDocCursor &start, const KateDocCursor &end)
1368{
1369 KateDocCursor cur (start);
1370 int endLine = end.line();
1371
1372 do {
1373 processLine(cur.line());
1374 if(!cur.gotoNextLine()) break;
1375 } while(cur.line() < endLine);
1376}
1377
1378void KateXmlIndent::getLineInfo (uint line, uint &prevIndent, int &numTags,
1379 uint &attrCol, bool &unclosedTag)
1380{
1381 prevIndent = 0;
1382 int firstChar;
1383 KateTextLine::Ptr prevLine = 0;
1384
1385 // get the indentation of the first non-empty line
1386 while(true) {
1387 prevLine = doc->plainKateTextLine(line);
1388 if( (firstChar = prevLine->firstChar()) < 0) {
1389 if(!line--) return;
1390 continue;
1391 }
1392 break;
1393 }
1394 prevIndent = prevLine->cursorX(prevLine->firstChar(), tabWidth);
1395 TQString text = prevLine->string();
1396
1397 // special case:
1398 // <a>
1399 // </a> <!-- indentation *already* decreased -->
1400 // requires that we discount the </a> from the number of closed tags
1401 if(text.find(startsWithCloseTag) != -1) ++numTags;
1402
1403 // count the number of open and close tags
1404 int lastCh = 0;
1405 uint pos, len = text.length();
1406 bool seenOpen = false;
1407 for(pos = 0; pos < len; ++pos) {
1408 int ch = text.at(pos).unicode();
1409 switch(ch) {
1410 case '<':
1411 seenOpen = true;
1412 unclosedTag = true;
1413 attrCol = pos;
1414 ++numTags;
1415 break;
1416
1417 // don't indent because of DOCTYPE, comment, CDATA, etc.
1418 case '!':
1419 if(lastCh == '<') --numTags;
1420 break;
1421
1422 // don't indent because of xml decl or PI
1423 case '?':
1424 if(lastCh == '<') --numTags;
1425 break;
1426
1427 case '>':
1428 if(!seenOpen) {
1429 // we are on a line like the second one here:
1430 // <element attr="val"
1431 // other="val">
1432 // so we need to set prevIndent to the indent of the first line
1433 //
1434 // however, we need to special case "<!DOCTYPE" because
1435 // it's not an open tag
1436
1437 prevIndent = 0;
1438
1439 for(uint backLine = line; backLine; ) {
1440 // find first line with an open tag
1441 KateTextLine::Ptr x = doc->plainKateTextLine(--backLine);
1442 if(x->string().find('<') == -1) continue;
1443
1444 // recalculate the indent
1445 if(x->string().find(unclosedDoctype) != -1) --numTags;
1446 getLineInfo(backLine, prevIndent, numTags, attrCol, unclosedTag);
1447 break;
1448 }
1449 }
1450 if(lastCh == '/') --numTags;
1451 unclosedTag = false;
1452 break;
1453
1454 case '/':
1455 if(lastCh == '<') numTags -= 2; // correct for '<', above
1456 break;
1457 }
1458 lastCh = ch;
1459 }
1460
1461 if(unclosedTag) {
1462 // find the start of the next attribute, so we can align with it
1463 do {
1464 lastCh = text.at(++attrCol).unicode();
1465 }while(lastCh && lastCh != ' ' && lastCh != '\t');
1466
1467 while(lastCh == ' ' || lastCh == '\t') {
1468 lastCh = text.at(++attrCol).unicode();
1469 }
1470
1471 attrCol = prevLine->cursorX(attrCol, tabWidth);
1472 }
1473}
1474
1475uint KateXmlIndent::processLine (uint line)
1476{
1477 KateTextLine::Ptr kateLine = doc->plainKateTextLine(line);
1478 if(!kateLine) return 0; // sanity check
1479
1480 // get details from previous line
1481 uint prevIndent = 0, attrCol = 0;
1482 int numTags = 0;
1483 bool unclosedTag = false; // for aligning attributes
1484
1485 if(line) {
1486 getLineInfo(line - 1, prevIndent, numTags, attrCol, unclosedTag);
1487 }
1488
1489 // compute new indent
1490 int indent = 0;
1491 if(unclosedTag) indent = attrCol;
1492 else indent = prevIndent + numTags * indentWidth;
1493 if(indent < 0) indent = 0;
1494
1495 // unindent lines that start with a close tag
1496 if(kateLine->string().find(startsWithCloseTag) != -1) {
1497 indent -= indentWidth;
1498 }
1499 if(indent < 0) indent = 0;
1500
1501 // apply new indent
1502 doc->removeText(line, 0, line, kateLine->firstChar());
1503 TQString filler = tabString(indent);
1504 doc->insertText(line, 0, filler);
1505
1506 return filler.length();
1507}
1508
1509//END
1510
1511//BEGIN KateCSAndSIndent
1512
1513KateCSAndSIndent::KateCSAndSIndent (KateDocument *doc)
1514: KateNormalIndent (doc)
1515{
1516}
1517
1518void KateCSAndSIndent::updateIndentString()
1519{
1520 if( useSpaces )
1521 indentString.fill( ' ', indentWidth );
1522 else
1523 indentString = '\t';
1524}
1525
1526KateCSAndSIndent::~KateCSAndSIndent ()
1527{
1528}
1529
1530void KateCSAndSIndent::processLine (KateDocCursor &line)
1531{
1532 KateTextLine::Ptr textLine = doc->plainKateTextLine(line.line());
1533
1534 if (!textLine)
1535 return;
1536
1537 updateIndentString();
1538
1539 const int oldCol = line.col();
1540 TQString whitespace = calcIndent(line);
1541 // strip off existing whitespace
1542 int oldIndent = textLine->firstChar();
1543 if ( oldIndent < 0 )
1544 oldIndent = doc->lineLength( line.line() );
1545 if( oldIndent > 0 )
1546 doc->removeText(line.line(), 0, line.line(), oldIndent);
1547 // add correct amount
1548 doc->insertText(line.line(), 0, whitespace);
1549
1550 // try to preserve the cursor position in the line
1551 if ( int(oldCol + whitespace.length()) >= oldIndent )
1552 line.setCol( oldCol + whitespace.length() - oldIndent );
1553 else
1554 line.setCol( 0 );
1555}
1556
1557void KateCSAndSIndent::processSection (const KateDocCursor &begin, const KateDocCursor &end)
1558{
1559 TQTime t; t.start();
1560 for( KateDocCursor cur = begin; cur.line() <= end.line(); )
1561 {
1562 processLine (cur);
1563 if (!cur.gotoNextLine())
1564 break;
1565 }
1566 kdDebug(13030) << "+++ total: " << t.elapsed() << endl;
1567}
1568
1574static TQString initialWhitespace(const KateTextLine::Ptr &line, int chars, bool convert = true)
1575{
1576 TQString text = line->string(0, chars);
1577 if( (int)text.length() < chars )
1578 {
1579 TQString filler; filler.fill(' ',chars - text.length());
1580 text += filler;
1581 }
1582 for( uint n = 0; n < text.length(); ++n )
1583 {
1584 if( text[n] != '\t' && text[n] != ' ' )
1585 {
1586 if( !convert )
1587 return text.left( n );
1588 text[n] = ' ';
1589 }
1590 }
1591 return text;
1592}
1593
1594TQString KateCSAndSIndent::findOpeningCommentIndentation(const KateDocCursor &start)
1595{
1596 KateDocCursor cur = start;
1597
1598 // Find the line with the opening /* and return the indentation of it
1599 do
1600 {
1601 KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
1602
1603 int pos = textLine->string().findRev("/*");
1604 // FIXME: /* inside /* is possible. This screws up in that case...
1605 if (pos >= 0)
1606 return initialWhitespace(textLine, pos);
1607 } while (cur.gotoPreviousLine());
1608
1609 // should never happen.
1610 kdWarning( 13030 ) << " in a comment, but can't find the start of it" << endl;
1611 return TQString::null;
1612}
1613
1614bool KateCSAndSIndent::handleDoxygen (KateDocCursor &begin)
1615{
1616 // Look backwards for a nonempty line
1617 int line = begin.line();
1618 int first = -1;
1619 while ((line > 0) && (first < 0))
1620 first = doc->plainKateTextLine(--line)->firstChar();
1621
1622 // no earlier nonempty line
1623 if (first < 0)
1624 return false;
1625
1626 KateTextLine::Ptr textLine = doc->plainKateTextLine(line);
1627
1628 // if the line doesn't end with a doxygen comment (that's not closed)
1629 // and doesn't start with a doxygen comment (that's not closed), we don't care.
1630 // note that we do need to check the start of the line, or lines ending with, say, @brief aren't
1631 // recognised.
1632 if ( !(textLine->attribute(textLine->lastChar()) == doxyCommentAttrib && !textLine->endingWith("*/")) &&
1633 !(textLine->attribute(textLine->firstChar()) == doxyCommentAttrib && !textLine->string().contains("*/")) )
1634 return false;
1635
1636 // our line is inside a doxygen comment. align the *'s and then maybe insert one too ...
1637 textLine = doc->plainKateTextLine(begin.line());
1638 first = textLine->firstChar();
1639 TQString indent = findOpeningCommentIndentation(begin);
1640
1641 bool doxygenAutoInsert = doc->config()->configFlags() & KateDocumentConfig::cfDoxygenAutoTyping;
1642
1643 // starts with *: indent one space more to line up *s
1644 if ( first >= 0 && textLine->stringAtPos(first, "*") )
1645 indent = indent + " ";
1646 // does not start with *: insert one if user wants that
1647 else if ( doxygenAutoInsert )
1648 indent = indent + " * ";
1649 // user doesn't want * inserted automatically: put in spaces?
1650 //else
1651 // indent = indent + " ";
1652
1653 doc->removeText (begin.line(), 0, begin.line(), first);
1654 doc->insertText (begin.line(), 0, indent);
1655 begin.setCol(indent.length());
1656
1657 return true;
1658}
1659
1666void KateCSAndSIndent::processNewline (KateDocCursor &begin, bool /*needContinue*/)
1667{
1668 // in a comment, add a * doxygen-style.
1669 if( handleDoxygen(begin) )
1670 return;
1671
1672 // TODO: if the user presses enter in the middle of a label, maybe the first half of the
1673 // label should be indented?
1674
1675 // where the cursor actually is...
1676 int cursorPos = doc->plainKateTextLine( begin.line() )->firstChar();
1677 if ( cursorPos < 0 )
1678 cursorPos = doc->lineLength( begin.line() );
1679 begin.setCol( cursorPos );
1680
1681 processLine( begin );
1682}
1683
1688bool KateCSAndSIndent::startsWithLabel( int line )
1689{
1690 // Get the current line.
1691 KateTextLine::Ptr indentLine = doc->plainKateTextLine(line);
1692 const int indentFirst = indentLine->firstChar();
1693
1694 // Not entirely sure what this check does.
1695 int attrib = indentLine->attribute(indentFirst);
1696 if (attrib != 0 && attrib != keywordAttrib && attrib != normalAttrib && attrib != extensionAttrib)
1697 return false;
1698
1699 // Get the line text.
1700 const TQString lineContents = indentLine->string();
1701 const int indentLast = indentLine->lastChar();
1702 bool whitespaceFound = false;
1703 for ( int n = indentFirst; n <= indentLast; ++n )
1704 {
1705 // Get the character as latin1. Can't use TQChar::isLetterOrNumber()
1706 // as that includes non 0-9 numbers.
1707 char c = lineContents[n].latin1();
1708 if ( c == ':' )
1709 {
1710 // See if the next character is ':' - if so, skip to the character after it.
1711 if ( n < lineContents.length() - 1 )
1712 {
1713 if ( lineContents[n+1].latin1() == ':' )
1714 {
1715 n += 2;
1716 continue;
1717 }
1718 }
1719 // Right this is the relevent ':'.
1720 if ( n == indentFirst)
1721 {
1722 // Just a line with a : on it.
1723 return false;
1724 }
1725 // It is a label of some kind!
1726 return true;
1727 }
1728 if (isspace(c))
1729 {
1730 if (!whitespaceFound)
1731 {
1732 if (lineContents.mid(indentFirst, n - indentFirst) == "case")
1733 return true;
1734 else if (lineContents.mid(indentFirst, n - indentFirst) == "class")
1735 return false;
1736 whitespaceFound = true;
1737 }
1738 }
1739 // All other characters don't indent.
1740 else if ( !isalnum(c) && c != '_' )
1741 {
1742 return false;
1743 }
1744 }
1745 return false;
1746}
1747
1748template<class T> T min(T a, T b) { return (a < b) ? a : b; }
1749
1750int KateCSAndSIndent::lastNonCommentChar( const KateDocCursor &line )
1751{
1752 KateTextLine::Ptr textLine = doc->plainKateTextLine( line.line() );
1753 TQString str = textLine->string();
1754
1755 // find a possible start-of-comment
1756 int p = -2; // so the first find starts at position 0
1757 do p = str.find( "//", p + 2 );
1758 while ( p >= 0 && textLine->attribute(p) != commentAttrib && textLine->attribute(p) != doxyCommentAttrib );
1759
1760 // no // found? use whole string
1761 if ( p < 0 )
1762 p = str.length();
1763
1764 // ignore trailing blanks. p starts one-past-the-end.
1765 while( p > 0 && str[p-1].isSpace() ) --p;
1766 return p - 1;
1767}
1768
1769bool KateCSAndSIndent::inForStatement( int line )
1770{
1771 // does this line end in a for ( ...
1772 // with no closing ) ?
1773 int parens = 0, semicolons = 0;
1774 for ( ; line >= 0; --line )
1775 {
1776 KateTextLine::Ptr textLine = doc->plainKateTextLine(line);
1777 const int first = textLine->firstChar();
1778 const int last = textLine->lastChar();
1779
1780 // look backwards for a symbol: (){};
1781 // match ()s, {...; and }...; => not in a for
1782 // ; ; ; => not in a for
1783 // ( ; and ( ; ; => a for
1784 for ( int curr = last; curr >= first; --curr )
1785 {
1786 if ( textLine->attribute(curr) != symbolAttrib )
1787 continue;
1788
1789 switch( textLine->getChar(curr) )
1790 {
1791 case ';':
1792 if( ++semicolons > 2 )
1793 return false;
1794 break;
1795 case '{': case '}':
1796 return false;
1797 case ')':
1798 ++parens;
1799 break;
1800 case '(':
1801 if( --parens < 0 )
1802 return true;
1803 break;
1804 }
1805 }
1806 }
1807 // no useful symbols before the ;?
1808 // not in a for then
1809 return false;
1810}
1811
1812
1813// is the start of the line containing 'begin' in a statement?
1814bool KateCSAndSIndent::inStatement( const KateDocCursor &begin )
1815{
1816 // if the current line starts with an open brace, it's not a continuation.
1817 // this happens after a function definition (which is treated as a continuation).
1818 KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line());
1819 const int first = textLine->firstChar();
1820 // note that if we're being called from processChar the attribute has not yet been calculated
1821 // should be reasonably safe to assume that unattributed {s are symbols; if the { is in a comment
1822 // we don't want to touch it anyway.
1823 const int attrib = textLine->attribute(first);
1824 if( first >= 0 && (attrib == 0 || attrib == symbolAttrib) && textLine->getChar(first) == '{' )
1825 return false;
1826
1827 int line;
1828 for ( line = begin.line() - 1; line >= 0; --line )
1829 {
1830 textLine = doc->plainKateTextLine(line);
1831 const int first = textLine->firstChar();
1832 if ( first == -1 )
1833 continue;
1834
1835 // starts with #: in a comment, don't care
1836 // outside a comment: preprocessor, don't care
1837 if ( textLine->getChar( first ) == '#' )
1838 continue;
1839 KateDocCursor currLine = begin;
1840 currLine.setLine( line );
1841 const int last = lastNonCommentChar( currLine );
1842 if ( last < first )
1843 continue;
1844
1845 // HACK: if we see a comment, assume boldly that this isn't a continuation.
1846 // detecting comments (using attributes) is HARD, since they may have
1847 // embedded alerts, or doxygen stuff, or just about anything. this is
1848 // wrong, and needs fixing. note that only multi-line comments and
1849 // single-line comments continued with \ are affected.
1850 const int attrib = textLine->attribute(last);
1851 if ( attrib == commentAttrib || attrib == doxyCommentAttrib )
1852 return false;
1853
1854 char c = textLine->getChar(last);
1855
1856 // brace => not a continuation.
1857 if ( attrib == symbolAttrib && c == '{' || c == '}' )
1858 return false;
1859
1860 // ; => not a continuation, unless in a for (;;)
1861 if ( attrib == symbolAttrib && c == ';' )
1862 return inForStatement( line );
1863
1864 // found something interesting. maybe it's a label?
1865 if ( attrib == symbolAttrib && c == ':' )
1866 {
1867 // the : above isn't necessarily the : in the label, eg in
1868 // case 'x': a = b ? c :
1869 // this will say no continuation incorrectly. but continued statements
1870 // starting on a line with a label at the start is Bad Style (tm).
1871 if( startsWithLabel( line ) )
1872 {
1873 // either starts with a label or a continuation. if the current line
1874 // starts in a continuation, we're still in one. if not, this was
1875 // a label, so we're not in one now. so continue to the next line
1876 // upwards.
1877 continue;
1878 }
1879 }
1880
1881 // any other character => in a continuation
1882 return true;
1883 }
1884 // no non-comment text found before here - not a continuation.
1885 return false;
1886}
1887
1888TQString KateCSAndSIndent::continuationIndent( const KateDocCursor &begin )
1889{
1890 if( !inStatement( begin ) )
1891 return TQString::null;
1892 return indentString;
1893}
1894
1898TQString KateCSAndSIndent::calcIndent (const KateDocCursor &begin)
1899{
1900 KateTextLine::Ptr currLine = doc->plainKateTextLine(begin.line());
1901 int currLineFirst = currLine->firstChar();
1902
1903 // if the line starts inside a comment, no change of indentation.
1904 // FIXME: this unnecessarily copies the current indentation over itself.
1905 // FIXME: on newline, this should copy from the previous line.
1906 if ( currLineFirst >= 0 &&
1907 (currLine->attribute(currLineFirst) == commentAttrib ||
1908 currLine->attribute(currLineFirst) == doxyCommentAttrib) )
1909 return currLine->string( 0, currLineFirst );
1910
1911 // if the line starts with # (but isn't a c# region thingy), no indentation at all.
1912 if( currLineFirst >= 0 && currLine->getChar(currLineFirst) == '#' )
1913 {
1914 if( !currLine->stringAtPos( currLineFirst+1, TQString::fromLatin1("region") ) &&
1915 !currLine->stringAtPos( currLineFirst+1, TQString::fromLatin1("endregion") ) )
1916 return TQString::null;
1917 }
1918
1919 /* Strategy:
1920 * Look for an open bracket or brace, or a keyword opening a new scope, whichever comes latest.
1921 * Found a brace: indent one tab in.
1922 * Found a bracket: indent to the first non-white after it.
1923 * Found a keyword: indent one tab in. for try, catch and switch, if newline is set, also add
1924 * an open brace, a newline, and indent two tabs in.
1925 */
1926 KateDocCursor cur = begin;
1927 int pos, openBraceCount = 0, openParenCount = 0;
1928 bool lookingForScopeKeywords = true;
1929 const char * const scopeKeywords[] = { "for", "do", "while", "if", "else" };
1930 const char * const blockScopeKeywords[] = { "try", "catch", "switch" };
1931
1932 while (cur.gotoPreviousLine())
1933 {
1934 KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
1935 const int lastChar = textLine->lastChar();
1936 const int firstChar = textLine->firstChar();
1937
1938 // look through line backwards for interesting characters
1939 for( pos = lastChar; pos >= firstChar; --pos )
1940 {
1941 if (textLine->attribute(pos) == symbolAttrib)
1942 {
1943 char tc = textLine->getChar (pos);
1944 switch( tc )
1945 {
1946 case '(': case '[':
1947 if( ++openParenCount > 0 )
1948 return calcIndentInBracket( begin, cur, pos );
1949 break;
1950 case ')': case ']': openParenCount--; break;
1951 case '{':
1952 if( ++openBraceCount > 0 )
1953 return calcIndentInBrace( begin, cur, pos );
1954 break;
1955 case '}': openBraceCount--; lookingForScopeKeywords = false; break;
1956 case ';':
1957 if( openParenCount == 0 )
1958 lookingForScopeKeywords = false;
1959 break;
1960 }
1961 }
1962
1963 // if we've not had a close brace or a semicolon yet, and we're at the same parenthesis level
1964 // as the cursor, and we're at the start of a scope keyword, indent from it.
1965 if ( lookingForScopeKeywords && openParenCount == 0 &&
1966 textLine->attribute(pos) == keywordAttrib &&
1967 (pos == 0 || textLine->attribute(pos-1) != keywordAttrib ) )
1968 {
1969 #define ARRLEN( array ) ( sizeof(array)/sizeof(array[0]) )
1970 for( uint n = 0; n < ARRLEN(scopeKeywords); ++n )
1971 if( textLine->stringAtPos(pos, TQString::fromLatin1(scopeKeywords[n]) ) )
1972 return calcIndentAfterKeyword( begin, cur, pos, false );
1973 for( uint n = 0; n < ARRLEN(blockScopeKeywords); ++n )
1974 if( textLine->stringAtPos(pos, TQString::fromLatin1(blockScopeKeywords[n]) ) )
1975 return calcIndentAfterKeyword( begin, cur, pos, true );
1976 #undef ARRLEN
1977 }
1978 }
1979 }
1980
1981 // no active { in file.
1982 return TQString::null;
1983}
1984
1985TQString KateCSAndSIndent::calcIndentInBracket(const KateDocCursor &indentCursor, const KateDocCursor &bracketCursor, int bracketPos)
1986{
1987 KateTextLine::Ptr indentLine = doc->plainKateTextLine(indentCursor.line());
1988 KateTextLine::Ptr bracketLine = doc->plainKateTextLine(bracketCursor.line());
1989
1990 // FIXME: hard-coded max indent to bracket width - use a kate variable
1991 // FIXME: expand tabs first...
1992 if ( bracketPos > 48 )
1993 {
1994 // how far to indent? we could look back for a brace or keyword, 2 from that.
1995 // as it is, we just indent one more than the line with the ( on it.
1996 // the potential problem with this is when
1997 // you have code ( which does <-- continuation + start of func call
1998 // something like this ); <-- extra indentation for func call
1999 // then again (
2000 // it works better than (
2001 // the other method for (
2002 // cases like this )));
2003 // consequently, i think this method wins.
2004 return indentString + initialWhitespace( bracketLine, bracketLine->firstChar() );
2005 }
2006
2007 const int indentLineFirst = indentLine->firstChar();
2008
2009 int indentTo;
2010 const int attrib = indentLine->attribute(indentLineFirst);
2011 if( indentLineFirst >= 0 && (attrib == 0 || attrib == symbolAttrib) &&
2012 ( indentLine->getChar(indentLineFirst) == ')' || indentLine->getChar(indentLineFirst) == ']' ) )
2013 {
2014 // If the line starts with a close bracket, line it up
2015 indentTo = bracketPos;
2016 }
2017 else
2018 {
2019 // Otherwise, line up with the text after the open bracket
2020 indentTo = bracketLine->nextNonSpaceChar( bracketPos + 1 );
2021 if( indentTo == -1 )
2022 indentTo = bracketPos + 2;
2023 }
2024 return initialWhitespace( bracketLine, indentTo );
2025}
2026
2027TQString KateCSAndSIndent::calcIndentAfterKeyword(const KateDocCursor &indentCursor, const KateDocCursor &keywordCursor, int keywordPos, bool blockKeyword)
2028{
2029 KateTextLine::Ptr keywordLine = doc->plainKateTextLine(keywordCursor.line());
2030 KateTextLine::Ptr indentLine = doc->plainKateTextLine(indentCursor.line());
2031
2032 TQString whitespaceToKeyword = initialWhitespace( keywordLine, keywordPos, false );
2033 if( blockKeyword ) {
2034 // FIXME: we could add the open brace and subsequent newline here since they're definitely needed.
2035 }
2036
2037 // If the line starts with an open brace, don't indent...
2038 int first = indentLine->firstChar();
2039 // if we're being called from processChar attribute won't be set
2040 const int attrib = indentLine->attribute(first);
2041 if( first >= 0 && (attrib == 0 || attrib == symbolAttrib) && indentLine->getChar(first) == '{' )
2042 return whitespaceToKeyword;
2043
2044 // don't check for a continuation. rules are simple here:
2045 // if we're in a non-compound statement after a scope keyword, we indent all lines
2046 // once. so:
2047 // if ( some stuff
2048 // goes here )
2049 // apples, and <-- continuation here is ignored. but this is Bad Style (tm) anyway.
2050 // oranges too;
2051 return indentString + whitespaceToKeyword;
2052}
2053
2054TQString KateCSAndSIndent::calcIndentInBrace(const KateDocCursor &indentCursor, const KateDocCursor &braceCursor, int bracePos)
2055{
2056 KateTextLine::Ptr braceLine = doc->plainKateTextLine(braceCursor.line());
2057 const int braceFirst = braceLine->firstChar();
2058
2059 TQString whitespaceToOpenBrace = initialWhitespace( braceLine, bracePos, false );
2060
2061 // if the open brace is the start of a namespace, don't indent...
2062 // FIXME: this is an extremely poor heuristic. it looks on the line with
2063 // the { and the line before to see if they start with a keyword
2064 // beginning 'namespace'. that's 99% of usage, I'd guess.
2065 {
2066 if( braceFirst >= 0 && braceLine->attribute(braceFirst) == keywordAttrib &&
2067 braceLine->stringAtPos( braceFirst, TQString::fromLatin1( "namespace" ) ) )
2068 return continuationIndent(indentCursor) + whitespaceToOpenBrace;
2069
2070 if( braceCursor.line() > 0 )
2071 {
2072 KateTextLine::Ptr prevLine = doc->plainKateTextLine(braceCursor.line() - 1);
2073 int firstPrev = prevLine->firstChar();
2074 if( firstPrev >= 0 && prevLine->attribute(firstPrev) == keywordAttrib &&
2075 prevLine->stringAtPos( firstPrev, TQString::fromLatin1( "namespace" ) ) )
2076 return continuationIndent(indentCursor) + whitespaceToOpenBrace;
2077 }
2078 }
2079
2080 KateTextLine::Ptr indentLine = doc->plainKateTextLine(indentCursor.line());
2081 const int indentFirst = indentLine->firstChar();
2082
2083 // if the line starts with a close brace, don't indent...
2084 if( indentFirst >= 0 && indentLine->getChar(indentFirst) == '}' )
2085 return whitespaceToOpenBrace;
2086
2087 // if : is the first character (and not followed by another :), this is the start
2088 // of an initialization list, or a continuation of a ?:. either way, indent twice.
2089 if ( indentFirst >= 0 && indentLine->attribute(indentFirst) == symbolAttrib &&
2090 indentLine->getChar(indentFirst) == ':' && indentLine->getChar(indentFirst+1) != ':' )
2091 {
2092 return indentString + indentString + whitespaceToOpenBrace;
2093 }
2094
2095 const bool continuation = inStatement(indentCursor);
2096 // if the current line starts with a label, don't indent...
2097 if( !continuation && startsWithLabel( indentCursor.line() ) )
2098 return whitespaceToOpenBrace;
2099
2100 // the normal case: indent once for the brace, again if it's a continuation
2101 TQString continuationIndent = continuation ? indentString : TQString::null;
2102 return indentString + continuationIndent + whitespaceToOpenBrace;
2103}
2104
2105void KateCSAndSIndent::processChar(TQChar c)
2106{
2107 // 'n' trigger is for c# regions.
2108 static const TQString triggers("}{)]/:;#n");
2109 if (triggers.find(c) == -1)
2110 return;
2111
2112 // for historic reasons, processChar doesn't get a cursor
2113 // to work on. so fabricate one.
2114 KateView *view = doc->activeView();
2115 KateDocCursor begin(view->cursorLine(), 0, doc);
2116
2117 KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line());
2118 if ( c == 'n' )
2119 {
2120 int first = textLine->firstChar();
2121 if( first < 0 || textLine->getChar(first) != '#' )
2122 return;
2123 }
2124
2125 if ( textLine->attribute( begin.col() ) == doxyCommentAttrib )
2126 {
2127 // dominik: if line is "* /", change it to "*/"
2128 if ( c == '/' )
2129 {
2130 int first = textLine->firstChar();
2131 // if the first char exists and is a '*', and the next non-space-char
2132 // is already the just typed '/', concatenate it to "*/".
2133 if ( first != -1
2134 && textLine->getChar( first ) == '*'
2135 && textLine->nextNonSpaceChar( first+1 ) == view->cursorColumnReal()-1 )
2136 doc->removeText( view->cursorLine(), first+1, view->cursorLine(), view->cursorColumnReal()-1);
2137 }
2138
2139 // anders: don't change the indent of doxygen lines here.
2140 return;
2141 }
2142
2143 processLine(begin);
2144}
2145
2146//END
2147
2148//BEGIN KateVarIndent
2149class KateVarIndentPrivate {
2150 public:
2151 TQRegExp reIndentAfter, reIndent, reUnindent;
2152 TQString triggers;
2153 uint couples;
2154 uchar coupleAttrib;
2155};
2156
2157KateVarIndent::KateVarIndent( KateDocument *doc )
2158: KateNormalIndent( doc )
2159{
2160 d = new KateVarIndentPrivate;
2161 d->reIndentAfter = TQRegExp( doc->variable( "var-indent-indent-after" ) );
2162 d->reIndent = TQRegExp( doc->variable( "var-indent-indent" ) );
2163 d->reUnindent = TQRegExp( doc->variable( "var-indent-unindent" ) );
2164 d->triggers = doc->variable( "var-indent-triggerchars" );
2165 d->coupleAttrib = 0;
2166
2167 slotVariableChanged( "var-indent-couple-attribute", doc->variable( "var-indent-couple-attribute" ) );
2168 slotVariableChanged( "var-indent-handle-couples", doc->variable( "var-indent-handle-couples" ) );
2169
2170 // update if a setting is changed
2171 connect( doc, TQ_SIGNAL(variableChanged( const TQString&, const TQString&) ),
2172 this, TQ_SLOT(slotVariableChanged( const TQString&, const TQString& )) );
2173}
2174
2175KateVarIndent::~KateVarIndent()
2176{
2177 delete d;
2178}
2179
2180void KateVarIndent::processNewline ( KateDocCursor &begin, bool /*needContinue*/ )
2181{
2182 // process the line left, as well as the one entered
2183 KateDocCursor left( begin.line()-1, 0, doc );
2184 processLine( left );
2185 processLine( begin );
2186}
2187
2188void KateVarIndent::processChar ( TQChar c )
2189{
2190 // process line if the c is in our list, and we are not in comment text
2191 if ( d->triggers.contains( c ) )
2192 {
2193 KateTextLine::Ptr ln = doc->plainKateTextLine( doc->activeView()->cursorLine() );
2194 if ( ln->attribute( doc->activeView()->cursorColumn()-1 ) == commentAttrib )
2195 return;
2196
2197 KateView *view = doc->activeView();
2198 KateDocCursor begin( view->cursorLine(), 0, doc );
2199 kdDebug(13030)<<"variable indenter: process char '"<<c<<", line "<<begin.line()<<endl;
2200 processLine( begin );
2201 }
2202}
2203
2204void KateVarIndent::processLine ( KateDocCursor &line )
2205{
2206 TQString indent; // store the indent string here
2207
2208 // find the first line with content that is not starting with comment text,
2209 // and take the position from that
2210 int ln = line.line();
2211 int pos = -1;
2212 KateTextLine::Ptr ktl = doc->plainKateTextLine( ln );
2213 if ( ! ktl ) return; // no line!?
2214
2215 // skip blank lines, except for the cursor line
2216 KateView *v = doc->activeView();
2217 if ( (ktl->firstChar() < 0) && (!v || (int)v->cursorLine() != ln ) )
2218 return;
2219
2220 int fc;
2221 if ( ln > 0 )
2222 do
2223 {
2224
2225 ktl = doc->plainKateTextLine( --ln );
2226 fc = ktl->firstChar();
2227 if ( ktl->attribute( fc ) != commentAttrib )
2228 pos = fc;
2229 }
2230 while ( (ln > 0) && (pos < 0) ); // search a not empty text line
2231
2232 if ( pos < 0 )
2233 pos = 0;
2234 else
2235 pos = ktl->cursorX( pos, tabWidth );
2236
2237 int adjustment = 0;
2238
2239 // try 'couples' for an opening on the above line first. since we only adjust by 1 unit,
2240 // we only need 1 match.
2241 if ( d->couples & Parens && coupleBalance( ln, '(', ')' ) > 0 )
2242 adjustment++;
2243 else if ( d->couples & Braces && coupleBalance( ln, '{', '}' ) > 0 )
2244 adjustment++;
2245 else if ( d->couples & Brackets && coupleBalance( ln, '[', ']' ) > 0 )
2246 adjustment++;
2247
2248 // Try 'couples' for a closing on this line first. since we only adjust by 1 unit,
2249 // we only need 1 match. For unindenting, we look for a closing character
2250 // *at the beginning of the line*
2251 // NOTE Assume that a closing brace with the configured attribute on the start
2252 // of the line is closing.
2253 // When acting on processChar, the character isn't highlighted. So I could
2254 // either not check, assuming that the first char *is* meant to close, or do a
2255 // match test if the attrib is 0. How ever, doing that is
2256 // a potentially huge job, if the match is several hundred lines away.
2257 // Currently, the check is done.
2258 {
2259 KateTextLine::Ptr tl = doc->plainKateTextLine( line.line() );
2260 int i = tl->firstChar();
2261 if ( i > -1 )
2262 {
2263 TQChar ch = tl->getChar( i );
2264 uchar at = tl->attribute( i );
2265 kdDebug(13030)<<"attrib is "<<at<<endl;
2266 if ( d->couples & Parens && ch == ')'
2267 && ( at == d->coupleAttrib
2268 || (! at && hasRelevantOpening( KateDocCursor( line.line(), i, doc ) ))
2269 )
2270 )
2271 adjustment--;
2272 else if ( d->couples & Braces && ch == '}'
2273 && ( at == d->coupleAttrib
2274 || (! at && hasRelevantOpening( KateDocCursor( line.line(), i, doc ) ))
2275 )
2276 )
2277 adjustment--;
2278 else if ( d->couples & Brackets && ch == ']'
2279 && ( at == d->coupleAttrib
2280 || (! at && hasRelevantOpening( KateDocCursor( line.line(), i, doc ) ))
2281 )
2282 )
2283 adjustment--;
2284 }
2285 }
2286#define ISCOMMENTATTR(attr) (attr==commentAttrib||attr==doxyCommentAttrib)
2287#define ISCOMMENT (ISCOMMENTATTR(ktl->attribute(ktl->firstChar()))||ISCOMMENTATTR(ktl->attribute(matchpos)))
2288 // check if we should indent, unless the line starts with comment text,
2289 // or the match is in comment text
2290 kdDebug(13030)<<"variable indenter: starting indent: "<<pos<<endl;
2291 // check if the above line indicates that we shuld add indentation
2292 int matchpos = 0;
2293 if ( ktl && ! d->reIndentAfter.isEmpty()
2294 && (matchpos = d->reIndentAfter.search( doc->textLine( ln ) )) > -1
2295 && ! ISCOMMENT )
2296 adjustment++;
2297
2298 // else, check if this line should indent unless ...
2299 ktl = doc->plainKateTextLine( line.line() );
2300 if ( ! d->reIndent.isEmpty()
2301 && (matchpos = d->reIndent.search( doc->textLine( line.line() ) )) > -1
2302 && ! ISCOMMENT )
2303 adjustment++;
2304
2305 // else, check if the current line indicates if we should remove indentation unless ...
2306 if ( ! d->reUnindent.isEmpty()
2307 && (matchpos = d->reUnindent.search( doc->textLine( line.line() ) )) > -1
2308 && ! ISCOMMENT )
2309 adjustment--;
2310
2311 kdDebug(13030)<<"variable indenter: adjusting by "<<adjustment<<" units"<<endl;
2312
2313 if ( adjustment > 0 )
2314 pos += indentWidth;
2315 else if ( adjustment < 0 )
2316 pos -= indentWidth;
2317
2318 ln = line.line();
2319 fc = doc->plainKateTextLine( ln )->firstChar();
2320
2321 // dont change if there is no change.
2322 // ### should I actually compare the strings?
2323 // FIXME for some odd reason, the document gets marked as changed
2324 // even if we don't change it !?
2325 if ( fc == pos )
2326 return;
2327
2328 if ( fc > 0 )
2329 doc->removeText (ln, 0, ln, fc );
2330
2331 if ( pos > 0 )
2332 indent = tabString( pos );
2333
2334 if ( pos > 0 )
2335 doc->insertText (ln, 0, indent);
2336
2337 // try to restore cursor ?
2338 line.setCol( pos );
2339}
2340
2341void KateVarIndent::processSection (const KateDocCursor &begin, const KateDocCursor &end)
2342{
2343 KateDocCursor cur = begin;
2344 while (cur.line() <= end.line())
2345 {
2346 processLine (cur);
2347 if (!cur.gotoNextLine())
2348 break;
2349 }
2350}
2351
2352void KateVarIndent::slotVariableChanged( const TQString &var, const TQString &val )
2353{
2354 if ( ! var.startsWith("var-indent") )
2355 return;
2356
2357 if ( var == "var-indent-indent-after" )
2358 d->reIndentAfter.setPattern( val );
2359 else if ( var == "var-indent-indent" )
2360 d->reIndent.setPattern( val );
2361 else if ( var == "var-indent-unindent" )
2362 d->reUnindent.setPattern( val );
2363 else if ( var == "var-indent-triggerchars" )
2364 d->triggers = val;
2365 else if ( var == "var-indent-handle-couples" )
2366 {
2367 d->couples = 0;
2368 TQStringList l = TQStringList::split( " ", val );
2369 if ( l.contains("parens") ) d->couples |= Parens;
2370 if ( l.contains("braces") ) d->couples |= Braces;
2371 if ( l.contains("brackets") ) d->couples |= Brackets;
2372 }
2373 else if ( var == "var-indent-couple-attribute" )
2374 {
2375 //read a named attribute of the config.
2376 KateHlItemDataList items;
2377 doc->highlight()->getKateHlItemDataListCopy (0, items);
2378
2379 for (uint i=0; i<items.count(); i++)
2380 {
2381 if ( items.at(i)->name.section( ':', 1 ) == val )
2382 {
2383 d->coupleAttrib = i;
2384 break;
2385 }
2386 }
2387 }
2388}
2389
2390int KateVarIndent::coupleBalance ( int line, const TQChar &open, const TQChar &close ) const
2391{
2392 int r = 0;
2393
2394 KateTextLine::Ptr ln = doc->plainKateTextLine( line );
2395 if ( ! ln || ! ln->length() ) return 0;
2396
2397 for ( uint z=0; z < ln->length(); z++ )
2398 {
2399 TQChar c = ln->getChar( z );
2400 if ( ln->attribute(z) == d->coupleAttrib )
2401 {
2402 kdDebug(13030)<<z<<", "<<c<<endl;
2403 if (c == open)
2404 r++;
2405 else if (c == close)
2406 r--;
2407 }
2408 }
2409 return r;
2410}
2411
2412bool KateVarIndent::hasRelevantOpening( const KateDocCursor &end ) const
2413{
2414 KateDocCursor cur = end;
2415 int count = 1;
2416
2417 TQChar close = cur.currentChar();
2418 TQChar opener;
2419 if ( close == '}' ) opener = '{';
2420 else if ( close = ')' ) opener = '(';
2421 else if (close = ']' ) opener = '[';
2422 else return false;
2423
2424 //Move backwards 1 by 1 and find the opening partner
2425 while (cur.moveBackward(1))
2426 {
2427 if (cur.currentAttrib() == d->coupleAttrib)
2428 {
2429 TQChar ch = cur.currentChar();
2430 if (ch == opener)
2431 count--;
2432 else if (ch == close)
2433 count++;
2434
2435 if (count == 0)
2436 return true;
2437 }
2438 }
2439
2440 return false;
2441}
2442
2443
2444//END KateVarIndent
2445
2446//BEGIN KateScriptIndent
2447KateScriptIndent::KateScriptIndent( KateDocument *doc )
2448 : KateNormalIndent( doc )
2449{
2450 m_script=KateFactory::self()->indentScript ("script-indent-c1-test");
2451}
2452
2453KateScriptIndent::~KateScriptIndent()
2454{
2455}
2456
2457void KateScriptIndent::processNewline( KateDocCursor &begin, bool needContinue )
2458{
2459 kdDebug(13030) << "processNewline" << endl;
2460 KateView *view = doc->activeView();
2461
2462 if (view)
2463 {
2464 TQString errorMsg;
2465
2466 TQTime t;
2467 t.start();
2468 kdDebug(13030)<<"calling m_script.processChar"<<endl;
2469 if( !m_script.processNewline( view, begin, needContinue , errorMsg ) )
2470 {
2471 kdDebug(13030) << "Error in script-indent: " << errorMsg << endl;
2472 }
2473 kdDebug(13030) << "ScriptIndent::TIME in ms: " << t.elapsed() << endl;
2474 }
2475}
2476
2477void KateScriptIndent::processChar( TQChar c )
2478{
2479 kdDebug(13030) << "processChar" << endl;
2480 KateView *view = doc->activeView();
2481
2482 if (view)
2483 {
2484 TQString errorMsg;
2485
2486 TQTime t;
2487 t.start();
2488 kdDebug(13030)<<"calling m_script.processChar"<<endl;
2489 if( !m_script.processChar( view, c , errorMsg ) )
2490 {
2491 kdDebug(13030) << "Error in script-indent: " << errorMsg << endl;
2492 }
2493 kdDebug(13030) << "ScriptIndent::TIME in ms: " << t.elapsed() << endl;
2494 }
2495}
2496
2497void KateScriptIndent::processLine (KateDocCursor &line)
2498{
2499 kdDebug(13030) << "processLine" << endl;
2500 KateView *view = doc->activeView();
2501
2502 if (view)
2503 {
2504 TQString errorMsg;
2505
2506 TQTime t;
2507 t.start();
2508 kdDebug(13030)<<"calling m_script.processLine"<<endl;
2509 if( !m_script.processLine( view, line , errorMsg ) )
2510 {
2511 kdDebug(13030) << "Error in script-indent: " << errorMsg << endl;
2512 }
2513 kdDebug(13030) << "ScriptIndent::TIME in ms: " << t.elapsed() << endl;
2514 }
2515}
2516//END KateScriptIndent
2517
2518//BEGIN ScriptIndentConfigPage, THIS IS ONLY A TEST! :)
2519#include <tqlabel.h>
2520ScriptIndentConfigPage::ScriptIndentConfigPage ( TQWidget *parent, const char *name )
2521 : IndenterConfigPage(parent, name)
2522{
2523 TQLabel* hello = new TQLabel("Hello world! Dummy for testing purpose.", this);
2524 hello->show();
2525}
2526
2527ScriptIndentConfigPage::~ScriptIndentConfigPage ()
2528{
2529}
2530
2531void ScriptIndentConfigPage::apply ()
2532{
2533 kdDebug(13030) << "ScriptIndentConfigPagE::apply() was called, save config options now!" << endl;
2534}
2535//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:2341
KateVarIndent::processChar
virtual void processChar(TQChar c)
Called every time a character is inserted into the document.
Definition: kateautoindent.cpp:2188
KateVarIndent::processLine
virtual void processLine(KateDocCursor &line)
Aligns/indents the given line to the proper indent position.
Definition: kateautoindent.cpp:2204
KateVarIndent::processNewline
virtual void processNewline(KateDocCursor &cur, bool needContinue)
Called every time a newline character is inserted in the document.
Definition: kateautoindent.cpp:2180
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.