--[[ indentation for SciTE version 1.1 - copyleft by Prim, Andras : do with this code whatever you want Handled languages: HTML, PHP, C, CPP, JavaScript, embedded JavaScript, XML This script uses the syntax highlight info, so it works correctly, when the correct lexer is used. Put something like this in your User options file (SciTEUser.properties) ext.lua.startup.script= /home/bandi/scite/indent.lua command.name.1.*=indent command.1.*=do_indent command.subsystem.1.*=3 command.mode.1.*=savebefore:no This will make a menu item in your "Tools" menu, that can be used with Control+1 shortcut ]] --[[ just a helper for me ]] function showstyle() print ('lexer: ' .. editor.Lexer .. ' style: ' .. editor.StyleAt[editor.CurrentPos]) end function do_indent() langdecide = t_langdecide[editor.Lexer] if nil == langdecide then print 'unknown language' return end editor:Colourise(0,-1) editor:BeginUndoAction() indent_init() for i = 0, editor.LineCount, 1 do iline = i+1 -- global for linenum --~ print (iline) local startpos = editor.LineIndentPosition[i] -- $lang: language of current line, according to the style of the first character local lang = langdecide[editor.StyleAt[startpos]] -- indent only if the language is known if (lang) then c_context = context[lang] -- current context local indent = c_context.indent local nextindent = c_context.nextindent local pstack = c_context.pstack local langrules = t_langrules[lang] editor.LineIndentation[i]=0 -- this is needed to uniformize the space/tabs in indentation startpos = editor.LineIndentPosition[i] local line = editor:GetLine(i) if (line) then --[[ $beginpos holds the beginning of found patterns i.e. beginpos[10]='if' means there is an 'if' beginning at 10 in the line ]] local linelength = string.len(line) local beginpos = {} for i = 1, linelength, 1 do beginpos[i] = false end for pattern,struct in pairs(langrules) do local bp, ep = string.find(line, pattern, 1) while bp do local match = string.sub(line, bp, ep) -- put in $beginpos only if it starts with correct Scintilla style if struct.beginstyle[editor.StyleAt[startpos + bp - 1]] then beginpos[bp] = {pattern , match} end bp, ep = string.find(line, pattern, bp + 1) end end -- now we found all the positions, let's count indentation for i = 1, linelength, 1 do if beginpos[i] then pattern = beginpos[i][1] match = beginpos[i][2] local go = true -- unindent things in the stack while go and table.getn(pstack) > 0 do local uni = langrules[pattern].unindent[pstack[table.getn(pstack)]] if 'function' == type(uni) then uni = uni(match) end if uni then if i == 1 then indent = indent - 1 end nextindent = nextindent - 1 if 0 ~= uni then go = false; end pstack[ table.getn(pstack) ] = nil -- removes last else go = false end end -- count the indentation local sti = langrules[pattern].indent if 'function' == type(sti) then sti = sti(match) end if 0 ~= sti then nextindent = nextindent + sti pstack[table.getn(pstack) + 1] = pattern end end -- if beginpos[i] end -- for i = 1, linelength, 1 end -- if line editor.LineIndentation[i]=editor.Indent * indent indent = nextindent -- save non-reference type members of context c_context.indent = nextindent c_context.nextindent = nextindent end -- if (indentstyles[editor.StyleAt[startpos]]) end -- for i = 1 to linenum editor:EndUndoAction() end --[[Here goes stuff, that must be done before each indentation run]] function indent_init () -- initialize context for all known languages context = {} for lang in pairs(t_langrules) do context[lang] = {['indent'] = 0, ['nextindent'] = 0, ['pstack'] = {} } end context['html'].tagstack = {} context['cpp'].forstack = {} context['php'].forstack = {} end --[[Rules and rule functions]] --[[ semantics of *rules[]: (where * is the name of the language) is a lua string.find pattern where this pattern is found in the indented source code, the followings should be done: 1. It must be checked whether the SciTE style at the beginning of the found pattern is a key in *rules[].beginstyle, and it's value evaluates to true. If not, this match should not be handled. 2. If the innermost pattern, that still has indentation effect is , and there is a key value pair: *rules[].unindent[] = then the indentation effect of ends in this line. If ==0 then the next innermost pattern is also checked. (And so on, while the for that pattern is still 0.) (This way we can unindent nested one-line conrulesions at the innermost statement like if(a) if(b) echo 'a and b'; ) If is a function, it should be called and the return value is to be used. When the return value is nil, it is treated as if there was no key in the table. 3. The next line should be indented additional *rules[].indent levels. If this field is a function, it is called and the return value is used. (currently only 0 and 1 are treated well) ]] --[[functions to handle C-style for]] -- functions for handling for (; ;) in which the two semicolons does not unindent the for function cstyle_for_indent() local forstack = c_context.forstack forstack[table.getn(forstack) + 1] = 2 return 1 -- it always increments indentation level end function cstyle_sc_for_unindent() local forstack = c_context.forstack if 0 == table.getn(forstack) then -- it's some syntax error return nil -- no unindentation end local i = forstack[table.getn(forstack)] if i == 0 then -- this semicolon is the third after the 'for' forstack[table.getn(forstack)] = nil return 0 -- unindentation else forstack[table.getn(forstack)] = i-1 return nil -- no unindentation end end function cstyle_ob_for_unindent() local forstack = c_context.forstack if 0 == table.getn(forstack) then -- it's some syntax error return nil -- no unindentation end forstack[table.getn(forstack)] = nil return 1 -- unindentation end --[[PHP rules]] phprules = {} phprules['if']={beginstyle = {[SCE_HPHP_WORD]=true}, -- SCITE styles that are acceptable at the beginning of the pattern indent = 1, -- how much it indents unindent = {['else'] = 1}} -- which patterns does this end --[[ 'elseif' can be parsed the same way as 'else if', but we have to put unindent = {['else'] = 1} in phprules['if'] to avoid double indentation ]] phprules['else']={beginstyle = {[SCE_HPHP_WORD]=true}, indent = 1, unindent = {}} phprules['while']={beginstyle = {[SCE_HPHP_WORD]=true}, indent = 1, unindent = {['do'] = 1}} phprules['do']={beginstyle = {[SCE_HPHP_WORD]=true}, indent = 1, unindent = {}} phprules['for']={beginstyle = {[SCE_HPHP_WORD]=true}, indent = cstyle_for_indent, unindent = {}} --[[ TODO if 'foreach' gets inserted later in phprules than 'for', it will be checked later in the script, therefore it will overwrite the match result of 'for'. That is fine, since we don't want to handle a 'for' where a 'foreach' appears in the source, but I don't know whether we can trust in this behaviour, or we should do something more to assure this order of 'for' and 'foreach' ]] phprules['foreach']={beginstyle = {[SCE_HPHP_WORD]=true}, indent = 1, unindent = {}} phprules['declare']={beginstyle = {[SCE_HPHP_WORD]=true}, indent = 1, unindent = {}} phprules['case']={beginstyle = {[SCE_HPHP_WORD]=true}, indent = 1, unindent = {['case'] = 1, ['default'] = 1}} phprules['default']={beginstyle = {[SCE_HPHP_WORD]=true}, indent = 1, unindent = {['case'] = 1, ['default'] = 1}} phprules['{']={beginstyle = {[SCE_HPHP_OPERATOR]=true}, indent = 1, unindent = {['if'] = 1, ['else'] = 1, ['while'] = 1, ['do'] = 1, ['for'] = cstyle_ob_for_unindent, ['foreach'] = 1, ['declare'] = 1}} phprules['}']={beginstyle = {[SCE_HPHP_OPERATOR]=true}, indent = 0, unindent = {['{'] = 1, ['case'] = 0, ['default'] = 0}} phprules[';']={beginstyle = {[SCE_HPHP_OPERATOR]=true}, indent = 0, unindent = {['if'] = 0, ['else'] = 0, ['while'] = 0, ['for'] = cstyle_sc_for_unindent, ['foreach'] = 0, ['declare'] = 0}} phprules['%(']={beginstyle = {[SCE_HPHP_OPERATOR]=true}, indent = 1, unindent = {['%('] = 1}} phprules['%)']={beginstyle = {[SCE_HPHP_OPERATOR]=true}, indent = 0, unindent = {['%('] = 1}} --[[CPP rules]] cpprules = {} cpprules['if']={beginstyle = {[SCE_C_WORD]=true}, indent = 1, unindent = {['else'] = 1}} cpprules['else']={beginstyle = {[SCE_C_WORD]=true}, indent = 1, unindent = {}} cpprules['while']={beginstyle = {[SCE_C_WORD]=true}, indent = 1, unindent = {['do%A'] = 1}} -- 'do%A' to avoid match of "double" cpprules['do%A']={beginstyle = {[SCE_C_WORD]=true}, indent = 1, unindent = {}} cpprules['for']={beginstyle = {[SCE_C_WORD]=true}, indent = cstyle_for_indent, unindent = {}} cpprules['case']={beginstyle = {[SCE_C_WORD]=true}, indent = 1, unindent = {['case'] = 1, ['default'] = 1}} cpprules['default']={beginstyle = {[SCE_C_WORD]=true}, indent = 1, unindent = {['case'] = 1, ['default'] = 1}} cpprules['{']={beginstyle = {[SCE_C_OPERATOR]=true}, indent = 1, unindent = {['if'] = 1, ['else'] = 1, ['while'] = 1, ['do%A'] = 1, ['for'] = cstyle_ob_for_unindent}} cpprules['}']={beginstyle = {[SCE_C_OPERATOR]=true}, indent = 0, unindent = {['{'] = 1, ['case'] = 0, ['default'] = 0}} cpprules[';']={beginstyle = {[SCE_C_OPERATOR]=true}, indent = 0, unindent = {['if'] = 0, ['else'] = 0, ['while'] = 0, ['for'] = cstyle_sc_for_unindent}} cpprules['%(']={beginstyle = {[SCE_C_OPERATOR]=true}, indent = 1, unindent = {}} cpprules['%)']={beginstyle = {[SCE_C_OPERATOR]=true}, indent = 0, unindent = {['%('] = 1}} --[[ add embedded JavaScript styles to cpprules ]] for k,v in pairs(cpprules) do if v.beginstyle[SCE_C_OPERATOR] then v.beginstyle[SCE_HJ_SYMBOLS] = true end if v.beginstyle[SCE_C_WORD] then v.beginstyle[SCE_HJ_KEYWORD] = true end end --[[HTML rule functions]] --[[ $html_indentable keys like this mean that the content between and should be indented ['body'] = true This line means, that content between and should be indented, and 1. the first table tells, what should be done, when parsing the tag 1.a. ['td'] = 0, ['th'] = 0 means, that if the last unended tag is a or then that tag must be implicite ended (and unindented) and the next unended tag must be checked too (and so on) 1.b. ['tr'] = 1 means, that if the last unended tag is a , that must be ended, and other unended tags should not be checked 2. The second table tells what should be done, when parsing the tag ['tr'] = {{['td'] = 0, ['th'] = 0, ['tr'] = 1}, {['td'] = 0, ['td'] = 0, ['tr'] = 1} } ]] html_indentable = {['address'] = true, ['blockquote'] = true, ['body'] = true, ['center'] = true, ['dir'] = true, ['div'] = true, ['dd'] = {{['dd'] = 1, ['dt'] = 1}, {['dd'] = 1} }, ['dl'] = true, ['dt'] = {{['dd'] = 1, ['dt'] = 1}, {['dt'] = 1} }, ['fieldset'] = true, ['form'] = true, ['frameset'] = true, ['h1'] = true, ['h2'] = true, ['h3'] = true, ['h4'] = true, ['h5'] = true, ['h6'] = true, ['head'] = true, ['isindex'] = true, ['li'] = true, ['menu'] = true, ['noframes'] = true, ['noscript'] = true, ['ol'] = true, --

behaves oddly, so I don't indent it --~ ['p'] = {{['p'] = 1}, {['p'] = 1} }, ['pre'] = true, ['table'] = {false, {['td'] = 0, ['th'] = 0, ['tr'] = 0, ['table'] = 1} }, ['ul'] = true, ['tbody'] = true, ['td'] = {{['td'] = 1, ['th'] = 1}, {['td'] = 1} }, ['tfoot'] = true, ['th'] = {{['th'] = 1, ['td'] = 1}, {['th'] = 1} }, ['thead'] = true, ['tr'] = {{['td'] = 0, ['th'] = 0, ['tr'] = 1}, {['td'] = 0, ['td'] = 0, ['tr'] = 1} } } function html_bt_indent (match) local tagstack = c_context.tagstack local tag = string.lower(string.sub(match, 2)) if html_indentable[tag] then c_context.tagstack[table.getn(c_context.tagstack) + 1] = tag return 1 end return 0 end function html_bt_unindent(match) local tagstack = c_context.tagstack local tag = string.lower(string.sub(match, 2)) if 'table' ~= type(html_indentable[tag]) then return nil end local stable = html_indentable[tag][1] if 'table' ~= type(stable) then return nil end local htl = table.getn(c_context.tagstack) local retval retval = stable[c_context.tagstack[htl]] if retval then c_context.tagstack[htl] = nil end return retval end function html_et_unindent(match) local tagstack = c_context.tagstack local tag = string.lower(string.sub(match, 3)) if not html_indentable[tag] then return nil end local htl = table.getn(c_context.tagstack) local retval if 'table' == type(html_indentable[tag]) then local stable = html_indentable[tag][2] retval = stable[c_context.tagstack[htl]] else if tag == c_context.tagstack[htl] then retval = 1 else retval = 0 end end c_context.tagstack[htl] = nil return retval end --[[HTML rules]] htmlrules = {} htmlrules['<%a+']={beginstyle = {[SCE_H_TAG]=true}, indent = html_bt_indent, unindent = {['<%a+'] = html_bt_unindent}} htmlrules['']={beginstyle = {[SCE_H_TAGEND]=true}, indent = 0, unindent = {['<%a+'] = 1}} --[[information for deciding what language does the document contain ]] htmldecide={} -- PHP styles: only lines that begin with one of these styles are treated as PHP htmldecide[SCE_HPHP_COMPLEX_VARIABLE] = 'php' htmldecide[SCE_HPHP_DEFAULT] = 'php' htmldecide[SCE_HPHP_HSTRING] = 'php' htmldecide[SCE_HPHP_SIMPLESTRING] = 'php' htmldecide[SCE_HPHP_WORD] = 'php' htmldecide[SCE_HPHP_NUMBER] = 'php' htmldecide[SCE_HPHP_VARIABLE] = 'php' htmldecide[SCE_HPHP_COMMENT] = 'php' htmldecide[SCE_HPHP_COMMENTLINE] = 'php' htmldecide[SCE_HPHP_HSTRING_VARIABLE] = 'php' htmldecide[SCE_HPHP_OPERATOR] = 'php' -- HTML styles htmldecide[SCE_H_DEFAULT]='html' htmldecide[SCE_H_TAG]='html' htmldecide[SCE_H_TAGUNKNOWN]='html' htmldecide[SCE_H_ATTRIBUTE]='html' htmldecide[SCE_H_ATTRIBUTEUNKNOWN]='html' htmldecide[SCE_H_NUMBER]='html' htmldecide[SCE_H_DOUBLESTRING]='html' htmldecide[SCE_H_SINGLESTRING]='html' htmldecide[SCE_H_OTHER]='html' htmldecide[SCE_H_COMMENT]='html' htmldecide[SCE_H_ENTITY]='html' htmldecide[SCE_H_TAGEND]='html' -- embedded JavaScript styles htmldecide[SCE_HJ_START]='cpp' htmldecide[SCE_HJ_DEFAULT]='cpp' htmldecide[SCE_HJ_COMMENT]='cpp' htmldecide[SCE_HJ_COMMENTLINE]='cpp' htmldecide[SCE_HJ_COMMENTDOC]='cpp' htmldecide[SCE_HJ_NUMBER]='cpp' htmldecide[SCE_HJ_WORD]='cpp' htmldecide[SCE_HJ_KEYWORD]='cpp' htmldecide[SCE_HJ_DOUBLESTRING]='cpp' htmldecide[SCE_HJ_SINGLESTRING]='cpp' htmldecide[SCE_HJ_SYMBOLS]='cpp' htmldecide[SCE_HJ_STRINGEOL]='cpp' htmldecide[SCE_HJ_REGEX]='cpp' -- CPP styles cppdecide = {} cppdecide[SCE_C_DEFAULT]='cpp' cppdecide[SCE_C_COMMENT]='cpp' cppdecide[SCE_C_COMMENTLINE]='cpp' cppdecide[SCE_C_COMMENTDOC]='cpp' cppdecide[SCE_C_NUMBER]='cpp' cppdecide[SCE_C_WORD]='cpp' cppdecide[SCE_C_STRING]='cpp' cppdecide[SCE_C_CHARACTER]='cpp' cppdecide[SCE_C_UUID]='cpp' cppdecide[SCE_C_PREPROCESSOR]='cpp' cppdecide[SCE_C_OPERATOR]='cpp' cppdecide[SCE_C_IDENTIFIER]='cpp' cppdecide[SCE_C_STRINGEOL]='cpp' cppdecide[SCE_C_VERBATIM]='cpp' cppdecide[SCE_C_REGEX]='cpp' cppdecide[SCE_C_COMMENTLINEDOC]='cpp' cppdecide[SCE_C_WORD2]='cpp' cppdecide[SCE_C_COMMENTDOCKEYWORD]='cpp' cppdecide[SCE_C_COMMENTDOCKEYWORDERROR]='cpp' cppdecide[SCE_C_GLOBALCLASS]='cpp' -- XML styles xmldecide = {} xmldecide[SCE_H_DEFAULT]='xml' xmldecide[SCE_H_TAG]='xml' xmldecide[SCE_H_TAGUNKNOWN]='xml' xmldecide[SCE_H_ATTRIBUTE]='xml' xmldecide[SCE_H_ATTRIBUTEUNKNOWN]='xml' xmldecide[SCE_H_NUMBER]='xml' xmldecide[SCE_H_DOUBLESTRING]='xml' xmldecide[SCE_H_SINGLESTRING]='xml' xmldecide[SCE_H_OTHER]='xml' xmldecide[SCE_H_COMMENT]='xml' xmldecide[SCE_H_ENTITY]='xml' xmldecide[SCE_H_TAGEND]='xml' -- t_langdecide[][]='' t_langdecide = {} t_langdecide[SCLEX_HTML] = htmldecide t_langdecide[SCLEX_CPP] = cppdecide t_langdecide[SCLEX_XML] = xmldecide t_langrules = {} t_langrules['php'] = phprules t_langrules['html'] = htmlrules t_langrules['cpp'] = cpprules t_langrules['xml'] = xmlrules