X-Git-Url: https://git.rmz.io/dotfiles.git/blobdiff_plain/0a3bac27ae514b44a6c343f2e2f957b3dcee4b31..b47ffe60e244c9165f40371736079320ea2d848b:/vim/ycm_extra_conf.py diff --git a/vim/ycm_extra_conf.py b/vim/ycm_extra_conf.py index b4202ef..f19fa08 100644 --- a/vim/ycm_extra_conf.py +++ b/vim/ycm_extra_conf.py @@ -1,86 +1,185 @@ -from glob import glob -from os import path +# https://github.com/Valloric/ycmd/blob/master/cpp/ycm/.ycm_extra_conf.py +# https://jonasdevlieghere.com/a-better-youcompleteme-config/ +# https://github.com/arximboldi/dotfiles/blob/master/emacs/.ycm_extra_conf.py +import os +import os.path +from glob import glob +import logging import ycm_core +import difflib +# flags used when no compilation_db is found +BASE_FLAGS = [ + '-std=c++1z', + '-x', 'c++', +] -def findCompilationDatabaseFolder(dir='.'): - dirs = [path.dirname(f) for f in glob(dir + '/**/compile_commands.json', - recursive=True)] - return dirs +# flags are always added +EXTRA_FLAGS = [ + '-Wall', + '-Wextra', + '-Weverything', + '-Wno-c++98-compat', + '-Wno-c++98-compat-pedantic', + # '-Wshadow', + # '-Werror', + # '-Wc++98-compat', + # '-Wno-long-long', + # '-Wno-variadic-macros', + # '-fexceptions', + # '-DNDEBUG', +] +SOURCE_EXTENSIONS = [ + '.cpp', + '.cxx', + '.cc', + '.c', + '.m', + '.mm' +] -def generateQtFlags(): + +def generate_qt_flags(): flags = ['-isystem', '/usr/include/qt/'] for p in glob('/usr/include/qt/*/'): flags += ['-isystem', p] return flags -def isHeader(filename): - ext = path.splitext(filename)[1] - return ext in ['.hpp', '.h'] +def similarity_ratio(s, t): + return difflib.SequenceMatcher(a=s.lower(), b=t.lower()).ratio() -def GetCompilationInfoForFile(database, filename): - if isHeader(filename): - basename = path.splitext(filename)[0] - for ext in ['.cpp', '.c']: - cpp_file = basename + ext - if path.exists(cpp_file): - compilation_info = database.GetCompilationInfoForFile( - cpp_file) - if compilation_info.compiler_flags_: - return compilation_info - return None - return database.GetCompilationInfoForFile(filename) +def find_similar_file_in_database(dbpath, filename): + import json + logging.info("Trying to find some file close to: " + filename) + db = json.load(open(dbpath+ "/compile_commands.json")) + best_filename = '' + best_ratio = 0 + for entry in db: + entry_filename = os.path.normpath(os.path.join(entry["directory"], + entry["file"])) -def FlagsForFile(filename, **kwargs): - client_data = kwargs['client_data'] - cwd = client_data['getcwd()'] - - extra_flags = [ - '-Wall', - '-Wextra', - # '-Wshadow', - # '-Werror', - # '-Wc++98-compat', - # '-Wno-long-long', - # '-Wno-variadic-macros', - # '-fexceptions', - # '-DNDEBUG', + if filename == entry_filename: + logging.info("Found exact match: " + entry_filename) + return entry_filename + + basename = os.path.splitext(filename)[0] + for extension in SOURCE_EXTENSIONS: + replacement_file = basename + extension + if entry_filename == replacement_file: + logging.info("Found match: " + replacement_file) + return entry_filename + + ratio = similarity_ratio(str(filename), str(entry_filename)) + if ratio > best_ratio: + best_filename = entry_filename + best_ratio = ratio + + logging.info("Found closest match: " + best_filename) + return best_filename + + +def find_nearest_compilation_database(root='.'): + dirs = glob(root + '/*/compile_commands.json', recursive=True) + + if len(dirs) == 1: + return dirs[0] + elif len(dirs) > 1: + logging.info("Multiple compilation databases found!") + logging.info(dirs) + dirs.sort(key=lambda x: os.stat(x).st_mtime, reverse=True) + logging.info("Selecting newest: %s" % (dirs[0])) + return dirs[0] + + parent = os.path.dirname(os.path.abspath(root)) + if parent == root: + raise RuntimeError("Could not find compile_commands.json") + return find_nearest_compilation_database(parent) + + +def find_nearest(path, target): + candidates = [ + os.path.join(path, target), + os.path.join(path, 'build', target), + os.path.join(path, 'output', target), ] + for candidate in candidates: + if os.path.isfile(candidate) or os.path.isdir(candidate): + logging.info("Found nearest " + target + " at " + candidate) + return candidate + parent = os.path.dirname(os.path.abspath(path)) + if parent == path: + raise RuntimeError("Could not find " + target) + return find_nearest(parent, target) - folders = findCompilationDatabaseFolder(cwd) - if not folders: - folder = None - else: - folder = folders[0] - if len(folders) > 1: - print("Multiple compilation databases found!") - print(folders) - print("Selecting first: %s" % (folder)) - - if folder: - database = ycm_core.CompilationDatabase(folder) - compilation_info = GetCompilationInfoForFile(database, filename) - if not compilation_info: + +def flags_for_include(root): + try: + include_path = find_nearest(root, 'include') + flags = [] + for dirroot, dirnames, filenames in os.walk(include_path): + for dir_path in dirnames: + real_path = os.path.join(dirroot, dir_path) + flags = flags + ["-I" + real_path] + return flags + except Exception as err: + logging.info("Error while looking flags for includes in root: " + root) + logging.error(err) + return None + + +def get_compilation_database(root): + try: + compilation_db_path = find_nearest_compilation_database(root) + compilation_db_dir = os.path.dirname(compilation_db_path) + logging.info("Set compilation database directory to " + compilation_db_dir) + db = ycm_core.CompilationDatabase(compilation_db_dir) + if db is None: + logging.info("Compilation database file found but unable to load") return None - flags = list(compilation_info.compiler_flags_) + return db + except Exception as err: + logging.info("Error while trying to find compilation database: " + root) + logging.error(err) + return None + + +def Settings(**kwargs): + if kwargs['language'] != 'cfamily': + return {} + + print(kwargs) + client_data = kwargs['client_data'] + root = client_data.get('getcwd()', '.') + filename = kwargs['filename'] + + database = get_compilation_database(root) + if database: + filename = find_similar_file_in_database(database.database_directory, + filename) + compilation_info = database.GetCompilationInfoForFile(filename) + print(compilation_info) + if not compilation_info.compiler_flags_: + return {} #TODO use default flags + final_flags = list(compilation_info.compiler_flags_) + include_path_relative_to_dir = compilation_info.compiler_working_dir_ else: - flags = [ - '-std=c++14', - '-stdlib=libstdc++', - '-x', 'c++', - '-isystem', '/usr/include', - '-isystem', '/usr/local/include', - '-I', cwd, - '-I', cwd + '/include', - ] - flags += generateQtFlags() + final_flags = BASE_FLAGS + include_flags = flags_for_include(root) + if include_flags: + final_flags += include_flags + final_flags += generate_qt_flags() + final_flags += ['-I', root, + '-I', root + '/include'] + include_path_relative_to_dir = root return { - 'flags': flags + extra_flags, + 'flags': final_flags + EXTRA_FLAGS, + 'include_paths_relative_to_dir': include_path_relative_to_dir, + 'override_filename': filename, 'do_cache': True }