File indexing completed on 2024-05-19 04:00:10
0001 var katescript = { 0002 "name": "XML Style", 0003 "author": "Milian Wolff <mail@milianw.de>, Gerald Senarclens de Grancy <oss@senarclens.eu>", 0004 "license": "LGPL", 0005 "revision": 3, 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 0016 // required katepart js libraries 0017 require ("range.js"); 0018 require ("string.js"); 0019 0020 var DEBUG = false; // disable before checking in! 0021 0022 function dbg(s) { 0023 if (DEBUG) 0024 debug(s); 0025 } 0026 0027 // specifies the characters which should trigger indent, beside the default '\n' 0028 triggerCharacters = ">"; 0029 0030 // used regular expressions 0031 // dear reader, please forgive me for using regular expressions in relation 0032 // with xml/html 0033 0034 // match the beginning of any opening tag; special tags (<?..., <[... and <!...) 0035 // are ignored 0036 opening = /<[^\/?\[!]/g; 0037 // matches the beginning of any closing tag 0038 closing = /<\//g; 0039 // matches if there is nothing but a single closing tag 0040 single_closing_tag = /^<\/[^<>]*>$/; 0041 open_tag = /<[^\[]/; // starting of a non-CDATA tag 0042 close_tag = /[^\]]>|^>/; // end of a non-CDATA tag 0043 // ignore indenting if a line is started with one of the following tags 0044 ignore_tags = [/^<html/, /^<body/]; 0045 0046 0047 // Return the code string of the given lineNr (no comments or strings etc). 0048 // Also strips self-closing tags. 0049 // Eg. 0050 // if document.line(x) is " <p id="go">test</p> " 0051 // getCode(x) -> "<p>test</p>" 0052 // if document.line(x) == " <p id="go">test<br /></p> " 0053 // getCode(x) -> "<p>test</p>" 0054 // if document.line(x) == "<p>test<!-- <em>comment</em> --></p>" 0055 // getCode(x) -> "<p>test</p>" 0056 function getCode(lineNr) { 0057 var line = document.line(lineNr); 0058 var code = ''; 0059 // // Skip any spaces at the front 0060 var col = document.firstVirtualColumn(lineNr); 0061 if (col == -1) 0062 return code; 0063 for (; col < line.length; ++col) { 0064 if (document.isCode(lineNr, col)) 0065 code += line[col]; 0066 } 0067 code = replaceSelfClosing(code); 0068 return code.trim(); 0069 } 0070 0071 0072 // Return given code with all self-closing tags removed. 0073 function replaceSelfClosing(code) { 0074 return code.replace(/<[^<>]*\/>/g, '<tag></tag>'); 0075 } 0076 0077 0078 // Check if the last line was endet inside a tag and return appropriate indent. 0079 // If the last line wasn't endet inside a tag, return -1. 0080 // Otherwise, return appropriate indent so that attributes are aligned. 0081 function _calcAttributeIndent(lineNr, indentWidth) { 0082 var text = document.line(lineNr); 0083 var num_open_tag = text.countMatches(open_tag); 0084 var num_close_tag = text.countMatches(close_tag); 0085 if (num_open_tag > num_close_tag) { 0086 dbg("unfinished tag"); 0087 for (col = text.lastIndexOf("<"); col < text.length; ++col) { 0088 if (document.isOthers(lineNr, col) && 0089 document.isSpace(lineNr, col)) 0090 return col + 1; 0091 } 0092 } else if (num_open_tag < num_close_tag) { 0093 dbg("closing unfinished tag"); 0094 code = getCode(lineNr); 0095 do { 0096 lineNr--; 0097 var line = document.line(lineNr); 0098 code = getCode(lineNr) + code; 0099 } while ((line.countMatches(open_tag) <= line.countMatches(close_tag)) && 0100 (lineNr > 0)); 0101 var prevIndent = Math.max(document.firstVirtualColumn(lineNr), 0); 0102 code = replaceSelfClosing(code); 0103 var steps = calcSteps(code); 0104 return prevIndent + indentWidth * steps; 0105 } 0106 return -1; // by default, keep last line's indent (-1) 0107 } 0108 0109 0110 // Return the number of steps to indent/ un-indent. 0111 // If the code is matched by any element of ignore_tags, 0 is returned. 0112 function calcSteps(code) { 0113 for (var key in ignore_tags) { 0114 if (code.match(ignore_tags[key])) 0115 return 0; 0116 } 0117 var num_opening = code.match(opening) ? code.match(opening).length : 0; 0118 var num_closing = code.match(closing) ? code.match(closing).length : 0; 0119 return num_opening - num_closing; 0120 } 0121 0122 0123 // Return the amount of characters (in spaces) to be indented. 0124 // Called for each newline (ch == '\n') and all characters specified in 0125 // the global variable triggerCharacters. When calling Tools → Align 0126 // the variable ch is empty, i.e. ch == ''. 0127 // Special indent() return values: 0128 // -1: keep last indent 0129 // -2: do nothing 0130 function indent(lineNr, indentWidth, char) { 0131 dbg("lineNr: " + lineNr + " indentWidth: " + indentWidth + " char: " + char); 0132 if (lineNr == 0) // don't ever act on document's first line 0133 return -2; 0134 0135 var lastLineNr = lineNr - 1; 0136 var lastLine = getCode(lastLineNr); 0137 dbg("lastLine1: " + lastLine); 0138 while (lastLine == "") { 0139 lastLineNr--; 0140 if (lastLineNr <= 0) { 0141 return -1; 0142 } 0143 lastLine = getCode(lastLineNr); 0144 } 0145 dbg("lastLine2: " + lastLine); 0146 0147 // default action (for char == '\n' or char == '') 0148 var indent = _calcAttributeIndent(lastLineNr, indentWidth); 0149 if (indent != -1) { 0150 return indent; 0151 } 0152 dbg("indent: " + indent); 0153 0154 indent = Math.max(document.firstVirtualColumn(lastLineNr), 0); 0155 dbg("indent: " + indent); 0156 0157 0158 var steps = calcSteps(lastLine); 0159 // unindenting separate closing tags are dealt with by last line 0160 if (steps && !lastLine.match(single_closing_tag)) 0161 indent += indentWidth * steps; 0162 0163 // set char if required (eg. in case of align or copy and paste) 0164 if (char == ">" || !char) { // ok b/c of inner if 0165 // if there is nothing but a separate closing tag, unindent 0166 var curLine = getCode(lineNr); 0167 dbg("curLine: " + curLine); 0168 if (curLine.match(single_closing_tag)) { 0169 return Math.max(indent - indentWidth, 0); 0170 } 0171 } 0172 return Math.max(indent, -1); 0173 } 0174 0175 // kate: space-indent on; indent-width 4; replace-tabs on;