File: xml.js

package info (click to toggle)
ktexteditor 5.103.0-1.1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 39,984 kB
  • sloc: cpp: 88,272; javascript: 19,743; xml: 237; sh: 20; ansic: 16; makefile: 11
file content (171 lines) | stat: -rw-r--r-- 5,713 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
var katescript = {
    "name": "XML Style",
    "author": "Milian Wolff <mail@milianw.de>, Gerald Senarclens de Grancy <oss@senarclens.eu>",
    "license": "LGPL",
    "revision": 3,
    "kate-version": "5.1"
}; // kate-script-header, must be at the start of the file without comments, pure json

/*
    This file is part of the Kate Project.

    SPDX-License-Identifier: LGPL-2.0-only
*/


// required katepart js libraries
require ("range.js");
require ("string.js");

var DEBUG = false;  // disable before checking in!

function dbg(s) {
    if (DEBUG)
        debug(s);
}

// specifies the characters which should trigger indent, beside the default '\n'
triggerCharacters = ">";

// used regular expressions
// dear reader, please forgive me for using regular expressions in relation
// with xml/html

// match the beginning of any opening tag; special tags (<?..., <[... and <!...)
// are ignored
opening = /<[^\/?\[!]/g;
// matches the beginning of any closing tag
closing = /<\//g;
// matches if there is nothing but a single closing tag
single_closing_tag = /^<\/[^<>]*>$/;
open_tag = /<[^\[]/;  // starting of a non-CDATA tag
close_tag = /[^\]]>|^>/;  // end of a non-CDATA tag
// ignore indenting if a line is started with one of the following tags
ignore_tags = [/^<html/, /^<body/];


// Return the code string of the given lineNr (no comments or strings etc).
// Also strips self-closing tags.
// Eg.
// if document.line(x) is "  <p id="go">test</p> "
//   getCode(x) -> "<p>test</p>"
// if document.line(x) == "  <p id="go">test<br /></p> "
//   getCode(x) -> "<p>test</p>"
// if document.line(x) == "<p>test<!-- <em>comment</em> --></p>"
//   getCode(x) -> "<p>test</p>"
function getCode(lineNr) {
    var line = document.line(lineNr);
    var code = "";
    for (var col = 0; col < line.length; ++col) {
        if (document.isCode(lineNr, col))
            code += line[col];
    }
    code = replaceSelfClosing(code);
    return code.trim();
}


// Return given code with all self-closing tags removed.
function replaceSelfClosing(code) {
    return code.replace(/<[^<>]*\/>/g, '<tag></tag>');
}


// Check if the last line was endet inside a tag and return appropriate indent.
// If the last line wasn't endet inside a tag, return -1.
// Otherwise, return appropriate indent so that attributes are aligned.
function _calcAttributeIndent(lineNr, indentWidth) {
    var text = document.line(lineNr);
    var num_open_tag = text.countMatches(open_tag);
    var num_close_tag = text.countMatches(close_tag);
    if (num_open_tag > num_close_tag) {
        dbg("unfinished tag");
        for (col = text.lastIndexOf("<"); col < text.length; ++col) {
            if (document.isOthers(lineNr, col) &&
                document.isSpace(lineNr, col))
                return col + 1;
        }
    } else if (num_open_tag < num_close_tag) {
        dbg("closing unfinished tag");
        code = getCode(lineNr);
        do {
            lineNr--;
            var line = document.line(lineNr);
            code = getCode(lineNr) + code;
        } while ((line.countMatches(open_tag) <= line.countMatches(close_tag)) &&
                 (lineNr > 0));
        var prevIndent = Math.max(document.firstVirtualColumn(lineNr), 0);
        code = replaceSelfClosing(code);
        var steps = calcSteps(code);
        return prevIndent + indentWidth * steps;
    }
    return -1;  // by default, keep last line's indent (-1)
}


// Return the number of steps to indent/ un-indent.
// If the code is matched by any element of ignore_tags, 0 is returned.
function calcSteps(code) {
    for (var key in ignore_tags) {
        if (code.match(ignore_tags[key]))
            return 0;
    }
    var num_opening = code.match(opening) ? code.match(opening).length : 0;
    var num_closing = code.match(closing) ? code.match(closing).length : 0;
    return num_opening - num_closing;
}


// Return the amount of characters (in spaces) to be indented.
// Called for each newline (ch == '\n') and all characters specified in
// the global variable triggerCharacters. When calling Tools → Align
// the variable ch is empty, i.e. ch == ''.
// Special indent() return values:
//   -1: keep last indent
//   -2: do nothing
function indent(lineNr, indentWidth, char) {
    dbg("lineNr: " + lineNr + " indentWidth: " + indentWidth + " char: " + char);
    if (lineNr == 0)  // don't ever act on document's first line
        return -2;

    var lastLineNr = lineNr - 1;
    var lastLine = getCode(lastLineNr);
    dbg("lastLine1: " + lastLine);
    while (lastLine == "") {
        lastLineNr--;
        if (lastLineNr <= 0) {
            return -1;
        }
        lastLine = getCode(lastLineNr);
    }
    dbg("lastLine2: " + lastLine);

    // default action (for char == '\n' or char == '')
    var indent = _calcAttributeIndent(lastLineNr, indentWidth);
    if (indent != -1) {
        return indent;
    }
    dbg("indent: " + indent);

    indent = Math.max(document.firstVirtualColumn(lastLineNr), 0);
    dbg("indent: " + indent);


    var steps = calcSteps(lastLine);
    // unindenting separate closing tags are dealt with by last line
    if (steps && !lastLine.match(single_closing_tag))
        indent += indentWidth * steps;

    // set char if required (eg. in case of align or copy and paste)
    if (char == ">" || !char) {  // ok b/c of inner if
        // if there is nothing but a separate closing tag, unindent
        var curLine = getCode(lineNr);
        dbg("curLine: " + curLine);
        if (curLine.match(single_closing_tag)) {
            return Math.max(indent - indentWidth, 0);
        }
    }
    return Math.max(indent, -1);
}

// kate: space-indent on; indent-width 4; replace-tabs on;