I've tried to convince vim to parse my import statements in EcmaScript modules. My goal is to use vim's native include-search (and definition search) tools without tags with vanilla EcmaScript. Though the language is of little concern. The point is, these import statements (i.e. includes) are often relative and arbitrarily deep.

I have started writing a vimscript that somewhat solves the problem - not always, but it's probably buggy and may be improved - but it is terrible guess work and certainly not a good solution. I'm missing one piece of information to solve this and that is:

What's the path of the file in which the import(/include) statement was found, that I'm currently processing in my includeexpr?

I've checked all defined variables and registers but with no luck. Is it possible to solve this?

Finally, here's what I currently have. Maybe you see another/better way of doing this that I missed:

let &l:include     = '\(from\s*\("\|''\)\zs[^''"]\+\)\|\(require(\zs[^)]\+\)'
let &l:define      = '\v(export\s+(default\s+)?)?(var|let|const|function|class)|export\s+'
let &l:includeexpr = 'JsIncludeExpr(v:fname)'

let s:lastFile = []

function! JsIncludeExpr(file)
        for f in reverse(copy(s:lastFile))
            let l:file = JsIncludeExpr2(a:file, f)
                let s:lastFile = s:lastFile[0:index(s:lastFile, f)] + [l:file]
                return l:file
    let l:file = JsIncludeExpr2(a:file, @%)
        let s:lastFile = [l:file]
        return l:file

function! JsIncludeExpr2(file, parent)
    let l:dir = substitute(a:parent, '\/[^/]\+$', '', 'g')
    " echom 'JsIncludeExpr ' . a:file . ' ' . l:dir
    if (a:file =~ '^\/')
        let l:topDir = substitute(substitute(a:file, '^/', '', 'g'), '/.*$', '', 'g')
        let l:prefixDir = finddir(l:topDir, l:dir . '/;')
        let l:postfix = substitute(a:file, '/' . l:topDir, '', 'g')
        if(!empty(l:prefixDir) && filereadable(l:prefixDir . l:postfix))
            return l:prefixDir . l:postfix
    elseif (a:file =~ '^\.\/')
        let l:file = l:dir . substitute(a:file, '^\.', '', 'g')
        if (filereadable(l:file))
            return l:file

It works like this: When it successfully resolves an import it assumes the next import will be inside of that file, so it remembers the stack of previous parent files and adds to that stack/removes from it as appropriate (assuming no bugs).


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.