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