• 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(),TQ_SIGNAL(aboutToShow()),this,TQ_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, TQ_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, TQ_SIGNAL(hlChanged()), this, TQ_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, "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 
545 void 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 
565 bool 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 
633 void 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 
674 static 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 
694 static 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 
715 void 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 
783 uint 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 
993 uint 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 
1091 uint 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 
1119 bool 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 
1139 uint 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 
1164 uint 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 
1189 TQRegExp KatePythonIndent::endWithColon = TQRegExp( "^[^#]*:\\s*(#.*)?$" );
1190 TQRegExp KatePythonIndent::stopStmt = TQRegExp( "^\\s*(break|continue|raise|return|pass)\\b.*" );
1191 TQRegExp KatePythonIndent::blockBegin = TQRegExp( "^\\s*(class|def|if|elif|else|for|while|try)\\b.*" );
1192 
1193 KatePythonIndent::KatePythonIndent (KateDocument *doc)
1194 : KateNormalIndent (doc)
1195 {
1196 }
1197 KatePythonIndent::~KatePythonIndent ()
1198 {
1199 }
1200 
1201 void 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 
1237 int 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 
1287 void 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 
1312 The XML indenter simply inherits the indentation of the previous line,
1313 with the first line starting at 0 (of course!). For each element that
1314 is opened on the previous line, the indentation is increased by one
1315 level; for each element that is closed, it is decreased by one.
1316 
1317 We also have a special case of opening an element on one line and then
1318 entering attributes on the following lines, in which case we would like
1319 to see the following layout:
1320 <elem attr="..."
1321  blah="..." />
1322 
1323 <x><a href="..."
1324  title="..." />
1325 </x>
1326 
1327 This is accomplished by checking for lines that contain an unclosed open
1328 tag.
1329 
1330 */
1331 
1332 const TQRegExp KateXmlIndent::startsWithCloseTag("^[ \t]*</");
1333 const TQRegExp KateXmlIndent::unclosedDoctype("<!DOCTYPE[^>]*$");
1334 
1335 KateXmlIndent::KateXmlIndent (KateDocument *doc)
1336 : KateNormalIndent (doc)
1337 {
1338 }
1339 
1340 KateXmlIndent::~KateXmlIndent ()
1341 {
1342 }
1343 
1344 void KateXmlIndent::processNewline (KateDocCursor &begin, bool /*newline*/)
1345 {
1346  begin.setCol(processLine(begin.line()));
1347 }
1348 
1349 void 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 
1362 void KateXmlIndent::processLine (KateDocCursor &line)
1363 {
1364  processLine (line.line());
1365 }
1366 
1367 void 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 
1378 void 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 
1475 uint 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 
1513 KateCSAndSIndent::KateCSAndSIndent (KateDocument *doc)
1514 : KateNormalIndent (doc)
1515 {
1516 }
1517 
1518 void KateCSAndSIndent::updateIndentString()
1519 {
1520  if( useSpaces )
1521  indentString.fill( ' ', indentWidth );
1522  else
1523  indentString = '\t';
1524 }
1525 
1526 KateCSAndSIndent::~KateCSAndSIndent ()
1527 {
1528 }
1529 
1530 void 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 
1557 void 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 
1574 static 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 
1594 TQString 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 
1614 bool 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 
1666 void 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 
1688 bool 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 
1748 template<class T> T min(T a, T b) { return (a < b) ? a : b; }
1749 
1750 int 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 
1769 bool 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?
1814 bool 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 
1888 TQString KateCSAndSIndent::continuationIndent( const KateDocCursor &begin )
1889 {
1890  if( !inStatement( begin ) )
1891  return TQString::null;
1892  return indentString;
1893 }
1894 
1898 TQString 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 
1985 TQString 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 
2027 TQString 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 
2054 TQString 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 
2105 void 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
2149 class KateVarIndentPrivate {
2150  public:
2151  TQRegExp reIndentAfter, reIndent, reUnindent;
2152  TQString triggers;
2153  uint couples;
2154  uchar coupleAttrib;
2155 };
2156 
2157 KateVarIndent::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 
2175 KateVarIndent::~KateVarIndent()
2176 {
2177  delete d;
2178 }
2179 
2180 void 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 
2188 void 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 
2204 void 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 
2341 void 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 
2352 void 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 
2390 int 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 
2412 bool 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
2447 KateScriptIndent::KateScriptIndent( KateDocument *doc )
2448  : KateNormalIndent( doc )
2449 {
2450  m_script=KateFactory::self()->indentScript ("script-indent-c1-test");
2451 }
2452 
2453 KateScriptIndent::~KateScriptIndent()
2454 {
2455 }
2456 
2457 void 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 
2477 void 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 
2497 void 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>
2520 ScriptIndentConfigPage::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 
2527 ScriptIndentConfigPage::~ScriptIndentConfigPage ()
2528 {
2529 }
2530 
2531 void 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
endl
kndbgstream & endl(kndbgstream &s)
kdWarning
kdbgstream kdWarning(int area=0)
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.1
This website is maintained by Timothy Pearson.