File indexing completed on 2024-05-19 04:00:09
0001 var katescript = { 0002 "name": "Julia", 0003 "author": "Ilia Kats <ilia-kats@gmx.net>, Paul Giannaros <paul@giannaros.org>, Gerald Senarclens de Grancy <oss@senarclens.eu>", 0004 "license": "LGPL", 0005 "revision": 1, 0006 "kate-version": "5.1", 0007 "indent-languages": ["Julia"] 0008 }; // kate-script-header, must be at the start of the file without comments, pure json 0009 0010 // required katepart js libraries 0011 require("range.js"); 0012 require("string.js"); 0013 0014 openings = ['(', '[', '{']; 0015 closings = [')', ']', '}']; // requires same order as in openings 0016 imports = ["using", "import"] 0017 linestart_block_start = ["function", "if", "try", "for", "while", "let", "quote", "mutable struct", "struct"]; 0018 linemid_block_start = ["do", "begin"]; 0019 block_continue = ["else", "elseif", "catch", "finally"]; 0020 block_continue_lineend = ["else", "finally", "catch"]; 0021 block_continue_linemid = ["elseif", "catch"]; 0022 unindenter = "end"; 0023 0024 triggerCharacters = "efhyd "; 0025 0026 immediate_unindenters = new Set(block_continue.concat([unindenter])); 0027 all_block_expr = new RegExp( 0028 "\\b(" + 0029 [ 0030 linestart_block_start.join("|"), 0031 linemid_block_start.join("|"), 0032 block_continue.join("|") + "|" + 0033 unindenter 0034 ].join("|") + 0035 ")\\b" 0036 , "g"); 0037 full_block_expr = new RegExp( 0038 "\\b(" + 0039 linestart_block_start.join("|") + "|" + 0040 linemid_block_start.join("|") + "|" + 0041 unindenter + 0042 ")\\b" 0043 , "g"); 0044 linestart_block_start_expr = new RegExp("\\b(" + linestart_block_start.join("|") + ")\\b"); 0045 linemid_block_start_expr = new RegExp("\\b(" + linemid_block_start.join("|") + ")\\b"); 0046 unindenters_newline_expr = new RegExp( 0047 "\\b((" + 0048 block_continue_lineend.join("$|") + "$|" + 0049 unindenter + "$)|((" + 0050 block_continue_linemid.join("|") + 0051 ")(?!.*" + all_block_expr.source + ")))\\b" 0052 ); 0053 0054 block_starts = new Set(linestart_block_start.concat(linemid_block_start)); 0055 0056 var imports_expr = new RegExp("((\\s*\\w+\\s*:)(\\s*\\w+\\s*,)*|(\\s*\\w+\\s*,)+)$"); 0057 var imports_end_expr = new RegExp("(\\s*(\\w+\\s*:?)\\s*,\\s*)*\\s*(\\w+)\\s*$"); 0058 0059 // Return the given line without comments and leading or trailing whitespace. 0060 // Eg. 0061 // getCode(x) -> "for i in range(3):" 0062 // if document.line(x) == " for i in range(3):" 0063 // getCode(x) -> "for i in range(3):" 0064 // if document.line(x) == "for i in range(3): " 0065 // getCode(x) -> "for i in range(3):" 0066 // if document.line(x) == "for i in range(3): # grand" 0067 function getCode(lineNr, virtcol=-1) { 0068 var line = document.line(lineNr); 0069 var code = ''; 0070 virtcol = virtcol >= 0 ? virtcol : document.firstVirtualColumn(lineNr); 0071 if (virtcol < 0) 0072 return code; 0073 for (;virtcol < line.length; ++virtcol) { 0074 if (!document.isComment(lineNr, virtcol)) { 0075 code += line[virtcol]; 0076 } 0077 } 0078 return code.trim(); 0079 } 0080 0081 function getLastCodePosition(lineNr, lastVirtCol=-1) { 0082 var line = document.line(lineNr); 0083 virtcol = lastVirtCol >= 0 ? lastVirtCol : document.lastVirtualColumn(lineNr); 0084 if (virtcol < 0) 0085 return -1 0086 for (; virtcol >= 0; --virtcol) { 0087 if (!document.isComment(lineNr, virtcol) && !document.isSpace(lineNr, virtcol)) 0088 break; 0089 } 0090 return virtcol; 0091 } 0092 0093 // Return the indent if a opening bracket is not closed (incomplete sequence). 0094 // The calculated intent is the innermost opening bracket's position plus 1. 0095 // `lineNr`: the number of the line on which the brackets should be counted 0096 function _calcOpeningIndent(lineNr) { 0097 var line = document.line(lineNr); 0098 var countClosing = new Array(); 0099 closings.forEach(function(elem) { 0100 countClosing[elem] = 0; 0101 }); 0102 for (i = line.length - 1; i >= 0; --i) { 0103 if (document.isComment(lineNr, i) || document.isString(lineNr, i)) 0104 continue; 0105 if (closings.indexOf(line[i]) > -1) 0106 countClosing[line[i]]++; 0107 var index = openings.indexOf(line[i]); 0108 if (index > -1) { 0109 if (countClosing[closings[index]] == 0) { 0110 return i + 1; 0111 } 0112 countClosing[closings[index]]--; 0113 } 0114 } 0115 return -1; 0116 } 0117 0118 0119 // Return the indent if a closing bracket not opened (incomplete sequence). 0120 // The intent is the same as on the line with the unmatched opening bracket. 0121 // `lineNr`: the number of the line on which the brackets should be counted 0122 function _calcClosingIndent(lineNr, indentWidth) { 0123 var line = document.line(lineNr); 0124 var countClosing = new Array(); 0125 closings.forEach(function(elem) { 0126 countClosing[elem] = 0; 0127 }); 0128 for (i = line.length - 1; i >= 0; --i) { 0129 if (document.isComment(lineNr, i) || document.isString(lineNr, i)) 0130 continue; 0131 if (closings.indexOf(line[i]) > -1) 0132 countClosing[line[i]]++; 0133 var index = openings.indexOf(line[i]); 0134 if (index > -1) 0135 countClosing[closings[index]]--; 0136 } 0137 for (var key in countClosing) { 0138 if (countClosing[key] > 0) { // unmatched closing bracket 0139 for (--lineNr; lineNr >= 0; --lineNr) { 0140 if (_calcOpeningIndent(lineNr) > -1) { 0141 var indent = document.firstVirtualColumn(lineNr); 0142 if (shouldUnindent(lineNr + 1)) 0143 return Math.max(0, indent - indentWidth); 0144 return indent; 0145 } 0146 } 0147 } 0148 } 0149 return -1; 0150 } 0151 0152 // Returns the indent for mismatched (opening or closing) brackets. 0153 // If there are no mismatched brackets, -1 is returned. 0154 // `lineNr`: number of the line for which the indent is calculated 0155 function calcBracketIndent(lineNr, indentWidth) { 0156 var indent = _calcOpeningIndent(lineNr); 0157 if (indent > -1) 0158 return indent 0159 indent = _calcClosingIndent(lineNr); 0160 if (indent > -1) 0161 return indent 0162 return -1; 0163 } 0164 0165 function findMatchingParenthesis(lineNr, charNr) { 0166 var nests = 1; 0167 charNr = charNr + document.firstVirtualColumn(lineNr); 0168 while (lineNr >= 0) { 0169 var line = document.line(lineNr); 0170 for (i = Math.min(charNr - 1, line.length - 1); i >= 0; --i) { 0171 if (document.isComment(lineNr, i) || document.isString(lineNr, i)) 0172 continue; 0173 if (line[i] == '(') 0174 nests -= 1; 0175 else if (line[i] == ')') 0176 nests += 1; 0177 if (nests == 0) 0178 return [lineNr, i - document.firstVirtualColumn(lineNr)]; 0179 } 0180 --lineNr; 0181 charNr = Infinity; 0182 } 0183 return [-1, -1] 0184 } 0185 0186 // Return true if a single unindent should occur. 0187 function shouldUnindent(LineNr, bracketOnly=true) { 0188 // unindent if the last line was indented b/c of an operator 0189 if (LineNr >= 1) { 0190 var lastcodepos = getLastCodePosition(LineNr - 1); 0191 if (lastcodepos > 0 && document.defStyleNum(LineNr - 1, lastcodepos) == dsOperator) 0192 return 1; 0193 if (!bracketOnly && document.line(LineNr)[lastcodepos] != ',') { 0194 var cline = getCode(LineNr); 0195 if (!imports.some(imp => cline.startsWith(imp)) && imports_end_expr.test(cline)) { 0196 --LineNr; 0197 for (; LineNr >= 0; --LineNr) { 0198 cline = getCode(LineNr); 0199 if (!cline.length) 0200 continue; 0201 if (imports_expr.test(cline)) { 0202 if (imports.some(imp => cline.startsWith(imp))) 0203 return 1; 0204 } else 0205 break; 0206 } 0207 } 0208 } 0209 } 0210 return 0; 0211 } 0212 0213 // finds the indentation level of a matching block start for a block end 0214 function findMatchingBlock(lineNr, trigger) { 0215 var nested_threshold = trigger == unindenter ? 1 : 0; 0216 var nestedBlocks = 0; 0217 var found = false; 0218 for (var linenr = lineNr; linenr >= 0; --linenr) { 0219 var lline = getCode(linenr); 0220 var matches = []; 0221 while (match = full_block_expr.exec(lline)) { 0222 matches.push(match); 0223 } 0224 if (matches.length > 0) { 0225 for (match of matches.reverse()) { 0226 if (match[0] == unindenter) { 0227 var lastclosingbracket = lline.lastIndexOf("]", match.index); 0228 var lastopeningbracket = lline.lastIndexOf("[", match.index); 0229 // end is also used in subsetting to get the last element, ignore this 0230 // for block calculation 0231 if (lastopeningbracket == -1 || lastclosingbracket > lastopeningbracket) 0232 ++nestedBlocks; 0233 } else if (nestedBlocks == nested_threshold) { 0234 found = block_starts.has(match[0]); 0235 if (found) 0236 break; 0237 } else { 0238 --nestedBlocks; 0239 } 0240 } 0241 } 0242 if (found) 0243 break; 0244 } 0245 return found && linenr < lineNr ? document.firstVirtualColumn(linenr) : -1; 0246 } 0247 0248 // Return the amount of characters (in spaces) to be indented. 0249 // Special indent() return values: 0250 // -2 = no indent 0251 // -1 = keep last indent 0252 // Follow PEP8 for unfinished sequences and argument lists. 0253 // Nested sequences are not implemented. (neither by Emacs' python-mode) 0254 function indent(line, indentWidth, character) { 0255 if (line == 0) // don't ever act on document's first line 0256 return -2; 0257 0258 var alignOnly = character == ""; 0259 0260 if (alignOnly || character != '\n') { 0261 var lline = getCode(line); 0262 if (immediate_unindenters.has(lline)) 0263 return findMatchingBlock(lline == unindenter ? line : line - 1, lline); 0264 else if (!alignOnly) 0265 return -2 0266 } 0267 0268 while (line > 0 && !document.line(line - 1).length) // ignore empty lines 0269 --line; 0270 --line; 0271 0272 var virtcol = document.firstVirtualColumn(line); 0273 var lastLine = getCode(line, virtcol); 0274 var lastChar = lastLine.substr(-1); 0275 0276 // indent when opening bracket or an operator is at the end the previous line 0277 if (openings.indexOf(lastChar) >= 0 || document.defStyleNum(line, getLastCodePosition(line)) == dsOperator) { 0278 return virtcol + indentWidth; 0279 } 0280 var indent = calcBracketIndent(line, indentWidth); 0281 if (lastChar == ')') { 0282 // could be a function definition with multi-line argument list 0283 var matching = findMatchingParenthesis(line, lastLine.length - 1); 0284 if (matching[0] > -1) { 0285 line = matching[0]; 0286 virtcol = document.firstVirtualColumn(line); 0287 lastLine = getCode(line, virtcol); 0288 } 0289 else 0290 return -1; 0291 } else if (lastChar == ',' || lastChar == ':') { 0292 var is_import = false; 0293 for (const imp of imports) { 0294 if (lastLine.startsWith(imp)) 0295 return virtcol + indentWidth; 0296 } 0297 } 0298 0299 // this also covers single-line blocks 0300 var match = lastLine.match(unindenters_newline_expr); 0301 if (match) { 0302 var mblock = findMatchingBlock(line, match[0]); 0303 if (mblock > -1) { 0304 return match[0] == unindenter ? mblock : mblock + indentWidth; 0305 } else 0306 return -1; 0307 } 0308 0309 startblock = lastLine.search(linestart_block_start_expr); 0310 if (startblock == 0) { 0311 if (indent > virtcol) 0312 return indent; // unmatched open bracket in proper block start 0313 else 0314 return virtcol + indentWidth; 0315 } else if (startblock > 0 || lastLine.search(linemid_block_start_expr) > -1) { // single-line blocks 0316 var endpos = lastLine.lastIndexOf(unindenter); 0317 var lastkeyword = unindenter; 0318 block_continue.forEach(function(keyw) { 0319 var pos = lastLine.lastIndexOf(keyw); 0320 if (pos >= 0 && pos > endpos) { 0321 lastkeyword = keyw; 0322 endpos = pos; 0323 } 0324 }); 0325 if (endpos >= 0) 0326 return findMatchingBlock(line, lastkeyword); 0327 else { 0328 if (indent > -1) 0329 indent += indentWidth; // unmatched open bracket in possibly nested blocks 0330 else 0331 indent = virtcol + indentWidth; 0332 } 0333 } 0334 if (shouldUnindent(line, false) && (indent == -1)) { 0335 indent = Math.max(0, virtcol - indentWidth); 0336 } 0337 return indent; 0338 } 0339 0340 // kate: space-indent on; indent-width 4; replace-tabs on;