• 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 
38 KateAutoIndent *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 
58 TQStringList 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 
74 TQString 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 
94 TQString 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 
114 uint 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 
134 bool KateAutoIndent::hasConfigPage (uint mode)
135 {
136 // if ( mode == KateDocumentConfig::imScriptIndent )
137 // return true;
138 
139  return false;
140 }
141 
142 IndenterConfigPage* 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 
150 KateAutoIndent::KateAutoIndent (KateDocument *_doc)
151 : TQObject(), doc(_doc)
152 {
153 }
154 KateAutoIndent::~KateAutoIndent ()
155 {
156 }
157 
158 //END KateAutoIndent
159 
160 //BEGIN KateViewIndentAction
161 KateViewIndentationAction::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 
167 void 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 
178 void KateViewIndentationAction::setMode (int mode)
179 {
180  doc->config()->setIndentationMode((uint)mode);
181 }
182 //END KateViewIndentationAction
183 
184 //BEGIN KateNormalIndent
185 
186 KateNormalIndent::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 
193 KateNormalIndent::~KateNormalIndent ()
194 {
195 }
196 
197 void 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 
282 bool 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 
328 bool 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 
370 uint 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 
378 TQString 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 
399 void 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 
421 KateCSmartIndent::KateCSmartIndent (KateDocument *doc)
422 : KateNormalIndent (doc),
423  allowSemi (false),
424  processingBlock (false)
425 {
426  kdDebug(13030)<<"CREATING KATECSMART INTDETER"<<endl;
427 }
428 
429 KateCSmartIndent::~KateCSmartIndent ()
430 {
431 
432 }
433 
434 void 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 
547 void 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 
567 bool 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 
635 void 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 
676 static 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 
696 static 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 
717 void 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 
785 uint 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 
999 uint 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 
1097 uint 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 
1125 bool 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 
1145 uint 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 
1170 uint 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 
1195 TQRegExp KatePythonIndent::endWithColon = TQRegExp( "^[^#]*:\\s*(#.*)?$" );
1196 TQRegExp KatePythonIndent::stopStmt = TQRegExp( "^\\s*(break|continue|raise|return|pass)\\b.*" );
1197 TQRegExp KatePythonIndent::blockBegin = TQRegExp( "^\\s*(class|def|if|elif|else|for|while|try)\\b.*" );
1198 
1199 KatePythonIndent::KatePythonIndent (KateDocument *doc)
1200 : KateNormalIndent (doc)
1201 {
1202 }
1203 KatePythonIndent::~KatePythonIndent ()
1204 {
1205 }
1206 
1207 void 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 
1243 int 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 
1293 void 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 
1318 The XML indenter simply inherits the indentation of the previous line,
1319 with the first line starting at 0 (of course!). For each element that
1320 is opened on the previous line, the indentation is increased by one
1321 level; for each element that is closed, it is decreased by one.
1322 
1323 We also have a special case of opening an element on one line and then
1324 entering attributes on the following lines, in which case we would like
1325 to see the following layout:
1326 <elem attr="..."
1327  blah="..." />
1328 
1329 <x><a href="..."
1330  title="..." />
1331 </x>
1332 
1333 This is accomplished by checking for lines that contain an unclosed open
1334 tag.
1335 
1336 */
1337 
1338 const TQRegExp KateXmlIndent::startsWithCloseTag("^[ \t]*</");
1339 const TQRegExp KateXmlIndent::unclosedDoctype("<!DOCTYPE[^>]*$");
1340 
1341 KateXmlIndent::KateXmlIndent (KateDocument *doc)
1342 : KateNormalIndent (doc)
1343 {
1344 }
1345 
1346 KateXmlIndent::~KateXmlIndent ()
1347 {
1348 }
1349 
1350 void KateXmlIndent::processNewline (KateDocCursor &begin, bool /*newline*/)
1351 {
1352  begin.setCol(processLine(begin.line()));
1353 }
1354 
1355 void 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 
1368 void KateXmlIndent::processLine (KateDocCursor &line)
1369 {
1370  processLine (line.line());
1371 }
1372 
1373 void 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 
1384 void 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 
1481 uint 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 
1519 KateCSAndSIndent::KateCSAndSIndent (KateDocument *doc)
1520 : KateNormalIndent (doc)
1521 {
1522 }
1523 
1524 void KateCSAndSIndent::updateIndentString()
1525 {
1526  if( useSpaces )
1527  indentString.fill( ' ', indentWidth );
1528  else
1529  indentString = '\t';
1530 }
1531 
1532 KateCSAndSIndent::~KateCSAndSIndent ()
1533 {
1534 }
1535 
1536 void 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 
1563 void 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 
1580 static 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 
1600 TQString 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 
1620 bool 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 
1672 void 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 
1694 bool 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 
1754 template<class T> T min(T a, T b) { return (a < b) ? a : b; }
1755 
1756 int 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 
1775 bool 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?
1820 bool 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 
1894 TQString KateCSAndSIndent::continuationIndent( const KateDocCursor &begin )
1895 {
1896  if( !inStatement( begin ) )
1897  return TQString::null;
1898  return indentString;
1899 }
1900 
1904 TQString 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 
1991 TQString 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 
2033 TQString 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 
2060 TQString 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 
2111 void 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
2155 class KateVarIndentPrivate {
2156  public:
2157  TQRegExp reIndentAfter, reIndent, reUnindent;
2158  TQString triggers;
2159  uint couples;
2160  uchar coupleAttrib;
2161 };
2162 
2163 KateVarIndent::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 
2181 KateVarIndent::~KateVarIndent()
2182 {
2183  delete d;
2184 }
2185 
2186 void 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 
2194 void 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 
2210 void 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 
2347 void 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 
2358 void 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 
2396 int 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 
2418 bool 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
2453 KateScriptIndent::KateScriptIndent( KateDocument *doc )
2454  : KateNormalIndent( doc )
2455 {
2456  m_script=KateFactory::self()->indentScript ("script-indent-c1-test");
2457 }
2458 
2459 KateScriptIndent::~KateScriptIndent()
2460 {
2461 }
2462 
2463 void 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 
2483 void 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 
2503 void 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>
2526 ScriptIndentConfigPage::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 
2533 ScriptIndentConfigPage::~ScriptIndentConfigPage ()
2534 {
2535 }
2536 
2537 void ScriptIndentConfigPage::apply ()
2538 {
2539  kdDebug(13030) << "ScriptIndentConfigPagE::apply() was called, save config options now!" << endl;
2540 }
2541 //END ScriptIndentConfigPage
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
Provides Auto-Indent functionality for katepart.
Definition: kateautoindent.h:217
KateNormalIndent::updateConfig
virtual void updateConfig()
Update indenter's configuration (indention width, attributes etc.)
Definition: kateautoindent.cpp:197
KateDocCursor
Cursor class with a pointer to its document.
Definition: katecursor.h:92
TDEStdAccel::next
const TDEShortcut & next()
KateNormalIndent::measureIndent
uint measureIndent(KateDocCursor &cur) const
Measures the indention of the current textline marked by cur.
Definition: kateautoindent.cpp:370
KateAutoIndent::listModes
static TQStringList listModes()
List all possible modes by name.
Definition: kateautoindent.cpp:58
KateVarIndent::processNewline
virtual void processNewline(KateDocCursor &cur, bool needContinue)
Called every time a newline character is inserted in the document.
Definition: kateautoindent.cpp:2186
KateVarIndent
This indenter uses document variables to determine when to add/remove indents.
Definition: kateautoindent.h:492
IndenterConfigPage
This widget will be embedded into a modal dialog when clicking the "Configure..." button in the inden...
Definition: kateautoindent.h:44
KateAutoIndent::hasConfigPage
static bool hasConfigPage(uint mode)
Config page support.
Definition: kateautoindent.cpp:134
KateVarIndent::processLine
virtual void processLine(KateDocCursor &line)
Aligns/indents the given line to the proper indent position.
Definition: kateautoindent.cpp:2210
kdDebug
kdbgstream kdDebug(int area=0)
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
TDEActionMenu
KateNormalIndent::tabString
TQString tabString(uint length) const
Produces a string with the proper indentation characters for its length.
Definition: kateautoindent.cpp:378
KateNormalIndent::tabWidth
uint tabWidth
The number of characters simulated for a tab.
Definition: kateautoindent.h:323
KateNormalIndent::useSpaces
bool useSpaces
Should we use spaces or tabs to indent.
Definition: kateautoindent.h:343
KateAutoIndent::modeNumber
virtual uint modeNumber() const
Mode index of this mode.
Definition: kateautoindent.h:185
KateAutoIndent
Provides Auto-Indent functionality for katepart.
Definition: kateautoindent.h:70
tdelocale.h
kdWarning
kdbgstream kdWarning(int area=0)
KateNormalIndent::~KateNormalIndent
virtual ~KateNormalIndent()
Virtual Destructor for the baseclass.
Definition: kateautoindent.cpp:193
KateAutoIndent::createIndenter
static KateAutoIndent * createIndenter(KateDocument *doc, uint mode)
Static methods to create and list indention modes.
Definition: kateautoindent.cpp:38
TDEStdAccel::replace
const TDEShortcut & replace()
KateNormalIndent::KateNormalIndent
KateNormalIndent(KateDocument *doc)
Constructor.
Definition: kateautoindent.cpp:186
KateAutoIndent::modeName
static TQString modeName(uint mode)
Return the mode name given the mode.
Definition: kateautoindent.cpp:74
KateAutoIndent::configPage
static IndenterConfigPage * configPage(TQWidget *parent, uint mode)
Support for a config page.
Definition: kateautoindent.cpp:142
KateNormalIndent::keepProfile
bool keepProfile
Always try to honor the leading whitespace of lines already in the file.
Definition: kateautoindent.h:345
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
KateVarIndent::processChar
virtual void processChar(TQChar c)
Called every time a character is inserted into the document.
Definition: kateautoindent.cpp:2194
KateNormalIndent::mixedIndent
bool mixedIndent
Optimize indent by mixing spaces and tabs, ala emacs.
Definition: kateautoindent.h:344
KateNormalIndent::processNewline
virtual void processNewline(KateDocCursor &cur, bool needContinue)
Called every time a newline character is inserted in the document.
Definition: kateautoindent.cpp:399
endl
kndbgstream & endl(kndbgstream &s)
KateAutoIndent::modeDescription
static TQString modeDescription(uint mode)
Return the mode description.
Definition: kateautoindent.cpp:94
TDESharedPtr
KateAutoIndent::KateAutoIndent
KateAutoIndent(KateDocument *doc)
Constructor.
Definition: kateautoindent.cpp:150
KateAutoIndent::~KateAutoIndent
virtual ~KateAutoIndent()
Virtual Destructor for the baseclass.
Definition: kateautoindent.cpp:154
KateNormalIndent::indentWidth
uint indentWidth
The number of characters used when tabs are replaced by spaces.
Definition: kateautoindent.h:324

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
  •     tdecore
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  • tdeioslave
  •   http
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for kate by doxygen 1.8.8
This website is maintained by Timothy Pearson.