2
* name: C++/boost Style
4
* author: Alex Turbov <i.zaufi@gmail.com>
8
* indent-languages: C++, C++/Qt4, ISO C++
9
* required-syntax-style: C++
11
* This file is part of the Kate Project.
13
* This library is free software; you can redistribute it and/or
14
* modify it under the terms of the GNU Library General Public
15
* License as published by the Free Software Foundation; either
16
* version 2 of the License, or (at your option) any later version.
18
* This library is distributed in the hope that it will be useful,
19
* but WITHOUT ANY WARRANTY; without even the implied warranty of
20
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21
* Library General Public License for more details.
23
* You should have received a copy of the GNU Library General Public License
24
* along with this library; see the file COPYING.LIB. If not, write to
25
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26
* Boston, MA 02110-1301, USA.
30
* \warning This indenter designed to be used with my C++ style! It consists
31
* of mix of boost and STL styles + some my, unfortunately (still)
32
* undocumented additions I've found useful after ~15 years of C++ coding.
33
* So \b LOT of things here are \b HARDCODED and I \b DON'T care about other
36
* Ok, you've been warned :-)
38
* More info available here: http://zaufi.github.io/programming/2013/11/29/kate-cppstyle-indenter/
40
* Some settings it assumes being in effect:
42
* - space-indent true;
43
* - auto-brackets true; <-- TODO REALLY?
44
* - replace-tabs true;
45
* - replace-tabs-save true;
47
* \todo Better to check (assert) some of that modelines...
50
// required katepart js libraries
52
require ("string.js");
55
// specifies the characters which should trigger indent, beside the default '\n'
56
// ':' is for `case'/`default' and class access specifiers: public, protected, private
57
// '/' is for single line comments
58
// ',' for parameter list
59
// '<' and '>' is for templates
60
// '#' is for preprocessor directives
61
// ')' is for align dangling close bracket
62
// ';' is for align `for' parts
63
// ' ' is to add a '()' after `if', `while', `for', ...
65
triggerCharacters = "{}()[]<>/:;,#\\?!|&/%.@ '\"=*^";
67
var debugMode = false;
69
//BEGIN global variables and functions
71
var gSameLineCommentStartAt = 60; ///< Position for same-line-comments (inline comments)
78
//END global variables and functions
81
* Try to (re)align (to 60th position) inline comment if present
82
* \return \c true if comment line was moved above
84
function alignInlineComment(line)
86
// Check is there any comment on the current line
87
var sc = splitByComment(line);
88
// Did we found smth and if so, make sure it is not a string or comment...
89
if (sc.hasComment && !isStringOrComment(line, sc.before.length - 1))
91
var rbefore = sc.before.rtrim();
92
var cursor = view.cursorPosition();
93
/// \attention Kate has a BUG: even if everything is Ok and no realign
94
/// required, document gets modified anyway! So condition below
95
/// designed to prevent document modification w/o real actions won't
96
/// help anyway :-( Need to fix Kate before!
97
if (rbefore.length < gSameLineCommentStartAt && sc.before.length != gSameLineCommentStartAt)
99
// Ok, test on the line is shorter than needed.
100
// But what about current padding?
101
if (sc.before.length < gSameLineCommentStartAt)
102
// Need to add some padding
106
, String().fill(' ', gSameLineCommentStartAt - sc.before.length)
109
// Need to remove a redundant padding
110
document.removeText(line, gSameLineCommentStartAt, line, sc.before.length);
111
// Keep cursor at the place we've found it before
112
view.setCursorPosition(cursor);
114
else if (gSameLineCommentStartAt < rbefore.length)
116
// Move inline comment before the current line
117
var startPos = document.firstColumn(line);
118
var currentLineText = String().fill(' ', startPos) + "//" + sc.after.rtrim() + "\n";
119
document.removeText(line, rbefore.length, line, document.lineLength(line));
120
document.insertText(line, 0, currentLineText);
121
// Keep cursor at the place we've found it before
122
view.setCursorPosition(new Cursor(line + 1, cursor.column));
130
function tryIndentRelativePrevNonCommentLine(line)
132
var current_line = line - 1;
133
while (0 <= current_line && isStringOrComment(current_line, document.firstColumn(current_line)))
135
if (current_line == -1)
138
var prevLineFirstChar = document.firstChar(current_line);
139
var needHalfUnindent = !(
140
prevLineFirstChar == ','
141
|| prevLineFirstChar == ':'
142
|| prevLineFirstChar == '?'
143
|| prevLineFirstChar == '<'
144
|| prevLineFirstChar == '>'
145
|| prevLineFirstChar == '&'
147
return document.firstColumn(current_line) - (needHalfUnindent ? 2 : 0);
151
* Try to keep same-line comment.
152
* I.e. if \c ENTER was hit on a line w/ inline comment and before it,
153
* try to keep it on a previous line...
155
function tryToKeepInlineComment(line)
157
// Make sure that there is some text still present on a prev line
158
// i.e. it was just splitted and same-line-comment must be moved back to it
159
if (document.line(line - 1).trim().length == 0)
162
// Check is there any comment on the current (non empty) line
163
var sc = splitByComment(line);
164
dbg("sc.hasComment="+sc.hasComment);
165
dbg("sc.before.rtrim().length="+sc.before.rtrim().length);
168
// Ok, here is few cases possible when ENTER pressed in different positions
169
// | |smth|was here; | |// comment
171
// If sc.before has some text, it means that cursor was in the middle of some
172
// non-commented text, and part of it left on a prev line, so we have to move
173
// the comment back to that line...
174
if (sc.before.trim().length > 0) // Is there some text before comment?
176
var lastPos = document.lastColumn(line - 1); // Get last position of non space char @ prev line
177
// Put the comment text to the prev line w/ padding
181
, String().fill(' ', gSameLineCommentStartAt - lastPos - 1)
185
// Remove it from current line starting from current position
186
// 'till the line end
187
document.removeText(line, sc.before.rtrim().length, line, document.lineLength(line));
191
// No text before comment. Need to remove possible spaces from prev line...
192
var prevLine = line - 1;
195
, document.lastColumn(prevLine) + 1
197
, document.lineLength(prevLine)
204
* Return a current preprocessor indentation level
205
* \note <em>preprocessor indentation</em> means how deep the current line
206
* inside of \c #if directives.
207
* \warning Negative result means that smth wrong w/ a source code
209
function getPreprocessorLevelAt(line)
211
// Just follow towards start and count #if/#endif directives
212
var currentLine = line;
214
while (currentLine >= 0)
217
var currentLineText = document.line(currentLine);
218
if (currentLineText.search(/^\s*#\s*(if|ifdef|ifndef)\s+.*$/) != -1)
220
else if (currentLineText.search(/^\s*#\s*endif.*$/) != -1)
227
* Check if \c ENTER was hit between ()/{}/[]/<>
228
* \todo Match closing brace forward, put content between
229
* braces on a separate line and align a closing brace.
231
function tryBraceSplit_ch(line)
234
// Get last char from previous line (opener) and a first from the current (closer)
235
var firstCharPos = document.lastColumn(line - 1);
236
var firstChar = document.charAt(line - 1, firstCharPos);
237
var lastCharPos = document.firstColumn(line);
238
var lastChar = document.charAt(line, lastCharPos);
240
var isCurveBracketsMatched = (firstChar == '{' && lastChar == '}');
241
var isBracketsMatched = isCurveBracketsMatched
242
|| (firstChar == '[' && lastChar == ']')
243
|| (firstChar == '(' && lastChar == ')')
244
|| (firstChar == '<' && lastChar == '>')
246
if (isBracketsMatched)
248
var currentIndentation = document.firstVirtualColumn(line - 1);
249
result = currentIndentation + gIndentWidth;
250
document.insertText(line, document.firstColumn(line), "\n");
251
document.indent(new Range(line + 1, 0, line + 1, 1), currentIndentation / gIndentWidth);
252
// Add half-tab (2 spaces) if matched not a curve bracket or
253
// open character isn't the only one on the line
254
var isOpenCharTheOnlyOnLine = (document.firstColumn(line - 1) == firstCharPos);
255
if (!(isCurveBracketsMatched || isOpenCharTheOnlyOnLine))
256
document.insertText(line + 1, document.firstColumn(line + 1), " ");
257
view.setCursorPosition(line, result);
261
dbg("tryBraceSplit_ch result="+result);
262
tryToKeepInlineComment(line);
268
* Even if counterpart brace not found (\sa \c tryBraceSplit_ch), align the current line
269
* to one level deeper if last char on a previous line is one of open braces.
277
* array[|idx] = blah;
280
function tryToAlignAfterOpenBrace_ch(line)
283
var pos = document.lastColumn(line - 1);
284
var ch = document.charAt(line - 1, pos);
286
if (ch == '(' || ch == '[')
288
result = document.firstColumn(line - 1) + gIndentWidth;
292
if (document.startsWith(line - 1, "namespace", true))
295
result = document.firstColumn(line - 1) + gIndentWidth;
299
// Does it looks like 'operator<<'?
300
if (document.charAt(line - 1, pos - 1) != '<')
301
result = document.firstColumn(line - 1) + gIndentWidth;
303
result = document.firstColumn(line - 1) + (gIndentWidth / 2);
308
tryToKeepInlineComment(line);
309
dbg("tryToAlignOpenBrace_ch result="+result);
314
function tryToAlignBeforeCloseBrace_ch(line)
317
var pos = document.firstColumn(line);
318
var ch = document.charAt(line, pos);
320
if (ch == '}' || ch == ')' || ch == ']')
322
var openBracePos = document.anchor(line, pos, ch);
323
dbg("Found open brace @", openBracePos);
324
if (openBracePos.isValid())
325
result = document.firstColumn(openBracePos.line) + (ch == '}' ? 0 : (gIndentWidth / 2));
334
tryToKeepInlineComment(line);
335
dbg("tryToAlignBeforeCloseBrace_ch result="+result);
340
function tryToAlignBeforeComma_ch(line)
343
var pos = document.firstColumn(line);
344
var ch = document.charAt(line, pos);
346
if (line > 0 && (ch == ',' || ch == ';'))
348
var openBracePos = document.anchor(line, pos, '(');
349
if (!openBracePos.isValid())
350
openBracePos = document.anchor(line, pos, '[');
352
if (openBracePos.isValid())
353
result = document.firstColumn(openBracePos.line) + 2;
358
tryToKeepInlineComment(line);
359
dbg("tryToAlignBeforeComma_ch result="+result);
364
/// Check if a multiline comment introduced on a previous line
365
function tryMultilineCommentStart_ch(line)
369
// Check if multiline comment was started on the line
370
// and ENTER wan't pressed right after a /*C-style comment*/
371
if (document.startsWith(line - 1, "/*", true) && !document.endsWith(line - 1, "*/", true))
373
var filler = String().fill(' ', document.firstVirtualColumn(line - 1) + 1);
374
var padding = filler + "* ";
375
// If next line (if present) doesn't looks like a continue of the current comment,
376
// then append a comment closer also...
377
if ((line + 1) < document.lines())
379
// Maybe user wants to extend a multiline C-style/Doxygen comment
380
// by pressing ENTER at start of it?
381
if (!document.startsWith(line + 1, "*", true))
383
// ... doesn't looks like a multiline comment
384
padding += "\n" + filler;
385
// Maybe user just splits a C-style comment?
386
if (!document.startsWith(line, "*/", true))
387
padding += document.endsWith(line, "*/", true) ? "* " : "*/";
389
document.removeText(line, 0, line, document.firstColumn(line))
390
} // else, no need to append a closing */
392
else // There is no a next line...
394
padding += "\n" + filler;
395
if (!document.startsWith(line, "*/", true))
396
padding += document.endsWith(line, "*/", true) ? "* " : "*/";
398
document.removeText(line, 0, line, document.firstColumn(line))
400
document.insertText(line, 0, padding);
401
view.setCursorPosition(line, filler.length + 2);
406
dbg("tryMultilineCommentStart_ch result="+result);
411
/// Check if \c ENTER was hit inside or at last line of a multiline comment
412
function tryMultilineCommentCont_ch(line)
415
// Check if multiline comment continued on the line:
416
// 0) it starts w/ a start
417
// 1) and followed by a space (i.e. it doesn't looks like a dereference) or nothing
418
var firstCharPos = document.firstColumn(line - 1);
419
var prevLineFirstChar = document.charAt(line - 1, firstCharPos);
420
var prevLineSecondChar = document.charAt(line - 1, firstCharPos + 1);
421
if (prevLineFirstChar == '*' && (prevLineSecondChar == ' ' || prevLineSecondChar == -1))
423
if (document.charAt(line - 1, firstCharPos + 1) == '/')
424
// ENTER pressed after multiline comment: unindent 1 space!
425
result = firstCharPos - 1;
428
// Ok, ENTER pressed inside of the multiline comment:
429
// just append one more line...
430
var filler = String().fill(' ', document.firstColumn(line - 1));
431
// Try to continue a C-style comment
432
document.insertText(line, 0, filler + "* ");
433
result = filler.length;
438
dbg("tryMultilineCommentCont_ch result="+result);
443
function tryAfterCloseMultilineComment_ch(line)
446
if (document.startsWith(line - 1, "*/", true))
448
result = document.firstColumn(line - 1) - 1;
452
dbg("tryAfterCloseMultilineComment_ch result="+result);
458
* Check if a current line has a text after cursor position
459
* and a previous one has a comment, then append a <em>"// "</em>
460
* before cursor and realign if latter was inline comment...
462
function trySplitComment_ch(line)
465
if (document.lastColumn(line) != -1)
467
// Ok, current line has some text after...
468
// NOTE There is should be at least one space between
469
// the text and the comment
470
var match = /^(.*\s)(\/\/)(.*)$/.exec(document.line(line - 1));
471
if (match != null && 0 < match[3].trim().length) // If matched and there is some text in a comment
473
if (0 < match[1].trim().length) // Is there some text before the comment?
475
// Align comment to gSameLineCommentStartAt
476
result = gSameLineCommentStartAt;
480
result = match[1].length;
482
var leadMatch = /^([^\s]*\s+).*$/.exec(match[3]);
484
if (leadMatch != null)
488
document.insertText(line, 0, "//" + lead);
493
dbg("trySplitComment_ch result="+result);
499
* \brief Indent a next line after some keywords.
501
* Incrase indent after the following keywords:
510
* - and access modifiers \c public, \c protected and \c private
512
function tryIndentAfterSomeKeywords_ch(line)
515
// Check if ENTER was pressed after some keywords...
516
var sr = splitByComment(line - 1);
517
var prevString = sr.before;
518
dbg("tryIndentAfterSomeKeywords_ch prevString='"+prevString+"'");
519
var r = /^(\s*)((if|for|while)\s*\(|\bdo\b|\breturn\b|(((public|protected|private)(\s+(slots|Q_SLOTS))?)|default|case\s+.*)\s*:).*$/
524
if (!r[2].startsWith("return") || !prevString.rtrim().endsWith(';'))
525
result = r[1].length + gIndentWidth;
529
r = /^\s*\belse\b.*$/.exec(prevString)
532
var prevPrevString = stripComment(line - 2);
533
dbg("tryIndentAfterSomeKeywords_ch prevPrevString='"+prevPrevString+"'");
534
if (prevPrevString.endsWith('}'))
535
result = document.firstColumn(line - 2);
536
else if (prevPrevString.match(/^\s*[\])>]/))
537
result = document.firstColumn(line - 2) - gIndentWidth - (gIndentWidth / 2);
539
result = document.firstColumn(line - 2) - gIndentWidth;
540
// Realign 'else' statement if needed
541
var pp = document.firstColumn(line - 1);
543
document.insertText(line - 1, 0, String().fill(' ', result - pp));
544
else if (result < pp)
545
document.removeText(line - 1, 0, line - 1, pp - result);
546
result += gIndentWidth;
551
tryToKeepInlineComment(line);
552
dbg("tryIndentAfterSomeKeywords_ch result="+result);
558
* Try to indent a line right after a dangling semicolon
559
* (possible w/ leading close braces and comment after)
566
function tryAfterDanglingSemicolon_ch(line)
569
var prevString = document.line(line - 1);
570
var r = /^(\s*)(([\)\]}]?\s*)*([\)\]]\s*))?;/.exec(prevString);
573
result = Math.floor(r[1].length / 4) * 4;
577
// Does it looks like a template tail?
578
// i.e. smth like this:
579
// typedef boost::mpl::blah<
582
r = /^(\s*)([>]+).*;/.exec(prevString);
584
result = Math.floor(r[1].length / 4) * 4;
588
tryToKeepInlineComment(line);
589
dbg("tryDanglingSemicolon_ch result="+result);
595
* Check if \c ENTER pressed after equal sign
601
function tryAfterEqualChar_ch(line)
604
var pos = document.lastColumn(line - 1);
605
if (document.charAt(line - 1, pos) == '=')
606
result = document.firstColumn(line - 1) + gIndentWidth;
609
tryToKeepInlineComment(line);
610
dbg("tryAfterEqualChar_ch result="+result);
615
/// Check if \c ENTER hits after \c #define w/ a backslash
616
function tryMacroDefinition_ch(line)
619
var prevString = document.line(line - 1);
620
if (prevString.search(/^\s*#\s*define\s+.*\\$/) != -1)
621
result = gIndentWidth;
624
dbg("tryMacroDefinition_ch result="+result);
630
* Do not incrase indent if ENTER pressed before access
631
* specifier (i.e. public/private/protected)
633
function tryBeforeAccessSpecifier_ch(line)
636
if (document.line(line).match(/(public|protected|private):/))
638
var openPos = document.anchor(line, 0, '{');
639
if (openPos.isValid())
640
result = document.firstColumn(openPos.line);
644
tryToKeepInlineComment(line);
645
dbg("tryBeforeAccessSpecifier_ch result="+result);
651
* Try to align a line w/ a leading (word) delimiter symbol
652
* (i.e. not an identifier and a brace)
654
function tryBeforeDanglingDelimiter_ch(line)
658
// current line do not starts w/ a comment
659
!document.line(line).ltrim().startsWith("//")
660
// if a previous line starts w/ an identifier
661
&& (document.line(line - 1).search(/^\s*[A-Za-z_][A-Za-z0-9_]*/) != -1)
662
// but the current one starts w/ a delimiter (which is looks like operator)
663
&& (document.line(line).search(/^\s*[,%&<=:\|\-\?\/\+\*\.]/) != -1)
665
// check if we r at function call or array index
666
var insideBraces = document.anchor(line, document.firstColumn(line), '(').isValid()
667
|| document.anchor(line, document.firstColumn(line), '[').isValid()
670
result = document.firstVirtualColumn(line - 1) + (insideBraces ? -2 : 2);
673
tryToKeepInlineComment(line);
674
dbg("tryBeforeDanglingDelimiter_ch result="+result);
679
function tryPreprocessor_ch(line)
682
if (document.firstChar(line) == '#')
685
var text = document.line(line);
686
// Get current depth level
687
var currentLevel = getPreprocessorLevelAt(line);
688
if (currentLevel > 0)
690
// How much spaces we have after hash?
692
var column = document.firstColumn(line) + 1;
694
for (; i < text.length; i++)
700
var wordAfterHash = document.wordAt(line, i);
701
dbg("wordAfterHash='"+wordAfterHash+"'");
702
if (wordAfterHash[0] == '#')
703
wordAfterHash = wordAfterHash.substring(1, wordAfterHash.length);
704
if (wordAfterHash == "else" || wordAfterHash == "elif" || wordAfterHash == "endif")
706
var paddingLen = (currentLevel == 0) ? 0 : (currentLevel - 1) * 2 + 1;
707
if (spacesCnt < paddingLen)
709
var padding = String().fill(' ', paddingLen - spacesCnt);
710
document.insertText(line, column, padding);
712
else if (paddingLen < spacesCnt)
714
document.removeText(line, column, line, column + spacesCnt - paddingLen);
720
dbg("tryPreprocessor_ch result="+result);
726
* Check if \c ENTER was pressed on a start of line and
727
* after a block comment.
729
function tryAfterBlockComment_ch(line)
734
var prev_non_empty_line = document.prevNonEmptyLine(line - 1);
735
if (prev_non_empty_line != -1 && document.line(prev_non_empty_line).trim().startsWith("*/"))
737
var p = document.firstColumn(prev_non_empty_line);
738
if ((p % gIndentWidth) != 0)
739
result = Math.floor(p / gIndentWidth) * gIndentWidth;
744
dbg("tryAfterBlockComment_ch result="+result);
750
* Check if \c ENTER was pressed after \c break or \c continue statements
751
* and if so, unindent the current line.
753
function tryAfterBreakContinue_ch(line)
756
var currentLineText = document.line(line - 1).ltrim();
757
var should_proceed = currentLineText.startsWith("break;") || currentLineText.startsWith("continue;")
760
result = document.firstColumn(line - 1) - gIndentWidth;
764
dbg("tryAfterBreakContinue_ch result="+result);
770
function getStringAligmentAfterSplit(line)
772
var prevLineFirstChar = document.firstChar(line - 1);
773
var halfIndent = prevLineFirstChar == ','
774
|| prevLineFirstChar == ':'
775
|| prevLineFirstChar == '?'
776
|| prevLineFirstChar == '<'
777
|| prevLineFirstChar == '>'
778
|| prevLineFirstChar == '&'
780
return document.firstColumn(line - 1) + (
781
prevLineFirstChar != '"'
782
? (halfIndent ? (gIndentWidth / 2) : gIndentWidth)
788
* Handle the case when \c ENTER has pressed in the middle of a string.
789
* Find a string begin (a quote char) and analyze if it is a C++11 raw
790
* string literal. If it is not, add a "closing" quote to a previous line
791
* and to the current one. Align a 2nd part (the moved down one) of a string
792
* according a previous line. If latter is a pure string, then give the same
793
* indentation level, otherwise incrase it to one \c TAB.
795
* Here is few cases possible:
796
* - \c ENTER has pressed in a line <code>auto some = ""|</code>, so a new
797
* line just have an empty string or some text which is doesn't matter now;
798
* - \c ENTER has pressed in a line <code>auto some = "possible some text here| and here"</code>,
799
* then a new line will have <code> and here"</code> text
801
* In both cases attribute at (line-1, lastColumn-1) will be \c String
803
function trySplitString_ch(line)
806
var column = document.lastColumn(line - 1);
808
if (isComment(line - 1, column))
809
return result; // Do nothing for comments
811
// Check if last char on a prev line has string attribute
812
var lastColumnIsString = isString(line - 1, column);
813
var firstColumnIsString = isString(line, 0);
814
var firstChar = (document.charAt(line, 0) == '"');
815
if (!lastColumnIsString) // If it is not,
818
if (firstColumnIsString && firstChar == '"')
819
result = getStringAligmentAfterSplit(line);
820
return result; // then nothing to do...
823
var lastChar = (document.charAt(line - 1, column) == '"');
824
var prevLastColumnIsString = isString(line - 1, column - 1);
825
var prevLastChar = (document.charAt(line - 1, column - 1) == '"');
826
dbg("trySplitString_ch: lastColumnIsString="+lastColumnIsString);
827
dbg("trySplitString_ch: lastChar="+lastChar);
828
dbg("trySplitString_ch: prevLastColumnIsString="+prevLastColumnIsString);
829
dbg("trySplitString_ch: prevLastChar="+prevLastChar);
830
dbg("trySplitString_ch: isString(line,0)="+firstColumnIsString);
831
dbg("trySplitString_ch: firstChar="+firstChar);
832
var startOfString = firstColumnIsString && firstChar;
833
var endOfString = !(firstColumnIsString || firstChar);
834
var should_proceed = !lastChar && prevLastColumnIsString && (endOfString || !prevLastChar && startOfString)
835
|| lastChar && !prevLastColumnIsString && !prevLastChar && (endOfString || startOfString)
837
dbg("trySplitString_ch: ------ should_proceed="+should_proceed);
840
// Add closing quote to the previous line
841
document.insertText(line - 1, document.lineLength(line - 1), '"');
842
// and open quote to the current one
843
document.insertText(line, 0, '"');
844
// NOTE If AutoBrace plugin is used, it won't add a quote
845
// char, if cursor positioned right before another quote char
846
// (which was moved from a line above in this case)...
847
// So, lets force it!
848
if (startOfString && document.charAt(line, 1) != '"')
850
document.insertText(line, 1, '"'); // Add one more!
851
view.setCursorPosition(line, 1); // Step back inside of string
853
result = getStringAligmentAfterSplit(line);
857
dbg("trySplitString_ch result="+result);
858
tryToKeepInlineComment(line);
864
* Here is few cases possible:
866
* // set some var to lambda function
867
* auto some = [foo](bar)|
869
* // lambda as a parameter (possible the first one,
870
* // i.e. w/o a leading comma)
874
* , [](const value_type& v)|
878
function tryAfterLambda_ch(line)
881
var column = document.lastColumn(line - 1);
883
if (isComment(line - 1, column))
884
return result; // Do nothing for comments
886
var sr = splitByComment(line - 1);
887
if (sr.before.match(/\[[^\]]*\]\([^{]*\)[^{}]*$/))
889
var align = document.firstColumn(line - 1);
890
var before = sr.before.ltrim();
891
if (before.startsWith(','))
893
var padding = String().fill(' ', align);
894
var tail = before.startsWith('auto ') ? "};" : "}";
898
, padding + "{\n" + padding + String().fill(' ', gIndentWidth) + "\n" + padding + tail
900
view.setCursorPosition(line + 1, align + gIndentWidth);
906
dbg("tryAfterLambda_ch result="+result);
911
/// Wrap \c tryToKeepInlineComment as \e caret-handler
912
function tryToKeepInlineComment_ch(line)
914
tryToKeepInlineComment(line);
919
* \brief Handle \c ENTER key
921
function caretPressed(cursor)
924
var line = cursor.line;
926
// Dunno what to do if previous line isn't available
928
return result; // Nothing (dunno) to do if no previous line...
930
// Register all indent functions
932
tryBraceSplit_ch // Handle ENTER between braces
933
, tryMultilineCommentStart_ch
934
, tryMultilineCommentCont_ch
935
, tryAfterCloseMultilineComment_ch
937
, tryToAlignAfterOpenBrace_ch // Handle {,[,(,< on a previous line
938
, tryToAlignBeforeCloseBrace_ch // Handle },],),> on a current line before cursor
939
, tryToAlignBeforeComma_ch // Handle ENTER pressed before comma or semicolon
940
, tryIndentAfterSomeKeywords_ch // NOTE It must follow after trySplitComment_ch!
941
, tryAfterDanglingSemicolon_ch
942
, tryMacroDefinition_ch
943
, tryBeforeDanglingDelimiter_ch
944
, tryBeforeAccessSpecifier_ch
945
, tryAfterEqualChar_ch
947
, tryAfterBlockComment_ch
948
, tryAfterBreakContinue_ch
949
, trySplitString_ch // Handle ENTER pressed in the middle of a string
950
, tryAfterLambda_ch // Handle ENTER after lambda prototype and before body
951
, tryToKeepInlineComment_ch // NOTE This must be a last checker!
954
// Apply all all functions until result gets changed
957
; i < handlers.length && result == -1
958
; result = handlers[i++](line)
965
* \brief Handle \c '/' key pressed
967
* Check if is it start of a comment. Here is few cases possible:
968
* \li very first \c '/' -- do nothing
969
* \li just entered \c '/' is a second in a sequence. If no text before or some present after,
970
* do nothing, otherwise align a \e same-line-comment to \c gSameLineCommentStartAt
972
* \li just entered \c '/' is a 3rd in a sequence. If there is some text before and no after,
973
* it looks like inlined doxygen comment, so append \c '<' char after. Do nothing otherwise.
974
* \li if there is a <tt>'// '</tt> string right before just entered \c '/', form a
975
* doxygen comment <tt>'///'</tt> or <tt>'///<'</tt> depending on presence of some text
976
* on a line before the comment.
978
* \todo Due the BUG #316809 in a current version of Kate, this code doesn't work as expected!
979
* It always returns a <em>"NormalText"</em>!
981
* var cm = document.attributeName(cursor);
982
* if (cm.indexOf("String") != -1)
986
* \bug This code doesn't work properly in the following case:
988
* std::string bug = "some text//
992
function trySameLineComment(cursor)
994
var line = cursor.line;
995
var column = cursor.column;
997
// First of all check that we are not withing a string
998
if (document.isString(line, column))
1001
var sc = splitByComment(line);
1002
if (sc.hasComment) // Is there any comment on a line?
1004
// Make sure we r not in a comment already -- it can be a multiline one...
1005
var fc = document.firstColumn(line);
1006
var text = document.line(line).ltrim();
1007
var nothing_to_do = (fc < (column - 1)) && document.isComment(line, fc);
1008
// Also check that line has smth that needs to be "fixed"...
1009
if (nothing_to_do && text != "//" && text != "// /" && text != "///" && text != "/// /")
1011
// If no text after the comment and it still not aligned
1012
var text_len = sc.before.rtrim().length;
1013
if (text_len != 0 && sc.after.length == 0 && text_len < gSameLineCommentStartAt)
1016
document.insertText(
1019
, String().fill(' ', gSameLineCommentStartAt - sc.before.length)
1021
document.insertText(line, gSameLineCommentStartAt + 2, ' ');
1023
// If text in a comment equals to '/' or ' /' -- it looks like a 3rd '/' pressed
1024
else if (sc.after == " /" || sc.after == "/")
1026
// Form a Doxygen comment!
1027
document.removeText(line, column - sc.after.length, line, column);
1028
document.insertText(line, column - sc.after.length, text_len != 0 ? "/< " : "/ ");
1030
// If right trimmed text in a comment equals to '/' -- it seems user moves cursor
1031
// one char left (through space) to add one more '/'
1032
else if (sc.after.rtrim() == "/")
1034
// Form a Doxygen comment!
1035
document.removeText(line, column, line, column + sc.after.length);
1036
document.insertText(line, column, text_len != 0 ? "< " : " ");
1038
else if (sc.after == "/ /")
1040
// Looks like user wants to "draw a fence" w/ '/'s
1041
document.removeText(line, column - 2, line, column - 1);
1043
else if (text_len == 0 && sc.after.length == 0)
1045
document.insertText(line, column, ' ');
1051
* \brief Maybe '>' needs to be added?
1053
* Here is a few cases possible:
1054
* \li user entered <em>"template ></em>
1055
* \li user entered smth like <em>std::map></em>
1056
* \li user wants to output smth to C++ I/O stream by typing <em>>></em>
1057
* possible at the line start, so it must be half indented
1058
* \li shortcut: <tt>some(<|)</tt> transformed into <tt>some()<|</tt>
1060
* But, do not add '>' if there some text after cursor.
1062
function tryTemplate(cursor)
1064
var line = cursor.line;
1065
var column = cursor.column;
1068
if (isStringOrComment(line, column))
1069
return result; // Do nothing for comments and strings
1071
// Check for 'template' keyword at line start
1072
var currentString = document.line(line);
1073
var prevWord = document.wordAt(line, column - 1);
1074
dbg("tryTemplate: prevWord='"+prevWord+"'");
1075
dbg("tryTemplate: prevWord.match="+prevWord.match(/\b[A-Za-z_][A-Za-z0-9_]*/));
1076
// Add a closing angle bracket if a prev word is not a 'operator'
1077
// and it looks like an identifier or current line starts w/ 'template' keyword
1078
var isCloseAngleBracketNeeded = (prevWord != "operator")
1079
&& (currentString.match(/^\s*template\s*<$/) || prevWord.match(/\b[A-Za-z_][A-Za-z0-9_]*/))
1080
&& (column == document.lineLength(line) || document.charAt(cursor).match(/\W/))
1082
if (isCloseAngleBracketNeeded)
1084
document.insertText(cursor, ">");
1085
view.setCursorPosition(cursor);
1087
else if (justEnteredCharIsFirstOnLine(line, column, '<'))
1089
result = tryIndentRelativePrevNonCommentLine(line);
1091
// Add a space after 2nd '<' if a word before is not a 'operator'
1092
else if (document.charAt(line, column - 2) == '<')
1094
if (document.wordAt(line, column - 3) != "operator")
1096
// Looks like case 3...
1097
// 0) try to remove '>' if user typed 'some<' before
1098
// (and closing '>' was added by tryTemplate)
1099
if (column < document.lineLength(line) && document.charAt(line, column) == '>')
1101
document.removeText(line, column, line, column + 1);
1102
addCharOrJumpOverIt(line, column - 2, ' ');
1103
view.setCursorPosition(line, column + 1);
1104
column = column + 1;
1106
// add a space after operator<<
1107
document.insertText(line, column, " ");
1111
document.insertText(line, column, "()");
1112
view.setCursorPosition(line, column + 1);
1117
cursor = tryJumpOverParenthesis(cursor); // Try to jump out of parenthesis
1118
tryAddSpaceAfterClosedBracketOrQuote(cursor);
1124
* This function called for some characters and trying to do the following:
1125
* if the cursor (right after a trigger character is entered) is positioned withing
1126
* a parenthesis, move the entered character out of parenthesis.
1130
* auto a = two_params_func(get_first(,|))
1131
* // ... transformed into
1132
* auto a = two_params_func(get_first(),|)
1135
* because entering comma right after <tt>(</tt> definitely incorrect, but
1136
* we can help the user (programmer) to avoid 3 key presses ;-)
1137
* (RightArrow, ',', space)
1139
* except comma here are other "impossible" characters:
1140
* \c ., \c ?, \c :, \c %, \c |, \c /, \c =, \c <, \c >, \c ], \c }
1142
* But \c ; will be handled separately to be able to jump over all closing \c ).
1144
* \sa \c trySemicolon()
1146
* \note This valid if we r not inside a comment or a string literal,
1147
* and the char out of the parenthesis is not the same as just entered ;-)
1149
* \param cursor initial cursor position
1150
* \param es edit session instance
1151
* \return new (possible modified) cursor position
1153
function tryJumpOverParenthesis(cursor)
1155
var line = cursor.line;
1156
var column = cursor.column;
1158
if (2 < column && isStringOrComment(line, column))
1159
return cursor; // Do nothing for comments of string literals
1161
// Check that we r inside of parenthesis and some symbol between
1162
var pc = document.charAt(line, column - 2);
1163
var cc = document.charAt(cursor);
1164
if ((pc == '(' && cc == ')') || (pc == '{' && cc == '}'))
1166
var c = document.charAt(line, column - 1);
1170
if (pc == '(' && cc == ')')
1172
// Make sure this is not a `catch (...)`
1173
if (document.startsWith(line, "catch (.)", true))
1175
document.insertText(line, column, "..");
1176
view.setCursorPosition(line, column + 3);
1186
case '/': // TODO ORLY?
1192
case ']': // NOTE '[' could be a part of lambda
1194
// Ok, move character out of parenthesis
1195
document.removeText(line, column - 1, line, column);
1196
// Check is a character after the closing brace the same as just entered one
1197
addCharOrJumpOverIt(line, column, c);
1198
return view.cursorPosition();
1208
* Handle the case when some character was entered after a some closing bracket.
1209
* Here is few close brackets possible:
1210
* \li \c ) -- ordinal function call
1211
* \li \c } -- C++11 constructor call
1212
* \li \c ] -- array access
1213
* \li \c " -- end of a string literal
1214
* \li \c ' -- and of a char literal
1216
* This function try to add a space between a closing quote/bracket and operator char.
1218
* \note This valid if we r not inside a comment or a string literal.
1220
function tryAddSpaceAfterClosedBracketOrQuote(cursor)
1222
var line = cursor.line;
1223
var column = cursor.column;
1225
if (isStringOrComment(line, column - 1))
1226
return cursor; // Do nothing for comments of string literals
1228
// Check if we have a closing bracket before a last entered char
1229
var b = document.charAt(line, column - 2);
1230
if (!(b == ']' || b == '}' || b == ')' || b == '"' || b == "'"))
1233
// Ok, lets check what we've got as a last char
1234
var c = document.charAt(line, column - 1);
1235
dbg("tryAddSpaceAfterClosedBracketOrQuote: c='"+c+"', @"+new Cursor(line, column-1));
1248
document.insertText(line, column - 1, " ");
1249
view.setCursorPosition(line, column + 1);
1250
return view.cursorPosition();
1252
// Close angle bracket may be a part of template instantiation
1253
// w/ some function type parameter... Otherwise, add a space after.
1256
document.insertText(line, column - 1, " ");
1257
view.setCursorPosition(line, column + 1);
1258
return view.cursorPosition();
1268
* \brief Try to align parameters list
1270
* If (just entered) comma is a first symbol on a line,
1271
* just move it on a half-tab left relative to a previous line
1272
* (if latter doesn't starts w/ comma or ':').
1273
* Do nothing otherwise. A space would be added after it anyway.
1275
function tryComma(cursor)
1278
var line = cursor.line;
1279
var column = cursor.column;
1280
// Check is comma a very first character on a line...
1281
if (justEnteredCharIsFirstOnLine(line, column, ','))
1283
result = tryIndentRelativePrevNonCommentLine(line);
1287
// Try to stick a comma to a previous non-space char
1288
var lastWordPos = document.text(line, 0, line, column - 1).rtrim().length;
1289
if (lastWordPos < column)
1291
document.removeText(line, column - 1, line, column);
1292
document.insertText(line, lastWordPos, ",");
1293
cursor = new Cursor(line, lastWordPos + 1);
1294
view.setCursorPosition(cursor);
1298
cursor = tryJumpOverParenthesis(cursor); // Try to jump out of parenthesis
1299
addCharOrJumpOverIt(cursor.line, cursor.column, ' ');
1304
* \brief Move towards a document start and look for control flow keywords
1306
* \note Keyword must be at the line start
1308
* \return found line's indent, otherwise \c -2 if nothing found
1310
function tryBreakContinue(line, is_break)
1313
// Ok, look backward and find a loop/switch statement
1314
for (; 0 <= line; --line)
1316
var text = document.line(line).ltrim();
1317
var is_loop_or_switch = text.startsWith("for ")
1318
|| text.startsWith("do ")
1319
|| text.startsWith("while ")
1320
|| text.startsWith("if ")
1321
|| text.startsWith("else if ")
1324
is_loop_or_switch = is_loop_or_switch
1325
|| text.startsWith("case ")
1326
|| text.startsWith("default:")
1328
if (is_loop_or_switch)
1331
if (line != -1) // Found smth?
1332
result = document.firstColumn(line) + gIndentWidth;
1338
* \brief Handle \c ; character.
1340
* Here is few cases possible (handled):
1341
* \li semicolon is a first char on a line -- then, it looks like \c for statement
1342
* splitted accross the lines
1343
* \li semicolon entered after some keywords: \c break or \c continue, then we
1344
* need to align this line taking in account a previous one
1345
* \li and finally here is a trick: when auto brackets extension enabled, and user types
1346
* a function call like this:
1348
* auto var = some_call(arg1, arg2|)
1350
* (\c '|' shows a cursor position). Note there is no final semicolon in this expression,
1351
* cuz pressing <tt>'('</tt> leads to the following snippet: <tt>some_call(|)</tt>, so to
1352
* add a semicolon you have to move cursor out of parenthesis. The trick is to allow to press
1353
* <tt>';'</tt> at position shown in the code snippet, so indenter will transform it into this:
1355
* auto var = some_call(arg1, arg2);|
1357
* same works even there is no arguments...
1359
* All the time, when simicolon is not a first non-space symbol (and not a part of a comment
1360
* or string) it will be stiked to the last non-space character on the line.
1362
function trySemicolon(cursor)
1365
var line = cursor.line;
1366
var column = cursor.column;
1368
if (isStringOrComment(line, column))
1369
return result; // Do nothing for comments and strings
1371
// If ';' is a first char on a line?
1372
if (justEnteredCharIsFirstOnLine(line, column, ';'))
1374
// Check if we are inside a `for' statement
1375
var openBracePos = document.anchor(line, column, '(');
1376
if (openBracePos.isValid())
1378
// Add a half-tab relative '('
1379
result = document.firstColumn(openBracePos.line) + 2;
1380
document.insertText(cursor, " ");
1385
// Stick ';' to the last "word"
1386
var lcsc = document.prevNonSpaceColumn(line, column - 2);
1387
if (2 < column && lcsc < (column - 2))
1389
document.removeText(line, column - 1, line, column);
1390
if (document.charAt(line, lcsc) != ';')
1392
document.insertText(line, lcsc + 1, ";");
1393
view.setCursorPosition(line, lcsc + 2);
1395
else view.setCursorPosition(line, lcsc + 1);
1396
cursor = view.cursorPosition();
1397
column = cursor.column;
1399
var text = document.line(line).ltrim();
1400
var is_break = text.startsWith("break;");
1401
var should_proceed = is_break || text.startsWith("continue;")
1404
result = tryBreakContinue(line - 1, is_break);
1408
// Make sure we r not inside of `for' statement
1409
/// \todo Make sure cursor is really inside of \c for and
1410
/// not smth like this: <tt>for (blah; blah; blah) some(arg,;|)</tt>
1411
else if (!text.startsWith("for "))
1413
// Check if next character(s) is ')' and nothing after
1414
should_proceed = true;
1415
var lineLength = document.lineLength(line);
1416
for (var i = column; i < lineLength; ++i)
1418
var c = document.charAt(line, i);
1419
if (!(c == ')' || c == ']'))
1421
should_proceed = false;
1425
// Ok, lets move ';' out of "a(b(c(;)))" of any level...
1428
// Remove ';' from column - 1
1429
document.removeText(line, column - 1, line, column);
1430
// Append ';' to the end of line
1431
document.insertText(line, lineLength - 1, ";");
1432
view.setCursorPosition(line, lineLength);
1433
cursor = view.cursorPosition();
1434
column = cursor.column;
1437
// In C++ there is no need to have more than one semicolon.
1438
// So remove a redundant one!
1439
if (document.charAt(line, column - 2) == ';')
1441
// Remove just entered ';'
1442
document.removeText(line, column - 1, line, column);
1449
* Handle possible dangling operators (moved from a previous line)
1451
* \c ?, \c |, \c ^, \c %, \c .
1453
* Add spaces around ternary operator.
1455
function tryOperator(cursor, ch)
1458
var line = cursor.line;
1459
var column = cursor.column;
1461
if (isStringOrComment(line, column))
1462
return result; // Do nothing for comments and strings
1464
var halfTabNeeded = justEnteredCharIsFirstOnLine(line, column, ch)
1465
&& document.line(line - 1).search(/^\s*[A-Za-z_][A-Za-z0-9_]*/) != -1
1467
dbg("tryOperator: halfTabNeeded =", halfTabNeeded);
1470
// check if we r at function call or array index
1471
var insideBraces = document.anchor(line, document.firstColumn(line), '(').isValid()
1472
|| document.anchor(line, document.firstColumn(line), '[').isValid()
1473
|| document.anchor(line, document.firstColumn(line), '{').isValid()
1475
dbg("tryOperator: insideBraces =",insideBraces);
1476
result = document.firstColumn(line - 1) + (insideBraces && ch != '.' ? -2 : 2);
1478
var prev_pos = cursor;
1479
cursor = tryJumpOverParenthesis(cursor); // Try to jump out of parenthesis
1480
cursor = tryAddSpaceAfterClosedBracketOrQuote(cursor);
1482
// Check if a space before '?' still needed
1483
if (prev_pos == cursor && ch == '?' && document.charAt(line, cursor.column - 1) != ' ')
1484
document.insertText(line, cursor.column - 1, " "); // Add it!
1486
cursor = view.cursorPosition(); // Update cursor position
1488
column = cursor.column;
1492
addCharOrJumpOverIt(line, column, ' ');
1494
// Handle operator| and/or operator||
1498
* Here is 6+3 cases possible (the last bar is just entered):
1499
* 0) <tt>???</tt> -- add a space before bar and after if needed
1500
* 1) <tt>?? </tt> -- add a space after if needed
1501
* 2) <tt>??|</tt> -- add a space before 1st bar and after the 2nd if needed
1502
* 3) <tt>? |</tt> -- add a space after the 2nd bar if needed
1503
* 4) <tt>?| </tt> -- add a space before 1st bar, remove the mid one, add a space after 2nd bar
1504
* 5) <tt> | </tt> -- remove the mid space, add one after 2nd bar
1506
* 6a) <tt>|| </tt> -- add a space before 1st bar if needed, remove the last bar
1507
* 6b) <tt> ||</tt> -- remove the last bar and add a space after 2nd bar if needed
1508
* 6c) <tt>||</tt> -- add a space after if needed
1510
var prev = document.text(line, column - 4, line, column - 1);
1511
dbg("tryOperator: checking @Cursor("+line+","+(column - 4)+"), prev='"+prev+"'");
1512
var space_offset = 0;
1513
if (prev.endsWith(" | "))
1515
// case 5: remove the mid space
1516
document.removeText(line, column - 2, line, column - 1);
1519
else if (prev.endsWith("|| "))
1521
// case 6a: add a space before 1st bar if needed, remove the last bar
1522
document.removeText(line, column - 1, line, column);
1523
var space_has_added = addCharOrJumpOverIt(line, column - 4, ' ');
1524
space_offset = (space_has_added ? 1 : 0) - 2;
1526
else if (prev.endsWith(" ||"))
1528
// case 6b: remove the last bar
1529
document.removeText(line, column - 1, line, column);
1532
else if (prev.endsWith("||"))
1534
// case 6a: add a space before and remove the last bar
1535
document.removeText(line, column - 1, line, column);
1536
document.insertText(line, column - 3, " ");
1538
else if (prev.endsWith("| "))
1540
// case 4: add a space before 1st bar, remove the mid one
1541
document.removeText(line, column - 2, line, column - 1);
1542
document.insertText(line, column - 3, " ");
1544
else if (prev.endsWith(" |") || prev.endsWith(" "))
1550
// case 2: add a space before 1st bar
1551
if (prev.endsWith('|'))
1553
// case 0: add a space before bar
1554
document.insertText(line, column - 1 - space_offset, " ");
1557
addCharOrJumpOverIt(line, column + space_offset, ' ');
1559
// Handle operator% and/or operator^
1560
else if (ch == '%' || ch == '^')
1562
var prev = document.text(line, column - 4, line, column - 1);
1563
dbg("tryOperator: checking2 @Cursor("+line+","+(column - 4)+"), prev='"+prev+"'");
1564
var patterns = [" % ", "% ", " %", "%", " "];
1567
; i < patterns.length
1569
) patterns[i] = patterns[i].replace('%', ch);
1571
var space_offset = 0;
1572
if (prev.endsWith(patterns[0]))
1574
// case 0: remove just entered char
1575
document.removeText(line, column - 1, line, column);
1578
else if (prev.endsWith(patterns[1]))
1580
// case 1: remove just entered char, add a space before
1581
document.removeText(line, column - 1, line, column);
1582
document.insertText(line, column - 3, " ");
1585
else if (prev.endsWith(patterns[2]))
1587
// case 2: remove just entered char
1588
document.removeText(line, column - 1, line, column);
1591
else if (prev.endsWith(patterns[3]))
1593
// case 3: add a space before
1594
document.removeText(line, column - 1, line, column);
1595
document.insertText(line, column - 2, " ");
1598
else if (prev.endsWith(patterns[4]))
1600
// case 4: no space needed before
1605
// case everything else: surround operator w/ spaces
1606
document.insertText(line, column - 1, " ");
1609
addCharOrJumpOverIt(line, column + space_offset, ' ');
1611
else if (ch == '.') // Replace '..' w/ '...'
1613
var prev = document.text(line, column - 3, line, column);
1614
dbg("tryOperator: checking3 @Cursor("+line+","+(column - 4)+"), prev='"+prev+"'");
1615
if (prev == "...") // If there is already 3 dots
1617
// Remove just entered (redundant) one
1618
document.removeText(line, column - 1, line, column);
1620
// Append one more if only two here and we are in 'Normal Mode'
1621
else if (prev[1] == '.' && prev[2] == '.' && document.defStyleNum(line, column) == 0)
1623
addCharOrJumpOverIt(line, column, '.');
1624
} // Otherwise, do nothing...
1628
dbg("tryOperator result="+result);
1634
* \brief Try to align a given close bracket
1636
function tryCloseBracket(cursor, ch)
1639
var line = cursor.line;
1640
var column = cursor.column;
1642
var braceCursor = Cursor.invalid();
1645
// TODO Make sure a given `ch` in the gBraceMap
1646
braceCursor = document.anchor(line, column - 1, gBraceMap[ch]);
1647
// TODO Otherwise, it seems we have a template parameters list...
1650
// Check if a given closing brace is a first char on a line
1651
// (i.e. it is 'dangling' brace)...
1652
if (justEnteredCharIsFirstOnLine(line, column, ch) && braceCursor.isValid())
1654
// Move to one half-TAB right, if anything but not closing '}', else
1655
// align to the corresponding open char
1656
result = document.firstColumn(braceCursor.line) + (ch != '}' ? 2 : 0);
1657
dbg("tryCloseBracket: setting result="+result);
1660
// Check if ';' required after closing '}'
1661
if (ch == '}' && braceCursor.isValid())
1663
var is_check_needed = false;
1664
// Check if corresponding anchor is a class/struct/union/enum,
1665
// (possible keyword located on same or prev line)
1666
// and check for trailing ';'...
1667
var anchoredString = document.line(braceCursor.line);
1668
dbg("tryCloseBracket: anchoredString='"+anchoredString+"'");
1669
var regex = /^(\s*)(class|struct|union|enum).*$/;
1670
var r = regex.exec(anchoredString);
1673
dbg("tryCloseBracket: same line");
1674
is_check_needed = true;
1676
else (!is_check_needed && 0 < braceCursor.line) // Is there any line before?
1678
dbg("tryCloseBracket: cheking prev line");
1680
// Ok, lets check it!
1681
anchoredString = document.line(braceCursor.line - 1);
1682
dbg("tryCloseBracket: anchoredString-1='"+anchoredString+"'");
1683
r = regex.exec(anchoredString);
1686
is_check_needed = true;
1687
dbg("tryCloseBracket: prev line");
1690
dbg("tryCloseBracket: is_check_needed="+is_check_needed);
1691
if (is_check_needed)
1693
var is_ok = document.line(line)
1694
.substring(column, document.lineLength(line))
1700
document.insertText(line, column, ';');
1701
view.setCursorPosition(line, column + 1);
1707
// If user typed 'some' + '<' + '>', jump over the '>'
1708
// (which was added by the tryTemplate)
1709
if (document.charAt(line, column) == '>')
1711
document.removeText(line, column, line, column + 1);
1715
tryJumpOverParenthesis(view.cursorPosition());
1721
* \brief Indent a new scope block
1723
* ... try to unindent to be precise... First of all check that open
1724
* \c '{' is a first symbol on a line, and if it doesn't,
1725
* add space (if absent at previous position) after <tt>')'</tt> or \c '='
1726
* or if line stats w/ some keywords: \c enum, \c class, \c struct or \c union.
1727
* Otherwise, look at the previous line for dangling <tt>')'</tt> or
1728
* a line started w/ one of flow control keywords.
1731
function tryBlock(cursor)
1734
var line = cursor.line;
1735
var column = cursor.column;
1737
// Make sure we r not in a comment or string
1738
dbg("tryBlock: isStringOrComment(line, column - 2)="+isStringOrComment(line, column - 2))
1739
if (isStringOrComment(line, column - 2))
1742
if (justEnteredCharIsFirstOnLine(line, column, '{'))
1744
// Check for a dangling close brace on a previous line
1745
// (this may mean that `for' or `if' or `while' w/ looong parameters list on it)
1746
if (document.firstChar(line - 1) == ')')
1747
result = Math.floor(document.firstColumn(line - 1) / gIndentWidth) * gIndentWidth;
1750
// Otherwise, check for a keyword on the previous line and
1751
// indent the started block to it...
1752
var prevString = document.line(line - 1);
1753
var r = /^(\s*)((catch|if|for|while)\s*\(|do|else|try|(default|case\s+.*)\s*:).*$/.exec(prevString);
1755
result = r[1].length;
1760
// '{' is not a first char. Check for a previous one...
1763
var prevChar = document.charAt(line, column - 2);
1764
dbg("tryBlock: prevChar='"+prevChar+"'");
1765
if (prevChar == ')' || prevChar == '=')
1766
document.insertText(line, column - 1, ' ');
1767
else if (prevChar != ' ')
1769
var currentLine = document.line(line).ltrim();
1770
var starts_with_keyword = currentLine.startsWith('struct ')
1771
|| currentLine.startsWith('class ')
1772
|| currentLine.startsWith('union ')
1773
|| currentLine.startsWith('enum ')
1775
if (starts_with_keyword)
1776
document.insertText(line, column - 1, ' ');
1784
* \brief Align preprocessor directives
1786
function tryPreprocessor(cursor)
1789
var line = cursor.line;
1790
var column = cursor.column;
1792
// Check if just entered '#' is a first on a line
1793
if (justEnteredCharIsFirstOnLine(line, column, '#'))
1795
// Get current indentation level
1796
var currentLevel = getPreprocessorLevelAt(line);
1797
if (currentLevel > 0)
1799
var padding = String().fill(' ', (currentLevel - 1) * 2 + 1);
1800
document.insertText(cursor, padding);
1808
* \brief Try to align access modifiers or class initialization list
1810
* Here is few cases possible:
1811
* \li \c ':' pressed after a keyword \c public, \c protected or \c private.
1812
* Then align a current line to corresponding class/struct definition.
1813
* Check a previous line and if it is not starts w/ \c '{' add a new line before.
1814
* \li \c ':' is a first char on the line, then it looks like a class initialization
1815
* list or 2nd line of ternary operator.
1816
* \li \c ':' is pressed on a line started w/ \c for statement and after a space
1817
* \li \c ':' after '>' looks like an access to template's member
1818
* \li shortcut: transform <tt>some(:|)</tt> into <tt>some() :|</tt>
1820
* \todo Should it be done only for non strings and comments?
1822
function tryColon(cursor)
1825
var line = cursor.line;
1826
var column = cursor.column;
1828
if (isStringOrComment(line, column))
1829
return result; // Do nothing for comments and strings
1831
// Check if just entered ':' is a first on a line
1832
if (justEnteredCharIsFirstOnLine(line, column, ':'))
1834
// Check if there a dangling ')' or '?' (ternary operator) on a previous line
1835
var ch = document.firstChar(line - 1);
1836
if (ch == ')' || ch == '?')
1837
result = document.firstVirtualColumn(line - 1);
1839
result = document.firstVirtualColumn(line - 1) + 2;
1840
document.insertText(cursor, " ");
1844
var currentLine = document.line(line);
1845
if (currentLine.search(/^\s*((public|protected|private)\s*(slots|Q_SLOTS)?|(signals|Q_SIGNALS)\s*):\s*$/) != -1)
1847
var definitionCursor = document.anchor(line, 0, '{');
1848
if (definitionCursor.isValid())
1850
result = document.firstVirtualColumn(definitionCursor.line);
1851
dbg("tryColon: result="+result);
1852
if (0 < line) // Is there any line before?
1854
// Check if previous line is not empty and not starts w/ '{'
1855
var prevLine = document.line(line - 1).trim()
1856
if (prevLine.length && !prevLine.startsWith("{"))
1858
// Cuz a new line will be added in place of current, returning
1859
// result will not affect indentation. So do it manually.
1860
var firstColumn = document.firstColumn(line);
1862
if (firstColumn < result)
1863
padding = String().fill(' ', result - firstColumn);
1864
else if (result < firstColumn)
1865
document.removeText(line, 0, line, firstColumn - result);
1866
// Add an empty line before the current
1867
document.insertText(line, 0, "\n" + padding);
1873
else if (document.charAt(line, column - 2) == ' ')
1875
// Is it looks like a range based `for' or class/struct/enum?
1876
var add_space = currentLine.ltrim().startsWith("for (")
1877
|| currentLine.ltrim().startsWith("class ")
1878
|| currentLine.ltrim().startsWith("struct ")
1879
|| currentLine.ltrim().startsWith("enum ")
1883
// Add a space after ':'
1884
document.insertText(line, column, " ");
1886
else if (document.charAt(line, column - 3) == ':')
1888
// Transform ': :' -> '::'
1889
document.removeText(line, column - 2, line, column - 1);
1892
else if (document.charAt(line, column - 2) == ':')
1894
// A char before is (already) a one more colon.
1895
// Make sure there is no more than two colons...
1896
// NOTE In C++ it is not possible to have more than two of them adjacent!
1897
if (document.charAt(line, column - 3) == ':')
1899
// Remove the current (just entered) one...
1900
document.removeText(line, column - 1, line, column);
1905
// Check that it is not a 'case' and not a magic sequence.
1906
// NOTE "Magic sequence" means support for dynamic expand functions.
1907
// http://zaufi.github.io/programming/2014/02/13/kate-c++-stuff/
1908
var is_magic_sequence = document.charAt(
1910
, document.wordRangeAt(line, column - 1).start.column - 1
1912
if (!currentLine.ltrim().startsWith("case ") && !is_magic_sequence)
1915
// Example some<T>: --> some<T>:: or std: --> std::
1916
document.insertText(line, column, ":");
1920
// Try to jump out of parenthesis
1921
cursor = tryJumpOverParenthesis(cursor);
1922
// Try add a space after close bracket
1923
tryAddSpaceAfterClosedBracketOrQuote(cursor);
1931
* \brief Try to add one space after keywords and before an open brace
1933
function tryOpenBrace(cursor)
1935
var line = cursor.line;
1936
var column = cursor.column;
1937
var wordBefore = document.wordAt(line, column - 1);
1938
dbg("word before: '"+wordBefore+"'");
1939
if (wordBefore.search(/\b(catch|for|if|switch|while|return)\b/) != -1)
1940
document.insertText(line, column - 1, " ");
1943
function getMacroRange(line)
1945
function stripLastCharAndRTrim(str)
1947
return str.substring(0, str.length - 1).rtrim();
1950
var macroStartLine = -1;
1951
// Look up towards begining of a document
1952
for (var i = line; i >= 0; --i)
1954
var currentLineText = document.line(i);
1955
dbg("up: '"+currentLineText+"'");
1956
if (currentLineText.search(/^\s*#\s*define\s+.*\\$/) != -1)
1959
maxLength = Math.max(maxLength, stripLastCharAndRTrim(currentLineText).length);
1960
break; // Ok, we've found the macro start!
1962
else if (currentLineText.search(/\\$/) == -1)
1963
break; // Oops! No backslash found and #define still not reached!
1964
maxLength = Math.max(maxLength, stripLastCharAndRTrim(currentLineText).length);
1967
if (macroStartLine == -1)
1970
// Look down towards end of the document
1971
var macroEndLine = -1;
1972
for (var i = line; i < document.lines(); ++i)
1974
var currentLineText = document.line(i);
1975
dbg("dw: '"+currentLineText+"'");
1976
if (currentLineText.search(/\\$/) != -1) // Make sure the current line have a '\' at the end
1979
maxLength = Math.max(maxLength, stripLastCharAndRTrim(currentLineText).length);
1981
else break; // No backslash at the end --> end of macro!
1984
if (macroEndLine == -1)
1989
range: new Range(macroStartLine, 0, macroEndLine, 0)
1995
* \brief Try to align a backslashes in macro definition
1997
* \note It is \b illegal to have smth after a backslash in source code!
1999
function tryBackslash(cursor)
2001
var line = cursor.line;
2002
var result = getMacroRange(line); // Look up and down for macro definition range
2005
dbg("macroRange:",result.range);
2006
dbg("maxLength:",result.max);
2007
// Iterate over macro definition, strip backslash
2008
// and add a padding string up to result.max length + backslash
2009
for (var i = result.range.start.line; i < result.range.end.line; ++i)
2011
var currentLineText = document.line(i);
2012
var originalTextLength = currentLineText.length;
2013
currentLineText = currentLineText.substring(0, currentLineText.length - 1).rtrim();
2014
var textLength = currentLineText.length;
2015
document.removeText(i, textLength, i, originalTextLength);
2016
document.insertText(i, textLength, String().fill(' ', result.max - textLength + 1) + "\\");
2022
* \brief Handle a <tt>@</tt> symbol
2024
* Possible user wants to add a Doxygen group
2026
function tryDoxygenGrouping(cursor)
2028
var line = cursor.line;
2029
var column = cursor.column;
2030
var firstColumn = document.firstColumn(line);
2031
// Check the symbol before the just entered
2032
var looks_like_doxgorup = isStringOrComment(line, column - 2)
2033
&& firstColumn == (column - 4)
2034
&& document.line(line).ltrim().startsWith("// ")
2036
if (looks_like_doxgorup)
2038
document.removeText(line, column - 2, line, column - 1);
2039
var padding = String().fill(' ', firstColumn);
2040
document.insertText(line, column - 1, "{\n" + padding + "\n" + padding + "//@}");
2041
view.setCursorPosition(line + 1, document.lineLength(line + 1));
2046
* \brief Handle quote character
2048
* Look back for \c 'R' char right before \c '"' and if
2049
* the next one (after \c '"') is not an alphanumeric,
2050
* then add a delimiters.
2052
* \attention Effect of AutoBrace extension has already neutralized at this point :)
2054
function tryStringLiteral(cursor, ch)
2056
var line = cursor.line;
2057
var column = cursor.column;
2059
if (isComment(line, column - 2)) // Do nothing for comments
2062
// First of all we have to determinate where we are:
2063
// 0) new string literal just started, or ...
2064
// 1) string literal just ends
2066
// Check if the '"' is a very first char on a line
2067
var new_string_just_started;
2069
// Yes, then we have to look to the last char of the previous line
2070
new_string_just_started = !(line != 0 && isString(line - 1, document.lastColumn(line - 1)));
2072
// Ok, just check attribute of the char right before '"'
2073
new_string_just_started = !isString(line, column - 2);
2075
// TODO Add a space after possible operator right before just
2076
// started string literal...
2077
if (new_string_just_started)
2079
// Is there anything after just entered '"'?
2080
var nc = document.charAt(line, column);
2081
var need_closing_quote = column == document.lineLength(line)
2082
|| document.isSpace(nc)
2083
|| nc == ',' // user tries to add new string param,
2084
|| nc == ')' // ... or one more param to the end of some call
2085
|| nc == ']' // ... or string literal as subscript index
2086
|| nc == ';' // ... or one more string before end of expression
2087
|| nc == '<' // ... or `some << "|<<`
2089
if (need_closing_quote)
2091
// Check for 'R' right before '"'
2092
if (ch == '"' && document.charAt(line, column - 2) == 'R')
2094
// Yeah, looks like a raw string literal
2095
/// \todo Make delimiter configurable... HOW?
2096
/// It would be nice if indenters can have a configuration page somehow...
2097
document.insertText(cursor, "~()~\"");
2098
view.setCursorPosition(line, column + 2);
2102
document.insertText(cursor, ch);
2103
view.setCursorPosition(line, column);
2110
* \brief Handle \c '!' char
2112
* Exclamation symbol can be a part of \c operator!= or unary operator.
2113
* in both cases, a space required before it! Except few cases:
2114
* - when it is at the line start
2115
* - when a char before it \c '(' -- i.e. argument of a control flow keyword (\c if, \c while)
2116
* or a function call parameter
2117
* - when a char before it \c '[' (array subscript)
2118
* - when a char before it \c '<' -- here is two cases possible:
2119
* - it is a first non-type template parameter (w/ type \c bool obviously)
2120
* - it is a part of less or shift operators.
2121
* To distinct last case, it is enough to check that a word before \c '<' (w/o space)
2123
* \note Yep, operators supposed to be separated from around text.
2125
function tryExclamation(cursor)
2127
var line = cursor.line;
2128
var column = cursor.column;
2130
if (column == 0) // Do nothing for very first char
2133
if (isStringOrComment(line, column - 1)) // Do nothing for comments and stings
2136
if (document.firstColumn(line) == column - 1) // Make sure '!' is not a first char on a line
2139
if (column < 2) // Do nothing if there is less than 2 chars before
2142
var pc = document.charAt(line, column - 2); // Do nothing if one of 'stop' chars:
2143
if (pc == ' ' || pc == '(' || pc == '[' || pc == '{')
2146
// And finally make sure it is not a part of 'relation operator'
2147
if (pc == '<' && column >= 3)
2149
// Make sure a char before is not a space or another '<'
2150
var ppc = document.charAt(line, column - 3);
2151
if (ppc != ' ' && ppc != '<')
2155
// Ok, if we r here, just insert a space ;)
2156
document.insertText(line, column - 1, " ");
2160
* \brief Handle a space
2162
* - add <tt>'()'</tt> pair after some keywords like: \c if, \c while, \c for, \c switch
2163
* - add <tt>';'</tt> if space pressed right after \c return, and no text after it
2164
* - if space pressed inside of angle brackets 'some<|>' transform into 'some < |'
2166
function tryKeywordsWithBrackets(cursor)
2168
var line = cursor.line;
2169
var column = cursor.column;
2170
var text = document.line(line).ltrim();
2171
var need_brackets = text == "if "
2172
|| text == "else if "
2175
|| text == "switch "
2180
document.insertText(cursor, "()");
2181
view.setCursorPosition(line, column + 1);
2183
else if (text == "return ")
2185
document.insertText(cursor, ";");
2186
view.setCursorPosition(line, column);
2188
else if (document.charAt(line, column - 2) == '<' && document.charAt(cursor) == '>')
2190
document.removeText(line, column, line, column + 1);
2191
document.insertText(line, column - 2, " ");
2196
* Try to add space before and after some equal operators.
2198
function tryEqualOperator(cursor)
2200
var line = cursor.line;
2201
var column = cursor.column;
2203
// Do nothing for comments or string literals or lines shorter than 2
2204
if (2 < column && isStringOrComment(line, column))
2207
var c = document.charAt(line, column - 2);
2208
dbg("tryEqualOperator: checking @Cursor("+line+","+(column - 2)+"), c='"+c+"'");
2212
// Two chars operators: !=, ==, ...
2221
addCharOrJumpOverIt(line, column, ' '); // Make sure there is a space after it!
2222
// Make sure there is a space before it!
2223
if (column >= 3 && document.charAt(line, column - 3) != ' ')
2224
document.insertText(line, column - 2, " ");
2226
case '(': // some(=|) --> some() =|
2227
cursor = tryJumpOverParenthesis(cursor);
2228
tryEqualOperator(cursor); // Call self again to handle "some()=|"
2230
case ')': // "some()=" or "(expr)=" --> ") =|"
2231
case '}': // It can be a ctor of some proxy object
2232
// Add a space between closing bracket and just entered '='
2233
document.insertText(line, column - 1, " ");
2234
addCharOrJumpOverIt(line, column + 1, ' '); // Make sure there is a space after it!
2237
// Shortcut: transfrom "some<=|>" -> "some <= |"
2238
if (document.charAt(cursor) == '>')
2239
document.removeText(line, column, line, column + 1);
2241
// This could be '<<=', '>>=', '<=', '>='
2242
// Make sure there is a space after it!
2243
addCharOrJumpOverIt(line, column, ' '); // Make sure there is a space after it!
2244
// Check if this is one of >>= or <<=
2247
if (document.charAt(line, column - 3) == c)
2249
if (column >= 4 && document.charAt(line, column - 4) != ' ')
2250
document.insertText(line, column - 3, " ");
2252
else if (document.charAt(line, column - 3) != ' ')
2255
document.insertText(line, column - 2, " ");
2259
case '[': // This could be a part of lambda capture [=]
2262
// Lookup one more character towards left
2265
var pc = document.charAt(line, column - 3);
2266
dbg("tryEqualOperator: checking @Cursor("+line+","+(column - 3)+"), pc='"+pc+"'");
2269
case '=': // Stick the current '=' to the previous char
2278
document.removeText(line, column - 1, line, column);
2279
document.insertText(line, column - 2, '=');
2288
addCharOrJumpOverIt(line, column, ' '); // Make sure there is a space after it!
2289
// Here is few things possible:
2290
// some+=| --> some += |
2291
// some++=| --> some++ = |
2292
// some+++=| --> some++ += |
2293
var space_offset = -1;
2296
if (document.charAt(line, column - 3) == c)
2300
if (document.charAt(line, column - 4) == c)
2302
else if (document.charAt(line, column - 4) != ' ')
2306
else if (document.charAt(line, column - 3) != ' ')
2309
if (space_offset != -1)
2310
document.insertText(line, column - space_offset, " ");
2313
dbg("tryEqualOperator: default");
2314
// '=' always surrounded by spaces!
2315
addCharOrJumpOverIt(line, column, ' '); // Make sure there is a space after it!
2316
document.insertText(line, column - 1, " "); // Make sure there is a space before it!
2322
* \brief Process one character
2324
* NOTE Cursor positioned right after just entered character and has +1 in column.
2326
* \attention This function will roll back the effect of \b AutoBrace extension
2327
* for quote chars. So this indenter can handle that chars withing predictable
2331
function processChar(line, ch)
2333
var result = -2; // By default, do nothing...
2334
var cursor = view.cursorPosition();
2338
// TODO Is there any `assert' in JS?
2339
if (line != cursor.line)
2341
dbg("ASSERTION FAILURE: line != cursor.line");
2345
document.editBegin();
2346
// Check if char under cursor is the same as just entered,
2347
// and if so, remove it... to make it behave like "overwrite" mode
2348
if (ch != ' ' && document.charAt(cursor) == ch)
2349
document.removeText(line, cursor.column, line, cursor.column + 1);
2354
result = caretPressed(cursor);
2357
trySameLineComment(cursor); // Possible user wants to start a comment
2360
result = tryTemplate(cursor); // Possible need to add closing '>' after template
2363
result = tryComma(cursor); // Possible need to align parameters list
2366
result = trySemicolon(cursor); // Possible `for ()` loop spread over lines
2373
result = tryOperator(cursor, ch); // Possible need to align some operator
2379
result = tryCloseBracket(cursor, ch); // Try to align a given close bracket
2382
result = tryBlock(cursor);
2385
result = tryPreprocessor(cursor);
2388
result = tryColon(cursor);
2391
tryOpenBrace(cursor); // Try to add a space after some keywords
2394
tryBackslash(cursor);
2397
tryDoxygenGrouping(cursor);
2401
tryStringLiteral(cursor, ch);
2403
case '!': // Almost all the time there should be a space before!
2404
tryExclamation(cursor);
2407
tryKeywordsWithBrackets(cursor);
2410
tryEqualOperator(cursor);
2414
tryAddSpaceAfterClosedBracketOrQuote(cursor);
2417
break; // Nothing to do...
2420
// Make sure it is not a pure comment line
2421
var currentLineText = document.line(cursor.line).ltrim();
2422
if (ch != '\n' && !currentLineText.startsWith("//"))
2424
// Ok, try to keep an inline comment aligned (if any)...
2425
// BUG If '=' was inserted (and a space added) in a code line w/ inline comment,
2426
// it seems kate do not update highlighting, so position, where comment was before,
2427
// still counted as a 'Comment' attribute, but actually it should be 'Normal Text'...
2428
// It is why adding '=' will not realign an inline comment...
2429
if (alignInlineComment(cursor.line) && ch == ' ')
2430
document.insertText(view.cursorPosition(), ' ');
2437
function alignPreprocessor(line)
2439
if (tryPreprocessor_ch(line) == -1) // Is smth happened?
2440
return -2; // No! Signal to upper level to try next aligner...
2441
return 0; // NOTE preprocessor directives always aligned to 0!
2445
* Try to find a next non comment line assuming that a given
2446
* one is a start or middle of a multi-line comment.
2448
* \attention This function would ignore anything else than
2449
* a simple comments like this one... I.e. if \b right after
2450
* star+slash starts anything (non comment, or even maybe after
2451
* that another one comment begins), it will be \b IGNORED.
2452
* (Just because this is a damn ugly style!)
2454
* \return line number or \c 0 if not found
2455
* \note \c 0 is impossible value, so suitable to indicate an error!
2457
* \sa \c alignInsideBraces()
2459
function findMultiLineCommentBlockEnd(line)
2461
for (; line < document.lines(); line++)
2463
var text = document.line(line).rtrim();
2464
if (text.endsWith("*/"))
2467
line++; // Move to *next* line
2468
if (line < document.lines())
2470
// Make sure it is not another one comment, and if so,
2471
// going to find it's end as well...
2472
var currentLineText = document.line(line).ltrim();
2473
if (currentLineText.startsWith("//"))
2474
line = findSingleLineCommentBlockEnd(line);
2475
else if (currentLineText.startsWith("/*"))
2476
line = findMultiLineCommentBlockEnd(line);
2478
else line = 0; // EOF found
2483
* Try to find a next non comment line assuming that a given
2484
* one is a single-line one
2486
* \return line number or \c 0 if not found
2487
* \note \c 0 is impossible value, so suitable to indicate an error!
2489
* \sa \c alignInsideBraces()
2491
function findSingleLineCommentBlockEnd(line)
2493
while (++line < document.lines())
2495
var text = document.line(line).ltrim();
2496
if (text.length == 0) continue; // Skip empty lines...
2497
if (!text.startsWith("//")) break; // Yeah! Smth was found finally.
2499
if (line < document.lines())
2501
var currentLineText = document.line(line).ltrim(); // Get text of the found line
2502
while (currentLineText.length == 0) // Skip empty lines if any
2503
currentLineText = document.line(++line).ltrim();
2504
// Make sure it is not another one multiline comment, and if so,
2505
// going to find it's end as well...
2506
if (currentLineText.startsWith("/*"))
2507
line = findMultiLineCommentBlockEnd(line);
2509
else line = 0; // EOF found
2514
* Almost anything in a code is placed withing some brackets.
2515
* So the ideas is simple:
2516
* \li find nearest open bracket of any kind
2517
* \li depending on its type and presence of leading delimiters (non identifier characters)
2518
* add one or half TAB relative a first non-space char of a line w/ found bracket.
2520
* But here is some details:
2521
* \li do nothing on empty lines
2522
* \li do nothing if first position is a \e string
2523
* \li align comments according next non-comment and non-preprocessor line
2524
* (i.e. it's desired indent cuz it maybe still unaligned)
2526
* \attention Current Kate version has a BUG: \c anchor() unable to find smth
2527
* in a multiline macro definition (i.e. where every line ends w/ a backslash)!
2529
function alignInsideBraces(line)
2531
// Make sure there is a text on a line, otherwise nothing to align here...
2532
var thisLineIndent = document.firstColumn(line);
2533
if (thisLineIndent == -1 || document.isString(line, 0))
2536
// Check for comment on the current line
2537
var currentLineText = document.line(line).ltrim();
2538
var nextNonCommentLine = -1;
2539
var middleOfMultilineBlock = false;
2540
var isSingleLineComment = false;
2541
if (currentLineText.startsWith('//')) // Is single line comment on this line?
2543
dbg("found a single-line comment");
2544
// Yep, go to find a next non-comment line...
2545
nextNonCommentLine = findSingleLineCommentBlockEnd(line);
2546
isSingleLineComment = true;
2548
else if (currentLineText.startsWith('/*')) // Is multiline comment starts on this line?
2550
// Yep, go to find a next non-comment line...
2551
dbg("found start of a multiline comment");
2552
nextNonCommentLine = findMultiLineCommentBlockEnd(line);
2554
// Are we already inside of a multiline comment?
2555
// NOTE To be sure that we are not inside of #if0/#endif block,
2556
// lets check that current line starts w/ '*' also!
2557
// NOTE Yep, it is expected (hardcoded) that multiline comment has
2558
// all lines started w/ a star symbol!
2559
// TODO BUG Kate has a bug: when multiline code snippet gets inserted into
2560
// a multiline comment block (like Doxygen's @code/@endcode)
2561
// document.isComment() returns true *only& for the first line of it!
2562
// So some other way needs to be found to indent comments properly...
2563
// TODO DAMN... it doesn't work that way also... for snippets longer than 2 lines.
2564
// I suppose kate first insert text, then indent it, and after that highlight it
2565
// So indenters based on a highlighting info will not work! BUT THEY DEFINITELY SHOULD!
2566
else if (currentLineText.startsWith("*") && document.isComment(line, 0))
2568
dbg("found middle of a multiline comment");
2569
// Yep, go to find a next non-comment line...
2570
nextNonCommentLine = findMultiLineCommentBlockEnd(line);
2571
middleOfMultilineBlock = true;
2574
dbg("document.isComment(line, 0)="+document.isComment(line, 0));
2575
//dbg("document.defStyleNum(line, 0)="+document.defStyleNum(line-1, 0));
2576
dbg("currentLineText='"+currentLineText+"'");
2577
dbg("middleOfMultilineBlock="+middleOfMultilineBlock);
2579
if (nextNonCommentLine == 0) // End of comment not found?
2580
// ... possible due temporary invalid code...
2581
// anyway, dunno how to align it!
2583
// So, are we inside a comment? (and we know where it ends)
2584
if (nextNonCommentLine != -1)
2586
// Yep, lets try to get desired indent for next non-comment line
2587
var desiredIndent = indentLine(nextNonCommentLine);
2588
if (desiredIndent < 0)
2590
// Have no idea how to indent this comment! So try to align it
2592
desiredIndent = document.firstColumn(nextNonCommentLine);
2594
// TODO Make sure that next non-comment line do not starts
2595
// w/ 'special' chars...
2596
return desiredIndent + (middleOfMultilineBlock|0);
2600
document.anchor(line, document.firstColumn(line), '(')
2601
, document.anchor(line, document.firstColumn(line), '{')
2602
, document.anchor(line, document.firstColumn(line), '[')
2604
dbg("Found open brackets @ "+brackets);
2606
// Check if we are at some brackets, otherwise do nothing
2607
var nearestBracket = brackets[brackets.length - 1];
2608
if (!nearestBracket.isValid())
2611
// Make sure it is not a `namespace' level
2612
// NOTE '{' brace should be at the same line w/ a 'namespace' keyword
2613
// (yep, according my style... :-)
2614
var bracketChar = document.charAt(nearestBracket);
2615
var parentLineText = document.line(nearestBracket.line).ltrim();
2616
if (bracketChar == '{' && parentLineText.startsWith("namespace"))
2619
// Ok, (re)align it!
2621
switch (bracketChar)
2626
// If current line has some leading delimiter, i.e. non alphanumeric character
2627
// add a half-TAB, otherwise add a one TAB... if needed!
2628
var parentIndent = document.firstColumn(nearestBracket.line);
2629
var openBraceIsFirst = parentIndent == nearestBracket.column;
2630
var firstChar = document.charAt(line, thisLineIndent);
2631
var isCloseBraceFirst = firstChar == ')' || firstChar == ']' || firstChar == '}';
2632
var doNotAddAnything = openBraceIsFirst && isCloseBraceFirst;
2633
var mustAddHalfTab = (!openBraceIsFirst && isCloseBraceFirst)
2639
var desiredIndent = parentIndent + (
2641
? (gIndentWidth / 2)
2642
: (doNotAddAnything ? 0 : gIndentWidth)
2644
result = desiredIndent; // Reassign a result w/ desired value!
2646
dbg("parentIndent="+parentIndent);
2647
dbg("openBraceIsFirst="+openBraceIsFirst);
2648
dbg("firstChar="+firstChar);
2649
dbg("isCloseBraceFirst="+isCloseBraceFirst);
2650
dbg("doNotAddAnything="+doNotAddAnything);
2651
dbg("mustAddHalfTab="+mustAddHalfTab);
2652
dbg("desiredIndent="+desiredIndent);
2656
dbg("Dunno how to align this line...");
2662
function alignAccessSpecifier(line)
2665
var currentLineText = document.line(line).ltrim();
2666
var match = currentLineText.search(
2667
/^\s*((public|protected|private)\s*(slots|Q_SLOTS)?|(signals|Q_SIGNALS)\s*):\s*$/
2671
// Ok, lets find an open brace of the `class'/`struct'
2672
var openBracePos = document.anchor(line, document.firstColumn(line), '{');
2673
if (openBracePos.isValid())
2674
result = document.firstColumn(openBracePos.line);
2680
* Try to align \c case statements in a \c switch
2682
function alignCase(line)
2685
var currentLineText = document.line(line).ltrim();
2686
if (currentLineText.startsWith("case ") || currentLineText.startsWith("default:"))
2688
// Ok, lets find an open brace of the `switch'
2689
var openBracePos = document.anchor(line, document.firstColumn(line), '{');
2690
if (openBracePos.isValid())
2691
result = document.firstColumn(openBracePos.line) + gIndentWidth;
2697
* Try to align \c break or \c continue statements in a loop or \c switch.
2699
* Also it take care about the following case:
2708
function alignBreakContinue(line)
2711
var currentLineText = document.line(line).ltrim();
2712
var is_break = currentLineText.startsWith("break;");
2713
var should_proceed = is_break || currentLineText.startsWith("continue;")
2715
result = tryBreakContinue(line - 1, is_break);
2720
* Try to align a given line
2721
* \todo More actions
2723
function indentLine(line)
2725
dbg(">> Going to indent line "+line);
2726
var result = alignPreprocessor(line); // Try to align a preprocessor directive
2727
if (result == -2) // Nothing has changed?
2728
result = alignAccessSpecifier(line); // Try to align access specifiers in a class
2729
if (result == -2) // Nothing has changed?
2730
result = alignCase(line); // Try to align `case' statements in a `switch'
2731
if (result == -2) // Nothing has changed?
2732
result = alignBreakContinue(line); // Try to align `break' or `continue' statements
2733
if (result == -2) // Nothing has changed?
2734
result = alignInsideBraces(line); // Try to align a generic line
2735
alignInlineComment(line); // Always try to align inline comments
2737
dbg("indentLine result="+result);
2739
if (result == -2) // Still dunno what to do?
2740
result = -1; // ... just align according a previous non empty line
2745
* \brief Process a newline or one of \c triggerCharacters character.
2747
* This function is called whenever the user hits \c ENTER key.
2749
* It gets three arguments: \c line, \c indentwidth in spaces and typed character
2751
* Called for each newline (<tt>ch == \n</tt>) and all characters specified in
2752
* the global variable \c triggerCharacters. When calling \e Tools->Align
2753
* the variable \c ch is empty, i.e. <tt>ch == ''</tt>.
2755
function indent(line, indentWidth, ch)
2757
// NOTE Update some global variables
2758
gIndentWidth = indentWidth;
2759
var crsr = view.cursorPosition();
2761
dbg("indentWidth: " + indentWidth);
2762
dbg(" Mode: " + document.highlightingModeAt(crsr));
2763
dbg(" Attribute: " + document.attributeName(crsr));
2764
dbg(" line: " + line);
2765
dbg(" char: " + crsr + " -> '" + ch + "'");
2768
return processChar(line, ch);
2770
return indentLine(line);
2774
* \todo Better to use \c defStyleNum() instead of \c attributeName() and string comparison
2776
* \todo Prevent second '//' on a line... ? Fix the current way anyway...
2779
// kate: space-indent on; indent-width 4; replace-tabs on;