]> git.rmz.io Git - dotfiles.git/blob - vim/ycm_extra_conf.py
zsh: only print task prompt if task is installed
[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
44 def generate_qt_flags():
45 flags = ['-isystem', '/usr/include/qt/']
46 for p in glob('/usr/include/qt/*/'):
47 flags += ['-isystem', p]
48 return flags
49
50
51 def similarity_ratio(s, t):
52 return difflib.SequenceMatcher(a=s.lower(), b=t.lower()).ratio()
53
54
55 def find_similar_file_in_database(dbpath, filename):
56 import json
57 logging.info("Trying to find some file close to: " + filename)
58 db = json.load(open(dbpath+ "/compile_commands.json"))
59
60 best_filename = ''
61 best_ratio = 0
62 for entry in db:
63 entry_filename = os.path.normpath(os.path.join(entry["directory"],
64 entry["file"]))
65
66 if filename == entry_filename:
67 logging.info("Found exact match: " + entry_filename)
68 return entry_filename
69
70 basename = os.path.splitext(filename)[0]
71 for extension in SOURCE_EXTENSIONS:
72 replacement_file = basename + extension
73 if entry_filename == replacement_file:
74 logging.info("Found match: " + replacement_file)
75 return entry_filename
76
77 ratio = similarity_ratio(str(filename), str(entry_filename))
78 if ratio > best_ratio:
79 best_filename = entry_filename
80 best_ratio = ratio
81
82 logging.info("Found closest match: " + best_filename)
83 return best_filename
84
85
86 def find_nearest_compilation_database(root='.'):
87 dirs = glob(root + '/*/compile_commands.json', recursive=True)
88
89 if len(dirs) == 1:
90 return dirs[0]
91 elif len(dirs) > 1:
92 logging.info("Multiple compilation databases found!")
93 logging.info(dirs)
94 dirs.sort(key=lambda x: os.stat(x).st_mtime, reverse=True)
95 logging.info("Selecting newest: %s" % (dirs[0]))
96 return dirs[0]
97
98 parent = os.path.dirname(os.path.abspath(root))
99 if parent == root:
100 raise RuntimeError("Could not find compile_commands.json")
101 return find_nearest_compilation_database(parent)
102
103
104 def find_nearest(path, target):
105 candidates = [
106 os.path.join(path, target),
107 os.path.join(path, 'build', target),
108 os.path.join(path, 'output', target),
109 ]
110 for candidate in candidates:
111 if os.path.isfile(candidate) or os.path.isdir(candidate):
112 logging.info("Found nearest " + target + " at " + candidate)
113 return candidate
114 parent = os.path.dirname(os.path.abspath(path))
115 if parent == path:
116 raise RuntimeError("Could not find " + target)
117 return find_nearest(parent, target)
118
119
120 def flags_for_include(root):
121 try:
122 include_path = find_nearest(root, 'include')
123 flags = []
124 for dirroot, dirnames, filenames in os.walk(include_path):
125 for dir_path in dirnames:
126 real_path = os.path.join(dirroot, dir_path)
127 flags = flags + ["-I" + real_path]
128 return flags
129 except Exception as err:
130 logging.info("Error while looking flags for includes in root: " + root)
131 logging.error(err)
132 return None
133
134
135 def get_compilation_database(root):
136 try:
137 compilation_db_path = find_nearest_compilation_database(root)
138 compilation_db_dir = os.path.dirname(compilation_db_path)
139 logging.info("Set compilation database directory to " + compilation_db_dir)
140 db = ycm_core.CompilationDatabase(compilation_db_dir)
141 if db is None:
142 logging.info("Compilation database file found but unable to load")
143 return None
144 return db
145 except Exception as err:
146 logging.info("Error while trying to find compilation database: " + root)
147 logging.error(err)
148 return None
149
150
151 def Settings(**kwargs):
152 if kwargs['language'] != 'cfamily':
153 return {}
154
155 print(kwargs)
156 client_data = kwargs['client_data']
157 root = client_data.get('getcwd()', '.')
158 filename = kwargs['filename']
159
160 database = get_compilation_database(root)
161 if database:
162 filename = find_similar_file_in_database(database.database_directory,
163 filename)
164 compilation_info = database.GetCompilationInfoForFile(filename)
165 print(compilation_info)
166 if not compilation_info.compiler_flags_:
167 return {} #TODO use default flags
168 final_flags = list(compilation_info.compiler_flags_)
169 include_path_relative_to_dir = compilation_info.compiler_working_dir_
170 else:
171 final_flags = BASE_FLAGS
172 include_flags = flags_for_include(root)
173 if include_flags:
174 final_flags += include_flags
175 final_flags += generate_qt_flags()
176 final_flags += ['-I', root,
177 '-I', root + '/include']
178 include_path_relative_to_dir = root
179
180 return {
181 'flags': final_flags + EXTRA_FLAGS,
182 'include_paths_relative_to_dir': include_path_relative_to_dir,
183 'override_filename': filename,
184 'do_cache': True
185 }