File indexing completed on 2024-05-19 04:00:08
0001 var katescript = { 0002 "name": "ada", 0003 "author": "Trevor Blight <trevor-b@ovi.com>", 0004 "license": "LGPL", 0005 "revision": 2, 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 // loosely based on the vim ada indent file 0015 //-------- extract from original vim header follows -------------- 0016 // 0017 // Description: Vim Ada indent file 0018 // Language: Ada (2005) 0019 // Copyright: Copyright (C) 2006 Martin Krischik 0020 // Maintainer: Martin Krischik <krischik@users.sourceforge.net> 0021 // Neil Bird <neil@fnxweb.com> 0022 // Ned Okie <nokie@radford.edu> 0023 // 0024 //--------------- end of extract ----------------- 0025 0026 // apart from porting and translating from vim script to kate javascript, 0027 // changes compared to orininal vim script: 0028 // - added handling for labels and 'generic' keyword 0029 // - recognise keyword 'private' as part of package specification 0030 // - handle multi line for/while ... loop 0031 // - cat open/close paren lines to handle scanning multi-line proc/func dec's 0032 // - recognise various multi line statements 0033 // - indent once only after case statement 0034 // - add checkParens() for aligning parameters in multiline arg list 0035 // - refactoring & optimisations 0036 0037 // TODO: 'protected', 'task', 'select' & 0038 // possibly other keywords not recognised 0039 0040 // required katepart js libraries 0041 require ("range.js"); 0042 require ("cursor.js"); 0043 require ("string.js"); 0044 0045 //BEGIN USER CONFIGURATION 0046 0047 // send debug output to terminal 0048 // the ada indenter recognises this as a document variable 0049 var debugMode = false; 0050 0051 //END USER CONFIGURATION 0052 0053 var AdaComment = /\s*--.*$/; 0054 var unfStmt = /([.=\(]|:=[^;]*)\s*$/; 0055 var unfLoop = /^\s*(for|while)\b(?!.*\bloop\b)/i; 0056 var AdaBlockStart = /^\s*(if\b|while\b|else\b|elsif\b|loop\b|for\b.*\b(loop|use)\b|declare\b|begin\b|type\b.*\bis\b[^;]*$|(type\b.*)?\brecord\b|procedure\b|function\b|with\s+function\b|accept\b|do\b|task\b|generic\b|package\b|private\b|then\b|when\b|is\b)/i; 0057 var StatementStart = /^\s*(if|when|while|else|elsif|loop|for\b.*\b(loop|use)|begin)\b/i; 0058 0059 function dbg() { 0060 if (debugMode) { 0061 debug.apply(this, arguments); 0062 } 0063 } 0064 0065 0066 //BEGIN global variables and functions 0067 0068 // none implemented 0069 0070 //END global variables and functions 0071 0072 0073 // regexp of keywords & patterns that cause reindenting of the current line. 0074 var AdaReIndent = /^\s*((then|end|elsif|when|exception|begin|is|record|private)\s+|<<\w+>>|end;|[#\)])(.*)$/; 0075 0076 // characters which trigger indent, beside the default '\n' 0077 var triggerCharacters = " \t)#>;"; 0078 0079 // check if the trigger characters are in the right context, 0080 // otherwise running the indenter might be annoying to the user 0081 function reindentTrigger(line) 0082 { 0083 var res = AdaReIndent.exec(document.line(line)); 0084 dbg("reindentTrigger: checking line, found", ((res && res[3] == "" )? "'"+ (res[2]?res[2]:res[1])+"'": "nothing") ); 0085 return ( res && (res[3] == "" )); 0086 } // reindentTrigger 0087 0088 0089 /** 0090 * Find last non-empty line that is code 0091 */ 0092 function lastCodeLine(line) 0093 { 0094 do { 0095 line = document.prevNonEmptyLine(--line); 0096 if ( line == -1 ) { 0097 return -1; 0098 } 0099 0100 // keep looking until we reach code 0101 // skip labels & preprocessor lines 0102 var p = document.firstColumn(line); 0103 } while( document.firstChar(line) == '#' 0104 || !( document.isCode(line, document.firstColumn(line) ) 0105 || document.isString(line, document.firstColumn(line) ) 0106 || document.isChar(line, document.firstColumn(line) ) ) 0107 || /^\s*<<\w+>>/.test(document.line(line)) ); 0108 0109 return line; 0110 } // lastCodeLine() 0111 0112 0113 /* test if line has an unclosed opening paren '(' 0114 * line is document line 0115 * n is nr chars to test, starting at pos 0 0116 * return index of unmatched '(', or 0117 * -1 if no unmatched '(' 0118 */ 0119 function checkOpenParen( line, n ) 0120 { 0121 var nest = 0; 0122 var i = n; 0123 while( nest <= 0 && --i >= 0 ) { 0124 if( document.isCode(line, i) ) { 0125 var c = document.charAt( line, i); 0126 if( c == ')' ) nest--; 0127 else if( c == '(' ) nest++; 0128 } 0129 } 0130 //dbg("string " + document.line(line) + (i==-1? " has matched open parens": " has unclosed paren at pos " + i )); 0131 return i; 0132 } 0133 0134 /* test if line has an unmatched close paren 0135 * line is document line 0136 * n is nr chars to test, starting at pos 0 0137 * return index of rightmost unclosed paren 0138 * -1 if no unmatched paren 0139 */ 0140 function checkCloseParen( line, n ) 0141 { 0142 var nest = 0; 0143 var i = -1; 0144 var bpos = -1; 0145 while( ++i < n ) { 0146 if( document.isCode(line, i) ) { 0147 var c = document.charAt( line, i); 0148 if( c == ')' ) nest++; 0149 else if( c == '(' ) nest--; 0150 } 0151 if( nest > 0 ) { 0152 nest = 0; 0153 bpos = i; 0154 } 0155 } 0156 //dbg("string " + document.line(line) + (bpos==-1? " has matched close parens": " has unbalanced paren at pos " + bpos) ); 0157 return bpos; 0158 } 0159 0160 0161 /********************************************* 0162 * line up args inside parens 0163 * if the opening line has no args, line up with first arg on next line 0164 * ignore trailing comments. 0165 * opening line ==> figure out indent based on pos of first arg 0166 * subsequent args ==> maintain indent 0167 * ');' ==> align closing paren 0168 * 0169 * paren indenting has these cases: 0170 * pline: ( no args -- new line indented 0171 * pline: ( args -- new line aligns with first arg 0172 * pline ) -- check if still inside parens 0173 * cline ); no args -- open line, align with ( or args 0174 * cline ); args -- align with line above, same as just args 0175 * just args -- align with line above 0176 */ 0177 function checkParens(line, indentWidth, newLine ) 0178 { 0179 0180 var openParen = document.anchor(line, 0, ')'); 0181 if( !openParen.isValid() ) { 0182 // nothing found, go back 0183 //dbg("checkParens: not in parens"); 0184 return -1; 0185 } 0186 0187 // note: pline is code, and 0188 // we get here only when we know it exists 0189 var pline = lastCodeLine(line); 0190 var plineStr = document.line(pline).replace(AdaComment, ""); 0191 0192 /** 0193 * look for the case where the new line starts with ')' 0194 */ 0195 if( document.firstChar(line) == ')' ) { 0196 // we pressed enter in e.g. () 0197 var argIndent = document.firstVirtualColumn(pline); 0198 0199 var parenIndent = document.toVirtualColumn(openParen); 0200 0201 // look for something like 0202 // argn <--- pline 0203 // ); <--- line, trailing close paren 0204 // the trailing close paren lines up with the args or the open paren, 0205 // whichever is the leftmost 0206 if( pline > openParen.line && argIndent > 0 && argIndent < parenIndent) { 0207 parenIndent = argIndent; 0208 } 0209 0210 // just align parens, not args 0211 if( !newLine || !/[,\(]$/.test(plineStr) ) { 0212 dbg("checkParens: align trailing ')'"); 0213 return parenIndent; 0214 } 0215 0216 /* now we have one of 2 scenarios: 0217 * arg, <--- pline, , ==> new arg to be inserted 0218 * ) <--- line 0219 * or 0220 * xxx( <--- pline 0221 * ) <--- line 0222 * 0223 * in both cases, we need to open a line for the new arg and 0224 * place closing paren in same column as the opening paren 0225 * we leave cursor on the blank line, ie 0226 * 0227 * () 0228 * becomes: 0229 * ( 0230 * | 0231 * ) 0232 */ 0233 0234 document.insertText(line, document.firstColumn(line), "\n"); 0235 var anchorLine = line+1; 0236 // indent closing anchor 0237 view.setCursorPosition(line, parenIndent); 0238 document.indent(new Range(anchorLine, 0, anchorLine, 1), parenIndent / indentWidth); 0239 // make sure we add spaces to align perfectly on left anchor 0240 var padding = parenIndent % indentWidth; 0241 if ( padding > 0 ) { 0242 document.insertText(anchorLine, 0243 document.fromVirtualColumn(anchorLine, parenIndent - padding), 0244 String().fill(' ', padding)); 0245 } 0246 } // leading close paren 0247 0248 0249 var indent = document.firstVirtualColumn(line); 0250 0251 // look for line ending with unclosed paren like "func(" 0252 // and no args following open paren 0253 var bpos = plineStr.search(/\($/ ); 0254 if( bpos != -1 && document.isCode(pline, bpos) ) { 0255 // we have something like 0256 // func( <--- pline 0257 // <--- line, first arg goes here 0258 // line contains the first arg in a multi line list 0259 // following args will be lined up against this 0260 // if line is not empty, assume user has set it - leave it as it is 0261 dbg( "checkParens: args indented from opening paren" ); 0262 indent = document.toVirtualColumn(pline, bpos) + indentWidth; 0263 return indent; 0264 } // trailing open paren 0265 0266 // check the line above 0267 // if it has unclosed paren ==> indent to first arg 0268 // if it has unbalanced closing paren ==> indent to anchor line 0269 // other lines, keep indent 0270 0271 // now look for something like 0272 // func( arg1, <--- pline 0273 // <--- line 0274 // note: already handled case when there are no args 0275 bpos = checkOpenParen(pline, document.lineLength(pline)); 0276 if( bpos != -1 ) { 0277 //dbg( "checkParens: found open paren in ", plineStr.trim() ); 0278 bpos = document.nextNonSpaceColumn(pline, bpos+1); 0279 dbg("checkParens: aligning arg to", document.wordAt(pline, bpos), "in", plineStr); 0280 indent = document.toVirtualColumn(pline, bpos ); 0281 return indent; 0282 } // trailing opening arg 0283 0284 0285 // check nested parens 0286 bpos = checkCloseParen(pline, plineStr.length); 0287 if( bpos != -1 ) { 0288 // we have something like this: 0289 // ) <-- pline 0290 // <-- line 0291 0292 // assert( indent == -1 ); 0293 //dbg( "arg list terminated with '" + plineStr[bpos] + "'", " at pos", bpos ); 0294 openParen = document.anchor( pline, bpos, plineStr[bpos] ); 0295 if (openParen.isValid() ) { 0296 // it's more complicated than it appears: 0297 // we might still be inside another arglist! 0298 // check back from openParen, 0299 // if another open paren is found then 0300 // indent to next non white space after that 0301 var testLine = openParen.line; 0302 //dbg("checkParens: testing " + document.line(testLine).substr(0,openParen.column) ); 0303 bpos = checkOpenParen(testLine, openParen.column); 0304 if( bpos != -1 ) { 0305 // line above closes parens, still inside another paren level 0306 //dbg("checkParens: found yet another open paren @ pos", bpos); 0307 bpos = document.nextNonSpaceColumn(testLine, bpos+1); 0308 dbg("checkParens: aligning with open paren in line", openParen.line); 0309 indent = document.toVirtualColumn(testLine, bpos ); 0310 return indent; 0311 } else if( document.anchor(line,0,'(').isValid() ) { 0312 dbg("checkParens: aligning to args at next outer paren level"); 0313 return document.firstVirtualColumn(testLine ); 0314 } 0315 } else { 0316 // dbg("checkOpenParens: line above closes arg list, can't find open paren!"); 0317 } 0318 } else { 0319 // line above doesn't close parens, we may or may not be inside parens 0320 } 0321 0322 // we are inside parens, so align with previous line 0323 dbg("checkParens: inside '(...)', aligning with previous line" ); 0324 return document.firstVirtualColumn(pline ); 0325 0326 } // checkParens() 0327 0328 0329 // Try to find indent of the parent of the block we're in 0330 // Hunt for a valid line with a lesser or equal indent than prev_lnum has. 0331 // Assume prev_lnum & all other lines before that are correctly indented 0332 // prev_indent = ignore every thing indented >= this, it's assumed to be an inner block level 0333 // prev_lnum = line to start looking on 0334 // blockstart = regexp that indicates a possible start of this block 0335 // stop_at = if non-null, if a matching line is found, gives up! 0336 function MainBlockIndent (prev_indent, prev_lnum, blockstart, stop_at) 0337 { 0338 // can't outdent from col 0 0339 if( prev_indent == 0 ) return 0; 0340 0341 var pline = prev_lnum; 0342 while( pline >= 0 ) { 0343 var plineStr = document.line(pline).replace(AdaComment, ""); 0344 //dbg("MainBlockIndent: plineStr is", plineStr, "stop_at is", stop_at? stop_at.source: "null"); 0345 var ind = document.firstVirtualColumn(pline); 0346 if( ind < prev_indent ) { 0347 // we are at an outer level 0348 if( new RegExp(/^\s*/.source + blockstart.source, "i").test(plineStr) ) { 0349 //dbg("MainBlockIndent: found start of block at", plineStr.trim() ); 0350 return ind 0351 } 0352 else if( stop_at 0353 && new RegExp(/^\s*/.source + stop_at.source, "i").test(plineStr) ) { 0354 dbg("MainBlockIndent: stopped hunting at", plineStr.trim() ); 0355 return prev_indent 0356 } // if 0357 } // if 0358 0359 pline = lastCodeLine( pline ); 0360 } // while 0361 0362 // Fallback - just use prev_indent 0363 dbg("MainBlockIndent: no more code"); 0364 return prev_indent; 0365 } // MainBlockIndent 0366 0367 0368 // Try to find indent of the block we're in (and about to complete), 0369 // including handling of nested blocks. Works on the 'end' of a block. 0370 // prev_indent = the previous line's indent 0371 // prev_lnum = previous line (to start looking on) 0372 // blockstart = expr. that indicates a possible start of this block 0373 // blockend = expr. that indicates a possible end of this block 0374 function EndBlockIndent( prev_indent, prev_lnum, blockstart, blockend ) 0375 { 0376 // can't outdent from col 0 0377 if( prev_indent == 0 ) return 0; 0378 0379 var pline = prev_lnum; 0380 var ends = 0; 0381 while( pline >= 0 ) { 0382 var plineStr = document.line(pline).replace(AdaComment, ""); 0383 //dbg("EndBlockIndent: ind is", ind, ", plineStr is", plineStr); 0384 if( new RegExp(/^\s*/.source + blockstart.source, "i").test(plineStr) ) { 0385 ind = document.firstVirtualColumn(pline); 0386 if( ends <= 0 ) { 0387 if( ind < prev_indent ) { 0388 //dbg("EndBlockIndent: ind is", ind, ", found start of block at '" + plineStr.trim() + "'"); 0389 return ind 0390 } // if 0391 } 0392 else { 0393 //dbg("EndBlockIndent: out one level,", plineStr.trim()); 0394 ends = ends - 1; 0395 } 0396 } 0397 else if( new RegExp(/^\s*/.source + blockend.source, "i").test(plineStr) ) { 0398 //dbg("EndBlockIndent: in one level,", plineStr.trim()); 0399 ends = ends + 1; 0400 } // if 0401 0402 pline = lastCodeLine(pline); // Get previous non-blank/non-comment-only line 0403 } // while 0404 0405 // Fallback - just use prev_indent 0406 dbg("EndBlockIndent: no more code"); 0407 return prev_indent; 0408 0409 } // EndBlockIndent 0410 0411 0412 // Return indent of previous statement-start 0413 // Find start of a (possibly multiline) statement 0414 // (after we've indented due to multi-line statements). 0415 // eg, 0416 // a := very_long_expression < -- pline, this determines indent 0417 // + another_long_expression; < -- prev_lnum, skip this line 0418 // s1; < -- indent this current line 0419 // This time, we start searching on the line *before* the one given 0420 // (which is the end of a statement - we want the previous beginning). 0421 // find earlier sibling or parent to determine indent of current statement 0422 function StatementIndent( current_indent, prev_lnum ) 0423 { 0424 // can't outdent from col 0 0425 if( current_indent == 0 ) return 0; 0426 0427 var pline = prev_lnum; 0428 var ind; 0429 0430 while( pline >= 0 ) { 0431 var ref_lnum = pline; 0432 pline = lastCodeLine( pline ); 0433 if( pline < 0 ) { 0434 return current_indent 0435 } // if 0436 0437 var plineStr = document.line(pline).replace(AdaComment, ""); 0438 //if(pline == lastCodeLine( prev_lnum )) 0439 // dbg("StatementIndent: current_indent is", current_indent, ", plineStr is", plineStr.trim()); 0440 // Leave indent alone if our ';' line is part of a ';'-delineated 0441 // aggregate (e.g., procedure args.) or first line after a block start. 0442 if( AdaBlockStart.test(plineStr) 0443 || /^\s*end\b/i.test(plineStr) // this was not in vim script 0444 || /\(\s*$/.test(plineStr) ) { 0445 ind = document.firstVirtualColumn(ref_lnum); 0446 if( ind < current_indent ) { // this was not in vim script 0447 //dbg("StatementIndent: ind is", ind, ", taking indent of parent", document.line(ref_lnum).trim()); 0448 return ind; 0449 } else { 0450 //dbg("StatementIndent: stopped looking at", plineStr.trim()); 0451 return current_indent; 0452 } // if 0453 } // if 0454 if( ! unfStmt.test(plineStr) ) { 0455 ind = document.firstVirtualColumn(ref_lnum); 0456 //dbg("StatementIndent: ind is", ind, ", sibling found", plineStr.trim()); 0457 if( ind < current_indent ) { 0458 //dbg("StatementIndent: ind is", ind, ", taking indent of sibling", document.line(ref_lnum).trim()); 0459 return ind; 0460 } // if 0461 } // if 0462 } // while 0463 0464 // Fallback - just use current one 0465 return current_indent; 0466 } // StatementIndent() 0467 0468 0469 //---------------------- 0470 // check what's on the previous line 0471 // return expected indent for next line 0472 function checkPreviousLine( pline, indentWidth ) 0473 { 0474 var kpos; // temp var to hold char positions in search strings 0475 var plineStr = document.line(pline).replace(AdaComment, ""); 0476 0477 // test for multi-line statements by concatenating them 0478 // NOTE: original vim script did not join lines together 0479 if( /^\s*(renames|access|new)\b/i.test(plineStr) ) { 0480 var l = lastCodeLine(pline); 0481 if( l >= 0 ) { 0482 pline = l; 0483 plineStr = document.line(pline).replace(AdaComment, "") + plineStr; 0484 //dbg("checkPreviousLine: combined split statement, pline is", plineStr.trim() ); 0485 } 0486 } 0487 0488 // now do lines that include open & close parens, so 0489 // 0490 // aaaaaa <---- new pline, use this to derive indent 0491 // ( .... 0492 // .... 0493 // ) zzzz <---- original pline 0494 // 0495 // becomes one line: 0496 // 0497 // aaaaaa ( .... ) zzzz 0498 // 0499 kpos = checkCloseParen(pline, plineStr.length); 0500 if( kpos != -1 ) { 0501 dbg("checkPreviousLine: unmatched close paren found"); 0502 var r = document.anchor(pline, kpos, ')' ); 0503 if(r.isValid()) { 0504 pline = r.line; 0505 plineStr = document.line(pline).replace(AdaComment, "") + plineStr; 0506 //dbg("checkPreviousLine: step 1, pline is", plineStr.trim() ); 0507 } 0508 } 0509 0510 if( /^\s*\(/.test(plineStr) ) { 0511 // open '(' on its own line - use previous indent 0512 pline = lastCodeLine( pline ); 0513 plineStr = document.line(pline).replace(AdaComment, "") + plineStr; 0514 //dbg("checkPreviousLine: step 2, pline is", plineStr.trim() ); 0515 } 0516 0517 var ind = document.firstVirtualColumn(pline); 0518 0519 //dbg("checkPreviousLine: ind is", ind + ", pline is", plineStr.trim() ); 0520 0521 if( AdaBlockStart.test( plineStr ) || (/\(\s*$/.test(plineStr)) ) { 0522 //dbg("checkPreviousLine: pline follows block start '", RegExp.$1, "'"); 0523 // Check for false matches to AdaBlockStart 0524 var false_match = false; 0525 if( /^\s*(procedure|function|package)\b.*\bis\s+new\b/i.test(plineStr) ) { 0526 //dbg("checkPreviousLine: generic instantiation ignored for now"); 0527 false_match = true; 0528 } 0529 else if( (/\)\s*;\s*$/.test(plineStr)) || (/^([^(]*\([^)]*\))*[^(]*;\s*$/.test(plineStr)) ) { 0530 // matches trailing ');' or 'xxx(...) yyy;' 0531 //dbg("checkPreviousLine: forward declaration ignored for now"); 0532 false_match = true; 0533 } 0534 // Move indent in 0535 if ( false_match ) { 0536 //dbg("checkPreviousLine: block start ignored"); 0537 } else { 0538 dbg("checkPreviousLine: indent after block start"); 0539 ind += indentWidth; 0540 } // if 0541 } 0542 else if( /^\s*return\b/i.test(plineStr) ) { 0543 // is it a return statement, or part of a function declaration? 0544 // NOTE: original vim script did not recognise 0545 // 'return' on a new line for function declarations 0546 0547 var l = lastCodeLine( pline ); 0548 while( l >= 0 ) { 0549 lStr = document.line(l); 0550 if( StatementStart.test(lStr) ) { 0551 dbg("checkPreviousLine: previous line is return statement"); 0552 break; 0553 } 0554 if(/^\s*(with\s*)?function\b/i.test(lStr)) { 0555 // move indent back to parent 'function' 0556 dbg("checkPreviousLine: 'return' is function return type"); 0557 ind = document.firstVirtualColumn(l); 0558 if( ! /\s*;\s*$/.test(plineStr) ) { 0559 ind += indentWidth; 0560 //dbg("checkPreviousLine: continue function body"); 0561 } 0562 break; 0563 } 0564 l = lastCodeLine(l); 0565 } // while 0566 } 0567 else if( /^\s*(case|exception)\b/i.test(plineStr) ) { 0568 dbg("checkPreviousLine: pline follows", RegExp.$1, "indenting"); 0569 // NOTE: original vim script indented 2x 0570 ind = ind + indentWidth; 0571 } 0572 else if( /^\s*end\s+record\b/i.test(plineStr) ) { 0573 dbg("checkPreviousLine: current line follows end of record"); 0574 // Move indent back to tallying 'type' preceeding the 'record'. 0575 // Allow indent to be equal to 'end record's. 0576 ind = MainBlockIndent( ind+indentWidth, pline, /type\b/, '' ); 0577 } 0578 else if( unfStmt.test(plineStr) ) { 0579 dbg("checkPreviousLine: previous line is an unfinished statement"); 0580 kpos = plineStr.indexOf(":="); 0581 if( kpos != -1 ) { 0582 kpos = document.nextNonSpaceColumn(pline, kpos+2); 0583 } 0584 if( kpos != -1 ) { 0585 // a := xxx ... unfinished assignment like this 0586 // ^-- line up here 0587 ind = document.toVirtualColumn(pline, kpos); 0588 } else { 0589 // A statement continuation - move in one 0590 ind += indentWidth 0591 } 0592 } 0593 else if( unfLoop.test(plineStr) ) { 0594 // Multi line for/while ... loop 0595 ind += indentWidth; 0596 dbg("checkPreviousLine: ind is", ind, ", previous line is an unfinished while/for loop"); 0597 } 0598 else if( /^(?!\s*end\b).*;\s*$/i.test(plineStr) ) { 0599 // end of statement (but not 'end' ) 0600 // start of statement might be on a previous line 0601 // - try to find current statement-start indent 0602 ind = StatementIndent( ind, pline ) 0603 dbg("checkPreviousLine: ind is", ind + ", pline follows end of statement"); 0604 } else { 0605 //dbg("checkPreviousLine: previous line does not influence indent"); 0606 } // if 0607 0608 return ind; 0609 0610 } // checkPreviousLine() 0611 0612 0613 // Check current line; search for simplistic matching start-of-block 0614 function indentLine( cline, indentWidth, newLine) 0615 { 0616 0617 // Find a non-blank code line above the current line. 0618 var pline = lastCodeLine( cline ); 0619 if( pline < 0 ) { 0620 if( document.startsWith( cline, "--", true ) ) { 0621 dbg("indentLine: align comment"); 0622 pline = document.prevNonEmptyLine( cline - 1 ); 0623 if( pline < 0 ) return -2; // first comment line 0624 return -1; // other comments follow 0625 } 0626 dbg("indent: first code line"); 0627 return 0; 0628 } 0629 0630 var plineStr = document.line(pline).replace(AdaComment, ""); 0631 //dbg("indentLine: plineStr is", plineStr.trim()); 0632 0633 var clineStr = document.line(cline).replace(AdaComment, ""); 0634 //dbg("indentLine: cline is", clineStr.trim() ); 0635 0636 var n = checkParens( cline, indentWidth, newLine ); 0637 if( n != -1 ) { 0638 //dbg("indentLine: current line aligned inside parens"); 0639 return n; 0640 } 0641 if( /^\s*#/.test(clineStr) ) { 0642 dbg( "indentLine: Start of line for ada-pp" ); 0643 return 0; 0644 } 0645 if( ((kpos = plineStr.search(/[A-Za-z0-9_.]+(\s+is)?$/)) != -1 ) 0646 && (/^\s*\(/.test(clineStr)) ) { 0647 dbg("indentLine: found potential argument list"); 0648 return document.toVirtualColumn(pline,kpos) + indentWidth; 0649 } 0650 0651 // Get default indent from prev. line 0652 var pind = checkPreviousLine(pline, indentWidth); 0653 //dbg( "indentLine: default indent is", pind ); 0654 0655 if( /^\s*(begin|is)\b/i.test(clineStr) ) { 0656 dbg("indentLine: pind is", pind, ", found ", RegExp.$1); 0657 return MainBlockIndent( pind, pline, /(procedure|function|declare|package|task)\b/i, /begin\b/i ) 0658 } 0659 if( /^\s*record\b/i.test(clineStr) ) { 0660 dbg("indentLine: line is 'record'"); 0661 return MainBlockIndent( pind, pline, /type\b|for\b.*\buse\b/i, '' ) + indentWidth 0662 } 0663 if( /^\s*(else|elsif)\b/i.test(clineStr) ) { 0664 dbg("indentLine: aligning", RegExp.$1, "with corresponding 'if'"); 0665 return MainBlockIndent( pind, pline, /if\b/, /^\s*begin\b/ ) 0666 } 0667 if( /^\s*when\b/i.test(clineStr) ) { 0668 // Align 'when' one /in/ from matching block start 0669 dbg("indentLine: 'when' clause indented from parent"); 0670 return MainBlockIndent( pind, pline, /(case|exception)\b/, /\s*begin\b/ ) + indentWidth; 0671 } 0672 if( /^\s*end\b\s*\bif\b/i.test(clineStr) ) { 0673 // End of if statements 0674 dbg("indentLine: 'end if' aligned to corresponding 'if' statement"); 0675 return EndBlockIndent( pind, pline, /if\b/, /end\b\s*\bif\b/ ) 0676 } 0677 if( /^\s*end\b\s*\bloop\b/i.test(clineStr) ) { 0678 dbg("indentLine: line is end of loop"); 0679 // End of loops 0680 return EndBlockIndent( pind, pline, /((while|for|loop)\b)/, /end\b\s*\bloop\b/ ); 0681 } 0682 if( /^\s*end\b\s*\brecord\b/i.test(clineStr) ) { 0683 // End of records 0684 dbg("indentLine: 'end record' aligned to corresponding parent"); 0685 return EndBlockIndent( pind, pline, /(type\b.*)?\brecord\b/, /end\b\s*\brecord\b/ ); 0686 } 0687 if( /^\s*end\s+procedure\b/i.test(clineStr) ) { 0688 dbg("indentLine: 'end procedure' aligned to corresponding parent"); 0689 // End of procedures 0690 // TODO: when does 'end procedure' occur? 0691 // TODO: multiline procedure heading 0692 return EndBlockIndent( pind, pline, /procedure\b.*\bis\b/, /end\b\s*\bprocedure\b/ ); 0693 } 0694 if( /^\s*end\b\s*\bcase\b/i.test(clineStr) ) { 0695 // NOTE: original vim script required 'is' on same line 0696 dbg("indentLine: 'end case' aligned to 'case'"); 0697 return EndBlockIndent( pind, pline, /case\b[^;]*(\bis\b|$)/i, /end\b\s*\bcase\b/i ); 0698 } 0699 if( /^\s*end\b/i.test( clineStr) ) { 0700 // General case for end 0701 dbg("indentLine: 'end' aligned to its parent"); 0702 return MainBlockIndent( pind, pline, /(if|while|for|loop|accept|begin|record|case|exception|package)\b/, '' ); 0703 } 0704 if( /^\s*exception\b/i.test( clineStr) ) { 0705 dbg("indentLine: 'exception' aligned to corresponding 'begin'"); 0706 return MainBlockIndent( pind, pline, /begin\b/, '' ); 0707 } 0708 if( /^\s*private\b/i.test( clineStr) ) { 0709 dbg("indentLine: 'private' aligned to corresponding 'package'"); 0710 return MainBlockIndent( pind, pline, /package\b/, '' ); 0711 } 0712 if( /^\s*<<\w+>>/.test( clineStr) ) { 0713 // NOTE: original vim script did not consider labels 0714 dbg("indentLine: 'label' aligned to corresponding 'begin'"); 0715 return MainBlockIndent( pind, pline, /begin\b/, '' ); 0716 } 0717 if( /^\s*then\b/i.test( clineStr ) ) { 0718 dbg("indentLine: 'then' aligned to corresponding 'if'"); 0719 return MainBlockIndent( pind, pline, /if\b/, /begin\b/ ); 0720 } 0721 if( /^\s*loop\b/i.test( clineStr ) ) { 0722 // is it a multiline while/for, or loop on its own? 0723 // search back for while/for without a loop, stop at any statement 0724 dbg("indentLine: 'loop' aligned either to corresponding 'for/while', or on its own"); 0725 return MainBlockIndent( pind, pline, unfLoop, StatementStart ); 0726 } 0727 if( /^\s*(function|procedure|package)\b/i.test( clineStr ) ) { 0728 dbg("indentLine:", RegExp.$1, " - checking for corresponding 'generic'"); 0729 return MainBlockIndent( pind, pline, /generic\b/, /^\s*(function|procedure|package)\b/ ); 0730 } 0731 if( /^\s*:=/.test( clineStr ) ) { 0732 dbg("indentLine: continuing assignment"); 0733 return pind + indentWidth; 0734 } 0735 0736 dbg("indentLine: current line has default indent"); 0737 return pind 0738 } // indentLine() 0739 0740 0741 // Find correct indent of a new line based upon what went before 0742 // 0743 // we assume all previous lines are correctly indented 0744 // indent of current line is to be determined 0745 // arguments: 0746 // cline -- current line 0747 // indentWidth -- in spaces 0748 // ch -- typed character indent 0749 // 0750 // returns the amount of characters (in spaces) to be indented. 0751 // Special return values: 0752 // -2 = no indent 0753 // -1 = keep previous indent 0754 0755 function indent(cline, indentWidth, ch) 0756 { 0757 var t = document.variable("debugMode"); 0758 if(t) debugMode = /^(true|on|enabled|1)$/i.test( t ); 0759 0760 dbg("\n------------------------------------ (" + cline + ")"); 0761 0762 // all functions assume valid line nr on entry 0763 if( cline < 0 ) return -2; 0764 0765 0766 var newLine = (ch == "\n"); 0767 0768 if( newLine ) { 0769 // cr entered - align the just completed line 0770 view.align(new Range(cline-1, 0, cline-1, 1)); 0771 } else if( ch == "" ) { 0772 if( document.firstVirtualColumn(cline) == -1 ) { 0773 dbg("empty line, zero indent"); 0774 return 0; 0775 } 0776 } else if( !reindentTrigger(cline) ) { 0777 return -2; 0778 } 0779 0780 return indentLine( cline, indentWidth, newLine); 0781 0782 } // indent() 0783 0784 ////////////////// end of ada.js //////////////