File indexing completed on 2024-05-19 04:00:09
0001 var katescript = { 0002 "name": "C Style", 0003 "author": "Dominik Haumann <dhdev@gmx.de>, Milian Wolff <mail@milianw.de>", 0004 "license": "LGPL", 0005 "revision": 6, 0006 "kate-version": "5.1" 0007 }; // kate-script-header, must be at the start of the file without comments, pure json 0008 0009 /* 0010 This file is part of the Kate Project. 0011 0012 SPDX-License-Identifier: LGPL-2.0-only 0013 */ 0014 0015 // required katepart js libraries 0016 require ("range.js"); 0017 require ("string.js"); 0018 require ("utils.js"); 0019 0020 //BEGIN USER CONFIGURATION 0021 var cfgIndentCase = true; // indent 'case' and 'default' in a switch? 0022 var cfgIndentNamespace = true; // indent after 'namespace'? 0023 var cfgAutoInsertStar = true; // auto insert '*' in C-comments 0024 var cfgSnapSlash = true; // snap '/' to '*/' in C-comments 0025 var cfgAutoInsertSlashes = false; // auto insert '//' after C++-comments 0026 var cfgAccessModifiers = 0; // indent level of access modifiers, relative to the class indent level 0027 // set to -1 to disable auto-indendation after access modifiers. 0028 //END USER CONFIGURATION 0029 0030 // indent gets three arguments: line, indentwidth in spaces, typed character 0031 // indent 0032 0033 // specifies the characters which should trigger indent, beside the default '\n' 0034 triggerCharacters = "{})/:;#"; 0035 0036 var debugMode = false; 0037 0038 function dbg() { 0039 if (debugMode) { 0040 debug.apply(this, arguments); 0041 } 0042 } 0043 0044 const multiLineStringLiteralStartRegex = /R"(.+)?\(/ 0045 0046 //BEGIN global variables and functions 0047 // maximum number of lines we look backwards/forward to find out the indentation 0048 // level (the bigger the number, the longer might be the delay) 0049 var gLineDelimiter = 50; // number 0050 0051 var gIndentWidth = 4; 0052 var gMode = "C"; 0053 //END global variables and functions 0054 0055 0056 /** 0057 * Search for a corresponding '{' and return its indentation level. If not found 0058 * return null; (line/column) are the start of the search. 0059 */ 0060 function findLeftBrace(line, column) 0061 { 0062 var cursor = document.anchor(line, column, '{'); 0063 if (cursor.isValid()) { 0064 var parenthesisCursor = tryParenthesisBeforeBrace(cursor.line, cursor.column); 0065 if (parenthesisCursor.isValid()) 0066 cursor = parenthesisCursor; 0067 0068 dbg("findLeftBrace: success in line " + cursor.line); 0069 return document.firstVirtualColumn(cursor.line); 0070 } 0071 0072 return -1; 0073 } 0074 0075 /** 0076 * Find last non-empty line that is not inside a comment or preprocessor 0077 */ 0078 function lastNonEmptyLine(line) 0079 { 0080 while (true) { 0081 line = document.prevNonEmptyLine(line); 0082 if ( line == -1 ) { 0083 return -1; 0084 } 0085 var string = document.line(line).ltrim(); 0086 ///TODO: cpp multiline comments 0087 ///TODO: multiline macros 0088 if ( string.startsWith("//") || string.startsWith('#') ) { 0089 --line; 0090 continue; 0091 } 0092 break; 0093 } 0094 0095 return line; 0096 } 0097 0098 /** 0099 * Character at (line, column) has to be a '{'. 0100 * Now try to find the right line for indentation for constructs like: 0101 * if (a == b 0102 * && c == d) { <- check for ')', and find '(', then return its indentation 0103 * Returns null, if no success, otherwise an object {line, column} 0104 */ 0105 function tryParenthesisBeforeBrace(line, column) 0106 { 0107 var firstColumn = document.firstColumn(line); 0108 while (column > firstColumn && document.isSpace(line, --column)); 0109 if (document.charAt(line, column) == ')') 0110 return document.anchor(line, column, '('); 0111 return Cursor.invalid(); 0112 } 0113 0114 /** 0115 * Check for default and case keywords and assume we are in a switch statement. 0116 * Try to find a previous default, case or switch and return its indentation or 0117 * -1 if not found. 0118 */ 0119 function trySwitchStatement(line) 0120 { 0121 var currentString = document.line(line); 0122 if (currentString.search(/^\s*(default\s*|case\b.*):/) == -1) 0123 return -1; 0124 0125 var indentation = -1; 0126 var lineDelimiter = gLineDelimiter; 0127 var currentLine = line; 0128 0129 while (currentLine > 0 && lineDelimiter > 0) { 0130 --currentLine; 0131 --lineDelimiter; 0132 if (document.firstColumn(currentLine) == -1) 0133 continue; 0134 0135 currentString = document.line(currentLine); 0136 if (currentString.search(/^\s*(default\s*|case\b.*):/) != -1) { 0137 indentation = document.firstVirtualColumn(currentLine); 0138 break; 0139 } else if (currentString.search(/^\s*switch\b/) != -1) { 0140 indentation = document.firstVirtualColumn(currentLine); 0141 if (cfgIndentCase) 0142 indentation += gIndentWidth; 0143 break; 0144 } 0145 } 0146 0147 if (indentation != -1) dbg("trySwitchStatement: success in line " + currentLine); 0148 return indentation; 0149 } 0150 0151 /** 0152 * Check for private, protected, public, signals etc... and assume we are in a 0153 * class definition. Try to find a previous private/protected/private... or 0154 * class and return its indentation or null if not found. 0155 */ 0156 function tryAccessModifiers(line) 0157 { 0158 if (cfgAccessModifiers < 0) 0159 return -1; 0160 0161 var currentString = document.line(line); 0162 if (currentString.search(/^\s*((public|protected|private)\s*(slots|Q_SLOTS)?|(signals|Q_SIGNALS)\s*):\s*$/) == -1) 0163 return -1; 0164 0165 var cursor = document.anchor(line, 0, '{'); 0166 if (!cursor.isValid()) 0167 return -1; 0168 0169 var indentation = document.firstVirtualColumn(cursor.line); 0170 for ( var i = 0; i < cfgAccessModifiers; ++i ) { 0171 indentation += gIndentWidth; 0172 } 0173 0174 if (indentation != -1) dbg("tryAccessModifiers: success in line " + cursor.line); 0175 return indentation; 0176 } 0177 0178 0179 0180 /** 0181 * Get the position of the first non-space character 0182 * return: position or -1, if there is no non-space character 0183 */ 0184 function firstNonSpace(text) 0185 { 0186 if (text && text.search(/^(\s*)\S/) != -1) 0187 return RegExp.$1.length; 0188 0189 return -1; 0190 } 0191 0192 /** 0193 * Get the position of the last non-space character 0194 * return: position or -1, if there is no non-space character 0195 */ 0196 function lastNonSpace(text) 0197 { 0198 if (text && text.search(/(.*)\S\s*$/) != -1) 0199 return RegExp.$1.length; 0200 0201 return -1; 0202 } 0203 0204 /** 0205 * Don't indent when in a multiline string literal of form "something\ 0206 nextline" 0207 * Note: the function does not check for comments 0208 */ 0209 function tryString(line) 0210 { 0211 var currentLine = line; 0212 var currentString; 0213 0214 // go line up as long as the previous line ends with an escape character '\' 0215 while (currentLine >= 0) { 0216 currentString = document.line(currentLine - 1); 0217 if (currentString.charAt(document.lastColumn(currentLine - 1)) != '\\') 0218 break; 0219 --currentLine; 0220 } 0221 0222 // iterate through all lines and toggle bool insideString for every quote 0223 var insideString = false; 0224 var indentation = -1; 0225 while (currentLine < line) { 0226 currentString = document.line(currentLine); 0227 var char1; 0228 var i; 0229 var lineLength = document.lineLength(currentLine); 0230 for (i = 0; i < lineLength; ++i) { 0231 char1 = currentString.charAt(i); 0232 if (char1 == "\\") { 0233 // skip escaped character 0234 ++i; 0235 } else if (char1 == "\"") { 0236 insideString = !insideString; 0237 } 0238 } 0239 ++currentLine; 0240 } 0241 0242 return insideString ? 0 : -1; 0243 } 0244 0245 /** 0246 * C comment checking. If the previous line begins with a "/*" or a "* ", then 0247 * return its leading white spaces + ' *' + the white spaces after the * 0248 * return: filler string or null, if not in a C comment 0249 */ 0250 function tryCComment(line) 0251 { 0252 var currentLine = document.prevNonEmptyLine(line - 1); 0253 if (currentLine < 0) 0254 return -1; 0255 0256 var indentation = -1; 0257 0258 // we found a */, search the opening /* and return its indentation level 0259 if (document.endsWith(currentLine, "*/", true)) { 0260 var cursor = document.rfind(currentLine, document.lastColumn(currentLine), "/*"); 0261 if (cursor.isValid() && cursor.column == document.firstColumn(cursor.line)) 0262 indentation = document.firstVirtualColumn(cursor.line); 0263 0264 if (indentation != -1) dbg("tryCComment: success (1) in line " + cursor.line); 0265 return indentation; 0266 } 0267 0268 // inbetween was an empty line, so do not copy the "*" character 0269 if (currentLine != line - 1) 0270 return -1; 0271 0272 var firstPos = document.firstColumn(currentLine); 0273 var lastPos = document.lastColumn(currentLine); 0274 var char1 = document.charAt(currentLine, firstPos); 0275 var char2 = document.charAt(currentLine, firstPos + 1); 0276 var currentString = document.line(currentLine); 0277 0278 if (char1 == '/' && char2 == '*' && !currentString.contains("*/")) { 0279 indentation = document.firstVirtualColumn(currentLine); 0280 if (cfgAutoInsertStar) { 0281 // only add '*', if there is none yet. 0282 indentation += 1; 0283 if (document.firstChar(line) != '*') 0284 document.insertText(line, view.cursorPosition().column, '*'); 0285 if (!document.isSpace(line, document.firstColumn(line) + 1) && !document.endsWith(line, "*/", true)) 0286 document.insertText(line, document.firstColumn(line) + 1, ' '); 0287 } 0288 } else if (char1 == '*') { 0289 var commentLine = currentLine; 0290 while (commentLine >= 0 && document.firstChar(commentLine) == '*' 0291 && !document.endsWith(commentLine, "*/", true) 0292 ) { 0293 --commentLine; 0294 } 0295 if (commentLine < 0) { 0296 indentation = document.firstVirtualColumn(currentLine); 0297 } else if (document.startsWith(commentLine, "/*", true) && !document.endsWith(commentLine, "*/", true)) { 0298 // found a /*, and all succeeding lines start with a *, so it's a comment block 0299 indentation = document.firstVirtualColumn(currentLine); 0300 0301 // only add '*', if there is none yet. 0302 if (cfgAutoInsertStar && document.firstChar(line) != '*') { 0303 document.insertText(line, view.cursorPosition().column, '*'); 0304 if (!document.isSpace(line, document.firstColumn(line) + 1)) 0305 document.insertText(line, document.firstColumn(line) + 1, ' '); 0306 } 0307 } 0308 } 0309 0310 if (indentation != -1) dbg("tryCComment: success (2) in line " + currentLine); 0311 return indentation; 0312 } 0313 0314 /** 0315 * C++ comment checking. when we want to insert slashes: 0316 * //, ///, //! ///<, //!< and ////... 0317 * return: filler string or null, if not in a star comment 0318 * NOTE: otherwise comments get skipped generally and we use the last code-line 0319 */ 0320 function tryCppComment(line) 0321 { 0322 var currentLine = line - 1; 0323 if (currentLine < 0 || !cfgAutoInsertSlashes) 0324 return -1; 0325 0326 var indentation = -1; 0327 var comment = document.startsWith(currentLine, "//", true); 0328 0329 // allowed are: //, ///, //! ///<, //!< and ////... 0330 if (comment) { 0331 var firstPos = document.firstColumn(currentLine); 0332 var currentString = document.line(currentLine); 0333 0334 var char3 = currentString.charAt(firstPos + 2); 0335 var char4 = currentString.charAt(firstPos + 3); 0336 indentation = document.firstVirtualColumn(currentLine); 0337 0338 if (cfgAutoInsertSlashes) { 0339 if (char3 == '/' && char4 == '/') { 0340 // match ////... and replace by only two: // 0341 currentString.search(/^\s*(\/\/)/); 0342 } else if (char3 == '/' || char3 == '!') { 0343 // match ///, //!, ///< and //! 0344 currentString.search(/^\s*(\/\/[\/!][<]?\s*)/); 0345 } else { 0346 // only //, nothing else 0347 currentString.search(/^\s*(\/\/\s*)/); 0348 } 0349 document.insertText(line, view.cursorPosition().column, RegExp.$1); 0350 } 0351 } 0352 0353 if (indentation != -1) dbg("tryCppComment: success in line " + currentLine); 0354 return indentation; 0355 } 0356 0357 /** 0358 * If the last non-empty line ends with a { or [, take its indentation level (or 0359 * maybe the one found by tryParenthesisBeforeBrace()) and return it increased 0360 * by 1 indetation level (special case: namespaces indentation depends on 0361 * cfgIndentNamespace). If not found, return null. 0362 */ 0363 function tryBrace(line) 0364 { 0365 function isNamespace(line, column) 0366 { 0367 if (document.firstColumn(line) == column && line > 0) 0368 --line; 0369 var currentString = document.line(line); 0370 return (currentString.search(/^\s*namespace\b/) != -1); 0371 } 0372 0373 var currentLine = lastNonEmptyLine(line - 1); 0374 if (currentLine < 0) 0375 return -1; 0376 0377 var lastPos = document.lastColumn(currentLine); 0378 var indentation = -1; 0379 0380 var currentString = document.line(currentLine); 0381 var matchColumn = currentString.search(/\{[^\}]*$/); 0382 // line ends with [, e.g., array in js or dart 0383 if (matchColumn == -1 && currentString.endsWith("[")) { 0384 matchColumn = currentString.length - 1; 0385 } 0386 0387 if (matchColumn != -1 && document.isCode(currentLine, matchColumn)) { 0388 dbg("tryBrace: Closing bracket in line " + currentLine); 0389 var cursor = tryParenthesisBeforeBrace(currentLine, lastPos); 0390 if (cursor.isValid()) { 0391 indentation = document.firstVirtualColumn(cursor.line) + gIndentWidth; 0392 } else { 0393 indentation = document.firstVirtualColumn(currentLine); 0394 if (cfgIndentNamespace || !isNamespace(currentLine, lastPos)) 0395 // take its indentation and add one indentation level 0396 indentation += gIndentWidth; 0397 } 0398 } 0399 0400 if (indentation != -1) dbg("tryBrace: success in line " + currentLine); 0401 return indentation; 0402 } 0403 0404 /** 0405 * Check for if, else, while, do, switch, private, public, protected, signals, 0406 * default, case etc... keywords, as we want to indent then. If is 0407 * non-null/true, then indentation is not increased. 0408 * Note: The code is written to be called *after* tryCComment and tryCppComment! 0409 */ 0410 function tryCKeywords(line, isBrace) 0411 { 0412 var currentLine = lastNonEmptyLine(line - 1); 0413 if (currentLine < 0) 0414 return -1; 0415 0416 // if line ends with ')', find the '(' and check this line then. 0417 var lastPos = document.lastColumn(currentLine); 0418 var cursor = Cursor.invalid(); 0419 if (document.charAt(currentLine, lastPos) == ')') 0420 cursor = document.anchor(currentLine, lastPos, '('); 0421 if (cursor.isValid()) 0422 currentLine = cursor.line; 0423 0424 // found non-empty line 0425 var currentString = document.line(currentLine); 0426 if (currentString.search(/^\s*(if\b|for(each)?|do\b|while|switch|[}]?\s*else(if)?|((private|public|protected|case|default|signals|Q_SIGNALS).*:))/) == -1) 0427 return -1; 0428 dbg("Found first word: " + RegExp.$1); 0429 lastPos = document.lastColumn(currentLine); 0430 var lastChar = currentString.charAt(lastPos); 0431 var indentation = -1; 0432 0433 // ignore trailing comments see: https://bugs.kde.org/show_bug.cgi?id=189339 0434 var commentPos = currentString.indexOf("//") 0435 if (commentPos != -1) { 0436 currentString = currentString.substring(0, commentPos).rtrim(); 0437 lastChar = currentString.charAt(currentString.length - 1); 0438 } 0439 0440 // try to ignore lines like: if (a) b; or if (a) { b; } 0441 if (lastChar != ';' && lastChar != '}') { 0442 // take its indentation and add one indentation level 0443 indentation = document.firstVirtualColumn(currentLine); 0444 if (!isBrace) 0445 indentation += gIndentWidth; 0446 } else if (lastChar == ';') { 0447 // stuff like: 0448 // for(int b; 0449 // b < 10; 0450 // --b) 0451 cursor = document.anchor(currentLine, lastPos, '('); 0452 if (cursor.isValid()) { 0453 // Same line, we know there is a keyword here 0454 if (cursor.line == line) { 0455 indentation = document.toVirtualColumn(cursor.line, cursor.column + 1); 0456 } else { 0457 // check that the returned cursor's line contains 0458 // a keyword and isn't a func call. 0459 var cursorLine = document.line(cursor.line); 0460 var s = cursorLine.search(/^\s*(if\b|for(each)?|do\b|while|switch|[}]?\s*else(if)?)/); 0461 if (s != -1) { 0462 indentation = document.toVirtualColumn(cursor.line, cursor.column + 1); 0463 } 0464 } 0465 } 0466 } 0467 0468 if (indentation != -1) dbg("tryCKeywords: success in line " + currentLine); 0469 return indentation; 0470 } 0471 0472 /** 0473 * Is this line a single statement of a condition: 0474 * if (true) 0475 * doWork(); 0476 * - Finds the previous non empty line 0477 * - Checks if it has control keyword 0478 * - Checks if it doesn't have brace 0479 */ 0480 function isSingleStmtCondition(line) 0481 { 0482 if (line > 1) { 0483 var prev = lastNonEmptyLine(line - 1); 0484 var prevText = document.line(prev); 0485 var hasKeyword = prevText.search(/^\s*(if\b|[}]?\s*else(if)?\b|do\b|while\b|for(each)?\b)/); 0486 if (hasKeyword != -1 && !prevText.contains("{")) { 0487 return true; 0488 } 0489 } 0490 return false; 0491 } 0492 0493 /** 0494 * Search for if, do, while, for, ... as we want to indent then. 0495 * Return null, if nothing useful found. 0496 * Note: The code is written to be called *after* tryCComment and tryCppComment! 0497 */ 0498 function tryCondition(line) 0499 { 0500 var currentLine = lastNonEmptyLine(line - 1); 0501 if (currentLine < 0) 0502 return -1; 0503 0504 // found non-empty line 0505 var currentString = document.line(currentLine); 0506 var lastPos = document.lastColumn(currentLine); 0507 var lastChar = currentString.charAt(lastPos); 0508 var indentation = -1; 0509 0510 if (lastChar == ';' 0511 && currentString.search(/^\s*(if\b|[}]?\s*else(if)?\b|do\b|while\b|for(each)?\b)/) == -1) 0512 { 0513 // idea: we had something like: 0514 // if/while/for (expression) 0515 // statement(); <-- we catch this trailing ';' 0516 // Now, look for a line that starts with if/for/while, that has one 0517 // indent level less. 0518 var currentIndentation = document.firstVirtualColumn(currentLine); 0519 if (currentIndentation == 0) 0520 return -1; 0521 0522 // Is this a condition with only one stmt in it 0523 var singleStmt = isSingleStmtCondition(currentLine); 0524 if (singleStmt) { 0525 var leftBrace = findLeftBrace(currentLine, 0); 0526 if (leftBrace != -1) { 0527 indentation = leftBrace + gIndentWidth; 0528 dbg("tryCondition(1): success in line " + currentLine); 0529 return indentation; 0530 } 0531 } 0532 0533 0534 var lineDelimiter = 10; // 10 limit search, hope this is a sane value 0535 while (currentLine > 0 && lineDelimiter > 0) { 0536 --currentLine; 0537 --lineDelimiter; 0538 var firstPosVirtual = document.firstVirtualColumn(currentLine); 0539 if (firstPosVirtual == -1) 0540 continue; 0541 0542 if (firstPosVirtual < currentIndentation) { 0543 currentString = document.line(currentLine); 0544 if (currentString.search(/^\s*(if\b|[}]?\s*else(if)?|do\b|while\b|for(each)?)[^{]*$/) != -1) 0545 indentation = firstPosVirtual; 0546 break; 0547 } else if (currentLine == 0 || lineDelimiter == 0) { 0548 return indentation; 0549 } 0550 } 0551 } 0552 0553 if (indentation != -1) dbg("tryCondition(2): success in line " + currentLine); 0554 return indentation; 0555 } 0556 0557 /** 0558 * If the non-empty line ends with ); or ',', then search for '(' and return its 0559 * indentation; also try to ignore trailing comments. 0560 */ 0561 function tryStatement(line) 0562 { 0563 var currentLine = lastNonEmptyLine(line - 1); 0564 if (currentLine < 0) 0565 return -1; 0566 0567 var indentation = -1; 0568 var currentString = document.line(currentLine); 0569 if (currentString.endsWith('(')) { 0570 // increase indent level 0571 indentation = document.firstVirtualColumn(currentLine) + gIndentWidth; 0572 dbg("tryStatement (1): success in line " + currentLine); 0573 return indentation; 0574 } 0575 var alignOnSingleQuote = gMode == "PHP/PHP" || gMode == "JavaScript"; 0576 // align on strings "..."\n => below the opening quote 0577 // multi-language support: [\.+] for javascript or php 0578 var result = /^(.*)(,|"|'|\))(;?)\s*[\.+]?\s*(\/\/.*|\/\*.*\*\/\s*)?$/.exec(currentString); 0579 if (result != null && result.index == 0) { 0580 var alignOnAnchor = result[3].length == 0 && result[2] != ')'; 0581 // search for opening ", ' or ( 0582 var cursor = Cursor.invalid(); 0583 if (result[2] == '"' || (alignOnSingleQuote && result[2] == "'")) { 0584 0585 while(true) { 0586 var i = result[1].length - 1; // start from matched closing ' or " 0587 // find string opener 0588 for ( ; i >= 0; --i ) { 0589 // make sure it's not commented out 0590 if (currentString[i] == result[2] && (i == 0 || currentString[i - 1] != '\\')) { 0591 // also make sure that this is not a line like '#include "..."' <-- we don't want to indent here 0592 if (currentString.match(/^#include/) || currentString.match(/'use strict'/)) { 0593 return indentation; 0594 } 0595 cursor = new Cursor(currentLine, i); 0596 break; 0597 } 0598 } 0599 if (!alignOnAnchor && currentLine) { 0600 // when we finished the statement (;) we need to get the first line and use it's indentation 0601 // i.e.: $foo = "asdf"; -> align on $ 0602 --i; // skip " or ' 0603 // skip whitespaces and stuff like + or . (for PHP, JavaScript, ...) 0604 for ( ; i >= 0; --i ) { 0605 if (currentString[i] == ' ' || currentString[i] == "\t" 0606 || currentString[i] == '.' || currentString[i] == '+') 0607 { 0608 continue; 0609 } else { 0610 break; 0611 } 0612 } 0613 if ( i > 0 ) { 0614 // there's something in this line, use it's indentation 0615 break; 0616 } else { 0617 // go to previous line 0618 --currentLine; 0619 currentString = document.line(currentLine); 0620 } 0621 } else { 0622 break; 0623 } 0624 } 0625 } else if (result[2] == ',' && !currentString.match(/\(/)) { 0626 // assume a function call: check for '(' brace 0627 // - if not found, use previous indentation 0628 // - if found, compare the indentation depth of current line and open brace line 0629 // - if current indentation depth is smaller, use that 0630 // - otherwise, use the '(' indentation + following white spaces 0631 var currentIndentation = document.firstVirtualColumn(currentLine); 0632 var braceCursor = document.anchor(currentLine, result[1].length, '('); 0633 0634 if (!braceCursor.isValid() || currentIndentation < braceCursor.column) 0635 indentation = currentIndentation; 0636 else { 0637 indentation = braceCursor.column + 1; 0638 while (document.isSpace(braceCursor.line, indentation)) 0639 ++indentation; 0640 } 0641 } else if (gMode == "Dart" && result[2] == ',' && currentString.contains("(") && currentString.contains(")")) { 0642 // Dart has a lot of stuff and function calls inside ctors 0643 // and code is not aligned at the opening paren hence: 0644 // do nothing 0645 } else { 0646 cursor = document.anchor(currentLine, result[1].length, '('); 0647 } 0648 if (cursor.isValid()) { 0649 if (alignOnAnchor) { 0650 currentLine = cursor.line; 0651 var column = cursor.column; 0652 var inc = 0; 0653 if (result[2] != '"' && result[2] != "'") { 0654 // place one column after the opening parens 0655 column++; 0656 inc = 1; 0657 } 0658 var lastColumn = document.lastColumn(currentLine); 0659 while (column < lastColumn && document.isSpace(currentLine, column)) { 0660 ++column; 0661 inc = 1; 0662 } 0663 if (inc > 0) 0664 indentation = document.toVirtualColumn(currentLine, column); 0665 else 0666 indentation = document.firstVirtualColumn(currentLine); 0667 } else { 0668 currentLine = cursor.line; 0669 indentation = document.firstVirtualColumn(currentLine); 0670 } 0671 } 0672 } else if ( currentString.rtrim().endsWith(';') ) { 0673 indentation = document.firstVirtualColumn(currentLine); 0674 } 0675 0676 if (indentation != -1) dbg("tryStatement (2): success in line " + currentLine); 0677 return indentation; 0678 } 0679 0680 /** 0681 * find out whether we pressed return in something like {} or () or [] and indent properly: 0682 * {} 0683 * becomes: 0684 * { 0685 * | 0686 * } 0687 */ 0688 function tryMatchedAnchor(line, alignOnly) 0689 { 0690 var char = document.firstChar(line); 0691 if ( char != '}' && char != ')' && char != ']' ) { 0692 return -1; 0693 } 0694 // we pressed enter in e.g. () 0695 var closingAnchor = document.anchor(line, 0, document.firstChar(line)); 0696 if (!closingAnchor.isValid()) { 0697 // nothing found, continue with other cases 0698 return -1; 0699 } 0700 if (alignOnly) { 0701 // when aligning only, don't be too smart and just take the indent level of the open anchor 0702 return document.firstVirtualColumn(closingAnchor.line); 0703 } 0704 var lastChar = document.lastChar(line - 1); 0705 var charsMatch = ( lastChar == '(' && char == ')' ) || 0706 ( lastChar == '{' && char == '}' ) || 0707 ( lastChar == '[' && char == ']' ); 0708 var indentLine = -1; 0709 var indentation = -1; 0710 if ( !charsMatch && char != '}' ) { 0711 // otherwise check whether the last line has the expected 0712 // indentation, if not use it instead and place the closing 0713 // anchor on the level of the openeing anchor 0714 var expectedIndentation = document.firstVirtualColumn(closingAnchor.line) + gIndentWidth; 0715 var actualIndentation = document.firstVirtualColumn(line - 1); 0716 var indentation = -1; 0717 if ( expectedIndentation <= actualIndentation ) { 0718 if ( lastChar == ',' && char != ')' ) { 0719 // use indentation of last line instead and place closing anchor 0720 // in same column of the openeing anchor 0721 document.insertText(line, document.firstColumn(line), "\n"); 0722 view.setCursorPosition(line, actualIndentation); 0723 // indent closing anchor 0724 document.indent(new Range(line + 1, 0, line + 1, 1), document.toVirtualColumn(closingAnchor) / gIndentWidth); 0725 // make sure we add spaces to align perfectly on closing anchor 0726 var padding = document.toVirtualColumn(closingAnchor) % gIndentWidth; 0727 if ( padding > 0 ) { 0728 document.insertText(line + 1, document.column - padding, String().fill(' ', padding)); 0729 } 0730 indentation = actualIndentation; 0731 } else if ( expectedIndentation == actualIndentation ) { 0732 // otherwise don't add a new line, just use indentation of closing anchor line 0733 indentation = document.firstVirtualColumn(closingAnchor.line); 0734 } else { 0735 // otherwise don't add a new line, just align on closing anchor 0736 indentation = document.toVirtualColumn(closingAnchor) + 1; 0737 } 0738 dbg("tryMatchedAnchor: success in line " + closingAnchor.line); 0739 return indentation; 0740 } else if ( lastChar == ',') { 0741 dbg("tryMatchedAnchor: success in line " + closingAnchor.line); 0742 return document.toVirtualColumn(closingAnchor) + 1; 0743 } 0744 } 0745 0746 // otherwise we i.e. pressed enter between (), [] or when we enter before curly brace 0747 // increase indentation and place closing anchor on the next line 0748 indentation = document.firstVirtualColumn(closingAnchor.line); 0749 document.insertText(line, document.firstColumn(line), "\n"); 0750 view.setCursorPosition(line, indentation); 0751 // indent closing brace 0752 document.indent(new Range(line + 1, 0, line + 1, 1), indentation / gIndentWidth); 0753 dbg("tryMatchedAnchor: success in line " + closingAnchor.line); 0754 return indentation + gIndentWidth; 0755 } 0756 0757 function escapeForRegex(string) { 0758 if (string !== null && string != undefined) { 0759 return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); 0760 } 0761 return string; 0762 } 0763 0764 /** 0765 * Don't indent on multiline string literals of form R"[optional delimiter text]()[optional delimiter text]" 0766 */ 0767 function tryMultiLineStringLiteral(line) { 0768 // The algorithm in this function is only valid for C++, don't waste time for other languages 0769 if (!gMode.contains("C++")) { 0770 return -1; 0771 } 0772 var currentLine = line; 0773 var currentString; 0774 var delim = ""; 0775 var found = false; 0776 0777 var limit = 0; // ensure we don't search the whole document all the time 0778 // go line up as long as the previous line ends with an escape character '\' 0779 // go line up until we find something of form R"[delim]( 0780 while (currentLine >= 0) { 0781 currentString = document.line(currentLine - 1); 0782 var startMatch = currentString.match(multiLineStringLiteralStartRegex); 0783 if (startMatch !== null) { 0784 found = true; 0785 delim = startMatch[1] != undefined ? startMatch[1] : ""; 0786 currentLine--; 0787 break; 0788 } 0789 --currentLine; 0790 if (limit >= 25) { 0791 break; 0792 } 0793 limit++; 0794 } 0795 0796 if (!found) { 0797 return -1; 0798 } 0799 0800 // iterate through all lines and toggle bool insideString for every quote 0801 var endReached = false; 0802 const multiLineStringLiteralEndRegex = delim.length != 0 ? new RegExp('\\)' + escapeForRegex(delim) + '"') : new RegExp("\)\""); 0803 while (currentLine < line) { 0804 currentString = document.line(currentLine); 0805 var endMatch = currentString.match(multiLineStringLiteralEndRegex); 0806 if (endMatch !== null) { 0807 dbg("End reached: " + currentString); 0808 endReached = true; 0809 break; 0810 } 0811 ++currentLine; 0812 } 0813 0814 return endReached ? -1 : document.firstVirtualColumn(line); 0815 } 0816 0817 /** 0818 * Indent line. 0819 * Return filler or null. 0820 */ 0821 function indentLine(line, alignOnly) 0822 { 0823 var firstChar = document.firstChar(line); 0824 var lastChar = document.lastChar(line); 0825 0826 var filler = -1; 0827 0828 if (filler == -1) 0829 filler = tryMatchedAnchor(line, alignOnly); 0830 if (filler == -1) 0831 filler = tryCComment(line); 0832 if (filler == -1 && !alignOnly) 0833 filler = tryCppComment(line); 0834 if (filler == -1) 0835 filler = tryMultiLineStringLiteral(line); 0836 if (filler == -1) 0837 filler = tryString(line); 0838 if (filler == -1) 0839 filler = trySwitchStatement(line); 0840 if (filler == -1) 0841 filler = tryAccessModifiers(line); 0842 if (filler == -1) 0843 filler = tryBrace(line); 0844 if (filler == -1) 0845 filler = tryCKeywords(line, firstChar == '{'); 0846 if (filler == -1) 0847 filler = tryCondition(line); 0848 if (filler == -1) 0849 filler = tryStatement(line); 0850 0851 return filler; 0852 } 0853 0854 function processChar(line, c) 0855 { 0856 if (c == ';' || !triggerCharacters.contains(c)) 0857 return -2; 0858 0859 var cursor = view.cursorPosition(); 0860 if (!cursor) 0861 return -2; 0862 0863 var column = cursor.column; 0864 var firstPos = document.firstColumn(line); 0865 var prevFirstPos = document.firstColumn(line - 1); 0866 var lastPos = document.lastColumn(line); 0867 0868 dbg("firstPos: " + firstPos); 0869 dbg("column..: " + column); 0870 dbg("char : " + c); 0871 0872 if (firstPos == column - 1 && c == '{') { 0873 // todo: maybe look for if etc. 0874 var filler = tryBrace(line); 0875 if (filler == -1) 0876 filler = tryCKeywords(line, true); 0877 if (filler == -1) 0878 filler = tryCComment(line); // checks, whether we had a "*/" 0879 if (filler == -1) 0880 filler = tryStatement(line); 0881 if (filler == -1) 0882 filler = -2; 0883 0884 return filler; 0885 } else if (firstPos == column - 1 && c == '}' && document.charAt(line, column - 1) == '}') { 0886 // unindent after closing brace, but not when brace is auto inserted (i.e., behind cursor) 0887 var indentation = findLeftBrace(line, firstPos); 0888 if (indentation == -1) 0889 indentation = -2; 0890 return indentation; 0891 } else if (firstPos == column - 1 && c == '}' && firstPos > prevFirstPos) { 0892 // align indentation to previous line when creating new block with auto brackets enabled 0893 // prevents over-indentation for if blocks and loops 0894 return document.toVirtualColumn(line - 1, prevFirstPos); 0895 } else if (cfgSnapSlash && c == '/' && lastPos == column - 1) { 0896 // try to snap the string "* /" to "*/" 0897 var currentString = document.line(line); 0898 if (/^(\s*)\*\s+\/\s*$/.test(currentString)) { 0899 currentString = RegExp.$1 + "*/"; 0900 document.editBegin(); 0901 document.removeLine(line); 0902 document.insertLine(line, currentString); 0903 view.setCursorPosition(line, currentString.length); 0904 document.editEnd(); 0905 } 0906 return -2; 0907 } else if (c == ':') { 0908 // todo: handle case, default, signals, private, public, protected, Q_SIGNALS 0909 var filler = trySwitchStatement(line); 0910 if (filler == -1) 0911 filler = tryAccessModifiers(line); 0912 if (filler == -1) 0913 filler = -2; 0914 return filler; 0915 } else if (c == ')' && firstPos == column - 1) { 0916 // align on start of identifier of function call 0917 var openParen = document.anchor(line, column - 1, '('); 0918 if (openParen.isValid()) { 0919 // get identifier 0920 var callLine = document.line(openParen.line); 0921 // strip starting from opening paren 0922 callLine = callLine.substring(0, openParen.column - 1); 0923 indentation = callLine.search(/\b(\w+)\s*$/); 0924 if (indentation != -1) { 0925 return document.toVirtualColumn(openParen.line, indentation); 0926 } 0927 } 0928 } else if (firstPos == column - 1 && c == '#' && ( gMode == 'C' || gMode == 'C++' || gMode == 'ISO C++' ) ) { 0929 // always put preprocessor stuff upfront 0930 return 0; 0931 } 0932 return -2; 0933 } 0934 0935 /** 0936 * Process a newline character. 0937 * This function is called whenever the user hits <return/enter>. 0938 */ 0939 function indent(line, indentWidth, ch) 0940 { 0941 gIndentWidth = indentWidth; 0942 gMode = document.highlightingModeAt(new Cursor(line, document.lineLength(line))); 0943 var alignOnly = (ch == ""); 0944 0945 if (ch != '\n' && !alignOnly) 0946 return processChar(line, ch); 0947 0948 return indentLine(line, alignOnly); 0949 } 0950 0951 // kate: space-indent on; indent-width 4; replace-tabs on;