" Vim indent file
" Language: Falcon
" Maintainer: Steven Oliver <oliver.steven@gmail.com>
" Website: https://steveno@github.com/steveno/falconpl-vim.git
" Credits: Thanks to the ruby.vim authors, I borrow a lot!
" Previous Maintainer: Brent A. Fulgham <bfulgham@debian.org>
" -----------------------------------------------------------

"======================================
"       SETUP
"======================================

" Only load this indent file when no other was loaded.
if exists("b:did_indent")
    finish
endif
let b:did_indent = 1

setlocal nosmartindent

" Setup indent function and when to use it
setlocal indentexpr=FalconGetIndent()
setlocal indentkeys=0{,0},0),0],!^F,o,O,e
setlocal indentkeys+==~case,=~catch,=~default,=~elif,=~else,=~end,=~\"

" Define the appropriate indent function but only once
if exists("*FalconGetIndent")
    finish
endif

let s:cpo_save = &cpo
set cpo&vim

"======================================
"       VARIABLES
"======================================

" Regex of syntax group names that are strings AND comments
let s:syng_strcom = '\<falcon\%(String\|StringEscape\|Comment\)\>'

" Regex of syntax group names that are strings
let s:syng_string = '\<falcon\%(String\|StringEscape\)\>'

" Keywords to indent on
let s:falcon_indent_keywords = '^\s*\(case\|catch\|class\|enum\|default\|elif\|else' .
    \ '\|for\|function\|if.*"[^"]*:.*"\|if \(\(:\)\@!.\)*$\|loop\|object\|select' .
    \ '\|switch\|try\|while\|\w*\s*=\s*\w*([$\)'

" Keywords to deindent on
let s:falcon_deindent_keywords = '^\s*\(case\|catch\|default\|elif\|else\|end\)'

"======================================
"       FUNCTIONS
"======================================

" Check if the character at lnum:col is inside a string
function s:IsInStringOrComment(lnum, col)
    return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_strcom
endfunction

"======================================
"       INDENT ROUTINE
"======================================

function FalconGetIndent()
    " Get the line to be indented
    let cline = getline(v:lnum)

    " Don't reindent comments on first column
    if cline =~ '^\/\/'
        return 0
    endif

    " Find the previous non-blank line
    let lnum = prevnonblank(v:lnum - 1)

    " Use zero indent at the top of the file
    if lnum == 0
        return 0
    endif

    let prevline=getline(lnum)
    let ind = indent(lnum)
    let chg = 0

    " If we are in a multi-line string or line-comment, don't do anything
    if s:IsInStringOrComment(v:lnum, matchend(cline, '^\s*') + 1 )
        return indent('.')
    endif

    " If the start of the line equals a double quote, then indent to the
    " previous lines first double quote
    if cline =~? '^\s*"'
        let chg = chg + &sw
    endif

    " If previous line started with a double quote and this one
    " doesn't, unindent
    if prevline =~? '^\s*"' && cline =~? '^\s*'
        let chg = chg - &sw
    endif

    " Indent if proper keyword
    if prevline =~? s:falcon_indent_keywords
        let chg = &sw
    " If previous line opened a parenthesis, and did not close it, indent
    elseif prevline =~ '^.*(\s*[^)]*\((.*)\)*[^)]*$'
        " Make sure this isn't just a function split between two lines
        if prevline =~ ',\s*$'
            return indent(prevnonblank(v:lnum - 1)) + &sw
        else
            return match(prevline, '(.*\((.*)\|[^)]\)*.*$') + 1
        endif
    elseif prevline =~ '^[^(]*)\s*$'
        " This line closes a parenthesis. Finds opening.
        let curr_line = prevnonblank(lnum - 1)
        while curr_line >= 0
            let str = getline(curr_line)
            if str !~ '^.*(\s*[^)]*\((.*)\)*[^)]*$'
                let curr_line = prevnonblank(curr_line - 1)
            else
                break
            endif
        endwhile
        if curr_line < 0
            return -1
        endif
        let ind = indent(curr_line)
    endif

    " If previous line ends in a semi-colon reset indent to previous
    " lines setting
    if prevline =~? ';\s*$' && prevnonblank(prevline) =~? ',\s*$'
        let chg = chg - (2 * &sw)
    endif

    " If previous line ended in a comma, indent again
    if prevline =~? ',\s*$'
        let chg = chg + &sw
    endif

    " If previous line ended in a =>, indent again
    if prevline =~? '=>\s*$'
        let chg = chg + &sw
    endif

    " Deindent on proper keywords
    if cline =~? s:falcon_deindent_keywords
        let chg = chg - &sw
    endif

    return ind + chg
endfunction

let &cpo = s:cpo_save
unlet s:cpo_save

" vim: set sw=4 sts=4 et tw=80 :
