diff --git a/README.markdown b/README.markdown index d8d8b72..3583925 100644 --- a/README.markdown +++ b/README.markdown @@ -66,6 +66,9 @@ file), `:Require` requires a namespace with `:reload` (`:Require!` does operator that evaluates a given motion (`cpp` for the expression under the cursor). +Any failed evaluation loads the stack trace into the location list, which +can be easily accessed with `:lopen`. + ### Navigating and Comprehending I'm new to Clojure, so stuff that helps me understand code is a top priority. diff --git a/doc/foreplay.txt b/doc/foreplay.txt index 88ca0ab..d3ffcff 100644 --- a/doc/foreplay.txt +++ b/doc/foreplay.txt @@ -97,7 +97,8 @@ EVALUATING CODE *foreplay-eval* All code is evaluated in the namespace of the current file, requiring it if necessary. If the current file sits outside the class path (project.clj, for -example), the user namespace is used instead. +example), the user namespace is used instead. If an exception occurs, the +stack trace is loaded into the |location-list|. Use |:lopen| to view it. *foreplay-:Require* :Require [ns] Require :reload the given/current namespace. diff --git a/plugin/foreplay.vim b/plugin/foreplay.vim index 67e3f05..eba2a53 100644 --- a/plugin/foreplay.vim +++ b/plugin/foreplay.vim @@ -347,6 +347,54 @@ function! foreplay#local_client(...) throw ':Connect to a REPL or install classpath.vim to evaluate code' endfunction +function! foreplay#findresource(resource) abort + if a:resource ==# '' + return '' + endif + try + let path = foreplay#local_client().path() + catch /^:Connect/ + return '' + endtry + let file = findfile(a:resource, escape(join(path, ','), ' ')) + if !empty(file) + return file + endif + for jar in path + if fnamemodify(jar, ':e') ==# 'jar' && index(foreplay#jar_contents(jar), a:resource) >= 0 + return 'zipfile:' . jar . '::' . a:resource + endif + endfor + return '' +endfunction + +function! foreplay#quickfix_for(stacktrace) abort + let qflist = [] + for line in a:stacktrace + let match = matchlist(line, '\(.*\)(\(.*\):\(\d\+\))') + let entry = {'text': line} + let [_, class, file, lnum; __] = match + let entry.lnum = lnum + let truncated = substitute(class, '\.[A-Za-z0-9_]\+\%($.*\)$', '', '') + if file == 'NO_SOURCE_FILE' + let entry.resource = '' + else + let entry.resource = tr(truncated, '.', '/').'/'.file + endif + let qflist += [entry] + endfor + let paths = map(copy(qflist), 'foreplay#findresource(v:val.resource)') + let i = 0 + for i in range(len(qflist)) + if !empty(paths[i]) + let qflist[i].filename = paths[i] + else + call remove(qflist[i], 'lnum') + endif + endfor + return qflist +endfunction + function! s:output_response(response) abort if get(a:response, 'err', '') !=# '' echohl ErrorMSG @@ -375,6 +423,10 @@ function! foreplay#eval(expr) abort call s:output_response(response) + if !empty(get(response, 'stacktrace', [])) + call setloclist(0, foreplay#quickfix_for(response.stacktrace)) + endif + if get(response, 'ex', '') !=# '' let err = 'Clojure: '.response.ex elseif has_key(response, 'value')