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