Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Per file compile flags and cwd #31

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 97 additions & 7 deletions plugin/clang_complete.vim
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,99 @@
"
" Description: Use of clang to complete in C/C++.
"
" Help: Use :help clang_complete
" Configuration: Each project can have a .clang_complete at his root,
" containing the compiler options. This is useful if
" you're using some non-standard include paths.
" For simplicity, please don't put relative and
" absolute include path on the same line. It is not
" currently correctly handled.
"
" Options:
" - g:clang_complete_auto:
" if equal to 1, automatically complete after ->, ., ::
" Default: 1
"
" - g:clang_complete_copen:
" if equal to 1, open quickfix window on error.
" Default: 0
"
" - g:clang_hl_errors:
" if equal to 1, it will highlight the warnings and errors the
" same way clang does it.
" Default: 1
"
" - g:clang_periodic_quickfix:
" if equal to 1, it will periodically update the quickfix window
" Note: You could use the g:ClangUpdateQuickFix() to do the same
" with a mapping.
" Default: 0
"
" - g:clang_snippets:
" if equal to 1, it will do some snippets magic after a ( or a ,
" inside function call. Not currently fully working.
" Default: 0
"
" - g:clang_conceal_snippets:
" if equal to 1, vim will use vim 7.3 conceal feature to hide <#
" and #> which delimit a snippets.
" Note: See concealcursor and conceallevel for conceal configuration.
" Default: 1 (0 if conceal not available)
"
" - g:clang_exec:
" Name or path of clang executable.
" Note: Use this if clang has a non-standard name, or isn't in the
" path. Not used if |g:clang_use_library| is set.
" Default: 'clang'
"
" - g:clang_user_options:
" Option added at the end of clang command. Useful if you want to
" filter the result, or if you want to ignore the error code
" returned by clang: on error, the completion is not shown.
" Default: ''
" Example: '|| exit 0' (it will discard clang return value)
"
" - g:clang_per_file_user_options:
" Can be set to a function that is called with the name of the current
" buffer. This should return a dictionary that can contain the following
" keys:
" 'flags': list of additional flags for this file. Useful if
" compilation flags differ on a per-file or per-directory
" base.
" 'cwd': The working directory the compile job should be executed in.
" Works only if |g:clang_use_library| is set.
" Default: Not set.
" Example:
" fu! g:clang_per_file_user_options(path)
" if a:path =~? 'subfolder'
" return { 'flags': '-I/Users/thakis/src/myproject/subfolder/include' }
" else
" return {}
" endif
" endfu
"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is too much limiting. Let's say that I have a directory with a.cpp and b.cpp files inside it, and for some reason I've specified two separate sets of build options in my Makefile. This approach would make it impossible to set the correct options for both of these files.

Also, designing clang_per_file_user_options this way causes another (IMO more major) problem. Let's say that you're working on a very large project with tons of directories. This would practically mean that you have to store the build options twice, once in your build system (Makefiles or whatnot) and once again in another db accessible by clang_per_file_user_options (as in https://gist.github.com/813631). However, if clang_per_file_user_options way designed to take the file name instead, it could do something like:

make source.cpp CC=echo CXX=echo

to make the build system print the correct command line for the file in question. This approach would address both of the above problems. What do you think?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, looking closer into this, I think the clang_per_file_user_options implementation in https://gist.github.com/813631 might have tricked me, although I'm still not sure if that would provide the flexibility of getting the build options from the build system itself or not...

" - g:clang_use_library:
" Instead of calling the clang/clang++ tool use libclang directly. This
" should improve the performance, but is still experimental.
" Don't forget to set g:clang_library_path.
" Default: has('python') && exists('g:clang_library_path')
"
" - g:clang_library_path:
" If libclang.[dll/so/dylib] is not in your library search path, set
" this to the absolute path where libclang is available.
" Default: variable doesn't exists
"
" - g:clang_sort_algo:
" How results are sorted (alpha, priority).
" Currently only works with libclang.
" Default: 'priority'
"
" - g:clang_debug:
" Output debugging informations, like timeing output of completion.
" Default: 0
"
" Todo: - Fix bugs
" - Parse fix-its and do something useful with it.
" - -code-completion-macros -code-completion-patterns
"

au FileType c,cpp,objc,objcpp call <SID>ClangCompleteInit()
Expand Down Expand Up @@ -63,12 +155,10 @@ function! s:ClangCompleteInit()
let g:clang_user_options = ''
endif

if !exists('g:clang_conceal_snippets')
let g:clang_conceal_snippets = has('conceal')
endif

if !exists('g:clang_trailing_placeholder')
let g:clang_trailing_placeholder = 0
if !exists('*g:clang_per_file_user_options')
function g:clang_per_file_user_options(path)
return ''
endfunction
endif

" Only use libclang if the user clearly show intent to do so for now
Expand Down
88 changes: 33 additions & 55 deletions plugin/libclang.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from clang.cindex import *
import vim
import time
import re
import time
import threading
import vim

def initClangComplete(clang_complete_flags, library_path = None):
global index
Expand All @@ -24,56 +24,10 @@ def getCurrentFile():
file = "\n".join(vim.eval("getline(1, '$')"))
return (vim.current.buffer.name, file)

class CodeCompleteTimer:
def __init__(self, debug, file, line, column):
self._debug = debug

if not debug:
return

content = vim.eval("getline('.')");
print " "
print "libclang code completion"
print "========================"
print "File: %s" % file
print "Line: %d, Column: %d" % (line, column)
print " "
print "%s" % content

print " "

current = time.time()
self._start = current
self._last = current
self._events = []

def registerEvent(self, event):
if not self._debug:
return
def getCurrentTranslationUnit(update = False):
currentFile = getCurrentFile()
fileName = vim.current.buffer.name

current = time.time()
since_last = current - self._last
self._last = current
self._events.append((event, since_last))

def finish(self):
if not self._debug:
return

overall = self._last - self._start

for event in self._events:
name, since_last = event
percent = 1 / overall * since_last * 100
print "libclang code completion - %25s: %.3fs (%5.1f%%)" % \
(name, since_last, percent)

print " "
print "Overall: %.3f s" % overall
print "========================"
print " "

def getCurrentTranslationUnit(args, currentFile, fileName, update = False):
if fileName in translationUnits:
tu = translationUnits[fileName]
if update:
Expand All @@ -85,10 +39,29 @@ def getCurrentTranslationUnit(args, currentFile, fileName, update = False):
print "LibClang - Reparsing: %.3f" % elapsed
return tu

userOptionsGlobal = vim.eval("g:clang_user_options").split(" ")
userOptionsLocal = vim.eval("b:clang_user_options").split(" ")
userOptionsPerFileDict = vim.eval(
"g:clang_per_file_user_options('%s')" % fileName)
userOptionsPerFile = userOptionsPerFileDict.get("flags", "").split(" ")
args = userOptionsGlobal + userOptionsLocal + userOptionsPerFile

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we add this new option, we should probably move the option handling of libclang and clang-binary handling into a single function. I would prefer to
not have this duplicated functionality. Can you introduce a new function clang_get_options() that returns the option string and is used by both the
clang-binary and the libclang part of clang_complete?

old_cwd = vim.eval('getcwd()')
new_cwd = userOptionsPerFileDict.get('cwd', old_cwd)
print old_cwd, new_cwd

if debug:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the print statement remaining debugging output? Can we remove it?

start = time.time()
flags = TranslationUnit.PARSE_PRECOMPILED_PREAMBLE
tu = index.parse(fileName, args, [currentFile], flags)
try:
vim.command('cd ' + new_cwd)
#vim.command('cd base')
#print vim.eval('getcwd()')
flags = TranslationUnit.PARSE_PRECOMPILED_PREAMBLE
tu = index.parse(fileName, args, [currentFile], flags)
tu.cwd = new_cwd
finally:
vim.command('cd ' + old_cwd)

if debug:
elapsed = (time.time() - start)
print "LibClang - First parse: %.3f" % elapsed
Expand Down Expand Up @@ -219,8 +192,13 @@ def getCurrentCompletionResults(line, column, args, currentFile, fileName,
tu = getCurrentTranslationUnit(args, currentFile, fileName)
timer.registerEvent("Get TU")

cr = tu.codeComplete(fileName, line, column, [currentFile],
complete_flags)
old_cwd = vim.eval('getcwd()')
try:
vim.command('cd ' + tu.cwd)
cr = tu.codeComplete(fileName, line, column, [currentFile],
complete_flags)
finally:
vim.command('cd ' + old_cwd)
timer.registerEvent("Code Complete")
return cr

Expand Down