4
* author: Erlend Hamberg <ehamberg@gmail.com>
10
// based on Paul Giannaro's Python indenter
12
var debugMode = false;
14
// words for which space character-triggered indentation should be done
15
var re_spaceIndent = /\bwhere\b|\bin\b|\belse\b/
17
// ‘|’-triggered indentation should only indent if the line starts with ‘|’
18
var re_pipeIndent = /^\s*\|/
21
var re_symbols = /^\s*[!$#%&*+.\/<=>?@\\^|~-]/
23
// escapes text w.r.t. regex special chars
24
function escape(text) {
25
return text.replace(/(\/|\.|,|\+|\?|\||\*|\(|\)|\[|\]|\{|\}|\\)/g, "\\$1");
28
String.prototype.startsWith = function(prefix) {
29
return this.search(RegExp("^"+escape(prefix)+"(\\b|\\s|$)")) != -1;
32
String.prototype.endsWith = function(suffix) {
33
return this.search(RegExp("(\\b|\\s|^)"+escape(suffix)+"\\s*$")) != -1;
36
String.prototype.lastCharacter = function() {
41
return this.charAt(l - 1);
44
String.prototype.stripWhiteSpace = function() {
45
return this.replace(/^[ \t\n\r]+/, '').replace(/[ \t\n\r]+$/, '');
50
// debug to the term in blue so that it's easier to make out amongst all
51
// of Kate's other debug output.
53
debug("\u001B[34m" + s + "\u001B[0m");
56
var triggerCharacters = "|} ";
59
// indent() returns the amount of characters (in spaces) to be indented.
60
// Special indent() return values:
62
// -1 = keep last indent
64
function indent(line, indentWidth, character) {
65
dbg(document.attribute.toString());
66
dbg("indent character: '" + character + "'");
67
var currentLine = document.line(line);
68
dbg("current line: " + currentLine);
69
var lastLine = document.line(line - 1);
70
dbg("last line: " + lastLine);
71
var lastCharacter = lastLine.lastCharacter();
73
// invocations triggered by a space character should be ignored unless the
74
// line starts with one of the words in re_spaceIndent
75
if (character == ' ') {
76
if (currentLine.search(re_spaceIndent) == -1 ||
77
!document.isCode(line, document.lineLength(line) - 2)) {
81
} else if (character == '|') {
82
if (currentLine.search(re_pipeIndent) == -1 ||
83
!document.isCode(line, document.lineLength(line) - 2)) {
89
// we can't really indent line 0
93
// make sure [some of] the last line is code
94
if (!document.isCode(line - 1, document.lineLength(line - 1) - 1)
95
&& !document.isCode(line - 1, 0)
96
&& lastCharacter != "\"" && lastCharacter != "'") {
97
dbg("attributes that we don't want! Returning");
101
// check the line contents ...
103
// rules that check the end of the last line.
104
// first, check that the end of the last line actually is code...
105
if (document.isCode(line - 1, document.lineLength(line - 1) - 1)) {
106
// indent lines following a line ending with '='
107
if (lastLine.endsWith("=")) {
108
dbg('indenting for =');
109
return document.firstVirtualColumn(line - 1) + indentWidth;
112
// indent lines following a line ending with '{'
113
if (lastLine.endsWith("{")) {
114
dbg('indenting for {');
115
return document.firstVirtualColumn(line - 1) + indentWidth;
118
// indent lines following a line ending with 'do'
119
if (lastLine.endsWith("do")) {
120
dbg('indenting for do');
121
return document.firstVirtualColumn(line - 1) + indentWidth;
124
// indent line after myFunction = do ...
125
// foo = do bar <- baz
126
// >>>>>>>>>qzx <- qqx
127
if (lastLine.search(/\s=\sdo\s\S/)!= -1) {
128
dbg('indenting line for “... = do ...”');
129
var doCol = lastLine.search(/do\s/);
130
return document.firstVirtualColumn(line - 1) + doCol + 3;
134
// indent line after 'where' unless it starts with 'module'
135
// instance Functor Tree where
136
// >>>>fmap = treeMap
137
if (lastLine.endsWith('where') && !lastLine.startsWith('module')) {
138
dbg('indenting line for where (3)');
139
return document.firstVirtualColumn(line - 1) + indentWidth;
143
// indent line after 'where' 6 characters for alignment:
146
if (lastLine.search(/\s*where\s+[^-]/) != -1) {
147
dbg('indenting line for where (0)');
148
return document.firstVirtualColumn(line - 1) + 6;
151
// indent line after 'where' 6 characters for alignment:
152
// ... where -- comment
154
if (lastLine.stripWhiteSpace().startsWith('where')) {
155
dbg('indenting line for where (1)');
156
return document.firstVirtualColumn(line - 1) + indentWidth;
159
// indent 'where' to column 0 + indentWidth
162
if (currentLine.stripWhiteSpace().startsWith('where')) {
163
dbg('indenting line for where (2)');
165
if (lastLine.startsWith('else')) {
166
return document.firstVirtualColumn(line - 1) + indentWidth;
172
// indent line after 'let' 4 characters for alignment:
177
// * we're in a do block OR
178
// * the current line starts with 'in'
179
var letCol = lastLine.search(/\blet\b/);
180
if (letCol != -1 && !currentLine.stripWhiteSpace().startsWith('in')) {
182
// do a basic test of whether we are in a do block or not
185
// find the last non-empty line with an indentation level different
186
// from the current line ...
187
while (document.firstVirtualColumn(l) == document.firstVirtualColumn(line-1)
188
|| document.line(l).search(/^\s*$/) != -1) {
192
// ... if that line ends with 'do', don't indent
193
if (document.line(l).endsWith('do')) {
194
dbg('not indenting for let; in a do block');
198
dbg('indenting line for let');
202
// deindent line starting with 'in' to the level of its corresponding 'let':
206
if (currentLine.stripWhiteSpace().startsWith('in')) {
207
dbg('indenting line for in');
210
while (t >= 0 && line-t < 100) {
211
var letCol = document.line(t).search(/\blet\b/);
221
// deindent line starting with 'else' to the level of 'then':
226
if (currentLine.stripWhiteSpace().startsWith('else')) {
227
dbg('indenting line for else');
230
while (t >= 0 && line-t < 100) {
231
var thenCol = document.line(t).search(/\bthen\b/); // \s*\bthen\b
241
// indent line after a line with just 'in' one indent width:
246
if (lastLine.stripWhiteSpace() == 'in') {
247
dbg('indenting line after in');
248
return document.firstVirtualColumn(line - 1) + indentWidth;
251
// indent line after 'case' 5 characters for alignment:
254
// >>>>>(y:ys) -> ...
255
var caseCol = lastLine.search(/\bcase\b/);
257
dbg('indenting line for case');
261
// indent line after 'if/else' 3 characters for alignment:
265
var ifCol = lastLine.search(/\bif\b/);
266
var thenCol = lastLine.search(/\bthen\b/);
267
var elseCol = lastLine.search(/\belse\b/);
268
if (ifCol != -1 && thenCol == -1 && elseCol == -1) {
269
dbg('indenting line for if');
273
// indent line starting with "deriving: ":
274
// data Tree a = Node a (Tree a) (Tree a)
276
// >>>>>>>>>>>>>>deriving (Show)
280
// data Bool = True | False
281
// >>>>>deriving (Show)
282
if (currentLine.stripWhiteSpace().startsWith('deriving')) {
283
dbg('indenting line for deriving');
285
var pipeCol = lastLine.search(/\|/);
286
if (lastLine.stripWhiteSpace().startsWith('data')) {
287
return document.firstVirtualColumn(line - 1) + indentWidth;
289
else if (pipeCol != -1) {
291
while (lastLine[document.firstVirtualColumn(line - 1)+pipeCol+t] == ' ') {
294
return pipeCol + t + 1;
297
return document.firstVirtualColumn(line - 1) + 2;
301
// indent lines starting with '|' (guards or alternate constructors):
310
if (currentLine.stripWhiteSpace().startsWith("|")) {
311
dbg('indenting line for |');
312
var equalsCol = lastLine.search(/=/);
313
var pipeCol = lastLine.search(/\|/);
314
if (equalsCol != -1 && lastLine.stripWhiteSpace().startsWith('data')) {
317
else if (pipeCol != -1) {
321
return document.firstVirtualColumn(line - 1) + indentWidth;
325
// line starting with !#$%&*+./<=>?@\^|~-
326
if (document.isCode(line, document.lineLength(line) - 1)
327
&& currentLine.search(re_symbols) != -1
328
&& lastLine.search(re_symbols) == -1) {
329
dbg('indenting for operator');
330
return document.firstVirtualColumn(line - 1) + indentWidth;
333
// the line after aline ending with a comma should be indented
334
if (document.isCode(line - 1, document.lineLength(lastLine) - 1)
335
&& lastLine.search(',\s*$') != -1) {
336
dbg('indenting after line ending with comma');
337
return document.firstVirtualColumn(line - 1) + indentWidth;
340
// [de]indent line starting wih '}' to match the indentation level of '{':
345
if (currentLine.stripWhiteSpace().endsWith('}')) {
346
dbg('indenting line for }');
349
while (t >= 0 && line-t < 100) {
350
var braceCol = document.line(t).search(/{/);
351
if (braceCol != -1) {
352
indent = document.firstVirtualColumn(t);
360
//if (lastLine.search(/^\s*$/) != -1) {
361
// dbg('indenting for empty line');
365
dbg('continuing with regular indent');
369
// kate: space-indent on; indent-width 4; replace-tabs on;