aboutsummaryrefslogtreecommitdiff
path: root/src/crc32c/.ycm_extra_conf.py
blob: 62daa8a4acf6d31d1e605360cc5996b875cc35e1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# Copyright 2017 The CRC32C Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""YouCompleteMe configuration that interprets a .clang_complete file.

This module implementes the YouCompleteMe configuration API documented at:
https://github.com/ycm-core/ycmd#ycm_extra_confpy-specification

The implementation loads and processes a .clang_complete file, documented at:
https://github.com/xavierd/clang_complete/blob/master/README.md
"""

import os

# Flags added to the list in .clang_complete.
BASE_FLAGS = [
    '-Werror',  # Unlike clang_complete, YCM can also be used as a linter.
    '-DUSE_CLANG_COMPLETER',  # YCM needs this.
    '-xc++',  # YCM needs this to avoid compiling headers as C code.
]

# Clang flags that take in paths.
# See https://clang.llvm.org/docs/ClangCommandLineReference.html
PATH_FLAGS = [
    '-isystem',
    '-I',
    '-iquote',
    '--sysroot='
]


def DirectoryOfThisScript():
  """Returns the absolute path to the directory containing this script."""
  return os.path.dirname(os.path.abspath(__file__))


def MakeRelativePathsInFlagsAbsolute(flags, build_root):
  """Expands relative paths in a list of Clang command-line flags.

  Args:
    flags: The list of flags passed to Clang.
    build_root: The current directory when running the Clang compiler. Should be
        an absolute path.

  Returns:
    A list of flags with relative paths replaced by absolute paths.
  """
  new_flags = []
  make_next_absolute = False
  for flag in flags:
    new_flag = flag

    if make_next_absolute:
      make_next_absolute = False
      if not flag.startswith('/'):
        new_flag = os.path.join(build_root, flag)

    for path_flag in PATH_FLAGS:
      if flag == path_flag:
        make_next_absolute = True
        break

      if flag.startswith(path_flag):
        path = flag[len(path_flag):]
        new_flag = path_flag + os.path.join(build_root, path)
        break

    if new_flag:
      new_flags.append(new_flag)
  return new_flags


def FindNearest(target, path, build_root):
  """Looks for a file with a specific name closest to a project path.

  This is similar to the logic used by a version-control system (like git) to
  find its configuration directory (.git) based on the current directory when a
  command is invoked.

  Args:
    target: The file name to search for.
    path: The directory where the search starts. The search will explore the
        given directory's ascendants using the parent relationship. Should be an
        absolute path.
    build_root: A directory that acts as a fence for the search. If the search
        reaches this directory, it will not advance to its parent. Should be an
        absolute path.

  Returns:
    The path to a file with the desired name. None if the search failed.
  """
  candidate = os.path.join(path, target)
  if os.path.isfile(candidate):
    return candidate

  if path == build_root:
    return None

  parent = os.path.dirname(path)
  if parent == path:
    return None

  return FindNearest(target, parent, build_root)


def FlagsForClangComplete(file_path, build_root):
  """Reads the .clang_complete flags for a source file.

  Args:
    file_path: The path to the source file. Should be inside the project. Used
      to locate the relevant .clang_complete file.
    build_root: The current directory when running the Clang compiler for this
        file. Should be an absolute path.

  Returns:
    A list of strings, where each element is a Clang command-line flag.
  """
  clang_complete_path = FindNearest('.clang_complete', file_path, build_root)
  if clang_complete_path is None:
    return None
  clang_complete_flags = open(clang_complete_path, 'r').read().splitlines()
  return clang_complete_flags


def FlagsForFile(filename, **kwargs):
  """Implements the YouCompleteMe API."""

  # kwargs can be used to pass 'client_data' to the YCM configuration. This
  # configuration script does not need any extra information, so
  # pylint: disable=unused-argument

  build_root = DirectoryOfThisScript()
  file_path = os.path.realpath(filename)

  flags = BASE_FLAGS
  clang_flags = FlagsForClangComplete(file_path, build_root)
  if clang_flags:
    flags += clang_flags

  final_flags = MakeRelativePathsInFlagsAbsolute(flags, build_root)

  return {'flags': final_flags}