]> git.rmz.io Git - dotfiles.git/blob - vim/ycm_extra_conf.py
77f13b32be6d47cfd747979a4f8dd92be7cbaa62
[dotfiles.git] / vim / ycm_extra_conf.py
1 # https://github.com/Valloric/ycmd/blob/master/cpp/ycm/.ycm_extra_conf.py
2 # https://jonasdevlieghere.com/a-better-youcompleteme-config/
3 # https://github.com/arximboldi/dotfiles/blob/master/emacs/.ycm_extra_conf.py
4
5 import os
6 import os.path
7 from glob import glob
8 import logging
9 import ycm_core
10 import difflib
11
12 # flags used when no compilation_db is found
13 BASE_FLAGS = [
14 '-std=c++1z',
15 '-x', 'c++',
16 ]
17
18 # flags are always added
19 EXTRA_FLAGS = [
20 '-Wall',
21 '-Wextra',
22 # '-Wshadow',
23 # '-Werror',
24 # '-Wc++98-compat',
25 # '-Wno-long-long',
26 # '-Wno-variadic-macros',
27 # '-fexceptions',
28 # '-DNDEBUG',
29 ]
30
31 SOURCE_EXTENSIONS = [
32 '.cpp',
33 '.cxx',
34 '.cc',
35 '.c',
36 '.m',
37 '.mm'
38 ]
39
40 HEADER_EXTENSIONS = [
41 '.h',
42 '.hxx',
43 '.hpp',
44 '.hh'
45 ]
46
47
48 def similarity_ratio(s, t):
49 return difflib.SequenceMatcher(a=s.lower(), b=t.lower()).ratio()
50
51
52 def generate_qt_flags():
53 flags = ['-isystem', '/usr/include/qt/']
54 for p in glob('/usr/include/qt/*/'):
55 flags += ['-isystem', p]
56 return flags
57
58
59 def find_similar_file_in_database(dbpath, filename):
60 import json
61 logging.info("Trying to find some file close to: " + filename)
62 db = json.load(open(dbpath))
63 best_filename = ''
64 best_ratio = 0
65 for entry in db:
66 entry_filename = os.path.normpath(os.path.join(entry["directory"],
67 entry["file"]))
68 ratio = similarity_ratio(str(filename), str(entry_filename))
69 if ratio > best_ratio:
70 best_filename = entry_filename
71 best_ratio = ratio
72 return best_filename
73
74 def ok_compilation_info(info):
75 return bool(info.compiler_flags_)
76
77 def get_compilation_info_for_file(dbpath, database, filename):
78 info = database.GetCompilationInfoForFile(filename)
79 if ok_compilation_info(info):
80 logging.info("Flags for file where found in database: " + filename)
81 return info
82 else:
83 logging.info("Flags for file not found in database: " + filename)
84 basename = os.path.splitext(filename)[0]
85 for extension in SOURCE_EXTENSIONS:
86 replacement_file = basename + extension
87 logging.info("Trying to replace extension with: " + extension)
88 info = database.GetCompilationInfoForFile(replacement_file)
89 if ok_compilation_info(info):
90 logging.info("Replacing header with: " + replacement_file)
91 return info
92 replacement_file = find_similar_file_in_database(dbpath, filename)
93 logging.info("Replacing header with: " + replacement_file)
94 return database.GetCompilationInfoForFile(replacement_file)
95
96
97 def find_nearest_compilation_database(root='.'):
98 dirs = glob(root + '/*/compile_commands.json', recursive=True)
99
100 if len(dirs) == 1:
101 return dirs[0]
102 elif len(dirs) > 1:
103 logging.info("Multiple compilation databases found!")
104 logging.info(dirs)
105 logging.info("Selecting first: %s" % (dir))
106 return dirs[0]
107
108 parent = os.path.dirname(os.path.abspath(root))
109 if parent == root:
110 raise RuntimeError("Could not find compile_commands.json")
111 return find_nearest_compilation_database(parent)
112
113
114 def find_nearest(path, target):
115 candidates = [
116 os.path.join(path, target),
117 os.path.join(path, 'build', target),
118 os.path.join(path, 'output', target),
119 ]
120 for candidate in candidates:
121 if os.path.isfile(candidate) or os.path.isdir(candidate):
122 logging.info("Found nearest " + target + " at " + candidate)
123 return candidate
124 parent = os.path.dirname(os.path.abspath(path))
125 if parent == path:
126 raise RuntimeError("Could not find " + target)
127 return find_nearest(parent, target)
128
129
130 def make_relative_paths_in_flags_absolute(flags, working_directory):
131 if not working_directory:
132 return list(flags)
133 new_flags = []
134 make_next_absolute = False
135 path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ]
136 for flag in flags:
137 new_flag = flag
138 if make_next_absolute:
139 make_next_absolute = False
140 if not flag.startswith('/'):
141 new_flag = os.path.join(working_directory, flag)
142 for path_flag in path_flags:
143 if flag == path_flag:
144 make_next_absolute = True
145 break
146 if flag.startswith(path_flag):
147 path = flag[ len(path_flag): ]
148 new_flag = path_flag + os.path.join(working_directory, path)
149 break
150 if new_flag:
151 new_flags.append(new_flag)
152 return new_flags
153
154
155 def flags_for_include(root):
156 try:
157 include_path = find_nearest(root, 'include')
158 flags = []
159 for dirroot, dirnames, filenames in os.walk(include_path):
160 for dir_path in dirnames:
161 real_path = os.path.join(dirroot, dir_path)
162 flags = flags + ["-I" + real_path]
163 return flags
164 except Exception as err:
165 logging.info("Error while looking flags for includes in root: " + root)
166 logging.error(err)
167 return None
168
169
170 def flags_for_compilation_database(root, filename):
171 try:
172 compilation_db_path = find_nearest_compilation_database(root)
173 compilation_db_dir = os.path.dirname(compilation_db_path)
174 logging.info("Set compilation database directory to " + compilation_db_dir)
175 compilation_db = ycm_core.CompilationDatabase(compilation_db_dir)
176 if not compilation_db:
177 logging.info("Compilation database file found but unable to load")
178 return None
179 compilation_info = get_compilation_info_for_file(
180 compilation_db_path, compilation_db, filename)
181 if not compilation_info:
182 logging.info("No compilation info for " + filename + " in compilation database")
183 return None
184 return make_relative_paths_in_flags_absolute(
185 compilation_info.compiler_flags_,
186 compilation_info.compiler_working_dir_)
187 except Exception as err:
188 logging.info("Error while trying to get flags for " + filename + " in compilation database")
189 logging.error(err)
190 return None
191
192
193 def FlagsForFile(filename, **kwargs):
194 client_data = kwargs['client_data']
195 root = client_data['getcwd()']
196
197 compilation_db_flags = flags_for_compilation_database(root, filename)
198 if compilation_db_flags:
199 final_flags = compilation_db_flags
200 else:
201 final_flags = BASE_FLAGS
202 include_flags = flags_for_include(root)
203 if include_flags:
204 final_flags = final_flags + include_flags
205
206 final_flags += generate_qt_flags()
207 final_flags += [
208 '-I', root,
209 '-I', root + '/include',
210 ]
211 return {
212 'flags': final_flags + EXTRA_FLAGS,
213 'do_cache': True
214 }