pax_global_header 0000666 0000000 0000000 00000000064 13601473553 0014521 g ustar 00root root 0000000 0000000 52 comment=2ed907ee56d09738ba078573d070b29585f70d3b
ycmd-0+20191222+git2771f6f+ds/ 0000775 0000000 0000000 00000000000 13601473553 0015160 5 ustar 00root root 0000000 0000000 ycmd-0+20191222+git2771f6f+ds/.clang-tidy 0000664 0000000 0000000 00000006136 13601473553 0017222 0 ustar 00root root 0000000 0000000 ---
Checks: 'clang-diagnostic-*,clang-analyzer-*,llvm-include-order,llvm-namespace-comment,readability-braces-around-statements,readability-container-size-empty,readability-else-after-return,readability-redundant*,readability-inconsistent-declaration-parameter-name,hicpp-explicit-conversions,hicpp-use-emplace,hicpp-use-equals-default,hicpp-use-equals-delete,performance-*,-performance-move-constructor-init'
WarningsAsErrors: '*'
HeaderFilterRegex: '.*'
AnalyzeTemporaryDtors: false
FormatStyle: none
User: bstaletic
CheckOptions:
- key: google-readability-braces-around-statements.ShortStatementLines
value: '1'
- key: google-readability-function-size.StatementThreshold
value: '800'
- key: google-readability-namespace-comments.ShortNamespaceLines
value: '10'
- key: google-readability-namespace-comments.SpacesBeforeComments
value: '2'
- key: hicpp-use-emplace.ContainersWithPushBack
value: '::std::vector;::std::list;::std::deque'
- key: hicpp-use-emplace.SmartPointers
value: '::std::shared_ptr;::std::unique_ptr;::std::auto_ptr;::std::weak_ptr'
- key: hicpp-use-emplace.TupleMakeFunctions
value: '::std::make_pair;::std::make_tuple'
- key: hicpp-use-emplace.TupleTypes
value: '::std::pair;::std::tuple'
- key: hicpp-use-equals-default.IgnoreMacros
value: '1'
- key: llvm-namespace-comment.ShortNamespaceLines
value: '1'
- key: llvm-namespace-comment.SpacesBeforeComments
value: '1'
- key: modernize-loop-convert.MaxCopySize
value: '16'
- key: modernize-loop-convert.MinConfidence
value: reasonable
- key: modernize-loop-convert.NamingStyle
value: CamelCase
- key: modernize-pass-by-value.IncludeStyle
value: llvm
- key: modernize-replace-auto-ptr.IncludeStyle
value: llvm
- key: modernize-use-nullptr.NullMacros
value: 'NULL'
- key: performance-faster-string-find.StringLikeClasses
value: 'std::basic_string'
- key: performance-for-range-copy.WarnOnAllAutoCopies
value: '0'
- key: performance-inefficient-string-concatenation.StrictMode
value: '0'
- key: performance-inefficient-vector-operation.VectorLikeClasses
value: '::std::vector'
- key: performance-move-const-arg.CheckTriviallyCopyableMove
value: '1'
- key: performance-type-promotion-in-math-fn.IncludeStyle
value: llvm
- key: performance-unnecessary-value-param.AllowedTypes
value: '^object|list$'
- key: performance-unnecessary-value-param.IncludeStyle
value: llvm
- key: readability-braces-around-statements.ShortStatementLines
value: '0'
...
ycmd-0+20191222+git2771f6f+ds/.coveragerc 0000664 0000000 0000000 00000000222 13601473553 0017275 0 ustar 00root root 0000000 0000000 [report]
omit =
*/third_party/requests/*
*/tests/*
*/__init__.py
[run]
parallel = True
source =
ycmd
[paths]
source =
ycmd/
ycmd-0+20191222+git2771f6f+ds/.gitignore 0000664 0000000 0000000 00000002702 13601473553 0017151 0 ustar 00root root 0000000 0000000 # Compiled Object files
*.slo
*.lo
*.o
# Compiled Dynamic libraries
*.dll
*.so*
*.dylib
# Compiled Static libraries
*.lai
*.la
*.a
# Clang archives
clang_archives
# CMake
CMakeCache.txt
CMakeFiles
Makefile
cmake_install.cmake
install_manifest.txt
# Python
*.py[cod]
# Installer logs
pip-log.txt
# Unit test / coverage reports
.coverage
.coverage.*
cover/
.tox
nosetests.xml
.noseids
# custom
ycm_core_tests
PYTHON_USED_DURING_BUILDING
# When we use the bcp tool to copy over the parts of boost we care about, it
# also copies some cruft files we don't need; this ignores them
cpp/BoostParts/libs/*/build
cpp/BoostParts/libs/*/test
# Exclude auto generated vim doc tags.
doc/tags
# Exclude tern installation area
third_party/tern_runtime/node_modules
third_party/tern_runtime/package-lock.json
# Exclude RoslynOmnisharp installation area
third_party/omnisharp-roslyn
# Exclude vagrant directory
.vagrant/
# Exclude the coverage build output directory
.build/
# Exclude gcov output
*.gcov
# And python coverage output, when running manually
coverage.xml
# API docs
docs/node_modules
docs/package-lock.json
# jdt.ls
third_party/eclipse.jdt.ls
# TSServer
third_party/tsserver
# clangd local installation
third_party/clangd
# Rust
third_party/rls
ycmd/tests/rust/testdata/common/target
ycmd/tests/rust/testdata/formatting/target
# generic LSP
third_party/generic_server/node_modules
third_party/generic_server/package-lock.json
# clangd index data
.clangd/
ycmd-0+20191222+git2771f6f+ds/.gitmodules 0000664 0000000 0000000 00000002770 13601473553 0017343 0 ustar 00root root 0000000 0000000 [submodule "third_party/bottle"]
path = third_party/bottle
url = https://github.com/defnull/bottle
[submodule "third_party/waitress"]
path = third_party/waitress
url = https://github.com/Pylons/waitress
[submodule "third_party/requests"]
path = third_party/requests_deps/requests
url = https://github.com/requests/requests
[submodule "third_party/jedi"]
path = third_party/jedi_deps/jedi
url = https://github.com/davidhalter/jedi
[submodule "third_party/jedi_deps/numpydoc"]
path = third_party/jedi_deps/numpydoc
url = https://github.com/numpy/numpydoc
[submodule "third_party/parso"]
path = third_party/jedi_deps/parso
url = https://github.com/davidhalter/parso
[submodule "third_party/python-future"]
path = third_party/python-future
url = https://github.com/PythonCharmers/python-future
[submodule "third_party/cregex"]
path = third_party/cregex
url = https://github.com/ycm-core/regex.git
[submodule "third_party/urllib3"]
path = third_party/requests_deps/urllib3
url = https://github.com/urllib3/urllib3
[submodule "third_party/chardet"]
path = third_party/requests_deps/chardet
url = https://github.com/chardet/chardet
[submodule "third_party/certifi"]
path = third_party/requests_deps/certifi
url = https://github.com/certifi/python-certifi
[submodule "third_party/idna"]
path = third_party/requests_deps/idna
url = https://github.com/kjd/idna
[submodule "third_party/go/src/golang.org/x/tools"]
path = third_party/go/src/golang.org/x/tools
url = https://github.com/golang/tools
ignore = dirty
ycmd-0+20191222+git2771f6f+ds/.mergify.yml 0000664 0000000 0000000 00000001301 13601473553 0017416 0 ustar 00root root 0000000 0000000 pull_request_rules:
- name: Automatic merge on Azure Pipelines and Reviewable successes
conditions:
- base=master
- status-success=ycm-core.ycmd
- status-success=code-review/reviewable
actions:
merge:
method: merge
strict: smart
comment:
message: Thanks for sending a PR!
- name: Manual merge on Azure Pipelines and Maintainer Override
conditions:
- base=master
- status-success=ycm-core.ycmd
- "#approved-reviews-by>=1"
- "#changes-requested-reviews-by=0"
- label="Ship It!"
actions:
merge:
method: merge
strict: smart
comment:
message: Thanks for sending a PR!
ycmd-0+20191222+git2771f6f+ds/.vimspector.json 0000664 0000000 0000000 00000002606 13601473553 0020330 0 ustar 00root root 0000000 0000000 {
"configurations": {
"python - launch nosetests": {
"adapter": "vscode-python",
"variables": [
{
"python": {
"shell": "/bin/bash -c 'if [ -z \"${dollar}VIRTUAL_ENV\" ]; then echo $$(which python); else echo \"${dollar}VIRTUAL_ENV/bin/python\"; fi'"
},
"nosetests": {
"shell": "/bin/bash -c 'if [ -z \"${dollar}VIRTUAL_ENV\" ]; then echo $$(which nosetests); else echo \"${dollar}VIRTUAL_ENV/bin/nosetests\"; fi'"
}
},
{
"python_path": {
"shell": [
"${python}",
"${workspaceRoot}/run_tests.py",
"--dump-path"
]
}
}
],
"configuration": {
"name": "Python nosetests",
"type": "vscode-python",
"request": "launch",
"cwd": "${workspaceRoot}",
"stopOnEntry": true,
"console": "integratedTerminal",
"justMyCode": false,
"debugOptions": [],
"program": "${nosetests}",
"pythonPath": "${python}",
"args": [
"-v",
"--with-id",
"--ignore-files=(^\\.|^setup\\.py$$)",
"${Test}"
],
"env": {
"PYTHONPATH": "${python_path}",
"LD_LIBRARY_PATH": "${workspaceRoot}/third_party/clang/lib",
"YCM_TEST_NO_RETRY": "1"
}
}
}
}
}
ycmd-0+20191222+git2771f6f+ds/.ycm_extra_conf.py 0000664 0000000 0000000 00000020005 13601473553 0020605 0 ustar 00root root 0000000 0000000 # This file is NOT licensed under the GPLv3, which is the license for the rest
# of YouCompleteMe.
#
# Here's the license text for this file:
#
# This is free and unencumbered software released into the public domain.
#
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
#
# In jurisdictions that recognize copyright laws, the author or authors
# of this software dedicate any and all copyright interest in the
# software to the public domain. We make this dedication for the benefit
# of the public at large and to the detriment of our heirs and
# successors. We intend this dedication to be an overt act of
# relinquishment in perpetuity of all present and future rights to this
# software under copyright law.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
# For more information, please refer to
from distutils.sysconfig import get_python_inc
import platform
import os.path as p
import subprocess
import ycm_core
DIR_OF_THIS_SCRIPT = p.abspath( p.dirname( __file__ ) )
DIR_OF_THIRD_PARTY = p.join( DIR_OF_THIS_SCRIPT, 'third_party' )
SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ]
# These are the compilation flags that will be used in case there's no
# compilation database set (by default, one is not set).
# CHANGE THIS LIST OF FLAGS. YES, THIS IS THE DROID YOU HAVE BEEN LOOKING FOR.
flags = [
'-Wall',
'-Wextra',
'-Werror',
'-Wno-long-long',
'-Wno-variadic-macros',
'-fexceptions',
'-DNDEBUG',
# You 100% do NOT need -DUSE_CLANG_COMPLETER and/or -DYCM_EXPORT in your flags;
# only the YCM source code needs it.
'-DUSE_CLANG_COMPLETER',
'-DYCM_EXPORT=',
# THIS IS IMPORTANT! Without the '-x' flag, Clang won't know which language to
# use when compiling headers. So it will guess. Badly. So C++ headers will be
# compiled as C headers. You don't want that so ALWAYS specify the '-x' flag.
# For a C project, you would set this to 'c' instead of 'c++'.
'-x',
'c++',
'-isystem',
'cpp/pybind11',
'-isystem',
'cpp/whereami',
'-isystem',
'cpp/BoostParts',
'-isystem',
get_python_inc(),
'-isystem',
'cpp/llvm/include',
'-isystem',
'cpp/llvm/tools/clang/include',
'-I',
'cpp/ycm',
'-I',
'cpp/ycm/ClangCompleter',
'-isystem',
'cpp/ycm/tests/gmock/gtest',
'-isystem',
'cpp/ycm/tests/gmock/gtest/include',
'-isystem',
'cpp/ycm/tests/gmock',
'-isystem',
'cpp/ycm/tests/gmock/include',
'-isystem',
'cpp/ycm/benchmarks/benchmark/include',
]
# Clang automatically sets the '-std=' flag to 'c++14' for MSVC 2015 or later,
# which is required for compiling the standard library, and to 'c++11' for older
# versions.
if platform.system() != 'Windows':
flags.append( '-std=c++11' )
# Set this to the absolute path to the folder (NOT the file!) containing the
# compile_commands.json file to use that instead of 'flags'. See here for
# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html
#
# You can get CMake to generate this file for you by adding:
# set( CMAKE_EXPORT_COMPILE_COMMANDS 1 )
# to your CMakeLists.txt file.
#
# Most projects will NOT need to set this to anything; you can just change the
# 'flags' list of compilation flags. Notice that YCM itself uses that approach.
compilation_database_folder = ''
if p.exists( compilation_database_folder ):
database = ycm_core.CompilationDatabase( compilation_database_folder )
else:
database = None
def IsHeaderFile( filename ):
extension = p.splitext( filename )[ 1 ]
return extension in [ '.h', '.hxx', '.hpp', '.hh' ]
def FindCorrespondingSourceFile( filename ):
if IsHeaderFile( filename ):
basename = p.splitext( filename )[ 0 ]
for extension in SOURCE_EXTENSIONS:
replacement_file = basename + extension
if p.exists( replacement_file ):
return replacement_file
return filename
def PathToPythonUsedDuringBuild():
try:
filepath = p.join( DIR_OF_THIS_SCRIPT, 'PYTHON_USED_DURING_BUILDING' )
with open( filepath ) as f:
return f.read().strip()
# We need to check for IOError for Python 2 and OSError for Python 3.
except ( IOError, OSError ):
return None
def Settings( **kwargs ):
language = kwargs[ 'language' ]
if language == 'cfamily':
# If the file is a header, try to find the corresponding source file and
# retrieve its flags from the compilation database if using one. This is
# necessary since compilation databases don't have entries for header files.
# In addition, use this source file as the translation unit. This makes it
# possible to jump from a declaration in the header file to its definition
# in the corresponding source file.
filename = FindCorrespondingSourceFile( kwargs[ 'filename' ] )
if not database:
return {
'flags': flags,
'include_paths_relative_to_dir': DIR_OF_THIS_SCRIPT,
'override_filename': filename
}
compilation_info = database.GetCompilationInfoForFile( filename )
if not compilation_info.compiler_flags_:
return {}
# Bear in mind that compilation_info.compiler_flags_ does NOT return a
# python list, but a "list-like" StringVec object.
final_flags = list( compilation_info.compiler_flags_ )
# NOTE: This is just for YouCompleteMe; it's highly likely that your project
# does NOT need to remove the stdlib flag. DO NOT USE THIS IN YOUR
# ycm_extra_conf IF YOU'RE NOT 100% SURE YOU NEED IT.
try:
final_flags.remove( '-stdlib=libc++' )
except ValueError:
pass
return {
'flags': final_flags,
'include_paths_relative_to_dir': compilation_info.compiler_working_dir_,
'override_filename': filename
}
if language == 'python':
return {
'interpreter_path': PathToPythonUsedDuringBuild()
}
return {}
def GetStandardLibraryIndexInSysPath( sys_path ):
for index, path in enumerate( sys_path ):
if p.isfile( p.join( path, 'os.py' ) ):
return index
raise RuntimeError( 'Could not find standard library path in Python path.' )
def PythonSysPath( **kwargs ):
sys_path = kwargs[ 'sys_path' ]
interpreter_path = kwargs[ 'interpreter_path' ]
major_version = subprocess.check_output( [
interpreter_path, '-c', 'import sys; print( sys.version_info[ 0 ] )' ]
).rstrip().decode( 'utf8' )
sys_path.insert( GetStandardLibraryIndexInSysPath( sys_path ) + 1,
p.join( DIR_OF_THIRD_PARTY, 'python-future', 'src' ) )
sys_path[ 0:0 ] = [ p.join( DIR_OF_THIS_SCRIPT ),
p.join( DIR_OF_THIRD_PARTY, 'bottle' ),
p.join( DIR_OF_THIRD_PARTY, 'cregex',
'regex_{}'.format( major_version ) ),
p.join( DIR_OF_THIRD_PARTY, 'frozendict' ),
p.join( DIR_OF_THIRD_PARTY, 'jedi_deps', 'jedi' ),
p.join( DIR_OF_THIRD_PARTY, 'jedi_deps', 'numpydoc' ),
p.join( DIR_OF_THIRD_PARTY, 'jedi_deps', 'parso' ),
p.join( DIR_OF_THIRD_PARTY, 'requests_deps', 'requests' ),
p.join( DIR_OF_THIRD_PARTY, 'requests_deps',
'urllib3',
'src' ),
p.join( DIR_OF_THIRD_PARTY, 'requests_deps',
'chardet' ),
p.join( DIR_OF_THIRD_PARTY, 'requests_deps',
'certifi' ),
p.join( DIR_OF_THIRD_PARTY, 'requests_deps',
'idna' ),
p.join( DIR_OF_THIRD_PARTY, 'waitress' ) ]
return sys_path
ycmd-0+20191222+git2771f6f+ds/CODE_OF_CONDUCT.md 0000664 0000000 0000000 00000004521 13601473553 0017761 0 ustar 00root root 0000000 0000000 # Contributor Code of Conduct
As contributors and maintainers of this project, and in the interest of
fostering an open and welcoming community, we pledge to respect all people who
contribute through reporting issues, posting feature requests, updating
documentation, submitting pull requests or patches, and other activities.
We are committed to making participation in this project a harassment-free
experience for everyone, regardless of level of experience, gender, gender
identity and expression, sexual orientation, disability, personal appearance,
body size, race, ethnicity, age, religion, or nationality.
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information, such as physical or electronic
addresses, without explicit permission
* Other unethical or unprofessional conduct
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
By adopting this Code of Conduct, project maintainers commit themselves to
fairly and consistently applying these principles to every aspect of managing
this project. Project maintainers who do not follow or enforce the Code of
Conduct may be permanently removed from the project team.
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting a project maintainer at val@markovic.io. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. Maintainers are
obligated to maintain confidentiality with regard to the reporter of an
incident.
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 1.3.0, available at
[http://contributor-covenant.org/version/1/3/0/][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/3/0/
ycmd-0+20191222+git2771f6f+ds/CONTRIBUTING.md 0000664 0000000 0000000 00000023760 13601473553 0017421 0 ustar 00root root 0000000 0000000 Writing issue reports
=====================
### Bugs and features only
First things first: **the issue tracker is NOT for tech support**. It is for
reporting bugs and requesting features. If your issue amounts to "I can't get
ycmd to work on my machine" and the reason why is obviously related to your
machine configuration and the problem would not be resolved with _reasonable_
changes to the ycmd codebase, then the issue is likely to be closed.
### Where to go for help
**A good place to ask questions is the [ycmd-users][] Google group**. Rule
of thumb: if you're not sure whether your problem is a real bug, ask on
the group.
### Installation problem - read the docs
**ycmd compiles just fine**; [the build bots say so][build-bots]. If the bots are
green and ycmd doesn't compile on your machine, then _your machine is the root
cause_. Now read the first paragraph again.
Realize that quite literally _thousands_ of people have gotten ycmd to work
successfully so if you can't, it's probably because you have a peculiar
system or configuration or you didn't go through the docs carefully enough.
It's very unlikely to be caused by an actual bug in ycmd because someone would
have already found it and reported it.
This leads us to point #2: **make sure you have checked the docs before
reporting an issue**. The docs are extensive and cover a ton of things; there's
also an FAQ at the bottom that quite possibly addresses your problem. NOTE:
installation of ycmd is predominantly documented in the YouCompleteMe (Vim client)
README.
For installation problems, make sure that any issue report includes the entire
output of any build or installation commands, including **the command used to
run them**.
### Other problems - check the issue tracker
Further, **search the issue tracker for similar issues** before creating a new
one. There's no point in duplication; if an existing issue addresses your
problem, please comment there instead of creating a duplicate. However, if the
issue you found is **closed as resolved** (e.g. with a PR or the original user's
problem was resolved), raise a **new issue**, because you've found a new
problem. Reference the original issue if you think that's useful information.
If you do find a similar open issue, **don't just post 'me too' or similar**
responses. This almost never helps resolve the issue, and just causes noise for
the maintainers. Only post if it will aid the maintainers in solving the issue;
if there are existing diagnostics requested in the thread, perform
them and post the results.
When replying, follow the instructions for getting the required diagnostics for
posting a new issue (see below), and add them to your response. This is likely
to help the maintainers find a fix for you, rather than have them spend time
requesting them again. To be clear, the maintainers *always* need the
diagnostics (debug info, log files, versions, etc.) even for responses on
existing issues.
You should also **search the archives of the [ycmd-users][] mailing list**.
### Check your ycmd version
Lastly, **make sure you are running the latest version of ycmd**. The issue you
have encountered may have already been fixed. **Don't forget to recompile
ycm_core.so too** (usually by just running `build.py` again).
## Creating an issue
OK, so we've reached this far. You need to create an issue. First realize that
the time it takes to fix your issue is a multiple of how long it takes the
developer to reproduce it. The easier it is to reproduce, the quicker it'll be
fixed.
Here are the things you should do when creating an issue:
1. **Write a step-by-step procedure that when performed repeatedly reproduces
your issue.** If we can't reproduce the issue, then we can't fix it. It's
that simple. The procedure can be written the Vim client or the API
directly. If you're using another client, we can try to help, but it's
easier if we can reproduce in the Vim client. Usually it's best to report
such issues to the client maintainer, who can pass you on to ycmd if that
looks like the cause.
2. Explain **what you expected to happen**, and **what actually happened**.
This helps us understand if it is a bug, or just a misunderstanding of the
behavior.
2. Add the output of the `/debug_info` request for the file that is
experiencing the issue.
3. Ensure that the `server_log_level` option is set to `debug`, then reproduce
your issue and attach the contents of the logfiles reported in the debug
info.
4. **Create a test case for your issue**. This is critical. Don't talk about how
"when I have X in my file" or similar, _create a file with X in it_ and put
the contents inside code blocks in your issue description. Try to make this
test file _as small as possible_. Don't just paste a huge, 500 line source
file you were editing and present that as a test. _Minimize_ the file so that
the problem is reproduced with the smallest possible amount of test data.
5. **Include your OS and OS version.**
Creating good pull requests
===========================
1. **Follow the code style of the existing codebase.**
- The Python code **DOES NOT** follow PEP 8. This is not an oversight, this
is by choice. You can dislike this as much as you want, but you still need
to follow the existing style. Look at other Python files to see what the
style is.
- The C++ code has an automated formatter (`style_format.sh` that runs
`astyle`) but it's not perfect. Again, look at the other C++ files and
match the code style you see.
2. **Your code needs to be well written and easy to maintain**. This is of the
_utmost_ importance. Other people will have to maintain your code so don't
just throw stuff against the wall until things kinda work.
3. **Split your pull request into several smaller ones if possible.** This
makes it easier to review your changes, which means they will be merged
faster.
4. **Write tests for your code**. Your pull request is unlikely to be merged
without tests. See [TESTS.md][ycmd-tests] for instructions on running the
tests.
5. **Explain in detail why your pull request makes sense.** Ask yourself, would
this feature be helpful to others? Not just a few people, but a lot of
ycmd's users? See, good features are useful to many. If your feature is only
useful to you and _maybe_ a couple of others, then that's not a good
feature. There is such a thing as “feature overload”. When software
accumulates so many features of which most are only useful to a handful,
then that software has become “bloated”. We don't want that.
Requests for features that are obscure or are helpful to but a few, or are
not part of ycmd's "vision" will be rejected. Yes, even if you provide a
patch that completely implements it.
Please include details on exactly what you would like to see, and why. The
why is important - it's not always clear why a feature is really useful. And
sometimes what you want can be done in a different way if the reason for the
change is known. _What goal is your change trying to accomplish?_
Writing code that runs on Python 2 & 3
======================================
We support Python 2.7 and 3.5+. Since we use
[`python-future`][python-future], you should mostly write Python 3 as normal.
Here's what you should watch out for:
- New files should start with the following prologue after the copyright header:
```python
from __future__ import absolute_import
from __future__ import unicode_literals
from __future__ import print_function
from __future__ import division
# Not installing aliases from python-future; it's unreliable and slow.
from builtins import * # noqa
```
- Write `dict(a=1, b=2)` instead of `{'a':1, 'b':2}`. `python-future` patches
`dict()` to return a dictionary like the one from Python 3, but it can't patch
dictionary literals. You could also create a dict with `d = dict()` and then
use `d.update()` on it with a dict literal.
- Read the [_What Else You Need to Know_][what-else] doc from `python-future`.
- Create `bytes` objects from literals like so: `bytes( b'foo' )`. Note that
`bytes` is patched by `python-future` on py2.
- Be careful when passing the `bytes` type from `python-future` to python
libraries (includes the standard library) on py2; while that type should in
theory behave just like `str` on py2, some libraries might have issues. If you
encounter any, pass the value through `future.utils`'s `native()` function
which will convert `bytes` to a real `str` (again, only on py2). Heed this
advice for your own sanity; behind it are 40 hours of debugging and an
instance of tears-down-the-cheek crying at 2 am.
- **Use the `ToBytes()` and `ToUnicode()` helper functions from
`ycmd/utils.py`.** They work around weirdness, complexity and bugs in
`python-future` and behave as you would expect. They're also extensively
covered with tests.
- Use `from future.utils import iteritems`
then `for key, value in iteritems( dict_obj )` to efficiently iterate dicts on
py2 & py3
- Use `from future.utils import itervalues` then `for value in itervalues(
dict_obj )` to efficiently iterate over values in dicts on py2 & py3
- `future.utils` has `PY2` and `PY3` constants that are `True` in the respective
interpreter; be careful about checking for py3 (better to check for py2);
don't write code that will break on py4!
- If you run tests and get failures on importing ycm_core that mention
`initycm_core` or `PyInit_ycm_core`, you've built the C++ parts of ycmd for
py2 and are trying to run tests in py3 (or vice-versa). Rebuild!
- Import the `urljoin` and `urlparse` functions from `ycmd/utils.py`:
```python
from ycmd.utils import urljoin, urlparse
```
[build-bots]: https://dev.azure.com/YouCompleteMe/YCM/_build/latest?definitionId=2&branchName=master
[ycmd-users]: https://groups.google.com/forum/?hl=en#!forum/ycmd-users
[ycmd-tests]: https://github.com/ycm-core/ycmd/blob/master/TESTS.md
[python-future]: http://python-future.org/index.html
[what-else]: http://python-future.org/what_else.html
ycmd-0+20191222+git2771f6f+ds/COPYING.txt 0000664 0000000 0000000 00000104513 13601473553 0017035 0 ustar 00root root 0000000 0000000 GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Copyright (C)
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
.
ycmd-0+20191222+git2771f6f+ds/CORE_VERSION 0000664 0000000 0000000 00000000003 13601473553 0017031 0 ustar 00root root 0000000 0000000 42
ycmd-0+20191222+git2771f6f+ds/DEBUG.md 0000664 0000000 0000000 00000007113 13601473553 0016332 0 ustar 00root root 0000000 0000000 # Debugging the Python layer
There are a number of Python debuggers. Presented here are just a couple of
options known to work for certain developers on the ycmd project.
The options presented are:
- Using [`ipdb`][ipdb] (this is known not to work well on OS X).
- Using [pyclewn][] and attaching to the running Python process.
## Using `ipdb`
1. If you're not using vagrant, install `ipdb` (`pip install ipdb`).
2. At the point in the code you want to break, add the following lines:
```python
import ipdb; ipdb.set_trace()
```
3. Run the tests without `flake8`, e.g.
```sh
./run_tests.py --skip-build --no-flake8 ycmd/tests/get_completions_test.py
```
4. The test breaks at your code breakpoint and offers a command interface to
debug.
See the `ipdb` docs for more info.
## Using `pyclewn` in Vim
The procedure is similar to using `ipdb` but you attach to the suspended process
and use Vim as a graphical debugger:
1. Install [pyclewna][pyclewn-install]
2. At the point you want to break, add the following lines:
```python
import clewn.vim as clewn; clewn.pdb()
```
3. Run the tests without `flake8`, e.g.
```sh
./run_tests.py --skip-build --no-flake8 ycmd/tests/get_completions_test.py
```
4. The tests will pause at the breakpoint. Now within Vim attach the debugger
with `:Pyclewn pdb`. Hope that it works. It can be a bit flaky.
See the pyclewn docs for more info.
# Debugging the C++ layer (C++ Python library)
If you want to debug the c++ code using gdb (or your favourite graphical
debugger, e.g. [pyclewn][] in Vim), there are a few things you need to do:
1. Ensure your Python is built with debug enabled. On Linux that's as simple as
installing [pyenv][] and running the commands:
```sh
export OPT='-g' # Ensure Python binary has debugging info
export PYTHON_CONFIGURE_OPTS='--enable-shared --with-pydebug'
pyenv install 2.7.11 # or whatever version
```
On OS X, you need a working debugger. You can either use `lldb`
which comes with XCode or `brew install gdb`. Note: If you use `gdb` from
homebrew, then you need to sign the binary otherwise you can't debug
anything. See later steps for a link.
2. Build ycm_core library with debugging information (and link against debug
Python):
```sh
pyenv shell 2.7.11
./build.py --all --enable-debug
```
3. Enable debugging in the OS. On Linux (Ubuntu at least, which is what all of
our tests are run on), you must set the following sysctl parameter (you can
make it permanent if you like by adding it to `/etc/sysctl.conf` or via any
other appropriate mechanism):
```sh
sudo sysctl kernel.yama.ptrace_scope=0
```
On OS X it is more fiddly:
- The binary must be signed. See
https://sourceware.org/gdb/wiki/BuildingOnDarwin
- You *can not* debug system Python. Again: you *must* use a Python that is
*not* the one provided by Apple. Use [pyenv][]. That is the rule.
Don't argue.
Don't ask why. It's for security.
4. Here you have choices: either use a Python debugger to break the tests, or
manually use Vim and [YouCompleteMe][] to simulate the scenario you want to
debug. In any case, you will need the PID of the running Python process
hosting ycmd to attach to. For example using Vim:
- `:YcmDebugInfo` to get the pid
- `gdb: attach `
- `break YouCompleteMe::FilterAndSortCandidates`
[YouCompleteMe]: https://github.com/Valloric/YouCompleteMe
[pyenv]: https://github.com/pyenv/pyenv
[pyclewn]: http://pyclewn.sourceforge.net
[pyclewn-install]: https://sourceforge.net/p/pyclewn/pyclewn/ci/default/tree/INSTALL
[ipdb]: https://pypi.python.org/pypi/ipdb
ycmd-0+20191222+git2771f6f+ds/JAVA_SUPPORT.md 0000664 0000000 0000000 00000024032 13601473553 0017460 0 ustar 00root root 0000000 0000000 This document briefly describes the work done to support Java (and other
Language Server Protocol-based completion engines).
# Overview
The [original PR][PR] implemented native support in ycmd for the Java language,
based on [jdt.ls][]. In summary, the following key features were added:
* Installation of jdt.ls (built from source with `build.py`)
* Management of the jdt.ls server instance, projects etc.
* A generic (ish) implementation of a [Language Server Protocol][lsp] client so
far as is required for jdt.ls (easily extensible to other engines)
* Support for the following Java semantic engine features:
* Semantic code completion, including automatic imports
* As-you-type diagnostics
* GoTo including GoToReferences
* FixIt
* RefactorRename
* GetType
* GetDoc
See the [trello board][trello] for a more complete picture.
## Overall design/goals
Key goals:
1. Support Java in ycmd and YCM; make it good enough to replace eclim and
javacomplete2 for most people
2. Make it possible/easy to support other [lsp][] servers in future (but, don't
suffer from yagni); prove that this works.
An overview of the objects involved can be seen on [this
card][design]. In short:
* 2 classes implement the language server protocol in the
`language_server_completer.py` module:
* `LanguageServerConnection` - an abstraction of the comminication with the
server, which may be over stdio or any number of TCP/IP ports (or a domain
socket, etc.). Only a single implementation is included (stdio), but
[implementations for TCP/IP](https://github.com/puremourning/ycmd-1/commit/f3cd06245692b05031a64745054326273d52d12f)
were written originally and dropped in favour of stdio's simplicity.
* `LanguageServerCompleter` - an abstract base for any completer based on LSP,
which implements as much standard functionality as possible including
completions, diagnostics, goto, fixit, rename, etc.
* The `java_completer` itself implements the `LanguageServerCompleter`, boots
the jdt.ls server, and instantiates a `LanguageServerConnection` for
communication with jdt.ls.
The overall plan and some general discussion around the project can be found on
the [trello board][trello] I used for development.
## Threads, and why we need them
LSP is by its nature an asyncronous protocol. There are request-reply like
`requests` and unsolicited `notifications`. Receipt of the latter is mandatory,
so we cannot rely on their being a `bottle` thread executing a client request.
So we need a message pump and despatch thread. This is actually the
`LanguageServerConnection`, which implements `thread`. It's main method simply
listens on the socket/stream and despatches complete messages to the
`LanguageServerCompleter`. It does this:
* For `requests`: similarly to the TypeScript completer, using python `event`
objects, wrapped in our `Response` class
* For `notifications`: via a synchronised `queue`. More on this later.
A representation of this is on the "Requests and notifications" page
of the [design][], including a rough sketch of the thread interaction.
### Some handling is done in the message pump.
While it is perhaps regrettable to do general processing directly in the message
pump, there are certain notifications which we have to handle immediately when
we get them, such as:
* Initialisation messages
* Diagnostics
In these cases, we allow some code to be executed inline within the message pump
thread, as there is no other thread guaranteed to execute. These are handled by
callback functions and state is protected mutexes.
## Startup sequence
See the 'initialisation sequence' tab on the [design][] for a bit of background.
In standard LSP, the initialisation sequence consists of an initialise
request-reply, followed by us sending the server an initialised notification. We
must not send any other requests until this has completed.
An additional wrinkle is that jdt.ls, being based on eclipse has a whole other
initialisation sequence during which time it is not fully functional, so we have
to determine when that has completed too. This is done by jdt.ls-specific
messages and controls the `ServerIsReady` response.
In order for none of these shenanigans to block the user, we must do them all
asynchronously, effectively in the message pump thread. In addition, we must
queue up any file contents changes during this period to ensure the server is up
to date when we start processing requests proper.
This is unfortunately complicated, but there were early issues with really bad
UI blocking that we just had to get rid of.
## Completion
Language server protocol requires that the client can apply textEdits,
rather than just simple text. This is not an optional feature, but ycmd
clients do not have this ability.
The protocol, however, restricts that the edit must include the original
requested completion position, so we can perform some simple text
manipulation to apply the edit to the current line and determine the
completion start column based on that.
In particular, the jdt.ls server returns textEdits that replace the
entered text for import completions, which is one of the most useful
completions.
We do this super inefficiently by attempting to normalise the TextEdits
into insertion_texts with the same start_codepoint. This is necessary
particularly due to the way that eclipse returns import completions for
packages.
We also include support for "additionalTextEdits" which
allow automatic insertion of, e.g., import statements when selecting
completion items. These are sent on the completion response as an
additional completer data item called 'fixits'. The client applies the
same logic as a standard FixIt once the selected completion item is
inserted.
## Diagnostics
Diagnostics in LSP are delivered asynchronously via `notifications`. Normally,
we would use the `OnFileReadyToParse` response to supply diagnostics, but due to
the lag between refreshing files and receiving diagnostics, this leads to a
horrible user experience where the diagnostics always lag one edit behind.
To resolve this, we use the long-polling mechanism added here (`ReceiveMessages`
request) to return diagnostics to the client asynchronously.
We deliver asynchronous diagnostics to the client in the same way that the
language server does, i.e. per-file. The client then fans them out or does
whatever makes sense for the client. This is necessary because it isn't possible
to know when we have received all diagnostics, and combining them into a single
message was becoming clunky and error prone.
In order to be relatively compatible with other clients, we also return
diagnostics on the file-ready-to-parse event, even though they might be
out of date wrt the code. The client is responsible for ignoring these
diagnostics when it handles the asynchronously delivered ones. This requires
that we hold the "latest" diagnostics for a file. As it turns out, this is also
required for FixIts.
## Projects
jdt.ls is based on eclipse. It is in fact an eclipse plugin. So it requires an
eclipse workspace. We try and hide this by creating an ad-hoc workspace for each
ycmd instance. This prevents the possibility of multiple "eclipse" instances
using the same workspace, but can lead to unreasonable startup times for large
projects.
The jdt.ls team strongly suggest that we should re-use a workspace based on the
hash of the "project directory" (essentially the dir containing the project
file: `.project`, `pom.xml` or `build.gradle`). They also say, however, that
eclipse frequently corrupts its workspace.
So we have a hidden switch to re-use a workspace as the jdt.ls devs suggest. In
testing at work, this was _mandatory_ due to a slow SAN, but at home, startup
time is not an issue, even for large projects. I think we'll just have to see
how things go to decide which one we want to keep.
## Subcommands
### GetDoc/GetType
There is no GetType in LSP. There's only "hover". The hover response is
hilariously server-specific, so in the base `LanguageServerCompleter` we just
provide the ability to get the `hover` response and `JavaCompleter` extracts the
appropriate info from there. Thanks to @bstaletic for this!
### FixIt
FixIts are implemented as code actions, and require the diagnostic they relate
to to be send from us to the server, rather than just a position. We use the
stored diags and find the nearest one based on the `request_data`.
What's worse is that the LSP provides _no documentation_ for what the "Code
action" response should be, and it is 100% implementation-specific. They just
have this `command` abstraction which is basically "do a thing".
From what I've seen, most servers just end up with either a `WorkspaceEdit` or a
series of `TextEdits`, which is fine for us as that's what ycmd's protocol looks
like.
The solution is that we have a callback into the `JavaCompleter` to handle the
(custom) `java.apply.workspaceEdit` "command".
### GoToReferences
Annoyingly, jdt.ls sometimes returns references to .class files within jar
archives using a custom `jdt://` protocol. We can't handle that, so we have to
dodge and weave so that we don't crash.
### Stopping the server
Much like the initialisation sequence, the LSP shutdown sequence is a bit
fiddly. 2 things are required:
1. A `shutdown` request-reply. The server tides up and _prepares to die!_
2. An `exit` notification. We tell the server to die.
This isn't so bad, but jdt.ls is buggy and actually dies without responding to
the `shutdown` request. So we have a bunch of code to handle that and to ensure
that the server dies eventually, as it had a habbit of getting stuck running,
particularly if we threw an exception.
[PR]: https://github.com/valloric/ycmd/pull/857
[jdt.ls]: https://github.com/eclipse/eclipse.jdt.ls
[lsp]: https://github.com/Microsoft/language-server-protocol/
[eclim]: http://eclim.org
[javacomplete2]: https://github.com/artur-shaik/vim-javacomplete2
[vscode-javac]: https://github.com/georgewfraser/vscode-javac
[VSCode]: https://code.visualstudio.com
[destign]: https://trello.com/c/78IkFBzp
[trello]: https://trello.com/b/Y6z8xag8/ycm-java-language-server
[client]: https://github.com/puremourning/YouCompleteMe/tree/language-server-java
ycmd-0+20191222+git2771f6f+ds/README.md 0000664 0000000 0000000 00000047752 13601473553 0016456 0 ustar 00root root 0000000 0000000 ycmd: a code-completion & comprehension server
==============================================
[](https://dev.azure.com/YouCompleteMe/YCM/_build/latest?definitionId=4&branchName=master)
[](https://codecov.io/gh/ycm-core/ycmd)
ycmd is a server that provides APIs for code-completion and other
code-comprehension use-cases like semantic GoTo commands (and others). For
certain filetypes, ycmd can also provide diagnostic errors and warnings.
ycmd was originally part of [YouCompleteMe][ycm]'s codebase, but has been split
out into a separate project so that it can be used in editors other than Vim.
Check [the API documentation][api-docs] if you want to implement a client. A
good way to learn how to interact with ycmd is by reading through (and running)
the [`example_client.py`][example-client] file. See the [README for the
examples][example-readme] folder for details on how to run the example client.
Known ycmd clients:
------------------
- [YouCompleteMe][ycm]: Vim client, stable and exposes all ycmd features.
- [emacs-ycmd][]: Emacs client.
- [you-complete-me][atom-you-complete-me]: Atom client.
- [YcmdCompletion][sublime-ycmd-completion]: Sublime client
- [sublime-ycmd][sublime-ycmd]: Sublime Text 3 client.
- [kak-ycmd][]: Kakoune client.
- [you-complete-me][vscode-you-complete-me]: VSCode client.
- [gycm][]: Geany client.
- [nano-ycmd][]: GNU nano client.
Feel free to send a pull request adding a link to your client here if you've
built one.
Building
--------
**If you're looking to develop ycmd, see the [instructions for running the
tests][test-setup].**
This is all for Ubuntu Linux. Details on getting ycmd running on other OS's can
be found in [YCM's instructions][ycm-install] (ignore the Vim-specific parts).
Note that **ycmd runs on Python 2.7.1+ and 3.5.1+.**
First, install the minimal dependencies:
```
sudo apt install build-essential cmake python3-dev
```
Next, install the language specific dependencies you need:
- `sudo apt install golang-go` for Go.
- `sudo apt install npm` for JavaScript and TypeScript.
- `sudo apt install mono-devel` for C#.
- `sudo apt install openjdk-8-jre` for Java.
When you first clone the repository you'll need to update the submodules:
```
git submodule update --init --recursive
```
Then run `python3 build.py --all` or any of the specific completers listed by
`python3 build.py --help`. This should get you going.
For more detailed instructions on building ycmd, see [YCM's
instructions][ycm-install] (ignore the Vim-specific parts).
Supported compilers
-------------------
- GCC 4.8 and later
- Clang 3.3 and later
- Microsoft Visual Studio 2015 Update 3 and later
API notes
---------
- All strings going into and out of the server are UTF-8 encoded.
- All lines end with `\n`.
- All line and column numbers are 1-based, not 0-based. They are also byte
offsets, _not_ Unicode codepoint offsets.
- All file paths are full, absolute paths.
- All requests to the server _must_ include an [HMAC][] in the `x-ycm-hmac` HTTP
header. The HMAC is computed from the shared secret passed to the server on
startup and the request/response body. The digest algorithm is SHA-256. The
server will also include the HMAC in its responses; you _must_ verify it
before using the response. See [`example_client.py`][example-client] to see
how it's done.
- API is documented in swagger and published on [the website][api-docs].
How ycmd works
--------------
There are several completion engines in ycmd. The most basic one is an
identifier-based completer that collects all of the identifiers in the file
provided in the completion request, other files of the same filetype that were
provided previously and any tags files produced by ctags. This engine is
non-semantic.
There are also several semantic engines in YCM. There's a libclang-based
completer and [clangd][clangd]-based completer that both provide semantic
completion for C-family languages. [clangd][clangd] support is currently
**experimental** and changes in the near future might break backwards
compatibility. There's also a Jedi-based completer for semantic completion for
Python, an OmniSharp-based completer for C#, a [Gocode][gocode]-based completer
for Go (using [Godef][godef] for jumping to definitions), a TSServer-based
completer for JavaScript and TypeScript, a [jdt.ls][jdtls]-based server for
Java, and a [RLS][]-based completer for Rust. More will be added with time.
There are also other completion engines, like the filepath completer (part of
the identifier completer).
The server will automatically detect which completion engine would be the best
in any situation. On occasion, it queries several of them at once, merges the
outputs and presents the results.
Semantic engines are triggered only after semantic "triggers" are inserted in
the code. If the request received shows that the user's cursor is after the last
character in `string foo; foo.` in a C# file, this would trigger the semantic
engine to
examine members of `foo` because `.` is a [default semantic
trigger][trigger-defaults] for C# (triggers can be changed dynamically). If the
text were `string foo; foo.zoo`, semantic completion would still be triggered
(the trigger is behind the `zoo` word the user is typing) and the results would
be filtered with the `zoo` query.
Semantic completion can also be forced by setting `force_semantic: true` in
the JSON data for the completion request, _but you should only do this if the
user explicitly requested semantic completion with a keyboard shortcut_;
otherwise, leave it up to ycmd to decide when to use which engine.
The reason why semantic completion isn't always used even when available is
because the semantic engines can be slow and because most of the time, the
user doesn't actually need semantic completion.
There are two main use-cases for code-completion:
1. The user knows which name they're looking for, they just don't want to type
the whole name.
2. The user either doesn't know the name they need or isn't sure what the name
is. This is also known as the "API exploration" use-case.
The first use case is the most common one and is trivially addressed with the
identifier completion engine (which BTW is blazing fast). The second one needs
semantic completion.
### Completion string filtering
A critical thing to note is that the completion **filtering is NOT based on
the input being a string prefix of the completion** (but that works too). The
input needs to be a _[subsequence][] match_ of a completion. This is a fancy way
of saying that any input characters need to be present in a completion string in
the order in which they appear in the input. So `abc` is a subsequence of
`xaybgc`, but not of `xbyxaxxc`.
### Completion string ranking
The subsequence filter removes any completions that do not match the input, but
then the sorting system kicks in. It's a bit involved, but roughly speaking
"word boundary" (WB) subsequence character matches are "worth" more than non-WB
matches. In effect, this means given an input of "gua", the completion
"getUserAccount" would be ranked higher in the list than the "Fooguxa"
completion (both of which are subsequence matches). A word-boundary character
are all capital characters, characters preceded by an underscore and the first
letter character in the completion string.
### Auto-shutdown if no requests for a while
If the server hasn't received any requests for a while (controlled by the
`--idle_suicide_seconds` ycmd flag), it will shut itself down. This is useful
for cases where the process that started ycmd dies without telling ycmd to die
too or if ycmd hangs (this should be extremely rare).
If you're implementing a client for ycmd, ensure that you have some sort of
keep-alive background thread that periodically pings ycmd (just call the
`/healthy` handler, although any handler will do).
You can also turn this off by passing `--idle_suicide_seconds=0`, although that
isn't recommended.
### Exit codes
During startup, ycmd attempts to load the `ycm_core` library and exits with one
of the following return codes if unsuccessful:
- 3: unexpected error while loading the library;
- 4: the `ycm_core` library is missing;
- 5: the `ycm_core` library is compiled for Python 2 but loaded in Python 3;
- 6: the `ycm_core` library is compiled for Python 3 but loaded in Python 2;
- 7: the version of the `ycm_core` library is outdated.
User-level customization
-----------------------
You can provide settings to ycmd on server startup. There's a
[`default_settings.json`][def-settings] file that you can tweak. See the
[_Options_ section in YCM's _User Guide_][options] for a description on what
each option does. Pass the path to the modified settings file to ycmd as an
`--options_file=/path/to/file` flag. Note that you must set the `hmac_secret`
setting (encode the value with [base64][]). Because the file you are passing
contains a secret token, ensure that you are creating the temporary file in a
secure way (the [`mkstemp()`][mkstemp] Linux system call is a good idea; use
something similar for other OS's).
After it starts up, ycmd will _delete_ the settings file you provided after
it reads it.
The settings file is something your editor should produce based on values your
user has configured. There's also an extra file (`.ycm_extra_conf.py`) your user
is supposed to provide to configure certain semantic completers. More
information on it can also be found in the [corresponding section of YCM's _User
Guide_][extra-conf-doc].
### `.ycm_extra_conf.py` specification
The `.ycm_extra_conf.py` module may define the following functions:
#### `Settings( **kwargs )`
This function allows users to configure the language completers on a per project
basis or globally. Currently, it is required by the libclang-based completer and
optional for other completers. The following arguments can be retrieved from
the `kwargs` dictionary and are common to all completers:
- `language`: an identifier of the completer that called the function. Its value
is `python` for the Python completer and `cfamily` for the C-family completer.
This argument is useful to configure several completers at once. For
instance:
```python
def Settings( **kwargs ):
language = kwargs[ 'language' ]
if language == 'cfamily':
return {
# Settings for the libclang and clangd-based completer.
}
if language == 'python':
return {
# Settings for the Python completer.
}
return {}
```
- `filename`: absolute path of the file currently edited.
- `client_data`: any additional data supplied by the client application.
See the [YouCompleteMe documentation][extra-conf-vim-data-doc] for an
example.
The return value is a dictionary whose content depends on the completer.
#### LSP based completers
LSP servers often support user configuration via the initialise request. These
are usually presented as options in the UI. Ycmd supports this using the
`.ycm_extra_conf.py` by allowing the user to specify the exact dictionary of
settings that are passed in the server initialise message. These options are
returned from `Settings` under the `ls` key. The python dictionary is converted
to json and included verbatim in the LSP initialize request. In order to
determine the set of options for a server, consult the server's documentation or
`package.json` file.
Example of LSP configuration:
```python
def Settings( **kwargs ):
if kwargs[ 'language' ] == 'java':
return { 'ls': { 'java.rename.enabled' : False } }
```
In addition, ycmd can use any language server, given a file type and a command
line. A user option `language_server` can be used to plug in a LSP server ycmd
wouldn't usually know about. The value is a list of dictionaries containing:
- `name`: the string representing the name of the server
- `cmdline`: the list representing the command line to execute the server
- `filetypes`: list of supported filetypes.
- `project_root_files`: Tells ycmd which files indicate project root.
```json
{
"language_server": [ {
"name": "gopls",
"cmdline": [ "/path/to/gopls", "-rpc.trace" ],
"filetypes": [ "go" ],
"project_root_files": [ "go.mod" ]
} ]
}
```
When plugging in a completer in this way, the `kwargs[ 'language' ]` will be set
to the value of the `name` key, i.e. `gopls` in the above example.
LSP completers currently supported without `language_server`:
- Java
- Rust
- Go
- C-family
##### C-family settings
The `Settings` function is called by the libclang and clangd-based completers to
get the compiler flags to use when compiling the current file. The absolute path
of this file is accessible under the `filename` key of the `kwargs` dictionary.
The return value expected by both completers is a dictionary containing the
following items:
- `flags`: (mandatory for libclang, optional for clangd) a list of compiler
flags.
- `include_paths_relative_to_dir`: (optional) the directory to which the include
paths in the list of flags are relative. Defaults to ycmd working directory
for the libclang completer and `.ycm_extra_conf.py`'s directory for the
clangd completer.
- `do_cache`: (optional) a boolean indicating whether or not the result of
this call (i.e. the list of flags) should be cached for this file name.
Defaults to `True`. If unsure, the default is almost always correct.
The libclang-based completer also supports the following items:
- `override_filename`: (optional) a string indicating the name of the file to
parse as the translation unit for the supplied file name. This fairly advanced
feature allows for projects that use a 'unity'-style build, or for header
files which depend on other includes in other files.
- `flags_ready`: (optional) a boolean indicating that the flags should be
used. Defaults to `True`. If unsure, the default is almost always correct.
A minimal example which simply returns a list of flags is:
```python
def Settings( **kwargs ):
return {
'flags': [ '-x', 'c++' ]
}
```
##### Java settings
[The java subserver][jdtls] allows [formatter configuration][jdt-formatter],
but it expects the configuration to be supplied in a different way than the rest.
For this purpose the `Settings` function can return a `formatter` property.
An example of the formatter configuration would be:
```python
def Settings( **kwargs ):
return {
'formatting_options': {
'org.eclipse.jdt.core.formatter.lineSplit': 30,
}
}
```
##### Python settings
The `Settings` function allows users to specify the Python interpreter and
the `sys.path` used by the completer to provide completion and code
comprehension. No additional arguments are passed.
The return value expected by the completer is a dictionary containing the
following items:
- `interpreter_path`: (optional) path to the Python interpreter. `~` and
environment variables in the path are expanded. If not an absolute path, it
will be searched through the `PATH`.
- `sys_path`: (optional) list of paths prepended to `sys.path`.
Usage example:
```python
def Settings( **kwargs ):
return {
'interpreter_path': '~/project/virtual_env/bin/python',
'sys_path': [ '~/project/third_party/module' ]
}
```
#### `PythonSysPath( **kwargs )`
Optional for Python support.
This function allows further customization of the Python path `sys.path`. Its
parameters are the possible items returned by the `Settings` function for the
Python completer:
- `interpreter_path`: path to the Python interpreter.
- `sys_path`: list of Python paths from `sys.path`.
The return value should be the modified list of Python paths.
See [ycmd's own `.ycm_extra_conf.py`][ycmd-extra-conf] for an example.
### Global extra conf file specification
The global extra module must expose the same functions as the
`.ycm_extra_conf.py` module with the following additions:
#### `YcmCorePreLoad()`
Optional.
This method, if defined, is called by the server prior to importing the c++
python plugin. It is not usually required and its use is for advanced users
only.
#### `Shutdown()`
Optional.
Called prior to the server exiting cleanly. It is not usually required and its
use is for advanced users only.
Backwards compatibility
-----------------------
ycmd's HTTP+JSON interface follows [SemVer][]. While ycmd has seen extensive use
over the last several years as part of YCM, the version number is below 1.0
because some parts of the API _might_ change slightly as people discover
possible problems integrating ycmd with other editors. In other words, the
current API might unintentionally be Vim-specific. We don't want that.
Note that ycmd's internal API's (i.e. anything other than HTTP+JSON) are **NOT**
covered by SemVer and _will_ randomly change underneath you. **DON'T** interact
with the Python/C++/etc code directly!
FAQ
---
### Is HMAC auth for requests/responses really necessary?
Without the HMAC auth, it's possible for a malicious website to impersonate the
user. Don't forget that evil.com can send requests to servers listening on
localhost if the user visits evil.com in a browser.
**This is not merely a theoretical concern**; a working proof-of-concept remote
code execution exploit [was created][exploit] for ycmd running on localhost. The
HMAC auth was added to block this attack vector.
Contributor Code of Conduct
---------------------------
Please note that this project is released with a [Contributor Code of
Conduct][ccoc]. By participating in this project you agree to abide by its
terms.
Contact
-------
If you have questions about the plugin or need help, please use the
[ycmd-users][] mailing list.
The author's homepage is .
License
-------
This software is licensed under the [GPL v3 license][gpl].
© 2015-2019 ycmd contributors
[ycmd-users]: https://groups.google.com/forum/?hl=en#!forum/ycmd-users
[ycm]: http://ycm-core.github.io/YouCompleteMe/
[atom-you-complete-me]: https://atom.io/packages/you-complete-me
[sublime-ycmd-completion]: https://packagecontrol.io/packages/YcmdCompletion
[sublime-ycmd]: https://packagecontrol.io/packages/YouCompleteMe
[semver]: http://semver.org/
[hmac]: http://en.wikipedia.org/wiki/Hash-based_message_authentication_code
[exploit]: https://groups.google.com/d/topic/ycm-users/NZAPrvaYgxo/discussion
[example-client]: https://github.com/ycm-core/ycmd/blob/master/examples/example_client.py
[example-readme]: https://github.com/ycm-core/ycmd/blob/master/examples/README.md
[trigger-defaults]: https://github.com/ycm-core/ycmd/blob/master/ycmd/completers/completer_utils.py#L143
[subsequence]: http://en.wikipedia.org/wiki/Subsequence
[ycm-install]: https://github.com/ycm-core/YouCompleteMe/blob/master/README.md#mac-os-x
[def-settings]: https://github.com/ycm-core/ycmd/blob/master/ycmd/default_settings.json
[base64]: http://en.wikipedia.org/wiki/Base64
[mkstemp]: http://man7.org/linux/man-pages/man3/mkstemp.3.html
[options]: https://github.com/ycm-core/YouCompleteMe#options
[extra-conf-doc]: https://github.com/ycm-core/YouCompleteMe#c-family-semantic-completion
[emacs-ycmd]: https://github.com/abingham/emacs-ycmd
[gpl]: http://www.gnu.org/copyleft/gpl.html
[gocode]: https://github.com/nsf/gocode
[godef]: https://github.com/Manishearth/godef
[kak-ycmd]: https://github.com/mawww/kak-ycmd
[ccoc]: https://github.com/ycm-core/ycmd/blob/master/CODE_OF_CONDUCT.md
[dev-setup]: https://github.com/ycm-core/ycmd/blob/master/DEV_SETUP.md
[test-setup]: https://github.com/ycm-core/ycmd/blob/master/TESTS.md
[extra-conf-vim-data-doc]: https://github.com/ycm-core/YouCompleteMe#the-gycm_extra_conf_vim_data-option
[vscode-you-complete-me]: https://marketplace.visualstudio.com/items?itemName=RichardHe.you-complete-me
[gycm]: https://github.com/jakeanq/gycm
[nano-ycmd]: https://github.com/orsonteodoro/nano-ycmd
[jdtls]: https://github.com/eclipse/eclipse.jdt.ls
[jdt-formatter]: https://github.com/eclipse/eclipse.jdt.ls/blob/master/org.eclipse.jdt.ls.core/.settings/org.eclipse.jdt.core.prefs
[api-docs]: https://ycm-core.github.io/ycmd/
[ycmd-extra-conf]: https://github.com/ycm-core/ycmd/blob/master/.ycm_extra_conf.py
[clangd]: https://clang.llvm.org/extra/clangd.html
[RLS]: https://github.com/rust-lang-nursery/rls
ycmd-0+20191222+git2771f6f+ds/TESTS.md 0000664 0000000 0000000 00000010701 13601473553 0016403 0 ustar 00root root 0000000 0000000 # Running ycmd tests
This readme documents instructions on running the test suite.
An alternative (canonical) reference is the scripts used for running the tests
on [Travis][], [CircleCI][], and [AppVeyor][]. These can be found in
`.travis.yml`, `appveyor.yml`, and the `ci` and `.circleci` directories.
## Requirements for running the tests
You need to have installed all the requirements in `test_requirements.txt`. The
simplest way to set this up is to use a [virtualenv][], for example:
```sh
$ mkdir ~/YouCompleteMe/tests
$ virtualenv ~/YouCompleteMe/tests
$ source ~/YouCompleteMe/tests/bin/activate
$ pip install -r test_requirements.txt
```
You also need to have all of ycmd's completers' requirements. See the
installation guide for details, but typically this involves manually installing:
* [Mono][]
* [Go][]
* [Node.js and npm][npm-install]
* [TypeScript][]
* [rustup][]
If you are unwilling or unable to install the requirements for all of the
completers, you can exclude certain completers with the `--no-completer`
option.
### mono non-standard path
Note: if your installation of mono is in a non-standard location,
OmniSharpServer will not start. Ensure that it is in a standard location, or
change the paths in `OmniSharpServer/OmniSharp/Solution/CSharpProject.cs`
## Running the tests
To run the full suite, just run `run_tests.py`. Options are:
* `--skip-build`: don't attempt to run the `build.py` script. Useful once
everything is built;
* `--no-completers`: do not build or test with listed semantic completion engine(s);
* `--completers`: only build and test with listed semantic completion engine(s);
* `--msvc`: the Microsoft Visual C++ version to build with (default: 15).
Windows only;
* `--coverage`: generate code coverage data.
Remaining arguments are passed to "nosetests" directly. This means that you
can run a specific script or a specific test as follows:
* Specific script: `./run_tests.py ycmd/tests/.py`
* Specific test: `./run_tests.py ycmd/tests/.py:`
For example:
* `./run_tests.py ycmd/tests/subcommands_test.py`
* `./run_tests.py ycmd/tests/subcommands_test.py:Subcommands_Basic_test`
NOTE: you must have UTF-8 support in your terminal when you do this, e.g.:
```sh
LANG=en_GB.utf8 ./run_tests.py --skip-build
```
## Coverage testing
We can generate coverage data for both the C++ layer and the Python layer. The
CI system will pass this coverage data to [codecov.io][] where you can view
coverage after pushing a branch.
C++ coverage testing is available only on Linux/Mac and uses gcov.
Stricly speaking, we use the `-coverage` option to your compiler, which in the
case of GNU and LLVM compilers, generate gcov-compatible data.
For Python, there's a coverage module which works nicely with `nosetests`. This
is very useful for highlighting areas of your code which are not covered by the
automated integration tests.
Run it like this:
```sh
./run_tests.py --coverage
```
This will print a summary and generate HTML output in `./cover`.
More information: https://coverage.readthedocs.org and
https://nose.readthedocs.org/en/latest/plugins/cover.html
## Troubleshooting
### All the tests fail with some missing package.
Make sure you have installed all the packages in `test_requirements.txt` with
`pip install -r test_requirements.txt`.
### All the CsCompleter tests fail on unix.
Likely to be a problem with the OmniSharpServer.
* Check that you have compiled OmniSharpServer in `third-party/OmniSharpServer`
* Check that OmniSharpServer starts manually from `ycmd/tests/cs/testdata` with
```sh
mono ../../../third_party/OmniSharpServer/OmniSharp/bin/Debug/OmniSharp.exe -s testy/testy.sln
```
### You get one or all of the following failures
ERROR: ycmd.tests.cs.get_completions_test.GetCompletions_PathWithSpace_test
FAIL: ycmd.tests.filename_completer_test.FilenameCompleter_test.SystemPathCompletion_test
Ensure that you have UTF-8 support in your environment (see above).
[travis]: https://travis-ci.org/Valloric/ycmd
[circleci]: https://circleci.com/gh/Valloric/ycmd
[appveyor]: https://ci.appveyor.com/project/Valloric/ycmd
[virtualenv]: https://packaging.python.org/guides/installing-using-pip-and-virtualenv/
[mono]: http://www.mono-project.com/download/stable/
[go]: https://golang.org/doc/install
[npm-install]: https://docs.npmjs.com/getting-started/installing-node
[typescript]: https://www.typescriptlang.org/#download-links
[rustup]: https://www.rustup.rs/
[codecov]: https://codecov.io/
ycmd-0+20191222+git2771f6f+ds/azure-pipelines.yml 0000664 0000000 0000000 00000014551 13601473553 0021025 0 ustar 00root root 0000000 0000000 jobs:
- job: linux
displayName: 'Linux'
pool:
# List of available software on this image:
# https://github.com/Microsoft/azure-pipelines-image-generation/blob/master/images/linux/Ubuntu1604-README.md
vmImage: 'ubuntu-16.04'
strategy:
matrix:
# Tests are failing on Python 2.7.0 with the exception
# "TypeError: argument can't be "
'Python 2.7 without libclang completer':
YCM_PYTHON_VERSION: '2.7.13'
USE_CLANG_COMPLETER: false
'Python 2.7':
YCM_PYTHON_VERSION: '2.7.13'
'Python 3.5':
YCM_PYTHON_VERSION: '3.5.3'
'Python 3.5 using Clang compiler':
YCM_PYTHON_VERSION: '3.5.3'
YCM_COMPILER: 'clang'
'C++ benchmark':
YCM_PYTHON_VERSION: '3.5.3'
YCM_BENCHMARK: true
COVERAGE: false
'C++ linting':
YCM_PYTHON_VERSION: '3.5.3'
YCM_CLANG_TIDY: true
COVERAGE: false
maxParallel: 6
variables:
COVERAGE: true
steps:
- checkout: self
submodules: recursive
- script: ./azure/linux/install_dependencies.sh
displayName: Install dependencies
- script: ./azure/run_tests.sh
displayName: Run tests
condition: and(succeeded(), and(ne(variables['YCM_BENCHMARK'], 'true'), ne(variables['YCM_CLANG_TIDY'], 'true')))
- script: ./azure/benchmark.sh
displayName: Benchmark
condition: and(succeeded(), eq(variables['YCM_BENCHMARK'], 'true'))
- script: ./azure/lint.sh
displayName: Lint
condition: and(succeeded(), eq(variables['YCM_CLANG_TIDY'], 'true'))
- script: ./azure/send_coverage.sh
displayName: Send coverage
condition: and(succeeded(), eq(variables['COVERAGE'], 'true'))
env:
CODECOV_TOKEN: $(CODECOV_TOKEN)
CODECOV_JOB_NAME: '$(Agent.JobName)'
- job: macos
displayName: 'macOS'
pool:
# List of available software on this image:
# https://github.com/Microsoft/azure-pipelines-image-generation/blob/master/images/macos/macos-10.13-Readme.md
vmImage: 'macOS-10.13'
strategy:
matrix:
'Python 2.7':
# Prior versions fail to compile with error "ld: library not found for
# -lSystemStubs"
YCM_PYTHON_VERSION: '2.7.13'
'Python 3.5':
YCM_PYTHON_VERSION: '3.5.3'
'C++ benchmark':
YCM_PYTHON_VERSION: '3.5.3'
YCM_BENCHMARK: true
maxParallel: 3
variables:
COVERAGE: true
steps:
- checkout: self
submodules: recursive
- script: ./azure/macos/install_dependencies.sh
displayName: Install dependencies
- script: ./azure/run_tests.sh
displayName: Run tests
condition: and(succeeded(), ne(variables['YCM_BENCHMARK'], 'true'))
- script: ./azure/benchmark.sh
displayName: Benchmark
condition: and(succeeded(), eq(variables['YCM_BENCHMARK'], 'true'))
- script: ./azure/send_coverage.sh
displayName: Send coverage
condition: and(succeeded(), eq(variables['COVERAGE'], 'true'))
env:
CODECOV_TOKEN: $(CODECOV_TOKEN)
CODECOV_JOB_NAME: '$(Agent.JobName)'
- job: windows_msvc14
displayName: 'Windows Visual Studio 2015'
pool:
# List of available software on this image:
# https://github.com/Microsoft/azure-pipelines-image-generation/blob/master/images/win/Vs2015-Server2012R2-Readme.md
vmImage: 'vs2015-win2012r2'
strategy:
matrix:
'Python 3.7 64-bit':
YCM_PYTHON_INSTALLER_URL: 'https://www.python.org/ftp/python/3.7.3/python-3.7.3-amd64.exe'
variables:
MSVC: 14
MSBUILD_PATH: 'C:\Program Files (x86)\MSBuild\14.0\Bin'
steps:
- checkout: self
submodules: recursive
- script: azure\windows\install_dependencies.bat
displayName: Install dependencies
- script: azure\windows\run_tests.bat
displayName: Run tests
- script: azure\windows\send_coverage.bat
displayName: Send coverage
env:
CODECOV_TOKEN: $(CODECOV_TOKEN)
CODECOV_JOB_NAME: '$(Agent.JobName)'
- job: windows_msvc15
displayName: 'Windows Visual Studio 2017'
pool:
# List of available software on this image:
# https://github.com/Microsoft/azure-pipelines-image-generation/blob/master/images/win/Vs2017-Server2016-Readme.md
vmImage: 'vs2017-win2016'
strategy:
matrix:
'Python 3.7 64-bit':
YCM_PYTHON_INSTALLER_URL: 'https://www.python.org/ftp/python/3.7.3/python-3.7.3-amd64.exe'
variables:
MSVC: 15
MSBUILD_PATH: 'C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin'
steps:
- checkout: self
submodules: recursive
- script: azure\windows\install_dependencies.bat
displayName: Install dependencies
- script: azure\windows\run_tests.bat
displayName: Run tests
- script: azure\windows\send_coverage.bat
displayName: Send coverage
env:
CODECOV_TOKEN: $(CODECOV_TOKEN)
CODECOV_JOB_NAME: '$(Agent.JobName)'
- job: windows_msvc17
displayName: 'Windows Visual Studio 2019'
pool:
# List of available software on this image:
# https://github.com/Microsoft/azure-pipelines-image-generation/blob/master/images/win/Vs2019-Server2019-Readme.md
vmImage: 'windows-2019'
strategy:
matrix:
'Python 2.7 64-bit':
YCM_PYTHON_INSTALLER_URL: 'https://www.python.org/ftp/python/2.7.15/python-2.7.15.amd64.msi'
'Python 3.7 32-bit':
YCM_PYTHON_INSTALLER_URL: 'https://www.python.org/ftp/python/3.7.3/python-3.7.3.exe'
'Python 3.7 64-bit':
YCM_PYTHON_INSTALLER_URL: 'https://www.python.org/ftp/python/3.7.3/python-3.7.3-amd64.exe'
'C++ benchmark':
YCM_PYTHON_INSTALLER_URL: 'https://www.python.org/ftp/python/3.7.3/python-3.7.3-amd64.exe'
YCM_BENCHMARK: true
COVERAGE: false
maxParallel: 4
variables:
MSVC: 16
MSBUILD_PATH: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin'
COVERAGE: true
steps:
- checkout: self
submodules: recursive
- script: azure\windows\install_dependencies.bat
displayName: Install dependencies
- script: azure\windows\run_tests.bat
displayName: Run tests
condition: and(succeeded(), ne(variables['YCM_BENCHMARK'], 'true'))
- script: azure\windows\benchmark.bat
displayName: Benchmark
condition: and(succeeded(), eq(variables['YCM_BENCHMARK'], 'true'))
- script: azure\windows\send_coverage.bat
displayName: Send coverage
condition: and(succeeded(), eq(variables['COVERAGE'], 'true'))
env:
CODECOV_TOKEN: $(CODECOV_TOKEN)
CODECOV_JOB_NAME: '$(Agent.JobName)'
ycmd-0+20191222+git2771f6f+ds/azure/ 0000775 0000000 0000000 00000000000 13601473553 0016306 5 ustar 00root root 0000000 0000000 ycmd-0+20191222+git2771f6f+ds/azure/README.md 0000664 0000000 0000000 00000000346 13601473553 0017570 0 ustar 00root root 0000000 0000000 Azure Pipelines Scripts
=======================
This directory contains scripts used for [testing ycmd on Azure
Pipelines](https://dev.azure.com/YouCompleteMe/YCM/_build?definitionId=2). They
should not normally be run by users.
ycmd-0+20191222+git2771f6f+ds/azure/benchmark.sh 0000775 0000000 0000000 00000001331 13601473553 0020575 0 ustar 00root root 0000000 0000000 # Exit immediately if a command returns a non-zero status.
set -e
# Required to enable Homebrew on Linux.
test -d /home/linuxbrew/.linuxbrew && eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)
eval "$(pyenv init -)"
pyenv global ${YCM_PYTHON_VERSION}
# It is quite easy to get the steps to configure Python wrong. Verify that the
# version of Python actually in the PATH and used is the version that was
# requested, and fail the build if we broke the setup.
python_version=$(python -c 'import sys; print( "{}.{}.{}".format( *sys.version_info[:3] ) )')
echo "Checking python version (actual ${python_version} vs expected ${YCM_PYTHON_VERSION})"
test ${python_version} == ${YCM_PYTHON_VERSION}
python benchmark.py
set +e
ycmd-0+20191222+git2771f6f+ds/azure/lint.sh 0000775 0000000 0000000 00000001377 13601473553 0017623 0 ustar 00root root 0000000 0000000 # Exit immediately if a command returns a non-zero status.
set -e
# Required to enable Homebrew on Linux.
test -d /home/linuxbrew/.linuxbrew && eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)
eval "$(pyenv init -)"
pyenv global ${YCM_PYTHON_VERSION}
# It is quite easy to get the steps to configure Python wrong. Verify that the
# version of Python actually in the PATH and used is the version that was
# requested, and fail the build if we broke the setup.
python_version=$(python -c 'import sys; print( "{}.{}.{}".format( *sys.version_info[:3] ) )')
echo "Checking python version (actual ${python_version} vs expected ${YCM_PYTHON_VERSION})"
test ${python_version} == ${YCM_PYTHON_VERSION}
python build.py --clang-completer --clang-tidy --no-regex
set +e
ycmd-0+20191222+git2771f6f+ds/azure/linux/ 0000775 0000000 0000000 00000000000 13601473553 0017445 5 ustar 00root root 0000000 0000000 ycmd-0+20191222+git2771f6f+ds/azure/linux/install_dependencies.sh 0000775 0000000 0000000 00000004611 13601473553 0024162 0 ustar 00root root 0000000 0000000 # Exit immediately if a command returns a non-zero status.
set -e
#
# Compiler setup
#
if [ "${YCM_COMPILER}" == "clang" ]; then
sudo apt-get install clang-3.5
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-3.5 100
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-3.5 100
else
sudo apt-get install gcc-4.8 g++-4.8
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-4.8 100
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++-4.8 100
fi
if [ "${YCM_CLANG_TIDY}" ]; then
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt-add-repository "deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-8 main"
sudo apt-get update
sudo apt-get install -y clang-tidy-8
sudo update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-8 100
fi
#
# Go setup
#
# Create manually the cache folder before pip does to avoid the error
#
# failed to initialize build cache at /home/vsts/.cache/go-build: mkdir /home/vsts/.cache/go-build: permission denied
#
# while installing the Go completer.
mkdir ${HOME}/.cache
#
# Python setup
#
sh -c "$(curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install.sh)"
eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)
# We need to use openssl 1.0 because the versions of python we build don't
# compile with openssl 1.1. We could bump python versions until they do, but
# we've generally committed to testing older versions.
brew install pyenv
eval "$(pyenv init -)"
# In order to work with ycmd, Python *must* be built as a shared library. This
# is set via the PYTHON_CONFIGURE_OPTS option.
PYTHON_CONFIGURE_OPTS="--enable-shared" \
CFLAGS="-I$(brew --prefix openssl)/include" \
LDFLAGS="-L$(brew --prefix openssl)/lib" \
pyenv install ${YCM_PYTHON_VERSION}
pyenv global ${YCM_PYTHON_VERSION}
pip install -r test_requirements.txt
# Enable coverage for Python subprocesses. See:
# http://coverage.readthedocs.io/en/latest/subprocess.html
echo -e "import coverage\ncoverage.process_startup()" > \
${HOME}/.pyenv/versions/${YCM_PYTHON_VERSION}/lib/python${YCM_PYTHON_VERSION%.*}/site-packages/sitecustomize.py
#
# Rust setup
#
# rustup is required to enable the Rust completer on Python versions older than
# 2.7.9.
if [ "${YCM_PYTHON_VERSION}" == "2.7.1" ]; then
curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain none
fi
set +e
ycmd-0+20191222+git2771f6f+ds/azure/macos/ 0000775 0000000 0000000 00000000000 13601473553 0017410 5 ustar 00root root 0000000 0000000 ycmd-0+20191222+git2771f6f+ds/azure/macos/install_dependencies.sh 0000775 0000000 0000000 00000001752 13601473553 0024130 0 ustar 00root root 0000000 0000000 # Exit immediately if a command returns a non-zero status.
set -e
#
# Python setup
#
brew install pyenv
eval "$(pyenv init -)"
# In order to work with ycmd, Python *must* be built as a shared library. The
# most compatible way to do this on macOS is with --enable-framework. This is
# set via the PYTHON_CONFIGURE_OPTS option.
PYTHON_CONFIGURE_OPTS="--enable-framework" \
pyenv install ${YCM_PYTHON_VERSION}
pyenv global ${YCM_PYTHON_VERSION}
pip install -r test_requirements.txt
# Enable coverage for Python subprocesses. See:
# http://coverage.readthedocs.io/en/latest/subprocess.html
echo -e "import coverage\ncoverage.process_startup()" > \
${HOME}/.pyenv/versions/${YCM_PYTHON_VERSION}/lib/python${YCM_PYTHON_VERSION%.*}/site-packages/sitecustomize.py
#
# Rust setup
#
# rustup is required to enable the Rust completer on Python versions older than
# 2.7.9.
if [ "${YCM_PYTHON_VERSION}" == "2.7.2" ]; then
curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain none
fi
set +e
ycmd-0+20191222+git2771f6f+ds/azure/run_tests.sh 0000775 0000000 0000000 00000001437 13601473553 0020700 0 ustar 00root root 0000000 0000000 # Exit immediately if a command returns a non-zero status.
set -e
# Required to enable Homebrew on Linux.
test -d /home/linuxbrew/.linuxbrew && eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)
eval "$(pyenv init -)"
pyenv global ${YCM_PYTHON_VERSION}
# It is quite easy to get the steps to configure Python wrong. Verify that the
# version of Python actually in the PATH and used is the version that was
# requested, and fail the build if we broke the setup.
python_version=$(python -c 'import sys; print( "{}.{}.{}".format( *sys.version_info[:3] ) )')
echo "Checking python version (actual ${python_version} vs expected ${YCM_PYTHON_VERSION})"
test ${python_version} == ${YCM_PYTHON_VERSION}
# Add the Cargo executable to PATH
PATH="${HOME}/.cargo/bin:${PATH}"
python run_tests.py
set +e
ycmd-0+20191222+git2771f6f+ds/azure/send_coverage.sh 0000775 0000000 0000000 00000000457 13601473553 0021457 0 ustar 00root root 0000000 0000000 # Exit immediately if a command returns a non-zero status.
set -e
# Required to enable Homebrew on Linux.
test -d /home/linuxbrew/.linuxbrew && eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)
eval "$(pyenv init -)"
pyenv global ${YCM_PYTHON_VERSION}
codecov --name "${CODECOV_JOB_NAME}"
set +e
ycmd-0+20191222+git2771f6f+ds/azure/windows/ 0000775 0000000 0000000 00000000000 13601473553 0020000 5 ustar 00root root 0000000 0000000 ycmd-0+20191222+git2771f6f+ds/azure/windows/benchmark.bat 0000775 0000000 0000000 00000000131 13601473553 0022420 0 ustar 00root root 0000000 0000000 :: Add Python to PATH
set "PATH=C:\Python;C:\Python\Scripts;%PATH%"
python benchmark.py
ycmd-0+20191222+git2771f6f+ds/azure/windows/install_dependencies.bat 0000775 0000000 0000000 00000001531 13601473553 0024647 0 ustar 00root root 0000000 0000000 ::
:: Python configuration
::
set python_installer_extension=%YCM_PYTHON_INSTALLER_URL:~-3%
if "%python_installer_extension%" == "exe" (
curl %YCM_PYTHON_INSTALLER_URL% -o C:\python-installer.exe
C:\python-installer.exe /quiet TargetDir=C:\Python
) else (
curl %YCM_PYTHON_INSTALLER_URL% -o C:\python-installer.msi
msiexec /i C:\python-installer.msi TARGETDIR=C:\Python /qn
)
C:\Python\Scripts\pip install -r test_requirements.txt
:: Enable coverage for Python subprocesses. See:
:: http://coverage.readthedocs.io/en/latest/subprocess.html
C:\Python\python -c "with open('C:\Python\Lib\site-packages\sitecustomize.py', 'w') as f: f.write('import coverage\ncoverage.process_startup()')"
::
:: Go configuration
::
curl https://dl.google.com/go/go1.12.4.windows-amd64.msi -o C:\go-installer.msi
msiexec /i C:\go-installer.msi TARGETDIR=C:\Go /qn
ycmd-0+20191222+git2771f6f+ds/azure/windows/run_tests.bat 0000775 0000000 0000000 00000000513 13601473553 0022520 0 ustar 00root root 0000000 0000000 :: Add the Python, MSBuild, Cargo, and Go executables to PATH.
set "PATH=C:\Python;C:\Python\Scripts;%PATH%"
set "PATH=%MSBUILD_PATH%;%PATH%"
set "PATH=%USERPROFILE%\.cargo\bin;%PATH%"
set "PATH=C:\Go\bin;%PATH%"
:: Prevent the already installed version of Go to conflict with ours.
set GOROOT=
python run_tests.py --msvc %MSVC%
ycmd-0+20191222+git2771f6f+ds/azure/windows/send_coverage.bat 0000775 0000000 0000000 00000000151 13601473553 0023274 0 ustar 00root root 0000000 0000000 :: Add Python to PATH
set "PATH=C:\Python;C:\Python\Scripts;%PATH%"
codecov --name "%CODECOV_JOB_NAME%"
ycmd-0+20191222+git2771f6f+ds/benchmark.py 0000775 0000000 0000000 00000002115 13601473553 0017466 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
from __future__ import print_function
from __future__ import division
from __future__ import unicode_literals
from __future__ import absolute_import
import argparse
import os
import os.path as p
import subprocess
import sys
DIR_OF_THIS_SCRIPT = p.dirname( p.abspath( __file__ ) )
def ParseArguments():
parser = argparse.ArgumentParser()
parser.add_argument( '--msvc', type = int, choices = [ 14, 15, 16 ],
default = 16, help = 'Choose the Microsoft Visual '
'Studio version (default: %(default)s).' )
return parser.parse_known_args()
def BuildYcmdLibsAndRunBenchmark( args, extra_args ):
build_cmd = [
sys.executable,
p.join( DIR_OF_THIS_SCRIPT, 'build.py' ),
'--clang-completer',
'--no-regex'
] + extra_args
os.environ[ 'YCM_BENCHMARK' ] = '1'
if args.msvc:
build_cmd.extend( [ '--msvc', str( args.msvc ) ] )
subprocess.check_call( build_cmd )
def Main():
args, extra_args = ParseArguments()
BuildYcmdLibsAndRunBenchmark( args, extra_args )
if __name__ == "__main__":
Main()
ycmd-0+20191222+git2771f6f+ds/build.py 0000775 0000000 0000000 00000116700 13601473553 0016641 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
# Passing an environment variable containing unicode literals to a subprocess
# on Windows and Python2 raises a TypeError. Since there is no unicode
# string in this script, we don't import unicode_literals to avoid the issue.
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
from tempfile import mkdtemp
import argparse
import errno
import hashlib
import multiprocessing
import os
import os.path as p
import platform
import re
import shlex
import shutil
import subprocess
import sys
import sysconfig
import tarfile
from zipfile import ZipFile
import tempfile
IS_64BIT = sys.maxsize > 2**32
PY_MAJOR, PY_MINOR = sys.version_info[ 0 : 2 ]
PY_VERSION = sys.version_info[ 0 : 3 ]
if PY_VERSION < ( 2, 7, 1 ) or ( 3, 0, 0 ) <= PY_VERSION < ( 3, 5, 1 ):
sys.exit( 'ycmd requires Python >= 2.7.1 or >= 3.5.1; '
'your version of Python is ' + sys.version )
DIR_OF_THIS_SCRIPT = p.dirname( p.abspath( __file__ ) )
DIR_OF_THIRD_PARTY = p.join( DIR_OF_THIS_SCRIPT, 'third_party' )
LIBCLANG_DIR = p.join( DIR_OF_THIRD_PARTY, 'clang', 'lib' )
for folder in os.listdir( DIR_OF_THIRD_PARTY ):
abs_folder_path = p.join( DIR_OF_THIRD_PARTY, folder )
if p.isdir( abs_folder_path ) and not os.listdir( abs_folder_path ):
sys.exit(
'ERROR: folder {} in {} is empty; you probably forgot to run:\n'
'\tgit submodule update --init --recursive\n'.format( folder,
DIR_OF_THIRD_PARTY )
)
sys.path[ 0:0 ] = [ p.join( DIR_OF_THIRD_PARTY, 'requests_deps', 'requests' ),
p.join( DIR_OF_THIRD_PARTY,
'requests_deps',
'urllib3',
'src' ),
p.join( DIR_OF_THIRD_PARTY, 'requests_deps', 'chardet' ),
p.join( DIR_OF_THIRD_PARTY, 'requests_deps', 'certifi' ),
p.join( DIR_OF_THIRD_PARTY, 'requests_deps', 'idna' ) ]
import requests
NO_DYNAMIC_PYTHON_ERROR = (
'ERROR: found static Python library ({library}) but a dynamic one is '
'required. You must use a Python compiled with the {flag} flag. '
'If using pyenv, you need to run the command:\n'
' export PYTHON_CONFIGURE_OPTS="{flag}"\n'
'before installing a Python version.' )
NO_PYTHON_LIBRARY_ERROR = 'ERROR: unable to find an appropriate Python library.'
NO_PYTHON_HEADERS_ERROR = 'ERROR: Python headers are missing in {include_dir}.'
# Regular expressions used to find static and dynamic Python libraries.
# Notes:
# - Python 3 library name may have an 'm' suffix on Unix platforms, for
# instance libpython3.5m.so;
# - the linker name (the soname without the version) does not always
# exist so we look for the versioned names too;
# - on Windows, the .lib extension is used instead of the .dll one. See
# https://en.wikipedia.org/wiki/Dynamic-link_library#Import_libraries
STATIC_PYTHON_LIBRARY_REGEX = '^libpython{major}\\.{minor}m?\\.a$'
DYNAMIC_PYTHON_LIBRARY_REGEX = """
^(?:
# Linux, BSD
libpython{major}\\.{minor}m?\\.so(\\.\\d+)*|
# OS X
libpython{major}\\.{minor}m?\\.dylib|
# Windows
python{major}{minor}\\.lib|
# Cygwin
libpython{major}\\.{minor}\\.dll\\.a
)$
"""
JDTLS_MILESTONE = '0.45.0'
JDTLS_BUILD_STAMP = '201910031256'
JDTLS_SHA256 = (
'06c499bf151d78027c2480bcbcca313f70ae0e8e07fc07cea6319359aea848f4'
)
TSSERVER_VERSION = '3.7.2'
RUST_TOOLCHAIN = 'nightly-2019-09-05'
RLS_DIR = p.join( DIR_OF_THIRD_PARTY, 'rls' )
BUILD_ERROR_MESSAGE = (
'ERROR: the build failed.\n\n'
'NOTE: it is *highly* unlikely that this is a bug but rather\n'
'that this is a problem with the configuration of your system\n'
'or a missing dependency. Please carefully read CONTRIBUTING.md\n'
'and if you\'re sure that it is a bug, please raise an issue on the\n'
'issue tracker, including the entire output of this script\n'
'and the invocation line used to run it.' )
CLANGD_VERSION = '9.0.0'
CLANGD_BINARIES_ERROR_MESSAGE = (
'No prebuilt Clang {version} binaries for {platform}. '
'You\'ll have to compile Clangd {version} from source '
'or use your system Clangd. '
'See the YCM docs for details on how to use a custom Clangd.' )
def RemoveDirectory( directory ):
try_number = 0
max_tries = 10
while try_number < max_tries:
try:
shutil.rmtree( directory )
return
except OSError:
try_number += 1
raise RuntimeError(
'Cannot remove directory {} after {} tries.'.format( directory,
max_tries ) )
def MakeCleanDirectory( directory_path ):
if p.exists( directory_path ):
RemoveDirectory( directory_path )
os.makedirs( directory_path )
def CheckFileIntegrity( file_path, check_sum ):
with open( file_path, 'rb' ) as existing_file:
existing_sha256 = hashlib.sha256( existing_file.read() ).hexdigest()
return existing_sha256 == check_sum
def DownloadFileTo( download_url, file_path ):
request = requests.get( download_url, stream = True )
with open( file_path, 'wb' ) as package_file:
package_file.write( request.content )
request.close()
def OnMac():
return platform.system() == 'Darwin'
def OnWindows():
return platform.system() == 'Windows'
def OnFreeBSD():
return platform.system() == 'FreeBSD'
def OnAArch64():
return platform.machine().lower().startswith( 'aarch64' )
def OnArm():
return platform.machine().lower().startswith( 'arm' )
def OnX86_64():
return platform.machine().lower().startswith( 'x86_64' )
def FindExecutableOrDie( executable, message ):
path = FindExecutable( executable )
if not path:
sys.exit( "ERROR: Unable to find executable '{}'. {}".format(
executable,
message ) )
return path
# On Windows, distutils.spawn.find_executable only works for .exe files
# but .bat and .cmd files are also executables, so we use our own
# implementation.
def FindExecutable( executable ):
# Executable extensions used on Windows
WIN_EXECUTABLE_EXTS = [ '.exe', '.bat', '.cmd' ]
paths = os.environ[ 'PATH' ].split( os.pathsep )
base, extension = os.path.splitext( executable )
if OnWindows() and extension.lower() not in WIN_EXECUTABLE_EXTS:
extensions = WIN_EXECUTABLE_EXTS
else:
extensions = [ '' ]
for extension in extensions:
executable_name = executable + extension
if not os.path.isfile( executable_name ):
for path in paths:
executable_path = os.path.join( path, executable_name )
if os.path.isfile( executable_path ):
return executable_path
else:
return executable_name
return None
def PathToFirstExistingExecutable( executable_name_list ):
for executable_name in executable_name_list:
path = FindExecutable( executable_name )
if path:
return path
return None
def NumCores():
ycm_cores = os.environ.get( 'YCM_CORES' )
if ycm_cores:
return int( ycm_cores )
try:
return multiprocessing.cpu_count()
except NotImplementedError:
return 1
def CheckCall( args, **kwargs ):
quiet = kwargs.pop( 'quiet', False )
status_message = kwargs.pop( 'status_message', None )
if quiet:
_CheckCallQuiet( args, status_message, **kwargs )
else:
_CheckCall( args, **kwargs )
def _CheckCallQuiet( args, status_message, **kwargs ):
if status_message:
# __future__ not appear to support flush= on print_function
sys.stdout.write( status_message + '...' )
sys.stdout.flush()
with tempfile.NamedTemporaryFile() as temp_file:
_CheckCall( args, stdout=temp_file, stderr=subprocess.STDOUT, **kwargs )
if status_message:
print( "OK" )
def _CheckCall( args, **kwargs ):
exit_message = kwargs.pop( 'exit_message', None )
stdout = kwargs.get( 'stdout', None )
try:
subprocess.check_call( args, **kwargs )
except subprocess.CalledProcessError as error:
if stdout is not None:
stdout.seek( 0 )
print( stdout.read().decode( 'utf-8' ) )
print( "FAILED" )
if exit_message:
sys.exit( exit_message )
sys.exit( error.returncode )
def GetGlobalPythonPrefix():
# In a virtualenv, sys.real_prefix points to the parent Python prefix.
if hasattr( sys, 'real_prefix' ):
return sys.real_prefix
# In a pyvenv (only available on Python 3), sys.base_prefix points to the
# parent Python prefix. Outside a pyvenv, it is equal to sys.prefix.
if PY_MAJOR >= 3:
return sys.base_prefix
return sys.prefix
def GetPossiblePythonLibraryDirectories():
prefix = GetGlobalPythonPrefix()
if OnWindows():
return [ p.join( prefix, 'libs' ) ]
# On pyenv and some distributions, there is no Python dynamic library in the
# directory returned by the LIBPL variable. Such library can be found in the
# "lib" or "lib64" folder of the base Python installation.
return [
sysconfig.get_config_var( 'LIBPL' ),
p.join( prefix, 'lib64' ),
p.join( prefix, 'lib' )
]
def FindPythonLibraries():
include_dir = sysconfig.get_config_var( 'INCLUDEPY' )
if not p.isfile( p.join( include_dir, 'Python.h' ) ):
sys.exit( NO_PYTHON_HEADERS_ERROR.format( include_dir = include_dir ) )
library_dirs = GetPossiblePythonLibraryDirectories()
# Since ycmd is compiled as a dynamic library, we can't link it to a Python
# static library. If we try, the following error will occur on Mac:
#
# Fatal Python error: PyThreadState_Get: no current thread
#
# while the error happens during linking on Linux and looks something like:
#
# relocation R_X86_64_32 against `a local symbol' can not be used when
# making a shared object; recompile with -fPIC
#
# On Windows, the Python library is always a dynamic one (an import library to
# be exact). To obtain a dynamic library on other platforms, Python must be
# compiled with the --enable-shared flag on Linux or the --enable-framework
# flag on Mac.
#
# So we proceed like this:
# - look for a dynamic library and return its path;
# - if a static library is found instead, raise an error with instructions
# on how to build Python as a dynamic library.
# - if no libraries are found, raise a generic error.
dynamic_name = re.compile( DYNAMIC_PYTHON_LIBRARY_REGEX.format(
major = PY_MAJOR, minor = PY_MINOR ), re.X )
static_name = re.compile( STATIC_PYTHON_LIBRARY_REGEX.format(
major = PY_MAJOR, minor = PY_MINOR ), re.X )
static_libraries = []
for library_dir in library_dirs:
if not p.exists( library_dir ):
continue
# Files are sorted so that we found the non-versioned Python library before
# the versioned one.
for filename in sorted( os.listdir( library_dir ) ):
if dynamic_name.match( filename ):
return p.join( library_dir, filename ), include_dir
if static_name.match( filename ):
static_libraries.append( p.join( library_dir, filename ) )
if static_libraries and not OnWindows():
dynamic_flag = ( '--enable-framework' if OnMac() else
'--enable-shared' )
sys.exit( NO_DYNAMIC_PYTHON_ERROR.format( library = static_libraries[ 0 ],
flag = dynamic_flag ) )
sys.exit( NO_PYTHON_LIBRARY_ERROR )
def CustomPythonCmakeArgs( args ):
# The CMake 'FindPythonLibs' Module does not work properly.
# So we are forced to do its job for it.
if not args.quiet:
print( 'Searching Python {major}.{minor} libraries...'.format(
major = PY_MAJOR, minor = PY_MINOR ) )
python_library, python_include = FindPythonLibraries()
if not args.quiet:
print( 'Found Python library: {0}'.format( python_library ) )
print( 'Found Python headers folder: {0}'.format( python_include ) )
return [
'-DPYTHON_LIBRARY={0}'.format( python_library ),
'-DPYTHON_INCLUDE_DIR={0}'.format( python_include )
]
def GetGenerator( args ):
if args.ninja:
return 'Ninja'
if OnWindows():
# The architecture must be specified through the -A option for the Visual
# Studio 16 generator.
if args.msvc == 16:
return 'Visual Studio 16'
return 'Visual Studio {version}{arch}'.format(
version = args.msvc, arch = ' Win64' if IS_64BIT else '' )
return 'Unix Makefiles'
def ParseArguments():
parser = argparse.ArgumentParser()
parser.add_argument( '--clang-completer', action = 'store_true',
help = 'Enable C-family semantic completion engine '
'through libclang.' )
parser.add_argument( '--clangd-completer', action = 'store_true',
help = 'Enable C-family semantic completion engine '
'through clangd lsp server.(EXPERIMENTAL)' )
parser.add_argument( '--cs-completer', action = 'store_true',
help = 'Enable C# semantic completion engine.' )
parser.add_argument( '--go-completer', action = 'store_true',
help = 'Enable Go semantic completion engine.' )
parser.add_argument( '--rust-completer', action = 'store_true',
help = 'Enable Rust semantic completion engine.' )
parser.add_argument( '--java-completer', action = 'store_true',
help = 'Enable Java semantic completion engine.' ),
parser.add_argument( '--ts-completer', action = 'store_true',
help = 'Enable JavaScript and TypeScript semantic '
'completion engine.' ),
parser.add_argument( '--system-boost', action = 'store_true',
help = 'Use the system boost instead of bundled one. '
'NOT RECOMMENDED OR SUPPORTED!' )
parser.add_argument( '--system-libclang', action = 'store_true',
help = 'Use system libclang instead of downloading one '
'from llvm.org. NOT RECOMMENDED OR SUPPORTED!' )
parser.add_argument( '--msvc', type = int, choices = [ 14, 15, 16 ],
default = 16, help = 'Choose the Microsoft Visual '
'Studio version (default: %(default)s).' )
parser.add_argument( '--ninja', action = 'store_true',
help = 'Use Ninja build system.' )
parser.add_argument( '--all',
action = 'store_true',
help = 'Enable all supported completers',
dest = 'all_completers' )
parser.add_argument( '--enable-coverage',
action = 'store_true',
help = 'For developers: Enable gcov coverage for the '
'c++ module' )
parser.add_argument( '--enable-debug',
action = 'store_true',
help = 'For developers: build ycm_core library with '
'debug symbols' )
parser.add_argument( '--build-dir',
help = 'For developers: perform the build in the '
'specified directory, and do not delete the '
'build output. This is useful for incremental '
'builds, and required for coverage data' )
parser.add_argument( '--quiet',
action = 'store_true',
help = 'Quiet installation mode. Just print overall '
'progress and errors' )
parser.add_argument( '--skip-build',
action = 'store_true',
help = "Don't build ycm_core lib, just install deps" )
parser.add_argument( '--no-regex',
action = 'store_true',
help = "Don't build the regex module" )
parser.add_argument( '--clang-tidy',
action = 'store_true',
help = 'For developers: Run clang-tidy static analysis '
'on the ycm_core code itself.' )
parser.add_argument( '--core-tests', nargs = '?', const = '*',
help = 'Run core tests and optionally filter them.' )
parser.add_argument( '--cmake-path',
help = 'For developers: specify the cmake executable. '
'Useful for testing with specific versions, or '
'if the system is unable to find cmake.' )
# These options are deprecated.
parser.add_argument( '--omnisharp-completer', action = 'store_true',
help = argparse.SUPPRESS )
parser.add_argument( '--gocode-completer', action = 'store_true',
help = argparse.SUPPRESS )
parser.add_argument( '--racer-completer', action = 'store_true',
help = argparse.SUPPRESS )
parser.add_argument( '--tern-completer', action = 'store_true',
help = argparse.SUPPRESS )
parser.add_argument( '--js-completer', action = 'store_true',
help = argparse.SUPPRESS )
args = parser.parse_args()
# coverage is not supported for c++ on MSVC
if not OnWindows() and args.enable_coverage:
# We always want a debug build when running with coverage enabled
args.enable_debug = True
if args.core_tests:
os.environ[ 'YCM_TESTRUN' ] = '1'
elif os.environ.get( 'YCM_TESTRUN' ):
args.core_tests = '*'
if not args.clang_tidy and os.environ.get( 'YCM_CLANG_TIDY' ):
args.clang_tidy = True
if ( args.system_libclang and
not args.clang_completer and
not args.all_completers ):
sys.exit( 'ERROR: you can\'t pass --system-libclang without also passing '
'--clang-completer or --all as well.' )
return args
def FindCmake( args ):
cmake_exe = 'cmake'
if args.cmake_path:
cmake_exe = args.cmake_path
return FindExecutableOrDie( cmake_exe, 'CMake is required to build ycmd' )
def GetCmakeCommonArgs( args ):
cmake_args = [ '-G', GetGenerator( args ) ]
# Set the architecture for the Visual Studio 16 generator.
if OnWindows() and args.msvc == 16:
arch = 'x64' if IS_64BIT else 'Win32'
cmake_args.extend( [ '-A', arch ] )
cmake_args.extend( CustomPythonCmakeArgs( args ) )
return cmake_args
def GetCmakeArgs( parsed_args ):
cmake_args = []
if parsed_args.clang_completer or parsed_args.all_completers:
cmake_args.append( '-DUSE_CLANG_COMPLETER=ON' )
if parsed_args.clang_tidy:
cmake_args.append( '-DUSE_CLANG_TIDY=ON' )
if parsed_args.system_libclang:
cmake_args.append( '-DUSE_SYSTEM_LIBCLANG=ON' )
if parsed_args.system_boost:
cmake_args.append( '-DUSE_SYSTEM_BOOST=ON' )
if parsed_args.enable_debug:
cmake_args.append( '-DCMAKE_BUILD_TYPE=Debug' )
cmake_args.append( '-DUSE_DEV_FLAGS=ON' )
# coverage is not supported for c++ on MSVC
if not OnWindows() and parsed_args.enable_coverage:
cmake_args.append( '-DCMAKE_CXX_FLAGS=-coverage' )
use_python2 = 'ON' if PY_MAJOR == 2 else 'OFF'
cmake_args.append( '-DUSE_PYTHON2=' + use_python2 )
extra_cmake_args = os.environ.get( 'EXTRA_CMAKE_ARGS', '' )
# We use shlex split to properly parse quoted CMake arguments.
cmake_args.extend( shlex.split( extra_cmake_args ) )
return cmake_args
def RunYcmdTests( args, build_dir ):
tests_dir = p.join( build_dir, 'ycm', 'tests' )
new_env = os.environ.copy()
if OnWindows():
# We prepend the ycm_core and Clang third-party directories to the PATH
# instead of overwriting it so that the executable is able to find the
# Python library.
new_env[ 'PATH' ] = ( DIR_OF_THIS_SCRIPT + ';' +
LIBCLANG_DIR + ';' +
new_env[ 'PATH' ] )
else:
new_env[ 'LD_LIBRARY_PATH' ] = LIBCLANG_DIR
tests_cmd = [ p.join( tests_dir, 'ycm_core_tests' ) ]
if args.core_tests != '*':
tests_cmd.append( '--gtest_filter={}'.format( args.core_tests ) )
CheckCall( tests_cmd,
env = new_env,
quiet = args.quiet,
status_message = 'Running ycmd tests' )
def RunYcmdBenchmarks( build_dir ):
benchmarks_dir = p.join( build_dir, 'ycm', 'benchmarks' )
new_env = os.environ.copy()
if OnWindows():
# We prepend the ycm_core and Clang third-party directories to the PATH
# instead of overwriting it so that the executable is able to find the
# Python library.
new_env[ 'PATH' ] = ( DIR_OF_THIS_SCRIPT + ';' +
LIBCLANG_DIR + ';' +
new_env[ 'PATH' ] )
else:
new_env[ 'LD_LIBRARY_PATH' ] = LIBCLANG_DIR
# Note we don't pass the quiet flag here because the output of the benchmark
# is the only useful info.
CheckCall( p.join( benchmarks_dir, 'ycm_core_benchmarks' ), env = new_env )
# On Windows, if the ycmd library is in use while building it, a LNK1104
# fatal error will occur during linking. Exit the script early with an
# error message if this is the case.
def ExitIfYcmdLibInUseOnWindows():
if not OnWindows():
return
ycmd_library = p.join( DIR_OF_THIS_SCRIPT, 'ycm_core.pyd' )
if not p.exists( ycmd_library ):
return
try:
open( p.join( ycmd_library ), 'a' ).close()
except IOError as error:
if error.errno == errno.EACCES:
sys.exit( 'ERROR: ycmd library is currently in use. '
'Stop all ycmd instances before compilation.' )
def GetCMakeBuildConfiguration( args ):
if OnWindows():
if args.enable_debug:
return [ '--config', 'Debug' ]
return [ '--config', 'Release' ]
return [ '--', '-j', str( NumCores() ) ]
def BuildYcmdLib( cmake, cmake_common_args, script_args ):
if script_args.build_dir:
build_dir = os.path.abspath( script_args.build_dir )
if not os.path.exists( build_dir ):
os.makedirs( build_dir )
else:
build_dir = mkdtemp( prefix = 'ycm_build_' )
try:
os.chdir( build_dir )
configure_command = ( [ cmake ] + cmake_common_args +
GetCmakeArgs( script_args ) )
configure_command.append( p.join( DIR_OF_THIS_SCRIPT, 'cpp' ) )
CheckCall( configure_command,
exit_message = BUILD_ERROR_MESSAGE,
quiet = script_args.quiet,
status_message = 'Generating ycmd build configuration' )
build_targets = [ 'ycm_core' ]
if script_args.core_tests:
build_targets.append( 'ycm_core_tests' )
if 'YCM_BENCHMARK' in os.environ:
build_targets.append( 'ycm_core_benchmarks' )
build_config = GetCMakeBuildConfiguration( script_args )
for target in build_targets:
build_command = ( [ cmake, '--build', '.', '--target', target ] +
build_config )
CheckCall( build_command,
exit_message = BUILD_ERROR_MESSAGE,
quiet = script_args.quiet,
status_message = 'Compiling ycmd target: {0}'.format(
target ) )
if script_args.core_tests:
RunYcmdTests( script_args, build_dir )
if 'YCM_BENCHMARK' in os.environ:
RunYcmdBenchmarks( build_dir )
finally:
os.chdir( DIR_OF_THIS_SCRIPT )
if script_args.build_dir:
print( 'The build files are in: ' + build_dir )
else:
RemoveDirectory( build_dir )
def BuildRegexModule( cmake, cmake_common_args, script_args ):
build_dir = mkdtemp( prefix = 'regex_build_' )
try:
os.chdir( build_dir )
configure_command = [ cmake ] + cmake_common_args
configure_command.append( p.join( DIR_OF_THIS_SCRIPT,
'third_party', 'cregex' ) )
CheckCall( configure_command,
exit_message = BUILD_ERROR_MESSAGE,
quiet = script_args.quiet,
status_message = 'Generating regex build configuration' )
build_config = GetCMakeBuildConfiguration( script_args )
build_command = ( [ cmake, '--build', '.', '--target', '_regex' ] +
build_config )
CheckCall( build_command,
exit_message = BUILD_ERROR_MESSAGE,
quiet = script_args.quiet,
status_message = 'Compiling regex module' )
finally:
os.chdir( DIR_OF_THIS_SCRIPT )
RemoveDirectory( build_dir )
def EnableCsCompleter( args ):
def WriteStdout( text ):
if not args.quiet:
sys.stdout.write( text )
sys.stdout.flush()
if args.quiet:
sys.stdout.write( 'Installing Omnisharp for C# support...' )
sys.stdout.flush()
build_dir = p.join( DIR_OF_THIRD_PARTY, "omnisharp-roslyn" )
try:
MkDirIfMissing( build_dir )
os.chdir( build_dir )
download_data = GetCsCompleterDataForPlatform()
version = download_data[ 'version' ]
WriteStdout( "Installing Omnisharp {}\n".format( version ) )
CleanCsCompleter( build_dir, version )
package_path = DownloadCsCompleter( WriteStdout, download_data )
ExtractCsCompleter( WriteStdout, build_dir, package_path )
WriteStdout( "Done installing Omnisharp\n" )
if args.quiet:
print( 'OK' )
finally:
os.chdir( DIR_OF_THIS_SCRIPT )
def MkDirIfMissing( path ):
try:
os.mkdir( path )
except OSError:
pass
def CleanCsCompleter( build_dir, version ):
for file_name in os.listdir( build_dir ):
file_path = os.path.join( build_dir, file_name )
if file_name == version:
continue
if os.path.isfile( file_path ):
os.remove( file_path )
elif os.path.isdir( file_path ):
import shutil
shutil.rmtree( file_path )
def DownloadCsCompleter( writeStdout, download_data ):
file_name = download_data[ 'file_name' ]
download_url = download_data[ 'download_url' ]
check_sum = download_data[ 'check_sum' ]
version = download_data[ 'version' ]
MkDirIfMissing( version )
package_path = p.join( version, file_name )
if ( p.exists( package_path )
and not CheckFileIntegrity( package_path, check_sum ) ):
writeStdout( 'Cached Omnisharp file does not match checksum.\n' )
writeStdout( 'Removing...' )
os.remove( package_path )
writeStdout( 'DONE\n' )
if p.exists( package_path ):
writeStdout( 'Using cached Omnisharp: {}\n'.format( file_name ) )
else:
writeStdout( 'Downloading Omnisharp from {}...'.format(
download_url ) )
DownloadFileTo( download_url, package_path )
writeStdout( 'DONE\n' )
return package_path
def ExtractCsCompleter( writeStdout, build_dir, package_path ):
writeStdout( 'Extracting Omnisharp to {}...'.format( build_dir ) )
if OnWindows():
with ZipFile( package_path, 'r' ) as package_zip:
package_zip.extractall()
else:
with tarfile.open( package_path ) as package_tar:
package_tar.extractall()
writeStdout( 'DONE\n' )
def GetCsCompleterDataForPlatform():
####################################
# GENERATED BY update_omnisharp.py #
# DON'T MANUALLY EDIT #
####################################
DATA = {
'win32': {
'file_name': 'omnisharp.http-win-x86.zip',
'version': 'v1.34.2',
'download_url': ( 'https://github.com/OmniSharp/omnisharp-roslyn/relea'
'ses/download/v1.34.2/omnisharp.http-win-x86.zip' ),
'check_sum': ( 'd66ee6ce347bba58de06a585bff63e8f42178c8b212883be0700919'
'61c3c63d6' ),
},
'win64': {
'file_name': 'omnisharp.http-win-x64.zip',
'version': 'v1.34.2',
'download_url': ( 'https://github.com/OmniSharp/omnisharp-roslyn/relea'
'ses/download/v1.34.2/omnisharp.http-win-x64.zip' ),
'check_sum': ( 'ab6bdac04b7225a69de11a0bdf0777facbe7d9895e9b6b4c8ebe8b5'
'4b51412e5' ),
},
'macos': {
'file_name': 'omnisharp.http-osx.tar.gz',
'version': 'v1.34.2',
'download_url': ( 'https://github.com/OmniSharp/omnisharp-roslyn/relea'
'ses/download/v1.34.2/omnisharp.http-osx.tar.gz' ),
'check_sum': ( 'bea5e6e35a45bcece293ad2a32b717be16242d5ee6ca0004ca1c7af'
'c9cacdbf7' ),
},
'linux64': {
'file_name': 'omnisharp.http-linux-x64.tar.gz',
'version': 'v1.34.2',
'download_url': ( 'https://github.com/OmniSharp/omnisharp-roslyn/relea'
'ses/download/v1.34.2/omnisharp.http-linux-x64.tar.g'
'z' ),
'check_sum': ( '16aa6f3d97c11829b3fc177cea5c221ddb952a5d372fe84e735f695'
'50d661722' ),
},
'linux32': {
'file_name': 'omnisharp.http-linux-x86.tar.gz',
'version': 'v1.34.2',
'download_url': ( 'https://github.com/OmniSharp/omnisharp-roslyn/relea'
'ses/download/v1.34.2/omnisharp.http-linux-x86.tar.g'
'z' ),
'check_sum': ( '6f89480ce95286640f670943f5d8e0d1f1c28db6bab07461be3f452'
'e8b43c70b' ),
},
}
if OnWindows():
return DATA[ 'win64' if IS_64BIT else 'win32' ]
else:
if OnMac():
return DATA[ 'macos' ]
return DATA[ 'linux64' if IS_64BIT else 'linux32' ]
def EnableGoCompleter( args ):
go = FindExecutableOrDie( 'go', 'go is required to build gocode.' )
go_dir = p.join( DIR_OF_THIS_SCRIPT, 'third_party', 'go' )
os.chdir( p.join(
go_dir, 'src', 'golang.org', 'x', 'tools', 'cmd', 'gopls' ) )
CheckCall( [ go, 'build' ],
quiet = args.quiet,
status_message = 'Building gopls for go completion' )
def WriteToolchainVersion( version ):
path = p.join( RLS_DIR, 'TOOLCHAIN_VERSION' )
with open( path, 'w' ) as f:
f.write( version )
def ReadToolchainVersion():
try:
filepath = p.join( RLS_DIR, 'TOOLCHAIN_VERSION' )
with open( filepath ) as f:
return f.read().strip()
# We need to check for IOError for Python 2 and OSError for Python 3.
except ( IOError, OSError ):
return None
def EnableRustCompleter( switches ):
if switches.quiet:
sys.stdout.write( 'Installing RLS for Rust support...' )
sys.stdout.flush()
toolchain_version = ReadToolchainVersion()
if toolchain_version != RUST_TOOLCHAIN:
install_dir = mkdtemp( prefix = 'rust_install_' )
new_env = os.environ.copy()
new_env[ 'RUSTUP_HOME' ] = install_dir
# Python versions older than 2.7.9 lack SNI support which is required to
# download rustup from the official website.
if PY_VERSION < ( 2, 7, 9 ):
rustup = FindExecutableOrDie( 'rustup',
'rustup is required to install RLS '
'on Python < 2.7.9.' )
else:
rustup_init = os.path.join( install_dir, 'rustup-init' )
if OnWindows():
rustup_cmd = [ rustup_init ]
rustup_url = 'https://win.rustup.rs/{}'.format(
'x86_64' if IS_64BIT else 'i686' )
else:
rustup_cmd = [ 'sh', rustup_init ]
rustup_url = 'https://sh.rustup.rs'
DownloadFileTo( rustup_url, rustup_init )
new_env[ 'CARGO_HOME' ] = install_dir
CheckCall( rustup_cmd + [ '-y',
'--default-toolchain', 'none',
'--no-modify-path' ],
env = new_env,
quiet = switches.quiet )
rustup = os.path.join( install_dir, 'bin', 'rustup' )
try:
CheckCall( [ rustup, 'toolchain', 'install', RUST_TOOLCHAIN ],
env = new_env,
quiet = switches.quiet )
for component in [ 'rls', 'rust-analysis', 'rust-src' ]:
CheckCall( [ rustup, 'component', 'add', component,
'--toolchain', RUST_TOOLCHAIN ],
env = new_env,
quiet = switches.quiet )
toolchain_dir = subprocess.check_output(
[ rustup, 'run', RUST_TOOLCHAIN, 'rustc', '--print', 'sysroot' ],
env = new_env
).rstrip().decode( 'utf8' )
if p.exists( RLS_DIR ):
RemoveDirectory( RLS_DIR )
os.makedirs( RLS_DIR )
for folder in os.listdir( toolchain_dir ):
shutil.move( p.join( toolchain_dir, folder ), RLS_DIR )
WriteToolchainVersion( RUST_TOOLCHAIN )
finally:
RemoveDirectory( install_dir )
if switches.quiet:
print( 'OK' )
def EnableJavaScriptCompleter( args ):
npm = FindExecutableOrDie( 'npm', 'npm is required to set up Tern.' )
# We install Tern into a runtime directory. This allows us to control
# precisely the version (and/or git commit) that is used by ycmd. We use a
# separate runtime directory rather than a submodule checkout directory
# because we want to allow users to install third party plugins to
# node_modules of the Tern runtime. We also want to be able to install our
# own plugins to improve the user experience for all users.
#
# This is not possible if we use a git submodule for Tern and simply run 'npm
# install' within the submodule source directory, as subsequent 'npm install
# tern-my-plugin' will (heinously) install another (arbitrary) version of Tern
# within the Tern source tree (e.g. third_party/tern/node_modules/tern. The
# reason for this is that the plugin that gets installed has "tern" as a
# dependency, and npm isn't smart enough to know that you're installing
# *within* the Tern distribution. Or it isn't intended to work that way.
#
# So instead, we have a package.json within our "Tern runtime" directory
# (third_party/tern_runtime) that defines the packages that we require,
# including Tern and any plugins which we require as standard.
os.chdir( p.join( DIR_OF_THIS_SCRIPT, 'third_party', 'tern_runtime' ) )
CheckCall( [ npm, 'install', '--production' ],
quiet = args.quiet,
status_message = 'Setting up Tern for JavaScript completion' )
def EnableJavaCompleter( switches ):
def Print( *args, **kwargs ):
if not switches.quiet:
print( *args, **kwargs )
if switches.quiet:
sys.stdout.write( 'Installing jdt.ls for Java support...' )
sys.stdout.flush()
TARGET = p.join( DIR_OF_THIRD_PARTY, 'eclipse.jdt.ls', 'target', )
REPOSITORY = p.join( TARGET, 'repository' )
CACHE = p.join( TARGET, 'cache' )
JDTLS_SERVER_URL_FORMAT = ( 'http://download.eclipse.org/jdtls/snapshots/'
'{jdtls_package_name}' )
JDTLS_PACKAGE_NAME_FORMAT = ( 'jdt-language-server-{jdtls_milestone}-'
'{jdtls_build_stamp}.tar.gz' )
package_name = JDTLS_PACKAGE_NAME_FORMAT.format(
jdtls_milestone = JDTLS_MILESTONE,
jdtls_build_stamp = JDTLS_BUILD_STAMP )
url = JDTLS_SERVER_URL_FORMAT.format(
jdtls_milestone = JDTLS_MILESTONE,
jdtls_package_name = package_name )
file_name = p.join( CACHE, package_name )
MakeCleanDirectory( REPOSITORY )
if not p.exists( CACHE ):
os.makedirs( CACHE )
elif p.exists( file_name ) and not CheckFileIntegrity( file_name,
JDTLS_SHA256 ):
Print( 'Cached tar file does not match checksum. Removing...' )
os.remove( file_name )
if p.exists( file_name ):
Print( 'Using cached jdt.ls: {0}'.format( file_name ) )
else:
Print( "Downloading jdt.ls from {0}...".format( url ) )
DownloadFileTo( url, file_name )
Print( "Extracting jdt.ls to {0}...".format( REPOSITORY ) )
with tarfile.open( file_name ) as package_tar:
package_tar.extractall( REPOSITORY )
Print( "Done installing jdt.ls" )
if switches.quiet:
print( 'OK' )
def EnableTypeScriptCompleter( args ):
npm = FindExecutableOrDie( 'npm', 'npm is required to install TSServer.' )
tsserver_folder = p.join( DIR_OF_THIRD_PARTY, 'tsserver' )
CheckCall( [ npm, 'install', '-g', '--prefix', tsserver_folder,
'typescript@{version}'.format( version = TSSERVER_VERSION ) ],
quiet = args.quiet,
status_message = 'Installing TSServer for JavaScript '
'and TypeScript completion' )
def GetClangdTarget():
if OnWindows():
return [
( 'clangd-{version}-win64',
'e9dce7ae8984cdb719747780323c2cdd2152f41b3aa773510b37ad8de6788edf' ),
( 'clangd-{version}-win32',
'48b33eeab7e20c5388bd29503be6486260449cc0fbf631999e14c4d98b97b7c6' ) ]
if OnMac():
return [
( 'clangd-{version}-x86_64-apple-darwin',
'c89609cd7dcdf60df62e0d28841266ebe7514b2b68739bd6f0399bf74928a165' ) ]
if OnFreeBSD():
return [
( 'clangd-{version}-amd64-unknown-freebsd11',
'e1169eb2b432af0c31d812fa5d0f68e670c1a5efa3e51d00400d847800e6b257' ),
( 'clangd-{version}-i386-unknown-freebsd11',
'34ded7733cd2bd23b6587d29d78dbf8192ef3134cf692f09263dd1b5e5a58f6f' ) ]
if OnAArch64():
return [
( 'clangd-{version}-aarch64-linux-gnu',
'e593f7d036434db023c1c323756d6630bb4a2f868c45d682ba967846061f5fa9' ) ]
if OnArm():
return [
( 'clangd-{version}-armv7a-linux-gnueabihf',
'ff1d8f20eddd7c9d659fb1e692fe961526ff1b858c0798781fad62f2f9e0522b' ) ]
if OnX86_64():
return [
( 'clangd-{version}-x86_64-unknown-linux-gnu',
'742ee805373b89e6b30711847af1fc391fe7f8ecb89cf8f8b9515f412571c0cb' ) ]
sys.exit( CLANGD_BINARIES_ERROR_MESSAGE.format( version = CLANGD_VERSION,
platform = 'this system' ) )
def DownloadClangd( printer ):
CLANGD_DIR = p.join( DIR_OF_THIRD_PARTY, 'clangd', )
CLANGD_CACHE_DIR = p.join( CLANGD_DIR, 'cache' )
CLANGD_OUTPUT_DIR = p.join( CLANGD_DIR, 'output' )
target = GetClangdTarget()
target_name, check_sum = target[ not IS_64BIT ]
target_name = target_name.format( version = CLANGD_VERSION )
file_name = '{}.tar.bz2'.format( target_name )
download_url = 'https://dl.bintray.com/ycm-core/clangd/{}'.format( file_name )
file_name = p.join( CLANGD_CACHE_DIR, file_name )
MakeCleanDirectory( CLANGD_OUTPUT_DIR )
if not p.exists( CLANGD_CACHE_DIR ):
os.makedirs( CLANGD_CACHE_DIR )
elif p.exists( file_name ) and not CheckFileIntegrity( file_name, check_sum ):
printer( 'Cached Clangd archive does not match checksum. Removing...' )
os.remove( file_name )
if p.exists( file_name ):
printer( 'Using cached Clangd: {}'.format( file_name ) )
else:
printer( "Downloading Clangd from {}...".format( download_url ) )
DownloadFileTo( download_url, file_name )
if not CheckFileIntegrity( file_name, check_sum ):
sys.exit( 'ERROR: downloaded Clangd archive does not match checksum.' )
printer( "Extracting Clangd to {}...".format( CLANGD_OUTPUT_DIR ) )
with tarfile.open( file_name ) as package_tar:
package_tar.extractall( CLANGD_OUTPUT_DIR )
printer( "Done installing Clangd" )
def EnableClangdCompleter( Args ):
if Args.quiet:
sys.stdout.write( 'Setting up Clangd completer...' )
sys.stdout.flush()
def Print( msg ):
if not Args.quiet:
print( msg )
DownloadClangd( Print )
if Args.quiet:
print( 'OK' )
if not Args.quiet:
print( 'Clangd completer enabled. If you are using .ycm_extra_conf.py '
'files, make sure they use Settings() instead of the old and '
'deprecated FlagsForFile().' )
def WritePythonUsedDuringBuild():
path = p.join( DIR_OF_THIS_SCRIPT, 'PYTHON_USED_DURING_BUILDING' )
with open( path, 'w' ) as f:
f.write( sys.executable )
def DoCmakeBuilds( args ):
cmake = FindCmake( args )
cmake_common_args = GetCmakeCommonArgs( args )
if not args.skip_build:
ExitIfYcmdLibInUseOnWindows()
BuildYcmdLib( cmake, cmake_common_args, args )
WritePythonUsedDuringBuild()
if not args.no_regex:
BuildRegexModule( cmake, cmake_common_args, args )
def Main():
args = ParseArguments()
if not args.skip_build or not args.no_regex:
DoCmakeBuilds( args )
if args.cs_completer or args.omnisharp_completer or args.all_completers:
EnableCsCompleter( args )
if args.go_completer or args.gocode_completer or args.all_completers:
EnableGoCompleter( args )
if args.js_completer or args.tern_completer or args.all_completers:
EnableJavaScriptCompleter( args )
if args.rust_completer or args.racer_completer or args.all_completers:
EnableRustCompleter( args )
if args.java_completer or args.all_completers:
EnableJavaCompleter( args )
if args.ts_completer or args.all_completers:
EnableTypeScriptCompleter( args )
if args.clangd_completer or args.all_completers:
EnableClangdCompleter( args )
if __name__ == '__main__':
Main()
ycmd-0+20191222+git2771f6f+ds/codecov.yml 0000664 0000000 0000000 00000001521 13601473553 0017324 0 ustar 00root root 0000000 0000000 codecov:
notify:
require_ci_to_pass: yes
coverage:
precision: 2
round: down
range: 70...100
status:
# Learn more at https://codecov.io/docs#yaml_default_commit_status
project: true
patch: true
changes: true
# We don't want statistics for the tests themselves and certainly not for the
# benchmarks and boost libraries. Note that while we exclude the gcov data for
# these patterns in the codecov call (codecov --gcov-glob ...), the fact that
# our code references these areas also means we need to tell codecov itself to
# ignore them from the stats.
ignore:
- .*/tests/.*
- .*/benchmarks/.*
- .*/BoostParts/.*
- .*/pybind11/.*
- .*/whereami/.*
fixes:
- "utils.py::ycmd/utils.py"
comment:
layout: "header, diff, changes, uncovered"
behavior: default # update if exists else create new
ycmd-0+20191222+git2771f6f+ds/cpp/ 0000775 0000000 0000000 00000000000 13601473553 0015742 5 ustar 00root root 0000000 0000000 ycmd-0+20191222+git2771f6f+ds/cpp/CMakeLists.txt 0000664 0000000 0000000 00000023144 13601473553 0020506 0 ustar 00root root 0000000 0000000 # Copyright (C) 2011-2018 ycmd contributors
#
# This file is part of ycmd.
#
# ycmd is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ycmd is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with ycmd. If not, see .
if ( APPLE )
# OSX requires CMake >= 2.8.12, see YCM issue #1439
cmake_minimum_required( VERSION 2.8.12 )
else()
# CMake 2.8.11 is the latest available version on RHEL/CentOS 7
cmake_minimum_required( VERSION 2.8.11 )
endif()
project( YouCompleteMe )
# Get the core version
file( STRINGS "../CORE_VERSION" YCMD_CORE_VERSION )
add_definitions( -DYCMD_CORE_VERSION=${YCMD_CORE_VERSION} )
option( UNIVERSAL "Build universal mac binary" OFF )
if ( CMAKE_GENERATOR STREQUAL Xcode )
set( CMAKE_GENERATOR_IS_XCODE true )
endif()
if ( ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" )
set( SYSTEM_IS_FREEBSD true )
endif()
if ( ${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD" )
set( SYSTEM_IS_OPENBSD true )
endif()
if ( ${CMAKE_SYSTEM_NAME} MATCHES "SunOS" )
set( SYSTEM_IS_SUNOS true )
endif()
# Check if platform is 64 bit
if( CMAKE_SIZEOF_VOID_P EQUAL 4 )
set( 64_BIT_PLATFORM 0 )
else()
set( 64_BIT_PLATFORM 1 )
endif()
#############################################################################
# Linux distribution detection.
# Red Hat and CentOS detection.
if ( EXISTS /etc/redhat-release )
file( READ /etc/redhat-release CONTENTS )
if ( CONTENTS MATCHES "Red Hat" )
set( DISTRIBUTION "Red Hat" )
elseif ( CONTENTS MATCHES "CentOS" )
set( DISTRIBUTION "CentOS" )
endif()
# Gentoo detection. Needed because Gentoo is a special snowflake that puts
# llvm in weird places.
elseif ( EXISTS /etc/os-release )
file( READ /etc/os-release CONTENTS )
if ( CONTENTS MATCHES "Gentoo" )
set( DISTRIBUTION "Gentoo" )
endif()
endif()
#############################################################################
# Turning on this flag tells cmake to emit a compile_commands.json file.
# This file can be used to load compilation flags into YCM. See here for more
# details: http://clang.llvm.org/docs/JSONCompilationDatabase.html
set( CMAKE_EXPORT_COMPILE_COMMANDS 1 )
#############################################################################
# This is needed so that on macs, the library is built in both 32 bit and 64 bit
# versions. Without this python might refuse to load the module, depending on
# how python was built.
# On Mac, boost needs to be compiled universal as well, if used instead of the
# included BoostParts lib. For brew, that's
# "brew install boost --universal"
# If the user chose to use the system libclang.dylib (or the libclang.dylib
# binary downloaded from llvm.org) on a mac, then we don't specify universal
# binary building since the system libclang on macs is not universal (and thus
# linking would fail with universal).
if ( UNIVERSAL AND NOT USE_SYSTEM_LIBCLANG )
set( CMAKE_OSX_ARCHITECTURES "i386;x86_64" )
endif()
#############################################################################
# To shut up the warning about CMake policy CMP0042
set( CMAKE_MACOSX_RPATH ON )
#############################################################################
if ( CMAKE_CXX_COMPILER_ID STREQUAL "Clang" )
set( COMPILER_IS_CLANG true )
# Linux machines don't necessarily have libc++ installed alongside clang,
# but HAS_LIBCXX11 doesn't always trigger for machines that DO have libc++. We
# know that at least all the Mac OS versions we support that use Clang have
# libc++, so we're safe there. On FreeBSD 9 libc++ is an optional build
# toggle. On FreeBSD 10 it is the default.
if ( HAS_LIBCXX11 OR APPLE OR SYSTEM_IS_FREEBSD )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++" )
endif()
# Ninja will by default prevent Clang from outputting diagnostics in color, so
# we force color output
if ( CMAKE_GENERATOR STREQUAL "Ninja" )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics" )
endif()
endif()
#############################################################################
# MSVC has symbols hidden by default. On GCC and Clang we need to explicitly
# set the visibility to hidden to achieve the same result and then manually
# expose what we need. This results in smaller ycm_core dynamic library and thus
# a shorter loading time and higher performance.
if( CMAKE_COMPILER_IS_GNUCXX OR COMPILER_IS_CLANG )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden" )
endif()
if ( DEFINED ENV{YCM_BENCHMARK} OR DEFINED ENV{YCM_TESTRUN})
if ( MSVC )
add_definitions( -DYCM_EXPORT=__declspec\(\ dllexport\ \) )
elseif( CMAKE_COMPILER_IS_GNUCXX OR COMPILER_IS_CLANG )
add_definitions(
-DYCM_EXPORT=__attribute__\(\(visibility\(\"default\"\)\)\) )
else()
add_definitions( -DYCM_EXPORT= )
endif()
else()
add_definitions( -DYCM_EXPORT= )
endif()
#############################################################################
# MSVC has snprintf since version 14 which is newer than what we support.
# *NOT* defining HAVE_SNPRINTF will break compiling the bundled boost libraries.
if ( MSVC )
add_definitions( -DHAVE_SNPRINTF )
endif()
#############################################################################
# Force release build by default, speed is of the essence
if ( NOT CMAKE_BUILD_TYPE )
set( CMAKE_BUILD_TYPE Release )
endif()
#############################################################################
# Determining the presence of C++11 support in the compiler
set( CPP11_AVAILABLE false )
if ( CMAKE_COMPILER_IS_GNUCXX )
if ( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8 )
set( CPP11_AVAILABLE true )
endif()
elseif( COMPILER_IS_CLANG )
if ( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.3 )
set( CPP11_AVAILABLE true )
set( CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++11" )
set( CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++" )
endif()
elseif( MSVC )
if ( NOT MSVC_VERSION LESS 1900 )
set( CPP11_AVAILABLE true )
endif()
endif()
#############################################################################
# For MSVC enable UNICODE and compilation on multiple processors. Increase the
# number of sections that an object file can hold. This is needed for storing
# the Unicode table. Also, force MSVC to treat source files as UTF-8 encoded.
if ( MSVC )
add_definitions( /DUNICODE /D_UNICODE /Zc:wchar_t- )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /bigobj /utf-8" )
endif()
if( MSVC OR CYGWIN OR MSYS)
# BOOST_ALL_NO_LIB turns off MSVC library autolinking
add_definitions( -DBOOST_ALL_NO_LIB )
endif()
# Enables Python compilation for 64-bit Windows. Already defined by Python
# headers when compiling with MSVC.
if( WIN32 OR CYGWIN OR MSYS AND NOT MSVC AND 64_BIT_PLATFORM )
add_definitions( -DMS_WIN64 )
endif()
#############################################################################
if ( CPP11_AVAILABLE )
# Cygwin needs its hand held a bit; see issue #473
if ( CYGWIN AND CMAKE_COMPILER_IS_GNUCXX )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11" )
elseif( NOT MSVC )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11" )
endif()
else()
# Platform-specific advice goes here. In particular, we have plenty of users
# in corporate environments on RHEL/CentOS, where the default system compiler
# is too old.
# on RHEL and CentOS, users require the devtoolset-3 or greater. However, we
# recommended the devtoolset-6 because it is the newest at the time of
# writing. And why not.
if ( DISTRIBUTION STREQUAL "CentOS" OR DISTRIBUTION STREQUAL "Red Hat" )
message( STATUS "NOTE: You appear to be on ${DISTRIBUTION}. "
"In order to use this application, you require a more modern compiler "
"than the default compiler on this platform. "
"Please install the devtoolset-6 or greater. For example, see this link: "
"https://www.softwarecollections.org/en/scls/rhscl/devtoolset-6/" )
# Finally, just check if it is installed and they just need to activate it.
if ( EXISTS /opt/rh/devtoolset-6 )
message( STATUS "NOTE: It looks like you have the devtoolset-6 "
"installed in /opt/rh/devtoolset-6, so you probably "
"just need to activate it and re-run the installation. "
"For example: source /opt/rh/devtoolset-6/enable")
endif()
endif()
message( FATAL_ERROR "Your C++ compiler does NOT fully support C++11." )
endif()
# Note: build.py always explicitly sets this option, so the default used here
# rarely matters.
option( USE_PYTHON2 "If on, link to Python 2 instead of 3" ON )
if ( USE_PYTHON2 )
find_package( PythonLibs 2.7 REQUIRED )
if ( NOT PYTHONLIBS_VERSION_STRING VERSION_LESS "3.0.0" )
message( FATAL_ERROR
"You set USE_PYTHON2 to make CMake find python2 libs only, but CMake "
"found python3 libs instead. Either don't set USE_PYTHON2 or install the "
"necessary python2 headers & libraries." )
endif()
else()
set( Python_ADDITIONAL_VERSIONS 3.9 3.8 3.7 3.6 3.5 )
find_package( PythonLibs 3.5 REQUIRED ) # 3.5 is ONLY the mininum
endif()
#############################################################################
option( USE_SYSTEM_BOOST "Set to ON to use the system boost libraries" OFF )
if (NOT USE_SYSTEM_BOOST)
add_subdirectory( BoostParts )
endif()
add_subdirectory( ycm )
ycmd-0+20191222+git2771f6f+ds/cpp/whereami/ 0000775 0000000 0000000 00000000000 13601473553 0017543 5 ustar 00root root 0000000 0000000 ycmd-0+20191222+git2771f6f+ds/cpp/whereami/LICENSE.MIT 0000664 0000000 0000000 00000002031 13601473553 0021174 0 ustar 00root root 0000000 0000000 Copyright Gregory Pakosz
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
ycmd-0+20191222+git2771f6f+ds/cpp/whereami/whereami.c 0000664 0000000 0000000 00000032150 13601473553 0021511 0 ustar 00root root 0000000 0000000 // (‑●‑●)> dual licensed under the WTFPL v2 and MIT licenses
// without any warranty.
// by Gregory Pakosz (@gpakosz)
// https://github.com/gpakosz/whereami
// in case you want to #include "whereami.c" in a larger compilation unit
#if !defined(WHEREAMI_H)
#include
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if !defined(WAI_MALLOC) || !defined(WAI_FREE) || !defined(WAI_REALLOC)
#include
#endif
#if !defined(WAI_MALLOC)
#define WAI_MALLOC(size) malloc(size)
#endif
#if !defined(WAI_FREE)
#define WAI_FREE(p) free(p)
#endif
#if !defined(WAI_REALLOC)
#define WAI_REALLOC(p, size) realloc(p, size)
#endif
#ifndef WAI_NOINLINE
#if defined(_MSC_VER)
#define WAI_NOINLINE __declspec(noinline)
#elif defined(__GNUC__)
#define WAI_NOINLINE __attribute__((noinline))
#else
#error unsupported compiler
#endif
#endif
#if defined(_MSC_VER)
#define WAI_RETURN_ADDRESS() _ReturnAddress()
#elif defined(__GNUC__)
#define WAI_RETURN_ADDRESS() __builtin_extract_return_addr(__builtin_return_address(0))
#else
#error unsupported compiler
#endif
#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#if defined(_MSC_VER)
#pragma warning(push, 3)
#endif
#include
#include
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
static int WAI_PREFIX(getModulePath_)(HMODULE module, char* out, int capacity, int* dirname_length)
{
wchar_t buffer1[MAX_PATH];
wchar_t buffer2[MAX_PATH];
wchar_t* path = NULL;
int length = -1;
for (;;)
{
DWORD size;
int length_, length__;
size = GetModuleFileNameW(module, buffer1, sizeof(buffer1) / sizeof(buffer1[0]));
if (size == 0)
break;
else if (size == (DWORD)(sizeof(buffer1) / sizeof(buffer1[0])))
{
DWORD size_ = size;
do
{
wchar_t* path_;
path_ = (wchar_t*)WAI_REALLOC(path, sizeof(wchar_t) * size_ * 2);
if (!path_)
break;
size_ *= 2;
path = path_;
size = GetModuleFileNameW(module, path, size_);
}
while (size == size_);
if (size == size_)
break;
}
else
path = buffer1;
if (!_wfullpath(buffer2, path, MAX_PATH))
break;
length_ = (int)wcslen(buffer2);
length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_ , out, capacity, NULL, NULL);
if (length__ == 0)
length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_, NULL, 0, NULL, NULL);
if (length__ == 0)
break;
if (length__ <= capacity && dirname_length)
{
int i;
for (i = length__ - 1; i >= 0; --i)
{
if (out[i] == '\\')
{
*dirname_length = i;
break;
}
}
}
length = length__;
break;
}
if (path != buffer1)
WAI_FREE(path);
return length;
}
WAI_NOINLINE WAI_FUNCSPEC
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
{
return WAI_PREFIX(getModulePath_)(NULL, out, capacity, dirname_length);
}
WAI_NOINLINE WAI_FUNCSPEC
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
{
HMODULE module;
int length = -1;
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable: 4054)
#endif
if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)WAI_RETURN_ADDRESS(), &module))
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
{
length = WAI_PREFIX(getModulePath_)(module, out, capacity, dirname_length);
}
return length;
}
#elif defined(__linux__) || defined(__CYGWIN__) || defined(__sun) || defined(WAI_USE_PROC_SELF_EXE)
#include
#include
#include
#if defined(__linux__)
#include
#else
#include
#endif
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
#include
#if !defined(WAI_PROC_SELF_EXE)
#if defined(__sun)
#define WAI_PROC_SELF_EXE "/proc/self/path/a.out"
#else
#define WAI_PROC_SELF_EXE "/proc/self/exe"
#endif
#endif
WAI_FUNCSPEC
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
{
char buffer[PATH_MAX];
char* resolved = NULL;
int length = -1;
for (;;)
{
resolved = realpath(WAI_PROC_SELF_EXE, buffer);
if (!resolved)
break;
length = (int)strlen(resolved);
if (length <= capacity)
{
memcpy(out, resolved, length);
if (dirname_length)
{
int i;
for (i = length - 1; i >= 0; --i)
{
if (out[i] == '/')
{
*dirname_length = i;
break;
}
}
}
}
break;
}
return length;
}
#if !defined(WAI_PROC_SELF_MAPS_RETRY)
#define WAI_PROC_SELF_MAPS_RETRY 5
#endif
#if !defined(WAI_PROC_SELF_MAPS)
#if defined(__sun)
#define WAI_PROC_SELF_MAPS "/proc/self/map"
#else
#define WAI_PROC_SELF_MAPS "/proc/self/maps"
#endif
#endif
#if defined(__ANDROID__) || defined(ANDROID)
#include
#include
#include
#endif
WAI_NOINLINE WAI_FUNCSPEC
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
{
int length = -1;
FILE* maps = NULL;
for (int r = 0; r < WAI_PROC_SELF_MAPS_RETRY; ++r)
{
maps = fopen(WAI_PROC_SELF_MAPS, "r");
if (!maps)
break;
for (;;)
{
char buffer[PATH_MAX < 1024 ? 1024 : PATH_MAX];
uint64_t low, high;
char perms[5];
uint64_t offset;
uint32_t major, minor;
char path[PATH_MAX];
uint32_t inode;
if (!fgets(buffer, sizeof(buffer), maps))
break;
if (sscanf(buffer, "%" PRIx64 "-%" PRIx64 " %s %" PRIx64 " %x:%x %u %s\n", &low, &high, perms, &offset, &major, &minor, &inode, path) == 8)
{
uint64_t addr = (uintptr_t)WAI_RETURN_ADDRESS();
if (low <= addr && addr <= high)
{
char* resolved;
resolved = realpath(path, buffer);
if (!resolved)
break;
length = (int)strlen(resolved);
#if defined(__ANDROID__) || defined(ANDROID)
if (length > 4
&&buffer[length - 1] == 'k'
&&buffer[length - 2] == 'p'
&&buffer[length - 3] == 'a'
&&buffer[length - 4] == '.')
{
int fd = open(path, O_RDONLY);
char* begin;
char* p;
begin = (char*)mmap(0, offset, PROT_READ, MAP_SHARED, fd, 0);
p = begin + offset;
while (p >= begin) // scan backwards
{
if (*((uint32_t*)p) == 0x04034b50UL) // local file header found
{
uint16_t length_ = *((uint16_t*)(p + 26));
if (length + 2 + length_ < (int)sizeof(buffer))
{
memcpy(&buffer[length], "!/", 2);
memcpy(&buffer[length + 2], p + 30, length_);
length += 2 + length_;
}
break;
}
p -= 4;
}
munmap(begin, offset);
close(fd);
}
#endif
if (length <= capacity)
{
memcpy(out, resolved, length);
if (dirname_length)
{
int i;
for (i = length - 1; i >= 0; --i)
{
if (out[i] == '/')
{
*dirname_length = i;
break;
}
}
}
}
break;
}
}
}
fclose(maps);
maps = NULL;
if (length != -1)
break;
}
if (maps)
fclose(maps);
return length;
}
#elif defined(__APPLE__)
#define _DARWIN_BETTER_REALPATH
#include
#include
#include
#include
#include
WAI_FUNCSPEC
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
{
char buffer1[PATH_MAX];
char buffer2[PATH_MAX];
char* path = buffer1;
char* resolved = NULL;
int length = -1;
for (;;)
{
uint32_t size = (uint32_t)sizeof(buffer1);
if (_NSGetExecutablePath(path, &size) == -1)
{
path = (char*)WAI_MALLOC(size);
if (!_NSGetExecutablePath(path, &size))
break;
}
resolved = realpath(path, buffer2);
if (!resolved)
break;
length = (int)strlen(resolved);
if (length <= capacity)
{
memcpy(out, resolved, length);
if (dirname_length)
{
int i;
for (i = length - 1; i >= 0; --i)
{
if (out[i] == '/')
{
*dirname_length = i;
break;
}
}
}
}
break;
}
if (path != buffer1)
WAI_FREE(path);
return length;
}
WAI_NOINLINE WAI_FUNCSPEC
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
{
char buffer[PATH_MAX];
char* resolved = NULL;
int length = -1;
for(;;)
{
Dl_info info;
if (dladdr(WAI_RETURN_ADDRESS(), &info))
{
resolved = realpath(info.dli_fname, buffer);
if (!resolved)
break;
length = (int)strlen(resolved);
if (length <= capacity)
{
memcpy(out, resolved, length);
if (dirname_length)
{
int i;
for (i = length - 1; i >= 0; --i)
{
if (out[i] == '/')
{
*dirname_length = i;
break;
}
}
}
}
}
break;
}
return length;
}
#elif defined(__QNXNTO__)
#include
#include
#include
#include
#include
#if !defined(WAI_PROC_SELF_EXE)
#define WAI_PROC_SELF_EXE "/proc/self/exefile"
#endif
WAI_FUNCSPEC
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
{
char buffer1[PATH_MAX];
char buffer2[PATH_MAX];
char* resolved = NULL;
FILE* self_exe = NULL;
int length = -1;
for (;;)
{
self_exe = fopen(WAI_PROC_SELF_EXE, "r");
if (!self_exe)
break;
if (!fgets(buffer1, sizeof(buffer1), self_exe))
break;
resolved = realpath(buffer1, buffer2);
if (!resolved)
break;
length = (int)strlen(resolved);
if (length <= capacity)
{
memcpy(out, resolved, length);
if (dirname_length)
{
int i;
for (i = length - 1; i >= 0; --i)
{
if (out[i] == '/')
{
*dirname_length = i;
break;
}
}
}
}
break;
}
fclose(self_exe);
return length;
}
WAI_FUNCSPEC
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
{
char buffer[PATH_MAX];
char* resolved = NULL;
int length = -1;
for(;;)
{
Dl_info info;
if (dladdr(WAI_RETURN_ADDRESS(), &info))
{
resolved = realpath(info.dli_fname, buffer);
if (!resolved)
break;
length = (int)strlen(resolved);
if (length <= capacity)
{
memcpy(out, resolved, length);
if (dirname_length)
{
int i;
for (i = length - 1; i >= 0; --i)
{
if (out[i] == '/')
{
*dirname_length = i;
break;
}
}
}
}
}
break;
}
return length;
}
#elif defined(__DragonFly__) || defined(__FreeBSD__) || \
defined(__FreeBSD_kernel__) || defined(__NetBSD__)
#include
#include
#include
#include
#include
#include
WAI_FUNCSPEC
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
{
char buffer1[PATH_MAX];
char buffer2[PATH_MAX];
char* path = buffer1;
char* resolved = NULL;
int length = -1;
for (;;)
{
#if defined(__NetBSD__)
int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME };
#else
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
#endif
size_t size = sizeof(buffer1);
if (sysctl(mib, (u_int)(sizeof(mib) / sizeof(mib[0])), path, &size, NULL, 0) != 0)
break;
resolved = realpath(path, buffer2);
if (!resolved)
break;
length = (int)strlen(resolved);
if (length <= capacity)
{
memcpy(out, resolved, length);
if (dirname_length)
{
int i;
for (i = length - 1; i >= 0; --i)
{
if (out[i] == '/')
{
*dirname_length = i;
break;
}
}
}
}
break;
}
if (path != buffer1)
WAI_FREE(path);
return length;
}
WAI_NOINLINE WAI_FUNCSPEC
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
{
char buffer[PATH_MAX];
char* resolved = NULL;
int length = -1;
for(;;)
{
Dl_info info;
if (dladdr(WAI_RETURN_ADDRESS(), &info))
{
resolved = realpath(info.dli_fname, buffer);
if (!resolved)
break;
length = (int)strlen(resolved);
if (length <= capacity)
{
memcpy(out, resolved, length);
if (dirname_length)
{
int i;
for (i = length - 1; i >= 0; --i)
{
if (out[i] == '/')
{
*dirname_length = i;
break;
}
}
}
}
}
break;
}
return length;
}
#else
#error unsupported platform
#endif
#ifdef __cplusplus
}
#endif
ycmd-0+20191222+git2771f6f+ds/cpp/whereami/whereami.h 0000664 0000000 0000000 00000003741 13601473553 0021522 0 ustar 00root root 0000000 0000000 // (‑●‑●)> dual licensed under the WTFPL v2 and MIT licenses
// without any warranty.
// by Gregory Pakosz (@gpakosz)
// https://github.com/gpakosz/whereami
#ifndef WHEREAMI_H
#define WHEREAMI_H
#ifdef __cplusplus
extern "C" {
#endif
#ifndef WAI_FUNCSPEC
#define WAI_FUNCSPEC
#endif
#ifndef WAI_PREFIX
#define WAI_PREFIX(function) wai_##function
#endif
/**
* Returns the path to the current executable.
*
* Usage:
* - first call `int length = wai_getExecutablePath(NULL, 0, NULL);` to
* retrieve the length of the path
* - allocate the destination buffer with `path = (char*)malloc(length + 1);`
* - call `wai_getExecutablePath(path, length, NULL)` again to retrieve the
* path
* - add a terminal NUL character with `path[length] = '\0';`
*
* @param out destination buffer, optional
* @param capacity destination buffer capacity
* @param dirname_length optional recipient for the length of the dirname part
* of the path.
*
* @return the length of the executable path on success (without a terminal NUL
* character), otherwise `-1`
*/
WAI_FUNCSPEC
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length);
/**
* Returns the path to the current module
*
* Usage:
* - first call `int length = wai_getModulePath(NULL, 0, NULL);` to retrieve
* the length of the path
* - allocate the destination buffer with `path = (char*)malloc(length + 1);`
* - call `wai_getModulePath(path, length, NULL)` again to retrieve the path
* - add a terminal NUL character with `path[length] = '\0';`
*
* @param out destination buffer, optional
* @param capacity destination buffer capacity
* @param dirname_length optional recipient for the length of the dirname part
* of the path.
*
* @return the length of the module path on success (without a terminal NUL
* character), otherwise `-1`
*/
WAI_FUNCSPEC
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length);
#ifdef __cplusplus
}
#endif
#endif // #ifndef WHEREAMI_H
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/ 0000775 0000000 0000000 00000000000 13601473553 0016532 5 ustar 00root root 0000000 0000000 ycmd-0+20191222+git2771f6f+ds/cpp/ycm/CMakeLists.txt 0000664 0000000 0000000 00000041446 13601473553 0021303 0 ustar 00root root 0000000 0000000 # Copyright (C) 2011-2019 ycmd contributors
#
# This file is part of YouCompleteMe.
#
# YouCompleteMe is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# YouCompleteMe is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with YouCompleteMe. If not, see .
cmake_minimum_required( VERSION 2.8.7 )
project( ycm_core )
option( USE_DEV_FLAGS "Use compilation flags meant for YCM developers" OFF )
option( USE_CLANG_COMPLETER "Use Clang semantic completer for C/C++/ObjC" OFF )
option( USE_SYSTEM_LIBCLANG "Set to ON to use the system libclang library" OFF )
set( PATH_TO_LLVM_ROOT "" CACHE PATH "Path to the root of a LLVM+Clang binary distribution" )
set( EXTERNAL_LIBCLANG_PATH "" CACHE PATH "Path to the libclang library to use" )
if ( USE_CLANG_COMPLETER AND
NOT USE_SYSTEM_LIBCLANG AND
NOT PATH_TO_LLVM_ROOT AND
NOT EXTERNAL_LIBCLANG_PATH )
set( CLANG_VERSION 9.0.0 )
if ( APPLE )
set( LIBCLANG_DIRNAME "libclang-${CLANG_VERSION}-x86_64-apple-darwin" )
set( LIBCLANG_SHA256
"7474ab23181bf0e6c36d34e7b36931012af7080f7fa56db13f09c138f27e3b7f" )
elseif ( WIN32 )
if( 64_BIT_PLATFORM )
set( LIBCLANG_DIRNAME "libclang-${CLANG_VERSION}-win64" )
set( LIBCLANG_SHA256
"4fe19f03b30ff2cb06a04c2d4fae667033f227a59f0104e055bee9885eca7b90" )
else()
set( LIBCLANG_DIRNAME "libclang-${CLANG_VERSION}-win32" )
set( LIBCLANG_SHA256
"4c3ca2dc69b798fbf76ce2f72b3fd5856d39721a3a62602155705ab465a59f18" )
endif()
elseif ( SYSTEM_IS_FREEBSD )
if ( 64_BIT_PLATFORM )
set( LIBCLANG_DIRNAME
"libclang-${CLANG_VERSION}-amd64-unknown-freebsd11" )
set( LIBCLANG_SHA256
"db00e7e87cd5332be7de21a39a4e96933086c5415175263cfa903f8e60a8e7f1" )
else()
set( LIBCLANG_DIRNAME
"libclang-${CLANG_VERSION}-i386-unknown-freebsd11" )
set( LIBCLANG_SHA256
"06b3e542b8c55ba69b8aec595ba5bbd91e4ba2e56a2ace24a8cf9dc37668e2ab" )
endif()
elseif ( CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64.*|AARCH64.*)" )
set( LIBCLANG_DIRNAME "libclang-${CLANG_VERSION}-aarch64-linux-gnu" )
set( LIBCLANG_SHA256
"4c49c7f0b117091fbc9fe8d09533ddf2f3d71b8933ae3264a3a50116d2ddc03e" )
elseif ( CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm.*|ARM.*)" )
set( LIBCLANG_DIRNAME "libclang-${CLANG_VERSION}-armv7a-linux-gnueabihf" )
set( LIBCLANG_SHA256
"17af77a241f61465c1ee7946e105f3d73d427761a3aad945f99c5b3fdf586bed" )
elseif ( CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64)" )
set( LIBCLANG_DIRNAME
"libclang-${CLANG_VERSION}-x86_64-unknown-linux-gnu" )
set( LIBCLANG_SHA256
"4b1986be100f0067848d3c15c2a66cc3bb91c7e648ccf89ddd5e77a208b72d26" )
else()
message( FATAL_ERROR
"No prebuilt Clang ${CLANG_VERSION} binaries for this system. "
"You'll have to compile Clang ${CLANG_VERSION} from source "
"or use your system libclang. "
"See the YCM docs for details on how to use a user-compiled libclang." )
endif()
set( LIBCLANG_FILENAME "${LIBCLANG_DIRNAME}.tar.bz2" )
set( LIBCLANG_DOWNLOAD ON )
set( LIBCLANG_URL
"https://dl.bintray.com/ycm-core/libclang/${LIBCLANG_FILENAME}" )
# Check if the Clang archive is already downloaded and its checksum is
# correct. If this is not the case, remove it if needed and download it.
set( LIBCLANG_LOCAL_FILE
"${CMAKE_SOURCE_DIR}/../clang_archives/${LIBCLANG_FILENAME}" )
if( EXISTS "${LIBCLANG_LOCAL_FILE}" )
file( SHA256 "${LIBCLANG_LOCAL_FILE}" LIBCLANG_LOCAL_SHA256 )
if( "${LIBCLANG_LOCAL_SHA256}" STREQUAL "${LIBCLANG_SHA256}" )
set( LIBCLANG_DOWNLOAD OFF )
else()
file( REMOVE "${LIBCLANG_LOCAL_FILE}" )
endif()
endif()
if( LIBCLANG_DOWNLOAD )
message( STATUS
"Downloading libclang ${CLANG_VERSION} from ${LIBCLANG_URL}" )
file(
DOWNLOAD "${LIBCLANG_URL}" "${LIBCLANG_LOCAL_FILE}"
SHOW_PROGRESS EXPECTED_HASH SHA256=${LIBCLANG_SHA256}
)
else()
message( STATUS "Using libclang archive: ${LIBCLANG_LOCAL_FILE}" )
endif()
# Copy and extract the Clang archive in the building directory.
file( COPY "${LIBCLANG_LOCAL_FILE}" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/../" )
execute_process( COMMAND ${CMAKE_COMMAND} -E tar -xjf ${LIBCLANG_FILENAME} )
# We determine PATH_TO_LLVM_ROOT by searching the libclang library path in
# CMake build folder.
file( GLOB_RECURSE PATH_TO_LIBCLANG ${CMAKE_BINARY_DIR}/libclang.* )
if ( NOT PATH_TO_LIBCLANG )
message( FATAL_ERROR "Cannot find path to libclang in prebuilt binaries" )
endif()
# file( GLOB_RECURSE ... ) returns a list of files. Take the first one.
list( GET PATH_TO_LIBCLANG 0 PATH_TO_LIBCLANG )
# We know that LLVM root is parent to the directory containing libclang so we
# need to go up two directories:
#
# /path/to/llvm/root/lib/libclang.so/../.. = /path/to/llvm/root/
#
get_filename_component( PATH_TO_LLVM_ROOT "${PATH_TO_LIBCLANG}/../.."
ABSOLUTE )
endif()
if ( PATH_TO_LLVM_ROOT OR USE_SYSTEM_LIBCLANG OR EXTERNAL_LIBCLANG_PATH )
set( USE_CLANG_COMPLETER TRUE )
endif()
if ( USE_CLANG_COMPLETER AND
NOT PATH_TO_LLVM_ROOT AND
NOT USE_SYSTEM_LIBCLANG AND
NOT EXTERNAL_LIBCLANG_PATH )
message( FATAL_ERROR
"You have not specified which libclang to use. You have several options:\n"
" 1. Set PATH_TO_LLVM_ROOT to a path to the root of a LLVM+Clang binary "
"distribution. You can download such a binary distro from llvm.org. This "
"is the recommended approach.\n"
" 2. Set USE_SYSTEM_LIBCLANG to ON; this makes YCM search for the system "
"version of libclang.\n"
" 3. Set EXTERNAL_LIBCLANG_PATH to a path to whatever "
"libclang.[so|dylib|dll] you wish to use.\n"
"You HAVE to pick one option. See the docs for more information.")
endif()
if ( USE_CLANG_COMPLETER )
message( STATUS "Using libclang to provide semantic completion for C/C++/ObjC" )
else()
message( STATUS "NOT using libclang, no semantic completion for "
"C/C++/ObjC will be available" )
endif()
if ( NOT LIBCLANG_FILENAME AND PATH_TO_LLVM_ROOT )
set( CLANG_INCLUDES_DIR "${PATH_TO_LLVM_ROOT}/include" )
else()
set( CLANG_INCLUDES_DIR "${CMAKE_SOURCE_DIR}/llvm/include" )
endif()
if ( NOT IS_ABSOLUTE "${CLANG_INCLUDES_DIR}" )
get_filename_component(CLANG_INCLUDES_DIR
"${CMAKE_BINARY_DIR}/${CLANG_INCLUDES_DIR}" ABSOLUTE)
endif()
if ( NOT EXTERNAL_LIBCLANG_PATH AND PATH_TO_LLVM_ROOT )
if ( MINGW )
set( LIBCLANG_SEARCH_PATH "${PATH_TO_LLVM_ROOT}/bin" )
else()
set( LIBCLANG_SEARCH_PATH "${PATH_TO_LLVM_ROOT}/lib" )
endif()
# Need TEMP because find_library does not work with an option variable
find_library( TEMP NAMES clang libclang
PATHS ${LIBCLANG_SEARCH_PATH}
NO_DEFAULT_PATH )
set( EXTERNAL_LIBCLANG_PATH ${TEMP} )
endif()
# This is a workaround for a CMake bug with include_directories(SYSTEM ...)
# on Mac OS X. Bug report: http://public.kitware.com/Bug/view.php?id=10837
if ( APPLE )
set( CMAKE_INCLUDE_SYSTEM_FLAG_CXX "-isystem " )
endif()
if ( USE_SYSTEM_BOOST )
set( Boost_COMPONENTS filesystem regex )
find_package( Boost REQUIRED COMPONENTS ${Boost_COMPONENTS} )
else()
set( Boost_INCLUDE_DIR ${BoostParts_SOURCE_DIR} )
set( Boost_LIBRARIES BoostParts )
endif()
set( PYBIND11_INCLUDES_DIR "${CMAKE_SOURCE_DIR}/pybind11" )
file( GLOB_RECURSE SERVER_SOURCES *.h *.cpp )
# The test and benchmark sources are a part of a different target, so we remove
# them. The CMakeFiles cpp file is picked up when the user creates an in-source
# build, and we don't want that. We also remove client-specific code.
file( GLOB_RECURSE to_remove tests/*.h tests/*.cpp benchmarks/*.h
benchmarks/*.cpp CMakeFiles/*.cpp *client* )
if( to_remove )
list( REMOVE_ITEM SERVER_SOURCES ${to_remove} )
endif()
if ( USE_CLANG_COMPLETER )
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
"${CMAKE_CURRENT_SOURCE_DIR}/ClangCompleter" )
add_definitions( -DUSE_CLANG_COMPLETER )
else()
file( GLOB_RECURSE to_remove_clang ClangCompleter/*.h ClangCompleter/*.cpp )
if( to_remove_clang )
list( REMOVE_ITEM SERVER_SOURCES ${to_remove_clang} )
endif()
endif()
# The SYSTEM flag makes sure that -isystem[header path] is passed to the
# compiler instead of the standard -I[header path]. Headers included with
# -isystem do not generate warnings (and they shouldn't; e.g. boost warnings are
# just noise for us since we won't be changing them).
# Since there is no -isystem flag equivalent on Windows, headers from external
# projects may conflict with our headers and override them. We prevent that by
# including these directories after ours.
include_directories(
SYSTEM
${Boost_INCLUDE_DIR}
${PYBIND11_INCLUDES_DIR}
${PYTHON_INCLUDE_DIRS}
${CLANG_INCLUDES_DIR}
)
#############################################################################
# One can use the system libclang.[so|dylib] like so:
# cmake -DUSE_SYSTEM_LIBCLANG=1 [...]
# One can also explicitly pick the external libclang.[so|dylib] for use like so:
# cmake -DEXTERNAL_LIBCLANG_PATH=/path/to/libclang.so [...]
# The final .so we build will then first look in the same dir in which it is
# located for libclang.so. This is provided by the rpath = $ORIGIN feature.
if ( EXTERNAL_LIBCLANG_PATH OR USE_SYSTEM_LIBCLANG )
if ( USE_SYSTEM_LIBCLANG )
if ( APPLE )
set( ENV_LIB_PATHS ENV DYLD_LIBRARY_PATH )
elseif ( UNIX )
set( ENV_LIB_PATHS ENV LD_LIBRARY_PATH )
elseif ( WIN32 )
set( ENV_LIB_PATHS ENV PATH )
else ()
set( ENV_LIB_PATHS "" )
endif()
find_program( LLVM_CONFIG_EXECUTABLE NAMES llvm-config )
if ( LLVM_CONFIG_EXECUTABLE )
execute_process( COMMAND ${LLVM_CONFIG_EXECUTABLE} --libdir
OUTPUT_VARIABLE LLVM_CONFIG_PATH
OUTPUT_STRIP_TRAILING_WHITESPACE )
endif()
# On Debian-based systems, llvm installs into /usr/lib/llvm-x.y.
file( GLOB SYS_LLVM_PATHS "/usr/lib/llvm*/lib" )
# On FreeBSD , llvm install into /usr/local/llvm-xy
file ( GLOB FREEBSD_LLVM_PATHS "/usr/local/llvm*/lib")
# Need TEMP because find_library does not work with an option variable
# On Debian-based systems only a symlink to libclang.so.1 is created
find_library( TEMP
NAMES
clang
libclang.so.1
PATHS
${ENV_LIB_PATHS}
${LLVM_CONFIG_PATH}
/usr/lib
/usr/lib/llvm
${SYS_LLVM_PATHS}
${FREEBSD_LLVM_PATHS}
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib
/Library/Developer/CommandLineTools/usr/lib )
set( EXTERNAL_LIBCLANG_PATH ${TEMP} )
else()
if ( NOT APPLE AND NOT MSVC )
# Setting this to true makes sure that libraries we build will have our
# rpath set even without having to do "make install"
set( CMAKE_BUILD_WITH_INSTALL_RPATH TRUE )
set( CMAKE_INSTALL_RPATH "\$ORIGIN" )
# Add directories from all libraries outside the build tree to the rpath.
# This makes the dynamic linker able to find non system libraries that
# our libraries require, in particular the Python one (from pyenv for
# instance).
set( CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE )
endif()
endif()
# On Linux, the library target is a symlink of the soversion one. Since it
# will be copied in the project folder, we need the symlinked library.
get_filename_component( LIBCLANG_TARGET "${EXTERNAL_LIBCLANG_PATH}" REALPATH )
message( STATUS "Using external libclang: ${LIBCLANG_TARGET}" )
else()
set( LIBCLANG_TARGET )
endif()
if ( EXTRA_RPATH )
set( CMAKE_INSTALL_RPATH "${EXTRA_RPATH}:${CMAKE_INSTALL_RPATH}" )
endif()
# Needed on Linux machines, but not on Macs and OpenBSD
if ( UNIX AND NOT ( APPLE OR SYSTEM_IS_OPENBSD OR HAIKU ) )
set( EXTRA_LIBS rt )
endif()
#############################################################################
add_library( ${PROJECT_NAME} SHARED
${SERVER_SOURCES}
)
if ( USE_CLANG_COMPLETER AND NOT LIBCLANG_TARGET )
message( FATAL_ERROR "Using Clang completer, but no libclang found. "
"Try setting EXTERNAL_LIBCLANG_PATH or revise "
"your configuration" )
endif()
target_link_libraries( ${PROJECT_NAME}
${Boost_LIBRARIES}
${PYTHON_LIBRARIES}
${LIBCLANG_TARGET}
${EXTRA_LIBS}
)
if( LIBCLANG_TARGET )
set( LIBCLANG_DIR "${CMAKE_SOURCE_DIR}/../third_party/clang/lib" )
# Remove all previous libclang libraries.
file( GLOB LIBCLANG_FILEPATHS "${LIBCLANG_DIR}/libclang*" )
foreach( FILEPATH ${LIBCLANG_FILEPATHS} )
file( REMOVE ${FILEPATH} )
endforeach()
# When building with MSVC, we need to copy libclang.dll instead of libclang.lib
if( MSVC )
add_custom_command(
TARGET ${PROJECT_NAME}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "${PATH_TO_LLVM_ROOT}/bin/libclang.dll" "${LIBCLANG_DIR}"
)
else()
add_custom_command(
TARGET ${PROJECT_NAME}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "${LIBCLANG_TARGET}" "${LIBCLANG_DIR}"
)
endif()
endif()
#############################################################################
# We don't want the "lib" prefix, it can screw up python when it tries to search
# for our module
set_target_properties( ${PROJECT_NAME} PROPERTIES PREFIX "")
if ( WIN32 OR CYGWIN OR MSYS )
# DLL platforms put dlls in the RUNTIME_OUTPUT_DIRECTORY
# First for the generic no-config case (e.g. with mingw)
set_target_properties( ${PROJECT_NAME} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../.. )
# Second, for multi-config builds (e.g. msvc)
foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} )
string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG )
set_target_properties( ${PROJECT_NAME} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${PROJECT_SOURCE_DIR}/../.. )
endforeach()
if ( WIN32 )
# This is the extension for compiled Python modules on Windows
set_target_properties( ${PROJECT_NAME} PROPERTIES SUFFIX ".pyd")
elseif ( CYGWIN OR MSYS )
# This is the extension for compiled Python modules in Cygwin and msys
set_target_properties( ${PROJECT_NAME} PROPERTIES SUFFIX ".dll")
endif()
else()
# Even on macs, we want a .so extension instead of a .dylib which is what
# cmake would give us by default. Python won't recognize a .dylib as a module,
# but it will recognize a .so
set_target_properties( ${PROJECT_NAME} PROPERTIES SUFFIX ".so")
endif()
set_target_properties( ${PROJECT_NAME} PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../.. )
#############################################################################
# For some reason, Xcode is too dumb to understand the -isystem flag and thus
# borks on warnings in Boost.
if ( USE_DEV_FLAGS AND ( CMAKE_COMPILER_IS_GNUCXX OR COMPILER_IS_CLANG ) AND
NOT CMAKE_GENERATOR_IS_XCODE )
# We want all warnings, and warnings should be treated as errors
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror" )
endif()
#############################################################################
if( SYSTEM_IS_SUNOS )
# SunOS needs this setting for thread support
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthreads" )
endif()
if( SYSTEM_IS_OPENBSD OR SYSTEM_IS_FREEBSD )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread" )
endif()
if ( DEFINED ENV{YCM_TESTRUN} )
add_subdirectory( tests )
endif()
if ( DEFINED ENV{YCM_BENCHMARK} )
add_subdirectory( benchmarks )
endif()
###############################################################################
if( USE_CLANG_TIDY )
if( NOT APPLE )
find_program( CLANG_TIDY NAMES clang-tidy )
else()
execute_process( COMMAND brew --prefix llvm OUTPUT_VARIABLE LLVM_ROOT )
string( STRIP ${LLVM_ROOT} LLVM_ROOT )
message( STATUS "${LLVM_ROOT}/bin" )
find_program( CLANG_TIDY NAMES clang-tidy PATHS "${LLVM_ROOT}/bin" )
endif()
if ( CLANG_TIDY )
message( STATUS "clang-tidy executable found: ${CLANG_TIDY}" )
set( CLANG_TIDY_ARGS "${CLANG_TIDY}" )
set_target_properties( ycm_core PROPERTIES
CXX_CLANG_TIDY "${CLANG_TIDY_ARGS}" )
else()
message( STATUS "clang-tidy not found" )
endif()
else()
message( STATUS "NOT using clang-tidy for static analysis." )
endif()
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/Candidate.cpp 0000664 0000000 0000000 00000010250 13601473553 0021110 0 ustar 00root root 0000000 0000000 // Copyright (C) 2011-2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#include "Candidate.h"
#include "Result.h"
namespace YouCompleteMe {
void Candidate::ComputeCaseSwappedText() {
for ( const auto &character : Characters() ) {
case_swapped_text_.append( character->SwappedCase() );
}
}
void Candidate::ComputeWordBoundaryChars() {
const CharacterSequence &characters = Characters();
auto character_pos = characters.begin();
if ( character_pos == characters.end() ) {
return;
}
const auto &first_character = *character_pos;
if ( !first_character->IsPunctuation() ) {
word_boundary_chars_.push_back( first_character );
}
auto previous_character_pos = characters.begin();
++character_pos;
for ( ; character_pos != characters.end(); ++previous_character_pos,
++character_pos ) {
const auto &previous_character = *previous_character_pos;
const auto &character = *character_pos;
if ( ( !previous_character->IsUppercase() && character->IsUppercase() ) ||
( previous_character->IsPunctuation() && character->IsLetter() ) ) {
word_boundary_chars_.push_back( character );
}
}
}
void Candidate::ComputeTextIsLowercase() {
for ( const auto &character : Characters() ) {
if ( character->IsUppercase() ) {
text_is_lowercase_ = false;
return;
}
}
text_is_lowercase_ = true;
}
Candidate::Candidate( std::string&& text )
: Word( std::move( text ) ) {
ComputeCaseSwappedText();
ComputeWordBoundaryChars();
ComputeTextIsLowercase();
}
Result Candidate::QueryMatchResult( const Word &query ) const {
// Check if the query is a subsequence of the candidate and return a result
// accordingly. This is done by simultaneously going through the characters of
// the query and the candidate. If both characters match, we move to the next
// character in the query and the candidate. Otherwise, we only move to the
// next character in the candidate. The matching is a combination of smart
// base matching and smart case matching. If there is no character left in the
// query, the query is not a subsequence and we return an empty result. If
// there is no character left in the candidate, the query is a subsequence and
// we return a result with the query, the candidate, the sum of indexes of the
// candidate where characters matched, and a boolean that is true if the query
// is a prefix of the candidate.
if ( query.IsEmpty() ) {
return Result( this, &query, 0, false );
}
if ( Length() < query.Length() ) {
return Result();
}
size_t query_index = 0;
size_t candidate_index = 0;
size_t index_sum = 0;
const CharacterSequence &query_characters = query.Characters();
const CharacterSequence &candidate_characters = Characters();
auto query_character_pos = query_characters.begin();
auto candidate_character_pos = candidate_characters.begin();
for ( ; candidate_character_pos != candidate_characters.end();
++candidate_character_pos, ++candidate_index ) {
const auto &candidate_character = *candidate_character_pos;
const auto &query_character = *query_character_pos;
if ( query_character->MatchesSmart( *candidate_character ) ) {
index_sum += candidate_index;
++query_character_pos;
if ( query_character_pos == query_characters.end() ) {
return Result( this,
&query,
index_sum,
candidate_index == query_index );
}
++query_index;
}
}
return Result();
}
} // namespace YouCompleteMe
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/Candidate.h 0000664 0000000 0000000 00000003447 13601473553 0020567 0 ustar 00root root 0000000 0000000 // Copyright (C) 2011-2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#ifndef CANDIDATE_H_R5LZH6AC
#define CANDIDATE_H_R5LZH6AC
#include "Word.h"
#include
#include
namespace YouCompleteMe {
class Result;
class Candidate : public Word {
public:
YCM_EXPORT explicit Candidate( std::string&& text );
// Make class noncopyable
Candidate( const Candidate& ) = delete;
Candidate& operator=( const Candidate& ) = delete;
Candidate( Candidate&& ) = default;
Candidate& operator=( Candidate&& ) = default;
~Candidate() = default;
inline const std::string &CaseSwappedText() const {
return case_swapped_text_;
}
inline const CharacterSequence &WordBoundaryChars() const {
return word_boundary_chars_;
}
inline bool TextIsLowercase() const {
return text_is_lowercase_;
}
YCM_EXPORT Result QueryMatchResult( const Word &query ) const;
private:
void ComputeCaseSwappedText();
void ComputeTextIsLowercase();
void ComputeWordBoundaryChars();
std::string case_swapped_text_;
CharacterSequence word_boundary_chars_;
bool text_is_lowercase_;
};
} // namespace YouCompleteMe
#endif /* end of include guard: CANDIDATE_H_R5LZH6AC */
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/CandidateRepository.cpp 0000664 0000000 0000000 00000004561 13601473553 0023220 0 ustar 00root root 0000000 0000000 // Copyright (C) 2011-2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#include "CandidateRepository.h"
#include "Candidate.h"
#include "Utils.h"
#include
#ifdef USE_CLANG_COMPLETER
# include "ClangCompleter/CompletionData.h"
#endif // USE_CLANG_COMPLETER
namespace YouCompleteMe {
namespace {
// We set a reasonable max limit to prevent issues with huge candidate strings
// entering the database. Such large candidates are almost never desirable.
const size_t MAX_CANDIDATE_SIZE = 80;
} // unnamed namespace
CandidateRepository &CandidateRepository::Instance() {
static CandidateRepository repo;
return repo;
}
size_t CandidateRepository::NumStoredCandidates() {
std::lock_guard< std::mutex > locker( candidate_holder_mutex_ );
return candidate_holder_.size();
}
std::vector< const Candidate * > CandidateRepository::GetCandidatesForStrings(
std::vector< std::string >&& strings ) {
std::vector< const Candidate * > candidates;
candidates.reserve( strings.size() );
{
std::lock_guard< std::mutex > locker( candidate_holder_mutex_ );
for ( auto&& candidate_text : strings ) {
if ( candidate_text.size() > MAX_CANDIDATE_SIZE ) {
candidate_text = "";
}
std::unique_ptr< Candidate > &candidate = GetValueElseInsert(
candidate_holder_,
candidate_text,
nullptr );
if ( !candidate ) {
candidate.reset( new Candidate( std::move( candidate_text ) ) );
}
candidates.push_back( candidate.get() );
}
}
return candidates;
}
void CandidateRepository::ClearCandidates() {
candidate_holder_.clear();
}
} // namespace YouCompleteMe
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/CandidateRepository.h 0000664 0000000 0000000 00000004407 13601473553 0022664 0 ustar 00root root 0000000 0000000 // Copyright (C) 2011-2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#ifndef CANDIDATEREPOSITORY_H_K9OVCMHG
#define CANDIDATEREPOSITORY_H_K9OVCMHG
#include "Candidate.h"
#include
#include
#include
#include
#include
namespace YouCompleteMe {
using CandidateHolder = std::unordered_map< std::string,
std::unique_ptr< Candidate > >;
// This singleton stores already built Candidate objects for candidate strings
// that were already seen. If Candidates are requested for previously unseen
// strings, new Candidate objects are built.
//
// This is shared by the identifier completer and the clang completer so that
// work is not repeated.
//
// This class is thread-safe.
class CandidateRepository {
public:
YCM_EXPORT static CandidateRepository &Instance();
// Make class noncopyable
CandidateRepository( const CandidateRepository& ) = delete;
CandidateRepository& operator=( const CandidateRepository& ) = delete;
size_t NumStoredCandidates();
YCM_EXPORT std::vector< const Candidate * > GetCandidatesForStrings(
std::vector< std::string >&& strings );
// This should only be used to isolate tests and benchmarks.
YCM_EXPORT void ClearCandidates();
private:
CandidateRepository() = default;
~CandidateRepository() = default;
const std::string &ValidatedCandidateText(
const std::string &candidate_text );
// This data structure owns all the Candidate pointers
CandidateHolder candidate_holder_;
std::mutex candidate_holder_mutex_;
};
} // namespace YouCompleteMe
#endif /* end of include guard: CANDIDATEREPOSITORY_H_K9OVCMHG */
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/Character.cpp 0000664 0000000 0000000 00000006260 13601473553 0021136 0 ustar 00root root 0000000 0000000 // Copyright (C) 2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#include "Character.h"
#include "CodePoint.h"
#include
namespace YouCompleteMe {
namespace {
bool CodePointCompare( const CodePoint *left, const CodePoint *right ) {
return *left < *right;
}
// Sort the code points according to the Canonical Ordering Algorithm.
// See https://www.unicode.org/versions/Unicode10.0.0/ch03.pdf#G49591
CodePointSequence CanonicalSort( CodePointSequence code_points ) {
auto code_point_start = code_points.begin();
while ( code_point_start != code_points.end() ) {
if ( ( *code_point_start )->CombiningClass() == 0 ) {
++code_point_start;
continue;
}
auto code_point_end = code_point_start + 1;
while ( code_point_end != code_points.end() &&
( *code_point_end )->CombiningClass() != 0 ) {
++code_point_end;
}
std::sort( code_point_start, code_point_end, CodePointCompare );
if ( code_point_end == code_points.end() ) {
break;
}
code_point_start = code_point_end + 1;
}
return code_points;
}
// Decompose a UTF-8 encoded string into a sequence of code points according to
// Canonical Decomposition. See
// https://www.unicode.org/versions/Unicode10.0.0/ch03.pdf#G733
CodePointSequence CanonicalDecompose( const std::string &text ) {
CodePointSequence code_points = BreakIntoCodePoints( text );
std::string normal;
for ( const auto &code_point : code_points ) {
normal.append( code_point->Normal() );
}
return CanonicalSort( BreakIntoCodePoints( normal ) );
}
} // unnamed namespace
Character::Character( const std::string &character )
: is_base_( true ),
is_letter_( false ),
is_punctuation_( false ),
is_uppercase_( false ) {
// Normalize the character through NFD (Normalization Form D). See
// https://www.unicode.org/versions/Unicode10.0.0/ch03.pdf#G49621
CodePointSequence code_points = CanonicalDecompose( character );
for ( const auto &code_point : code_points ) {
normal_.append( code_point->Normal() );
folded_case_.append( code_point->FoldedCase() );
swapped_case_.append( code_point->SwappedCase() );
is_letter_ |= code_point->IsLetter();
is_punctuation_ |= code_point->IsPunctuation();
is_uppercase_ |= code_point->IsUppercase();
switch ( code_point->GetBreakProperty() ) {
case BreakProperty::PREPEND:
case BreakProperty::EXTEND:
case BreakProperty::SPACINGMARK:
is_base_ = false;
break;
default:
base_.append( code_point->FoldedCase() );
}
}
}
} // namespace YouCompleteMe
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/Character.h 0000664 0000000 0000000 00000006300 13601473553 0020576 0 ustar 00root root 0000000 0000000 // Copyright (C) 2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#ifndef CHARACTER_H_YTIET2HZ
#define CHARACTER_H_YTIET2HZ
#include
#include
namespace YouCompleteMe {
// This class represents a UTF-8 character. It takes a UTF-8 encoded string
// corresponding to a grapheme cluster (see
// https://www.unicode.org/glossary/#grapheme_cluster), normalize it through NFD
// (see https://www.unicode.org/versions/Unicode10.0.0/ch03.pdf#G49621), and
// compute the folded and swapped case versions of the normalized character. It
// also holds some properties like if the character is a letter or a
// punctuation, and if it is uppercase.
class Character {
public:
YCM_EXPORT explicit Character( const std::string &character );
// Make class noncopyable
Character( const Character& ) = delete;
Character& operator=( const Character& ) = delete;
Character( Character&& ) = default;
Character& operator=( Character&& ) = default;
inline std::string Normal() const {
return normal_;
}
inline std::string Base() const {
return base_;
}
inline std::string FoldedCase() const {
return folded_case_;
}
inline std::string SwappedCase() const {
return swapped_case_;
}
inline bool IsBase() const {
return is_base_;
}
inline bool IsLetter() const {
return is_letter_;
}
inline bool IsPunctuation() const {
return is_punctuation_;
}
inline bool IsUppercase() const {
return is_uppercase_;
}
inline bool operator== ( const Character &other ) const {
return normal_ == other.normal_;
};
inline bool EqualsBase( const Character &other ) const {
return base_ == other.base_;
}
inline bool EqualsIgnoreCase( const Character &other ) const {
return folded_case_ == other.folded_case_;
};
// Smart base matching on top of smart case matching, e.g.:
// - e matches e, é, E, É;
// - E matches E, É but not e, é;
// - é matches é, É but not e, E;
// - É matches É but not e, é, E.
inline bool MatchesSmart( const Character &other ) const {
return ( is_base_ && EqualsBase( other ) &&
( !is_uppercase_ || other.is_uppercase_ ) ) ||
( !is_uppercase_ && EqualsIgnoreCase( other ) ) ||
normal_ == other.normal_;
};
private:
std::string normal_;
std::string base_;
std::string folded_case_;
std::string swapped_case_;
bool is_base_;
bool is_letter_;
bool is_punctuation_;
bool is_uppercase_;
};
using CharacterSequence = std::vector< const Character * >;
} // namespace YouCompleteMe
#endif /* end of include guard: CHARACTER_H_YTIET2HZ */
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/CharacterRepository.cpp 0000664 0000000 0000000 00000003741 13601473553 0023237 0 ustar 00root root 0000000 0000000 // Copyright (C) 2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#include "CharacterRepository.h"
#include "Character.h"
#include "Utils.h"
#include
namespace YouCompleteMe {
CharacterRepository &CharacterRepository::Instance() {
static CharacterRepository repo;
return repo;
}
size_t CharacterRepository::NumStoredCharacters() {
std::lock_guard< std::mutex > locker( character_holder_mutex_ );
return character_holder_.size();
}
CharacterSequence CharacterRepository::GetCharacters(
const std::vector< std::string > &characters ) {
CharacterSequence character_objects;
character_objects.reserve( characters.size() );
{
std::lock_guard< std::mutex > locker( character_holder_mutex_ );
for ( const std::string & character : characters ) {
std::unique_ptr< Character > &character_object = GetValueElseInsert(
character_holder_,
character,
nullptr );
if ( !character_object ) {
character_object.reset( new Character( character ) );
}
character_objects.push_back( character_object.get() );
}
}
return character_objects;
}
void CharacterRepository::ClearCharacters() {
character_holder_.clear();
}
} // namespace YouCompleteMe
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/CharacterRepository.h 0000664 0000000 0000000 00000004074 13601473553 0022704 0 ustar 00root root 0000000 0000000 // Copyright (C) 2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#ifndef CHARACTER_REPOSITORY_H_36TXTS6C
#define CHARACTER_REPOSITORY_H_36TXTS6C
#include "Character.h"
#include
#include
#include
#include
#include
namespace YouCompleteMe {
using CharacterHolder = std::unordered_map< std::string,
std::unique_ptr< Character > >;
// This singleton stores already built Character objects for character strings
// that were already seen. If Characters are requested for previously unseen
// strings, new Character objects are built.
//
// This class is thread-safe.
class CharacterRepository {
public:
YCM_EXPORT static CharacterRepository &Instance();
// Make class noncopyable
CharacterRepository( const CharacterRepository& ) = delete;
CharacterRepository& operator=( const CharacterRepository& ) = delete;
YCM_EXPORT size_t NumStoredCharacters();
YCM_EXPORT CharacterSequence GetCharacters(
const std::vector< std::string > &characters );
// This should only be used to isolate tests and benchmarks.
YCM_EXPORT void ClearCharacters();
private:
CharacterRepository() = default;
~CharacterRepository() = default;
// This data structure owns all the Character pointers
CharacterHolder character_holder_;
std::mutex character_holder_mutex_;
};
} // namespace YouCompleteMe
#endif /* end of include guard: CHARACTER_REPOSITORY_H_36TXTS6C */
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/ClangCompleter/ 0000775 0000000 0000000 00000000000 13601473553 0021431 5 ustar 00root root 0000000 0000000 ycmd-0+20191222+git2771f6f+ds/cpp/ycm/ClangCompleter/ClangCompleter.cpp 0000664 0000000 0000000 00000023551 13601473553 0025042 0 ustar 00root root 0000000 0000000 // Copyright (C) 2011-2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#include "ClangCompleter.h"
#include "Candidate.h"
#include "CandidateRepository.h"
#include "ClangUtils.h"
#include "CompletionData.h"
#include "Result.h"
#include "TranslationUnit.h"
#include "Utils.h"
#include
#include
using std::shared_ptr;
namespace YouCompleteMe {
ClangCompleter::ClangCompleter()
: clang_index_( clang_createIndex( 0, 0 ) ),
translation_unit_store_( clang_index_ ) {
// The libclang docs don't say what is the default value for crash recovery.
// I'm pretty sure it's turned on by default, but I'm not going to take any
// chances.
clang_toggleCrashRecovery( true );
}
ClangCompleter::~ClangCompleter() {
// We need to destroy all TUs before calling clang_disposeIndex because
// the translation units need to be destroyed before the index is destroyed.
// Technically, a thread could still be holding onto a shared_ptr object
// when we destroy the clang index, but since we're shutting down, we don't
// really care.
// In practice, this situation shouldn't happen because the server threads are
// Python deamon threads and will all be killed before the main thread exits.
translation_unit_store_.RemoveAll();
clang_disposeIndex( clang_index_ );
}
bool ClangCompleter::UpdatingTranslationUnit( const std::string &filename ) {
shared_ptr< TranslationUnit > unit = translation_unit_store_.Get( filename );
if ( !unit ) {
return false;
}
// Thankfully, an invalid, sentinel TU always returns true for
// IsCurrentlyUpdating, so no caller will try to rely on the TU object, even
// if unit is currently pointing to a sentinel.
return unit->IsCurrentlyUpdating();
}
std::vector< Diagnostic > ClangCompleter::UpdateTranslationUnit(
const std::string &translation_unit,
const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags ) {
bool translation_unit_created;
shared_ptr< TranslationUnit > unit = translation_unit_store_.GetOrCreate(
translation_unit,
unsaved_files,
flags,
translation_unit_created );
try {
return unit->Reparse( unsaved_files );
} catch ( const ClangParseError & ) {
// If unit->Reparse fails, then the underlying TranslationUnit object is not
// valid anymore and needs to be destroyed and removed from the filename ->
// TU map.
translation_unit_store_.Remove( translation_unit );
throw;
}
}
std::vector< CompletionData >
ClangCompleter::CandidatesForLocationInFile(
const std::string &translation_unit,
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags ) {
shared_ptr< TranslationUnit > unit =
translation_unit_store_.GetOrCreate( translation_unit,
unsaved_files,
flags );
return unit->CandidatesForLocation( filename,
line,
column,
unsaved_files );
}
Location ClangCompleter::GetDeclarationLocation(
const std::string &translation_unit,
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags,
bool reparse ) {
shared_ptr< TranslationUnit > unit =
translation_unit_store_.GetOrCreate( translation_unit,
unsaved_files,
flags );
return unit->GetDeclarationLocation( filename,
line,
column,
unsaved_files,
reparse );
}
Location ClangCompleter::GetDefinitionLocation(
const std::string &translation_unit,
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags,
bool reparse ) {
shared_ptr< TranslationUnit > unit =
translation_unit_store_.GetOrCreate( translation_unit,
unsaved_files,
flags );
return unit->GetDefinitionLocation( filename,
line,
column,
unsaved_files,
reparse );
}
Location ClangCompleter::GetDefinitionOrDeclarationLocation(
const std::string &translation_unit,
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags,
bool reparse ) {
shared_ptr< TranslationUnit > unit =
translation_unit_store_.GetOrCreate( translation_unit,
unsaved_files,
flags );
return unit->GetDefinitionOrDeclarationLocation( filename,
line,
column,
unsaved_files,
reparse );
}
std::string ClangCompleter::GetTypeAtLocation(
const std::string &translation_unit,
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags,
bool reparse ) {
shared_ptr< TranslationUnit > unit =
translation_unit_store_.GetOrCreate( translation_unit,
unsaved_files,
flags );
return unit->GetTypeAtLocation( filename,
line,
column,
unsaved_files,
reparse );
}
std::string ClangCompleter::GetEnclosingFunctionAtLocation(
const std::string &translation_unit,
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags,
bool reparse ) {
shared_ptr< TranslationUnit > unit =
translation_unit_store_.GetOrCreate( translation_unit,
unsaved_files,
flags );
return unit->GetEnclosingFunctionAtLocation( filename,
line,
column,
unsaved_files,
reparse );
}
std::vector< FixIt >
ClangCompleter::GetFixItsForLocationInFile(
const std::string &translation_unit,
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags,
bool reparse ) {
shared_ptr< TranslationUnit > unit =
translation_unit_store_.GetOrCreate( translation_unit,
unsaved_files,
flags );
return unit->GetFixItsForLocationInFile( filename,
line,
column,
unsaved_files,
reparse );
}
DocumentationData ClangCompleter::GetDocsForLocationInFile(
const std::string &translation_unit,
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags,
bool reparse ) {
shared_ptr< TranslationUnit > unit =
translation_unit_store_.GetOrCreate( translation_unit,
unsaved_files,
flags );
Location location( unit->GetDeclarationLocation( filename,
line,
column,
unsaved_files,
reparse ) );
// By default, libclang ignores comments from system headers and, in
// particular, headers included with the -isystem flag. If the declaration is
// found in such header, get the documentation directly from the corresponding
// translation unit. Comments in the main file of a translation unit are not
// ignored.
if ( unit->LocationIsInSystemHeader( location ) ) {
unit = translation_unit_store_.GetOrCreate( location.filename_,
unsaved_files,
flags );
return unit->GetDocsForLocation( location, unsaved_files, reparse );
}
// This translation unit has already been parsed when getting the
// declaration's location.
return unit->GetDocsForLocation( location, unsaved_files, false );
}
void ClangCompleter::DeleteCachesForFile( const std::string &filename ) {
translation_unit_store_.Remove( filename );
}
} // namespace YouCompleteMe
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/ClangCompleter/ClangCompleter.h 0000664 0000000 0000000 00000010171 13601473553 0024501 0 ustar 00root root 0000000 0000000 // Copyright (C) 2011-2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#ifndef CLANGCOMPLETE_H_WLKDU0ZV
#define CLANGCOMPLETE_H_WLKDU0ZV
#include "Diagnostic.h"
#include "Documentation.h"
#include "TranslationUnitStore.h"
#include "UnsavedFile.h"
#include
using CXTranslationUnit = CXTranslationUnitImpl*;
namespace YouCompleteMe {
class TranslationUnit;
struct CompletionData;
struct Location;
using CompletionDatas = std::vector< CompletionData >;
// All filename parameters must be absolute paths.
class ClangCompleter {
public:
YCM_EXPORT ClangCompleter();
YCM_EXPORT ~ClangCompleter();
ClangCompleter( const ClangCompleter& ) = delete;
ClangCompleter& operator=( const ClangCompleter& ) = delete;
bool UpdatingTranslationUnit( const std::string &filename );
YCM_EXPORT std::vector< Diagnostic > UpdateTranslationUnit(
const std::string &translation_unit,
const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags );
YCM_EXPORT std::vector< CompletionData > CandidatesForLocationInFile(
const std::string &translation_unit,
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags );
YCM_EXPORT Location GetDeclarationLocation(
const std::string &translation_unit,
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags,
bool reparse = true );
YCM_EXPORT Location GetDefinitionLocation(
const std::string &translation_unit,
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags,
bool reparse = true );
YCM_EXPORT Location GetDefinitionOrDeclarationLocation(
const std::string &translation_unit,
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags,
bool reparse = true );
YCM_EXPORT std::string GetTypeAtLocation(
const std::string &translation_unit,
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags,
bool reparse = true );
YCM_EXPORT std::string GetEnclosingFunctionAtLocation(
const std::string &translation_unit,
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags,
bool reparse = true );
YCM_EXPORT std::vector< FixIt > GetFixItsForLocationInFile(
const std::string &translation_unit,
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags,
bool reparse = true );
YCM_EXPORT DocumentationData GetDocsForLocationInFile(
const std::string &translation_unit,
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags,
bool reparse = true );
void DeleteCachesForFile( const std::string &filename );
private:
/////////////////////////////
// PRIVATE MEMBER VARIABLES
/////////////////////////////
CXIndex clang_index_;
TranslationUnitStore translation_unit_store_;
};
} // namespace YouCompleteMe
#endif /* end of include guard: CLANGCOMPLETE_H_WLKDU0ZV */
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/ClangCompleter/ClangHelpers.cpp 0000664 0000000 0000000 00000021505 13601473553 0024507 0 ustar 00root root 0000000 0000000 // Copyright (C) 2013-2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#include "ClangHelpers.h"
#include "ClangUtils.h"
#include "Location.h"
#include "PythonSupport.h"
#include "Range.h"
#include "UnsavedFile.h"
#include "Utils.h"
#include
#include
using std::unordered_map;
namespace YouCompleteMe {
namespace {
DiagnosticKind DiagnosticSeverityToType( CXDiagnosticSeverity severity ) {
switch ( severity ) {
case CXDiagnostic_Ignored:
case CXDiagnostic_Note:
return DiagnosticKind::INFORMATION;
case CXDiagnostic_Warning:
return DiagnosticKind::WARNING;
case CXDiagnostic_Error:
case CXDiagnostic_Fatal:
default:
return DiagnosticKind::ERROR;
}
}
FixIt BuildDiagnosticFixIt( const std::string& text, CXDiagnostic diagnostic ) {
FixIt fixit;
size_t num_chunks = clang_getDiagnosticNumFixIts( diagnostic );
if ( !num_chunks ) {
return fixit;
}
fixit.chunks.reserve( num_chunks );
fixit.location = Location( clang_getDiagnosticLocation( diagnostic ) );
fixit.text = text;
for ( size_t idx = 0; idx < num_chunks; ++idx ) {
FixItChunk chunk;
CXSourceRange range;
chunk.replacement_text = CXStringToString(
clang_getDiagnosticFixIt( diagnostic,
idx,
&range ) );
chunk.range = Range( range );
fixit.chunks.push_back( chunk );
}
return fixit;
}
/// This method generates a FixIt object for the supplied diagnostic, and any
/// child diagnostics (recursively), should a FixIt be available and appends
/// them to fixits.
/// Similarly it populates full_diagnostic_text with a concatenation of the
/// diagnostic text for the supplied diagnostic and each child diagnostic
/// (recursively).
/// Warning: This method is re-entrant (recursive).
void BuildFullDiagnosticDataFromChildren(
std::string& full_diagnostic_text,
std::vector< FixIt >& fixits,
CXDiagnostic diagnostic ) {
std::string diag_text = CXStringToString( clang_formatDiagnostic(
diagnostic,
clang_defaultDiagnosticDisplayOptions() ) );
full_diagnostic_text.append( diag_text );
// Populate any fixit attached to this diagnostic.
FixIt fixit = BuildDiagnosticFixIt( diag_text, diagnostic );
if ( !fixit.chunks.empty() ) {
fixits.push_back( fixit );
}
// Note: clang docs say that a CXDiagnosticSet retrieved with
// clang_getChildDiagnostics do NOT need to be released with
// clang_diposeDiagnosticSet
CXDiagnosticSet diag_set = clang_getChildDiagnostics( diagnostic );
if ( !diag_set ) {
return;
}
size_t num_child_diagnostics = clang_getNumDiagnosticsInSet( diag_set );
if ( !num_child_diagnostics ) {
return;
}
for ( size_t i = 0; i < num_child_diagnostics; ++i ) {
CXDiagnostic child_diag = clang_getDiagnosticInSet( diag_set, i );
if( !child_diag ) {
continue;
}
full_diagnostic_text.append( "\n" );
// recurse
BuildFullDiagnosticDataFromChildren( full_diagnostic_text,
fixits,
child_diag );
}
}
// Returns true when the provided completion string is available to the user;
// unavailable completion strings refer to entities that are private/protected,
// deprecated etc.
bool CompletionStringAvailable( CXCompletionString completion_string ) {
return clang_getCompletionAvailability( completion_string ) ==
CXAvailability_Available;
}
std::vector< Range > GetRanges( const DiagnosticWrap &diagnostic_wrap ) {
std::vector< Range > ranges;
size_t num_ranges = clang_getDiagnosticNumRanges( diagnostic_wrap.get() );
ranges.reserve( num_ranges );
for ( size_t i = 0; i < num_ranges; ++i ) {
ranges.emplace_back( clang_getDiagnosticRange( diagnostic_wrap.get(), i ) );
}
return ranges;
}
Range GetLocationExtent( CXSourceLocation source_location,
CXTranslationUnit translation_unit ) {
// If you think the below code is an idiotic way of getting the source range
// for an identifier at a specific source location, you are not the only one.
// I cannot believe that this is the only way to achieve this with the
// libclang API in a robust way.
// I've tried many simpler ways of doing this and they all fail in various
// situations.
CXSourceRange range = clang_getRange( source_location, source_location );
CXToken *tokens;
unsigned int num_tokens;
clang_tokenize( translation_unit, range, &tokens, &num_tokens );
Location location( source_location );
Range final_range( range );
for ( size_t i = 0; i < num_tokens; ++i ) {
CXToken token = tokens[ i ];
if ( clang_getTokenKind( token ) == CXToken_Comment ) {
continue;
}
Location token_location( clang_getTokenLocation( translation_unit,
token ) );
if ( token_location == location ) {
final_range = Range( clang_getTokenExtent( translation_unit, token ) );
break;
}
}
clang_disposeTokens( translation_unit, tokens, num_tokens );
return final_range;
}
} // unnamed namespace
std::vector< CXUnsavedFile > ToCXUnsavedFiles(
const std::vector< UnsavedFile > &unsaved_files ) {
std::vector< CXUnsavedFile > clang_unsaved_files( unsaved_files.size() );
for ( size_t i = 0; i < unsaved_files.size(); ++i ) {
clang_unsaved_files[ i ].Filename = unsaved_files[ i ].filename_.c_str();
clang_unsaved_files[ i ].Contents = unsaved_files[ i ].contents_.c_str();
clang_unsaved_files[ i ].Length = unsaved_files[ i ].length_;
}
return clang_unsaved_files;
}
std::vector< CompletionData > ToCompletionDataVector(
CXCodeCompleteResults *results ) {
std::vector< CompletionData > completions;
if ( !results || !results->Results ) {
return completions;
}
completions.reserve( results->NumResults );
unordered_map< std::string, size_t > seen_data;
for ( size_t i = 0; i < results->NumResults; ++i ) {
CXCompletionResult result = results->Results[ i ];
CXCompletionString completion_string = result.CompletionString;
if ( !completion_string ||
!CompletionStringAvailable( completion_string ) ) {
continue;
}
CompletionData data( completion_string, result.CursorKind, results, i );
size_t index = GetValueElseInsert( seen_data,
data.original_string_,
completions.size() );
if ( index == completions.size() ) {
completions.push_back( std::move( data ) );
} else {
// If we have already seen this completion, then this is an overload of a
// function we have seen. We add the signature of the overload to the
// detailed information.
completions[ index ].detailed_info_
.append( data.return_type_ )
.append( " " )
.append( data.everything_except_return_type_ )
.append( "\n" );
}
}
return completions;
}
Diagnostic BuildDiagnostic( const DiagnosticWrap &diagnostic_wrap,
CXTranslationUnit translation_unit ) {
Diagnostic diagnostic;
if ( !diagnostic_wrap ) {
return diagnostic;
}
diagnostic.kind_ = DiagnosticSeverityToType(
clang_getDiagnosticSeverity( diagnostic_wrap.get() ) );
// If this is an "ignored" diagnostic, there's no point in continuing since we
// won't display those to the user
if ( diagnostic.kind_ == DiagnosticKind::INFORMATION ) {
return diagnostic;
}
CXSourceLocation source_location =
clang_getDiagnosticLocation( diagnostic_wrap.get() );
diagnostic.location_ = Location( source_location );
diagnostic.location_extent_ = GetLocationExtent( source_location,
translation_unit );
diagnostic.ranges_ = GetRanges( diagnostic_wrap );
diagnostic.text_ = CXStringToString(
clang_getDiagnosticSpelling( diagnostic_wrap.get() ) );
BuildFullDiagnosticDataFromChildren( diagnostic.long_formatted_text_,
diagnostic.fixits_,
diagnostic_wrap.get() );
return diagnostic;
}
} // namespace YouCompleteMe
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/ClangCompleter/ClangHelpers.h 0000664 0000000 0000000 00000003025 13601473553 0024151 0 ustar 00root root 0000000 0000000 // Copyright (C) 2013-2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#ifndef CLANGHELPERS_H_T3ME71LG
#define CLANGHELPERS_H_T3ME71LG
#include "CompletionData.h"
#include "Diagnostic.h"
#include "UnsavedFile.h"
#include
#include
#include
namespace YouCompleteMe {
using DiagnosticWrap =
std::shared_ptr< std::remove_pointer< CXDiagnostic >::type >;
std::vector< CompletionData > ToCompletionDataVector(
CXCodeCompleteResults *results );
// NOTE: CXUnsavedFiles store pointers to data in UnsavedFiles, so UnsavedFiles
// need to outlive CXUnsavedFiles!
std::vector< CXUnsavedFile > ToCXUnsavedFiles(
const std::vector< UnsavedFile > &unsaved_files );
Diagnostic BuildDiagnostic( const DiagnosticWrap &diagnostic_wrap,
CXTranslationUnit translation_unit );
} // namespace YouCompleteMe
#endif /* end of include guard: CLANGHELPERS_H_T3ME71LG */
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/ClangCompleter/ClangUtils.cpp 0000664 0000000 0000000 00000004274 13601473553 0024211 0 ustar 00root root 0000000 0000000 // Copyright (C) 2011, 2012 Google Inc.
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#include "ClangUtils.h"
namespace YouCompleteMe {
std::string CXStringToString( CXString text ) {
std::string final_string;
if ( !text.data ) {
return final_string;
}
final_string = std::string( clang_getCString( text ) );
clang_disposeString( text );
return final_string;
}
bool CursorIsValid( CXCursor cursor ) {
return !clang_Cursor_isNull( cursor ) &&
!clang_isInvalid( clang_getCursorKind( cursor ) );
}
std::string CXFileToFilepath( CXFile file ) {
return CXStringToString( clang_getFileName( file ) );
}
std::string ClangVersion() {
return CXStringToString( clang_getClangVersion() );
}
const char *CXErrorCodeToString( CXErrorCode code ) {
switch ( code ) {
case CXError_Success:
return "No error encountered while parsing the translation unit.";
case CXError_Failure:
return "Failed to parse the translation unit.";
case CXError_Crashed:
return "Libclang crashed while parsing the translation unit.";
case CXError_InvalidArguments:
return "Invalid arguments supplied when parsing the translation unit.";
case CXError_ASTReadError:
return "An AST deserialization error occurred "
"while parsing the translation unit.";
}
return "Unknown error while parsing the translation unit.";
}
ClangParseError::ClangParseError( const char *what_arg )
: std::runtime_error( what_arg ) {
};
ClangParseError::ClangParseError( CXErrorCode code )
: ClangParseError( CXErrorCodeToString( code ) ) {
};
} // namespace YouCompleteMe
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/ClangCompleter/ClangUtils.h 0000664 0000000 0000000 00000003034 13601473553 0023647 0 ustar 00root root 0000000 0000000 // Copyright (C) 2011, 2012 Google Inc.
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#ifndef CLANGUTILS_H_9MVHQLJS
#define CLANGUTILS_H_9MVHQLJS
#include
#include
#include
namespace YouCompleteMe {
/**
* Return a std::string from the supplied CXString.
*
* Takes ownership of, and destroys, the supplied CXString which must not be used
* subsequently.
*/
std::string CXStringToString( CXString text );
bool CursorIsValid( CXCursor cursor );
std::string CXFileToFilepath( CXFile file );
std::string ClangVersion();
const char *CXErrorCodeToString( CXErrorCode code );
/**
* Thrown when libclang fails to parse (or reparse) the translation unit.
*/
struct YCM_EXPORT ClangParseError : std::runtime_error {
explicit ClangParseError( const char *what_arg );
explicit ClangParseError( CXErrorCode code );
};
} // namespace YouCompleteMe
#endif /* end of include guard: CLANGUTILS_H_9MVHQLJS */
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/ClangCompleter/CompilationDatabase.cpp 0000664 0000000 0000000 00000006111 13601473553 0026037 0 ustar 00root root 0000000 0000000 // Copyright (C) 2011-2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#include "CompilationDatabase.h"
#include "ClangUtils.h"
#include "PythonSupport.h"
#include
using std::lock_guard;
using std::unique_lock;
using std::try_to_lock_t;
using std::remove_pointer;
using std::shared_ptr;
using std::mutex;
namespace YouCompleteMe {
using CompileCommandsWrap =
shared_ptr< remove_pointer< CXCompileCommands >::type >;
CompilationDatabase::CompilationDatabase(
pybind11::object path_to_directory )
: is_loaded_( false ),
path_to_directory_( GetUtf8String( path_to_directory ) ) {
CXCompilationDatabase_Error status;
compilation_database_ = clang_CompilationDatabase_fromDirectory(
path_to_directory_.c_str(),
&status );
is_loaded_ = status == CXCompilationDatabase_NoError;
}
CompilationDatabase::~CompilationDatabase() {
clang_CompilationDatabase_dispose( compilation_database_ );
}
bool CompilationDatabase::DatabaseSuccessfullyLoaded() {
return is_loaded_;
}
bool CompilationDatabase::AlreadyGettingFlags() {
unique_lock< mutex > lock( compilation_database_mutex_, try_to_lock_t() );
return !lock.owns_lock();
}
CompilationInfoForFile CompilationDatabase::GetCompilationInfoForFile(
pybind11::object path_to_file ) {
CompilationInfoForFile info;
if ( !is_loaded_ ) {
return info;
}
std::string path_to_file_string = GetUtf8String( path_to_file );
pybind11::gil_scoped_release unlock;
lock_guard< mutex > lock( compilation_database_mutex_ );
CompileCommandsWrap commands(
clang_CompilationDatabase_getCompileCommands(
compilation_database_,
path_to_file_string.c_str() ), clang_CompileCommands_dispose );
size_t num_commands = clang_CompileCommands_getSize( commands.get() );
if ( num_commands < 1 ) {
return info;
}
// We always pick the first command offered
CXCompileCommand command = clang_CompileCommands_getCommand(
commands.get(),
0 );
info.compiler_working_dir_ = CXStringToString(
clang_CompileCommand_getDirectory( command ) );
size_t num_flags = clang_CompileCommand_getNumArgs( command );
info.compiler_flags_.reserve( num_flags );
for ( size_t i = 0; i < num_flags; ++i ) {
info.compiler_flags_.push_back(
CXStringToString( clang_CompileCommand_getArg( command, i ) ) );
}
return info;
}
} // namespace YouCompleteMe
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/ClangCompleter/CompilationDatabase.h 0000664 0000000 0000000 00000004324 13601473553 0025510 0 ustar 00root root 0000000 0000000 // Copyright (C) 2011-2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#ifndef COMPILATIONDATABASE_H_ZT7MQXPG
#define COMPILATIONDATABASE_H_ZT7MQXPG
#include
#include
#include
#include
#include
namespace YouCompleteMe {
struct CompilationInfoForFile {
std::vector< std::string > compiler_flags_;
std::string compiler_working_dir_;
};
// Access to Clang's internal CompilationDatabase. This class is thread-safe.
class CompilationDatabase {
public:
// |path_to_directory| should be a string-like object.
explicit CompilationDatabase( pybind11::object path_to_directory );
CompilationDatabase( const CompilationDatabase& ) = delete;
CompilationDatabase& operator=( const CompilationDatabase& ) = delete;
~CompilationDatabase();
bool DatabaseSuccessfullyLoaded();
// Returns true when a separate thread is already getting flags; this is
// useful so that the caller doesn't need to block.
bool AlreadyGettingFlags();
// NOTE: Multiple calls to this function from separate threads will be
// serialized since Clang internals are not thread-safe.
// |path_to_file| should be a string-like object.
CompilationInfoForFile GetCompilationInfoForFile(
pybind11::object path_to_file );
std::string GetDatabaseDirectory() {
return path_to_directory_;
}
private:
bool is_loaded_;
std::string path_to_directory_;
CXCompilationDatabase compilation_database_;
std::mutex compilation_database_mutex_;
};
} // namespace YouCompleteMe
#endif /* end of include guard: COMPILATIONDATABASE_H_ZT7MQXPG */
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/ClangCompleter/CompletionData.cpp 0000664 0000000 0000000 00000021346 13601473553 0025046 0 ustar 00root root 0000000 0000000 // Copyright (C) 2011, 2012 Google Inc.
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#include "CompletionData.h"
#include "ClangUtils.h"
#include
namespace YouCompleteMe {
namespace {
CompletionKind CursorKindToCompletionKind( CXCursorKind kind ) {
switch ( kind ) {
case CXCursor_StructDecl:
return CompletionKind::STRUCT;
case CXCursor_ClassDecl:
case CXCursor_ClassTemplate:
case CXCursor_ObjCInterfaceDecl:
case CXCursor_ObjCImplementationDecl:
return CompletionKind::CLASS;
case CXCursor_EnumDecl:
return CompletionKind::ENUM;
case CXCursor_UnexposedDecl:
case CXCursor_UnionDecl:
case CXCursor_TypedefDecl:
return CompletionKind::TYPE;
case CXCursor_FieldDecl:
case CXCursor_ObjCIvarDecl:
case CXCursor_ObjCPropertyDecl:
case CXCursor_EnumConstantDecl:
return CompletionKind::MEMBER;
case CXCursor_FunctionDecl:
case CXCursor_CXXMethod:
case CXCursor_FunctionTemplate:
case CXCursor_ConversionFunction:
case CXCursor_Constructor:
case CXCursor_Destructor:
case CXCursor_ObjCClassMethodDecl:
case CXCursor_ObjCInstanceMethodDecl:
return CompletionKind::FUNCTION;
case CXCursor_VarDecl:
return CompletionKind::VARIABLE;
case CXCursor_MacroDefinition:
return CompletionKind::MACRO;
case CXCursor_ParmDecl:
return CompletionKind::PARAMETER;
case CXCursor_Namespace:
case CXCursor_NamespaceAlias:
return CompletionKind::NAMESPACE;
default:
return CompletionKind::UNKNOWN;
}
}
bool IsMainCompletionTextInfo( CXCompletionChunkKind kind ) {
return
kind == CXCompletionChunk_Optional ||
kind == CXCompletionChunk_TypedText ||
kind == CXCompletionChunk_Placeholder ||
kind == CXCompletionChunk_LeftParen ||
kind == CXCompletionChunk_RightParen ||
kind == CXCompletionChunk_RightBracket ||
kind == CXCompletionChunk_LeftBracket ||
kind == CXCompletionChunk_LeftBrace ||
kind == CXCompletionChunk_RightBrace ||
kind == CXCompletionChunk_RightAngle ||
kind == CXCompletionChunk_LeftAngle ||
kind == CXCompletionChunk_Comma ||
kind == CXCompletionChunk_Colon ||
kind == CXCompletionChunk_SemiColon ||
kind == CXCompletionChunk_Equal ||
kind == CXCompletionChunk_Informative ||
kind == CXCompletionChunk_HorizontalSpace ||
kind == CXCompletionChunk_Text;
}
std::string ChunkToString( CXCompletionString completion_string,
size_t chunk_num ) {
if ( !completion_string ) {
return std::string();
}
return CXStringToString(
clang_getCompletionChunkText( completion_string, chunk_num ) );
}
std::string OptionalChunkToString( CXCompletionString completion_string,
size_t chunk_num ) {
std::string final_string;
if ( !completion_string ) {
return final_string;
}
CXCompletionString optional_completion_string =
clang_getCompletionChunkCompletionString( completion_string, chunk_num );
if ( !optional_completion_string ) {
return final_string;
}
size_t optional_num_chunks = clang_getNumCompletionChunks(
optional_completion_string );
for ( size_t j = 0; j < optional_num_chunks; ++j ) {
CXCompletionChunkKind kind = clang_getCompletionChunkKind(
optional_completion_string, j );
if ( kind == CXCompletionChunk_Optional ) {
final_string.append( OptionalChunkToString( optional_completion_string,
j ) );
} else {
final_string.append( ChunkToString( optional_completion_string, j ) );
}
}
return final_string;
}
bool IdentifierEndsWith( const std::string &identifier,
const std::string &end ) {
if ( identifier.size() >= end.size() ) {
return 0 == identifier.compare( identifier.length() - end.length(),
end.length(),
end );
}
return false;
}
// foo( -> foo
// foo() -> foo
std::string RemoveTrailingParens( std::string text ) {
if ( IdentifierEndsWith( text, "(" ) ) {
text.erase( text.length() - 1, 1 );
} else if ( IdentifierEndsWith( text, "()" ) ) {
text.erase( text.length() - 2, 2 );
}
return text;
}
} // unnamed namespace
CompletionData::CompletionData( CXCompletionString completion_string,
CXCursorKind kind,
CXCodeCompleteResults *results,
size_t index ) {
size_t num_chunks = clang_getNumCompletionChunks( completion_string );
bool saw_left_paren = false;
bool saw_function_params = false;
bool saw_placeholder = false;
for ( size_t j = 0; j < num_chunks; ++j ) {
ExtractDataFromChunk( completion_string,
j,
saw_left_paren,
saw_function_params,
saw_placeholder );
}
original_string_ = RemoveTrailingParens( std::move( original_string_ ) );
kind_ = CursorKindToCompletionKind( kind );
detailed_info_.append( return_type_ )
.append( " " )
.append( everything_except_return_type_ )
.append( "\n" );
doc_string_ = CXStringToString(
clang_getCompletionBriefComment( completion_string ) );
BuildCompletionFixIt( results, index );
}
void CompletionData::ExtractDataFromChunk( CXCompletionString completion_string,
size_t chunk_num,
bool &saw_left_paren,
bool &saw_function_params,
bool &saw_placeholder ) {
CXCompletionChunkKind kind = clang_getCompletionChunkKind(
completion_string, chunk_num );
if ( IsMainCompletionTextInfo( kind ) ) {
if ( kind == CXCompletionChunk_LeftParen ) {
saw_left_paren = true;
} else if ( saw_left_paren &&
!saw_function_params &&
kind != CXCompletionChunk_RightParen &&
kind != CXCompletionChunk_Informative ) {
saw_function_params = true;
everything_except_return_type_.append( " " );
} else if ( saw_function_params && kind == CXCompletionChunk_RightParen ) {
everything_except_return_type_.append( " " );
}
if ( kind == CXCompletionChunk_Optional ) {
everything_except_return_type_.append(
OptionalChunkToString( completion_string, chunk_num ) );
} else {
everything_except_return_type_.append(
ChunkToString( completion_string, chunk_num ) );
}
}
switch ( kind ) {
case CXCompletionChunk_ResultType:
return_type_ = ChunkToString( completion_string, chunk_num );
break;
case CXCompletionChunk_Placeholder:
saw_placeholder = true;
break;
case CXCompletionChunk_TypedText:
case CXCompletionChunk_Text:
// need to add paren to insert string
// when implementing inherited methods or declared methods in objc.
case CXCompletionChunk_LeftParen:
case CXCompletionChunk_RightParen:
case CXCompletionChunk_HorizontalSpace:
if ( !saw_placeholder ) {
original_string_ += ChunkToString( completion_string, chunk_num );
}
break;
default:
break;
}
}
void CompletionData::BuildCompletionFixIt( CXCodeCompleteResults *results,
size_t index ) {
size_t num_chunks = clang_getCompletionNumFixIts( results, index );
if ( !num_chunks ) {
return;
}
fixit_.chunks.reserve( num_chunks );
for ( size_t chunk_index = 0; chunk_index < num_chunks; ++chunk_index ) {
FixItChunk chunk;
CXSourceRange range;
chunk.replacement_text = CXStringToString(
clang_getCompletionFixIt( results,
index,
chunk_index,
&range ) );
chunk.range = Range( range );
fixit_.chunks.push_back( chunk );
}
}
} // namespace YouCompleteMe
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/ClangCompleter/CompletionData.h 0000664 0000000 0000000 00000007573 13601473553 0024521 0 ustar 00root root 0000000 0000000 // Copyright (C) 2011-2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#ifndef COMPLETIONDATA_H_2JCTF1NU
#define COMPLETIONDATA_H_2JCTF1NU
#include "FixIt.h"
namespace YouCompleteMe {
enum class CompletionKind {
STRUCT = 0,
CLASS,
ENUM,
TYPE,
MEMBER,
FUNCTION,
VARIABLE,
MACRO,
PARAMETER,
NAMESPACE,
UNKNOWN
};
// This class holds pieces of information about a single completion coming from
// clang. These pieces are shown in Vim's UI in different ways.
//
// Normally, the completion menu looks like this (without square brackets):
//
// [main completion text] [kind] [extra menu info]
// [main completion text] [kind] [extra menu info]
// [main completion text] [kind] [extra menu info]
// ... (etc.) ...
//
// The user can also enable a "preview" window that will show extra information
// about a completion at the top of the buffer.
struct CompletionData {
CompletionData() = default;
CompletionData( CXCompletionString completion_string,
CXCursorKind kind,
CXCodeCompleteResults *results,
size_t index );
// What should actually be inserted into the buffer. For a function like
// "int foo(int x)", this is just "foo". Same for a data member like "foo_":
// we insert just "foo_".
std::string TextToInsertInBuffer() const {
return original_string_;
}
// Currently, here we show the full function signature (without the return
// type) if the current completion is a function or just the raw TypedText if
// the completion is, say, a data member. So for a function like "int foo(int
// x)", this would be "foo(int x)". For a data member like "count_", it would
// be just "count_".
std::string MainCompletionText() const {
return everything_except_return_type_;
}
// This is extra info shown in the pop-up completion menu, after the
// completion text and the kind. Currently we put the return type of the
// function here, if any.
std::string ExtraMenuInfo() const {
return return_type_;
}
// This is used to show extra information in vim's preview window. This is the
// window that vim usually shows at the top of the buffer. This should be used
// for extra information about the completion.
std::string DetailedInfoForPreviewWindow() const {
return detailed_info_;
}
std::string DocString() const {
return doc_string_;
}
std::string detailed_info_;
std::string return_type_;
CompletionKind kind_;
// The original, raw completion string. For a function like "int foo(int x)",
// the original string is "foo". For a member data variable like "foo_", this
// is just "foo_". This corresponds to clang's TypedText chunk of the
// completion string.
std::string original_string_;
std::string everything_except_return_type_;
std::string doc_string_;
FixIt fixit_;
private:
void ExtractDataFromChunk( CXCompletionString completion_string,
size_t chunk_num,
bool &saw_left_paren,
bool &saw_function_params,
bool &saw_placeholder );
void BuildCompletionFixIt( CXCodeCompleteResults *results, size_t index );
};
} // namespace YouCompleteMe
#endif /* end of include guard: COMPLETIONDATA_H_2JCTF1NU */
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/ClangCompleter/Diagnostic.h 0000664 0000000 0000000 00000002657 13601473553 0023700 0 ustar 00root root 0000000 0000000 // Copyright (C) 2011-2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#ifndef DIAGNOSTIC_H_BZH3BWIZ
#define DIAGNOSTIC_H_BZH3BWIZ
#include "FixIt.h"
namespace YouCompleteMe {
enum class DiagnosticKind {
INFORMATION = 0,
ERROR,
WARNING
};
struct Diagnostic {
Location location_;
Range location_extent_;
std::vector< Range > ranges_;
DiagnosticKind kind_;
std::string text_;
std::string long_formatted_text_;
/// The (cached) changes required to fix this diagnostic.
/// Note: when there are child diagnostics, there may be multiple possible
/// FixIts for the main reported diagnostic. These are typically notes,
/// offering alternative ways to fix the error.
std::vector< FixIt > fixits_;
};
} // namespace YouCompleteMe
#endif /* end of include guard: DIAGNOSTIC_H_BZH3BWIZ */
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/ClangCompleter/Documentation.cpp 0000664 0000000 0000000 00000003161 13601473553 0024747 0 ustar 00root root 0000000 0000000 // Copyright (C) 2015-2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#include "Documentation.h"
#include "ClangHelpers.h"
#include
namespace YouCompleteMe {
namespace {
bool CXCommentValid( const CXComment &comment ) {
return clang_Comment_getKind( comment ) != CXComment_Null;
}
} // unnamed namespace
DocumentationData::DocumentationData( const CXCursor &cursor )
: raw_comment( CXStringToString(
clang_Cursor_getRawCommentText( cursor ) ) ),
brief_comment( CXStringToString(
clang_Cursor_getBriefCommentText( cursor ) ) ),
canonical_type( CXStringToString(
clang_getTypeSpelling( clang_getCursorType( cursor ) ) ) ),
display_name( CXStringToString( clang_getCursorSpelling( cursor ) ) ) {
CXComment parsed_comment = clang_Cursor_getParsedComment( cursor );
if ( CXCommentValid( parsed_comment ) ) {
comment_xml = CXStringToString(
clang_FullComment_getAsXML( parsed_comment ) );
}
}
} // namespace YouCompleteMe
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/ClangCompleter/Documentation.h 0000664 0000000 0000000 00000004241 13601473553 0024414 0 ustar 00root root 0000000 0000000 // Copyright (C) 2015 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#ifndef DOCUMENTATION_H_POYSHVX8
#define DOCUMENTATION_H_POYSHVX8
#include
#include
namespace YouCompleteMe {
/// This class holds information useful for generating a documentation response
/// for a given cursor
struct DocumentationData {
/// Construct an empty object
DocumentationData() = default;
/// Construct and extract information from the supplied cursor. The cursor
/// should be pointing to a canonical declaration, such as returned by
/// clang_getCanonicalCursor( clang_getCursorReferenced( cursor ) )
explicit DocumentationData( const CXCursor &cursor );
/// XML data as parsed by libclang. This provides full semantic parsing of
/// doxygen-syntax comments.
std::string comment_xml;
/// The raw text of the comment preceding the declaration
std::string raw_comment;
/// The brief comment (either first paragraph or \brief) as parsed by libclang
std::string brief_comment;
/// The canonical type of the referenced cursor
std::string canonical_type;
/// The display name of the referenced cursor
std::string display_name;
bool operator == ( const DocumentationData &other ) const {
return comment_xml == other.comment_xml &&
raw_comment == other.raw_comment &&
brief_comment == other.brief_comment &&
canonical_type == other.canonical_type &&
display_name == other.display_name;
}
};
} // namespace YouCompleteMe
#endif /* end of include guard: DOCUMENTATION_H_POYSHVX8 */
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/ClangCompleter/FixIt.h 0000664 0000000 0000000 00000003557 13601473553 0022637 0 ustar 00root root 0000000 0000000 // Copyright (C) 2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#ifndef FIXIT_H_LHWQA2O9
#define FIXIT_H_LHWQA2O9
#include "Location.h"
#include "Range.h"
#include
#include
namespace YouCompleteMe {
/// Information about a replacement that can be made to the source to "fix" a
/// diagnostic.
struct FixItChunk {
/// The replacement string. This string should replace the source range
/// represented by 'range'.
std::string replacement_text;
/// The range within the file to replace with replacement_text.
Range range;
};
/// Collection of FixItChunks which, when applied together, fix a particular
/// diagnostic. This structure forms the reply to the "FixIt" subcommand, and
/// represents a lightweight view of a diagnostic. The location is included to
/// aid clients in applying the most appropriate FixIt based on context.
struct FixIt {
std::vector< FixItChunk > chunks;
Location location;
/// This is the text of the diagnostic. This is useful when there are
/// multiple diagnostics offering different fixit options. The text is
/// displayed to the user, allowing them choose which diagnostic to apply.
std::string text;
};
} // namespace YouCompleteMe
#endif /* end of include guard: FIXIT_H_LHWQA2O9 */
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/ClangCompleter/Location.h 0000664 0000000 0000000 00000004003 13601473553 0023347 0 ustar 00root root 0000000 0000000 // Copyright (C) 2011-2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#ifndef LOCATION_H_6TLFQH4I
#define LOCATION_H_6TLFQH4I
#include "ClangUtils.h"
#include
#include
namespace YouCompleteMe {
struct Location {
// Creates an invalid location
Location()
: line_number_( 0 ),
column_number_( 0 ),
filename_( "" ) {
}
Location( const std::string &filename,
unsigned int line,
unsigned int column )
: line_number_( line ),
column_number_( column ),
filename_( filename ) {
}
explicit Location( const CXSourceLocation &location ) {
CXFile file;
unsigned int unused_offset;
clang_getExpansionLocation( location,
&file,
&line_number_,
&column_number_,
&unused_offset );
filename_ = CXFileToFilepath( file );
}
bool operator== ( const Location &other ) const {
return line_number_ == other.line_number_ &&
column_number_ == other.column_number_ &&
filename_ == other.filename_;
}
bool IsValid() const {
return !filename_.empty();
}
unsigned int line_number_;
unsigned int column_number_;
// The full, absolute path
std::string filename_;
};
} // namespace YouCompleteMe
#endif /* end of include guard: LOCATION_H_6TLFQH4I */
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/ClangCompleter/Range.cpp 0000664 0000000 0000000 00000001660 13601473553 0023174 0 ustar 00root root 0000000 0000000 // Copyright (C) 2013 Google Inc.
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#include "Range.h"
#include "ClangUtils.h"
namespace YouCompleteMe {
Range::Range( const CXSourceRange &range ) {
start_ = Location( clang_getRangeStart( range ) );
end_ = Location( clang_getRangeEnd( range ) );
}
} // namespace YouCompleteMe
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/ClangCompleter/Range.h 0000664 0000000 0000000 00000002232 13601473553 0022635 0 ustar 00root root 0000000 0000000 // Copyright (C) 2013-2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#ifndef RANGE_H_4MFTIGQK
#define RANGE_H_4MFTIGQK
#include "Location.h"
namespace YouCompleteMe {
// Half-open, [start, end>
struct Range {
Range() = default;
Range( const Location &start_location, const Location &end_location )
: start_( start_location ),
end_( end_location ) {
}
explicit Range( const CXSourceRange &range );
Location start_;
Location end_;
};
} // namespace YouCompleteMe
#endif /* end of include guard: RANGE_H_4MFTIGQK */
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/ClangCompleter/TranslationUnit.cpp 0000664 0000000 0000000 00000044017 13601473553 0025301 0 ustar 00root root 0000000 0000000 // Copyright (C) 2011-2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#include "ClangUtils.h"
#include "ClangHelpers.h"
#include "CompletionData.h"
#include "TranslationUnit.h"
#include "Utils.h"
#include
#include
#include
using std::unique_lock;
using std::mutex;
using std::try_to_lock_t;
using std::shared_ptr;
using std::remove_pointer;
namespace YouCompleteMe {
namespace {
unsigned EditingOptions() {
// See cpp/llvm/include/clang-c/Index.h file for detail on these options.
return CXTranslationUnit_DetailedPreprocessingRecord |
CXTranslationUnit_Incomplete |
CXTranslationUnit_IncludeBriefCommentsInCodeCompletion |
CXTranslationUnit_CreatePreambleOnFirstParse |
CXTranslationUnit_KeepGoing |
clang_defaultEditingTranslationUnitOptions();
}
unsigned ReparseOptions( CXTranslationUnit translationUnit ) {
return clang_defaultReparseOptions( translationUnit );
}
unsigned CompletionOptions() {
return clang_defaultCodeCompleteOptions() |
CXCodeComplete_IncludeBriefComments |
CXCodeComplete_IncludeCompletionsWithFixIts;
}
void EnsureCompilerNamePresent( std::vector< const char * > &flags ) {
bool no_compiler_name_set = !flags.empty() && flags.front()[ 0 ] == '-';
if ( flags.empty() || no_compiler_name_set ) {
flags.insert( flags.begin(), "clang" );
}
}
} // unnamed namespace
using CodeCompleteResultsWrap =
shared_ptr< remove_pointer< CXCodeCompleteResults >::type >;
TranslationUnit::TranslationUnit()
: clang_translation_unit_( nullptr ) {
}
TranslationUnit::TranslationUnit(
const std::string &filename,
const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags,
CXIndex clang_index )
: clang_translation_unit_( nullptr ) {
std::vector< const char * > pointer_flags;
pointer_flags.reserve( flags.size() );
for ( const std::string & flag : flags ) {
pointer_flags.push_back( flag.c_str() );
}
EnsureCompilerNamePresent( pointer_flags );
std::vector< CXUnsavedFile > cxunsaved_files =
ToCXUnsavedFiles( unsaved_files );
const CXUnsavedFile *unsaved = cxunsaved_files.empty()
? nullptr : &cxunsaved_files[ 0 ];
// Actually parse the translation unit.
CXErrorCode failure = clang_parseTranslationUnit2FullArgv(
clang_index,
filename.c_str(),
&pointer_flags[ 0 ],
pointer_flags.size(),
const_cast( unsaved ),
cxunsaved_files.size(),
EditingOptions(),
&clang_translation_unit_ );
if ( failure != CXError_Success ) {
throw ClangParseError( failure );
}
}
TranslationUnit::~TranslationUnit() {
Destroy();
}
void TranslationUnit::Destroy() {
unique_lock< mutex > lock( clang_access_mutex_ );
if ( clang_translation_unit_ ) {
clang_disposeTranslationUnit( clang_translation_unit_ );
clang_translation_unit_ = nullptr;
}
}
bool TranslationUnit::IsCurrentlyUpdating() const {
// We return true when the TU is invalid; an invalid TU also acts a sentinel,
// preventing other threads from trying to use it.
if ( !clang_translation_unit_ ) {
return true;
}
unique_lock< mutex > lock( clang_access_mutex_, try_to_lock_t() );
return !lock.owns_lock();
}
std::vector< Diagnostic > TranslationUnit::Reparse(
const std::vector< UnsavedFile > &unsaved_files ) {
std::vector< CXUnsavedFile > cxunsaved_files =
ToCXUnsavedFiles( unsaved_files );
Reparse( cxunsaved_files );
unique_lock< mutex > lock( diagnostics_mutex_ );
return latest_diagnostics_;
}
std::vector< CompletionData > TranslationUnit::CandidatesForLocation(
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files ) {
unique_lock< mutex > lock( clang_access_mutex_ );
if ( !clang_translation_unit_ ) {
return std::vector< CompletionData >();
}
std::vector< CXUnsavedFile > cxunsaved_files =
ToCXUnsavedFiles( unsaved_files );
const CXUnsavedFile *unsaved = cxunsaved_files.empty()
? nullptr : &cxunsaved_files[ 0 ];
// codeCompleteAt reparses the TU if the underlying source file has changed on
// disk since the last time the TU was updated and there are no unsaved files.
// If there are unsaved files, then codeCompleteAt will parse the in-memory
// file contents we are giving it. In short, it is NEVER a good idea to call
// clang_reparseTranslationUnit right before a call to clang_codeCompleteAt.
// This only makes clang reparse the whole file TWICE, which has a huge impact
// on latency. At the time of writing, it seems that most users of libclang
// in the open-source world don't realize this (I checked). Some don't even
// call reparse*, but parse* which is even less efficient.
CodeCompleteResultsWrap results(
clang_codeCompleteAt( clang_translation_unit_,
filename.c_str(),
line,
column,
const_cast( unsaved ),
cxunsaved_files.size(),
CompletionOptions() ),
clang_disposeCodeCompleteResults );
std::vector< CompletionData > candidates = ToCompletionDataVector(
results.get() );
return candidates;
}
Location TranslationUnit::GetDeclarationLocationForCursor( CXCursor cursor ) {
CXCursor referenced_cursor = clang_getCursorReferenced( cursor );
if ( !CursorIsValid( referenced_cursor ) ) {
return Location();
}
CXCursor canonical_cursor = clang_getCanonicalCursor( referenced_cursor );
if ( !CursorIsValid( canonical_cursor ) ) {
return Location( clang_getCursorLocation( referenced_cursor ) );
}
return Location( clang_getCursorLocation( canonical_cursor ) );
}
Location TranslationUnit::GetDeclarationLocation(
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
bool reparse ) {
if ( reparse ) {
Reparse( unsaved_files );
}
unique_lock< mutex > lock( clang_access_mutex_ );
if ( !clang_translation_unit_ ) {
return Location();
}
CXCursor cursor = GetCursor( filename, line, column );
if ( !CursorIsValid( cursor ) ) {
return Location();
}
return GetDeclarationLocationForCursor( cursor );
}
Location TranslationUnit::GetDefinitionLocationForCursor( CXCursor cursor ) {
CXCursor definition_cursor = clang_getCursorDefinition( cursor );
if ( !CursorIsValid( definition_cursor ) ) {
return Location();
}
return Location( clang_getCursorLocation( definition_cursor ) );
}
Location TranslationUnit::GetDefinitionLocation(
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
bool reparse ) {
if ( reparse ) {
Reparse( unsaved_files );
}
unique_lock< mutex > lock( clang_access_mutex_ );
if ( !clang_translation_unit_ ) {
return Location();
}
CXCursor cursor = GetCursor( filename, line, column );
if ( !CursorIsValid( cursor ) ) {
return Location();
}
return GetDefinitionLocationForCursor( cursor );
}
Location TranslationUnit::GetDefinitionOrDeclarationLocation(
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
bool reparse ) {
if ( reparse ) {
Reparse( unsaved_files );
}
unique_lock< mutex > lock( clang_access_mutex_ );
if ( !clang_translation_unit_ ) {
return Location();
}
CXCursor cursor = GetCursor( filename, line, column );
if ( !CursorIsValid( cursor ) ) {
return Location();
}
// Return the definition or the declaration of a symbol under the cursor
// according to the following logic:
// - if the cursor is already on the definition, return the location of the
// declaration;
// - otherwise, search for the definition and return its location;
// - if no definition is found, return the location of the declaration.
if ( clang_isCursorDefinition( cursor ) ) {
return GetDeclarationLocationForCursor( cursor );
}
Location location = GetDefinitionLocationForCursor( cursor );
if ( location.IsValid() ) {
return location;
}
return GetDeclarationLocationForCursor( cursor );
}
std::string TranslationUnit::GetTypeAtLocation(
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
bool reparse ) {
if ( reparse ) {
Reparse( unsaved_files );
}
unique_lock< mutex > lock( clang_access_mutex_ );
if ( !clang_translation_unit_ ) {
return "Internal error: no translation unit";
}
CXCursor cursor = GetCursor( filename, line, column );
if ( !CursorIsValid( cursor ) ) {
return "Internal error: cursor not valid";
}
// Cursors on member functions return a rather unhelpful type text of
// "bound member function type". To get a meaningful type, we must examine
// the referenced cursor. We must be careful though, as both member variables
// and member functions are of kind MemberRefExpr, and getting the referenced
// cursor of a cv-qualified type discards the cv-qualification.
if ( clang_getCursorKind( cursor ) == CXCursor_MemberRefExpr ) {
CXCursor ref = clang_getCursorReferenced( cursor );
if ( clang_getCursorKind( ref ) == CXCursor_CXXMethod ) {
cursor = ref;
}
}
CXType type = clang_getCursorType( cursor );
std::string type_description =
CXStringToString( clang_getTypeSpelling( type ) );
if ( type_description.empty() ) {
return "Unknown type";
}
// We have a choice here; libClang provides clang_getCanonicalType which will
// return the "underlying" type for the type returned by clang_getCursorType
// e.g. for a typedef
// type = clang_getCanonicalType( type );
//
// Without the above, something like the following would return "MyType"
// rather than int:
// typedef int MyType;
// MyType i = 100; <-- type = MyType, canonical type = int
//
// There is probably more semantic value in calling it MyType. Indeed, if we
// opt for the more specific type, we can get very long or
// confusing STL types even for simple usage. e.g. the following:
// std::string test = "test"; <-- type = std::string;
// canonical type = std::basic_string
//
// So as a compromise, we return both if and only if the types differ, like
// std::string => std::basic_string
CXType canonical_type = clang_getCanonicalType( type );
if ( !clang_equalTypes( type, canonical_type ) ) {
std::string canonical_type_description = CXStringToString(
clang_getTypeSpelling( canonical_type ) );
// Clang may return that the canonical type of a symbol is distinct from its
// type even though they result in the same string. Only append the
// canonical type if the strings are different.
if ( type_description != canonical_type_description ) {
type_description += " => " + canonical_type_description;
}
}
return type_description;
}
std::string TranslationUnit::GetEnclosingFunctionAtLocation(
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
bool reparse ) {
if ( reparse ) {
Reparse( unsaved_files );
}
unique_lock< mutex > lock( clang_access_mutex_ );
if ( !clang_translation_unit_ ) {
return "Internal error: no translation unit";
}
CXCursor cursor = GetCursor( filename, line, column );
if ( !CursorIsValid( cursor ) ) {
return "Internal error: cursor not valid";
}
CXCursor parent = clang_getCursorSemanticParent( cursor );
std::string parent_str =
CXStringToString( clang_getCursorDisplayName( parent ) );
if ( parent_str.empty() ) {
return "Unknown semantic parent";
}
return parent_str;
}
// Argument taken as non-const ref because we need to be able to pass a
// non-const pointer to clang. This function (and clang too) will not modify the
// param though.
void TranslationUnit::Reparse(
std::vector< CXUnsavedFile > &unsaved_files ) {
unsigned options = ( clang_translation_unit_
? ReparseOptions( clang_translation_unit_ )
: static_cast( CXReparse_None ) );
Reparse( unsaved_files, options );
}
// Argument taken as non-const ref because we need to be able to pass a
// non-const pointer to clang. This function (and clang too) will not modify the
// param though.
void TranslationUnit::Reparse( std::vector< CXUnsavedFile > &unsaved_files,
size_t parse_options ) {
CXErrorCode failure;
{
unique_lock< mutex > lock( clang_access_mutex_ );
if ( !clang_translation_unit_ ) {
return;
}
CXUnsavedFile *unsaved = unsaved_files.empty()
? nullptr : &unsaved_files[ 0 ];
// This function should technically return a CXErrorCode enum but return an
// int instead.
failure = static_cast< CXErrorCode >(
clang_reparseTranslationUnit( clang_translation_unit_,
unsaved_files.size(),
unsaved,
parse_options ) );
}
if ( failure != CXError_Success ) {
Destroy();
throw ClangParseError( failure );
}
UpdateLatestDiagnostics();
}
void TranslationUnit::UpdateLatestDiagnostics() {
unique_lock< mutex > lock1( clang_access_mutex_ );
unique_lock< mutex > lock2( diagnostics_mutex_ );
latest_diagnostics_.clear();
size_t num_diagnostics = clang_getNumDiagnostics( clang_translation_unit_ );
latest_diagnostics_.reserve( num_diagnostics );
for ( size_t i = 0; i < num_diagnostics; ++i ) {
Diagnostic diagnostic =
BuildDiagnostic(
DiagnosticWrap( clang_getDiagnostic( clang_translation_unit_, i ),
clang_disposeDiagnostic ),
clang_translation_unit_ );
if ( diagnostic.kind_ != DiagnosticKind::INFORMATION ) {
latest_diagnostics_.push_back( diagnostic );
}
}
}
namespace {
/// Sort a FixIt container by its location's distance from a given column
/// (such as the cursor location).
///
/// PreCondition: All FixIts in the container are on the same line.
struct sort_by_location {
explicit sort_by_location( int column ) : column_( column ) { }
bool operator()( const FixIt &a, const FixIt &b ) {
int a_distance = a.location.column_number_ - column_;
int b_distance = b.location.column_number_ - column_;
return std::abs( a_distance ) < std::abs( b_distance );
}
private:
int column_;
};
} // unnamed namespace
std::vector< FixIt > TranslationUnit::GetFixItsForLocationInFile(
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
bool reparse ) {
if ( reparse ) {
Reparse( unsaved_files );
}
std::vector< FixIt > fixits;
auto normal_filename = NormalizePath( filename );
{
unique_lock< mutex > lock( diagnostics_mutex_ );
for ( const Diagnostic& diagnostic : latest_diagnostics_ ) {
auto this_filename = NormalizePath( diagnostic.location_.filename_ );
if ( normal_filename != this_filename ) {
continue;
}
// Find all diagnostics for the supplied line which have FixIts attached
if ( diagnostic.location_.line_number_ !=
static_cast< size_t >( line ) ) {
continue;
}
fixits.insert( fixits.end(),
diagnostic.fixits_.begin(),
diagnostic.fixits_.end() );
}
}
// Sort them by the distance to the supplied column
std::sort( fixits.begin(),
fixits.end(),
sort_by_location( column ) );
return fixits;
}
DocumentationData TranslationUnit::GetDocsForLocation(
const Location &location,
const std::vector< UnsavedFile > &unsaved_files,
bool reparse ) {
if ( reparse ) {
Reparse( unsaved_files );
}
unique_lock< mutex > lock( clang_access_mutex_ );
if ( !clang_translation_unit_ ) {
return DocumentationData();
}
CXCursor cursor = GetCursor( location.filename_,
location.line_number_,
location.column_number_ );
if ( !CursorIsValid( cursor ) ) {
return DocumentationData();
}
return DocumentationData( cursor );
}
bool TranslationUnit::LocationIsInSystemHeader( const Location &location ) {
unique_lock< mutex > lock( clang_access_mutex_ );
if ( !clang_translation_unit_ || !location.IsValid() ) {
return false;
}
return clang_Location_isInSystemHeader(
GetSourceLocation( location.filename_,
location.line_number_,
location.column_number_ ) );
}
CXSourceLocation TranslationUnit::GetSourceLocation(
const std::string &filename,
int line,
int column ) {
// ASSUMES A LOCK IS ALREADY HELD ON clang_access_mutex_ AND THE TU IS VALID!
CXFile file = clang_getFile( clang_translation_unit_, filename.c_str() );
return clang_getLocation( clang_translation_unit_, file, line, column );
}
CXCursor TranslationUnit::GetCursor( const std::string &filename,
int line,
int column ) {
// ASSUMES A LOCK IS ALREADY HELD ON clang_access_mutex_ AND THE TU IS VALID!
return clang_getCursor( clang_translation_unit_,
GetSourceLocation( filename, line, column ) );
}
} // namespace YouCompleteMe
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/ClangCompleter/TranslationUnit.h 0000664 0000000 0000000 00000010426 13601473553 0024743 0 ustar 00root root 0000000 0000000 // Copyright (C) 2011, 2012 Google Inc.
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#ifndef TRANSLATIONUNIT_H_XQ7I6SVA
#define TRANSLATIONUNIT_H_XQ7I6SVA
#include "Diagnostic.h"
#include "Documentation.h"
#include "Location.h"
#include "UnsavedFile.h"
#include
#include
#include
#include
namespace YouCompleteMe {
struct CompletionData;
class TranslationUnit {
public:
// This constructor creates an invalid, sentinel TU. All of it's methods
// return empty vectors, and IsCurrentlyUpdating always returns true so that
// no callers try to rely on the invalid TU.
YCM_EXPORT TranslationUnit();
TranslationUnit( const TranslationUnit& ) = delete;
TranslationUnit& operator=( const TranslationUnit& ) = delete;
YCM_EXPORT TranslationUnit(
const std::string &filename,
const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags,
CXIndex clang_index );
YCM_EXPORT ~TranslationUnit();
void Destroy();
YCM_EXPORT bool IsCurrentlyUpdating() const;
YCM_EXPORT std::vector< Diagnostic > Reparse(
const std::vector< UnsavedFile > &unsaved_files );
YCM_EXPORT std::vector< CompletionData > CandidatesForLocation(
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files );
YCM_EXPORT Location GetDeclarationLocation(
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
bool reparse = true );
YCM_EXPORT Location GetDefinitionLocation(
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
bool reparse = true );
YCM_EXPORT Location GetDefinitionOrDeclarationLocation(
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
bool reparse = true );
YCM_EXPORT std::string GetTypeAtLocation(
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
bool reparse = true );
YCM_EXPORT std::string GetEnclosingFunctionAtLocation(
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
bool reparse = true );
std::vector< FixIt > GetFixItsForLocationInFile(
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files,
bool reparse = true );
YCM_EXPORT DocumentationData GetDocsForLocation(
const Location &location,
const std::vector< UnsavedFile > &unsaved_files,
bool reparse = true );
bool LocationIsInSystemHeader( const Location &location );
private:
void Reparse( std::vector< CXUnsavedFile > &unsaved_files );
void Reparse( std::vector< CXUnsavedFile > &unsaved_files,
size_t parse_options );
void UpdateLatestDiagnostics();
// These four methods must be called under the clang_access_mutex_ lock.
CXSourceLocation GetSourceLocation( const std::string& filename,
int line,
int column );
CXCursor GetCursor( const std::string& filename, int line, int column );
Location GetDeclarationLocationForCursor( CXCursor cursor );
Location GetDefinitionLocationForCursor( CXCursor cursor );
/////////////////////////////
// PRIVATE MEMBER VARIABLES
/////////////////////////////
std::mutex diagnostics_mutex_;
std::vector< Diagnostic > latest_diagnostics_;
mutable std::mutex clang_access_mutex_;
CXTranslationUnit clang_translation_unit_;
};
} // namespace YouCompleteMe
#endif /* end of include guard: TRANSLATIONUNIT_H_XQ7I6SVA */
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/ClangCompleter/TranslationUnitStore.cpp 0000664 0000000 0000000 00000010554 13601473553 0026315 0 ustar 00root root 0000000 0000000 // Copyright (C) 2013 Google Inc.
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#include "TranslationUnitStore.h"
#include "TranslationUnit.h"
#include "Utils.h"
#include
using std::lock_guard;
using std::shared_ptr;
using std::make_shared;
using std::mutex;
namespace YouCompleteMe {
namespace {
std::size_t HashForFlags( const std::vector< std::string > &flags ) {
// The algorithm has been taken straight from a TR1:
// "Library Extension Technical Report - Issue List" section 6.18.
// This is also the way Boost implements it.
size_t seed = 0;
for ( const auto &flag : flags ) {
seed ^= std::hash< std::string >()( flag ) + ( seed << 6 ) + ( seed >> 2 );
}
return seed;
}
} // unnamed namespace
TranslationUnitStore::TranslationUnitStore( CXIndex clang_index )
: clang_index_( clang_index ) {
}
TranslationUnitStore::~TranslationUnitStore() {
RemoveAll();
}
shared_ptr< TranslationUnit > TranslationUnitStore::GetOrCreate(
const std::string &filename,
const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags ) {
bool dont_care;
return GetOrCreate( filename, unsaved_files, flags, dont_care );
}
shared_ptr< TranslationUnit > TranslationUnitStore::GetOrCreate(
const std::string &filename,
const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags,
bool &translation_unit_created ) {
translation_unit_created = false;
{
lock_guard< mutex > lock( filename_to_translation_unit_and_flags_mutex_ );
shared_ptr< TranslationUnit > current_unit = GetNoLock( filename );
if ( current_unit &&
HashForFlags( flags ) == filename_to_flags_hash_[ filename ] ) {
return current_unit;
}
// We create and store an invalid, sentinel TU so that other threads don't
// try to create a TU for the same file while we are trying to create this
// TU object. When we are done creating the TU, we will overwrite this value
// with the valid object.
filename_to_translation_unit_[ filename ] =
make_shared< TranslationUnit >();
// We need to store the flags for the sentinel TU so that other threads end
// up returning the sentinel TU while the real one is being created.
filename_to_flags_hash_[ filename ] = HashForFlags( flags );
}
shared_ptr< TranslationUnit > unit;
try {
unit = make_shared< TranslationUnit >( filename,
unsaved_files,
flags,
clang_index_ );
} catch ( const ClangParseError & ) {
Remove( filename );
throw;
}
{
lock_guard< mutex > lock( filename_to_translation_unit_and_flags_mutex_ );
filename_to_translation_unit_[ filename ] = unit;
// Flags have already been stored.
}
translation_unit_created = true;
return unit;
}
shared_ptr< TranslationUnit > TranslationUnitStore::Get(
const std::string &filename ) {
lock_guard< mutex > lock( filename_to_translation_unit_and_flags_mutex_ );
return GetNoLock( filename );
}
bool TranslationUnitStore::Remove( const std::string &filename ) {
lock_guard< mutex > lock( filename_to_translation_unit_and_flags_mutex_ );
Erase( filename_to_flags_hash_, filename );
return Erase( filename_to_translation_unit_, filename );
}
void TranslationUnitStore::RemoveAll() {
lock_guard< mutex > lock( filename_to_translation_unit_and_flags_mutex_ );
filename_to_translation_unit_.clear();
filename_to_flags_hash_.clear();
}
shared_ptr< TranslationUnit > TranslationUnitStore::GetNoLock(
const std::string &filename ) {
return FindWithDefault( filename_to_translation_unit_,
filename,
shared_ptr< TranslationUnit >() );
}
} // namespace YouCompleteMe
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/ClangCompleter/TranslationUnitStore.h 0000664 0000000 0000000 00000005475 13601473553 0025770 0 ustar 00root root 0000000 0000000 // Copyright (C) 2013-2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#ifndef TRANSLATIONUNITSTORE_H_NGN0MCKB
#define TRANSLATIONUNITSTORE_H_NGN0MCKB
#include "TranslationUnit.h"
#include "UnsavedFile.h"
#include
#include
#include
#include
#include
using CXIndex = void*;
namespace YouCompleteMe {
class TranslationUnitStore {
public:
YCM_EXPORT explicit TranslationUnitStore( CXIndex clang_index );
YCM_EXPORT ~TranslationUnitStore();
TranslationUnitStore( const TranslationUnitStore& ) = delete;
TranslationUnitStore& operator=( const TranslationUnitStore& ) = delete;
// You can even call this function for the same filename from multiple
// threads; the TU store will ensure only one TU is created.
YCM_EXPORT std::shared_ptr< TranslationUnit > GetOrCreate(
const std::string &filename,
const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags );
std::shared_ptr< TranslationUnit > GetOrCreate(
const std::string &filename,
const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags,
bool &translation_unit_created );
// Careful here! While GetOrCreate makes sure to take into account the flags
// for the file before returning a stored TU (if the flags changed, the TU is
// not really valid anymore and a new one should be built), this function does
// not. You might end up getting a stale TU.
std::shared_ptr< TranslationUnit > Get( const std::string &filename );
bool Remove( const std::string &filename );
void RemoveAll();
private:
// WARNING: This accesses filename_to_translation_unit_ without a lock!
std::shared_ptr< TranslationUnit > GetNoLock( const std::string &filename );
using TranslationUnitForFilename =
std::unordered_map< std::string, std::shared_ptr< TranslationUnit > >;
using FlagsHashForFilename = std::unordered_map< std::string, std::size_t >;
CXIndex clang_index_;
TranslationUnitForFilename filename_to_translation_unit_;
FlagsHashForFilename filename_to_flags_hash_;
std::mutex filename_to_translation_unit_and_flags_mutex_;
};
} // namespace YouCompleteMe
#endif // TRANSLATIONUNITSTORE_H_NGN0MCKB
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/ClangCompleter/UnsavedFile.h 0000664 0000000 0000000 00000001767 13601473553 0024022 0 ustar 00root root 0000000 0000000 // Copyright (C) 2011-2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#ifndef UNSAVEDFILE_H_0GIYZQL4
#define UNSAVEDFILE_H_0GIYZQL4
#include
struct UnsavedFile {
UnsavedFile() : filename_( "" ), contents_( "" ), length_( 0 ) {}
std::string filename_;
std::string contents_;
unsigned long length_;
};
#endif /* end of include guard: UNSAVEDFILE_H_0GIYZQL4 */
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/CodePoint.cpp 0000664 0000000 0000000 00000007176 13601473553 0021135 0 ustar 00root root 0000000 0000000 // Copyright (C) 2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#include "CodePoint.h"
#include "CodePointRepository.h"
#include
#include
namespace YouCompleteMe {
namespace {
int GetCodePointLength( uint8_t leading_byte ) {
// 0xxxxxxx
if ( ( leading_byte & 0x80 ) == 0x00 ) {
return 1;
}
// 110xxxxx
if ( ( leading_byte & 0xe0 ) == 0xc0 ) {
return 2;
}
// 1110xxxx
if ( ( leading_byte & 0xf0 ) == 0xe0 ) {
return 3;
}
// 11110xxx
if ( ( leading_byte & 0xf8 ) == 0xf0 ) {
return 4;
}
throw UnicodeDecodeError( "Invalid leading byte in code point." );
}
RawCodePoint FindCodePoint( const char *text ) {
#include "UnicodeTable.inc"
// Do a binary search on the array of code points to find the raw code point
// corresponding to the text. If no code point is found, return the default
// raw code point for that text.
const auto& original = code_points.original;
auto first = original.begin();
const auto start = first;
size_t count = original.size();
while ( count > 0 ) {
size_t step = count / 2;
auto it = first + step;
int cmp = std::strcmp( *it, text );
if ( cmp == 0 ) {
size_t index = std::distance( start, it );
return { *it,
code_points.normal[ index ],
code_points.folded_case[ index ],
code_points.swapped_case[ index ],
code_points.is_letter[ index ],
code_points.is_punctuation[ index ],
code_points.is_uppercase[ index ],
code_points.break_property[ index ],
code_points.combining_class[ index ] };
}
if ( cmp < 0 ) {
first = ++it;
count -= step + 1;
} else {
count = step;
}
}
return { text, text, text, text, false, false, false, 0, 0 };
}
} // unnamed namespace
CodePoint::CodePoint( const std::string &code_point )
: CodePoint( FindCodePoint( code_point.c_str() ) ) {
}
CodePoint::CodePoint( RawCodePoint&& code_point )
: normal_( code_point.normal ),
folded_case_( code_point.folded_case ),
swapped_case_( code_point.swapped_case ),
is_letter_( code_point.is_letter ),
is_punctuation_( code_point.is_punctuation ),
is_uppercase_( code_point.is_uppercase ),
break_property_(
static_cast< BreakProperty >( code_point.break_property ) ),
combining_class_( code_point.combining_class ) {
}
CodePointSequence BreakIntoCodePoints( const std::string &text ) {
// NOTE: for efficiency, we don't check if the number of continuation bytes
// and the bytes themselves are valid (they must start with bits '10').
std::vector< std::string > code_points;
for ( auto iter = text.begin(); iter != text.end(); ) {
int length = GetCodePointLength( *iter );
if ( text.end() - iter < length ) {
throw UnicodeDecodeError( "Invalid code point length." );
}
code_points.emplace_back( iter, iter + length );
iter += length;
}
return CodePointRepository::Instance().GetCodePoints( code_points );
}
} // namespace YouCompleteMe
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/CodePoint.h 0000664 0000000 0000000 00000011166 13601473553 0020574 0 ustar 00root root 0000000 0000000 // Copyright (C) 2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#ifndef CODE_POINT_H_3W0LNCLY
#define CODE_POINT_H_3W0LNCLY
#include
#include
#include
namespace YouCompleteMe {
// See
// http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Break_Property_Values
// NOTE: The properties must take the same value as the ones defined in the
// update_unicode.py script.
enum class BreakProperty : uint8_t {
OTHER = 0,
CR = 1,
LF = 2,
CONTROL = 3,
EXTEND = 4,
ZWJ = 5,
REGIONAL_INDICATOR = 6,
PREPEND = 7,
SPACINGMARK = 8,
L = 9,
V = 10,
T = 11,
LV = 12,
LVT = 13,
EXTPICT = 18
};
// This is the structure used to store the data in the Unicode table. See the
// CodePoint class for a description of the members.
struct RawCodePoint {
const char *original;
const char *normal;
const char *folded_case;
const char *swapped_case;
bool is_letter;
bool is_punctuation;
bool is_uppercase;
uint8_t break_property;
uint8_t combining_class;
};
// This class represents a UTF-8 code point. It takes a UTF-8 encoded string
// corresponding to a UTF-8 code point and compute the following properties
// from a Unicode table:
// - the UTF-8 code point itself;
// - its normalized version: two code points (or sequence of code points)
// represent the same character if they have identical normalized version;
// - its case-folded version: identical to the normalized version if the code
// point is caseless;
// - its case-swapped version: lowercase if the code point is uppercase,
// uppercase if the code point is lowercase, identical to the normalized
// version if the code point is caseless;
// - if the code point is a letter;
// - if the code point is a punctuation;
// - if the code point is in uppercase: false if the code point has no
// uppercase version;
// - its breaking property: used to split a word into characters.
// - its combining class: used to sort a sequence of code points according to
// the Canonical Ordering algorithm (see
// https://www.unicode.org/versions/Unicode10.0.0/ch03.pdf#G49591).
class CodePoint {
public:
YCM_EXPORT explicit CodePoint( const std::string &code_point );
// Make class noncopyable
CodePoint( const CodePoint& ) = delete;
CodePoint& operator=( const CodePoint& ) = delete;
CodePoint( CodePoint&& ) = default;
CodePoint& operator=( CodePoint&& ) = default;
inline std::string Normal() const {
return normal_;
}
inline std::string FoldedCase() const {
return folded_case_;
}
inline std::string SwappedCase() const {
return swapped_case_;
}
inline bool IsLetter() const {
return is_letter_;
}
inline bool IsPunctuation() const {
return is_punctuation_;
}
inline bool IsUppercase() const {
return is_uppercase_;
}
inline BreakProperty GetBreakProperty() const {
return break_property_;
}
inline uint8_t CombiningClass() const {
return combining_class_;
}
inline bool operator< ( const CodePoint &other ) const {
return combining_class_ < other.combining_class_;
};
private:
explicit CodePoint( RawCodePoint&& code_point );
std::string normal_;
std::string folded_case_;
std::string swapped_case_;
bool is_letter_;
bool is_punctuation_;
bool is_uppercase_;
BreakProperty break_property_;
uint8_t combining_class_;
};
using CodePointSequence = std::vector< const CodePoint * >;
// Split a UTF-8 encoded string into UTF-8 code points.
YCM_EXPORT CodePointSequence BreakIntoCodePoints( const std::string &text );
// Thrown when an error occurs while decoding a UTF-8 string.
struct YCM_EXPORT UnicodeDecodeError : std::runtime_error {
explicit UnicodeDecodeError( const char *what_arg )
: std::runtime_error( what_arg ) {
}
};
} // namespace YouCompleteMe
#endif /* end of include guard: CODE_POINT_H_3W0LNCLY */
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/CodePointRepository.cpp 0000664 0000000 0000000 00000003767 13601473553 0023237 0 ustar 00root root 0000000 0000000 // Copyright (C) 2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#include "CodePointRepository.h"
#include "CodePoint.h"
#include "Utils.h"
#include
namespace YouCompleteMe {
CodePointRepository &CodePointRepository::Instance() {
static CodePointRepository repo;
return repo;
}
size_t CodePointRepository::NumStoredCodePoints() {
std::lock_guard< std::mutex > locker( code_point_holder_mutex_ );
return code_point_holder_.size();
}
CodePointSequence CodePointRepository::GetCodePoints(
const std::vector< std::string > &code_points ) {
CodePointSequence code_point_objects;
code_point_objects.reserve( code_points.size() );
{
std::lock_guard< std::mutex > locker( code_point_holder_mutex_ );
for ( const std::string & code_point : code_points ) {
std::unique_ptr< CodePoint > &code_point_object = GetValueElseInsert(
code_point_holder_,
code_point,
nullptr );
if ( !code_point_object ) {
code_point_object.reset( new CodePoint( code_point ) );
}
code_point_objects.push_back( code_point_object.get() );
}
}
return code_point_objects;
}
void CodePointRepository::ClearCodePoints() {
code_point_holder_.clear();
}
} // namespace YouCompleteMe
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/CodePointRepository.h 0000664 0000000 0000000 00000004100 13601473553 0022662 0 ustar 00root root 0000000 0000000 // Copyright (C) 2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#ifndef CODE_POINT_REPOSITORY_H_ENE9FWXL
#define CODE_POINT_REPOSITORY_H_ENE9FWXL
#include "CodePoint.h"
#include
#include
#include
#include
#include
namespace YouCompleteMe {
using CodePointHolder = std::unordered_map< std::string,
std::unique_ptr< CodePoint > >;
// This singleton stores already built CodePoint objects for code points that
// were already seen. If CodePoints are requested for previously unseen code
// points, new CodePoint objects are built.
//
// This class is thread-safe.
class CodePointRepository {
public:
YCM_EXPORT static CodePointRepository &Instance();
// Make class noncopyable
CodePointRepository( const CodePointRepository& ) = delete;
CodePointRepository& operator=( const CodePointRepository& ) = delete;
YCM_EXPORT size_t NumStoredCodePoints();
YCM_EXPORT CodePointSequence GetCodePoints(
const std::vector< std::string > &code_points );
// This should only be used to isolate tests and benchmarks.
YCM_EXPORT void ClearCodePoints();
private:
CodePointRepository() = default;
~CodePointRepository() = default;
// This data structure owns all the CodePoint pointers
CodePointHolder code_point_holder_;
std::mutex code_point_holder_mutex_;
};
} // namespace YouCompleteMe
#endif /* end of include guard: CODE_POINT_REPOSITORY_H_ENE9FWXL */
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/IdentifierCompleter.cpp 0000664 0000000 0000000 00000006170 13601473553 0023177 0 ustar 00root root 0000000 0000000 // Copyright (C) 2011-2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#include "IdentifierCompleter.h"
#include "Candidate.h"
#include "IdentifierUtils.h"
#include "Result.h"
#include "Utils.h"
namespace YouCompleteMe {
IdentifierCompleter::IdentifierCompleter(
std::vector< std::string > candidates ) {
identifier_database_.AddIdentifiers( std::move( candidates ), "", "" );
}
IdentifierCompleter::IdentifierCompleter(
std::vector< std::string >&& candidates,
const std::string &filetype,
const std::string &filepath ) {
identifier_database_.AddIdentifiers( std::move( candidates ),
filetype,
filepath );
}
void IdentifierCompleter::AddIdentifiersToDatabase(
std::vector< std::string > new_candidates,
const std::string &filetype,
const std::string &filepath ) {
identifier_database_.AddIdentifiers( std::move( new_candidates ),
filetype,
filepath );
}
void IdentifierCompleter::ClearForFileAndAddIdentifiersToDatabase(
std::vector< std::string > new_candidates,
const std::string &filetype,
const std::string &filepath ) {
identifier_database_.ClearCandidatesStoredForFile( filetype, filepath );
AddIdentifiersToDatabase( std::move( new_candidates ), filetype, filepath );
}
void IdentifierCompleter::AddIdentifiersToDatabaseFromTagFiles(
const std::vector< std::string > &absolute_paths_to_tag_files ) {
for( const std::string & path : absolute_paths_to_tag_files ) {
identifier_database_.AddIdentifiers(
ExtractIdentifiersFromTagsFile( path ) );
}
}
std::vector< std::string > IdentifierCompleter::CandidatesForQuery(
std::string&& query,
const size_t max_candidates ) const {
return CandidatesForQueryAndType( std::move( query ), "", max_candidates );
}
std::vector< std::string > IdentifierCompleter::CandidatesForQueryAndType(
std::string query,
const std::string &filetype,
const size_t max_candidates ) const {
std::vector< Result > results;
identifier_database_.ResultsForQueryAndType( std::move( query ),
filetype,
results,
max_candidates );
std::vector< std::string > candidates;
candidates.reserve( results.size() );
for ( const Result & result : results ) {
candidates.push_back( result.Text() );
}
return candidates;
}
} // namespace YouCompleteMe
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/IdentifierCompleter.h 0000664 0000000 0000000 00000005205 13601473553 0022642 0 ustar 00root root 0000000 0000000 // Copyright (C) 2011, 2012 Google Inc.
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#ifndef COMPLETER_H_7AR4UGXE
#define COMPLETER_H_7AR4UGXE
#include "IdentifierDatabase.h"
#include
#include
namespace YouCompleteMe {
class Candidate;
class IdentifierCompleter {
public:
IdentifierCompleter( const IdentifierCompleter& ) = delete;
IdentifierCompleter& operator=( const IdentifierCompleter& ) = delete;
YCM_EXPORT IdentifierCompleter() = default;
YCM_EXPORT explicit IdentifierCompleter(
std::vector< std::string > candidates );
YCM_EXPORT IdentifierCompleter(
std::vector< std::string >&& candidates,
const std::string &filetype,
const std::string &filepath );
void AddIdentifiersToDatabase(
std::vector< std::string > new_candidates,
const std::string &filetype,
const std::string &filepath );
// Same as above, but clears all identifiers stored for the file before adding
// new identifiers.
void ClearForFileAndAddIdentifiersToDatabase(
std::vector< std::string > new_candidates,
const std::string &filetype,
const std::string &filepath );
YCM_EXPORT void AddIdentifiersToDatabaseFromTagFiles(
const std::vector< std::string > &absolute_paths_to_tag_files );
void AddIdentifiersToDatabaseFromBuffer(
const std::string &buffer_contents,
const std::string &filetype,
const std::string &filepath,
bool collect_from_comments_and_strings );
// Only provided for tests!
YCM_EXPORT std::vector< std::string > CandidatesForQuery(
std::string&& query,
const size_t max_candidates = 0 ) const;
YCM_EXPORT std::vector< std::string > CandidatesForQueryAndType(
std::string query,
const std::string &filetype,
const size_t max_candidates = 0 ) const;
private:
/////////////////////////////
// PRIVATE MEMBER VARIABLES
/////////////////////////////
IdentifierDatabase identifier_database_;
};
} // namespace YouCompleteMe
#endif /* end of include guard: COMPLETER_H_7AR4UGXE */
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/IdentifierDatabase.cpp 0000664 0000000 0000000 00000011162 13601473553 0022746 0 ustar 00root root 0000000 0000000 // Copyright (C) 2013-2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#include "IdentifierDatabase.h"
#include "Candidate.h"
#include "CandidateRepository.h"
#include "IdentifierUtils.h"
#include "Result.h"
#include "Utils.h"
#include
namespace YouCompleteMe {
IdentifierDatabase::IdentifierDatabase()
: candidate_repository_( CandidateRepository::Instance() ) {
}
void IdentifierDatabase::AddIdentifiers(
FiletypeIdentifierMap&& filetype_identifier_map ) {
std::lock_guard< std::mutex > locker( filetype_candidate_map_mutex_ );
for ( auto&& filetype_and_map : filetype_identifier_map ) {
for ( auto&& filepath_and_identifiers : filetype_and_map.second ) {
AddIdentifiersNoLock( std::move( filepath_and_identifiers.second ),
filetype_and_map.first,
filepath_and_identifiers.first );
}
}
}
void IdentifierDatabase::AddIdentifiers(
std::vector< std::string >&& new_candidates,
const std::string &filetype,
const std::string &filepath ) {
std::lock_guard< std::mutex > locker( filetype_candidate_map_mutex_ );
AddIdentifiersNoLock( std::move( new_candidates ), filetype, filepath );
}
void IdentifierDatabase::ClearCandidatesStoredForFile(
const std::string &filetype,
const std::string &filepath ) {
std::lock_guard< std::mutex > locker( filetype_candidate_map_mutex_ );
GetCandidateSet( filetype, filepath ).clear();
}
void IdentifierDatabase::ResultsForQueryAndType(
std::string&& query,
const std::string &filetype,
std::vector< Result > &results,
const size_t max_results ) const {
FiletypeCandidateMap::const_iterator it;
{
std::lock_guard< std::mutex > locker( filetype_candidate_map_mutex_ );
it = filetype_candidate_map_.find( filetype );
if ( it == filetype_candidate_map_.end() ) {
return;
}
}
Word query_object( std::move( query ) );
std::unordered_set< const Candidate * > seen_candidates;
seen_candidates.reserve( candidate_repository_.NumStoredCandidates() );
{
std::lock_guard< std::mutex > locker( filetype_candidate_map_mutex_ );
for ( const auto& path_and_candidates : *it->second ) {
for ( const Candidate * candidate : *path_and_candidates.second ) {
if ( ContainsKey( seen_candidates, candidate ) ) {
continue;
}
seen_candidates.insert( candidate );
if ( candidate->IsEmpty() ||
!candidate->ContainsBytes( query_object ) ) {
continue;
}
Result result = candidate->QueryMatchResult( query_object );
if ( result.IsSubsequence() ) {
results.push_back( result );
}
}
}
}
PartialSort( results, max_results );
}
// WARNING: You need to hold the filetype_candidate_map_mutex_ before calling
// this function and while using the returned set.
std::set< const Candidate * > &IdentifierDatabase::GetCandidateSet(
const std::string &filetype,
const std::string &filepath ) {
std::shared_ptr< FilepathToCandidates > &path_to_candidates =
filetype_candidate_map_[ filetype ];
if ( !path_to_candidates ) {
path_to_candidates.reset( new FilepathToCandidates() );
}
std::shared_ptr< std::set< const Candidate * > > &candidates =
( *path_to_candidates )[ filepath ];
if ( !candidates ) {
candidates.reset( new std::set< const Candidate * >() );
}
return *candidates;
}
// WARNING: You need to hold the filetype_candidate_map_mutex_ before calling
// this function and while using the returned set.
void IdentifierDatabase::AddIdentifiersNoLock(
std::vector< std::string >&& new_candidates,
const std::string &filetype,
const std::string &filepath ) {
std::set< const Candidate *> &candidates =
GetCandidateSet( filetype, filepath );
std::vector< const Candidate * > repository_candidates =
candidate_repository_.GetCandidatesForStrings(
std::move( new_candidates ) );
candidates.insert( repository_candidates.begin(),
repository_candidates.end() );
}
} // namespace YouCompleteMe
ycmd-0+20191222+git2771f6f+ds/cpp/ycm/IdentifierDatabase.h 0000664 0000000 0000000 00000006520 13601473553 0022415 0 ustar 00root root 0000000 0000000 // Copyright (C) 2013-2018 ycmd contributors
//
// This file is part of ycmd.
//
// ycmd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ycmd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ycmd. If not, see .
#ifndef IDENTIFIERDATABASE_H_ZESX3CVR
#define IDENTIFIERDATABASE_H_ZESX3CVR
#include