pax_global_header00006660000000000000000000000064137463246010014520gustar00rootroot0000000000000052 comment=d0065cbda31ca3500fb8bf013d139f3df6e1f3f8 ycmd-0+20201028+git1d415c5+ds/000077500000000000000000000000001374632460100151375ustar00rootroot00000000000000ycmd-0+20201028+git1d415c5+ds/.clang-tidy000066400000000000000000000061361374632460100172010ustar00rootroot00000000000000--- 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+20201028+git1d415c5+ds/.coveragerc000066400000000000000000000002221374632460100172540ustar00rootroot00000000000000[report] omit = */third_party/requests/* */tests/* */__init__.py [run] parallel = True source = ycmd [paths] source = ycmd/ ycmd-0+20201028+git1d415c5+ds/.gitignore000066400000000000000000000031611374632460100171300ustar00rootroot00000000000000# Editor temp files *.sw[a-z] *.un~ # 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/node_modules third_party/tsserver/package-lock.json # clangd local installation third_party/clangd # Rust third_party/rust-analyzer 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/ # installation directory for gopls third_party/go third_party/regex-build ycmd-0+20201028+git1d415c5+ds/.gitmodules000066400000000000000000000027751374632460100173270ustar00rootroot00000000000000[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/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/watchdog_deps/pathtools"] path = third_party/watchdog_deps/pathtools url = https://github.com/gorakhargosh/pathtools [submodule "third_party/watchdog"] path = third_party/watchdog_deps/watchdog url = https://github.com/gorakhargosh/watchdog [submodule "third_party/mrab-regex"] path = third_party/mrab-regex url = https://bitbucket.org/mrabarnett/mrab-regex.git ycmd-0+20201028+git1d415c5+ds/.mergify.yml000066400000000000000000000013011374632460100173750ustar00rootroot00000000000000pull_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+20201028+git1d415c5+ds/.vimspector.json000066400000000000000000000025561374632460100203130ustar00rootroot00000000000000{ "$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json", "configurations": { "python - launch pytest": { "adapter": "debugpy", "variables": [ { "python": { "shell": "/bin/bash -c 'if [ -z \"${dollar}VIRTUAL_ENV\" ]; then echo $$(which python3); else echo \"${dollar}VIRTUAL_ENV/bin/python\"; 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": true, "debugOptions": [], "module": "pytest", "python": "${python}", "args": [ "-v", "--ignore=ycmd/tests/python/testdata", "${Test}" ], "env": { "PYTHONPATH": "${python_path}", "LD_LIBRARY_PATH": "${workspaceRoot}/third_party/clang/lib", "YCM_TEST_NO_RETRY": "1" } } }, "python - attach": { "adapter": "multi-session", "configuration": { "request": "attach" } } } } ycmd-0+20201028+git1d415c5+ds/.ycm_extra_conf.py000066400000000000000000000170041374632460100205710ustar00rootroot00000000000000# 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 DIR_OF_THIS_SCRIPT = p.abspath( p.dirname( __file__ ) ) DIR_OF_THIRD_PARTY = p.join( DIR_OF_THIS_SCRIPT, 'third_party' ) DIR_OF_WATCHDOG_DEPS = p.join( DIR_OF_THIRD_PARTY, 'watchdog_deps' ) SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ] database = None # 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/googlemock/include', '-isystem', 'cpp/ycm/tests/gmock/googletest/include', '-isystem', 'cpp/ycm/benchmarks/benchmark/include', '-std=c++17', ] # 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 = '' 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() except OSError: return None def Settings( **kwargs ): # Do NOT import ycm_core at module scope. import ycm_core global database if database is None and p.exists( compilation_database_folder ): database = ycm_core.CompilationDatabase( compilation_database_folder ) 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 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[ 0:0 ] = [ p.join( DIR_OF_THIS_SCRIPT ), p.join( DIR_OF_THIRD_PARTY, 'bottle' ), p.join( DIR_OF_THIRD_PARTY, 'regex-build' ), p.join( DIR_OF_THIRD_PARTY, 'frozendict' ), p.join( DIR_OF_THIRD_PARTY, 'jedi_deps', 'jedi' ), 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_WATCHDOG_DEPS, 'watchdog', 'build', 'lib3' ), p.join( DIR_OF_WATCHDOG_DEPS, 'pathtools' ), p.join( DIR_OF_THIRD_PARTY, 'waitress' ) ] sys_path.append( p.join( DIR_OF_THIRD_PARTY, 'jedi_deps', 'numpydoc' ) ) return sys_path ycmd-0+20201028+git1d415c5+ds/CODE_OF_CONDUCT.md000066400000000000000000000045211374632460100177400ustar00rootroot00000000000000# 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+20201028+git1d415c5+ds/CONTRIBUTING.md000066400000000000000000000164031374632460100173740ustar00rootroot00000000000000Writing 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?_ [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 ycmd-0+20201028+git1d415c5+ds/COPYING.txt000066400000000000000000001045131374632460100170140ustar00rootroot00000000000000 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+20201028+git1d415c5+ds/CORE_VERSION000066400000000000000000000000031374632460100170100ustar00rootroot0000000000000044 ycmd-0+20201028+git1d415c5+ds/CheckJavaVersion.java000066400000000000000000000002701374632460100211660ustar00rootroot00000000000000class CheckJavaVersion { public static void main( String[] args ) { int featureVersion = java.lang.Runtime.version().feature(); System.out.println( featureVersion ); } } ycmd-0+20201028+git1d415c5+ds/DEBUG.md000066400000000000000000000071131374632460100163110ustar00rootroot00000000000000# 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+20201028+git1d415c5+ds/JAVA_SUPPORT.md000066400000000000000000000240321374632460100174370ustar00rootroot00000000000000This 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 communication 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 asynchronous 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 dispatch 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 habit 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+20201028+git1d415c5+ds/README.md000066400000000000000000000531341374632460100164240ustar00rootroot00000000000000ycmd: a code-completion & comprehension server ============================================== [![Build status](https://dev.azure.com/YouCompleteMe/YCM/_apis/build/status/ycm-core.ycmd?branchName=master)](https://dev.azure.com/YouCompleteMe/YCM/_build/latest?definitionId=4&branchName=master) [![Coverage status](https://img.shields.io/codecov/c/github/ycm-core/ycmd/master.svg)](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 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 [gopls][gopls]-based completer for Go (using [gopls][gopls] 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; - 7: the version of the `ycm_core` library is outdated. - 8: server is started with python; recompile with python3. 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. A `config_sections` object is a dictionary whose keys are "sections" and values are pieces of settings (usually found in the `ls` object) corresponding to those sections. This is even more underspecified and requires trial and error to make it work. It is optional and only useful if you explicitly enable `workspace/configuration` support. Example of LSP configuration: ```python def Settings( **kwargs ): if kwargs[ 'language' ] == 'java': return { 'ls': { 'java.rename.enabled' : False }, # `config_sections` is not used for java... 'config_sections': { 'section0': {} } ``` 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 (optional; mandatory if port not specified) - `port`: optional. If specified, a TCP connection is used to this port. If set to `*`, an unused locall port is selected and made availble in the `cmdline` as `${port}` (see below examples). - `filetypes`: list of supported filetypes. - `project_root_files`: Tells ycmd which files indicate project root. - `capabilities'`: Overrides the default LSP capabilities of ycmd. - If you enable `workspace/configuration` support, check the extra conf details, relevant to LSP servers. ```json { "language_server": [ { "name": "gopls", "cmdline": [ "/path/to/gopls", "-rpc.trace" ], "filetypes": [ "go" ], "project_root_files": [ "go.mod" ] } ] } ``` Or, to use a TCP connection: ```json { "language_server": [ { "name": "godot", "port": "6008", "filetypes": [ "gdscript" ] } ] } ``` Or, to use an unused local port, set `port` to `*` and use `${port}` in the `cmdline`: ```json { "language_server": [ { "name": "someserver", "cmdline": [ "/path/to/some/server", "--port", "${port}" ], "port": "*", "filetypes": [ "somethign" ], "project_root_files": [ "somethingfile" ] } ] } ``` 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 One can also override the root directory, with `project_directory`. ```python def Settings( **kwargs ): return { 'project_directory': 'src/' } # The path may be absolute as well. ``` ##### 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++' ] } ``` ##### Formatting settings The configuration for `Format` subcommand can be specified with an extra conf for [the java subserver][jdtls] and for the typescript subserver. The formatter options can be found below: - [Java configuration][jdt-formatter] - [TSServer configuration][ts-formatter], These servers support custom formatting options 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 [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 [ts-formatter]: https://github.com/Microsoft/TypeScript/blob/master/lib/protocol.d.ts#L2384-L2421 [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+20201028+git1d415c5+ds/TESTS.md000066400000000000000000000106561374632460100163730ustar00rootroot00000000000000# 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 "pytest" 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 `pytest`. 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://pytest-cov.readthedocs.io/en/stable/ ## 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+20201028+git1d415c5+ds/azure-pipelines.yml000066400000000000000000000121661374632460100210040ustar00rootroot00000000000000trigger: branches: include: - '*' pr: branches: include: - '*' 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-20.04' strategy: matrix: 'Python 3.6 without libclang completer': YCM_PYTHON_VERSION: '3.6.3' USE_CLANG_COMPLETER: false 'Python 3.6': YCM_PYTHON_VERSION: '3.6.3' 'Python 3.6 using Clang compiler': YCM_PYTHON_VERSION: '3.6.3' YCM_COMPILER: 'clang' 'C++ benchmark': YCM_PYTHON_VERSION: '3.6.3' YCM_BENCHMARK: true COVERAGE: false 'C++ linting': YCM_PYTHON_VERSION: '3.9.0' YCM_CLANG_TIDY: true COVERAGE: false maxParallel: 5 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.15' strategy: matrix: 'Python 3.6': YCM_PYTHON_VERSION: '3.6.3' 'C++ benchmark': YCM_PYTHON_VERSION: '3.6.3' YCM_BENCHMARK: true maxParallel: 2 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_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.9 64-bit': YCM_PYTHON_INSTALLER_URL: 'https://www.python.org/ftp/python/3.9.0/python-3.9.0-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 3.9 32-bit': YCM_PYTHON_INSTALLER_URL: 'https://www.python.org/ftp/python/3.9.0/python-3.9.0.exe' 'Python 3.9 64-bit': YCM_PYTHON_INSTALLER_URL: 'https://www.python.org/ftp/python/3.9.0/python-3.9.0-amd64.exe' 'C++ benchmark': YCM_PYTHON_INSTALLER_URL: 'https://www.python.org/ftp/python/3.9.0/python-3.9.0-amd64.exe' YCM_BENCHMARK: true COVERAGE: false maxParallel: 3 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+20201028+git1d415c5+ds/azure/000077500000000000000000000000001374632460100162655ustar00rootroot00000000000000ycmd-0+20201028+git1d415c5+ds/azure/README.md000066400000000000000000000003461374632460100175470ustar00rootroot00000000000000Azure 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+20201028+git1d415c5+ds/azure/benchmark.sh000077500000000000000000000012311374632460100205530ustar00rootroot00000000000000# Exit immediately if a command returns a non-zero status. set -e test -d "$HOME/.pyenv/bin" && export PATH="$HOME/.pyenv/bin:$PATH" 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+20201028+git1d415c5+ds/azure/lint.sh000077500000000000000000000014041374632460100175710ustar00rootroot00000000000000# Exit immediately if a command returns a non-zero status. set -e test -d "$HOME/.pyenv/bin" && export PATH="$HOME/.pyenv/bin:$PATH" 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} YCM_TESTRUN=1 python build.py --clang-completer --clang-tidy --valgrind python run_tests.py --valgrind --skip-build --no-flake8 set +e ycmd-0+20201028+git1d415c5+ds/azure/linux/000077500000000000000000000000001374632460100174245ustar00rootroot00000000000000ycmd-0+20201028+git1d415c5+ds/azure/linux/install_dependencies.sh000077500000000000000000000046711374632460100241470ustar00rootroot00000000000000# Exit immediately if a command returns a non-zero status. set -e # # Compiler setup # sudo apt-get update sudo apt-get install libsqlite3-dev if [ "${YCM_COMPILER}" == "clang" ]; then sudo apt-get install clang-7 sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-7 100 sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-7 100 else sudo apt-get install gcc-8 g++-8 sudo update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-8 100 sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++-8 100 fi if [ "${YCM_CLANG_TIDY}" ]; then wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - sudo apt-get update sudo apt-get install -y clang-tidy valgrind sudo update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-10 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 # sudo apt-get install -y build-essential \ libssl-dev \ zlib1g-dev \ libbz2-dev \ libreadline-dev \ libsqlite3-dev \ wget \ curl \ llvm \ libncurses5-dev \ libncursesw5-dev \ xz-utils \ tk-dev \ libffi-dev \ liblzma-dev \ python-openssl \ git curl https://pyenv.run | bash export PATH="$HOME/.pyenv/bin:$PATH" 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 set +e ycmd-0+20201028+git1d415c5+ds/azure/macos/000077500000000000000000000000001374632460100173675ustar00rootroot00000000000000ycmd-0+20201028+git1d415c5+ds/azure/macos/install_dependencies.sh000077500000000000000000000014031374632460100241000ustar00rootroot00000000000000# 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 set +e ycmd-0+20201028+git1d415c5+ds/azure/run_tests.sh000077500000000000000000000020331374632460100206500ustar00rootroot00000000000000# Exit immediately if a command returns a non-zero status. set -e test -d "$HOME/.pyenv/bin" && export PATH="$HOME/.pyenv/bin:$PATH" 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}" # JDT requires Java 11 if [[ -d /usr/lib/jvm/adoptopenjdk-11-hotspot-amd64 ]]; then export JAVA_HOME='/usr/lib/jvm/adoptopenjdk-11-hotspot-amd64' export PATH="${JAVA_HOME}/bin:${PATH}" else export JAVA_HOME='/Library/Java/JavaVirtualMachines/adoptopenjdk-11.jdk/Contents/Home' fi java -version javac -version python run_tests.py set +e ycmd-0+20201028+git1d415c5+ds/azure/send_coverage.sh000077500000000000000000000003571374632460100214350ustar00rootroot00000000000000# Exit immediately if a command returns a non-zero status. set -e test -d "$HOME/.pyenv/bin" && export PATH="$HOME/.pyenv/bin:$PATH" eval "$(pyenv init -)" pyenv global ${YCM_PYTHON_VERSION} codecov --name "${CODECOV_JOB_NAME}" set +e ycmd-0+20201028+git1d415c5+ds/azure/windows/000077500000000000000000000000001374632460100177575ustar00rootroot00000000000000ycmd-0+20201028+git1d415c5+ds/azure/windows/benchmark.bat000077500000000000000000000001311374632460100223770ustar00rootroot00000000000000:: Add Python to PATH set "PATH=C:\Python;C:\Python\Scripts;%PATH%" python benchmark.py ycmd-0+20201028+git1d415c5+ds/azure/windows/install_dependencies.bat000077500000000000000000000011421374632460100246240ustar00rootroot00000000000000:: :: Python configuration :: curl %YCM_PYTHON_INSTALLER_URL% -o C:\python-installer.exe C:\python-installer.exe /quiet TargetDir=C:\Python 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.14.3.windows-amd64.msi -o C:\go-installer.msi msiexec /i C:\go-installer.msi TARGETDIR=C:\Go /qn ycmd-0+20201028+git1d415c5+ds/azure/windows/run_tests.bat000077500000000000000000000007271374632460100225060ustar00rootroot00000000000000:: 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%" :: JDT requires Java 11 set "JAVA_HOME=C:\Program Files\Java\jdk-11.0.8+10" set "PATH=%JAVA_HOME%\bin;%PATH%" java -version javac -version :: Prevent the already installed version of Go to conflict with ours. set GOROOT= python run_tests.py --msvc %MSVC% ycmd-0+20201028+git1d415c5+ds/azure/windows/send_coverage.bat000077500000000000000000000001511374632460100232530ustar00rootroot00000000000000:: Add Python to PATH set "PATH=C:\Python;C:\Python\Scripts;%PATH%" codecov --name "%CODECOV_JOB_NAME%" ycmd-0+20201028+git1d415c5+ds/benchmark.py000077500000000000000000000020751374632460100174520ustar00rootroot00000000000000#!/usr/bin/env python3 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', ] + 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+20201028+git1d415c5+ds/build.py000077500000000000000000001204661374632460100166240ustar00rootroot00000000000000#!/usr/bin/env python3 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 < ( 3, 6, 0 ): sys.exit( 'ycmd requires Python >= 3.6.0; ' 'your version of Python is ' + sys.version + '\nHint: Try running python3 ' + ' '.join( sys.argv ) ) 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.6m.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.63.0' JDTLS_BUILD_STAMP = '202010141717' JDTLS_SHA256 = ( '56eef6b138bdaa60ff8a794d622d390abac8286c0e5e1370616baf9a601fd1c8' ) RUST_TOOLCHAIN = 'nightly-2020-10-05' RUST_ANALYZER_DIR = p.join( DIR_OF_THIRD_PARTY, 'rust-analyzer' ) 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 = '10.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 RemoveDirectoryIfExists( directory_path ): if p.exists( directory_path ): RemoveDirectory( directory_path ) def MakeCleanDirectory( directory_path ): RemoveDirectoryIfExists( 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 = p.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. return sys.base_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 = [ 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( '--valgrind', action = 'store_true', help = 'For developers: ' 'Run core tests inside valgrind.' ) 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 and not args.ninja: 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' ) 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 ) ) if not args.valgrind: CheckCall( tests_cmd, env = new_env, quiet = args.quiet, status_message = 'Running ycmd tests' ) else: new_env[ 'PYTHONMALLOC' ] = 'malloc' cmd = [ 'valgrind', '--gen-suppressions=all', '--error-exitcode=1', '--leak-check=full', '--show-leak-kinds=definite,indirect', '--errors-for-leak-kinds=definite,indirect', '--suppressions=' + p.join( DIR_OF_THIS_SCRIPT, 'valgrind.suppressions' ), p.join( tests_dir, 'ycm_core_tests' ) ] CheckCall( cmd, env = new_env ) def RunYcmdBenchmarks( args, 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( script_args, 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( script_args ): build_dir = p.join( DIR_OF_THIRD_PARTY, 'regex-build', '3' ) lib_dir = p.join( DIR_OF_THIRD_PARTY, 'regex-build' ) try: os.chdir( p.join( DIR_OF_THIRD_PARTY, 'mrab-regex' ) ) RemoveDirectoryIfExists( build_dir ) RemoveDirectoryIfExists( lib_dir ) try: import setuptools # noqa CheckCall( [ sys.executable, 'setup.py', 'build', '--build-base=' + build_dir, '--build-lib=' + lib_dir ], exit_message = 'Failed to build regex module.', quiet = script_args.quiet, status_message = 'Building regex module' ) except ImportError: pass # Swallow the error - ycmd will fall back to the standard `re`. finally: RemoveDirectoryIfExists( build_dir ) os.chdir( DIR_OF_THIS_SCRIPT ) 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 = p.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': { 'version': 'v1.35.4', 'download_url': ( 'https://github.com/OmniSharp/omnisharp-roslyn/release' 's/download/v1.35.4/omnisharp.http-win-x86.zip' ), 'file_name': 'omnisharp.http-win-x86.zip', 'check_sum': ( 'f6a44ec4e9edfbb4cb13626b09859d3dcd9b92e202f00b484d3c5956' '4dfa236b' ), }, 'win64': { 'version': 'v1.35.4', 'download_url': ( 'https://github.com/OmniSharp/omnisharp-roslyn/release' 's/download/v1.35.4/omnisharp.http-win-x64.zip' ), 'file_name': 'omnisharp.http-win-x64.zip', 'check_sum': ( '18ea074d099592c211929754cbc616e9b640b4143d60b20b374e015b' '97932703' ), }, 'macos': { 'version': 'v1.35.4', 'download_url': ( 'https://github.com/OmniSharp/omnisharp-roslyn/release' 's/download/v1.35.4/omnisharp.http-osx.tar.gz' ), 'file_name': 'omnisharp.http-osx.tar.gz', 'check_sum': ( '5e7e4870605ea53c1588d6a11e31a277b062b29477c3486d43a3c609' '99f1cae8' ), }, 'linux32': { 'version': 'v1.35.4', 'download_url': ( 'https://github.com/OmniSharp/omnisharp-roslyn/release' 's/download/v1.35.4/omnisharp.http-linux-x86.tar.gz' ), 'file_name': 'omnisharp.http-linux-x86.tar.gz', 'check_sum': ( '5998daa508e79e2e1f1bbf018ef59a7b82420506cb6fa3fa75a54248' '94f89c19' ), }, 'linux64': { 'version': 'v1.35.4', 'download_url': ( 'https://github.com/OmniSharp/omnisharp-roslyn/release' 's/download/v1.35.4/omnisharp.http-linux-x64.tar.gz' ), 'file_name': 'omnisharp.http-linux-x64.tar.gz', 'check_sum': ( 'a1b89e5cb67afedfc17515eae565c58a31c36d660dde7f15e4de4ef8' '5e464b1c' ), }, } 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 gopls.' ) new_env = os.environ.copy() new_env[ 'GO111MODULE' ] = 'on' new_env[ 'GOPATH' ] = p.join( DIR_OF_THIS_SCRIPT, 'third_party', 'go' ) new_env.pop( 'GOROOT', None ) new_env[ 'GOBIN' ] = p.join( new_env[ 'GOPATH' ], 'bin' ) CheckCall( [ go, 'get', 'golang.org/x/tools/gopls@v0.5.1' ], env = new_env, quiet = args.quiet, status_message = 'Building gopls for go completion' ) def WriteToolchainVersion( version ): path = p.join( RUST_ANALYZER_DIR, 'TOOLCHAIN_VERSION' ) with open( path, 'w' ) as f: f.write( version ) def ReadToolchainVersion(): try: filepath = p.join( RUST_ANALYZER_DIR, 'TOOLCHAIN_VERSION' ) with open( filepath ) as f: return f.read().strip() except OSError: return None def EnableRustCompleter( switches ): if switches.quiet: sys.stdout.write( 'Installing rust-analyzer 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 rustup_init = p.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 = p.join( install_dir, 'bin', 'rustup' ) try: CheckCall( [ rustup, 'toolchain', 'install', RUST_TOOLCHAIN ], env = new_env, quiet = switches.quiet ) for component in [ 'rust-src', 'rust-analyzer-preview', 'rustfmt', 'clippy' ]: 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( RUST_ANALYZER_DIR ): RemoveDirectory( RUST_ANALYZER_DIR ) os.makedirs( RUST_ANALYZER_DIR ) for folder in os.listdir( toolchain_dir ): shutil.move( p.join( toolchain_dir, folder ), RUST_ANALYZER_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.' ) 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 CheckJavaVersion( required_version ): java = FindExecutableOrDie( 'java', f'java { required_version } is required to install JDT.LS' ) java_version = None try: new_env = os.environ.copy() new_env.pop( 'JAVA_TOOL_OPTIONS', None ) java_version = int( subprocess.check_output( [ java, p.join( DIR_OF_THIS_SCRIPT, 'CheckJavaVersion.java' ) ], stderr=subprocess.STDOUT, env = new_env ) .decode( 'utf-8' ) .strip() ) except subprocess.CalledProcessError: pass if java_version is None or java_version < required_version: print( f'\n\n*** WARNING ***: jdt.ls requires Java { required_version }.' ' You must set the option java_binary_path to point to a working ' f'java { required_version }.\n\n' ) 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() CheckJavaVersion( 11 ) 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 ): RemoveDirectoryIfExists( p.join( DIR_OF_THIRD_PARTY, 'tsserver', 'bin' ) ) RemoveDirectoryIfExists( p.join( DIR_OF_THIRD_PARTY, 'tsserver', 'lib' ) ) npm = FindExecutableOrDie( 'npm', 'npm is required to install TSServer.' ) os.chdir( p.join( DIR_OF_THIRD_PARTY, 'tsserver' ) ) CheckCall( [ npm, 'install', '--production' ], quiet = args.quiet, status_message = 'Setting up TSserver for TypeScript completion' ) def GetClangdTarget(): if OnWindows(): return [ ( 'clangd-{version}-win64', '193ebaa9bddb750d3678342de16cd59724e10b919d5da1f78e0202db0c67c339' ), ( 'clangd-{version}-win32', '83d9cb3f18e23558997f6248559095d468dab3e3132b9e12b62401707a085d11' ) ] if OnMac(): return [ ( 'clangd-{version}-x86_64-apple-darwin', '641e07ba19a38a9570260125df6464cd75a3d209c661177bfc8221ceb090bd6a' ) ] if OnFreeBSD(): return [ ( 'clangd-{version}-amd64-unknown-freebsd11', 'eef613abbc5946f489cd5ef369eb29ad78881b08514be780c0a4dc1cec9cdbec' ), ( 'clangd-{version}-i386-unknown-freebsd11', 'aba343ba9bc080b19fa8162018f8e1bf4cc55907a892d50ce8ff7515cf666fd7' ) ] if OnAArch64(): return [ ( 'clangd-{version}-aarch64-linux-gnu', '1ee49413e2216d59f7df994a3b528d9deed2f0d999716ab2d5c584fbb0c1a337' ) ] if OnArm(): return [ None, # First list index is for 64bit archives. ARMv7 is 32bit only. ( 'clangd-{version}-armv7a-linux-gnueabihf', 'db2e81ae6d70bc53d6558181aebc8dcd6d0ec6ae30fee7d533c1fa4a66d6ed4c' ) ] if OnX86_64(): return [ ( 'clangd-{version}-x86_64-unknown-linux-gnu', '36b99e35b00ce7aa20738e33ede95a775ff62e10711130917711a3cab38fd85d' ) ] 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 BuildWatchdogModule( script_args ): DIR_OF_WATCHDOG_DEPS = p.join( DIR_OF_THIRD_PARTY, 'watchdog_deps' ) build_dir = p.join( DIR_OF_WATCHDOG_DEPS, 'watchdog', 'build', '3' ) lib_dir = p.join( DIR_OF_WATCHDOG_DEPS, 'watchdog', 'build', 'lib3' ) try: os.chdir( p.join( DIR_OF_WATCHDOG_DEPS, 'watchdog' ) ) RemoveDirectoryIfExists( build_dir ) RemoveDirectoryIfExists( lib_dir ) try: import setuptools # noqa CheckCall( [ sys.executable, 'setup.py', 'build', '--build-base=' + build_dir, '--build-lib=' + lib_dir ], exit_message = 'Failed to build watchdog module.', quiet = script_args.quiet, status_message = 'Building watchdog module' ) except ImportError: if OnMac(): print( 'WARNING: setuptools unavailable. Watchdog will fall back to ' 'the slower kqueue filesystem event API.\n' 'To use the faster fsevents, install setuptools and ' 'rerun this script.' ) os.makedirs( lib_dir ) shutil.copytree( p.join( 'src', 'watchdog' ), p.join( lib_dir, 'watchdog' ) ) finally: RemoveDirectoryIfExists( build_dir ) os.chdir( DIR_OF_THIS_SCRIPT ) def DoCmakeBuilds( args ): cmake = FindCmake( args ) cmake_common_args = GetCmakeCommonArgs( args ) ExitIfYcmdLibInUseOnWindows() BuildYcmdLib( cmake, cmake_common_args, args ) WritePythonUsedDuringBuild() BuildRegexModule( args ) BuildWatchdogModule( args ) def Main(): args = ParseArguments() if not args.skip_build: 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+20201028+git1d415c5+ds/codecov.yml000066400000000000000000000015211374632460100173030ustar00rootroot00000000000000codecov: 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+20201028+git1d415c5+ds/cpp/000077500000000000000000000000001374632460100157215ustar00rootroot00000000000000ycmd-0+20201028+git1d415c5+ds/cpp/CMakeLists.txt000066400000000000000000000211721374632460100204640ustar00rootroot00000000000000# Copyright (C) 2011-2020 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. # 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. 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++17 support in the compiler set( CPP17_AVAILABLE false ) if ( CMAKE_COMPILER_IS_GNUCXX ) if ( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8 ) set( CPP17_AVAILABLE true ) endif() elseif( COMPILER_IS_CLANG ) if ( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7 ) set( CPP17_AVAILABLE true ) set( CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++17" ) set( CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++" ) endif() elseif( MSVC ) if ( NOT MSVC_VERSION LESS 1914 ) set( CPP17_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 ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /bigobj /utf-8" ) 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 ( CPP17_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++17" ) elseif( NOT MSVC ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17" ) elseif( MSVC ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++17" ) 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-8 or greater. However, we # recommended the devtoolset-8 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-8 or greater. For example, see this link: " "https://www.softwarecollections.org/en/scls/rhscl/devtoolset-8/" ) # Finally, just check if it is installed and they just need to activate it. if ( EXISTS /opt/rh/devtoolset-8 ) message( STATUS "NOTE: It looks like you have the devtoolset-8 " "installed in /opt/rh/devtoolset-8, so you probably " "just need to activate it and re-run the installation. " "For example: source /opt/rh/devtoolset-8/enable") endif() endif() message( FATAL_ERROR "Your C++ compiler does NOT fully support C++17." ) endif() set( Python_ADDITIONAL_VERSIONS 3.10 3.9 3.8 3.7 3.6 ) find_package( PythonLibs 3.6 REQUIRED ) # 3.6 is ONLY the minimum ############################################################################# add_subdirectory( ycm ) ycmd-0+20201028+git1d415c5+ds/cpp/ycm/000077500000000000000000000000001374632460100165115ustar00rootroot00000000000000ycmd-0+20201028+git1d415c5+ds/cpp/ycm/CMakeLists.txt000066400000000000000000000433271374632460100212620ustar00rootroot00000000000000# 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 10.0.0 ) if ( APPLE ) set( LIBCLANG_DIRNAME "libclang-${CLANG_VERSION}-x86_64-apple-darwin" ) set( LIBCLANG_SHA256 "157c755c0d208414203b56057fd4f9b81dba93fd968e4435217ba1e4026136a8" ) elseif ( WIN32 ) if( 64_BIT_PLATFORM ) set( LIBCLANG_DIRNAME "libclang-${CLANG_VERSION}-win64" ) set( LIBCLANG_SHA256 "45a2d797fbf9ff53ad5de3891f20c73cf6e2256761501dd32b60948c5fa84dc8" ) else() set( LIBCLANG_DIRNAME "libclang-${CLANG_VERSION}-win32" ) set( LIBCLANG_SHA256 "9715703fc7f82bc79310296a811e611bc43406faf9102d89ab44ee6a5807a4cb" ) endif() elseif ( SYSTEM_IS_FREEBSD ) if ( 64_BIT_PLATFORM ) set( LIBCLANG_DIRNAME "libclang-${CLANG_VERSION}-amd64-unknown-freebsd11" ) set( LIBCLANG_SHA256 "7e4f7ee6cff7360c65b73955b6fe177f4c896fc2376d86acaa8c835ee9ca0da9" ) else() set( LIBCLANG_DIRNAME "libclang-${CLANG_VERSION}-i386-unknown-freebsd11" ) set( LIBCLANG_SHA256 "0e34866976876af3c8635ee3aab74361c10904ff617250fa199c5fb1c948c881" ) endif() elseif ( CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64.*|AARCH64.*)" ) set( LIBCLANG_DIRNAME "libclang-${CLANG_VERSION}-aarch64-linux-gnu" ) set( LIBCLANG_SHA256 "558cea25b1a7331be13c74b2e14246ac3762c81b68c064a5eaf4e7a1afffebf0" ) elseif ( CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm.*|ARM.*)" ) set( LIBCLANG_DIRNAME "libclang-${CLANG_VERSION}-armv7a-linux-gnueabihf" ) set( LIBCLANG_SHA256 "ea6eb8d3183f55323b13179fa5a884e86c6c23cdab6cb21df8f340de220f4747" ) elseif ( CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64)" ) set( LIBCLANG_DIRNAME "libclang-${CLANG_VERSION}-x86_64-unknown-linux-gnu" ) set( LIBCLANG_SHA256 "23c213ae2dba9cc98d09a4997de60e10f270276335f05f1bacf861678e231809" ) 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() set( PYBIND11_INCLUDES_DIR "${CMAKE_SOURCE_DIR}/pybind11" ) # Makes MSVC conform to the standard if ( MSVC ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /permissive-" ) endif() 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. pybind11 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 ${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() # Check if we need to add -lstdc++fs or -lc++fs or nothing if( MSVC ) set( STD_FS_NO_LIB_NEEDED TRUE ) else() file( WRITE ${CMAKE_CURRENT_BINARY_DIR}/main.cpp "#include \nint main( int argc, char ** argv ) {\n std::filesystem::path p( argv[ 0 ] );\n return p.string().length();\n}" ) try_compile( STD_FS_NO_LIB_NEEDED ${CMAKE_CURRENT_BINARY_DIR} SOURCES ${CMAKE_CURRENT_BINARY_DIR}/main.cpp COMPILE_DEFINITIONS -std=c++17 ) try_compile( STD_FS_NEEDS_STDCXXFS ${CMAKE_CURRENT_BINARY_DIR} SOURCES ${CMAKE_CURRENT_BINARY_DIR}/main.cpp COMPILE_DEFINITIONS -std=c++17 LINK_LIBRARIES stdc++fs ) try_compile( STD_FS_NEEDS_CXXFS ${CMAKE_CURRENT_BINARY_DIR} SOURCES ${CMAKE_CURRENT_BINARY_DIR}/main.cpp COMPILE_DEFINITIONS -std=c++17 LINK_LIBRARIES c++fs ) file( REMOVE ${CMAKE_CURRENT_BINARY_DIR}/main.cpp ) endif() if( ${STD_FS_NEEDS_STDCXXFS} ) set( STD_FS_LIB stdc++fs ) elseif( ${STD_FS_NEEDS_CXXFS} ) set( STD_FS_LIB c++fs ) elseif( ${STD_FS_NO_LIB_NEEDED} ) set( STD_FS_LIB "" ) else() message( WARNING "Unknown compiler - not passing -lstdc++fs" ) set( STD_FS_LIB "" ) 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} ${PYTHON_LIBRARIES} ${LIBCLANG_TARGET} ${STD_FS_LIB} ${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}/../.. ) ############################################################################# if ( USE_DEV_FLAGS AND ( CMAKE_COMPILER_IS_GNUCXX OR COMPILER_IS_CLANG ) ) # 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+20201028+git1d415c5+ds/cpp/ycm/Candidate.cpp000066400000000000000000000102501374632460100210670ustar00rootroot00000000000000// 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+20201028+git1d415c5+ds/cpp/ycm/Candidate.h000066400000000000000000000034471374632460100205460ustar00rootroot00000000000000// 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+20201028+git1d415c5+ds/cpp/ycm/CandidateRepository.cpp000066400000000000000000000044741374632460100232020ustar00rootroot00000000000000// 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 "Utils.h" #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() const { std::shared_lock 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 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 = std::make_unique< Candidate >( std::move( candidate_text ) ); } candidates.push_back( candidate.get() ); } } return candidates; } void CandidateRepository::ClearCandidates() { candidate_holder_.clear(); } } // namespace YouCompleteMe ycmd-0+20201028+git1d415c5+ds/cpp/ycm/CandidateRepository.h000066400000000000000000000043121374632460100226360ustar00rootroot00000000000000// 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() const; 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; // This data structure owns all the Candidate pointers CandidateHolder candidate_holder_; mutable std::shared_mutex candidate_holder_mutex_; }; } // namespace YouCompleteMe #endif /* end of include guard: CANDIDATEREPOSITORY_H_K9OVCMHG */ ycmd-0+20201028+git1d415c5+ds/cpp/ycm/Character.cpp000066400000000000000000000064361374632460100211220ustar00rootroot00000000000000// 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/Unicode13.0.0/ch03.pdf#G49591 CodePointSequence CanonicalSort( CodePointSequence code_points ) { auto code_point_start = code_points.begin(); auto code_point_end = code_points.end(); while ( code_point_start != code_points.end() ) { // Find the first sortable code point code_point_start = std::find_if( code_point_start, code_points.end(), [](const CodePoint* cp ) { return cp->CombiningClass() != 0; } ); // Find the last consecutive sortable code point code_point_end = std::find_if( code_point_start, code_points.end(), []( const CodePoint* cp ) { return cp->CombiningClass() == 0; } ); std::sort( code_point_start, code_point_end, CodePointCompare ); code_point_start = code_point_end; } 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/Unicode13.0.0/ch03.pdf#G733 CodePointSequence CanonicalDecompose( std::string_view 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( std::string_view 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/Unicode13.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+20201028+git1d415c5+ds/cpp/ycm/Character.h000066400000000000000000000063221374632460100205610ustar00rootroot00000000000000// 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 #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/Unicode13.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( std::string_view 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+20201028+git1d415c5+ds/cpp/ycm/CharacterRepository.cpp000066400000000000000000000037011374632460100232120ustar00rootroot00000000000000// 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 "Utils.h" #include namespace YouCompleteMe { CharacterRepository &CharacterRepository::Instance() { static CharacterRepository repo; return repo; } size_t CharacterRepository::NumStoredCharacters() const { std::shared_lock 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 locker( character_holder_mutex_ ); for ( std::string_view character : characters ) { std::unique_ptr< Character > &character_object = GetValueElseInsert( character_holder_, character, nullptr ); if ( !character_object ) { character_object = std::make_unique< Character >( character ); } character_objects.push_back( character_object.get() ); } } return character_objects; } void CharacterRepository::ClearCharacters() { character_holder_.clear(); } } // namespace YouCompleteMe ycmd-0+20201028+git1d415c5+ds/cpp/ycm/CharacterRepository.h000066400000000000000000000041301374632460100226540ustar00rootroot00000000000000// 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() const; 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_; mutable std::shared_mutex character_holder_mutex_; }; } // namespace YouCompleteMe #endif /* end of include guard: CHARACTER_REPOSITORY_H_36TXTS6C */ ycmd-0+20201028+git1d415c5+ds/cpp/ycm/ClangCompleter/000077500000000000000000000000001374632460100214105ustar00rootroot00000000000000ycmd-0+20201028+git1d415c5+ds/cpp/ycm/ClangCompleter/ClangCompleter.cpp000066400000000000000000000235511374632460100250210ustar00rootroot00000000000000// 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 daemon 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+20201028+git1d415c5+ds/cpp/ycm/ClangCompleter/ClangCompleter.h000066400000000000000000000101711374632460100244600ustar00rootroot00000000000000// 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+20201028+git1d415c5+ds/cpp/ycm/ClangCompleter/ClangHelpers.cpp000066400000000000000000000215051374632460100244660ustar00rootroot00000000000000// 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+20201028+git1d415c5+ds/cpp/ycm/ClangCompleter/ClangHelpers.h000066400000000000000000000030251374632460100241300ustar00rootroot00000000000000// 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+20201028+git1d415c5+ds/cpp/ycm/ClangCompleter/ClangUtils.cpp000066400000000000000000000042741374632460100241700ustar00rootroot00000000000000// 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+20201028+git1d415c5+ds/cpp/ycm/ClangCompleter/ClangUtils.h000066400000000000000000000030341374632460100236260ustar00rootroot00000000000000// 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+20201028+git1d415c5+ds/cpp/ycm/ClangCompleter/CompilationDatabase.cpp000066400000000000000000000061111374632460100260160ustar00rootroot00000000000000// 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+20201028+git1d415c5+ds/cpp/ycm/ClangCompleter/CompilationDatabase.h000066400000000000000000000043241374632460100254670ustar00rootroot00000000000000// 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+20201028+git1d415c5+ds/cpp/ycm/ClangCompleter/CompletionData.cpp000066400000000000000000000213461374632460100250250ustar00rootroot00000000000000// 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+20201028+git1d415c5+ds/cpp/ycm/ClangCompleter/CompletionData.h000066400000000000000000000075731374632460100245000ustar00rootroot00000000000000// 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+20201028+git1d415c5+ds/cpp/ycm/ClangCompleter/Diagnostic.h000066400000000000000000000026571374632460100236570ustar00rootroot00000000000000// 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+20201028+git1d415c5+ds/cpp/ycm/ClangCompleter/Documentation.cpp000066400000000000000000000031611374632460100247260ustar00rootroot00000000000000// 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+20201028+git1d415c5+ds/cpp/ycm/ClangCompleter/Documentation.h000066400000000000000000000042411374632460100243730ustar00rootroot00000000000000// 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+20201028+git1d415c5+ds/cpp/ycm/ClangCompleter/FixIt.h000066400000000000000000000035571374632460100226160ustar00rootroot00000000000000// 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+20201028+git1d415c5+ds/cpp/ycm/ClangCompleter/Location.h000066400000000000000000000040031374632460100233260ustar00rootroot00000000000000// 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+20201028+git1d415c5+ds/cpp/ycm/ClangCompleter/Range.cpp000066400000000000000000000016601374632460100231530ustar00rootroot00000000000000// 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+20201028+git1d415c5+ds/cpp/ycm/ClangCompleter/Range.h000066400000000000000000000022321374632460100226140ustar00rootroot00000000000000// 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+20201028+git1d415c5+ds/cpp/ycm/ClangCompleter/TranslationUnit.cpp000066400000000000000000000440631374632460100252610ustar00rootroot00000000000000// 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 #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 = fs::weakly_canonical( filename ); { unique_lock< mutex > lock( diagnostics_mutex_ ); for ( const Diagnostic& diagnostic : latest_diagnostics_ ) { auto this_filename = fs::weakly_canonical( 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+20201028+git1d415c5+ds/cpp/ycm/ClangCompleter/TranslationUnit.h000066400000000000000000000104261374632460100247220ustar00rootroot00000000000000// 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+20201028+git1d415c5+ds/cpp/ycm/ClangCompleter/TranslationUnitStore.cpp000066400000000000000000000105541374632460100262740ustar00rootroot00000000000000// 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+20201028+git1d415c5+ds/cpp/ycm/ClangCompleter/TranslationUnitStore.h000066400000000000000000000054751374632460100257470ustar00rootroot00000000000000// 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+20201028+git1d415c5+ds/cpp/ycm/ClangCompleter/UnsavedFile.h000066400000000000000000000017671374632460100240010ustar00rootroot00000000000000// 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+20201028+git1d415c5+ds/cpp/ycm/CodePoint.cpp000066400000000000000000000070311374632460100211020ustar00rootroot00000000000000// 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 #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( std::string_view 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 it = std::lower_bound( original.begin(), original.end(), text ); if ( it != original.end() && text == *it ) { size_t index = std::distance( original.begin(), 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 ] }; } return { text, text, text, text, false, false, false, 0, 0 }; } } // unnamed namespace CodePoint::CodePoint( std::string_view code_point ) : CodePoint( FindCodePoint( code_point ) ) { } 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( std::string_view 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( static_cast< uint8_t >( *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 ); } const char* UnicodeDecodeError::what() const noexcept { return std::runtime_error::what(); }; } // namespace YouCompleteMe ycmd-0+20201028+git1d415c5+ds/cpp/ycm/CodePoint.h000066400000000000000000000112111374632460100205420ustar00rootroot00000000000000// 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/tr29-37.html#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 { std::string_view original; std::string_view normal; std::string_view folded_case; std::string_view 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/Unicode13.0.0/ch03.pdf#G49591). class CodePoint { public: YCM_EXPORT explicit CodePoint( std::string_view 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( std::string_view text ); // Thrown when an error occurs while decoding a UTF-8 string. struct YCM_EXPORT UnicodeDecodeError : std::runtime_error { using std::runtime_error::runtime_error; const char* what() const noexcept override; }; } // namespace YouCompleteMe #endif /* end of include guard: CODE_POINT_H_3W0LNCLY */ ycmd-0+20201028+git1d415c5+ds/cpp/ycm/CodePointRepository.cpp000066400000000000000000000037261374632460100232110ustar00rootroot00000000000000// 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" namespace YouCompleteMe { CodePointRepository &CodePointRepository::Instance() { static CodePointRepository repo; return repo; } size_t CodePointRepository::NumStoredCodePoints() const { std::shared_lock 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 locker( code_point_holder_mutex_ ); for ( std::string_view 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 = std::make_unique< 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+20201028+git1d415c5+ds/cpp/ycm/CodePointRepository.h000066400000000000000000000041341374632460100226500ustar00rootroot00000000000000// 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() const; 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_; mutable std::shared_mutex code_point_holder_mutex_; }; } // namespace YouCompleteMe #endif /* end of include guard: CODE_POINT_REPOSITORY_H_ENE9FWXL */ ycmd-0+20201028+git1d415c5+ds/cpp/ycm/IdentifierCompleter.cpp000066400000000000000000000063411374632460100231560ustar00rootroot00000000000000// 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, std::string&& filetype, std::string&& filepath ) { identifier_database_.AddIdentifiers( std::move( candidates ), std::move( filetype ), std::move( filepath ) ); } void IdentifierCompleter::AddIdentifiersToDatabase( std::vector< std::string > new_candidates, std::string& filetype, std::string& filepath ) { identifier_database_.AddIdentifiers( std::move( new_candidates ), std::move( filetype ), std::move( filepath ) ); } void IdentifierCompleter::ClearForFileAndAddIdentifiersToDatabase( std::vector< std::string > new_candidates, std::string& filetype, std::string& filepath ) { identifier_database_.ClearCandidatesStoredForFile( std::string( filetype ), std::string( filepath ) ); AddIdentifiersToDatabase( std::move( new_candidates ), filetype, filepath ); } void IdentifierCompleter::AddIdentifiersToDatabaseFromTagFiles( std::vector< std::string >& absolute_paths_to_tag_files ) { for( auto&& path : absolute_paths_to_tag_files ) { identifier_database_.AddIdentifiers( ExtractIdentifiersFromTagsFile( std::move( 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, max_candidates ); std::vector< std::string > candidates; candidates.reserve( results.size() ); for ( const Result & result : results ) { candidates.emplace_back( result.Text() ); } return candidates; } } // namespace YouCompleteMe ycmd-0+20201028+git1d415c5+ds/cpp/ycm/IdentifierCompleter.h000066400000000000000000000046311374632460100226230ustar00rootroot00000000000000// 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, std::string&& filetype, std::string&& filepath ); void AddIdentifiersToDatabase( std::vector< std::string > new_candidates, std::string& filetype, 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, std::string& filetype, std::string& filepath ); YCM_EXPORT void AddIdentifiersToDatabaseFromTagFiles( std::vector< std::string >& absolute_paths_to_tag_files ); // 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+20201028+git1d415c5+ds/cpp/ycm/IdentifierDatabase.cpp000066400000000000000000000114201374632460100227220ustar00rootroot00000000000000// 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 #include namespace YouCompleteMe { IdentifierDatabase::IdentifierDatabase() : candidate_repository_( CandidateRepository::Instance() ) { } void IdentifierDatabase::AddIdentifiers( FiletypeIdentifierMap&& filetype_identifier_map ) { std::lock_guard locker( filetype_candidate_map_mutex_ ); for ( auto&& filetype_and_map : filetype_identifier_map ) { for ( auto&& filepath_and_identifiers : filetype_and_map.second ) { auto filetype = filetype_and_map.first; auto filepath = filepath_and_identifiers.first; AddIdentifiersNoLock( std::move( filepath_and_identifiers.second ), std::move( filetype ), std::move( filepath ) ); } } } void IdentifierDatabase::AddIdentifiers( std::vector< std::string >&& new_candidates, std::string&& filetype, std::string&& filepath ) { std::lock_guard locker( filetype_candidate_map_mutex_ ); AddIdentifiersNoLock( std::move( new_candidates ), std::move( filetype ), std::move( filepath ) ); } void IdentifierDatabase::ClearCandidatesStoredForFile( std::string&& filetype, std::string&& filepath ) { std::lock_guard locker( filetype_candidate_map_mutex_ ); GetCandidateSet( std::move( filetype ), std::move( filepath ) ).clear(); } std::vector< Result > IdentifierDatabase::ResultsForQueryAndType( std::string&& query, const std::string &filetype, const size_t max_results ) const { FiletypeCandidateMap::const_iterator it; { std::shared_lock 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::vector< Result > results; { std::lock_guard 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 ); return 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( std::string&& filetype, std::string&& filepath ) { std::unique_ptr< FilepathToCandidates > &path_to_candidates = filetype_candidate_map_[ std::move( filetype ) ]; if ( !path_to_candidates ) { path_to_candidates = std::make_unique< FilepathToCandidates >(); } std::unique_ptr< std::set< const Candidate * > > &candidates = ( *path_to_candidates )[ std::move( filepath ) ]; if ( !candidates ) { candidates = std::make_unique< 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, std::string&& filetype, std::string&& filepath ) { std::set< const Candidate *> &candidates = GetCandidateSet( std::move( filetype ), std::move( 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+20201028+git1d415c5+ds/cpp/ycm/IdentifierDatabase.h000066400000000000000000000063261374632460100224000ustar00rootroot00000000000000// 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 #include #include #include #include #include #include namespace YouCompleteMe { class Candidate; class Result; class CandidateRepository; // filepath -> identifiers using FilepathToIdentifiers = std::map< std::string, std::vector< std::string > >; // filetype -> (filepath -> identifiers) using FiletypeIdentifierMap = std::map< std::string, FilepathToIdentifiers >; // This class stores the database of identifiers the identifier completer has // seen. It stores them in a data structure that makes it easy to tell which // identifier came from which file and what files have which filetypes. // // The main point of this class is to isolate the parts of the code that need // access to this internal data structure so that it's easier to confirm that // mutexes are used correctly to protect concurrent access. // // This class is thread-safe. class IdentifierDatabase { public: YCM_EXPORT IdentifierDatabase(); IdentifierDatabase( const IdentifierDatabase& ) = delete; IdentifierDatabase& operator=( const IdentifierDatabase& ) = delete; void AddIdentifiers( FiletypeIdentifierMap&& filetype_identifier_map ); void AddIdentifiers( std::vector< std::string >&& new_candidates, std::string&& filetype, std::string&& filepath ); void ClearCandidatesStoredForFile( std::string&& filetype, std::string&& filepath ); std::vector< Result > ResultsForQueryAndType( std::string&& query, const std::string &filetype, const size_t max_results ) const; private: std::set< const Candidate * > &GetCandidateSet( std::string&& filetype, std::string&& filepath ); void AddIdentifiersNoLock( std::vector< std::string >&& new_candidates, std::string&& filetype, std::string&& filepath ); // filepath -> *( *candidate ) using FilepathToCandidates = std::unordered_map < std::string, std::unique_ptr< std::set< const Candidate * > > >; // filetype -> *( filepath -> *( *candidate ) ) using FiletypeCandidateMap = std::unordered_map < std::string, std::unique_ptr< FilepathToCandidates > >; CandidateRepository &candidate_repository_; FiletypeCandidateMap filetype_candidate_map_; mutable std::shared_mutex filetype_candidate_map_mutex_; }; } // namespace YouCompleteMe #endif /* end of include guard: IDENTIFIERDATABASE_H_ZESX3CVR */ ycmd-0+20201028+git1d415c5+ds/cpp/ycm/IdentifierUtils.cpp000066400000000000000000000222741374632460100223270ustar00rootroot00000000000000// 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 "IdentifierUtils.h" #include "Utils.h" #include #include #include namespace YouCompleteMe { namespace fs = std::filesystem; namespace { // Only used as the equality comparer for the below unordered_map which stores // const char* pointers and not std::string but needs to hash based on string // values and not pointer values. struct StringEqualityComparer { bool operator()( std::string_view a, std::string_view b ) const { return a == b; } }; // List of languages Universal Ctags supports: // ctags --list-languages // To map a language name to a filetype, see this file: // :e $VIMRUNTIME/filetype.vim // This is a map of const char* and not std::string to prevent issues with // static initialization. const std::unordered_map < std::string_view, std::string_view > LANG_TO_FILETYPE = { { "Ada" , "ada" }, { "AnsiblePlaybook" , "ansibleplaybook" }, { "Ant" , "ant" }, { "Asm" , "asm" }, { "Asp" , "asp" }, { "Autoconf" , "autoconf" }, { "Automake" , "automake" }, { "Awk" , "awk" }, { "Basic" , "basic" }, { "BETA" , "beta" }, { "C" , "c" }, { "C#" , "cs" }, { "C++" , "cpp" }, { "Clojure" , "clojure" }, { "Cobol" , "cobol" }, { "CPreProcessor" , "cpreprocessor" }, { "CSS" , "css" }, { "ctags" , "ctags" }, { "CUDA" , "cuda" }, { "D" , "d" }, { "DBusIntrospect" , "dbusintrospect" }, { "Diff" , "diff" }, { "DosBatch" , "dosbatch" }, { "DTD" , "dtd" }, { "DTS" , "dts" }, { "Eiffel" , "eiffel" }, { "elm" , "elm" }, { "Erlang" , "erlang" }, { "Falcon" , "falcon" }, { "Flex" , "flex" }, { "Fortran" , "fortran" }, { "gdbinit" , "gdb" }, { "Glade" , "glade" }, { "Go" , "go" }, { "HTML" , "html" }, { "Iniconf" , "iniconf" }, { "ITcl" , "itcl" }, { "Java" , "java" }, { "JavaProperties" , "jproperties" }, { "JavaScript" , "javascript" }, { "JSON" , "json" }, { "LdScript" , "ldscript" }, { "Lisp" , "lisp" }, { "Lua" , "lua" }, { "M4" , "m4" }, { "Make" , "make" }, { "man" , "man" }, { "MatLab" , "matlab" }, { "Maven2" , "maven2" }, { "Myrddin" , "myrddin" }, { "ObjectiveC" , "objc" }, { "OCaml" , "ocaml" }, { "Pascal" , "pascal" }, { "passwd" , "passwd" }, { "Perl" , "perl" }, { "Perl6" , "perl6" }, { "PHP" , "php" }, { "PlistXML" , "plistxml" }, { "pod" , "pod" }, { "Protobuf" , "protobuf" }, { "PuppetManifest" , "puppet" }, { "Python" , "python" }, { "PythonLoggingConfig" , "pythonloggingconfig" }, { "QemuHX" , "qemuhx" }, { "R" , "r" }, { "RelaxNG" , "rng" }, { "reStructuredText" , "rst" }, { "REXX" , "rexx" }, { "Robot" , "robot" }, { "RpmSpec" , "spec" }, { "RSpec" , "rspec" }, { "Ruby" , "ruby" }, { "Rust" , "rust" }, { "Scheme" , "scheme" }, { "Sh" , "sh" }, { "SLang" , "slang" }, { "SML" , "sml" }, { "SQL" , "sql" }, { "SVG" , "svg" }, { "SystemdUnit" , "systemd" }, { "SystemVerilog" , "systemverilog" }, { "Tcl" , "tcl" }, { "TclOO" , "tcloo" }, { "Tex" , "tex" }, { "TTCN" , "ttcn" }, { "Vera" , "vera" }, { "Verilog" , "verilog" }, { "VHDL" , "vhdl" }, { "Vim" , "vim" }, { "WindRes" , "windres" }, { "XSLT" , "xslt" }, { "YACC" , "yacc" }, { "Yaml" , "yaml" }, { "YumRepo" , "yumrepo" }, { "Zephir" , "zephir" } }; } // unnamed namespace // For details on the tag format supported, see here for details: // http://ctags.sourceforge.net/FORMAT // TL;DR: The only supported format is the one Exuberant Ctags emits. FiletypeIdentifierMap ExtractIdentifiersFromTagsFile( const fs::path &path_to_tag_file ) { FiletypeIdentifierMap filetype_identifier_map; const auto lines = [ &path_to_tag_file ]{ try { return ReadUtf8File( path_to_tag_file ); } catch ( ... ) { return std::vector< std::string >{}; } }(); for (auto&& line : lines) { // Identifier name is from the start of the line to the first \t. const size_t id_end = line.find( '\t' ); if ( id_end == std::string::npos ) { continue; } // File path the identifier is in is the second field. const size_t path_begin = line.find_first_not_of( '\t', id_end + 1 ); if ( path_begin == std::string::npos ) { continue; } const size_t path_end = line.find( '\t', path_begin + 1 ); if ( path_end == std::string::npos ) { continue; } // IdentifierCompleter depends on the "language:Foo" field. // strlen( "language:" ) == 9 const size_t lang_begin = line.find( "language:", path_end + 1 ) + 9; if ( lang_begin == std::string::npos + 9 ) { continue; } const size_t lang_end = [ &line, lang_begin ] { auto end = line.find( '\t', lang_begin + 1 ); if (end == std::string::npos) { end = line.back() == '\r' ? line.size() - 1 : line.size(); } return end; }(); std::string_view identifier(&line[ 0 ], id_end ); fs::path path( line.begin() + path_begin, line.begin() + path_end ); path = fs::weakly_canonical( path_to_tag_file.parent_path() / path ); std::string_view language( &line[ lang_begin ], lang_end - lang_begin ); std::string filetype( FindWithDefault( LANG_TO_FILETYPE, language, Lowercase( language ) ) ); filetype_identifier_map[ std::move( filetype ) ] [ std::move( path ).string() ] .emplace_back( identifier ); } return filetype_identifier_map; } } // namespace YouCompleteMe ycmd-0+20201028+git1d415c5+ds/cpp/ycm/IdentifierUtils.h000066400000000000000000000020611374632460100217640ustar00rootroot00000000000000// 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 IDENTIFIERUTILS_CPP_WFFUZNET #define IDENTIFIERUTILS_CPP_WFFUZNET #include "IdentifierDatabase.h" #include namespace YouCompleteMe { YCM_EXPORT FiletypeIdentifierMap ExtractIdentifiersFromTagsFile( const std::filesystem::path &path_to_tag_file ); } // namespace YouCompleteMe #endif /* end of include guard: IDENTIFIERUTILS_CPP_WFFUZNET */ ycmd-0+20201028+git1d415c5+ds/cpp/ycm/PythonSupport.cpp000066400000000000000000000067751374632460100221120ustar00rootroot00000000000000// 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 "PythonSupport.h" #include "Candidate.h" #include "CandidateRepository.h" #include "Result.h" #include "Utils.h" #include #include using pybind11::len; using pybind11::str; using pybind11::bytes; using pybind11::object; using pybind11::isinstance; using pylist = pybind11::list; namespace YouCompleteMe { namespace { std::vector< const Candidate * > CandidatesFromObjectList( pylist candidates, const std::string &candidate_property ) { size_t num_candidates = len( candidates ); std::vector< std::string > candidate_strings; candidate_strings.reserve( num_candidates ); // Store the property in a native Python string so that the below doesn't need // to reconvert over and over: str py_prop( candidate_property ); for ( size_t i = 0; i < num_candidates; ++i ) { if ( candidate_property.empty() ) { candidate_strings.emplace_back( GetUtf8String( candidates[ i ] ) ); } else { candidate_strings.emplace_back( GetUtf8String( candidates[ i ][ py_prop ] ) ); } } return CandidateRepository::Instance().GetCandidatesForStrings( std::move( candidate_strings ) ); } } // unnamed namespace pylist FilterAndSortCandidates( pylist candidates, const std::string &candidate_property, std::string query, const size_t max_candidates ) { pylist filtered_candidates; size_t num_candidates = len( candidates ); std::vector< const Candidate * > repository_candidates = CandidatesFromObjectList( candidates, candidate_property ); std::vector< ResultAnd< size_t > > result_and_objects; { pybind11::gil_scoped_release unlock; Word query_object( std::move( query ) ); for ( size_t i = 0; i < num_candidates; ++i ) { const Candidate *candidate = repository_candidates[ i ]; if ( candidate->IsEmpty() || !candidate->ContainsBytes( query_object ) ) { continue; } Result result = candidate->QueryMatchResult( query_object ); if ( result.IsSubsequence() ) { result_and_objects.emplace_back( result, i ); } } PartialSort( result_and_objects, max_candidates ); } for ( const ResultAnd< size_t > &result_and_object : result_and_objects ) { filtered_candidates.append( candidates[ result_and_object.extra_object_ ] ); } return filtered_candidates; } std::string GetUtf8String( const object &value ) { // If already a unicode or string (or something derived from it) // pybind will already convert to utf8 when converting to std::string. // For `bytes` the contents are left untouched: if ( isinstance< str >( value ) || isinstance< bytes >( value ) ) { return value.cast< std::string >(); } // Otherwise go through `pybind11::str()`, // which goes through Python's built-in `str`. return str( value ); } } // namespace YouCompleteMe ycmd-0+20201028+git1d415c5+ds/cpp/ycm/PythonSupport.h000066400000000000000000000033161374632460100215430ustar00rootroot00000000000000// Copyright (C) 2011-2020 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 PYTHONSUPPORT_H_KWGFEX0V #define PYTHONSUPPORT_H_KWGFEX0V #include namespace YouCompleteMe { /// Given a list of python objects (that represent completion candidates) in a /// python list |candidates|, a |candidate_property| on which to filter and sort /// the candidates and a user query, returns a new sorted python list with the /// original objects that survived the filtering. This list contains at most /// |max_candidates|. If |max_candidates| is omitted or 0, all candidates are /// sorted. YCM_EXPORT pybind11::list FilterAndSortCandidates( pybind11::list candidates, const std::string &candidate_property, std::string query, const size_t max_candidates = 0 ); /// Given a Python object that's supposed to be "string-like", returns a UTF-8 /// encoded std::string. Raises an exception if the object can't be converted to /// a string. std::string GetUtf8String( const pybind11::object &value ); } // namespace YouCompleteMe #endif /* end of include guard: PYTHONSUPPORT_H_KWGFEX0V */ ycmd-0+20201028+git1d415c5+ds/cpp/ycm/Result.cpp000066400000000000000000000125361374632460100205020ustar00rootroot00000000000000// 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 "Result.h" #include "Utils.h" namespace YouCompleteMe { namespace { size_t LongestCommonSubsequenceLength( const CharacterSequence &first, const CharacterSequence &second ) { const auto &longer = first.size() > second.size() ? first : second; const auto &shorter = first.size() > second.size() ? second : first; size_t longer_len = longer.size(); size_t shorter_len = shorter.size(); std::vector< size_t > previous( shorter_len + 1, 0 ); std::vector< size_t > current( shorter_len + 1, 0 ); for ( size_t i = 0; i < longer_len; ++i ) { for ( size_t j = 0; j < shorter_len; ++j ) { if ( longer[ i ]->EqualsBase( *shorter[ j ] ) ) { current[ j + 1 ] = previous[ j ] + 1; } else { current[ j + 1 ] = std::max( current[ j ], previous[ j + 1 ] ); } } for ( size_t j = 0; j < shorter_len; ++j ) { previous[ j + 1 ] = current[ j + 1 ]; } } return current[ shorter_len ]; } } // unnamed namespace Result::Result( const Candidate *candidate, const Word *query, size_t char_match_index_sum, bool query_is_candidate_prefix ) : is_subsequence_( true ), first_char_same_in_query_and_text_( false ), query_is_candidate_prefix_( query_is_candidate_prefix ), char_match_index_sum_( char_match_index_sum ), num_wb_matches_( 0 ), candidate_( candidate ), query_( query ) { SetResultFeaturesFromQuery(); } bool Result::operator< ( const Result &other ) const { // Yes, this is ugly but it also needs to be fast. Since this is called a // bazillion times, we have to make sure only the required comparisons are // made, and no more. if ( !query_->IsEmpty() ) { // This is the core of the ranking system. A result has more weight than // another if one of these conditions is satisfied, in that order: // - it starts with the same character as the query while the other does // not; // - one of the results has all its word boundary characters matched and // it has more word boundary characters matched than the other; // - both results have all their word boundary characters matched and it // has less word boundary characters than the other; // - the query is a prefix of the result but not a prefix of the other; // - it has more word boundary characters matched than the other; // - it has less word boundary characters than the other; // - its sum of indexes of its matched characters is less than the sum of // indexes of the other result; // - it has less characters than the other result; // - all its characters are in lowercase while the other has at least one // uppercase character; // - it appears before the other result in lexicographic order. if ( first_char_same_in_query_and_text_ != other.first_char_same_in_query_and_text_ ) { return first_char_same_in_query_and_text_; } if ( num_wb_matches_ == query_->Length() || other.num_wb_matches_ == query_->Length() ) { if ( num_wb_matches_ != other.num_wb_matches_ ) { return num_wb_matches_ > other.num_wb_matches_; } if ( NumWordBoundaryChars() != other.NumWordBoundaryChars() ) { return NumWordBoundaryChars() < other.NumWordBoundaryChars(); } } if ( query_is_candidate_prefix_ != other.query_is_candidate_prefix_ ) { return query_is_candidate_prefix_; } if ( num_wb_matches_ != other.num_wb_matches_ ) { return num_wb_matches_ > other.num_wb_matches_; } if ( NumWordBoundaryChars() != other.NumWordBoundaryChars() ) { return NumWordBoundaryChars() < other.NumWordBoundaryChars(); } if ( char_match_index_sum_ != other.char_match_index_sum_ ) { return char_match_index_sum_ < other.char_match_index_sum_; } if ( candidate_->Length() != other.candidate_->Length() ) { return candidate_->Length() < other.candidate_->Length(); } if ( candidate_->TextIsLowercase() != other.candidate_->TextIsLowercase() ) { return candidate_->TextIsLowercase(); } } // Lexicographic comparison, but we prioritize lowercase letters over // uppercase ones. So "foo" < "Foo". return candidate_->CaseSwappedText() < other.candidate_->CaseSwappedText(); } void Result::SetResultFeaturesFromQuery() { if ( query_->IsEmpty() || candidate_->IsEmpty() ) { return; } first_char_same_in_query_and_text_ = candidate_->Characters()[ 0 ]->EqualsBase( *query_->Characters()[ 0 ] ); num_wb_matches_ = LongestCommonSubsequenceLength( query_->Characters(), candidate_->WordBoundaryChars() ); } } // namespace YouCompleteMe ycmd-0+20201028+git1d415c5+ds/cpp/ycm/Result.h000066400000000000000000000073231374632460100201450ustar00rootroot00000000000000// 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 RESULT_H_CZYD2SGN #define RESULT_H_CZYD2SGN #include "Candidate.h" #include namespace YouCompleteMe { class Result { public: Result() : is_subsequence_( false ), first_char_same_in_query_and_text_( false ), query_is_candidate_prefix_( false ), char_match_index_sum_( 0 ), num_wb_matches_( 0 ), candidate_( nullptr ), query_( nullptr ) {} Result( const Candidate *candidate, const Word *query, size_t char_match_index_sum, bool query_is_candidate_prefix ); bool operator< ( const Result &other ) const; inline const std::string &Text() const { return candidate_->Text(); } inline size_t NumWordBoundaryChars() const { return candidate_->WordBoundaryChars().size(); } inline bool IsSubsequence() const { return is_subsequence_; } private: void SetResultFeaturesFromQuery(); // true when the characters of the query are a subsequence of the characters // in the candidate text, e.g. the characters "abc" are a subsequence for // "xxaygbefc" but not for "axxcb" since they occur in the correct order ('a' // then 'b' then 'c') in the first string but not in the second. bool is_subsequence_; // true when the first character of the query and the candidate match bool first_char_same_in_query_and_text_; // true when the query is a prefix of the candidate string, e.g. "foo" query // for "foobar" candidate. bool query_is_candidate_prefix_; // The sum of the indexes of all the letters the query "hit" in the candidate // text. For instance, the result for the query "abc" in the candidate // "012a45bc8" has char_match_index_sum of 3 + 6 + 7 = 16 because those are // the char indexes of those letters in the candidate string. size_t char_match_index_sum_; // The number of characters in the query that match word boundary characters // in the candidate. Characters must match in the same order of appearance // (i.e. these characters must be a subsequence of the word boundary // characters). Case is ignored. A character is a word boundary character if // one of these is true: // - this is the first character and not a punctuation; // - the character is uppercase but not the previous one; // - the character is a letter and the previous one is a punctuation. size_t num_wb_matches_; // NOTE: we don't use references for the query and the candidate because we // are sorting results through std::sort or std::partial_sort and these // functions require move assignments which is not possible with reference // members. // Points to the candidate. const Candidate *candidate_; // Points to the query. const Word *query_; }; template< class T > struct ResultAnd { ResultAnd( const Result &result, T extra_object ) : extra_object_( extra_object ), result_( result ) { } bool operator< ( const ResultAnd &other ) const { return result_ < other.result_; } T extra_object_; Result result_; }; } // namespace YouCompleteMe #endif /* end of include guard: RESULT_H_CZYD2SGN */ ycmd-0+20201028+git1d415c5+ds/cpp/ycm/Utils.cpp000066400000000000000000000025141374632460100203170ustar00rootroot00000000000000// 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 "Utils.h" #include #include #include #include #include #include namespace fs = std::filesystem; namespace YouCompleteMe { std::vector< std::string > ReadUtf8File( const fs::path &filepath ) { std::vector< std::string > contents; if ( !fs::is_empty( filepath ) && fs::is_regular_file( filepath ) ) { std::string line; for( std::ifstream file( filepath.string(), std::ios::in | std::ios::binary ); std::getline( file, line ); ) { contents.push_back( std::move( line ) ); } } return contents; } } // namespace YouCompleteMe ycmd-0+20201028+git1d415c5+ds/cpp/ycm/Utils.h000066400000000000000000000115421374632460100177650ustar00rootroot00000000000000// 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 UTILS_H_KEPMRPBH #define UTILS_H_KEPMRPBH #include #include #include #include #include #include #include #include namespace fs = std::filesystem; namespace YouCompleteMe { YCM_EXPORT inline bool IsUppercase( uint8_t ascii_character ) { return 'A' <= ascii_character && ascii_character <= 'Z'; } // An uppercase ASCII character can be converted to lowercase and vice versa by // flipping its third most significant bit. YCM_EXPORT inline char Lowercase( uint8_t ascii_character ) { if ( IsUppercase( ascii_character ) ) { return static_cast< char >( ascii_character ^ 0x20 ); } return static_cast< char >( ascii_character ); } YCM_EXPORT inline std::string Lowercase( std::string_view text ) { std::string result; result.reserve( text.size() ); for ( auto ascii_character : text ) { result.push_back( Lowercase( static_cast< uint8_t >( ascii_character ) ) ); } return result; } // Reads the entire contents of the specified file. If the file does not exist, // an exception is thrown. std::vector< std::string > ReadUtf8File( const fs::path &filepath ); template typename Container::mapped_type & GetValueElseInsert( Container &container, const Key &key, typename Container::mapped_type &&value ) { return container.insert( typename Container::value_type( key, std::move( value ) ) ).first->second; } template bool ContainsKey( Container &container, const Key &key ) { return container.find( key ) != container.end(); } template Ret FindWithDefault( Container &container, const Key &key, Ret&& value ) { static_assert( std::is_constructible_v< Ret, typename Container::mapped_type > ); typename Container::const_iterator it = container.find( key ); if ( it != container.end() ) { return Ret{ it->second }; } return std::move( value ); } template bool Erase( Container &container, const Key &key ) { typename Container::iterator it = container.find( key ); if ( it != container.end() ) { container.erase( it ); return true; } return false; } // Shrink a vector to its sorted |num_sorted_elements| smallest elements. If // |num_sorted_elements| is 0 or larger than the vector size, sort the whole // vector. template void PartialSort( std::vector< Element > &elements, const size_t num_sorted_elements ) { using diff = typename std::vector< Element >::iterator::difference_type; size_t nb_elements = elements.size(); size_t max_elements = num_sorted_elements > 0 && nb_elements >= num_sorted_elements ? num_sorted_elements : nb_elements; // When the number of elements to sort is more than 1024 and one sixty-fourth // of the total number of elements, switch to std::nth_element followed by // std::sort. This heuristic is based on the observation that // std::partial_sort (heapsort) is the most efficient algorithm when the // number of elements to sort is small and that std::nth_element (introselect) // combined with std::sort (introsort) always perform better than std::sort // alone in other cases. if ( max_elements <= std::max( static_cast< size_t >( 1024 ), nb_elements / 64 ) ) { std::partial_sort( elements.begin(), elements.begin() + static_cast< diff >( max_elements ), elements.end() ); } else { std::nth_element( elements.begin(), elements.begin() + static_cast< diff >( max_elements ), elements.end() ); std::sort( elements.begin(), elements.begin() + static_cast< diff >( max_elements ) ); } // Remove the unsorted elements. Use erase instead of resize as it doesn't // require a default constructor on Element. elements.erase( elements.begin() + static_cast< diff >( max_elements ), elements.end() ); } } // namespace YouCompleteMe #endif /* end of include guard: UTILS_H_KEPMRPBH */ ycmd-0+20201028+git1d415c5+ds/cpp/ycm/Word.cpp000066400000000000000000000236761374632460100201460ustar00rootroot00000000000000// 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 "CodePoint.h" #include "Word.h" #include namespace YouCompleteMe { namespace { // Break a sequence of code points into characters (grapheme clusters) according // to the rules in // https://www.unicode.org/reports/tr29/tr29-37.html#Grapheme_Cluster_Boundary_Rules std::vector< std::string > BreakCodePointsIntoCharacters( const CodePointSequence &code_points ) { std::vector< std::string > characters; // Rules GB1 and GB2 (break at the start and at the end of the text) are // automatically satisfied. auto code_point_pos = code_points.begin(); if ( code_point_pos == code_points.end() ) { return characters; } std::string character; character.append( ( *code_point_pos )->Normal() ); auto previous_code_point_pos = code_point_pos; ++code_point_pos; if ( code_point_pos == code_points.end() ) { characters.push_back( character ); return characters; } bool is_regional_indicator_nb_odd = false; bool within_emoji_modifier = false; for ( ; code_point_pos != code_points.end() ; ++previous_code_point_pos, ++code_point_pos ) { auto previous_property = ( *previous_code_point_pos )->GetBreakProperty(); const auto &code_point = ( *code_point_pos )->Normal(); auto property = ( *code_point_pos )->GetBreakProperty(); switch( previous_property ) { case BreakProperty::CR: switch( property ) { // Rule GB3: do not break between a CR and LF. case BreakProperty::LF: character.append( code_point ); break; // Rule GB4: otherwise, break after CR. default: characters.push_back( character ); character = code_point; } break; // Rule GB4: break after controls and LF. case BreakProperty::CONTROL: case BreakProperty::LF: characters.push_back( character ); character = code_point; break; case BreakProperty::L: switch( property ) { // Rule GB6: do not break Hangul syllable sequences. case BreakProperty::L: case BreakProperty::V: case BreakProperty::LV: case BreakProperty::LVT: // Rule GB9: do not break before extending characters or when using a // zero-width joiner (ZWJ). case BreakProperty::EXTEND: case BreakProperty::ZWJ: // Rule GB9a: do not break before spacing marks. case BreakProperty::SPACINGMARK: character.append( code_point ); break; default: characters.push_back( character ); character = code_point; } break; case BreakProperty::LV: case BreakProperty::V: switch( property ) { // Rule GB7: do not break Hangul syllable sequences. case BreakProperty::V: case BreakProperty::T: // Rule GB9: do not break before extending characters or when using a // zero-width joiner (ZWJ). case BreakProperty::EXTEND: case BreakProperty::ZWJ: // Rule GB9a: do not break before spacing marks. case BreakProperty::SPACINGMARK: character.append( code_point ); break; default: characters.push_back( character ); character = code_point; } break; case BreakProperty::LVT: case BreakProperty::T: switch( property ) { // Rule GB8: do not break Hangul syllable sequences. case BreakProperty::T: // Rule GB9: do not break before extending characters or when using a // zero-width joiner (ZWJ). case BreakProperty::EXTEND: case BreakProperty::ZWJ: // Rule GB9a: do not break before spacing marks. case BreakProperty::SPACINGMARK: character.append( code_point ); break; default: characters.push_back( character ); character = code_point; } break; case BreakProperty::PREPEND: switch( property ) { // Rules GB5: break before controls. case BreakProperty::CONTROL: case BreakProperty::CR: case BreakProperty::LF: characters.push_back( character ); character = code_point; break; // Rule GB9b: do not break after prepend characters. default: character.append( code_point ); } break; case BreakProperty::EXTEND: switch( property ) { // Rule GB9: do not break before extending characters or when using a // zero-width joiner (ZWJ). case BreakProperty::EXTEND: case BreakProperty::ZWJ: character.append( code_point ); break; // Rule GB9a: do not break before spacing marks. case BreakProperty::SPACINGMARK: character.append( code_point ); within_emoji_modifier = false; break; default: characters.push_back( character ); character = code_point; within_emoji_modifier = false; } break; case BreakProperty::ZWJ: switch( property ) { // Rule GB9: do not break before extending characters or when using a // zero-width joiner (ZWJ). case BreakProperty::EXTEND: case BreakProperty::ZWJ: // Rule GB9a: do not break before spacing marks. case BreakProperty::SPACINGMARK: character.append( code_point ); within_emoji_modifier = false; break; // Rule GB11: do not break within emoji modifier sequences of emoji // zwj sequences. case BreakProperty::EXTPICT: if ( within_emoji_modifier ) { character.append( code_point ); within_emoji_modifier = false; } else { characters.push_back( character ); character = code_point; } break; default: characters.push_back( character ); character = code_point; within_emoji_modifier = false; } break; case BreakProperty::EXTPICT: switch( property ) { // Rule GB9a: do not break before spacing marks. case BreakProperty::SPACINGMARK: character.append( code_point ); break; // Rule GB11: do not break within emoji modifier sequences of emoji // zwj sequences. case BreakProperty::EXTEND: case BreakProperty::ZWJ: character.append( code_point ); within_emoji_modifier = true; break; default: characters.push_back( character ); character = code_point; } break; case BreakProperty::REGIONAL_INDICATOR: switch( property ) { // Rule GB9: do not break before extending characters or when using a // zero-width joiner (ZWJ). case BreakProperty::EXTEND: case BreakProperty::ZWJ: // Rule GB9a: do not break before spacing marks. case BreakProperty::SPACINGMARK: character.append( code_point ); is_regional_indicator_nb_odd = false; break; // Rules GB12 and GB13: do not break within emoji flag sequences. That // is, do not break between regional indicator (RI) symbols if there // is an odd number of RI characters before the break point. case BreakProperty::REGIONAL_INDICATOR: is_regional_indicator_nb_odd = !is_regional_indicator_nb_odd; if ( is_regional_indicator_nb_odd ) { character.append( code_point ); } else { characters.push_back( character ); character = code_point; } break; default: characters.push_back( character ); character = code_point; is_regional_indicator_nb_odd = false; } break; default: switch( property ) { // Rule GB9: do not break before extending characters or when using a // zero-width joiner (ZWJ). case BreakProperty::EXTEND: case BreakProperty::ZWJ: // Rule GB9a: do not break before spacing marks. case BreakProperty::SPACINGMARK: character.append( code_point ); break; // Rules GB5: break before controls. // Rules GB999. default: characters.push_back( character ); character = code_point; } } } characters.push_back( character ); return characters; } } // unnamed namespace void Word::BreakIntoCharacters() { const CodePointSequence &code_points = BreakIntoCodePoints( text_ ); characters_ = CharacterRepository::Instance().GetCharacters( BreakCodePointsIntoCharacters( code_points ) ); } void Word::ComputeBytesPresent() { for ( const auto &character : characters_ ) { for ( auto byte : character->Base() ) { bytes_present_.set( static_cast< uint8_t >( byte ) ); } } } Word::Word( std::string&& text ) : text_( std::move( text ) ) { BreakIntoCharacters(); ComputeBytesPresent(); } } // namespace YouCompleteMe ycmd-0+20201028+git1d415c5+ds/cpp/ycm/Word.h000066400000000000000000000042171374632460100176010ustar00rootroot00000000000000// 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 WORD_H_UOHAUKVQ #define WORD_H_UOHAUKVQ #include "Character.h" #include #include #include #define NUM_BYTES 256 namespace YouCompleteMe { using Bitset = std::bitset< NUM_BYTES >; // This class represents a sequence of UTF-8 characters. It takes a UTF-8 // encoded string and splits that string into characters following the rules in // https://www.unicode.org/reports/tr29/tr29-37.html#Grapheme_Cluster_Boundary_Rules class Word { public: YCM_EXPORT explicit Word( std::string&& text ); // Make class noncopyable Word( const Word& ) = delete; Word& operator=( const Word& ) = delete; Word( Word&& ) = default; Word& operator=( Word&& ) = default; ~Word() = default; inline const CharacterSequence &Characters() const { return characters_; } inline const std::string &Text() const { return text_; } inline size_t Length() const { return characters_.size(); } // Returns true if the word contains the bytes from another word (it may also // contain other bytes). inline bool ContainsBytes( const Word &other ) const { return ( bytes_present_ & other.bytes_present_ ) == other.bytes_present_; } inline bool IsEmpty() const { return characters_.empty(); } private: void BreakIntoCharacters(); void ComputeBytesPresent(); std::string text_; CharacterSequence characters_; Bitset bytes_present_; }; } // namespace YouCompleteMe #endif /* end of include guard: WORD_H_UOHAUKVQ */ ycmd-0+20201028+git1d415c5+ds/cpp/ycm/benchmarks/000077500000000000000000000000001374632460100206265ustar00rootroot00000000000000ycmd-0+20201028+git1d415c5+ds/cpp/ycm/benchmarks/BenchUtils.cpp000066400000000000000000000023451374632460100233760ustar00rootroot00000000000000// Copyright (C) 2017 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 "BenchUtils.h" namespace YouCompleteMe { std::vector< std::string > GenerateCandidatesWithCommonPrefix( const std::string prefix, int number ) { std::vector< std::string > candidates; for ( int i = 0; i < number; ++i ) { std::string candidate = ""; int letter = i; for ( int pos = 0; pos < 5; letter /= 26, ++pos ) { candidate = std::string( 1, letter % 26 + 'a' ) + candidate; } candidate = prefix + candidate; candidates.push_back( candidate ); } return candidates; } } // namespace YouCompleteMe ycmd-0+20201028+git1d415c5+ds/cpp/ycm/benchmarks/BenchUtils.h000066400000000000000000000021021374632460100230320ustar00rootroot00000000000000// Copyright (C) 2017 ycmd contributores // // 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 BENCHUTILS_H_7UY2GEP1 #define BENCHUTILS_H_7UY2GEP1 #include #include namespace YouCompleteMe { // Generate a list of |number| candidates of the form |prefix|[a-z]{5}. std::vector< std::string > GenerateCandidatesWithCommonPrefix( const std::string prefix, int number ); } // namespace YouCompleteMe #endif /* end of include guard: BENCHUTILS_H_7UY2GEP1 */ ycmd-0+20201028+git1d415c5+ds/cpp/ycm/benchmarks/CMakeLists.txt000066400000000000000000000040041374632460100233640ustar00rootroot00000000000000# Copyright (C) 2017 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 . project( ycm_core_benchmarks ) cmake_minimum_required( VERSION 2.8 ) # We don't want to test the benchmark library. set( BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "Enable testing of the benchmark library" ) set( BUILD_SHARED_LIBS OFF ) add_subdirectory( benchmark ) set( BENCHMARK_INCLUDE_DIRS ${benchmark_SOURCE_DIR}/include ) set( BENCHMARK_LIBRARIES benchmark ) include_directories( ${ycm_core_SOURCE_DIR} ${BENCHMARK_INCLUDE_DIRS} ) file( GLOB_RECURSE SOURCES *.h *.cpp ) # We don't want benchmark sources in this target. file( GLOB_RECURSE to_remove benchmark/*.h benchmark/*.cpp CMakeFiles/*.cpp ) if( to_remove ) list( REMOVE_ITEM SOURCES ${to_remove} ) endif() add_executable( ${PROJECT_NAME} ${SOURCES} ) if( MSVC ) # Build benchmark and ycm_core_benchmarks targets in cmake ycm/benchmarks # folder. foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} ) string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG ) set_target_properties( ${BENCHMARK_LIBRARIES} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${PROJECT_BINARY_DIR} ) set_target_properties( ${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${PROJECT_BINARY_DIR} ) endforeach() endif() target_link_libraries( ${PROJECT_NAME} ycm_core ${BENCHMARK_LIBRARIES} ) ycmd-0+20201028+git1d415c5+ds/cpp/ycm/benchmarks/IdentifierCompleter_bench.cpp000066400000000000000000000037551374632460100264400ustar00rootroot00000000000000// Copyright (C) 2017-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 "BenchUtils.h" #include "CandidateRepository.h" #include "CharacterRepository.h" #include "CodePointRepository.h" #include "IdentifierCompleter.h" #include namespace YouCompleteMe { class IdentifierCompleterFixture : public benchmark::Fixture { public: void SetUp( const benchmark::State& ) { CodePointRepository::Instance().ClearCodePoints(); CharacterRepository::Instance().ClearCharacters(); CandidateRepository::Instance().ClearCandidates(); } }; BENCHMARK_DEFINE_F( IdentifierCompleterFixture, CandidatesWithCommonPrefix )( benchmark::State& state ) { std::vector< std::string > candidates = GenerateCandidatesWithCommonPrefix( "a_A_a_", state.range( 0 ) ); IdentifierCompleter completer( std::move( candidates ) ); while ( state.KeepRunning() ) { completer.CandidatesForQuery( "aA", state.range( 1 ) ); } state.SetComplexityN( state.range( 0 ) ); } BENCHMARK_REGISTER_F( IdentifierCompleterFixture, CandidatesWithCommonPrefix ) ->RangeMultiplier( 1 << 4 ) ->Ranges( { { 1, 1 << 16 }, { 0, 0 } } ) ->Complexity(); BENCHMARK_REGISTER_F( IdentifierCompleterFixture, CandidatesWithCommonPrefix ) ->RangeMultiplier( 1 << 4 ) ->Ranges( { { 1, 1 << 16 }, { 10, 10 } } ) ->Complexity(); } // namespace YouCompleteMe ycmd-0+20201028+git1d415c5+ds/cpp/ycm/benchmarks/PythonSupport_bench.cpp000066400000000000000000000074361374632460100253610ustar00rootroot00000000000000// Copyright (C) 2017-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 "BenchUtils.h" #include "CandidateRepository.h" #include "CharacterRepository.h" #include "CodePointRepository.h" #include "PythonSupport.h" #include namespace YouCompleteMe { class PythonSupportFixture : public benchmark::Fixture { public: void SetUp( const benchmark::State& ) { CodePointRepository::Instance().ClearCodePoints(); CharacterRepository::Instance().ClearCharacters(); CandidateRepository::Instance().ClearCandidates(); } }; BENCHMARK_DEFINE_F( PythonSupportFixture, FilterAndSortUnstoredCandidatesWithCommonPrefix )( benchmark::State& state ) { std::vector< std::string > raw_candidates; raw_candidates = GenerateCandidatesWithCommonPrefix( "a_A_a_", state.range( 0 ) ); pybind11::list candidates; for ( auto insertion_text : raw_candidates ) { pybind11::dict candidate; candidate[ "insertion_text" ] = insertion_text; candidates.append( candidate ); } while ( state.KeepRunning() ) { state.PauseTiming(); CharacterRepository::Instance().ClearCharacters(); CandidateRepository::Instance().ClearCandidates(); state.ResumeTiming(); FilterAndSortCandidates( candidates, "insertion_text", "aA", state.range( 1 ) ); } state.SetComplexityN( state.range( 0 ) ); } BENCHMARK_DEFINE_F( PythonSupportFixture, FilterAndSortStoredCandidatesWithCommonPrefix )( benchmark::State& state ) { std::vector< std::string > raw_candidates; raw_candidates = GenerateCandidatesWithCommonPrefix( "a_A_a_", state.range( 0 ) ); pybind11::list candidates; for ( auto insertion_text : raw_candidates ) { pybind11::dict candidate; candidate[ "insertion_text" ] = insertion_text; candidates.append( candidate ); } // Store the candidates in the repository. FilterAndSortCandidates( candidates, "insertion_text", "aA", state.range( 1 ) ); while ( state.KeepRunning() ) { FilterAndSortCandidates( candidates, "insertion_text", "aA", state.range( 1 ) ); } state.SetComplexityN( state.range( 0 ) ); } BENCHMARK_REGISTER_F( PythonSupportFixture, FilterAndSortUnstoredCandidatesWithCommonPrefix ) ->RangeMultiplier( 1 << 4 ) ->Ranges( { { 1, 1 << 16 }, { 0, 0 } } ) ->Complexity(); BENCHMARK_REGISTER_F( PythonSupportFixture, FilterAndSortUnstoredCandidatesWithCommonPrefix ) ->RangeMultiplier( 1 << 4 ) ->Ranges( { { 1, 1 << 16 }, { 50, 50 } } ) ->Complexity(); BENCHMARK_REGISTER_F( PythonSupportFixture, FilterAndSortStoredCandidatesWithCommonPrefix ) ->RangeMultiplier( 1 << 4 ) ->Ranges( { { 1, 1 << 16 }, { 0, 0 } } ) ->Complexity(); BENCHMARK_REGISTER_F( PythonSupportFixture, FilterAndSortStoredCandidatesWithCommonPrefix ) ->RangeMultiplier( 1 << 4 ) ->Ranges( { { 1, 1 << 16 }, { 50, 50 } } ) ->Complexity(); } // namespace YouCompleteMe ycmd-0+20201028+git1d415c5+ds/cpp/ycm/benchmarks/main.cpp000066400000000000000000000016531374632460100222630ustar00rootroot00000000000000// Copyright (C) 2017-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 #include int main( int argc, char** argv ) { pybind11::scoped_interpreter guard{}; benchmark::Initialize(&argc, argv); benchmark::RunSpecifiedBenchmarks(); return 0; } ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/000077500000000000000000000000001374632460100176535ustar00rootroot00000000000000ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/CMakeLists.txt000066400000000000000000000106041374632460100224140ustar00rootroot00000000000000# Copyright (C) 2011 Google Inc. # # 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 . project( ycm_core_tests ) cmake_minimum_required( VERSION 2.8 ) # The gtest library triggers warnings, so we turn them off; it's not up to us to # fix gtest warnings, it's up to upstream. if ( COMPILER_IS_CLANG ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-long-long -Wno-variadic-macros -Wno-missing-field-initializers -Wno-unused-private-field" ) elseif( MSVC ) add_definitions( /W0 ) endif() option( USE_SYSTEM_GMOCK "Set to ON to use the system gmock/gtest libraries" OFF ) if ( USE_SYSTEM_GMOCK ) set( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}" ) find_package( GTest REQUIRED ) find_package( GMock REQUIRED ) else() if ( WIN32 ) # Override BUILD_SHARED_LIBS option in gmock and gtest CMakeLists set( BUILD_SHARED_LIBS ON CACHE BOOL "Build shared libraries (DLLs)." ) endif() add_subdirectory( gmock ) set( GTEST_INCLUDE_DIRS ${gtest_SOURCE_DIR} ${gtest_SOURCE_DIR}/include ) set( GMOCK_INCLUDE_DIRS ${gmock_SOURCE_DIR} ${gmock_SOURCE_DIR}/include ) set( GTEST_LIBRARIES "" ) set( GMOCK_LIBRARIES gmock ) endif() include_directories( ${ycm_core_SOURCE_DIR} ${ycm_core_SOURCE_DIR}/../whereami ) include_directories( SYSTEM ${GMOCK_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS} ) link_directories( ${PYTHON_LIBRARIES} ) file( GLOB_RECURSE SOURCES *.h *.cpp ) # We don't want gmock sources in this target file( GLOB_RECURSE to_remove gmock/*.h gmock/*.cpp CMakeFiles/*.cpp testdata/*.cpp testdata/*.h ) if( to_remove ) list( REMOVE_ITEM SOURCES ${to_remove} ) endif() if ( NOT USE_CLANG_COMPLETER ) file( GLOB_RECURSE to_remove_clang ClangCompleter/*.h ClangCompleter/*.cpp ) if( to_remove_clang ) list( REMOVE_ITEM SOURCES ${to_remove_clang} ) endif() endif() add_executable( ${PROJECT_NAME} ${SOURCES} ) if ( MSVC ) # This is needed to compile tests with the gtest shared library set_target_properties( ${PROJECT_NAME} PROPERTIES COMPILE_DEFINITIONS "GTEST_LINKED_AS_SHARED_LIBRARY=1" ) # Fix gtest build on MSVC 11. See: http://stackoverflow.com/a/8274747 if ( MSVC_VERSION EQUAL 1700 ) add_definitions( /D _VARIADIC_MAX=10 ) endif() # Build gmock and ycm_core_tests targets in cmake ycm/tests folder foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} ) string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG ) set_target_properties( ${GMOCK_LIBRARIES} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${PROJECT_BINARY_DIR} ) set_target_properties( ${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${PROJECT_BINARY_DIR} ) endforeach() endif() target_link_libraries( ${PROJECT_NAME} ycm_core ${GTEST_LIBRARIES} ${GMOCK_LIBRARIES} ) if ( NOT CMAKE_GENERATOR_IS_XCODE ) # There is no portable way of discovering the absolute path of the executable, # but whereami library supports all OS's on which we run tests regularly plus # some *BSD flavours on top of that. add_custom_target( copy_testdata COMMAND cmake -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/testdata ${CMAKE_CURRENT_BINARY_DIR}/testdata ) else() add_custom_target( copy_testdata COMMAND cmake -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/testdata ${CMAKE_CURRENT_BINARY_DIR}/Debug/testdata COMMAND cmake -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/testdata ${CMAKE_CURRENT_BINARY_DIR}/Release/testdata ) endif() add_dependencies( ${PROJECT_NAME} copy_testdata ) ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/CandidateRepository_test.cpp000066400000000000000000000050061374632460100253730ustar00rootroot00000000000000// 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 #include "CandidateRepository.h" #include "Candidate.h" #include "Result.h" namespace YouCompleteMe { class CandidateRepositoryTest : public ::testing::Test { protected: CandidateRepositoryTest() : repo_( CandidateRepository::Instance() ) { } virtual void SetUp() { repo_.ClearCandidates(); } CandidateRepository &repo_; }; TEST_F( CandidateRepositoryTest, Basic ) { std::vector< std::string > inputs; inputs.push_back( "foobar" ); std::vector< const Candidate * > candidates = repo_.GetCandidatesForStrings( std::move( inputs ) ); EXPECT_EQ( "foobar", candidates[ 0 ]->Text() ); } TEST_F( CandidateRepositoryTest, TooLongCandidateSkipped ) { std::vector< std::string > inputs; inputs.push_back( std::string( 81, 'a' ) ); // this one is too long inputs.push_back( std::string( 80, 'b' ) ); // this one is *just* right std::vector< const Candidate * > candidates = repo_.GetCandidatesForStrings( std::move( inputs ) ); EXPECT_EQ( "", candidates[ 0 ]->Text() ); EXPECT_EQ( 'b', candidates[ 1 ]->Text()[ 0 ] ); } TEST_F( CandidateRepositoryTest, UnicodeCandidates ) { std::vector< std::string > inputs; inputs.push_back( "fooδιακριτικός" ); inputs.push_back( "fooδιακός" ); std::vector< const Candidate * > candidates = repo_.GetCandidatesForStrings( std::move( inputs ) ); EXPECT_EQ( "fooδιακριτικός", candidates[ 0 ]->Text() ); EXPECT_EQ( "fooδιακός", candidates[ 1 ]->Text() ); } TEST_F( CandidateRepositoryTest, NonPrintableCandidates ) { std::vector< std::string > inputs; inputs.push_back( "\x01\x05\x0a\x15" ); std::vector< const Candidate * > candidates = repo_.GetCandidatesForStrings( std::move( inputs ) ); EXPECT_EQ( "\x01\x05\x0a\x15", candidates[ 0 ]->Text() ); } } // namespace YouCompleteMe ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/Candidate_test.cpp000066400000000000000000000215041374632460100232740ustar00rootroot00000000000000// 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" #include #include using ::testing::Not; namespace YouCompleteMe { MATCHER_P( HasWordBoundaryCharacters, boundary_chars, std::string( negation ? "has not" : "has" ) + " word boundary characters " + boundary_chars ) { return Candidate( arg ).WordBoundaryChars() == Word( boundary_chars ).Characters(); } TEST( WordBoundaryCharsTest, SimpleOneWord ) { EXPECT_THAT( "simple", HasWordBoundaryCharacters( "s" ) ); } TEST( WordBoundaryCharsTest, PunctuationInMiddle ) { EXPECT_THAT( "simple_foo", HasWordBoundaryCharacters( "sf" ) ); } TEST( WordBoundaryCharsTest, PunctuationStart ) { EXPECT_THAT( "_simple", HasWordBoundaryCharacters( "s" ) ); EXPECT_THAT( ".simple", HasWordBoundaryCharacters( "s" ) ); EXPECT_THAT( "/simple", HasWordBoundaryCharacters( "s" ) ); EXPECT_THAT( ":simple", HasWordBoundaryCharacters( "s" ) ); EXPECT_THAT( "-simple", HasWordBoundaryCharacters( "s" ) ); EXPECT_THAT( "«simple", HasWordBoundaryCharacters( "s" ) ); EXPECT_THAT( "…simple", HasWordBoundaryCharacters( "s" ) ); EXPECT_THAT( "𐬺simple", HasWordBoundaryCharacters( "s" ) ); } TEST( WordBoundaryCharsTest, PunctuationStartButFirstDigit ) { EXPECT_THAT( "_1simple", HasWordBoundaryCharacters( "" ) ); EXPECT_THAT( "_1simPle", HasWordBoundaryCharacters( "P" ) ); EXPECT_THAT( "…𝟝simple", HasWordBoundaryCharacters( "" ) ); EXPECT_THAT( "…𝟝simPle", HasWordBoundaryCharacters( "P" ) ); } TEST( WordBoundaryCharsTest, ManyPunctuationStart ) { EXPECT_THAT( "___simple", HasWordBoundaryCharacters( "s" ) ); EXPECT_THAT( ".;/simple", HasWordBoundaryCharacters( "s" ) ); EXPECT_THAT( "«…𐬺simple", HasWordBoundaryCharacters( "s" ) ); } TEST( WordBoundaryCharsTest, PunctuationStartAndInMiddle ) { EXPECT_THAT( "_simple_foo", HasWordBoundaryCharacters( "sf" ) ); EXPECT_THAT( "/simple.foo", HasWordBoundaryCharacters( "sf" ) ); EXPECT_THAT( "𐬺simple—foo", HasWordBoundaryCharacters( "sf" ) ); } TEST( WordBoundaryCharsTest, ManyPunctuationStartAndInMiddle ) { EXPECT_THAT( "___simple__foo", HasWordBoundaryCharacters( "sf" ) ); EXPECT_THAT( "./;:simple..foo", HasWordBoundaryCharacters( "sf" ) ); EXPECT_THAT( "«𐬺…simple——foo", HasWordBoundaryCharacters( "sf" ) ); } TEST( WordBoundaryCharsTest, SimpleCapitalStart ) { EXPECT_THAT( "Simple", HasWordBoundaryCharacters( "S" ) ); EXPECT_THAT( "Σimple", HasWordBoundaryCharacters( "Σ" ) ); } TEST( WordBoundaryCharsTest, SimpleCapitalTwoWord ) { EXPECT_THAT( "SimpleStuff", HasWordBoundaryCharacters( "SS" ) ); EXPECT_THAT( "ΣimpleΣtuff", HasWordBoundaryCharacters( "ΣΣ" ) ); } TEST( WordBoundaryCharsTest, SimpleCapitalTwoWordPunctuationMiddle ) { EXPECT_THAT( "Simple_Stuff", HasWordBoundaryCharacters( "SS" ) ); EXPECT_THAT( "Σimple…Σtuff", HasWordBoundaryCharacters( "ΣΣ" ) ); } TEST( WordBoundaryCharsTest, JavaCase ) { EXPECT_THAT( "simpleStuffFoo", HasWordBoundaryCharacters( "sSF" ) ); EXPECT_THAT( "σimpleΣtuffΦoo", HasWordBoundaryCharacters( "σΣΦ" ) ); } TEST( WordBoundaryCharsTest, UppercaseSequence ) { EXPECT_THAT( "simpleSTUFF", HasWordBoundaryCharacters( "sS" ) ); EXPECT_THAT( "σimpleΣTUFF", HasWordBoundaryCharacters( "σΣ" ) ); } TEST( WordBoundaryCharsTest, UppercaseSequenceInMiddle ) { EXPECT_THAT( "simpleSTUFFfoo", HasWordBoundaryCharacters( "sS" ) ); EXPECT_THAT( "σimpleΣTUFFφoo", HasWordBoundaryCharacters( "σΣ" ) ); } TEST( WordBoundaryCharsTest, UppercaseSequenceInMiddlePunctuation ) { EXPECT_THAT( "simpleSTUFF_Foo", HasWordBoundaryCharacters( "sSF" ) ); EXPECT_THAT( "σimpleΣTUFF…Φoo", HasWordBoundaryCharacters( "σΣΦ" ) ); } TEST( WordBoundaryCharsTest, UppercaseSequenceInMiddlePunctuationLowercase ) { EXPECT_THAT( "simpleSTUFF_foo", HasWordBoundaryCharacters( "sSf" ) ); EXPECT_THAT( "simpleSTUFF.foo", HasWordBoundaryCharacters( "sSf" ) ); EXPECT_THAT( "σimpleΣTUFF…φoo", HasWordBoundaryCharacters( "σΣφ" ) ); } TEST( WordBoundaryCharsTest, AllCapsSimple ) { EXPECT_THAT( "SIMPLE", HasWordBoundaryCharacters( "S" ) ); EXPECT_THAT( "ΣIMPLE", HasWordBoundaryCharacters( "Σ" ) ); } TEST( GetWordBoundaryCharsTest, AllCapsPunctuationStart ) { EXPECT_THAT( "_SIMPLE", HasWordBoundaryCharacters( "S" ) ); EXPECT_THAT( ".SIMPLE", HasWordBoundaryCharacters( "S" ) ); EXPECT_THAT( "«ΣIMPLE", HasWordBoundaryCharacters( "Σ" ) ); EXPECT_THAT( "…ΣIMPLE", HasWordBoundaryCharacters( "Σ" ) ); } TEST( WordBoundaryCharsTest, AllCapsPunctuationMiddle ) { EXPECT_THAT( "SIMPLE_STUFF", HasWordBoundaryCharacters( "SS" ) ); EXPECT_THAT( "SIMPLE/STUFF", HasWordBoundaryCharacters( "SS" ) ); EXPECT_THAT( "SIMPLE—ΣTUFF", HasWordBoundaryCharacters( "SΣ" ) ); EXPECT_THAT( "ΣIMPLE…STUFF", HasWordBoundaryCharacters( "ΣS" ) ); } TEST( WordBoundaryCharsTest, AllCapsPunctuationMiddleAndStart ) { EXPECT_THAT( "_SIMPLE_STUFF", HasWordBoundaryCharacters( "SS" ) ); EXPECT_THAT( ":SIMPLE.STUFF", HasWordBoundaryCharacters( "SS" ) ); EXPECT_THAT( "«ΣIMPLE—ΣTUFF", HasWordBoundaryCharacters( "ΣΣ" ) ); EXPECT_THAT( "𐬺SIMPLE—ΣTUFF", HasWordBoundaryCharacters( "SΣ" ) ); } TEST( CandidateTest, TextValid ) { EXPECT_EQ( "foo", Candidate( "foo" ).Text() ); } MATCHER_P( IsSubsequence, candidate, std::string( negation ? "is not" : "is" ) + " a subsequence of " + candidate ) { Result result = Candidate( candidate ).QueryMatchResult( Word( arg ) ); return result.IsSubsequence(); } TEST( CandidateTest, QueryMatchResultIsSubsequence ) { EXPECT_THAT( "F𐍈oβaÅAr", IsSubsequence( "F𐍈oβaÅAr" ) ); EXPECT_THAT( "FβÅA", IsSubsequence( "F𐍈oβaÅAr" ) ); EXPECT_THAT( "F", IsSubsequence( "F𐍈oβaÅAr" ) ); EXPECT_THAT( "ÅA", IsSubsequence( "F𐍈oβaÅAr" ) ); EXPECT_THAT( "A", IsSubsequence( "F𐍈oβaÅAr" ) ); EXPECT_THAT( "β", IsSubsequence( "F𐍈oβaÅAr" ) ); EXPECT_THAT( "f𐍈oβaåar", IsSubsequence( "F𐍈oβaÅAr" ) ); EXPECT_THAT( "f𐍈oβaåAr", IsSubsequence( "F𐍈oβaÅAr" ) ); EXPECT_THAT( "f𐍈oβaÅar", IsSubsequence( "F𐍈oβaÅAr" ) ); EXPECT_THAT( "f𐍈oβaÅAr", IsSubsequence( "F𐍈oβaÅAr" ) ); EXPECT_THAT( "F𐍈oβaÅAr", IsSubsequence( "F𐍈oβaÅAr" ) ); EXPECT_THAT( "f𐍈oβaaar", IsSubsequence( "F𐍈oβaÅAr" ) ); EXPECT_THAT( "f𐍈oβaAar", IsSubsequence( "F𐍈oβaÅAr" ) ); EXPECT_THAT( "fβÅA", IsSubsequence( "F𐍈oβaÅAr" ) ); EXPECT_THAT( "fβaa", IsSubsequence( "F𐍈oβaÅAr" ) ); EXPECT_THAT( "β", IsSubsequence( "F𐍈oβaÅAr" ) ); EXPECT_THAT( "f", IsSubsequence( "F𐍈oβaÅAr" ) ); EXPECT_THAT( "fβår", IsSubsequence( "F𐍈oβaÅAr" ) ); } TEST( CandidateTest, QueryMatchResultIsNotSubsequence ) { EXPECT_THAT( "g𐍈o", Not( IsSubsequence( "F𐍈oβaÅAr" ) ) ); EXPECT_THAT( "R", Not( IsSubsequence( "F𐍈oβaÅAr" ) ) ); EXPECT_THAT( "O", Not( IsSubsequence( "F𐍈oβaÅAr" ) ) ); EXPECT_THAT( "𐍈O", Not( IsSubsequence( "F𐍈oβaÅAr" ) ) ); EXPECT_THAT( "OβA", Not( IsSubsequence( "F𐍈oβaÅAr" ) ) ); EXPECT_THAT( "FβAR", Not( IsSubsequence( "F𐍈oβaÅAr" ) ) ); EXPECT_THAT( "FβÅAR", Not( IsSubsequence( "F𐍈oβaÅAr" ) ) ); EXPECT_THAT( "Oar", Not( IsSubsequence( "F𐍈oβaÅAr" ) ) ); EXPECT_THAT( "F𐍈oβaÅår", Not( IsSubsequence( "F𐍈oβaÅAr" ) ) ); EXPECT_THAT( "F𐍈oβaåår", Not( IsSubsequence( "F𐍈oβaÅAr" ) ) ); EXPECT_THAT( "F𐍈oβaÅÅr", Not( IsSubsequence( "F𐍈oβaÅAr" ) ) ); EXPECT_THAT( "F𐍈oβaåÅr", Not( IsSubsequence( "F𐍈oβaÅAr" ) ) ); EXPECT_THAT( "f𐍈oβaÅÅr", Not( IsSubsequence( "F𐍈oβaÅAr" ) ) ); EXPECT_THAT( "F𐍈oβaaÅr", Not( IsSubsequence( "F𐍈oβaÅAr" ) ) ); EXPECT_THAT( "F𐍈OβaÅAr", Not( IsSubsequence( "F𐍈oβaÅAr" ) ) ); EXPECT_THAT( "F𐍈Oβaåar", Not( IsSubsequence( "F𐍈oβaÅAr" ) ) ); EXPECT_THAT( "f𐍈Oβaåar", Not( IsSubsequence( "F𐍈oβaÅAr" ) ) ); EXPECT_THAT( "f𐍈oβaåaR", Not( IsSubsequence( "F𐍈oβaÅAr" ) ) ); } } // namespace YouCompleteMe ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/CharacterRepository_test.cpp000066400000000000000000000032251374632460100254140ustar00rootroot00000000000000// 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 "CharacterRepository.h" #include "TestUtils.h" #include #include using ::testing::Pointee; using ::testing::UnorderedElementsAre; namespace YouCompleteMe { class CharacterRepositoryTest : public ::testing::Test { protected: CharacterRepositoryTest() : repo_( CharacterRepository::Instance() ) { } virtual void SetUp() { repo_.ClearCharacters(); } CharacterRepository &repo_; }; TEST_F( CharacterRepositoryTest, GetCharacters ) { CharacterSequence character_objects = repo_.GetCharacters( { "α", "ω" } ); EXPECT_THAT( repo_.NumStoredCharacters(), 2 ); EXPECT_THAT( character_objects, UnorderedElementsAre( Pointee( IsCharacterWithProperties< CharacterTuple >( { "α", "α", "α", "Α", true, true, false, false } ) ), Pointee( IsCharacterWithProperties< CharacterTuple >( { "ω", "ω", "ω", "Ω", true, true, false, false } ) ) ) ); } } // namespace YouCompleteMe ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/Character_test.cpp000066400000000000000000000343301374632460100233150ustar00rootroot00000000000000// 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 "CharacterRepository.h" #include "CodePoint.h" #include "TestUtils.h" #include #include #include using ::testing::TestWithParam; using ::testing::ValuesIn; namespace YouCompleteMe { // Check that characters equalities and inequalities are symmetric (a == b if // and only if b == a). MATCHER( CharactersAreEqual, "" ) { for ( size_t i = 0; i < arg.size() - 1; ++i ) { for ( size_t j = i + 1; j < arg.size(); ++j ) { if ( !( *arg[ i ] == *arg[ j ] ) || !( *arg[ j ] == *arg[ i ] ) ) { return false; } } } return true; } MATCHER( CharactersAreNotEqual, "" ) { for ( size_t i = 0; i < arg.size() - 1; ++i ) { for ( size_t j = i + 1; j < arg.size(); ++j ) { if ( *arg[ i ] == *arg[ j ] || *arg[ j ] == *arg[ i ] ) { return false; } } } return true; } MATCHER( CharactersAreEqualWhenCaseIsIgnored, "" ) { for ( size_t i = 0; i < arg.size() - 1; ++i ) { for ( size_t j = i + 1; j < arg.size(); ++j ) { if ( !( arg[ i ]->EqualsIgnoreCase( *arg[ j ] ) ) || !( arg[ j ]->EqualsIgnoreCase( *arg[ i ] ) ) ) { return false; } } } return true; } MATCHER( CharactersAreNotEqualWhenCaseIsIgnored, "" ) { for ( size_t i = 0; i < arg.size() - 1; ++i ) { for ( size_t j = i + 1; j < arg.size(); ++j ) { if ( arg[ i ]->EqualsIgnoreCase( *arg[ j ] ) || arg[ j ]->EqualsIgnoreCase( *arg[ i ] ) ) { return false; } } } return true; } MATCHER( BaseCharactersAreEqual, "" ) { for ( size_t i = 0; i < arg.size() - 1; ++i ) { for ( size_t j = i + 1; j < arg.size(); ++j ) { if ( !( arg[ i ]->EqualsBase( *arg[ j ] ) ) || !( arg[ j ]->EqualsBase( *arg[ i ] ) ) ) { return false; } } } return true; } MATCHER( BaseCharactersAreNotEqual, "" ) { for ( size_t i = 0; i < arg.size() - 1; ++i ) { for ( size_t j = i + 1; j < arg.size(); ++j ) { if ( arg[ i ]->EqualsBase( *arg[ j ] ) || arg[ j ]->EqualsBase( *arg[ i ] ) ) { return false; } } } return true; } struct TextCharacterPair { const char* text; CharacterTuple character_tuple; }; std::ostream& operator<<( std::ostream& os, const TextCharacterPair &pair ) { os << "{ " << PrintToString( pair.text ) << ", " << PrintToString( pair.character_tuple ) << " }"; return os; } class CharacterTest : public TestWithParam< TextCharacterPair > { protected: CharacterTest() : repo_( CharacterRepository::Instance() ) { } virtual void SetUp() { repo_.ClearCharacters(); pair_ = GetParam(); } CharacterRepository &repo_; const char* text_; TextCharacterPair pair_; }; TEST( CharacterTest, ExceptionThrownWhenLeadingByteInCodePointIsInvalid ) { try { // Leading byte cannot start with bits '10'. Character( "\xaf" ); FAIL() << "Expected UnicodeDecodeError exception."; } catch ( const UnicodeDecodeError &error ) { EXPECT_STREQ( error.what(), "Invalid leading byte in code point." ); } catch ( ... ) { FAIL() << "Expected UnicodeDecodeError exception."; } } TEST( CharacterTest, ExceptionThrownWhenCodePointIsOutOfBound ) { try { // Leading byte indicates a sequence of three bytes but only two are given. Character( "\xe4\xbf" ); FAIL() << "Expected UnicodeDecodeError exception."; } catch ( const UnicodeDecodeError &error ) { EXPECT_STREQ( error.what(), "Invalid code point length." ); } catch ( ... ) { FAIL() << "Expected UnicodeDecodeError exception."; } } TEST_P( CharacterTest, PropertiesAreCorrect ) { EXPECT_THAT( Character( pair_.text ), IsCharacterWithProperties( pair_.character_tuple ) ); } const std::array< TextCharacterPair, 13 > tests = { { // Musical symbol eighth note (three code points) { "𝅘𝅥𝅮", { "𝅘𝅥𝅮", "𝅘", "𝅘𝅥𝅮", "𝅘𝅥𝅮", false, false, false, false } }, // Punctuations // Fullwidth low line { "_", { "_", "_", "_", "_", true, false, true, false } }, // Wavy dash { "〰", { "〰", "〰", "〰", "〰", true, false, true, false } }, // Left floor { "⌊", { "⌊", "⌊", "⌊", "⌊", true, false, true, false } }, // Fullwidth right square bracket { "]", { "]", "]", "]", "]", true, false, true, false } }, { "«", { "«", "«", "«", "«", true, false, true, false } }, // Right substitution bracket { "⸃", { "⸃", "⸃", "⸃", "⸃", true, false, true, false } }, // Large one dot over two dots punctuation { "𐬽", { "𐬽", "𐬽", "𐬽", "𐬽", true, false, true, false } }, // Letters // Latin capital letter S with dot below and dot above (three code points) { "Ṩ", { "Ṩ", "s", "ṩ", "ṩ", false, true, false, true } }, // Greek small letter alpha with psili and varia and ypogegrammeni (four code // points) { "ᾂ", { "ᾂ", "α", "ἂι", "ἊΙ", false, true, false, false } }, // Greek capital letter eta with dasia and perispomeni and prosgegrammeni // (four code points) { "ᾟ", { "ᾟ", "η", "ἧι", "ἧΙ", false, true, false, true } }, // Hiragana voiced iteration mark (two code points) { "ゞ", { "ゞ", "ゝ", "ゞ", "ゞ", false, true, false, false } }, // Hebrew letter shin with Dagesh and Shin dot (three code points) { "שּׁ", { "שּׁ", "ש", "שּׁ", "שּׁ", false, true, false, false } } } }; INSTANTIATE_TEST_SUITE_P( UnicodeTest, CharacterTest, ValuesIn( tests ) ); TEST( CharacterTest, Equality ) { CharacterRepository &repo( CharacterRepository::Instance() ); // The lowercase of the Latin capital letter e with acute "É" (which can be // represented as the Latin capital letter "E" plus the combining acute // character) is the Latin small letter e with acute "é". EXPECT_THAT( repo.GetCharacters( { "e", "é", "E", "É" } ), CharactersAreNotEqual() ); EXPECT_THAT( repo.GetCharacters( { "é", "é" } ), CharactersAreEqual() ); EXPECT_THAT( repo.GetCharacters( { "É", "É" } ), CharactersAreEqual() ); EXPECT_THAT( repo.GetCharacters( { "e", "E" } ), CharactersAreEqualWhenCaseIsIgnored() ); EXPECT_THAT( repo.GetCharacters( { "é", "É", "É" } ), CharactersAreEqualWhenCaseIsIgnored() ); EXPECT_THAT( repo.GetCharacters( { "e", "é", "é", "E", "É", "É" } ), BaseCharactersAreEqual() ); // The Greek capital letter omega "Ω" is the same character as the ohm sign // "Ω". The lowercase of both characters is the Greek small letter omega "ω". EXPECT_THAT( repo.GetCharacters( { "Ω", "Ω" } ), CharactersAreEqual() ); EXPECT_THAT( repo.GetCharacters( { "ω", "Ω", "Ω" } ), CharactersAreEqualWhenCaseIsIgnored() ); EXPECT_THAT( repo.GetCharacters( { "ω", "Ω", "Ω" } ), BaseCharactersAreEqual() ); // The Latin capital letter a with ring above "Å" (which can be represented as // the Latin capital letter "A" plus the combining ring above character) is // the same character as the angstrom sign "Å". The lowercase of these // characters is the Latin small letter a with ring above "å" (which can also // be represented as the Latin small letter "a" plus the combining ring above // character). EXPECT_THAT( repo.GetCharacters( { "a", "å", "A", "Å" } ), CharactersAreNotEqual() ); EXPECT_THAT( repo.GetCharacters( { "å", "å" } ), CharactersAreEqual() ); EXPECT_THAT( repo.GetCharacters( { "Å", "Å", "Å" } ), CharactersAreEqual() ); EXPECT_THAT( repo.GetCharacters( { "å", "å", "Å", "Å", "Å" } ), CharactersAreEqualWhenCaseIsIgnored() ); EXPECT_THAT( repo.GetCharacters( { "a", "å", "å", "A", "Å", "Å", "Å" } ), BaseCharactersAreEqual() ); // The uppercase of the Greek small letter sigma "σ" and Greek small letter // final sigma "ς" is the Greek capital letter sigma "Σ". EXPECT_THAT( repo.GetCharacters( { "σ", "ς", "Σ" } ), CharactersAreNotEqual() ); EXPECT_THAT( repo.GetCharacters( { "σ", "ς", "Σ" } ), CharactersAreEqualWhenCaseIsIgnored() ); EXPECT_THAT( repo.GetCharacters( { "σ", "ς", "Σ" } ), BaseCharactersAreEqual() ); // The lowercase of the Greek capital theta symbol "ϴ" and capital letter // theta "Θ" is the Greek small letter theta "θ". There is also the Greek // theta symbol "ϑ" whose uppercase is "Θ". EXPECT_THAT( repo.GetCharacters( { "θ", "ϑ", "ϴ", "Θ" } ), CharactersAreNotEqual() ); EXPECT_THAT( repo.GetCharacters( { "θ", "ϑ", "ϴ", "Θ" } ), CharactersAreEqualWhenCaseIsIgnored() ); EXPECT_THAT( repo.GetCharacters( { "θ", "ϑ", "ϴ", "Θ" } ), BaseCharactersAreEqual() ); // In the Latin alphabet, the uppercase of "i" (with a dot) is "I" (without a // dot). However, in the Turkish alphabet (a variant of the Latin alphabet), // there are two distinct versions of the letter "i": // - "ı" (without a dot) whose uppercase is "I" (without a dot); // - "i" (with a dot) whose uppercase is "İ" (with a dot), which can also be // represented as the letter "I" plus the combining dot above character. // Since our matching is language-independent, the Turkish form is ignored and // the letter "ı" (without a dot) does not match "I" (without a dot) when the // case is ignored. Similarly, "ı" plus the combining dot above character does // not match "İ" (with a dot) or "I" plus the combining dot above character // but "i" (with a dot) plus the combining dot above does. EXPECT_THAT( repo.GetCharacters( { "i", "I", "ı", "ı̇", "i̇", "İ" } ), CharactersAreNotEqual() ); EXPECT_THAT( repo.GetCharacters( { "İ", "İ" } ), CharactersAreEqual() ); EXPECT_THAT( repo.GetCharacters( { "i", "I" } ), CharactersAreEqualWhenCaseIsIgnored() ); EXPECT_THAT( repo.GetCharacters( { "i̇", "İ", "İ" } ), CharactersAreEqualWhenCaseIsIgnored() ); EXPECT_THAT( repo.GetCharacters( { "ı", "ı̇", "I", "İ" } ), CharactersAreNotEqualWhenCaseIsIgnored() ); EXPECT_THAT( repo.GetCharacters( { "i", "ı" } ), BaseCharactersAreNotEqual() ); EXPECT_THAT( repo.GetCharacters( { "i", "i̇", "I", "İ", "İ" } ), BaseCharactersAreEqual() ); EXPECT_THAT( repo.GetCharacters( { "ı", "ı̇" } ), BaseCharactersAreEqual() ); } TEST( CharacterTest, SmartMatching ) { // The letter "é" and "É" appear twice in the tests as they can be represented // on one code point or two ("e"/"E" plus the combining acute character). EXPECT_TRUE ( Character( "e" ).MatchesSmart( Character( "e" ) ) ); EXPECT_TRUE ( Character( "e" ).MatchesSmart( Character( "é" ) ) ); EXPECT_TRUE ( Character( "e" ).MatchesSmart( Character( "é" ) ) ); EXPECT_TRUE ( Character( "e" ).MatchesSmart( Character( "E" ) ) ); EXPECT_TRUE ( Character( "e" ).MatchesSmart( Character( "É" ) ) ); EXPECT_TRUE ( Character( "e" ).MatchesSmart( Character( "É" ) ) ); EXPECT_FALSE( Character( "é" ).MatchesSmart( Character( "e" ) ) ); EXPECT_TRUE ( Character( "é" ).MatchesSmart( Character( "é" ) ) ); EXPECT_TRUE ( Character( "é" ).MatchesSmart( Character( "é" ) ) ); EXPECT_FALSE( Character( "é" ).MatchesSmart( Character( "E" ) ) ); EXPECT_TRUE ( Character( "é" ).MatchesSmart( Character( "É" ) ) ); EXPECT_TRUE ( Character( "é" ).MatchesSmart( Character( "É" ) ) ); EXPECT_FALSE( Character( "é" ).MatchesSmart( Character( "e" ) ) ); EXPECT_TRUE ( Character( "é" ).MatchesSmart( Character( "é" ) ) ); EXPECT_TRUE ( Character( "é" ).MatchesSmart( Character( "é" ) ) ); EXPECT_FALSE( Character( "é" ).MatchesSmart( Character( "E" ) ) ); EXPECT_TRUE ( Character( "é" ).MatchesSmart( Character( "É" ) ) ); EXPECT_TRUE ( Character( "é" ).MatchesSmart( Character( "É" ) ) ); EXPECT_FALSE( Character( "E" ).MatchesSmart( Character( "e" ) ) ); EXPECT_FALSE( Character( "E" ).MatchesSmart( Character( "é" ) ) ); EXPECT_FALSE( Character( "E" ).MatchesSmart( Character( "é" ) ) ); EXPECT_TRUE ( Character( "E" ).MatchesSmart( Character( "E" ) ) ); EXPECT_TRUE ( Character( "E" ).MatchesSmart( Character( "É" ) ) ); EXPECT_TRUE ( Character( "E" ).MatchesSmart( Character( "É" ) ) ); EXPECT_FALSE( Character( "É" ).MatchesSmart( Character( "e" ) ) ); EXPECT_FALSE( Character( "É" ).MatchesSmart( Character( "é" ) ) ); EXPECT_FALSE( Character( "É" ).MatchesSmart( Character( "é" ) ) ); EXPECT_FALSE( Character( "É" ).MatchesSmart( Character( "E" ) ) ); EXPECT_TRUE ( Character( "É" ).MatchesSmart( Character( "É" ) ) ); EXPECT_TRUE ( Character( "É" ).MatchesSmart( Character( "É" ) ) ); EXPECT_FALSE( Character( "É" ).MatchesSmart( Character( "e" ) ) ); EXPECT_FALSE( Character( "É" ).MatchesSmart( Character( "é" ) ) ); EXPECT_FALSE( Character( "É" ).MatchesSmart( Character( "é" ) ) ); EXPECT_FALSE( Character( "É" ).MatchesSmart( Character( "E" ) ) ); EXPECT_TRUE ( Character( "É" ).MatchesSmart( Character( "É" ) ) ); EXPECT_TRUE ( Character( "É" ).MatchesSmart( Character( "É" ) ) ); EXPECT_FALSE( Character( "è" ).MatchesSmart( Character( "e" ) ) ); EXPECT_FALSE( Character( "è" ).MatchesSmart( Character( "é" ) ) ); EXPECT_FALSE( Character( "è" ).MatchesSmart( Character( "é" ) ) ); EXPECT_FALSE( Character( "è" ).MatchesSmart( Character( "E" ) ) ); EXPECT_FALSE( Character( "è" ).MatchesSmart( Character( "É" ) ) ); EXPECT_FALSE( Character( "è" ).MatchesSmart( Character( "É" ) ) ); } } // namespace YouCompleteMe ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/ClangCompleter/000077500000000000000000000000001374632460100225525ustar00rootroot00000000000000ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/ClangCompleter/ClangCompleter_test.cpp000066400000000000000000000163051374632460100272210ustar00rootroot00000000000000// 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 "ClangCompleter.h" #include "CompletionData.h" #include "../TestUtils.h" #include #include namespace YouCompleteMe { using ::testing::ElementsAre; using ::testing::WhenSorted; using ::testing::StrEq; using ::testing::Property; using ::testing::Contains; TEST( ClangCompleterTest, CandidatesForLocationInFile ) { ClangCompleter completer; std::vector< CompletionData > completions_class = completer.CandidatesForLocationInFile( PathToTestFile( "basic.cpp" ).string(), PathToTestFile( "basic.cpp" ).string(), 29, 7, std::vector< UnsavedFile >(), std::vector< std::string >() ); std::vector< CompletionData > completions_struct = completer.CandidatesForLocationInFile( PathToTestFile( "basic.cpp" ).string(), PathToTestFile( "basic.cpp" ).string(), 30, 7, std::vector< UnsavedFile >(), std::vector< std::string >() ); ASSERT_TRUE( !completions_struct.empty() ); ASSERT_TRUE( !completions_class.empty() ); } TEST( ClangCompleterTest, BufferTextNoParens ) { ClangCompleter completer; std::vector< CompletionData > completions = completer.CandidatesForLocationInFile( PathToTestFile( "basic.cpp" ).string(), PathToTestFile( "basic.cpp" ).string(), 29, 7, std::vector< UnsavedFile >(), std::vector< std::string >() ); ASSERT_TRUE( !completions.empty() ); EXPECT_THAT( completions, Contains( Property( &CompletionData::TextToInsertInBuffer, StrEq( "barbar" ) ) ) ); } TEST( ClangCompleterTest, MemberFunctionWithDefaults ) { ClangCompleter completer; std::vector< CompletionData > completions = completer.CandidatesForLocationInFile( PathToTestFile( "basic.cpp" ).string(), PathToTestFile( "basic.cpp" ).string(), 30, 7, std::vector< UnsavedFile >(), std::vector< std::string >() ); for ( size_t i = 0; i < completions.size(); ++i ) { if ( completions[i].TextToInsertInBuffer() == "foobar" ) { EXPECT_STREQ( "foobar( int a, float b = 3.0, char c = '\\n' )", completions[i].MainCompletionText().c_str() ); break; } } } TEST( ClangCompleterTest, CandidatesObjCForLocationInFile ) { ClangCompleter completer; std::vector< std::string > flags; flags.push_back( "-x" ); flags.push_back( "objective-c" ); std::vector< CompletionData > completions = completer.CandidatesForLocationInFile( PathToTestFile( "SWObject.m" ).string(), PathToTestFile( "SWObject.m" ).string(), 6, 16, std::vector< UnsavedFile >(), flags ); ASSERT_TRUE( !completions.empty() ); EXPECT_THAT( completions[0].TextToInsertInBuffer(), StrEq( "withArg2:" ) ); } TEST( ClangCompleterTest, CandidatesObjCFuncForLocationInFile ) { ClangCompleter completer; std::vector< std::string > flags; flags.push_back( "-x" ); flags.push_back( "objective-c" ); std::vector< CompletionData > completions = completer.CandidatesForLocationInFile( PathToTestFile( "SWObject.m" ).string(), PathToTestFile( "SWObject.m" ).string(), 9, 3, std::vector< UnsavedFile >(), flags ); ASSERT_TRUE( !completions.empty() ); EXPECT_THAT( completions[0].TextToInsertInBuffer(), StrEq( "(void)test:(int)arg1 withArg2:(int)arg2 withArg3:(int)arg3" ) ); } TEST( ClangCompleterTest, GetDefinitionLocation ) { ClangCompleter completer; std::string filename = PathToTestFile( "basic.cpp" ).string(); // Clang operates on the reasonable assumption that line and column numbers // are 1-based. Location actual_location_struct = completer.GetDefinitionLocation( filename, filename, 26, 3, std::vector< UnsavedFile >(), std::vector< std::string >() ); Location actual_location_class_method = completer.GetDefinitionLocation( filename, filename, 29, 7, std::vector< UnsavedFile >(), std::vector< std::string >() ); Location actual_location_class = completer.GetDefinitionLocation( filename, filename, 27, 3, std::vector< UnsavedFile >(), std::vector< std::string >() ); Location actual_location_enum_value = completer.GetDefinitionLocation( filename, filename, 31, 25, std::vector< UnsavedFile >(), std::vector< std::string >() ); Location actual_location_enum = completer.GetDefinitionLocation( filename, filename, 31, 3, std::vector< UnsavedFile >(), std::vector< std::string >() ); EXPECT_EQ( Location( filename, 12, 8 ), actual_location_struct ); EXPECT_EQ( Location( filename, 1, 7 ), actual_location_class ); EXPECT_EQ( Location( filename, 22, 35 ), actual_location_enum ); EXPECT_EQ( Location( filename, 22, 16 ), actual_location_enum_value ); EXPECT_EQ( Location( filename, 7, 8 ), actual_location_class_method ); } TEST( ClangCompleterTest, GetDocString ) { ClangCompleter completer; std::vector< CompletionData > completions = completer.CandidatesForLocationInFile( PathToTestFile( "basic.cpp" ).string(), PathToTestFile( "basic.cpp" ).string(), 30, 7, std::vector< UnsavedFile >(), std::vector< std::string >() ); for ( size_t i = 0; i < completions.size(); ++i ) { if ( completions[i].TextToInsertInBuffer() == "x" ) { EXPECT_STREQ( "A docstring.", completions[i].DocString().c_str() ); break; } } } TEST( ClangCompleterTest, ExceptionThrownOnReparseFailure ) { ClangCompleter completer; // Create a translation unit for a C++ file that is not saved on disk. std::string filename = PathToTestFile( "unsaved_file.cpp" ).string(); UnsavedFile unsaved_file; unsaved_file.filename_ = filename; completer.UpdateTranslationUnit( filename, std::vector< UnsavedFile >{ unsaved_file }, std::vector< std::string >() ); try { // libclang cannot reparse a file that doesn't exist and is not in the list // of unsaved files. completer.UpdateTranslationUnit( filename, std::vector< UnsavedFile >(), std::vector< std::string >() ); FAIL() << "Expected ClangParseError exception."; } catch ( const ClangParseError &error ) { EXPECT_STREQ( error.what(), "Failed to parse the translation unit." ); } catch ( ... ) { FAIL() << "Expected ClangParseError exception."; } } } // namespace YouCompleteMe ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/ClangCompleter/TranslationUnit_test.cpp000066400000000000000000000172011374632460100274540ustar00rootroot00000000000000// 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 "CompletionData.h" #include "TranslationUnitStore.h" #include "Utils.h" #include "../TestUtils.h" #include #include #include using ::testing::ElementsAre; using ::testing::WhenSorted; namespace YouCompleteMe { class TranslationUnitTest : public ::testing::Test { protected: virtual void SetUp() { clang_index_ = clang_createIndex( 0, 0 ); } virtual void TearDown() { clang_disposeIndex( clang_index_ ); } CXIndex clang_index_; }; TEST_F( TranslationUnitTest, ExceptionThrownOnParseFailure ) { // Create a translation unit for a C++ file that is not saved on disk. std::string filename = PathToTestFile( "unsaved_file.cpp" ).string(); UnsavedFile unsaved_file; unsaved_file.filename_ = filename; try { // libclang requires a valid index to parse a file. TranslationUnit( filename, std::vector< UnsavedFile >{ unsaved_file }, std::vector< std::string >(), nullptr ); FAIL() << "Expected ClangParseError exception."; } catch ( const ClangParseError &error ) { EXPECT_STREQ( error.what(), "Invalid arguments supplied " "when parsing the translation unit." ); } catch ( ... ) { FAIL() << "Expected ClangParseError exception."; } } TEST_F( TranslationUnitTest, GoToDefinitionWorks ) { auto test_file = PathToTestFile( "goto.cpp" ).string(); TranslationUnit unit( test_file, std::vector< UnsavedFile >(), std::vector< std::string >(), clang_index_ ); Location location = unit.GetDefinitionLocation( test_file, 17, 3, std::vector< UnsavedFile >() ); EXPECT_EQ( 1, location.line_number_ ); EXPECT_EQ( 8, location.column_number_ ); EXPECT_TRUE( !location.filename_.empty() ); } TEST_F( TranslationUnitTest, GoToDefinitionFails ) { auto test_file = PathToTestFile( "goto.cpp" ).string(); TranslationUnit unit( test_file, std::vector< UnsavedFile >(), std::vector< std::string >(), clang_index_ ); Location location = unit.GetDefinitionLocation( test_file, 19, 3, std::vector< UnsavedFile >() ); EXPECT_FALSE( location.IsValid() ); } TEST_F( TranslationUnitTest, GoToDeclarationWorks ) { auto test_file = PathToTestFile( "goto.cpp" ).string(); TranslationUnit unit( test_file, std::vector< UnsavedFile >(), std::vector< std::string >(), clang_index_ ); Location location = unit.GetDeclarationLocation( test_file, 19, 3, std::vector< UnsavedFile >() ); EXPECT_EQ( 12, location.line_number_ ); EXPECT_EQ( 8, location.column_number_ ); EXPECT_TRUE( !location.filename_.empty() ); } TEST_F( TranslationUnitTest, GoToDeclarationWorksOnDefinition ) { auto test_file = PathToTestFile( "goto.cpp" ).string(); TranslationUnit unit( test_file, std::vector< UnsavedFile >(), std::vector< std::string >(), clang_index_ ); Location location = unit.GetDeclarationLocation( test_file, 16, 6, std::vector< UnsavedFile >() ); EXPECT_EQ( 14, location.line_number_ ); EXPECT_EQ( 6, location.column_number_ ); EXPECT_TRUE( !location.filename_.empty() ); } TEST_F( TranslationUnitTest, GoToWorks ) { auto test_file = PathToTestFile( "goto.cpp" ).string(); TranslationUnit unit( test_file, std::vector< UnsavedFile >(), std::vector< std::string >(), clang_index_ ); Location location = unit.GetDefinitionOrDeclarationLocation( test_file, 16, 8, std::vector< UnsavedFile >() ); EXPECT_EQ( 14, location.line_number_ ); EXPECT_EQ( 6, location.column_number_ ); EXPECT_TRUE( !location.filename_.empty() ); location = unit.GetDefinitionOrDeclarationLocation( test_file, 14, 9, std::vector< UnsavedFile >() ); EXPECT_EQ( 16, location.line_number_ ); EXPECT_EQ( 6, location.column_number_ ); EXPECT_TRUE( !location.filename_.empty() ); } TEST_F( TranslationUnitTest, InvalidTranslationUnitStore ) { // libclang fails to parse a file with no extension and no language flag -x // given. TranslationUnitStore translation_unit_store{ clang_index_ }; try { translation_unit_store.GetOrCreate( PathToTestFile( "file_without_extension" ).string(), std::vector< UnsavedFile >(), std::vector< std::string >() ); FAIL() << "Expected ClangParseError exception."; } catch ( const ClangParseError &error ) { EXPECT_STREQ( error.what(), "An AST deserialization error occurred while parsing " "the translation unit." ); } catch ( ... ) { FAIL() << "Expected ClangParseError exception."; } } TEST_F( TranslationUnitTest, InvalidTranslationUnit ) { TranslationUnit unit; EXPECT_TRUE( unit.IsCurrentlyUpdating() ); std::vector< CompletionData > completion_data_vector = unit.CandidatesForLocation( "", 1, 1, std::vector< UnsavedFile >() ); EXPECT_TRUE( completion_data_vector.empty() ); EXPECT_EQ( Location(), unit.GetDeclarationLocation( "", 1, 1, std::vector< UnsavedFile >() ) ); EXPECT_EQ( Location(), unit.GetDefinitionLocation( "", 1, 1, std::vector< UnsavedFile >() ) ); EXPECT_EQ( Location(), unit.GetDefinitionOrDeclarationLocation( "", 1, 1, std::vector< UnsavedFile >() ) ); EXPECT_EQ( std::string( "Internal error: no translation unit" ), unit.GetTypeAtLocation( "", 1, 1, std::vector< UnsavedFile >() ) ); EXPECT_EQ( std::string( "Internal error: no translation unit" ), unit.GetEnclosingFunctionAtLocation( "", 1, 1, std::vector< UnsavedFile >() ) ); EXPECT_EQ( DocumentationData(), unit.GetDocsForLocation( Location(), std::vector< UnsavedFile >(), false ) ); } } // namespace YouCompleteMe ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/CodePointRepository_test.cpp000066400000000000000000000032531374632460100254050ustar00rootroot00000000000000// 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 "TestUtils.h" #include #include using ::testing::Pointee; using ::testing::UnorderedElementsAre; namespace YouCompleteMe { class CodePointRepositoryTest : public ::testing::Test { protected: CodePointRepositoryTest() : repo_( CodePointRepository::Instance() ) { } virtual void SetUp() { repo_.ClearCodePoints(); } CodePointRepository &repo_; }; TEST_F( CodePointRepositoryTest, GetCodePoints ) { CodePointSequence code_point_objects = repo_.GetCodePoints( { "α", "ω" } ); EXPECT_THAT( repo_.NumStoredCodePoints(), 2 ); EXPECT_THAT( code_point_objects, UnorderedElementsAre( Pointee( IsCodePointWithProperties< CodePointTuple >( { "α", "α", "Α", true, false, false, BreakProperty::OTHER } ) ), Pointee( IsCodePointWithProperties< CodePointTuple >( { "ω", "ω", "Ω", true, false, false, BreakProperty::OTHER } ) ) ) ); } } // namespace YouCompleteMe ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/CodePoint_test.cpp000066400000000000000000000170711374632460100233100ustar00rootroot00000000000000// 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 "TestUtils.h" #include #include #include using ::testing::TestWithParam; using ::testing::ValuesIn; namespace YouCompleteMe { struct TextCodePointPair { const char* text; CodePointTuple code_point_tuple; }; std::ostream& operator<<( std::ostream& os, const TextCodePointPair &pair ) { os << "{ " << PrintToString( pair.text ) << ", " << PrintToString( pair.code_point_tuple ) << " }"; return os; } class CodePointTest : public TestWithParam< TextCodePointPair > { protected: CodePointTest() : repo_( CodePointRepository::Instance() ) { } virtual void SetUp() { repo_.ClearCodePoints(); pair_ = GetParam(); } CodePointRepository &repo_; TextCodePointPair pair_; }; TEST_P( CodePointTest, PropertiesAreCorrect ) { EXPECT_THAT( CodePoint( pair_.text ), IsCodePointWithProperties( pair_.code_point_tuple ) ); } // Tests mostly based on the table // http://www.unicode.org/reports/tr29/tr29-37.html#Grapheme_Cluster_Break_Property_Values const TextCodePointPair tests[] = { { "\r", { "\r", "\r", "\r", false, false, false, BreakProperty::CR } }, { "\n", { "\n", "\n", "\n", false, false, false, BreakProperty::LF } }, { "\t", { "\t", "\t", "\t", false, false, false, BreakProperty::CONTROL } }, // Line separator { "\xe2\x80\xa8", { "\xe2\x80\xa8", "\xe2\x80\xa8", "\xe2\x80\xa8", false, false, false, BreakProperty::CONTROL } }, // Paragraph separator { "\xe2\x80\xa9", { "\xe2\x80\xa9", "\xe2\x80\xa9", "\xe2\x80\xa9", false, false, false, BreakProperty::CONTROL } }, // Zero-width space { "​", { "​", "​", "​", false, false, false, BreakProperty::CONTROL } }, // Combining grave accent { "̀", { "̀", "̀", "̀", false, false, false, BreakProperty::EXTEND } }, // Bengali vowel sign Aa { "া", { "া", "া", "া", false, false, false, BreakProperty::EXTEND } }, // Zero-width non-joiner { "‌", { "‌", "‌", "‌", false, false, false, BreakProperty::EXTEND } }, // Combining cyrillic millions sign { "҈", { "҈", "҈", "҈", false, false, false, BreakProperty::EXTEND } }, // Zero-width joiner { "‍", { "‍", "‍", "‍", false, false, false, BreakProperty::ZWJ } }, // Regional indicator symbol letter b { "🇧", { "🇧", "🇧", "🇧", false, false, false, BreakProperty::REGIONAL_INDICATOR } }, // Arabic number sign { "؀", { "؀", "؀", "؀", false, false, false, BreakProperty::PREPEND } }, // Thai character Sara Am { "ำ", { "ำ", "ำ", "ำ", true, false, false, BreakProperty::SPACINGMARK } }, // Lao vowel sign Am { "ຳ", { "ຳ", "ຳ", "ຳ", true, false, false, BreakProperty::SPACINGMARK } }, // Hangul Choseong Kiyeok { "ᄀ", { "ᄀ", "ᄀ", "ᄀ", true, false, false, BreakProperty::L } }, // Hangul Choseong Filler { "ᅟ", { "ᅟ", "ᅟ", "ᅟ", true, false, false, BreakProperty::L } }, // Hangul Choseong Tikeut-mieum { "ꥠ", { "ꥠ", "ꥠ", "ꥠ", true, false, false, BreakProperty::L } }, // Hangul Choseong Ssangyeorinhieuh { "ꥼ", { "ꥼ", "ꥼ", "ꥼ", true, false, false, BreakProperty::L } }, // Hangul Jungseong Filler { "ᅠ", { "ᅠ", "ᅠ", "ᅠ", true, false, false, BreakProperty::V } }, // Hangul Jungseong Ssangaraea { "ᆢ", { "ᆢ", "ᆢ", "ᆢ", true, false, false, BreakProperty::V } }, // Hangul Jungseong O-yeo { "ힰ", { "ힰ", "ힰ", "ힰ", true, false, false, BreakProperty::V } }, // Hangul Jungseong Araea-e { "ퟆ", { "ퟆ", "ퟆ", "ퟆ", true, false, false, BreakProperty::V } }, // Hangul Jongseong Kiyeok { "ᆨ", { "ᆨ", "ᆨ", "ᆨ", true, false, false, BreakProperty::T } }, // Hangul Jongseong Yeorinhieuh { "ᇹ", { "ᇹ", "ᇹ", "ᇹ", true, false, false, BreakProperty::T } }, // Hangul Jongseong Nieun-rieul { "ퟋ", { "ퟋ", "ퟋ", "ퟋ", true, false, false, BreakProperty::T } }, // Hangul Jongseong Phieuph-thieuth { "ퟻ", { "ퟻ", "ퟻ", "ퟻ", true, false, false, BreakProperty::T } }, // Hangul syllable Ga { "가", { "가", "가", "가", true, false, false, BreakProperty::LV } }, // Hangul syllable Gae { "개", { "개", "개", "개", true, false, false, BreakProperty::LV } }, // Hangul syllable Gya { "갸", { "갸", "갸", "갸", true, false, false, BreakProperty::LV } }, // Hangul syllable Gag { "각", { "각", "각", "각", true, false, false, BreakProperty::LVT } }, // Hangul syllable Gagg { "갂", { "갂", "갂", "갂", true, false, false, BreakProperty::LVT } }, // Hangul syllable Gags { "갃", { "갃", "갃", "갃", true, false, false, BreakProperty::LVT } }, // Hangul syllable Gan { "간", { "간", "간", "간", true, false, false, BreakProperty::LVT } }, // Copyright sign { "©", { "©", "©", "©", false, false, false, BreakProperty::EXTPICT } }, // Characters with none of the above break properties. // One byte characters // NOTE: there are no Unicode letters coded with one byte (i.e. ASCII letters) // without a lowercase or uppercase version. { "r", { "r", "r", "R", true, false, false, BreakProperty::OTHER } }, { "R", { "R", "r", "r", true, false, true, BreakProperty::OTHER } }, { "'", { "'", "'", "'", false, true, false, BreakProperty::OTHER } }, { "=", { "=", "=", "=", false, false, false, BreakProperty::OTHER } }, // Two bytes characters { "é", { "é", "é", "É", true, false, false, BreakProperty::OTHER } }, { "É", { "É", "é", "é", true, false, true, BreakProperty::OTHER } }, { "ĸ", { "ĸ", "ĸ", "ĸ", true, false, false, BreakProperty::OTHER } }, { "»", { "»", "»", "»", false, true, false, BreakProperty::OTHER } }, { "¥", { "¥", "¥", "¥", false, false, false, BreakProperty::OTHER } }, // Three bytes characters { "ⱥ", { "ⱥ", "ⱥ", "Ⱥ", true, false, false, BreakProperty::OTHER } }, { "Ɐ", { "Ɐ", "ɐ", "ɐ", true, false, true, BreakProperty::OTHER } }, { "の", { "の", "の", "の", true, false, false, BreakProperty::OTHER } }, { "•", { "•", "•", "•", false, true, false, BreakProperty::OTHER } }, { "∅", { "∅", "∅", "∅", false, false, false, BreakProperty::OTHER } }, // Four bytes characters { "𐐫", { "𐐫", "𐐫", "𐐃", true, false, false, BreakProperty::OTHER } }, { "𐐃", { "𐐃", "𐐫", "𐐫", true, false, true, BreakProperty::OTHER } }, { "𐰬", { "𐰬", "𐰬", "𐰬", true, false, false, BreakProperty::OTHER } }, { "𐬿", { "𐬿", "𐬿", "𐬿", false, true, false, BreakProperty::OTHER } }, { "𝛁", { "𝛁", "𝛁", "𝛁", false, false, false, BreakProperty::OTHER } }, }; INSTANTIATE_TEST_SUITE_P( UnicodeTest, CodePointTest, ValuesIn( tests ) ); } // namespace YouCompleteMe ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/IdentifierCompleter_test.cpp000066400000000000000000000256301374632460100253610ustar00rootroot00000000000000// 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 #include #include "IdentifierCompleter.h" #include "Utils.h" #include "TestUtils.h" using ::testing::ElementsAre; using ::testing::IsEmpty; using ::testing::WhenSorted; namespace YouCompleteMe { TEST( IdentifierCompleterTest, SortOnEmptyQuery ) { EXPECT_THAT( IdentifierCompleter( { "foo", "bar" } ).CandidatesForQuery( "" ), ElementsAre( "bar", "foo" ) ); } TEST( IdentifierCompleterTest, IgnoreEmptyCandidate ) { EXPECT_THAT( IdentifierCompleter( { "" } ).CandidatesForQuery( "" ), IsEmpty() ); } TEST( IdentifierCompleterTest, IgnoreCandidatesShorterThanQuery ) { EXPECT_THAT( IdentifierCompleter( { "fo", "foo" } ).CandidatesForQuery( "fooo" ), IsEmpty() ); } TEST( IdentifierCompleterTest, NoDuplicatesReturned ) { EXPECT_THAT( IdentifierCompleter( { "foobar", "foobar", "foobar" } ).CandidatesForQuery( "foo" ), ElementsAre( "foobar" ) ); } TEST( IdentifierCompleterTest, OneCandidate ) { EXPECT_THAT( IdentifierCompleter( { "foobar" } ).CandidatesForQuery( "fbr" ), ElementsAre( "foobar" ) ); } TEST( IdentifierCompleterTest, ManyCandidateSimple ) { EXPECT_THAT( IdentifierCompleter( { "foobar", "foobartest", "Foobartest" } ).CandidatesForQuery( "fbr" ), WhenSorted( ElementsAre( "Foobartest", "foobar", "foobartest" ) ) ); } TEST( IdentifierCompleterTest, SmartCaseFiltering ) { EXPECT_THAT( IdentifierCompleter( { "fooBar", "fooBaR" } ).CandidatesForQuery( "fBr" ), ElementsAre( "fooBaR", "fooBar" ) ); } TEST( IdentifierCompleterTest, FirstCharSameAsQueryWins ) { EXPECT_THAT( IdentifierCompleter( { "foobar", "afoobar" } ).CandidatesForQuery( "fbr" ), ElementsAre( "foobar", "afoobar" ) ); } TEST( IdentifierCompleterTest, CompleteMatchForWordBoundaryCharsWins ) { EXPECT_THAT( IdentifierCompleter( { "FooBarQux", "FBaqux" } ).CandidatesForQuery( "fbq" ), ElementsAre( "FooBarQux", "FBaqux" ) ); EXPECT_THAT( IdentifierCompleter( { "CompleterTest", "CompleteMatchForWordBoundaryCharsWins" } ) .CandidatesForQuery( "ct" ), ElementsAre( "CompleterTest", "CompleteMatchForWordBoundaryCharsWins" ) ); EXPECT_THAT( IdentifierCompleter( { "FooBar", "FooBarRux" } ).CandidatesForQuery( "fbr" ), ElementsAre( "FooBarRux", "FooBar" ) ); EXPECT_THAT( IdentifierCompleter( { "foo-bar", "foo-bar-rux" } ).CandidatesForQuery( "fbr" ), ElementsAre( "foo-bar-rux", "foo-bar" ) ); EXPECT_THAT( IdentifierCompleter( { "foo.bar", "foo.bar.rux" } ).CandidatesForQuery( "fbr" ), ElementsAre( "foo.bar.rux", "foo.bar" ) ); } TEST( IdentifierCompleterTest, RatioUtilizationTieBreak ) { EXPECT_THAT( IdentifierCompleter( { "aGaaFooBarQux", "aBaafbq" } ).CandidatesForQuery( "fbq" ), ElementsAre( "aGaaFooBarQux", "aBaafbq" ) ); EXPECT_THAT( IdentifierCompleter( { "aFooBarQux", "afbq" } ).CandidatesForQuery( "fbq" ), ElementsAre( "aFooBarQux", "afbq" ) ); EXPECT_THAT( IdentifierCompleter( { "acaaCaaFooGxx", "aCaafoog" } ).CandidatesForQuery( "caafoo" ), ElementsAre( "acaaCaaFooGxx", "aCaafoog" ) ); EXPECT_THAT( IdentifierCompleter( { "FooBarQux", "FooBarQuxZaa" } ).CandidatesForQuery( "fbq" ), ElementsAre( "FooBarQux", "FooBarQuxZaa" ) ); EXPECT_THAT( IdentifierCompleter( { "FooBar", "FooBarRux" } ).CandidatesForQuery( "fba" ), ElementsAre( "FooBar", "FooBarRux" ) ); } TEST( IdentifierCompleterTest, QueryPrefixOfCandidateWins ) { EXPECT_THAT( IdentifierCompleter( { "foobar", "fbaroo" } ).CandidatesForQuery( "foo" ), ElementsAre( "foobar", "fbaroo" ) ); } TEST( IdentifierCompleterTest, LowerMatchCharIndexSumWins ) { EXPECT_THAT( IdentifierCompleter( { "ratio_of_word_boundary_chars_in_query_", "first_char_same_in_query_and_text_" } ).CandidatesForQuery( "charinq" ), ElementsAre( "first_char_same_in_query_and_text_", "ratio_of_word_boundary_chars_in_query_" ) ); EXPECT_THAT( IdentifierCompleter( { "barfooq", "barquxfoo" } ).CandidatesForQuery( "foo" ), ElementsAre( "barfooq", "barquxfoo" ) ); EXPECT_THAT( IdentifierCompleter( { "xxxxxxabc", "xxabcxxxx" } ).CandidatesForQuery( "abc" ), ElementsAre( "xxabcxxxx", "xxxxxxabc" ) ); EXPECT_THAT( IdentifierCompleter( { "FooBarQux", "FaBarQux" } ).CandidatesForQuery( "fbq" ), ElementsAre( "FaBarQux", "FooBarQux" ) ); } TEST( IdentifierCompleterTest, ShorterCandidateWins ) { EXPECT_THAT( IdentifierCompleter( { "CompleterT", "CompleterTest" } ).CandidatesForQuery( "co" ), ElementsAre( "CompleterT", "CompleterTest" ) ); EXPECT_THAT( IdentifierCompleter( { "CompleterT", "CompleterTest" } ).CandidatesForQuery( "plet" ), ElementsAre( "CompleterT", "CompleterTest" ) ); } TEST( IdentifierCompleterTest, SameLowercaseCandidateWins ) { EXPECT_THAT( IdentifierCompleter( { "foobar", "Foobar" } ).CandidatesForQuery( "foo" ), ElementsAre( "foobar", "Foobar" ) ); } TEST( IdentifierCompleterTest, PreferLowercaseCandidate ) { EXPECT_THAT( IdentifierCompleter( { "chatContentExtension", "ChatContentExtension" } ).CandidatesForQuery( "chatContent" ), ElementsAre( "chatContentExtension", "ChatContentExtension" ) ); EXPECT_THAT( IdentifierCompleter( { "fooBar", "FooBar" } ).CandidatesForQuery( "oba" ), ElementsAre( "fooBar", "FooBar" ) ); } TEST( IdentifierCompleterTest, ShorterAndLowercaseWins ) { EXPECT_THAT( IdentifierCompleter( { "STDIN_FILENO", "stdin" } ).CandidatesForQuery( "std" ), ElementsAre( "stdin", "STDIN_FILENO" ) ); } TEST( IdentifierCompleterTest, NonAlnumChars ) { EXPECT_THAT( IdentifierCompleter( { "font-family", "font-face" } ).CandidatesForQuery( "fo" ), ElementsAre( "font-face", "font-family" ) ); } TEST( IdentifierCompleterTest, NonAlnumStartChar ) { EXPECT_THAT( IdentifierCompleter( { "-zoo-foo" } ).CandidatesForQuery( "-z" ), ElementsAre( "-zoo-foo" ) ); } TEST( IdentifierCompleterTest, UnicodeCandidates ) { EXPECT_THAT( IdentifierCompleter( { "uni¢𐍈d€" } ).CandidatesForQuery( "¢" ), ElementsAre( "uni¢𐍈d€" ) ); } TEST( IdentifierCompleterTest, NonPrintableCandidates ) { EXPECT_THAT( IdentifierCompleter( { "\x01\x1f\x7f" } ).CandidatesForQuery( "\x1f" ), ElementsAre( "\x01\x1f\x7f" ) ); } TEST( IdentifierCompleterTest, LotOfCandidates ) { // Generate a lot of candidates of the form [a-z]{5} in reverse order. std::vector< std::string > candidates; for ( int i = 0; i < 2048; ++i ) { std::string candidate = ""; int letter = i; for ( int pos = 0; pos < 5; letter /= 26, ++pos ) { candidate = std::string( 1, letter % 26 + 'a' ) + candidate; } candidates.insert( candidates.begin(), candidate ); } IdentifierCompleter completer( candidates ); std::reverse( candidates.begin(), candidates.end() ); EXPECT_THAT( completer.CandidatesForQuery( "aa" ), candidates ); EXPECT_THAT( completer.CandidatesForQuery( "aa", 2 ), ElementsAre( "aaaaa", "aaaab" ) ); } TEST( IdentifierCompleterTest, TagsEndToEndWorks ) { IdentifierCompleter completer; std::vector< std::string > tag_files; tag_files.push_back( PathToTestFile( "basic.tags" ).string() ); completer.AddIdentifiersToDatabaseFromTagFiles( tag_files ); EXPECT_THAT( completer.CandidatesForQueryAndType( "fo", "cpp" ), ElementsAre( "foosy", "fooaaa" ) ); } // Filetype checking TEST( IdentifierCompleterTest, ManyCandidateSimpleFileType ) { IdentifierCompleter completer; EXPECT_THAT( IdentifierCompleter( { "foobar", "foobartest", "Foobartest" }, "c", "foo" ).CandidatesForQueryAndType( "fbr", "c" ), WhenSorted( ElementsAre( "Foobartest", "foobar", "foobartest" ) ) ); } TEST( IdentifierCompleterTest, ManyCandidateSimpleWrongFileType ) { IdentifierCompleter completer; EXPECT_THAT( IdentifierCompleter( { "foobar", "foobartest", "Foobartest" }, "c", "foo" ).CandidatesForQueryAndType( "fbr", "cpp" ), IsEmpty() ); } } // namespace YouCompleteMe ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/IdentifierUtils_test.cpp000066400000000000000000000065521374632460100245310ustar00rootroot00000000000000// 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 "IdentifierUtils.h" #include "TestUtils.h" #include "IdentifierDatabase.h" #include #include #include namespace YouCompleteMe { namespace fs = std::filesystem; using ::testing::ElementsAre; using ::testing::ContainerEq; using ::testing::IsEmpty; using ::testing::WhenSorted; using ::testing::Pair; using ::testing::UnorderedElementsAre; TEST( IdentifierUtilsTest, ExtractIdentifiersFromTagsFileWorks ) { fs::path root = fs::current_path().root_path(); fs::path testfile = PathToTestFile( "basic.tags" ); /* VS2017 returns C:\DIRECT~1\ paths without fs::weakly_canonical() */ fs::path testfile_parent = fs::weakly_canonical( testfile.parent_path() ); EXPECT_THAT( ExtractIdentifiersFromTagsFile( testfile ), UnorderedElementsAre( Pair( "cpp", UnorderedElementsAre( Pair( ( testfile_parent / "foo" ).string(), ElementsAre( "i1", "foosy" ) ), Pair( ( testfile_parent / "bar" ).string(), ElementsAre( "i1", "fooaaa" ) ) ) ), Pair( "fakelang", UnorderedElementsAre( Pair( ( root / "foo" ).string(), ElementsAre( "zoro" ) ) ) ), Pair( "cs", UnorderedElementsAre( Pair( ( root / "m_oo" ).string(), ElementsAre( "#bleh" ) ) ) ), Pair( "foobar", UnorderedElementsAre( Pair( ( testfile_parent / "foo.bar" ).string(), ElementsAre( "API", "DELETE" ) ) ) ), Pair( "c", UnorderedElementsAre( Pair( ( root / "foo" / "zoo" ).string(), ElementsAre( "Floo::goo" ) ), Pair( ( root / "foo" / "goo maa" ).string(), ElementsAre( "!goo" ) ) ) ) ) ); } TEST( IdentifierUtilsTest, TagFileIsDirectory ) { fs::path testfile = PathToTestFile( "directory.tags" ); EXPECT_THAT( ExtractIdentifiersFromTagsFile( testfile ), IsEmpty() ); } TEST( IdentifierUtilsTest, TagFileIsEmpty ) { fs::path testfile = PathToTestFile( "empty.tags" ); EXPECT_THAT( ExtractIdentifiersFromTagsFile( testfile ), IsEmpty() ); } TEST( IdentifierUtilsTest, TagLanguageMissing ) { fs::path testfile = PathToTestFile( "invalid_tag_file_format.tags" ); EXPECT_THAT( ExtractIdentifiersFromTagsFile( testfile ), IsEmpty() ); } TEST( IdentifierUtilsTest, TagFileInvalidPath ) { fs::path testfile = PathToTestFile( "invalid_path_to_tag_file.tags" ); EXPECT_THAT( ExtractIdentifiersFromTagsFile( testfile ), IsEmpty() ); } } // namespace YouCompleteMe ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/Normalization_test.cpp000066400000000000000000000047361374632460100242560ustar00rootroot00000000000000// 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 "CharacterRepository.h" #include "CodePoint.h" #include "TestUtils.h" #include #include #include using ::testing::TestWithParam; using ::testing::ValuesIn; namespace YouCompleteMe { struct NormalizationTuple { const char* source; const char* nfc; const char* nfd; const char* nfkc; const char* nfkd; }; std::ostream& operator<<( std::ostream& os, const NormalizationTuple &tuple ) { os << "{ " << PrintToString( tuple.source ) << ", " << PrintToString( tuple.nfc ) << ", " << PrintToString( tuple.nfd ) << ", " << PrintToString( tuple.nfkc ) << ", " << PrintToString( tuple.nfkd ) << " }"; return os; } class NormalizationTest : public TestWithParam< NormalizationTuple > { protected: NormalizationTest() : repo_( CharacterRepository::Instance() ) { } virtual void SetUp() { repo_.ClearCharacters(); tuple_ = GetParam(); } CharacterRepository &repo_; NormalizationTuple tuple_; }; TEST_P( NormalizationTest, NormalizationFormDecompositionIsConform ) { EXPECT_THAT( Character( tuple_.source ).Normal(), Equals( tuple_.nfd ) ); EXPECT_THAT( Character( tuple_.nfc ).Normal(), Equals( tuple_.nfd ) ); EXPECT_THAT( Character( tuple_.nfd ).Normal(), Equals( tuple_.nfd ) ); EXPECT_THAT( Character( tuple_.nfkc ).Normal(), Equals( tuple_.nfkd ) ); EXPECT_THAT( Character( tuple_.nfkd ).Normal(), Equals( tuple_.nfkd ) ); } // Tests generated from // https://unicode.org/Public/UCD/latest/ucd/NormalizationTest.txt const NormalizationTuple tests[] = { #include "NormalizationCases.inc" }; INSTANTIATE_TEST_SUITE_P( UnicodeTest, NormalizationTest, ValuesIn( tests ) ); } // namespace YouCompleteMe ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/TestUtils.cpp000066400000000000000000000071551374632460100223270ustar00rootroot00000000000000// 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 "TestUtils.h" #include namespace std { namespace filesystem { void PrintTo( const fs::path &path, std::ostream *os ) { *os << path; } } // namespace filesystem } // namespace std namespace YouCompleteMe { std::ostream& operator<<( std::ostream& os, const CodePointTuple &code_point ) { os << "{ " << PrintToString( code_point.normal_ ) << ", " << PrintToString( code_point.folded_case_ ) << ", " << PrintToString( code_point.swapped_case_ ) << ", " << PrintToString( code_point.is_letter_ ) << ", " << PrintToString( code_point.is_punctuation_ ) << ", " << PrintToString( code_point.is_uppercase_ ) << ", " << PrintToString( code_point.break_property_ ) << " }"; return os; } std::ostream& operator<<( std::ostream& os, const CodePoint &code_point ) { os << CodePointTuple( code_point ); return os; } std::ostream& operator<<( std::ostream& os, const CodePoint *code_point ) { os << "*" << *code_point; return os; } std::ostream& operator<<( std::ostream& os, const CharacterTuple &character ) { os << "{ " << PrintToString( character.normal_ ) << ", " << PrintToString( character.base_ ) << ", " << PrintToString( character.folded_case_ ) << ", " << PrintToString( character.swapped_case_ ) << ", " << PrintToString( character.is_base_ ) << ", " << PrintToString( character.is_letter_ ) << ", " << PrintToString( character.is_punctuation_ ) << ", " << PrintToString( character.is_uppercase_ ) << " }"; return os; } std::ostream& operator<<( std::ostream& os, const Character &character ) { os << PrintToString( CharacterTuple( character ) ); return os; } std::ostream& operator<<( std::ostream& os, const Character *character ) { os << "*" << *character; return os; } std::ostream& operator<<( std::ostream& os, const WordTuple &word ) { os << "{ " << PrintToString( word.text_ ) << ", { "; const std::vector< const char* > &characters( word.characters_ ); auto character_pos = characters.begin(); if ( character_pos != characters.end() ) { os << PrintToString( *character_pos ); ++character_pos; for ( ; character_pos != characters.end() ; ++character_pos ) { os << ", " << PrintToString( *character_pos ); } os << " }"; } return os; } std::ostream& operator<<( std::ostream& os, const fs::path *path ) { os << *path; return os; } fs::path PathToTestFile( std::string_view filepath ) { int dirname_length; int exec_length = wai_getExecutablePath( NULL, 0, NULL ); std::unique_ptr< char[] > executable( new char [ exec_length ] ); wai_getExecutablePath( executable.get(), exec_length, &dirname_length ); executable[ dirname_length ] = '\0'; fs::path path_to_testdata = fs::path( executable.get() ) / "testdata"; return path_to_testdata / fs::path( filepath ); } } // namespace YouCompleteMe ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/TestUtils.h000066400000000000000000000151471374632460100217740ustar00rootroot00000000000000// 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 TESTUTILS_H_G4RKMGUD #define TESTUTILS_H_G4RKMGUD #include "Character.h" #include "CodePoint.h" #include "Word.h" #include #include #include #include using ::testing::PrintToString; namespace fs = std::filesystem; namespace YouCompleteMe { // Tuple-like structures to help writing tests for CodePoint, Character, and // Word objects. We can't use std::tuple because its constructor is explicit in // old versions of the GNU C++ library which prevent us to do things like: // std::vector< std::tuple< ... > > tuples{ { ... } } struct CodePointTuple { CodePointTuple() : CodePointTuple( "", "", "", false, false, false, BreakProperty::OTHER ) { } CodePointTuple( const CodePoint &code_point ) : CodePointTuple( code_point.Normal().c_str(), code_point.FoldedCase().c_str(), code_point.SwappedCase().c_str(), code_point.IsLetter(), code_point.IsPunctuation(), code_point.IsUppercase(), code_point.GetBreakProperty() ) { } CodePointTuple( std::string_view normal, std::string_view folded_case, std::string_view swapped_case, bool is_letter, bool is_punctuation, bool is_uppercase, BreakProperty break_property ) : normal_( normal ), folded_case_( folded_case ), swapped_case_( swapped_case ), is_letter_( is_letter ), is_punctuation_( is_punctuation ), is_uppercase_( is_uppercase ), break_property_( break_property ) { } bool operator== ( const CodePointTuple &other ) const { return normal_ == other.normal_ && folded_case_ == other.folded_case_ && swapped_case_ == other.swapped_case_ && is_letter_ == other.is_letter_ && is_punctuation_ == other.is_punctuation_ && is_uppercase_ == other.is_uppercase_ && break_property_ == other.break_property_; }; std::string normal_; std::string folded_case_; std::string swapped_case_; bool is_letter_; bool is_punctuation_; bool is_uppercase_; BreakProperty break_property_; }; struct CharacterTuple { CharacterTuple() : CharacterTuple( "", "", "", "", false, false, false, false ) { } CharacterTuple( const Character &character ) : CharacterTuple( character.Normal(), character.Base(), character.FoldedCase(), character.SwappedCase(), character.IsBase(), character.IsLetter(), character.IsPunctuation(), character.IsUppercase() ) { } CharacterTuple( std::string_view normal, std::string_view base, std::string_view folded_case, std::string_view swapped_case, bool is_base, bool is_letter, bool is_punctuation, bool is_uppercase ) : normal_( normal ), base_( base ), folded_case_( folded_case ), swapped_case_( swapped_case ), is_base_( is_base ), is_letter_( is_letter ), is_punctuation_( is_punctuation ), is_uppercase_( is_uppercase ) { } bool operator== ( const CharacterTuple &other ) const { return normal_ == other.normal_ && base_ == other.base_ && folded_case_ == other.folded_case_ && swapped_case_ == other.swapped_case_ && is_base_ == other.is_base_ && is_letter_ == other.is_letter_ && is_punctuation_ == other.is_punctuation_ && is_uppercase_ == other.is_uppercase_; }; 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_; }; struct WordTuple { WordTuple() : WordTuple( "", {} ) { } WordTuple( const char* text, const std::vector< const char* > &characters ) : text_( text ), characters_( characters ) { } bool operator== ( const WordTuple &other ) const { return text_ == other.text_ && characters_ == other.characters_; }; const char* text_; std::vector< const char* > characters_; }; // Pretty print the CodePoint, Character, and Word objects in the tests. std::ostream& operator<<( std::ostream& os, const CodePointTuple &code_point ); std::ostream& operator<<( std::ostream& os, const CodePoint &code_point ); std::ostream& operator<<( std::ostream& os, const CodePoint *code_point ); std::ostream& operator<<( std::ostream& os, const CharacterTuple &character ); std::ostream& operator<<( std::ostream& os, const Character &character ); std::ostream& operator<<( std::ostream& os, const Character *character ); std::ostream& operator<<( std::ostream& os, const WordTuple &word ); // These matchers are used to remove the "is equal to" output from gtest. MATCHER_P( IsCodePointWithProperties, properties, PrintToString( properties ) ) { return CodePointTuple( arg ) == properties; } MATCHER_P( IsCharacterWithProperties, properties, PrintToString( properties ) ) { return CharacterTuple( arg ) == properties; } MATCHER_P( Equals, expected, PrintToString( expected ) ) { return arg == expected; } MATCHER_P( ContainsPointees, expected, PrintToString( expected ) ) { if ( arg.size() != expected.size() ) { return false; } auto actual_pos = arg.begin(); auto expected_pos = expected.begin(); for ( ; actual_pos != arg.end() && expected_pos != expected.end(); ++actual_pos, ++expected_pos ) { if ( !( **actual_pos == **expected_pos ) ) { return false; } } return true; } fs::path PathToTestFile( std::string_view filepath ); } // namespace YouCompleteMe #endif /* end of include guard: TESTUTILS_H_G4RKMGUD */ ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/Utils_test.cpp000066400000000000000000000027201374632460100225170ustar00rootroot00000000000000// Copyright (C) 2017-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 "TestUtils.h" #include "Utils.h" #include namespace YouCompleteMe { TEST( UtilsTest, IsUppercase ) { EXPECT_TRUE( IsUppercase( 'A' ) ); EXPECT_TRUE( IsUppercase( 'B' ) ); EXPECT_TRUE( IsUppercase( 'Z' ) ); EXPECT_FALSE( IsUppercase( 'a' ) ); EXPECT_FALSE( IsUppercase( 'b' ) ); EXPECT_FALSE( IsUppercase( 'z' ) ); EXPECT_FALSE( IsUppercase( '$' ) ); EXPECT_FALSE( IsUppercase( '@' ) ); EXPECT_FALSE( IsUppercase( '~' ) ); } TEST( UtilsTest, Lowercase ) { EXPECT_EQ( Lowercase( 'a' ), 'a' ); EXPECT_EQ( Lowercase( 'z' ), 'z' ); EXPECT_EQ( Lowercase( 'A' ), 'a' ); EXPECT_EQ( Lowercase( 'Z' ), 'z' ); EXPECT_EQ( Lowercase( ';' ), ';' ); EXPECT_EQ( Lowercase( "lOwER_CasE" ), "lower_case" ); } } // namespace YouCompleteMe ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/Word_test.cpp000066400000000000000000000057241374632460100223410ustar00rootroot00000000000000// 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 "CharacterRepository.h" #include "TestUtils.h" #include "Word.h" #include #include #include using ::testing::TestWithParam; using ::testing::ValuesIn; namespace YouCompleteMe { class WordTest : public TestWithParam< WordTuple > { protected: WordTest() : repo_( CharacterRepository::Instance() ) { } virtual void SetUp() { repo_.ClearCharacters(); word_ = WordTuple( GetParam() ); } CharacterRepository &repo_; WordTuple word_; }; std::ostream& operator<<( std::ostream& os, const CharacterSequence &characters ) { os << PrintToString( characters ); return os; } TEST_P( WordTest, BreakIntoCharacters ) { std::vector< std::string > characters; for ( const auto &character : word_.characters_ ) { characters.push_back( character ); } EXPECT_THAT( Word( word_.text_ ).Characters(), ContainsPointees( repo_.GetCharacters( std::move( characters ) ) ) ); } // Tests generated from // https://www.unicode.org/Public/UCD/latest/ucd/auxiliary/GraphemeBreakTest.txt const WordTuple tests[] = { #include "GraphemeBreakCases.inc" }; INSTANTIATE_TEST_SUITE_P( UnicodeTest, WordTest, ValuesIn( tests ) ); TEST( WordTest, MatchesBytes ) { Word word( "f𐍈oβaAaR" ); EXPECT_TRUE( word.ContainsBytes( Word( "f𐍈oβaAar" ) ) ); EXPECT_TRUE( word.ContainsBytes( Word( "F𐍈oβaaaR" ) ) ); EXPECT_TRUE( word.ContainsBytes( Word( "foΒar" ) ) ); EXPECT_TRUE( word.ContainsBytes( Word( "RAβof" ) ) ); EXPECT_TRUE( word.ContainsBytes( Word( "βfr𐍈a" ) ) ); EXPECT_TRUE( word.ContainsBytes( Word( "fβr" ) ) ); EXPECT_TRUE( word.ContainsBytes( Word( "r" ) ) ); EXPECT_TRUE( word.ContainsBytes( Word( "βββ" ) ) ); EXPECT_TRUE( word.ContainsBytes( Word( "" ) ) ); } TEST( WordTest, DoesntMatchBytes ) { Word word( "Fo𐍈βAr" ); EXPECT_FALSE( word.ContainsBytes( Word( "Fo𐍈βArε" ) ) ); EXPECT_FALSE( word.ContainsBytes( Word( "gggg" ) ) ); EXPECT_FALSE( word.ContainsBytes( Word( "χ" ) ) ); EXPECT_FALSE( word.ContainsBytes( Word( "nfooΒar" ) ) ); EXPECT_FALSE( word.ContainsBytes( Word( "Fβrmmm" ) ) ); } } // namespace YouCompleteMe ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/cmake/000077500000000000000000000000001374632460100207335ustar00rootroot00000000000000ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/cmake/FindGMock.cmake000066400000000000000000000107771374632460100235520ustar00rootroot00000000000000# Locate the Google C++ Mocking Framework. # (This file is almost an identical copy of the original FindGTest.cmake file, # feel free to use it as it is or modify it for your own needs.) # # # Defines the following variables: # # GMOCK_FOUND - Found the Google Testing framework # GMOCK_INCLUDE_DIRS - Include directories # # Also defines the library variables below as normal # variables. These contain debug/optimized keywords when # a debugging library is found. # # GMOCK_BOTH_LIBRARIES - Both libgmock & libgmock-main # GMOCK_LIBRARIES - libgmock # GMOCK_MAIN_LIBRARIES - libgmock-main # # Accepts the following variables as input: # # GMOCK_ROOT - (as a CMake or environment variable) # The root directory of the gmock install prefix # # GMOCK_MSVC_SEARCH - If compiling with MSVC, this variable can be set to # "MD" or "MT" to enable searching a gmock build tree # (defaults: "MD") # #----------------------- # Example Usage: # # find_package(GMock REQUIRED) # include_directories(${GMOCK_INCLUDE_DIRS}) # # add_executable(foo foo.cc) # target_link_libraries(foo ${GMOCK_BOTH_LIBRARIES}) # #============================================================================= # This file is released under the MIT licence: # # Copyright (c) 2011 Matej Svec # # 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. #============================================================================= function( _gmock_append_debugs _endvar _library ) if( ${_library} AND ${_library}_DEBUG ) set( _output optimized ${${_library}} debug ${${_library}_DEBUG} ) else() set( _output ${${_library}} ) endif() set( ${_endvar} ${_output} PARENT_SCOPE ) endfunction() function( _gmock_find_library _name ) find_library( ${_name} NAMES ${ARGN} HINTS $ENV{GMOCK_ROOT} ${GMOCK_ROOT} PATH_SUFFIXES ${_gmock_libpath_suffixes} ) mark_as_advanced( ${_name} ) endfunction() if( NOT DEFINED GMOCK_MSVC_SEARCH ) set( GMOCK_MSVC_SEARCH MD ) endif() set( _gmock_libpath_suffixes lib ) if( MSVC ) if( GMOCK_MSVC_SEARCH STREQUAL "MD" ) list( APPEND _gmock_libpath_suffixes msvc/gmock-md/Debug msvc/gmock-md/Release ) elseif( GMOCK_MSVC_SEARCH STREQUAL "MT" ) list( APPEND _gmock_libpath_suffixes msvc/gmock/Debug msvc/gmock/Release ) endif() endif() find_path( GMOCK_INCLUDE_DIR gmock/gmock.h HINTS $ENV{GMOCK_ROOT}/include ${GMOCK_ROOT}/include ) mark_as_advanced( GMOCK_INCLUDE_DIR ) if( MSVC AND GMOCK_MSVC_SEARCH STREQUAL "MD" ) # The provided /MD project files for Google Mock add -md suffixes to the # library names. _gmock_find_library( GMOCK_LIBRARY gmock-md gmock ) _gmock_find_library( GMOCK_LIBRARY_DEBUG gmock-mdd gmockd ) _gmock_find_library( GMOCK_MAIN_LIBRARY gmock_main-md gmock_main ) _gmock_find_library( GMOCK_MAIN_LIBRARY_DEBUG gmock_main-mdd gmock_maind ) else() _gmock_find_library( GMOCK_LIBRARY gmock ) _gmock_find_library( GMOCK_LIBRARY_DEBUG gmockd ) _gmock_find_library( GMOCK_MAIN_LIBRARY gmock_main ) _gmock_find_library( GMOCK_MAIN_LIBRARY_DEBUG gmock_maind ) endif() FIND_PACKAGE_HANDLE_STANDARD_ARGS( GMock DEFAULT_MSG GMOCK_LIBRARY GMOCK_INCLUDE_DIR GMOCK_MAIN_LIBRARY ) if( GMOCK_FOUND ) set( GMOCK_INCLUDE_DIRS ${GMOCK_INCLUDE_DIR} ) _gmock_append_debugs( GMOCK_LIBRARIES GMOCK_LIBRARY ) _gmock_append_debugs( GMOCK_MAIN_LIBRARIES GMOCK_MAIN_LIBRARY ) set( GMOCK_BOTH_LIBRARIES ${GMOCK_LIBRARIES} ${GMOCK_MAIN_LIBRARIES} ) endif() ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/main.cpp000066400000000000000000000016351374632460100213100ustar00rootroot00000000000000// 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 #include #include int main( int argc, char **argv ) { pybind11::scoped_interpreter guard{}; testing::InitGoogleMock( &argc, argv ); return RUN_ALL_TESTS(); } ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/testdata/000077500000000000000000000000001374632460100214645ustar00rootroot00000000000000ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/testdata/SWObject.h000066400000000000000000000002041374632460100233110ustar00rootroot00000000000000@interface SWObject /** testFunc * * a detail description */ - (void)test:(int)arg1 withArg2:(int)arg2 withArg3:(int)arg3; @end ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/testdata/SWObject.m000066400000000000000000000002041374632460100233160ustar00rootroot00000000000000#import "SWObject.h" @implementation SWObject - (void)test { [self test:0 /*complete at here*/] } - /*complete at here*/ @end ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/testdata/basic.cpp000066400000000000000000000006561374632460100232600ustar00rootroot00000000000000class Bar { public: int x; int y; char c; void barbar() { x = 5; } }; struct Foo { int x; //!< A docstring. int y; char c; int foobar( int a, float b = 3.0, char c = '\n' ) { return 5; } }; typedef enum { VALUE_1, VALUE_2 } enum_test; int main() { Foo foo; Bar bar; // The location after the dots are lines 29 and 30, column 7 bar.barbar(); foo.x = 3; enum_test enumerate = VALUE_1; } ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/testdata/basic.tags000066400000000000000000000013611374632460100234260ustar00rootroot00000000000000!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ !_TAG_FILE_SORTED 2 /0=unsorted, 1=sorted, 2=foldcase/ !_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/ !_TAG_PROGRAM_NAME Exuberant Ctags // !_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/ !_TAG_PROGRAM_VERSION 5.8 // i1 foo junky;'junklanguage:C++ i1 bar junky;'junklanguage:C++ foosy foo junky;"'junk language:C++ zanzibar fooaaa bar junky;"'junk language:C++ zanzibar bloa foo junky Floo::goo /foo/zoo language:C !goo /foo/goo maa language:C zoro /foo language:fakelang #bleh /m_oo 123ntoh;;;\"eu language:C# ;\" API foo.bar /^ class TEST $/;" c language:FooBar DELETE foo.bar /^ DELETE: =>$/;" m language:FooBar ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/testdata/directory.tags/000077500000000000000000000000001374632460100244255ustar00rootroot00000000000000ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/testdata/directory.tags/.gitignore000066400000000000000000000000161374632460100264120ustar00rootroot00000000000000* !.gitignore ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/testdata/empty.tags000066400000000000000000000000001374632460100234700ustar00rootroot00000000000000ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/testdata/goto.cpp000066400000000000000000000002571374632460100231440ustar00rootroot00000000000000struct Foo { int bar; int zoo; }; struct Bar { int foo; int zoo; }; struct Foo; struct Zoo; void func(); void func() { Foo foo; foo.bar = 5; Zoo *zoo = 0; } ycmd-0+20201028+git1d415c5+ds/cpp/ycm/tests/testdata/invalid_tag_file_format.tags000066400000000000000000000010231374632460100271700ustar00rootroot00000000000000!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ !_TAG_FILE_SORTED 2 /0=unsorted, 1=sorted, 2=foldcase/ !_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/ !_TAG_PROGRAM_NAME Exuberant Ctags // !_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/ !_TAG_PROGRAM_VERSION 5.8 // i1 foo junky;'junk i1 bar junky;'junk foosy foo junky;"'junk zanzibar fooaaa bar junky;"'junk zanzibar bloa foo junky Floo::goo /foo/zoo !goo /foo/goo maa zoro /foo #bleh /m_oo 123ntoh;;;\"eu ;\" ycmd-0+20201028+git1d415c5+ds/cpp/ycm/versioning.cpp000066400000000000000000000014751374632460100214070ustar00rootroot00000000000000// 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 "versioning.h" namespace YouCompleteMe { int YcmCoreVersion() { return YCMD_CORE_VERSION; } } // namespace YouCompleteMe ycmd-0+20201028+git1d415c5+ds/cpp/ycm/versioning.h000066400000000000000000000021151374632460100210440ustar00rootroot00000000000000// 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 . #ifndef VERSIONING_H_EEJUU0AH #define VERSIONING_H_EEJUU0AH // The true value of this preprocessor definition is set in a compiler // command-line flag. This is done in the main CMakeLists.txt file. #if !defined( YCMD_CORE_VERSION ) #define YCMD_CORE_VERSION 0 #endif namespace YouCompleteMe { int YcmCoreVersion(); } // namespace YouCompleteMe #endif /* end of include guard: VERSIONING_H_EEJUU0AH */ ycmd-0+20201028+git1d415c5+ds/cpp/ycm/ycm_core.cpp000066400000000000000000000236331374632460100210240ustar00rootroot00000000000000// 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 "CodePoint.h" #include "IdentifierCompleter.h" #include "PythonSupport.h" #include "versioning.h" #ifdef USE_CLANG_COMPLETER # include "ClangCompleter.h" # include "ClangUtils.h" # include "CompilationDatabase.h" # include "CompletionData.h" # include "Diagnostic.h" # include "Documentation.h" # include "Location.h" # include "Range.h" # include "UnsavedFile.h" #endif // USE_CLANG_COMPLETER #include namespace py = pybind11; using namespace YouCompleteMe; bool HasClangSupport() { #ifdef USE_CLANG_COMPLETER return true; #else return false; #endif // USE_CLANG_COMPLETER } PYBIND11_MAKE_OPAQUE( std::vector< std::string > ); #ifdef USE_CLANG_COMPLETER PYBIND11_MAKE_OPAQUE( std::vector< UnsavedFile > ); PYBIND11_MAKE_OPAQUE( std::vector< Range > ); PYBIND11_MAKE_OPAQUE( std::vector< CompletionData > ); PYBIND11_MAKE_OPAQUE( std::vector< Diagnostic > ); PYBIND11_MAKE_OPAQUE( std::vector< FixIt > ); PYBIND11_MAKE_OPAQUE( std::vector< FixItChunk > ); #endif // USE_CLANG_COMPLETER PYBIND11_MODULE( ycm_core, mod ) { mod.def( "HasClangSupport", &HasClangSupport ); mod.def( "FilterAndSortCandidates", &FilterAndSortCandidates, py::arg("candidates"), py::arg("candidate_property"), py::arg("query"), py::arg("max_candidates") = 0 ); mod.def( "YcmCoreVersion", &YcmCoreVersion ); // This is exposed so that we can test it. mod.def( "GetUtf8String", []( py::object o ) -> py::bytes { return GetUtf8String( o ); } ); py::class_< IdentifierCompleter >( mod, "IdentifierCompleter" ) .def( py::init<>() ) .def( "AddIdentifiersToDatabase", &IdentifierCompleter::AddIdentifiersToDatabase, py::call_guard< py::gil_scoped_release >() ) .def( "ClearForFileAndAddIdentifiersToDatabase", &IdentifierCompleter::ClearForFileAndAddIdentifiersToDatabase, py::call_guard< py::gil_scoped_release >() ) .def( "AddIdentifiersToDatabaseFromTagFiles", &IdentifierCompleter::AddIdentifiersToDatabaseFromTagFiles, py::call_guard< py::gil_scoped_release >() ) .def( "CandidatesForQueryAndType", &IdentifierCompleter::CandidatesForQueryAndType, py::call_guard< py::gil_scoped_release >(), py::arg( "query" ), py::arg( "filetype" ), py::arg( "max_candidates" ) = 0 ); py::bind_vector< std::vector< std::string > >( mod, "StringVector" ); #ifdef USE_CLANG_COMPLETER py::register_exception< ClangParseError >( mod, "ClangParseError" ); mod.def( "ClangVersion", ClangVersion ); // CAREFUL HERE! For filename and contents we are referring directly to // Python-allocated and -managed memory since we are accepting pointers to // data members of python objects. We need to ensure that those objects // outlive our UnsavedFile objects. py::class_< UnsavedFile >( mod, "UnsavedFile" ) .def( py::init<>() ) .def_readwrite( "filename_", &UnsavedFile::filename_ ) .def_readwrite( "contents_", &UnsavedFile::contents_ ) .def_readwrite( "length_", &UnsavedFile::length_ ); py::bind_vector< std::vector< UnsavedFile > >( mod, "UnsavedFileVector" ); py::class_< ClangCompleter >( mod, "ClangCompleter" ) .def( py::init<>() ) .def( "GetDeclarationLocation", &ClangCompleter::GetDeclarationLocation, py::call_guard< py::gil_scoped_release >() ) .def( "GetDefinitionLocation", &ClangCompleter::GetDefinitionLocation, py::call_guard< py::gil_scoped_release >() ) .def( "GetDefinitionOrDeclarationLocation", &ClangCompleter::GetDefinitionOrDeclarationLocation, py::call_guard< py::gil_scoped_release >() ) .def( "DeleteCachesForFile", &ClangCompleter::DeleteCachesForFile, py::call_guard< py::gil_scoped_release >() ) .def( "UpdatingTranslationUnit", &ClangCompleter::UpdatingTranslationUnit, py::call_guard< py::gil_scoped_release >() ) .def( "UpdateTranslationUnit", &ClangCompleter::UpdateTranslationUnit, py::call_guard< py::gil_scoped_release >() ) .def( "CandidatesForLocationInFile", &ClangCompleter::CandidatesForLocationInFile, py::call_guard< py::gil_scoped_release >() ) .def( "GetTypeAtLocation", &ClangCompleter::GetTypeAtLocation, py::call_guard< py::gil_scoped_release >() ) .def( "GetEnclosingFunctionAtLocation", &ClangCompleter::GetEnclosingFunctionAtLocation, py::call_guard< py::gil_scoped_release >() ) .def( "GetFixItsForLocationInFile", &ClangCompleter::GetFixItsForLocationInFile, py::call_guard< py::gil_scoped_release >() ) .def( "GetDocsForLocationInFile", &ClangCompleter::GetDocsForLocationInFile, py::call_guard< py::gil_scoped_release >() ); py::enum_< CompletionKind >( mod, "CompletionKind" ) .value( "STRUCT", CompletionKind::STRUCT ) .value( "CLASS", CompletionKind::CLASS ) .value( "ENUM", CompletionKind::ENUM ) .value( "TYPE", CompletionKind::TYPE ) .value( "MEMBER", CompletionKind::MEMBER ) .value( "FUNCTION", CompletionKind::FUNCTION ) .value( "VARIABLE", CompletionKind::VARIABLE ) .value( "MACRO", CompletionKind::MACRO ) .value( "PARAMETER", CompletionKind::PARAMETER ) .value( "NAMESPACE", CompletionKind::NAMESPACE ) .value( "UNKNOWN", CompletionKind::UNKNOWN ); py::class_< CompletionData >( mod, "CompletionData" ) .def( py::init<>() ) .def( "TextToInsertInBuffer", &CompletionData::TextToInsertInBuffer ) .def( "MainCompletionText", &CompletionData::MainCompletionText ) .def( "ExtraMenuInfo", &CompletionData::ExtraMenuInfo ) .def( "DetailedInfoForPreviewWindow", &CompletionData::DetailedInfoForPreviewWindow ) .def( "DocString", &CompletionData::DocString ) .def_readonly( "kind_", &CompletionData::kind_ ) .def_readonly( "fixit_", &CompletionData::fixit_ ); py::bind_vector< std::vector< CompletionData > >( mod, "CompletionVector" ); py::class_< Location >( mod, "Location" ) .def( py::init<>() ) .def_readonly( "line_number_", &Location::line_number_ ) .def_readonly( "column_number_", &Location::column_number_ ) .def_readonly( "filename_", &Location::filename_ ) .def( "IsValid", &Location::IsValid ); py::class_< Range >( mod, "Range" ) .def( py::init<>() ) .def_readonly( "start_", &Range::start_ ) .def_readonly( "end_", &Range::end_ ); py::bind_vector< std::vector< Range > >( mod, "RangeVector" ); py::class_< FixItChunk >( mod, "FixItChunk" ) .def( py::init<>() ) .def_readonly( "replacement_text", &FixItChunk::replacement_text ) .def_readonly( "range", &FixItChunk::range ); py::bind_vector< std::vector< FixItChunk > >( mod, "FixItChunkVector" ); py::class_< FixIt >( mod, "FixIt" ) .def( py::init<>() ) .def_readonly( "chunks", &FixIt::chunks ) .def_readonly( "location", &FixIt::location ) .def_readonly( "text", &FixIt::text ) .def_property_readonly( "kind", [](const py::handle) { return py::none(); }); py::bind_vector< std::vector< FixIt > >( mod, "FixItVector" ); py::enum_< DiagnosticKind >( mod, "DiagnosticKind" ) .value( "ERROR", DiagnosticKind::ERROR ) .value( "WARNING", DiagnosticKind::WARNING ) .value( "INFORMATION", DiagnosticKind::INFORMATION ); py::class_< Diagnostic >( mod, "Diagnostic" ) .def( py::init<>() ) .def_readonly( "ranges_", &Diagnostic::ranges_ ) .def_readonly( "location_", &Diagnostic::location_ ) .def_readonly( "location_extent_", &Diagnostic::location_extent_ ) .def_readonly( "kind_", &Diagnostic::kind_ ) .def_readonly( "text_", &Diagnostic::text_ ) .def_readonly( "long_formatted_text_", &Diagnostic::long_formatted_text_ ) .def_readonly( "fixits_", &Diagnostic::fixits_ ); py::bind_vector< std::vector< Diagnostic > >( mod, "DiagnosticVector" ); py::class_< DocumentationData >( mod, "DocumentationData" ) .def( py::init<>() ) .def_readonly( "comment_xml", &DocumentationData::comment_xml ) .def_readonly( "raw_comment", &DocumentationData::raw_comment ) .def_readonly( "brief_comment", &DocumentationData::brief_comment ) .def_readonly( "canonical_type", &DocumentationData::canonical_type ) .def_readonly( "display_name", &DocumentationData::display_name ); py::class_< CompilationDatabase >( mod, "CompilationDatabase" ) .def( py::init< py::object >() ) .def( "DatabaseSuccessfullyLoaded", &CompilationDatabase::DatabaseSuccessfullyLoaded ) .def( "AlreadyGettingFlags", &CompilationDatabase::AlreadyGettingFlags ) .def( "GetCompilationInfoForFile", &CompilationDatabase::GetCompilationInfoForFile ) .def_property_readonly( "database_directory", &CompilationDatabase::GetDatabaseDirectory ); py::class_< CompilationInfoForFile, std::shared_ptr< CompilationInfoForFile > >( mod, "CompilationInfoForFile" ) .def_readonly( "compiler_working_dir_", &CompilationInfoForFile::compiler_working_dir_ ) .def_readonly( "compiler_flags_", &CompilationInfoForFile::compiler_flags_ ); #endif // USE_CLANG_COMPLETER } ycmd-0+20201028+git1d415c5+ds/docs/000077500000000000000000000000001374632460100160675ustar00rootroot00000000000000ycmd-0+20201028+git1d415c5+ds/docs/README.md000066400000000000000000000006141374632460100173470ustar00rootroot00000000000000API documentation ================= This directory contains the API docs which are generated from the `openapi.yaml` file. This file follows [the OpenAPI v2 specification][openapi]. When ycmd's API changes, edit that file and run the `update_api_docs.py` script in the ycmd folder to update the documentation. [openapi]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md ycmd-0+20201028+git1d415c5+ds/docs/openapi.yml000066400000000000000000001670531374632460100202610ustar00rootroot00000000000000# Copyright (C) 2017-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 . swagger: "2.0" info: title: ycmd description: |- ycmd is a code-completion & code-comprehension server. ycmd presents a JSON/REST interface to clients over HTTP, validated by HMAC. An instance of ycmd is started for each client application. ## General Notes The REST interface typically uses HTTP POST requests and expects the payload data to be a valid JSON document. The following general principles are applied: - 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. If a file has not been written to disk yet, supply a temporary path. - All requests to the server must include an HMAC in the x-ycm-hmac HTTP header. - All responses from the server must be validated using the x-ycm-hmac header. ## The example client ycmd contains a simple example client in the `example` directory of the source tree. It is written in pure Python with only minimal dependencies. It contains trivial implementations of most endpoints and serves to supplement this API documentation. Consult `example/README.md` for instructions on getting it running. ## Starting the server The ycmd server is typically started by a client in order to provide services to it. Briefly, starting the server requires supplying some configuration and initializing some security features (HMAC). The server should be started with a supported version of Python. The full list of command line options can be obtained by passing `--help` to ycmd (e.g. `python -m ycmd --help`). However, the following options warrant further discussion: ### `--options_file` This mandatory option supplies the name of a file containing user options data. This option (and thus the file) are mandatory, as they are required to set the shared HMAC secret. Once the server has read the options, the server deletes the options file. Therefore, the client should typically create a secure temporary file (e.g. `mkstemp`) which contains the options to be used. The default options and their values are found in `default_settings.json` within the ycmd codebase. The file contains a JSON-formatted object, where the keys are the names of options (with any `ycm_` prefix removed) and the values are the option values. The canonical list of supported user options can be found in YouCompleteMe's `README.md`. The option values should have the `ycm_` prefix removed. The following additional options are required to be set by the client application and should *not* be visible to the user: - `hmac_secret`: The shared secret for HMAC authentication. This should be 16 bytes of random data with as much entropy as possible, encoded as a base-64 UTF-8 string. Code for generating this in Python can be found in the ycmd example client. ## HMAC 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 the example client to see how it's done. ## Filetypes ycmd uses `filetypes` to identify the "language" of a given buffer. These filetype values are strictly the values understood by Vim. Please see the YCM README.md for the list of filetypes recognized by ycmd for semantic completion. version: 0.3.0 externalDocs: description: ycmd GitHub page url: https://github.com/Valloric/ycmd definitions: YesNo: type: boolean description: "Result of the query: `true` if yes, `false` otherwise." AlwaysYes: type: boolean description: "`true` is always returned, unless there is an error." LineNumber: type: integer description: 1-based line number. ColumnNumber: type: integer description: 1-based column byte offset within the line. FilePath: type: string description: |- Absolute path to the file in the filesystem. If the file does not yet exist (for example, a new file), then the value should be an arbitrary unique string. CompleterTarget: type: string description: |- The completer implementation for which the command is intended. Typically, this is set to `filetype_default`, but may also take any of the following values: - `filetype_default` Use the semantic completer associated with the filetype of the current buffer. This is the default when a target is not supplied. - `identifier` Use the identifier completer. - Any filetype Use the completer associated with the supplied filetype. WorkingDirectory: type: string description: |- Absolute path to the filesystem working directory of the client. This is used by completer engines to determine things like the project root for the file being modified, amongst other things. ExtraConfData: type: object description: |- A dictionary passed to the `Settings( **kwargs )` function of the `.ycm_extra_conf.py` file under the `client_data` key of `kwargs`. This optional field should be used to give users a way to customize the `.ycm_extra_conf.py` file with their own set of options. FileData: type: object description: |- Contents and details of a dirty buffer. required: - filetypes - contents properties: filetypes: type: array items: type: string contents: type: string description: The entire contents of the buffer encoded as UTF-8. FileDataMap: type: object description: |- An object mapping whose keys are the absolute paths to the files and whose values are data relating unsaved buffers. An unsaved buffer is any file that is opened in the editor and has been changed without saving the contents to disk. The file referred to in the request `filepath` entry must _always_ be included. For most requests this is the user's current buffer, but may be any buffer (e.g. in the case of closing a buffer which is not current). When a file is closed in the editor, a `BufferUnload` event should be sent and the file should not be included in further `FileDataMap` entries until (or unless) it is opened and changed again. additionalProperties: $ref: "#/definitions/FileData" # Due to the way the API combines keys at the top level, we are not able to # compose this item per-request. So this definition must be copy-pasted for # some requests. SimpleRequest: type: object required: - line_num - column_num - filepath - file_data properties: line_num: $ref: "#/definitions/LineNumber" column_num: $ref: "#/definitions/ColumnNumber" filepath: $ref: "#/definitions/FilePath" file_data: $ref: "#/definitions/FileDataMap" completer_target: $ref: "#/definitions/CompleterTarget" working_dir: $ref: "#/definitions/WorkingDirectory" extra_conf_data: $ref: "#/definitions/ExtraConfData" Exception: type: object description: JSON-encoded representation of a Python Exception object. # We don't document the contents of this object, as it is defined in the # Python documentation. additionalProperties: true ExceptionResponse: type: object description: |- The server raised an exception processing the request. This response is often returned when the server is unable to perform the requested action, not due to a bug or other error, but because it is not possible to do so. For example, it is common for an exception response to be returned when requesting "GoToDefinition" on an identifier for which the semantic, engine is unable to find such a definition. properties: exception: $ref: "#/definitions/Exception" message: type: string description: |- The exception message. Typically a single line and suitable for display to a user. traceback: type: string description: |- A detailed report of the ycmd call stack at the point the exception was thrown. It is typically not required to display this to a user, as ycmd will print all exceptions to its log file (standard error). Location: type: object description: |- A contiguous range of bytes in a source buffer starting at `start` and finishing at `end`. The range is (effectively) exclusive. That is if start points to (10,1) and end points to (10,3), then the length of the range is 2 characters. required: - line_num - column_num - filepath properties: line_num: $ref: "#/definitions/LineNumber" column_num: $ref: "#/definitions/ColumnNumber" filepath: $ref: "#/definitions/FilePath" Range: type: object description: A contiguous range of bytes in a source buffer. required: - start - end properties: start: $ref: "#/definitions/Location" end: $ref: "#/definitions/Location" DiagnosticData: type: object description: |- - `location` The source location where the diagnostic applies. This is typically (but not always) the start of `location_extent`. - `location_extent` The source range to which the diagnostic applies. This differs from the `location` in that it refers to the whole range. Typically this is used to underline, or otherwise render as "error" the source code which caused the diagnostic. required: - ranges - location - location_extent - text - kind properties: ranges: type: array description: |- List of ranges to which this diagnostic applies. These ranges typically identify the source locations which should be "highlighted" as incorrect. items: $ref: "#/definitions/Range" location: $ref: "#/definitions/Location" location_extent: $ref: "#/definitions/Range" text: type: string description: The diagnostic text (e.g. error message) kind: type: string enum: - WARNING - ERROR - INFORMATION - HINT description: |- The type of diagnostic being reported. Typically semantic engines will differentiate between warnings and fatal errors. Informational and hint messages should be treated as warnings where the client does not differentiate. fixit_available: type: boolean description: |- If set to true, indicates that a quick fix action (FixIt) is available for this diagnostic. Typically this is used to indicate to the user that the `FixIt` subcommand is available. DiagnosticResponse: type: array items: $ref: "#/definitions/DiagnosticData" SubcommandResponse: type: object properties: fixits: type: array description: |- If present, this is a *FixIt* or *Refactor* response and the value of this property is a list of potential changes to buffers to apply the quick fix or refactoring operation. An empty `fixits` list means that no FixIt or refactoring was available. If multiple entries are supplied, the user is prompted to select which one to apply. items: $ref: "#/definitions/FixIt" minItems: 0 message: type: string description: |- If present, this is a *simple display message* and the value of this property is the message to display. detailed_info: type: string description: |- If present, this is a *detailed information* response and the value of this property is the multi-line information to display as unformatted plain text. filepath: type: string description: |- If present, this is a single *GoTo response* and this value contains the absolute path of the buffer containing the target location (identified in `line_num` and `column_num`). line_num: $ref: "#/definitions/LineNumber" column_num: $ref: "#/definitions/ColumnNumber" UnresolvedFixIt: type: object required: - resolve properties: resolve: type: boolean description: |- Indicates whether the fixit requires additional processing. FixIt: type: object required: - location - resolve - chunks properties: text: type: string description: |- The diagnostic text or a description of the modification to be made. This is the text displayed to the user when selecting from multiple available FixIts. location: $ref: "#/definitions/Location" resolve: type: boolean description: |- Indicates whether the fixit requires additional processing. kind: type: string description: |- FixIt kind is meant to suggest what sort of fixit this is. Clients are free to ignore this property or use it to decide whether to automatically apply the fixit. SUpported values are those of LSP. Search for `namespace CodeActionKind` in the following link https://microsoft.github.io/language-server-protocol/specifications/specification-3-15/#textDocument_codeAction chunks: type: array description: |- A list of ranges and replacements which must be applied to source files. *NOTE*: The source ranges may span arbitrary files and the sequence of `chunks` is not defined. items: type: object required: - replacement_text - range properties: replacement_text: type: string description: |- The text with which to replace the range identified by `range`. range: $ref: "#/definitions/Range" Candidate: type: object properties: insertion_text: type: string description: |- The word to insert when selecting the completion. Equivalent of the Vim `complete-items` entry: `word`. menu_text: type: string description: |- The word to display as the suggestion to the user. If not supplied, `insertion_text` is displayed to the user. Equivalent of the Vim `complete-items` entry: `abbr`. extra_menu_info: type: string description: |- Additional information to display about the suggestion within the completion menu (or equivalent). Typically this is the signature/declaration of the item being suggested, or some additional free-text qualifiers (such as `[File]` etc.). These are a single line and typically short. For more detailed information, such as usage and docs, see `detailed_info`. Equivalent of the Vim `complete-items` entry: `menu`. detailed_info: type: string description: |- Additional information, such as the full signature and method documentation, suitable for display in a preview window or tooltip. Equivalent of the Vim `complete-items` entry: `info`. kind: type: string description: |- An indicator of the type of suggestion. Only the first character of `kind` should be displayed. Equivalent of the Vim `complete-items` entry: `kind`. extra_data: type: object description: |- Completer-specific additional information. properties: doc_string: type: string description: |- Additional documentation/information to be displayed alongside (after) information in `detailed_info`. fixits: type: array description: |- Any editional edits to apply when selecting this completion items: $ref: "#/definitions/FixIt" resolve: type: number description: |- Identifier used to resolve this completion item using /resolve_completion. If not supplied, the item is already fully resolved. CompletionResponse: type: object required: - completions - completion_start_column properties: completions: type: array description: List of completion suggestions. items: $ref: "#/definitions/Candidate" completion_start_column: type: integer description: |- 1-based byte index of the column from which the completion should be applied. This is the column in which the words suggested in `completions` should be placed. errors: type: array description: Any errors reported by the semantic completion engine. items: $ref: "#/definitions/ExceptionResponse" ResolveCompletionResponse: type: object required: - completion - errors properties: completion: $ref: "#/definitions/Candidate" errors: type: array description: Any errors reported by the semantic completion engine. items: $ref: "#/definitions/ExceptionResponse" ItemData: type: object required: - key - value properties: key: type: string value: type: string ServerData: type: object required: - name - is_running - executable - address - port - pid - logfiles - extras properties: name: type: string description: The server name. is_running: type: boolean description: |- `true` if the server is running, `false` otherwise. executable: type: string description: The executable path used to start the server. address: type: string description: |- The address on which the server is listening. `null` if not applicable. port: type: integer description: |- The port on which the server is listening. `null` if not applicable. pid: type: integer description: |- The process identifier of the server. `null` if the server is not running. logfiles: type: array description: A list of logging files used by the server. items: type: string description: A logging file path. extras: type: array items: $ref: "#/definitions/ItemData" DebugInfoResponse: type: object required: - name - servers - items properties: name: type: string description: The completer name. servers: type: array description: A list of servers used by the completer. items: $ref: "#/definitions/ServerData" items: type: array description: Additional debugging information. items: $ref: "#/definitions/ItemData" MessagePollResponse: type: boolean description: |- When `true` is returned, the request timed out (meaning no messages were returned in the poll period). Clients should send another `receive_messages` request immediately. When `false` is returned, the server determined that message polling should abort for the current file type context. Clients should not re-send this request until the filetype being edited changes or the server is restarted. MessageList: type: array description: |- A list of messages in the sequence they should be handled. The type of message in each item is determined by the property name: - An object with a property `message` is a *simple display message* where the property value is the message. - An object with a property `diagnostics` contains diagnostics for a project file. The value of the property is described below. items: $ref: '#/definitions/Message' Message: type: object description: An object containing a single asynchronous message. It is either a `SimpleDisplayMessage` or a `DiagnosticsMessage` properties: message: $ref: '#/definitions/SimpleDisplayMessage' description: If present, this object is a `SimpleDisplayMessage` diagnostics: $ref: '#/definitions/DiagnosticsMessage' description: If present, this object is a `DiagnosticsMessage` SimpleDisplayMessage: type: string description: |- A message for display to the user. Note: the message should be displayed discreetly (such as in a status bar) and should not block the user or interrupt them. DiagnosticsMessage: type: object description: |- Diagnostics for a particular file. Note: diagnostics may be supplied for any arbitrary file. The client is responsible for displaying the diagnostics in an appropriate manner. The server supplies an empty set of diagnostics to clear the diagnostics for a particular file. required: - filepath - diagnostics properties: filpath: $ref: '#/definitions/FilePath' diagnostics: type: array items: $ref: "#/definitions/DiagnosticData" paths: /event_notification: post: summary: Notify the server of various client events. description: |- The server needs to react to a number of client events in order to provide things like diagnostics and to handle downstream server states etc. The client must inform the server of the following events, populating the event name in the `event_name` property: - `FileReadyToParse` Call when a new buffer is opened or after the user has stopped typing for some time, or at any other time when the client believes that it is worthwhile reparsing the current file and updating semantic engines' ASTs and reporting things like updated diagnostics. - `BufferUnload` Call when the user closes a buffer that was previously known to be open. Closing buffers is important to limit resource usage. - `BufferVisit` (optional) Call when the user focuses on a buffer that is already known. *Note*: The `ultisnips_snippets` property is optional when firing calling this event. Otherwise it is ignored. - `InsertLeave` (optional) For modal editors, call when exiting insert mode. It is equivalent to `CurrentIdentifierFinished`. - `CurrentIdentifierFinished` (optional) Call when the user finishes typing what appears to the client to be an identifier. - `FileSave` Call when the user writes to a file on disk. (optional) produces: - application/json parameters: - name: request_data in: body description: |- The notification data. The line and column are typically not used, but the `filepath` and file data properties are used to perform semantic analysis (e.g. in the `FileReadyToParse` handler). required: true schema: type: object required: - line_num - column_num - filepath - file_data - event_name properties: line_num: $ref: "#/definitions/LineNumber" column_num: $ref: "#/definitions/ColumnNumber" filepath: $ref: "#/definitions/FilePath" file_data: $ref: "#/definitions/FileDataMap" completer_target: $ref: "#/definitions/CompleterTarget" working_dir: $ref: "#/definitions/WorkingDirectory" extra_conf_data: $ref: "#/definitions/ExtraConfData" event_name: type: string enum: - FileReadyToParse - BufferUnload - BufferVisit - InsertLeave - CurrentIdentifierFinished description: The event that occurred. ultisnips_snippets: type: object description: |- *Optional when `event_name` is BufferVisit* Supplies the ultisips snippets known for the current filetypes. Can be used to supply any other type of additional completion suggestions generated by the client. required: - trigger - description properties: trigger: type: string description: |- The text to insert in order to trigger the snippet. When supplying non-ultisnips suggestions, this is the text to be inserted. description: type: string description: |- Additional text to display in the completion menu, such as a summary of the snippet to be inserted. example: line_num: 10 column_num: 20 filepath: "/home/test/dev/test.js" file_data: "/home/test/dev/test.cc": filetypes: [ 'cpp' ] contents: "" event_name: "FileReadyToParse" responses: 200: description: Optional set of diagnostics for the current buffer. schema: $ref: "#/definitions/DiagnosticResponse" 500: description: An error occurred. schema: $ref: "#/definitions/ExceptionResponse" /run_completer_command: post: summary: Run a semantic completer subcommand. description: |- Semantic completers offer additional engine-specific commands, known as completer subcommands. This endpoint requests a specific command to be executed. Typically the "default" semantic completer for the current filetype is used, but it is possible to force the use of a particular completer, using the `completer_target` property. The command to execute is passed as a list of arguments, the first of which is the command name followed by any command- and completer-specific additional arguments. This "command line" is passed in the `command_arguments` property. The list of available subcommands for a particular semantic completer can be queried using the `/defined_subcommands` endpoint. Subcommands may return one of a number of actions, depending on the type of command. The following types of response are returned: - A *simple display message* response. This is identified where the type of the response is scalar (i.e. boolean, number, string) or the type of the response is an object with a `message` property. These messages are typically only a single line and can be displayed in a message-box or similarly echoed in a status bar or equivalent. - A *FixIt* response. This is identified where the type of the response is an object with a property named `fixits`. - A *detailed information* response. This is identified where the type of the response is an object with a `detailed_info` property. These messages are typically multiple lines (such as the documentation and signature for a method) and are best displayed in a panel or preview area (or equivalent). - A *GoTo* response. This is identified where the response type cannot be determined by any of the above methods. A GoTo response may contain either a single location (e.g. for `GoToDeclaration`), or a list of possible locations for the user to chose from (such as in a `GoToReferences` subcommand). produces: - application/json parameters: - name: request_data in: body description: |- The context data, including the current cursor position, and details of dirty buffers. required: true schema: type: object required: - line_num - column_num - filepath - file_data - command_arguments - options properties: line_num: $ref: "#/definitions/LineNumber" column_num: $ref: "#/definitions/ColumnNumber" filepath: $ref: "#/definitions/FilePath" file_data: $ref: "#/definitions/FileDataMap" completer_target: $ref: "#/definitions/CompleterTarget" working_dir: $ref: "#/definitions/WorkingDirectory" extra_conf_data: $ref: "#/definitions/ExtraConfData" command_arguments: type: array description: |- The command line to execute as a list of arguments. The first such argument is the subcommand to execute (for example: `GoTo`). items: type: string range: description: |- The range to which the command is applied (only used by the *Format* command). $ref: "#/definitions/Range" options: type: object description: |- A set of editor-related options for the current buffer (only used by the *Format* command). required: - tab_size - insert_spaces properties: tab_size: description: The size of a tabulation in spaces. type: integer insert_spaces: description: Use spaces to represent tabulations. type: boolean example: line_num: 10 column_num: 20 filepath: "/home/test/dev/test.js" file_data: "/home/test/dev/test.js": filetypes: [ 'javascript' ] contents: "" completer_target: "filetype_default" command_arguments: [ 'RefactorRename', 'Testing' ] options: tab_size: 4 insert_spaces: true responses: 200: description: |- Optional action or display text in response to the request. *NOTE*: If the type of the response is not an object or list, then the response is a *simple display message*. *NOTE*: If the type of the response is a list, then the response is a GoTo list (e.g. quick-fix list or equivalent) and the items in the list are of type `GoToLocations` (see definitions). schema: $ref: "#/definitions/SubcommandResponse" 500: description: An error occurred. schema: $ref: "#/definitions/ExceptionResponse" /completions: post: summary: Get completion suggestions for the current file context. description: |- Returns the autocompletion suggestions for the current cursor position. Typically, the server determines whether or not to use semantic completion or general (i.e. identifier-based) completion based on the language (filetype) and the context. Clients can force the use of semantic completion by setting the `force_semantic` property to `true`. Note that if `force_semantic` is not set to `true` and the completion engine returns an error, the server still tries to fall back to general completion, so this endpoint will typically always return successfully. Any semantic completer error raised is included in the response to be dealt with by the client. When `force_semantic` is `true`, any error returned by the semantic engine is returned via a 500 response. produces: - application/json parameters: - name: request_data in: body description: |- The context data, including the current cursor position, and details of dirty buffers. required: true schema: $ref: "#/definitions/SimpleRequest" responses: 200: description: The list of completion suggestions. schema: $ref: "#/definitions/CompletionResponse" 500: description: An error occurred. schema: $ref: "#/definitions/ExceptionResponse" /resolve_completion: post: summary: Resolve detailed_info for a completion item description: |- Given the `resolve` ID for a completion item, returns the item with additional data filled in. Note, the supplied `request_data` must match exactly the `request_data` for the corresponding completion request, except with the `resolve` key filled in with the `resolve` ID from the completion item's `extra_data`. produces: - application/json parameters: - name: request_data in: body description: |- The context data, including the current cursor position, and details of dirty buffers. required: true schema: type: object required: - line_num - column_num - filepath - file_data - resolve properties: line_num: $ref: "#/definitions/LineNumber" column_num: $ref: "#/definitions/ColumnNumber" filepath: $ref: "#/definitions/FilePath" file_data: $ref: "#/definitions/FileDataMap" completer_target: $ref: "#/definitions/CompleterTarget" working_dir: $ref: "#/definitions/WorkingDirectory" extra_conf_data: $ref: "#/definitions/ExtraConfData" resolve: description: |- The 'resolve' key from the 'extra_data' in the completion item to resolve. responses: 200: description: The list of completion suggestions. schema: $ref: "#/definitions/ResolveCompletionResponse" 500: description: An error occurred. schema: $ref: "#/definitions/ExceptionResponse" /signature_help_available: get: summary: Is /signature_help supported for some filetype description: |- Since /signature_help can be expensive, clients can query ycmd with /signature_help_available and then stop sending /signature_help if there is no support for that filetype. If the response is PENDING, the client should make another /signature_help_available request later. produces: - application/json parameters: - name: subserver in: query description: |- Subserver filetype for which the request is made. If a subserver supports multiple filetypes, the parameter may hold any one of them. required: true type: string responses: 200: description: Signature help support info. schema: type: object required: - available properties: available: type: string enum: - YES - NO - PENDING 500: description: An error occurred. schema: $ref: "#/definitions/ExceptionResponse" /signature_help: post: summary: Get signature help (argument hints) for current file context. description: |- Returns a list of method signatures relevant to the current cursor position, along with an indication of which signature and argument are 'active'. Only returned by semantic engines. Signatures are actually queried immediately after a trigger character. The result of this is that the client and server collude on remembering the current "trigger" state for signature help. This trades off some complexity in clients for significant performance benefits in the general case. The client is responsible for maintaining the state of any displayed signature help popup, and is responsible for hiding it when this request returns an empty set of signatures. That is, when this request returns a non-empty list of signatures, signature help is considered "triggered' and the client must record this and return it in the 'signature_help_state' request field (value `ACTIVE`). The client continues to request signature help on new input and should update its state to `INACTIVE` whenever this request returns an empty list of signatures. If any errors are reported by the semantic engine, they are reported in the `errors` key in the response. The first signature and the first argument are both 0. produces: - application/json parameters: - name: request_data in: body description: |- The context data, including the current cursor position, and details of dirty buffers. required: true schema: type: object required: - line_num - column_num - filepath - file_data properties: line_num: $ref: "#/definitions/LineNumber" column_num: $ref: "#/definitions/ColumnNumber" filepath: $ref: "#/definitions/FilePath" file_data: $ref: "#/definitions/FileDataMap" completer_target: $ref: "#/definitions/CompleterTarget" extra_conf_data: $ref: "#/definitions/ExtraConfData" signature_help_state: type: string enum: - ACTIVE - INACTIVE description: |- The current state of the signature help triggering. After signatures are returned for the first time (triggered), the client sends further requests with `signature_help_state` set to `ACTIVE` until the request returns no signatures. responses: 200: description: Signature info. schema: type: object required: - signature_help - errors properties: errors: type: array description: errors reported by the semantic completion engine. items: $ref: "#/definitions/ExceptionResponse" signature_help: type: object required: - activeSignature - activeParameter - signatures properties: activeSignature: type: number description: The active signature. The first is 0. activeParameter: type: number description: The active parameter. The first is 0. signatures: type: array description: The list of signatures items: type: object properties: label: description: | The full signature text, including parameters type: string parameters: description: List of parameters type: array items: type: object properties: label: description: | The array of offsets representing an inclusive start and exclusive end within the parameter's containing signature label. Offsets are byte offsets into the UTF8 encoded string 'label' type: array maxItems: 2 minItems: 2 items: type: integer required: - label - parameters 500: description: An error occurred. schema: $ref: "#/definitions/ExceptionResponse" /filter_and_sort_candidates: post: summary: Filter and sort a set of candidates using ycmd's fuzzy matching. description: |- For filetypes not natively supported by ycmd, clients can often determine a set of suitable candidate completion suggestions by other means. In Vim this is typically from the `omnifunc` and other clients will have equivalents. This endpoint allows clients to use ycmd's powerful filtering and ranking capabilities (including longest-common-subsequence and word-boundary-character ranking) on arbitrary sets of identifiers. *NOTE:* This API is primarily intended for use when subclassing the `Completer` class outside of ycmd which is a very niche case, specific to the `OmniCompleter` in the Vim client. It is not expected that this API be used elsewhere. produces: - application/json parameters: - name: request_data in: body required: true description: |- The set of candidates to sort of the query parameters. *NOTE:* This `request_data` object is different from most other ycmd requests as it does not contain buffer data or cursor locations. schema: type: object properties: candidates: type: array description: The candidates to filter and sort items: $ref: "#/definitions/Candidate" sort_property: type: string enum: - word - insertion_text description: |- Typically set to `insertion_text`, but can be set to `word` when the candidates are in the form of a simple list of words. In the latter case, `candidates` must be a list of strings. query: type: string description: |- The text the user has typed so far for the current identifier. This is to filter against the suggestions in `candidates`. responses: 200: description: The filtered list of candidated schema: type: array items: $ref: "#/definitions/Candidate" 500: description: An error occurred schema: $ref: "#/definitions/ExceptionResponse" /healthy: get: summary: Check if the server is healthy. description: |- Return `true` if the server is healthy, `false` otherwise. The client should use this endpoint to keep the server alive. # We don't document the subserver query parameter as it is only for # testing. produces: - application/json responses: 200: description: Server is healthy. schema: $ref: "#/definitions/AlwaysYes" 500: description: An error occurred. schema: $ref: "#/definitions/ExceptionResponse" # We don't document the /ready handler as it is only for testing. /semantic_completer_available: post: summary: Determine if semantic completion is available for current buffer. produces: - application/json parameters: - name: request_data in: body description: |- The context data, including the current cursor position, and details of dirty buffers. required: true schema: $ref: "#/definitions/SimpleRequest" responses: 200: description: Whether or not semantic completion is available. schema: $ref: "#/definitions/YesNo" 500: description: An error occurred schema: $ref: "#/definitions/ExceptionResponse" /defined_subcommands: post: summary: Get the list of subcommands which are available for a completer. description: |- Returns the list of completer subcommands for a given completer. See also: `/run_completer_command` produces: - application/json parameters: - name: request_data in: body description: |- The context data, including the current cursor position, and details of dirty buffers. required: true schema: $ref: "#/definitions/SimpleRequest" responses: 200: description: The list of available subcommands. schema: type: array items: type: string 500: description: An error occurred schema: $ref: "#/definitions/ExceptionResponse" /detailed_diagnostic: post: summary: Get additional information about diagnostic under cursor. description: |- Where available returns addition information about the diagnostic, such as the full text of the compile error, suggestions, etc. Typically, details are returned for the diagnostic nearest to the current cursor position. produces: - application/json parameters: - name: request_data in: body description: |- The context data, including the current cursor position, and details of dirty buffers. required: true schema: $ref: "#/definitions/SimpleRequest" responses: 200: description: The detailed diagnostic schema: type: object required: - message properties: message: type: string 500: description: An error occurred, or no diagnostic was available schema: $ref: "#/definitions/ExceptionResponse" /load_extra_conf_file: post: summary: Forcefully load the config for the current buffer. description: |- By default, ycmd will not load `.ycm_extra_conf.py` files for security reasons and instead raises an exception the first time it requires loading. In the case that exception is raised, the client should call this API endpoint after confirming with the user that it is safe to load the reported `.ycm_extra_conf.py` file. produces: - application/json parameters: - name: request_data in: body description: |- The context data, including the current cursor position, and details of dirty buffers. required: true schema: $ref: "#/definitions/SimpleRequest" responses: 200: description: Configuration file loaded. schema: $ref: "#/definitions/AlwaysYes" 500: description: An error occurred loading the configuration file. schema: $ref: "#/definitions/ExceptionResponse" /ignore_extra_conf_file: post: summary: Forcefully ignore the config for the current buffer. description: |- As opposed to `/load_extra_conf_file`, this API endpoint must be called when the user declines to load the associated `.ycm_extra_conf.py` file. produces: - application/json parameters: - name: request_data in: body description: |- The context data, including the current cursor position, and details of dirty buffers. required: true schema: $ref: "#/definitions/SimpleRequest" responses: 200: description: Configuration file ignored. schema: $ref: "#/definitions/AlwaysYes" 500: description: An error occurred ignoring the configuration file. schema: $ref: "#/definitions/ExceptionResponse" /debug_info: post: summary: Return server and semantic engine debug information. description: |- Returns debugging information about the server itself and the semantic completer for the current buffer (if there is one). This data includes things like the versions of linked-in libraries, log file locations, etc. produces: - application/json parameters: - name: request_data in: body description: |- The context data, including the current cursor position, and details of dirty buffers. required: true schema: $ref: "#/definitions/SimpleRequest" responses: 200: description: A dictionary of debugging information. schema: type: object properties: python: type: object description: |- Debugging information on the Python interpreter in which the server is running. properties: executable: type: string description: Python interpreter path. version: type: string description: Python interpreter version. clang: type: object description: Debugging information on Clang. properties: has_support: type: boolean description: |- `true` if the ycmd server was built with Clang support, `false` otherwise. version: type: string description: |- if `has_support` is `true`, return the version of Clang. Otherwise, return `null`. extra_conf: type: object description: |- Debugging information on the extra configuration file for the current buffer. properties: path: type: string description: |- Path of the found extra configuration file, loaded or not. is_loaded: type: boolean description: |- `true` if the extra configuration file is loaded, `false` otherwise. completer: description: |- Contains debugging information on the completer for the given filetypes. `null` if no completer is available. $ref: "#/definitions/DebugInfoResponse" examples: application/json: python: executable: "/path/to/python/interpreter" version: "python version" clang: has_support: true version: "clang version" extra_conf: is_loaded: false path: "/path/to/extra/conf" completer: name: "completer name" servers: - name: "server name" is_running: true executable: "/path/to/executable" address: "127.0.0.1" port: 1234 pid: 12345 logfiles: - "/path/to/stdout/logfile" - "/path/to/stderr/logfile" extras: - description: "description" - value: "value" items: - description: "description" value: "value" 500: description: An error occurred. schema: $ref: "#/definitions/ExceptionResponse" /receive_messages: post: summary: Long poll for asynchronous server messages. description: |- Return asynchronous messages from the server. This request is used by clients in a "long poll" style, and does not return until either: - A message (or messages) becomes available, in which case a list of messages is returned, or - a timeout occurs (after 60s), in which case `true` is returned and the client should re-send this request, or - for some reason the server determined that the client should stop sending `receive_messages` requests, in which case `false` is returned, and the client should only send the request again when something substantial changes such as a new file type is opened, or the completer server is manually restarted. The following types of event are delivered asynchronously for certain filetypes: - Status messages to be displayed unobtrusively to the user. - Diagnostics (for Java only). This message is optional. Clients do not require to implement this method, but it is strongly recommended for certain languages to offer the best user experience. produces: - application/json parameters: - name: request_data in: body description: |- The context data, including the current cursor position, and details of dirty buffers. required: true schema: $ref: "#/definitions/SimpleRequest" responses: 200: description: |- Messages are ready, the request timed out, or the request is not supported and should not be retried. The response may be **one of** `MessagePollResponse` or `MessagesList`. schema: allOf: - $ref: '#/definitions/MessagePollResponse' - $ref: '#/definitions/MessageList' examples: application/json: - message: 'Initializing: 19% complete' - message: 'Initializing: Done.' - diagonostics: filepath: '/file' diagnostics: - ranges: - start: { line_num: 10, column_num: 11, filepath: '/file' } end: { line_num: 10, column_num: 20, filepath: '/file' } location: { line_num: 10, column_num: 11, filepath: '/file' } location_extent: start: { line_num: 10, column_num: 11, filepath: '/file' } end: { line_num: 10, column_num: 11, filepath: '/file' } text: Very naughty code! kind: WARNING fixit_available: false - ranges: - start: { line_num: 19, column_num: 11, filepath: '/file' } end: { line_num: 19, column_num: 20, filepath: '/file' } location: { line_num: 19, column_num: 11, filepath: '/file' } location_extent: start: { line_num: 19, column_num: 11, filepath: '/file' } end: { line_num: 19, column_num: 11, filepath: '/file' } text: Very dangerous code! kind: ERROR fixit_available: true 500: description: An error occurred. schema: $ref: "#/definitions/ExceptionResponse" /resolve_fixit: post: summary: Resolve an incomplete FixIt. description: |- Resolves an incomplete fixit, indicated by `'resolve'` fixit property. Some completers return incomplete fixits in order to avoid blocking on expensive fixit calculations. When a client wishes to resolve a fixit, the entire `FixIt` object should be sent in the `fixit` property. If a client tries to resolve an already resolved fixit, the same fixit is returned in a SubcommandResponse. produces: application/json parameters: - name: request_data in: body description: |- The context data, including the current cursor position, and details of dirty buffers. required: true schema: type: object required: - filepath - file_data - fixit properties: filepath: $ref: "#/definitions/FilePath" file_data: $ref: "#/definitions/FileDataMap" fixit: $ref: "#/definitions/FixIt" example: filepath: "/home/test/dev/test.c" file_data: "/home/test/dev/test.c": filetypes: [ 'c' ] contents: "" fixit: $ref: "#/definitions/UnresolvedFixIt" responses: 200: description: |- The fixit has been resolved and a FixIt response is returned. schema: $ref: "#/definitions/SubcommandResponse" 500: description: An error occurred schema: $ref: "#/definitions/ExceptionResponse" ycmd-0+20201028+git1d415c5+ds/docs/package.json000066400000000000000000000002241374632460100203530ustar00rootroot00000000000000{ "description": "Dependencies for building the API docs", "dependencies": { "bootprint": "^1.0.0", "bootprint-openapi": "^1.0.1" } } ycmd-0+20201028+git1d415c5+ds/examples/000077500000000000000000000000001374632460100167555ustar00rootroot00000000000000ycmd-0+20201028+git1d415c5+ds/examples/.gitignore000066400000000000000000000000141374632460100207400ustar00rootroot00000000000000samples/obj ycmd-0+20201028+git1d415c5+ds/examples/.ycm_extra_conf.py000066400000000000000000000077661374632460100224250ustar00rootroot00000000000000# Copyright (C) 2014 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 . import os import ycm_core # 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', '-fexceptions', '-DNDEBUG', # THIS IS IMPORTANT! Without a "-std=" 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 # a "-std=". # For a C project, you would set this to something like 'c99' instead of # 'c++11'. '-std=c++11', # ...and the same thing goes for the magic -x option which specifies the # language that the files to be compiled are written in. This is mostly # relevant for c++ headers. # For a C project, you would set this to 'c' instead of 'c++'. '-x', 'c++', '-isystem', '/usr/include', '-isystem', '/usr/local/include', '-isystem', '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1', '-isystem', '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include', ] # 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 # # Most projects will NOT need to set this to anything; you can just change the # 'flags' list of compilation flags. compilation_database_folder = '' if os.path.exists( compilation_database_folder ): database = ycm_core.CompilationDatabase( compilation_database_folder ) else: database = None SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ] def DirectoryOfThisScript(): return os.path.dirname( os.path.abspath( __file__ ) ) def IsHeaderFile( filename ): extension = os.path.splitext( filename )[ 1 ] return extension in [ '.h', '.hxx', '.hpp', '.hh' ] def GetCompilationInfoForFile( filename ): # The compilation_commands.json file generated by CMake does not have entries # for header files. So we do our best by asking the db for flags for a # corresponding source file, if any. If one exists, the flags for that file # should be good enough. if IsHeaderFile( filename ): basename = os.path.splitext( filename )[ 0 ] for extension in SOURCE_EXTENSIONS: replacement_file = basename + extension if os.path.exists( replacement_file ): compilation_info = database.GetCompilationInfoForFile( replacement_file ) if compilation_info.compiler_flags_: return compilation_info return None return database.GetCompilationInfoForFile( filename ) # This is the entry point; this function is called by ycmd to produce flags for # a file. def Settings( **kwargs ): if not database: return { 'flags': flags, 'include_paths_relative_to_dir': DirectoryOfThisScript() } filename = kwargs[ 'filename' ] compilation_info = GetCompilationInfoForFile( filename ) if not compilation_info: return None # Bear in mind that compilation_info.compiler_flags_ does NOT return a # python list, but a "list-like" StringVec object. return { 'flags': list( compilation_info.compiler_flags_ ), 'include_paths_relative_to_dir': compilation_info.compiler_working_dir_ } ycmd-0+20201028+git1d415c5+ds/examples/LICENSE.txt000066400000000000000000000261361374632460100206100ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ycmd-0+20201028+git1d415c5+ds/examples/README.md000066400000000000000000000015661374632460100202440ustar00rootroot00000000000000# ycmd example client The example client **requires** Python 3.6+. First make sure you have built ycmd; see the top-level README for details. Then install all the Python requirements with [pip][] (run as admin/super user): ``` pip install -r requirements.txt --use-mirrors ``` Then just run `./example_client.py` from the console. It will start `ycmd`, send it some example requests while logging the full HTTP request & response and then shut everything down. The best way to learn how to use ycmd is to play around with the example client; tweak the code, send other requests etc. Start by looking at the `Main()` function at the bottom of the file. NOTE: Everything in this folder and below is licensed under the [Apache2 license][apache], not the GPLv3 like the rest of ycmd. [pip]: http://pip.readthedocs.org/en/latest/ [apache]: http://www.apache.org/licenses/LICENSE-2.0 ycmd-0+20201028+git1d415c5+ds/examples/__init__.py000066400000000000000000000000001374632460100210540ustar00rootroot00000000000000ycmd-0+20201028+git1d415c5+ds/examples/example_client.py000077500000000000000000000424541374632460100223340ustar00rootroot00000000000000#!/usr/bin/env python # # Copyright (C) 2020 ycmd contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import sys import platform if sys.version_info[ 0 ] < 3: sys.exit( 'example_client.py requires Python 3.6+; detected Python ' + platform.python_version() ) from base64 import b64encode, b64decode from hmac import compare_digest, new from tempfile import NamedTemporaryFile import collections import hashlib import json import os import socket import subprocess import sys import urllib.parse import time import requests from enum import Enum HMAC_HEADER = 'X-Ycm-Hmac' HMAC_SECRET_LENGTH = 16 SERVER_IDLE_SUICIDE_SECONDS = 10800 # 3 hours MAX_SERVER_WAIT_TIME_SECONDS = 5 # Set this to True to see ycmd's output interleaved with the client's INCLUDE_YCMD_OUTPUT = True DEFINED_SUBCOMMANDS_HANDLER = '/defined_subcommands' CODE_COMPLETIONS_HANDLER = '/completions' COMPLETER_COMMANDS_HANDLER = '/run_completer_command' EVENT_HANDLER = '/event_notification' EXTRA_CONF_HANDLER = '/load_extra_conf_file' RECEIVE_MESSAGES_HANDLER = '/receive_messages' DIR_OF_THIS_SCRIPT = os.path.dirname( os.path.abspath( __file__ ) ) PATH_TO_YCMD = os.path.join( DIR_OF_THIS_SCRIPT, '..', 'ycmd' ) PATH_TO_EXTRA_CONF = os.path.join( DIR_OF_THIS_SCRIPT, '.ycm_extra_conf.py' ) class Event( Enum ): FileReadyToParse = 1 BufferUnload = 2 BufferVisit = 3 InsertLeave = 4 CurrentIdentifierFinished = 5 # Wrapper around ycmd's HTTP+JSON API class YcmdHandle( object ): def __init__( self, popen_handle, port, hmac_secret ): self._popen_handle = popen_handle self._port = port self._hmac_secret = hmac_secret self._server_location = 'http://127.0.0.1:' + str( port ) @classmethod def StartYcmdAndReturnHandle( cls ): prepared_options = DefaultSettings() hmac_secret = os.urandom( HMAC_SECRET_LENGTH ) prepared_options[ 'hmac_secret' ] = str( b64encode( hmac_secret ), 'utf-8' ) # The temp options file is deleted by ycmd during startup. with NamedTemporaryFile( mode = 'w+', delete = False ) as options_file: json.dump( prepared_options, options_file ) server_port = GetUnusedLocalhostPort() ycmd_args = [ sys.executable, PATH_TO_YCMD, f'--port={server_port}', f'--options_file={options_file.name}', f'--idle_suicide_seconds={SERVER_IDLE_SUICIDE_SECONDS}' ] std_handles = None if INCLUDE_YCMD_OUTPUT else subprocess.PIPE child_handle = subprocess.Popen( ycmd_args, stdout = std_handles, stderr = std_handles ) return cls( child_handle, server_port, hmac_secret ) def IsAlive( self ): returncode = self._popen_handle.poll() # When the process hasn't finished yet, poll() returns None. return returncode is None def IsReady( self, filetype = None ): if not self.IsAlive(): return False params = { 'subserver': filetype } if filetype else None response = self.GetFromHandler( 'ready', params ) response.raise_for_status() return response.json() def Shutdown( self ): if self.IsAlive(): self.PostToHandlerAndLog( 'shutdown' ) def PostToHandlerAndLog( self, handler, data = None ): self._CallHttpie( 'post', handler, data ) def GetFromHandlerAndLog( self, handler ): self._CallHttpie( 'get', handler ) def GetFromHandler( self, handler, params = None ): request_uri = self._BuildUri( handler ) extra_headers = self._ExtraHeaders( 'GET', urllib.parse.urlparse( request_uri ).path, '' ) response = requests.get( request_uri, headers = extra_headers, params = params ) self._ValidateResponseObject( response ) return response def SendDefinedSubcommandsRequest( self, completer_target ): request_json = BuildRequestData( completer_target = completer_target ) print( '==== Sending defined subcommands request ====' ) self.PostToHandlerAndLog( DEFINED_SUBCOMMANDS_HANDLER, request_json ) def SendCodeCompletionRequest( self, test_filename, filetype, line_num, column_num ): request_json = BuildRequestData( test_filename = test_filename, filetype = filetype, line_num = line_num, column_num = column_num ) print( '==== Sending code-completion request ====' ) self.PostToHandlerAndLog( CODE_COMPLETIONS_HANDLER, request_json ) def SendGoToRequest( self, test_filename, filetype, line_num, column_num ): request_json = BuildRequestData( test_filename = test_filename, command_arguments = ['GoTo'], filetype = filetype, line_num = line_num, column_num = column_num ) print( '==== Sending GoTo request ====' ) self.PostToHandlerAndLog( COMPLETER_COMMANDS_HANDLER, request_json ) def SendEventNotification( self, event_enum, test_filename, filetype, line_num = 1, # just placeholder values column_num = 1, extra_data = None ): request_json = BuildRequestData( test_filename = test_filename, filetype = filetype, line_num = line_num, column_num = column_num ) if extra_data: request_json.update( extra_data ) request_json[ 'event_name' ] = event_enum.name print( '==== Sending event notification ====' ) self.PostToHandlerAndLog( EVENT_HANDLER, request_json ) def ReceiveMessages( self, test_filename, filetype ): request_json = BuildRequestData( test_filename = test_filename, filetype = filetype ) print( '==== Sending Messages request ====' ) self.PostToHandlerAndLog( RECEIVE_MESSAGES_HANDLER, request_json ) def LoadExtraConfFile( self, extra_conf_filename ): request_json = { 'filepath': extra_conf_filename } self.PostToHandlerAndLog( EXTRA_CONF_HANDLER, request_json ) def WaitUntilReady( self, filetype = None ): total_slept = 0 time.sleep( 0.5 ) total_slept += 0.5 while True: try: if total_slept > MAX_SERVER_WAIT_TIME_SECONDS: raise RuntimeError( 'waited for the server for ' f'{MAX_SERVER_WAIT_TIME_SECONDS} seconds, aborting' ) if self.IsReady( filetype ): return except requests.exceptions.ConnectionError: pass finally: time.sleep( 0.1 ) total_slept += 0.1 def _ExtraHeaders( self, method, path, body ): return { HMAC_HEADER: self._HmacForRequest( method, path, body ) } def _HmacForRequest( self, method, path, body ): return str( b64encode( CreateRequestHmac( method, path, body, self._hmac_secret ) ), 'utf8' ) def _BuildUri( self, handler ): return urllib.parse.urljoin( self._server_location, handler ) def _ValidateResponseObject( self, response ): if not ContentHmacValid( response.content, b64decode( response.headers[ HMAC_HEADER ] ), self._hmac_secret ): raise RuntimeError( 'Received invalid HMAC for response!' ) return True # Use httpie instead of Requests directly so that we get the nice json # pretty-printing, output colorization and full request/response logging for # free def _CallHttpie( self, method, handler, data = None ): method = method.upper() request_uri = self._BuildUri( handler ) args = [ 'http', '-v', method, request_uri ] if isinstance( data, collections.abc.Mapping ): args.append( 'content-type:application/json' ) data = ToUtf8Json( data ) hmac = self._HmacForRequest( method, urllib.parse.urlparse( request_uri ).path, data ) args.append( HMAC_HEADER + ':' + hmac ) if method == 'GET': popen = subprocess.Popen( args ) else: popen = subprocess.Popen( args, stdin = subprocess.PIPE ) popen.communicate( data ) popen.wait() def ToBytes( value ): if not value: return bytes() if isinstance( value, bytes ): return value if isinstance( value, int ): value = str( value ) return bytes( value, encoding = 'utf-8' ) def ContentHmacValid( content, hmac, hmac_secret ): return compare_digest( CreateHmac( content, hmac_secret ), hmac ) def CreateRequestHmac( method, path, body, hmac_secret ): method = ToBytes( method ) path = ToBytes( path ) body = ToBytes( body ) hmac_secret = ToBytes( hmac_secret ) method_hmac = CreateHmac( method, hmac_secret ) path_hmac = CreateHmac( path, hmac_secret ) body_hmac = CreateHmac( body, hmac_secret ) joined_hmac_input = bytes().join( ( method_hmac, path_hmac, body_hmac ) ) return CreateHmac( joined_hmac_input, hmac_secret ) def CreateHmac( content, hmac_secret ): return bytes( new( ToBytes( hmac_secret ), msg = ToBytes( content ), digestmod = hashlib.sha256 ).digest() ) # Recurses through the object if it's a dict/iterable and converts all the # unicode objects to utf-8 encoded bytes. def RecursiveEncodeUnicodeToUtf8( value ): if isinstance( value, str ): return value.encode( 'utf-8' ) if isinstance( value, bytes ): return value elif isinstance( value, collections.Mapping ): return dict( list( map( RecursiveEncodeUnicodeToUtf8, iter( value.items() ) ) ) ) elif isinstance( value, collections.Iterable ): return type( value )( list( map( RecursiveEncodeUnicodeToUtf8, value ) ) ) else: return value def ToUtf8Json( data ): return json.dumps( data, ensure_ascii = False ).encode( 'utf-8' ) def PathToTestFile( filename ): return os.path.join( DIR_OF_THIS_SCRIPT, 'samples', filename ) def DefaultSettings(): default_options_path = os.path.join( DIR_OF_THIS_SCRIPT, '..', 'ycmd', 'default_settings.json' ) with open( default_options_path ) as f: return json.loads( f.read() ) def GetUnusedLocalhostPort(): sock = socket.socket() # This tells the OS to give us any free port in the range [1024 - 65535] sock.bind( ( '', 0 ) ) port = sock.getsockname()[ 1 ] sock.close() return port def PrettyPrintDict( value ): # Sad that this works better than pprint... return json.dumps( value, sort_keys = True, indent = 2 ).replace( '\\n', '\n') def BuildRequestData( test_filename = None, filetype = None, line_num = None, column_num = None, command_arguments = None, completer_target = None ): test_path = PathToTestFile( test_filename ) if test_filename else '' # Normally, this would be the contents of the file as loaded in the editor # (possibly unsaved data). contents = open( test_path ).read() if test_path else '' data = { 'line_num': line_num, 'column_num': column_num, 'filepath': test_path, 'file_data': { test_path: { 'filetypes': [ filetype ], 'contents': contents } } } if command_arguments: data[ 'command_arguments' ] = command_arguments if completer_target: data[ 'completer_target' ] = completer_target return data def PythonSemanticCompletionResults( server ): server.SendEventNotification( Event.FileReadyToParse, test_filename = 'some_python.py', filetype = 'python' ) server.SendCodeCompletionRequest( test_filename = 'some_python.py', filetype = 'python', line_num = 25, column_num = 6 ) def LanguageAgnosticIdentifierCompletion( server ): # We're using vimscript here, but the language doesn't matter; the identifier # completion engine just extracts identifiers. server.SendEventNotification( Event.FileReadyToParse, test_filename = 'some_vimscript.vim', filetype = 'vim' ) server.SendCodeCompletionRequest( test_filename = 'some_vimscript.vim', filetype = 'vim', line_num = 21, column_num = 6 ) def CppSemanticCompletionResults( server ): # TODO: document this better server.LoadExtraConfFile( PATH_TO_EXTRA_CONF ) # NOTE: The server will return diagnostic information about an error in the # some_cpp.cpp file that we placed there intentionally (as an example). # Clang will recover from this error and still manage to parse the file # though. server.SendEventNotification( Event.FileReadyToParse, test_filename = 'some_cpp.cpp', filetype = 'cpp' ) server.SendCodeCompletionRequest( test_filename = 'some_cpp.cpp', filetype = 'cpp', line_num = 25, column_num = 7 ) def PythonGetSupportedCommands( server ): server.SendDefinedSubcommandsRequest( completer_target = 'python' ) def CppGotoDeclaration( server ): # NOTE: No need to load extra conf file or send FileReadyToParse event, it was # already done in CppSemanticCompletionResults. server.SendGoToRequest( test_filename = 'some_cpp.cpp', filetype = 'cpp', line_num = 23, column_num = 4 ) def CsharpSemanticCompletionResults( server ): # First such request starts the OmniSharpServer server.SendEventNotification( Event.FileReadyToParse, test_filename = 'some_csharp.cs', filetype = 'cs' ) # We have to wait until OmniSharpServer has started and loaded the solution # file print( 'Waiting for OmniSharpServer to become ready...' ) server.WaitUntilReady( filetype = 'cs' ) server.SendCodeCompletionRequest( test_filename = 'some_csharp.cs', filetype = 'cs', line_num = 10, column_num = 15 ) def JavaMessages( server ): # NOTE: The server will return diagnostic information about an error in the # some_java.java file that we placed there intentionally (as an example). # It is _not_returned in the FileReadyToParse, but the ReceiveMessages poll server.SendEventNotification( Event.FileReadyToParse, test_filename = 'some_java.java', filetype = 'java' ) # Send the long poll 5 times (only the first N will return any useful # messages) for i in range(1, 6): server.ReceiveMessages( test_filename = 'some_java.java', filetype = 'java' ) # Send a code complete request server.SendCodeCompletionRequest( test_filename = 'some_java.java', filetype = 'java', line_num = 5, column_num = 8 ) # NOTE: The server will return diagnostic information about an error in the # some_java.java file that we placed there intentionally (as an example). # It is _not_returned in the FileReadyToParse, but the ReceiveMessages poll server.SendEventNotification( Event.FileReadyToParse, test_filename = 'some_java.java', filetype = 'java' ) # Send the long poll 5 times (only the first N will return any useful # messages) for i in range(1, 6): server.ReceiveMessages( test_filename = 'some_java.java', filetype = 'java' ) def Main(): print( 'Trying to start server...' ) server = YcmdHandle.StartYcmdAndReturnHandle() server.WaitUntilReady() LanguageAgnosticIdentifierCompletion( server ) PythonSemanticCompletionResults( server ) CppSemanticCompletionResults( server ) CsharpSemanticCompletionResults( server ) JavaMessages( server ) # This will ask the server for a list of subcommands supported by a given # language completer. PythonGetSupportedCommands( server ) # GoTo is an example of a completer subcommand. # Python and C# completers also support the GoTo subcommand. CppGotoDeclaration( server ) print( 'Shutting down server...' ) server.Shutdown() if __name__ == "__main__": Main() ycmd-0+20201028+git1d415c5+ds/examples/requirements.txt000066400000000000000000000000361374632460100222400ustar00rootroot00000000000000requests>=2.5.1 httpie>=0.5.0 ycmd-0+20201028+git1d415c5+ds/examples/samples/000077500000000000000000000000001374632460100204215ustar00rootroot00000000000000ycmd-0+20201028+git1d415c5+ds/examples/samples/some_cpp.cpp000066400000000000000000000013651374632460100227370ustar00rootroot00000000000000// Copyright (C) 2014 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. struct Foo { int x; int y // There's a missing semicolon here char c; }; int main() { Foo foo; // The location after the dot is line 25, col 7 foo. } ycmd-0+20201028+git1d415c5+ds/examples/samples/some_csharp.cs000066400000000000000000000003021374632460100232460ustar00rootroot00000000000000using System; namespace some_csharp { class MainClass { public static void Main (string[] args) { // location after second dot is line 9, column 15 Console. } } } ycmd-0+20201028+git1d415c5+ds/examples/samples/some_csharp.csproj000066400000000000000000000031301374632460100241430ustar00rootroot00000000000000 Debug x86 10.0.0 2.0 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F} Exe some_csharp some_csharp true full false bin\Debug DEBUG; prompt 4 true x86 full true bin\Release prompt 4 true x86 ycmd-0+20201028+git1d415c5+ds/examples/samples/some_csharp.sln000066400000000000000000000015261374632460100234460ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "some_csharp", "some_csharp.csproj", "{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.ActiveCfg = Debug|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.Build.0 = Debug|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.ActiveCfg = Release|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution StartupItem = some_csharp.csproj EndGlobalSection EndGlobal ycmd-0+20201028+git1d415c5+ds/examples/samples/some_java.java000066400000000000000000000002031374632460100232230ustar00rootroot00000000000000public class some_java { private int an_int = 1.0f; public static void main( String[] args ) { some_java j; j.an } } ycmd-0+20201028+git1d415c5+ds/examples/samples/some_python.py000066400000000000000000000014061374632460100233400ustar00rootroot00000000000000# Copyright (C) 2014 Google Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. class Example( object ): def __init__( self ): self.x = 1 self.y = 2 self.z = 3 if __name__ == "__main__": ex = Example() # location after the dot is line 25, column 6 ex. ycmd-0+20201028+git1d415c5+ds/examples/samples/some_vimscript.vim000066400000000000000000000013471374632460100242060ustar00rootroot00000000000000" Copyright (C) 2020 ycmd contributors " " Licensed under the Apache License, Version 2.0 (the "License"); " you may not use this file except in compliance with the License. " You may obtain a copy of the License at " " http://www.apache.org/licenses/LICENSE-2.0 " " Unless required by applicable law or agreed to in writing, software " distributed under the License is distributed on an "AS IS" BASIS, " WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. " See the License for the specific language governing permissions and " limitations under the License. function s:F() let x = 10 let y = 15 let foobar = x + y let foozoo = x + y " location after the second 'o' is line 21, column 6 foo endfunction ycmd-0+20201028+git1d415c5+ds/pytest.ini000066400000000000000000000003521374632460100171700ustar00rootroot00000000000000[pytest] python_functions = *_test python_classes = *_test xfail_strict = true log_level = debug log_date_format = %Y-%m-%d %H:%M:%S log_format = %(asctime)s - %(levelname)s - %(message)s log_cli_level = debug markers = valgrind_skip ycmd-0+20201028+git1d415c5+ds/requirements.txt000066400000000000000000000001651374632460100204250ustar00rootroot00000000000000bottle >= 0.12.18 regex >= 2020.2.20 jedi >= 0.16.0 requests >= 2.22.0 waitress >= 1.4.3 watchdog >= 0.10.2 ycmd-0+20201028+git1d415c5+ds/run_tests.py000077500000000000000000000274111374632460100175470ustar00rootroot00000000000000#!/usr/bin/env python3 import argparse import platform import os import glob import subprocess import os.path as p import sys BASE_PYTEST_ARGS = [ '-v', '--color=yes' ] DIR_OF_THIS_SCRIPT = p.dirname( p.abspath( __file__ ) ) DIR_OF_THIRD_PARTY = p.join( DIR_OF_THIS_SCRIPT, 'third_party' ) DIR_OF_WATCHDOG_DEPS = p.join( DIR_OF_THIRD_PARTY, 'watchdog_deps' ) LIBCLANG_DIR = p.join( DIR_OF_THIRD_PARTY, 'clang', 'lib' ) python_path = [ p.join( DIR_OF_THIRD_PARTY, 'bottle' ), p.join( DIR_OF_THIRD_PARTY, 'regex-build' ), p.join( DIR_OF_THIRD_PARTY, 'frozendict' ), p.join( DIR_OF_THIRD_PARTY, 'jedi_deps', 'jedi' ), p.join( DIR_OF_THIRD_PARTY, 'jedi_deps', 'parso' ), p.join( DIR_OF_THIRD_PARTY, 'requests_deps', 'certifi' ), p.join( DIR_OF_THIRD_PARTY, 'requests_deps', 'chardet' ), p.join( DIR_OF_THIRD_PARTY, 'requests_deps', 'idna' ), p.join( DIR_OF_THIRD_PARTY, 'requests_deps', 'requests' ), p.join( DIR_OF_THIRD_PARTY, 'requests_deps', 'urllib3', 'src' ), p.join( DIR_OF_WATCHDOG_DEPS, 'watchdog', 'build', 'lib3' ), p.join( DIR_OF_WATCHDOG_DEPS, 'pathtools' ), p.join( DIR_OF_THIRD_PARTY, 'waitress' ), ] if os.environ.get( 'PYTHONPATH' ) is not None: python_path.append( os.environ[ 'PYTHONPATH' ] ) os.environ[ 'PYTHONPATH' ] = ( os.pathsep.join( python_path ) + os.pathsep + p.join( DIR_OF_THIRD_PARTY, 'jedi_deps', 'numpydoc' ) ) def OnWindows(): return platform.system() == 'Windows' def RunFlake8(): print( 'Running flake8' ) args = [ sys.executable, '-m', 'flake8', p.join( DIR_OF_THIS_SCRIPT, 'ycmd' ) ] root_dir_scripts = glob.glob( p.join( DIR_OF_THIS_SCRIPT, '*.py' ) ) args.extend( root_dir_scripts ) subprocess.check_call( args ) # Newer completers follow a standard convention of: # - build: ---completer # - test directory: ycmd/tests/ # - no aliases. SIMPLE_COMPLETERS = [ 'clangd', 'rust', 'go', ] # More complex or legacy cases can specify all of: # - build: flags to add to build.py to include this completer # - test: flags to add to run_tests.py when _not_ testing this completer # - aliases?: list of completer aliases for the --completers option COMPLETERS = { 'cfamily': { 'build': [ '--clang-completer' ], 'test': [ '--ignore=ycmd/tests/clang' ], 'aliases': [ 'c', 'cpp', 'c++', 'objc', 'clang', ] }, 'cs': { 'build': [ '--cs-completer' ], 'test': [ '--ignore=ycmd/tests/cs' ], 'aliases': [ 'omnisharp', 'csharp', 'c#' ] }, 'javascript': { 'build': [ '--js-completer' ], 'test': [ '--ignore=ycmd/tests/tern' ], 'aliases': [ 'js', 'tern' ] }, 'typescript': { 'build': [ '--ts-completer' ], 'test': [ '--ignore=ycmd/tests/javascript', '--ignore=ycmd/tests/typescript' ], 'aliases': [ 'ts' ] }, 'python': { 'build': [], 'test': [ '--ignore=ycmd/tests/python' ], 'aliases': [ 'jedi', 'jedihttp', ] }, 'java': { 'build': [ '--java-completer' ], 'test': [ '--ignore=ycmd/tests/java' ], 'aliases': [ 'jdt' ], }, } # Add in the simple completers for completer in SIMPLE_COMPLETERS: COMPLETERS[ completer ] = { 'build': [ '--{}-completer'.format( completer ) ], 'test': [ '--ignore=ycmd/tests/{}'.format( completer ) ], } def CompleterType( value ): value = value.lower() if value in COMPLETERS: return value else: aliases_to_completer = { i: k for k, v in COMPLETERS.items() for i in v[ 'aliases' ] } if value in aliases_to_completer: return aliases_to_completer[ value ] else: raise argparse.ArgumentTypeError( '{0} is not a valid completer - should be one of {1}'.format( value, COMPLETERS.keys() ) ) def ParseArguments(): parser = argparse.ArgumentParser() group = parser.add_mutually_exclusive_group() group.add_argument( '--no-clang-completer', action = 'store_true', help = argparse.SUPPRESS ) # deprecated group.add_argument( '--no-completers', nargs ='*', type = CompleterType, help = 'Do not build or test with listed semantic ' 'completion engine(s).', choices = COMPLETERS.keys() ) group.add_argument( '--completers', nargs ='*', type = CompleterType, help = 'Only build and test with listed semantic ' 'completion engine(s).', choices = COMPLETERS.keys() ) parser.add_argument( '--skip-build', action = 'store_true', help = 'Do not build ycmd before testing.' ) 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( '--coverage', action = 'store_true', help = 'Enable coverage report (requires coverage pkg)' ) parser.add_argument( '--no-flake8', action = 'store_true', help = 'Disable flake8 run.' ) parser.add_argument( '--dump-path', action = 'store_true', help = 'Dump the PYTHONPATH required to run tests ' 'manually, then exit.' ) parser.add_argument( '--no-retry', action = 'store_true', help = 'Disable retry of flaky tests' ) parser.add_argument( '--quiet', action = 'store_true', help = 'Quiet installation mode. Just print overall ' 'progress and errors' ) parser.add_argument( '--valgrind', action = 'store_true', help = 'Run tests inside valgrind.' ) parsed_args, pytests_args = parser.parse_known_args() parsed_args.completers = FixupCompleters( parsed_args ) if 'COVERAGE' in os.environ: parsed_args.coverage = ( os.environ[ 'COVERAGE' ] == 'true' ) return parsed_args, pytests_args def FixupCompleters( parsed_args ): completers = set( COMPLETERS.keys() ) if parsed_args.completers is not None: completers = set( parsed_args.completers ) elif parsed_args.no_completers is not None: completers = completers.difference( parsed_args.no_completers ) elif parsed_args.no_clang_completer: print( 'WARNING: The "--no-clang-completer" flag is deprecated. ' 'Please use "--no-completers cfamily" instead.' ) completers.discard( 'cfamily' ) if 'USE_CLANG_COMPLETER' in os.environ: if os.environ[ 'USE_CLANG_COMPLETER' ] == 'false': completers.discard( 'cfamily' ) else: completers.add( 'cfamily' ) return list( completers ) def BuildYcmdLibs( args ): if not args.skip_build: if 'EXTRA_CMAKE_ARGS' in os.environ: os.environ[ 'EXTRA_CMAKE_ARGS' ] += ' -DUSE_DEV_FLAGS=ON' else: os.environ[ 'EXTRA_CMAKE_ARGS' ] = '-DUSE_DEV_FLAGS=ON' build_cmd = [ sys.executable, p.join( DIR_OF_THIS_SCRIPT, 'build.py' ), '--core-tests' ] for key in COMPLETERS: if key in args.completers: build_cmd.extend( COMPLETERS[ key ][ 'build' ] ) if args.msvc: build_cmd.extend( [ '--msvc', str( args.msvc ) ] ) if args.coverage: # In order to generate coverage data for C++, we use gcov. This requires # some files generated when building (*.gcno), so we store the build # output in a known directory, which is then used by the CI infrastructure # to generate the c++ coverage information. build_cmd.extend( [ '--enable-coverage', '--build-dir', '.build' ] ) if args.quiet: build_cmd.append( '--quiet' ) subprocess.check_call( build_cmd ) def PytestValgrind( parsed_args, extra_pytests_args ): pytests_args = BASE_PYTEST_ARGS if extra_pytests_args: pytests_args.extend( extra_pytests_args ) else: pytests_args += glob.glob( p.join( DIR_OF_THIS_SCRIPT, 'ycmd', 'tests', 'bindings', '*_test.py' ) ) pytests_args += glob.glob( p.join( DIR_OF_THIS_SCRIPT, 'ycmd', 'tests', 'clang', '*_test.py' ) ) pytests_args += glob.glob( p.join( DIR_OF_THIS_SCRIPT, 'ycmd', 'tests', '*_test.py' ) ) # Avoids needing all completers for a valgrind run pytests_args += [ '-m', 'not valgrind_skip' ] new_env = os.environ.copy() new_env[ 'PYTHONMALLOC' ] = 'malloc' new_env[ 'LD_LIBRARY_PATH' ] = LIBCLANG_DIR cmd = [ 'valgrind', '--gen-suppressions=all', '--error-exitcode=1', '--leak-check=full', '--show-leak-kinds=definite,indirect', '--errors-for-leak-kinds=definite,indirect', '--suppressions=' + p.join( DIR_OF_THIS_SCRIPT, 'valgrind.suppressions' ) ] subprocess.check_call( cmd + [ sys.executable, '-m', 'pytest' ] + pytests_args, env = new_env ) def PytestTests( parsed_args, extra_pytests_args ): pytests_args = BASE_PYTEST_ARGS for key in COMPLETERS: if key not in parsed_args.completers: pytests_args.extend( COMPLETERS[ key ][ 'test' ] ) if parsed_args.coverage: # We need to exclude the ycmd/tests/python/testdata directory since it # contains Python files and its base name starts with "test". pytests_args += [ '--ignore=ycmd/tests/python/testdata', '--cov=ycmd' ] if extra_pytests_args: pytests_args.extend( extra_pytests_args ) else: pytests_args.append( p.join( DIR_OF_THIS_SCRIPT, 'ycmd' ) ) env = os.environ.copy() if parsed_args.no_retry: # Useful for _writing_ tests env[ 'YCM_TEST_NO_RETRY' ] = '1' if OnWindows(): # We prepend the Clang third-party directory to the PATH instead of # overwriting it so that the executable is able to find the Python library. env[ 'PATH' ] = LIBCLANG_DIR + ';' + env[ 'PATH' ] else: env[ 'LD_LIBRARY_PATH' ] = LIBCLANG_DIR subprocess.check_call( [ sys.executable, '-m', 'pytest' ] + pytests_args, env=env ) # 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 FindExecutableOrDie( executable, message ): path = FindExecutable( executable ) if not path: sys.exit( "ERROR: Unable to find executable '{0}'. {1}".format( executable, message ) ) return path def SetUpGenericLSPCompleter(): old_cwd = os.getcwd() os.chdir( os.path.join( DIR_OF_THIRD_PARTY, 'generic_server' ) ) npm = FindExecutableOrDie( 'npm', 'npm is required to' 'run GenericLSPCompleter tests.' ) subprocess.check_call( [ npm, 'install' ] ) subprocess.check_call( [ npm, 'run', 'compile' ] ) os.chdir( old_cwd ) def Main(): parsed_args, pytests_args = ParseArguments() if parsed_args.dump_path: print( os.environ[ 'PYTHONPATH' ] ) sys.exit() print( 'Running tests on Python', platform.python_version() ) if not parsed_args.skip_build: SetUpGenericLSPCompleter() if not parsed_args.no_flake8: RunFlake8() BuildYcmdLibs( parsed_args ) if parsed_args.valgrind: PytestValgrind( parsed_args, pytests_args ) else: PytestTests( parsed_args, pytests_args ) if __name__ == "__main__": Main() ycmd-0+20201028+git1d415c5+ds/style_format.sh000077500000000000000000000007751374632460100202170ustar00rootroot00000000000000#!/bin/bash astyle \ --style=attach \ --indent=spaces=2 \ --indent-switches \ --indent-col1-comments \ --indent-preprocessor \ --max-instatement-indent=80 \ --break-blocks \ --pad-oper \ --pad-paren-in \ --pad-header \ --keep-one-line-blocks \ --convert-tabs \ --align-pointer=name \ --align-reference=name \ --suffix=none \ --lineend=linux \ --recursive \ --exclude=gmock \ --exclude=testdata \ --exclude=ycm_core.cpp \ --exclude=CustomAssert.h \ --exclude=CustomAssert.cpp \ "cpp/ycm/*.cpp" \ "cpp/ycm/*.h" ycmd-0+20201028+git1d415c5+ds/test_requirements.txt000066400000000000000000000004261374632460100214640ustar00rootroot00000000000000flake8 >= 3.0.0 flake8-comprehensions flake8-ycm >= 0.1.0 PyHamcrest >= 1.10.1 WebTest >= 2.0.20 psutil >= 5.6.6 coverage >= 4.2 codecov >= 2.0.5 pytest pytest-cov pytest-rerunfailures ycmd-0+20201028+git1d415c5+ds/tox.ini000066400000000000000000000003011374632460100164440ustar00rootroot00000000000000[flake8] ignore = E111,E114,E121,E125,E126,E127,E128,E129,E131,E133,E201,E202,E203,E221,E222,E241,E251,E261,E301,E303,E402,W503,W504 max-complexity = 10 max-line-length = 80 exclude = testdata ycmd-0+20201028+git1d415c5+ds/update_api_docs.py000077500000000000000000000034361374632460100206450ustar00rootroot00000000000000#!/usr/bin/env python3 from __future__ import print_function from __future__ import division from __future__ import unicode_literals from __future__ import absolute_import import os import platform import sys import subprocess DIR_OF_THIS_SCRIPT = os.path.dirname( os.path.abspath( __file__ ) ) DIR_OF_DOCS = os.path.join( DIR_OF_THIS_SCRIPT, 'docs' ) def OnWindows(): return platform.system() == 'Windows' # 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 GenerateApiDocs(): npm = FindExecutable( 'npm' ) if not npm: sys.exit( 'ERROR: NPM is required to generate API docs.' ) os.chdir( os.path.join( DIR_OF_DOCS ) ) subprocess.call( [ npm, 'install', '--production' ] ) bootprint = FindExecutable( os.path.join( DIR_OF_DOCS, 'node_modules', '.bin', 'bootprint' ) ) api = os.path.join( DIR_OF_DOCS, 'openapi.yml' ) subprocess.call( [ bootprint, 'openapi', api, DIR_OF_DOCS ] ) if __name__ == '__main__': GenerateApiDocs() ycmd-0+20201028+git1d415c5+ds/update_clang.py000077500000000000000000000430001374632460100201370ustar00rootroot00000000000000#!/usr/bin/env python3 import argparse import contextlib import os import os.path as p import platform import shutil import subprocess import sys import tempfile import tarfile import hashlib from distutils.spawn import find_executable try: import lzma except ImportError: from backports import lzma DIR_OF_THIS_SCRIPT = p.dirname( p.abspath( __file__ ) ) DIR_OF_THIRD_PARTY = p.join( DIR_OF_THIS_SCRIPT, '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 from io import BytesIO def OnWindows(): return platform.system() == 'Windows' def OnMac(): return platform.system() == 'Darwin' LLVM_DOWNLOAD_DATA = { 'win32': { 'url': 'https://github.com/llvm/llvm-project/releases/download/' 'llvmorg-{llvm_version}/{llvm_package}', 'format': 'nsis', 'llvm_package': 'LLVM-{llvm_version}-{os_name}.exe', 'clangd_package': { 'name': 'clangd-{llvm_version}-{os_name}.tar.bz2', 'files_to_copy': [ os.path.join( 'bin', 'clangd.exe' ), ] }, 'libclang_package': { 'name': 'libclang-{llvm_version}-{os_name}.tar.bz2', 'files_to_copy': [ os.path.join( 'bin', 'libclang.dll' ), os.path.join( 'lib', 'libclang.lib' ), ] } }, 'win64': { 'url': 'https://github.com/llvm/llvm-project/releases/download/' 'llvmorg-{llvm_version}/{llvm_package}', 'format': 'nsis', 'llvm_package': 'LLVM-{llvm_version}-{os_name}.exe', 'clangd_package': { 'name': 'clangd-{llvm_version}-{os_name}.tar.bz2', 'files_to_copy': [ os.path.join( 'bin', 'clangd.exe' ), ] }, 'libclang_package': { 'name': 'libclang-{llvm_version}-{os_name}.tar.bz2', 'files_to_copy': [ os.path.join( 'bin', 'libclang.dll' ), os.path.join( 'lib', 'libclang.lib' ), ] } }, 'x86_64-apple-darwin': { 'url': 'https://github.com/llvm/llvm-project/releases/download/' 'llvmorg-{llvm_version}/{llvm_package}', 'format': 'lzma', 'llvm_package': 'clang+llvm-{llvm_version}-{os_name}.tar.xz', 'clangd_package': { 'name': 'clangd-{llvm_version}-{os_name}.tar.bz2', 'files_to_copy': [ os.path.join( 'bin', 'clangd' ), ] }, 'libclang_package': { 'name': 'libclang-{llvm_version}-{os_name}.tar.bz2', 'files_to_copy': [ os.path.join( 'lib', 'libclang.dylib' ), ], } }, 'x86_64-unknown-linux-gnu': { 'url': ( 'https://github.com/ycm-core/llvm/' 'releases/download/{llvm_version}/{llvm_package}' ), 'format': 'lzma', 'llvm_package': 'clang+llvm-{llvm_version}-{os_name}.tar.xz', 'clangd_package': { 'name': 'clangd-{llvm_version}-{os_name}.tar.bz2', 'files_to_copy': [ os.path.join( 'bin', 'clangd' ), ] }, 'libclang_package': { 'name': 'libclang-{llvm_version}-{os_name}.tar.bz2', 'files_to_copy': [ os.path.join( 'lib', 'libclang.so' ), os.path.join( 'lib', 'libclang.so.{llvm_version:.2}' ) ] } }, 'i386-unknown-freebsd11': { 'url': 'https://github.com/llvm/llvm-project/releases/download/' 'llvmorg-{llvm_version}/{llvm_package}', 'format': 'lzma', 'llvm_package': 'clang+llvm-{llvm_version}-{os_name}.tar.xz', 'clangd_package': { 'name': 'clangd-{llvm_version}-{os_name}.tar.bz2', 'files_to_copy': [ os.path.join( 'bin', 'clangd' ), ] }, 'libclang_package': { 'name': 'libclang-{llvm_version}-{os_name}.tar.bz2', 'files_to_copy': [ os.path.join( 'lib', 'libclang.so' ), os.path.join( 'lib', 'libclang.so.{llvm_version:.2}' ) ] } }, 'amd64-unknown-freebsd11': { 'url': 'https://github.com/llvm/llvm-project/releases/download/' 'llvmorg-{llvm_version}/{llvm_package}', 'format': 'lzma', 'llvm_package': 'clang+llvm-{llvm_version}-{os_name}.tar.xz', 'clangd_package': { 'name': 'clangd-{llvm_version}-{os_name}.tar.bz2', 'files_to_copy': [ os.path.join( 'bin', 'clangd' ), ] }, 'libclang_package': { 'name': 'libclang-{llvm_version}-{os_name}.tar.bz2', 'files_to_copy': [ os.path.join( 'lib', 'libclang.so' ), os.path.join( 'lib', 'libclang.so.{llvm_version:.2}' ) ] } }, 'aarch64-linux-gnu': { 'url': 'https://github.com/llvm/llvm-project/releases/download/' 'llvmorg-{llvm_version}/{llvm_package}', 'format': 'lzma', 'llvm_package': 'clang+llvm-{llvm_version}-{os_name}.tar.xz', 'clangd_package': { 'name': 'clangd-{llvm_version}-{os_name}.tar.bz2', 'files_to_copy': [ os.path.join( 'bin', 'clangd' ), ] }, 'libclang_package': { 'name': 'libclang-{llvm_version}-{os_name}.tar.bz2', 'files_to_copy': [ os.path.join( 'lib', 'libclang.so' ), os.path.join( 'lib', 'libclang.so.{llvm_version:.2}' ) ] } }, 'armv7a-linux-gnueabihf': { 'url': 'https://github.com/llvm/llvm-project/releases/download/' 'llvmorg-{llvm_version}/{llvm_package}', 'format': 'lzma', 'llvm_package': 'clang+llvm-{llvm_version}-{os_name}.tar.xz', 'clangd_package': { 'name': 'clangd-{llvm_version}-{os_name}.tar.bz2', 'files_to_copy': [ os.path.join( 'bin', 'clangd' ), ] }, 'libclang_package': { 'name': 'libclang-{llvm_version}-{os_name}.tar.bz2', 'files_to_copy': [ os.path.join( 'lib', 'libclang.so' ), os.path.join( 'lib', 'libclang.so.{llvm_version:.2}' ) ] } }, } @contextlib.contextmanager def TemporaryDirectory( keep_temp ): temp_dir = tempfile.mkdtemp() try: yield temp_dir finally: if keep_temp: print( "*** Please delete temp dir: {}".format( temp_dir ) ) else: shutil.rmtree( temp_dir ) def DownloadClangLicense( version, destination ): print( 'Downloading license...' ) request = requests.get( 'https://releases.llvm.org/{version}/LICENSE.TXT'.format( version=version ), stream = True ) request.raise_for_status() file_name = os.path.join( destination, 'LICENSE.TXT' ) with open( file_name, 'wb' ) as f: f.write( request.content ) request.close() return file_name def Download( url ): print( 'Downloading {}'.format( url.rsplit( '/', 1 )[ -1 ] ) ) request = requests.get( url, stream=True ) request.raise_for_status() content = request.content request.close() return content def ExtractTar( uncompressed_data, destination ): with tarfile.TarFile( fileobj=uncompressed_data, mode='r' ) as tar_file: a_member = tar_file.getmembers()[ 0 ] tar_file.extractall( destination ) # Determine the directory name return os.path.join( destination, a_member.name.split( '/' )[ 0 ] ) def ExtractLZMA( compressed_data, destination ): uncompressed_data = BytesIO( lzma.decompress( compressed_data ) ) return ExtractTar( uncompressed_data, destination ) def Extract7Z( llvm_package, archive, destination ): # Extract with appropriate tool if OnWindows(): import winreg with winreg.OpenKey( winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\7-Zip' ) as key: executable = os.path.join( winreg.QueryValueEx( key, "Path" )[ 0 ], '7z.exe' ) elif OnMac(): # p7zip is available from homebrew (brew install p7zip) executable = find_executable( '7z' ) else: # On Linux, p7zip 16.02 is required. executable = find_executable( '7z' ) command = [ executable, '-y', 'x', archive, '-o' + destination ] # Silence 7-Zip output. subprocess.check_call( command, stdout = subprocess.PIPE ) return destination def MakeBundle( files_to_copy, license_file_name, source_dir, bundle_file_name, hashes, version ): archive_name = os.path.basename( bundle_file_name ) print( 'Bundling files to {}'.format( archive_name ) ) with tarfile.open( name=bundle_file_name, mode='w:bz2' ) as tar_file: tar_file.add( license_file_name, arcname='LICENSE.TXT' ) for item in files_to_copy: source_file_name = item.format( llvm_version = version ) target_file_name = source_file_name name = os.path.join( source_dir, source_file_name ) if not os.path.exists( name ): raise RuntimeError( 'File {} does not exist.'.format( name ) ) tar_file.add( name = name, arcname = target_file_name ) sys.stdout.write( 'Calculating checksum: ' ) with open( bundle_file_name, 'rb' ) as f: hashes[ archive_name ] = hashlib.sha256( f.read() ).hexdigest() print( hashes[ archive_name ] ) def UploadBundleToBintray( user_name, api_token, subject, os_name, version, bundle_file_name ): print( 'Uploading to bintray...' ) repo = bundle_file_name[ : bundle_file_name.find( '-' ) ] with open( bundle_file_name, 'rb' ) as bundle: request = requests.put( 'https://api.bintray.com/content/{subject}/{repo}/{file_path}'.format( subject = subject, repo = repo, file_path = os.path.basename( bundle_file_name ) ), data = bundle, auth = ( user_name, api_token ), headers = { 'X-Bintray-Package': repo, 'X-Bintray-Version': version, 'X-Bintray-Publish': '1', 'X-Bintray-Override': '1', } ) request.raise_for_status() def ParseArguments(): parser = argparse.ArgumentParser() parser.add_argument( 'version', action='store', help = 'The LLVM version' ) parser.add_argument( '--bt-user', action='store', help = 'Bintray user name. Defaults to environment ' 'variable: YCMD_BINTRAY_USERNAME' ) parser.add_argument( '--bt-subject', action='store', help = 'Bintray subject. Defaults to bt-user. For prod, ' 'use "ycm-core"' ) parser.add_argument( '--bt-token', action='store', help = 'Bintray api token. Defaults to environment ' 'variable: YCMD_BINTRAY_API_TOKEN.' ) parser.add_argument( '--from-cache', action='store', help = 'Use the clang packages from this dir. Useful ' 'if releases.llvm.org is unreliable.' ) parser.add_argument( '--output-dir', action='store', help = 'For testing, directory to put bundles in.' ) parser.add_argument( '--no-upload', action='store_true', help = "For testing, just build the bundles; don't " "upload to bintray. Useful with --output-dir." ) parser.add_argument( '--keep-temp', action='store_true', help = "For testing, don't delete the temp dir" ) args = parser.parse_args() if not args.bt_user: if 'YCMD_BINTRAY_USERNAME' not in os.environ: raise RuntimeError( 'ERROR: Must specify either --bt-user or ' 'YCMD_BINTRAY_USERNAME in environment' ) args.bt_user = os.environ[ 'YCMD_BINTRAY_USERNAME' ] if not args.bt_token: if 'YCMD_BINTRAY_API_TOKEN' not in os.environ: raise RuntimeError( 'ERROR: Must specify either --bt-token or ' 'YCMD_BINTRAY_API_TOKEN in environment' ) args.bt_token = os.environ[ 'YCMD_BINTRAY_API_TOKEN' ] if not args.bt_subject: args.bt_subject = args.bt_user return args def PrepareBundleBuiltIn( extract_fun, cache_dir, llvm_package, download_url, temp_dir ): package_dir = None if cache_dir: archive = os.path.join( cache_dir, llvm_package ) print( 'Extracting cached {}'.format( llvm_package ) ) try: with open( archive, 'rb' ) as f: package_dir = extract_fun( f.read(), temp_dir ) except IOError: pass if not package_dir: compressed_data = Download( download_url ) if cache_dir: try: archive = os.path.join( cache_dir, llvm_package ) with open( archive, 'wb' ) as f: f.write( compressed_data ) except IOError as e: print( "Unable to write cache file: {}".format( e.message ) ) pass print( 'Extracting {}'.format( llvm_package ) ) package_dir = extract_fun( compressed_data, temp_dir ) return package_dir def PrepareBundleLZMA( cache_dir, llvm_package, download_url, temp_dir ): return PrepareBundleBuiltIn( ExtractLZMA, cache_dir, llvm_package, download_url, temp_dir ) def PrepareBundleNSIS( cache_dir, llvm_package, download_url, temp_dir ): archive = None if cache_dir: archive = os.path.join( cache_dir, llvm_package ) if os.path.exists( archive ): print( 'Extracting cached {}'.format( llvm_package ) ) else: archive = None if not archive: compressed_data = Download( download_url ) dest_dir = cache_dir if cache_dir else temp_dir archive = os.path.join( dest_dir, llvm_package ) with open( archive, 'wb' ) as f: f.write( compressed_data ) print( 'Extracting {}'.format( llvm_package ) ) return Extract7Z( llvm_package, archive, temp_dir ) def BundleAndUpload( args, temp_dir, output_dir, os_name, download_data, license_file_name, hashes ): llvm_package = download_data[ 'llvm_package' ].format( os_name = os_name, llvm_version = args.version ) download_url = download_data[ 'url' ].format( llvm_version = args.version, llvm_package = llvm_package ) temp_dir = os.path.join( temp_dir, os_name ) os.makedirs( temp_dir ) try: if download_data[ 'format' ] == 'lzma': package_dir = PrepareBundleLZMA( args.from_cache, llvm_package, download_url, temp_dir ) elif download_data[ 'format' ] == 'nsis': package_dir = PrepareBundleNSIS( args.from_cache, llvm_package, download_url, temp_dir ) else: raise AssertionError( 'Format not yet implemented: {}'.format( download_data[ 'format' ] ) ) except requests.exceptions.HTTPError as error: if error.response.status_code != 404: raise print( 'Cannot download {}'.format( llvm_package ) ) return for binary in [ 'libclang', 'clangd' ]: package_name = binary + '_package' archive_name = download_data[ package_name ][ 'name' ].format( os_name = os_name, llvm_version = args.version ) archive_path = os.path.join( output_dir, archive_name ) MakeBundle( download_data[ package_name ][ 'files_to_copy' ], license_file_name, package_dir, archive_path, hashes, args.version ) if not args.no_upload: UploadBundleToBintray( args.bt_user, args.bt_token, args.bt_subject, os_name, args.version, archive_path ) def Overwrite( src, dest ): if os.path.exists( dest ): shutil.rmtree( dest ) shutil.copytree( src, dest ) def UpdateClangHeaders( args, temp_dir ): src_name = 'clang-{version}.src'.format( version = args.version ) archive_name = src_name + '.tar.xz' compressed_data = Download( 'https://github.com/llvm/llvm-project/releases/' 'download/llvmorg-{version}/' '{archive}'.format( version = args.version, archive = archive_name ) ) print( 'Extracting {}'.format( archive_name ) ) src = ExtractLZMA( compressed_data, temp_dir ) print( 'Updating Clang headers...' ) includes_dir = os.path.join( DIR_OF_THIRD_PARTY, 'clang', 'lib', 'clang', args.version, 'include' ) Overwrite( os.path.join( src, 'lib', 'Headers' ), includes_dir ) os.remove( os.path.join( includes_dir, 'CMakeLists.txt' ) ) Overwrite( os.path.join( src, 'include', 'clang-c' ), os.path.join( DIR_OF_THIS_SCRIPT, 'cpp', 'llvm', 'include', 'clang-c' ) ) def Main(): args = ParseArguments() output_dir = args.output_dir if args.output_dir else tempfile.mkdtemp() try: hashes = {} with TemporaryDirectory( args.keep_temp ) as temp_dir: license_file_name = DownloadClangLicense( args.version, temp_dir ) for os_name, download_data in LLVM_DOWNLOAD_DATA.items(): BundleAndUpload( args, temp_dir, output_dir, os_name, download_data, license_file_name, hashes ) UpdateClangHeaders( args, temp_dir ) finally: if not args.output_dir: shutil.rmtree( output_dir ) for bundle_file_name, sha256 in hashes.items(): print( 'Checksum for {bundle_file_name}: {sha256}'.format( bundle_file_name = bundle_file_name, sha256 = sha256 ) ) if __name__ == "__main__": Main() ycmd-0+20201028+git1d415c5+ds/update_omnisharp.py000077500000000000000000000101721374632460100210570ustar00rootroot00000000000000#!/usr/bin/env python3 import argparse import contextlib from os import ( listdir, mkdir ) import os.path as p import shutil import sys import tempfile import hashlib DIR_OF_THIS_SCRIPT = p.dirname( p.abspath( __file__ ) ) DIR_OF_THIRD_PARTY = p.join( DIR_OF_THIS_SCRIPT, 'third_party' ) def AddRequestDependencies(): request_dep_root = p.abspath( p.join( DIR_OF_THIRD_PARTY, 'requests_deps' ) ) for path in listdir( request_dep_root ): sys.path.insert( 0, p.join( request_dep_root, path ) ) sys.path.insert( 0, p.abspath( p.join( DIR_OF_THIRD_PARTY, 'requests_deps', 'urllib3', 'src' ) ) ) AddRequestDependencies() import requests URL_FORMAT = { 'release': ( "https://github.com/OmniSharp/omnisharp-roslyn/" "releases/download/{version}/{file_name}" ), 'ci': ( "https://roslynomnisharp.blob.core.windows.net/" "releases/{version}/{file_name}" ), } FILE_NAME = { 'win32': 'omnisharp.http-win-x86.zip', 'win64': 'omnisharp.http-win-x64.zip', 'macos': 'omnisharp.http-osx.tar.gz', 'linux32': 'omnisharp.http-linux-x86.tar.gz', 'linux64': 'omnisharp.http-linux-x64.tar.gz', } @contextlib.contextmanager def TemporaryDirectory(): temp_dir = tempfile.mkdtemp() try: yield temp_dir finally: shutil.rmtree( temp_dir ) def Download( url ): print( 'Downloading {}'.format( url.rsplit( '/', 1 )[ -1 ] ) ) request = requests.get( url, stream=True ) request.raise_for_status() content = request.content request.close() return content def ParseArguments(): parser = argparse.ArgumentParser() parser.add_argument( 'version', action='store', help = 'The Omnisharp version' ) parser.add_argument( '--cache-dir', action='store', help = 'For testing, directory to cache packages.' ) args = parser.parse_args() return args def GetDownloadUrl( version, file_name ): download_url_key = 'ci' if "-" in version else 'release' return URL_FORMAT[ download_url_key ].format( version = version, file_name = file_name ) def FetchAndHash( download_url, output_dir, file_name ): try: archive = p.join( output_dir, file_name ) if not p.exists( archive ): compressed_data = Download( download_url ) with open( archive, 'wb' ) as f: f.write( compressed_data ) except requests.exceptions.HTTPError as error: if error.response.status_code != 404: raise print( 'Cannot download {}'.format( file_name ) ) return with open( archive, 'rb' ) as f: return hashlib.sha256( f.read() ).hexdigest() def Process( output_dir, version ): result = {} for os_name, file_name in FILE_NAME.items(): download_url = GetDownloadUrl( version, file_name ) result[ os_name ] = { 'version': version, 'download_url': download_url, 'file_name': file_name, 'check_sum': FetchAndHash( download_url, output_dir, file_name ) } return result def MkDirIfMissing( dir ): try: mkdir( dir ) except OSError: pass def Main(): args = ParseArguments() version = args.version if args.cache_dir: MkDirIfMissing( args.cache_dir ) cache_dir = p.join( args.cache_dir, version ) MkDirIfMissing( cache_dir ) output = Process( cache_dir, version ) else: with TemporaryDirectory() as temp_dir: output = Process( temp_dir, version ) print( "Omnisharp configuration for {} is:".format( version ) ) for os_name, os_data in output.items(): print( " {}: {{".format( repr( os_name ) ) ) for key, value in os_data.items(): line = " {}: {},".format( repr( key ), repr( value ) ) if len( line ) > 80: line = " {}: ( {} ),".format( repr( key ), repr( value ) ) format_index = line.index( '(' ) + 2 while len( line ) > 80: print( line[ 0:78 ] + "'" ) line = ( ' ' * format_index ) + "'" + line[ 78: ] print( line ) print( " }," ) if __name__ == "__main__": Main() ycmd-0+20201028+git1d415c5+ds/update_unicode.py000077500000000000000000000532071374632460100205130ustar00rootroot00000000000000#!/usr/bin/env python3 # Copyright (C) 2020 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 . import pprint import sys from collections import defaultdict, OrderedDict from os import path as p from io import StringIO DIR_OF_THIS_SCRIPT = p.dirname( p.abspath( __file__ ) ) DIR_OF_THIRD_PARTY = p.join( DIR_OF_THIS_SCRIPT, 'third_party' ) sys.path[ 0:0 ] = [ p.join( DIR_OF_THIRD_PARTY, 'requests_deps', 'requests' ), p.join( DIR_OF_THIRD_PARTY, 'regex-build' ), 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 regex as re import requests DIR_OF_CPP_SOURCES = p.join( DIR_OF_THIS_SCRIPT, 'cpp', 'ycm' ) UNICODE_TABLE_TEMPLATE = ( """// This file was automatically generated with the update_unicode.py script // using version {unicode_version} of the Unicode Character Database. #include struct RawCodePointArray {{ std::array< char[{original_size}], {size} > original; std::array< char[{normal_size}], {size} > normal; std::array< char[{folded_case_size}], {size} > folded_case; std::array< char[{swapped_case_size}], {size} > swapped_case; std::array< bool, {size} > is_letter; std::array< bool, {size} > is_punctuation; std::array< bool, {size} > is_uppercase; std::array< uint8_t, {size} > break_property; std::array< uint8_t, {size} > combining_class; }}; static const RawCodePointArray code_points = {{ {code_points} }};""" ) UNICODE_VERSION_REGEX = re.compile( r'Version (?P\d+(?:\.\d+){2})' ) GRAPHEME_BREAK_PROPERTY_REGEX = re.compile( r'^(?P[A-F0-9.]+)\s+; (?P\w+) # .*$' ) GRAPHEME_BREAK_PROPERTY_TOTAL = re.compile( r'# Total code points: (?P\d+)' ) # See # https://www.unicode.org/reports/tr29/tr29-37.html#Grapheme_Cluster_Break_Property_Values GRAPHEME_BREAK_PROPERTY_MAP = { # "Other" is the term used in the Unicode data while "Any" is used in the # docs. '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" is used in the GraphemeBreakTest.txt file for # Extended_Pictographic. 'ExtPict' : 18, } SPECIAL_FOLDING_REGEX = re.compile( r'^(?P[A-F0-9]+); (?P.*); (?P.*); (?P<upper>.*); ' '(?:.*; )?# .*$' ) CASE_FOLDING_REGEX = re.compile( r'^(?P<code>[A-F0-9]+); (?P<status>[CFST]); (?P<mapping>[A-F0-9 ]+); # ' '(?P<name>.*)$' ) EMOJI_PROPERTY_REGEX = re.compile( r'^(?P<code>[A-F0-9.]+)\s*; (?P<property>[\w_]+)\s*# .*$' ) EMOJI_PROPERTY_TOTAL = re.compile( r'# Total elements: (?P<total>\d+)' ) HANGUL_BASE = 0xAC00 HANGUL_L_BASE = 0x1100 HANGUL_V_BASE = 0x1161 HANGUL_T_BASE = 0x11A7 HANGUL_L_COUNT = 19 HANGUL_V_COUNT = 21 HANGUL_T_COUNT = 28 HANGUL_VT_COUNT = HANGUL_V_COUNT * HANGUL_T_COUNT HANGUL_LVT_COUNT = HANGUL_L_COUNT * HANGUL_VT_COUNT def Download( url ): request = requests.get( url ) request.raise_for_status() return request.text.splitlines() # Encode a Unicode code point in UTF-8 binary form. def UnicodeToBinaryUtf8( code_point ): binary = bin( int( code_point, 16 ) )[ 2: ] binary_length = len( binary ) if binary_length <= 7: return binary.zfill( 8 ) if binary_length <= 11: binary = binary.zfill( 11 ) return '110' + binary[ :5 ] + '10' + binary[ 5: ] if binary_length <= 16: binary = binary.zfill( 16 ) return '1110' + binary[ :4 ] + '10' + binary[ 4:10 ] + '10' + binary[ 10: ] if binary_length <= 21: binary = binary.zfill( 21 ) return ( '11110' + binary[ :3 ] + '10' + binary[ 3:9 ] + '10' + binary[ 9:15 ] + '10' + binary[ 15: ] ) raise RuntimeError( 'Cannot encode a Unicode code point to UTF-8 on more ' 'than 4 bytes.' ) # Convert a Unicode code point into a UTF-8 string that can be included in a C++ # file when surrounded with double quotes. def UnicodeToUtf8( code_point ): utf8_binary = UnicodeToBinaryUtf8( code_point ) utf8_binary_length = ( len( utf8_binary ) + 1 ) // 4 # Strip the L from the hexa value that Python adds for big numbers. utf8_hex = hex( int( utf8_binary, 2 ) )[ 2: ].rstrip( 'L' ) utf8_hex = utf8_hex.zfill( utf8_binary_length ) # GCC and Clang raises the warning "null character(s) preserved in string # literal" if we don't escape the null character. if utf8_hex == '00': return '\\x00' # Escape newline characters. if utf8_hex == '0a': return '\\n' if utf8_hex == '0d': return '\\r' # Escape the " character. if utf8_hex == '22': return '\\"' # Escape the \ character. if utf8_hex == '5c': return '\\\\' # MSVC fails to compile with error "unexpected end-of-file found" if we don't # escape that character. if utf8_hex == '1a': return '\\x1a' try: return bytearray.fromhex( utf8_hex ).decode( 'utf8' ) except UnicodeDecodeError: # If Python fails to encode the character, we insert it with the \x # notation. return pprint.pformat( bytearray.fromhex( utf8_hex ) )[ 12: -2 ] def JoinUnicodeToUtf8( code_points ): return ''.join( [ UnicodeToUtf8( code_point.strip() ) for code_point in code_points ] ) def DecToHex( code_point ): return hex( code_point )[ 2: ].zfill( 4 ).upper() def GetUnicodeVersion(): readme = Download( 'https://www.unicode.org/Public/UCD/latest/ReadMe.txt' ) for line in readme: match = UNICODE_VERSION_REGEX.search( line ) if match: return match.group( 'version' ) raise RuntimeError( 'Cannot find the version of the Unicode Standard.' ) # See https://www.unicode.org/reports/tr44/tr44-26.html#UnicodeData.txt def GetUnicodeData(): data = Download( 'https://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt' ) unicode_data = OrderedDict() previous_value = None for line in data: ( value, name, general_category, ccc, _, decomposition, _, _, _, _, _, _, uppercase, lowercase, _ ) = line.split( ';' ) # Some pairs of lines correspond to a range. Add all code points in that # range. if name.endswith( 'First>' ): previous_value = value continue if name.endswith( 'Last>' ): range_start = int( previous_value, 16 ) range_end = int( value, 16 ) + 1 for dec_value in range( range_start, range_end ): unicode_data[ DecToHex( dec_value ) ] = { 'name': name, 'general_category': general_category, 'ccc': ccc, 'decomposition': decomposition, 'uppercase': uppercase, 'lowercase': lowercase } continue unicode_data[ value ] = { 'name': name, 'general_category': general_category, 'ccc': ccc, 'decomposition': decomposition, 'uppercase': uppercase, 'lowercase': lowercase } return unicode_data # See # https://www.unicode.org/reports/tr44/tr44-26.html#GraphemeBreakProperty.txt def GetGraphemeBreakProperty(): data = Download( 'https://www.unicode.org/' 'Public/UCD/latest/ucd/auxiliary/GraphemeBreakProperty.txt' ) nb_code_points = 0 break_data = {} for line in data: # Check if the number of code points collected for each property is the same # as the number indicated in the document. match = GRAPHEME_BREAK_PROPERTY_TOTAL.search( line ) if match: total = int( match.group( 'total' ) ) if nb_code_points != total: raise RuntimeError( 'Expected {} code points. Got {}.'.format( total, nb_code_points ) ) nb_code_points = 0 match = GRAPHEME_BREAK_PROPERTY_REGEX.search( line ) if not match: continue value = match.group( 'value' ) prop = match.group( 'property' ) if '..' not in value: break_data[ value ] = prop nb_code_points += 1 continue range_start, range_end = value.split( '..' ) range_start = int( range_start, 16 ) range_end = int( range_end, 16 ) + 1 for value in range( range_start, range_end ): break_data[ DecToHex( value ) ] = prop nb_code_points += 1 return break_data # See https://www.unicode.org/reports/tr44/tr44-26.html#SpecialCasing.txt def GetSpecialFolding(): data = Download( 'https://www.unicode.org/Public/UCD/latest/ucd/SpecialCasing.txt' ) folding_data = {} for line in data: # Ignore all context-sensitive and language-sensitive mappings. if line.startswith( '# Conditional Mappings' ): break match = SPECIAL_FOLDING_REGEX.search( line ) if not match: continue code = match.group( 'code' ) folding_data[ code ] = { 'lowercase': match.group( 'lower' ), 'titlecase': match.group( 'title' ), 'uppercase': match.group( 'upper' ) } return folding_data # See https://www.unicode.org/reports/tr44/tr44-26.html#CaseFolding.txt def GetCaseFolding(): data = Download( 'https://www.unicode.org/Public/UCD/latest/ucd/CaseFolding.txt' ) folding_data = {} for line in data: match = CASE_FOLDING_REGEX.search( line ) if not match: continue code = match.group( 'code' ) status = match.group( 'status' ) mapping = match.group( 'mapping' ) # Support full case folding. if status in [ 'C', 'F' ]: folding_data[ code ] = mapping return folding_data def GetEmojiData(): data = Download( 'https://unicode.org/Public/emoji/latest/emoji-data.txt' ) nb_code_points = 0 emoji_data = defaultdict( list ) for line in data: # Check if the number of code points collected for the property is the same # as the number indicated in the document. match = EMOJI_PROPERTY_TOTAL.search( line ) if match: total = int( match.group( 'total' ) ) if nb_code_points != total: raise RuntimeError( 'Expected {} code points. Got {}.'.format( total, nb_code_points ) ) nb_code_points = 0 match = EMOJI_PROPERTY_REGEX.search( line ) if not match: continue code = match.group( 'code' ) prop = match.group( 'property' ) if '..' not in code: emoji_data[ code ].append( prop ) nb_code_points += 1 continue range_start, range_end = code.split( '..' ) range_start = int( range_start, 16 ) range_end = int( range_end, 16 ) + 1 for value in range( range_start, range_end ): emoji_data[ DecToHex( value ) ].append( prop ) nb_code_points += 1 return emoji_data # Decompose a hangul syllable using the algorithm described in # https://www.unicode.org/versions/Unicode13.0.0/ch03.pdf#G61399 def DecomposeHangul( code_point ): index = int( code_point, 16 ) - HANGUL_BASE if index < 0 or index >= HANGUL_LVT_COUNT: return None hangul_l = HANGUL_L_BASE + index // HANGUL_VT_COUNT hangul_v = HANGUL_V_BASE + ( index % HANGUL_VT_COUNT ) // HANGUL_T_COUNT hangul_t = HANGUL_T_BASE + index % HANGUL_T_COUNT code_points = [ DecToHex( hangul_l ), DecToHex( hangul_v ) ] if hangul_t != HANGUL_T_BASE: code_points.append( DecToHex( hangul_t ) ) return code_points # Recursively decompose a Unicode code point into a list of code points # according to canonical decomposition. # See https://www.unicode.org/versions/Unicode13.0.0/ch03.pdf#G733 def Decompose( code_point, unicode_data ): code_points = DecomposeHangul( code_point ) if code_points: return code_points raw_decomposition = unicode_data[ code_point ][ 'decomposition' ] if not raw_decomposition: return [ code_point ] # Ignore compatibility decomposition. if raw_decomposition.startswith( '<' ): return [ code_point ] decomposition = [] for code_point in raw_decomposition.split( ' ' ): decomposition.extend( Decompose( code_point, unicode_data ) ) return decomposition def Lowercase( code_points, unicode_data, special_folding ): lower_code_points = [] for code_point in code_points: lowercase = special_folding.get( code_point, unicode_data[ code_point ] )[ 'lowercase' ] lower_code_point = lowercase if lowercase else code_point lower_code_points.extend( lower_code_point.split( ' ' ) ) return lower_code_points def Uppercase( code_points, unicode_data, special_folding ): upper_code_points = [] for code_point in code_points: uppercase = special_folding.get( code_point, unicode_data[ code_point ] )[ 'uppercase' ] upper_code_point = uppercase if uppercase else code_point upper_code_points.extend( upper_code_point.split( ' ' ) ) return upper_code_points def Foldcase( code_points, unicode_data, case_folding ): decomposed_code_points = [] for code_point in code_points: folded_code_points = case_folding.get( code_point, code_point ).split( ' ' ) for folded_code_point in folded_code_points: decomposed_code_point = Decompose( folded_code_point, unicode_data ) decomposed_code_points.extend( decomposed_code_point ) return decomposed_code_points def GetCodePoints(): code_points = [] unicode_data = GetUnicodeData() break_data = GetGraphemeBreakProperty() special_folding = GetSpecialFolding() case_folding = GetCaseFolding() emoji_data = GetEmojiData() for key, value in unicode_data.items(): general_category = value[ 'general_category' ] normal_code_points = Decompose( key, unicode_data ) folded_code_points = Foldcase( normal_code_points, unicode_data, case_folding ) lower_code_points = Lowercase( normal_code_points, unicode_data, special_folding ) upper_code_points = Uppercase( normal_code_points, unicode_data, special_folding ) code_point = UnicodeToUtf8( key ) normal_code_point = JoinUnicodeToUtf8( normal_code_points ) folded_code_point = JoinUnicodeToUtf8( folded_code_points ) lower_code_point = JoinUnicodeToUtf8( lower_code_points ) upper_code_point = JoinUnicodeToUtf8( upper_code_points ) is_uppercase = normal_code_point != lower_code_point swapped_code_point = lower_code_point if is_uppercase else upper_code_point is_letter = general_category.startswith( 'L' ) is_punctuation = general_category.startswith( 'P' ) break_property = break_data.get( key, 'Other' ) emoji_property = emoji_data.get( key, [] ) if 'Extended_Pictographic' in emoji_property: if break_property == 'Other': break_property = 'ExtPict' else: raise RuntimeError( 'Cannot handle Extended_Pictographic combined with ' '{} property'.format( break_property ) ) break_property = GRAPHEME_BREAK_PROPERTY_MAP[ break_property ] combining_class = int( value[ 'ccc' ] ) # See https://unicode.org/reports/tr44/tr44-26.html#General_Category_Values # for the list of categories. if ( code_point != normal_code_point or code_point != folded_code_point or code_point != swapped_code_point or is_letter or is_punctuation or is_uppercase or break_property or combining_class ): code_points.append( { 'original': code_point, 'normal': normal_code_point, 'folded_case': folded_code_point, 'swapped_case': swapped_code_point, 'is_letter': is_letter, 'is_punctuation': is_punctuation, 'is_uppercase': is_uppercase, 'break_property': break_property, 'combining_class': combining_class } ) return code_points def CppChar( character ): return '"{}"'.format( character ) def CppBool( statement ): # We use 1/0 for C++ booleans instead of true/false to reduce the size of the # generated table. if statement: return '1' return '0' # If a codepoint is written in hex (\x61) instead of a literal (a) # then the backslash needs to be escaped in order for the correct # string end up in the generated C++ file. # To calculate the actual length for these, we can't count bytes. # Instead, we split on '\\x', leaving only the an array of hex values. # \\x61 would end up as [ '', '61' ] def CppLength( utf8_code_point ): nb_utf8_hex = len( utf8_code_point.split( '\\x' )[ 1: ] ) if nb_utf8_hex > 0: # +1 for NULL terminator return nb_utf8_hex + 1 return len( bytearray( utf8_code_point, encoding = 'utf8' ) ) + 1 def GenerateUnicodeTable( header_path, code_points ): unicode_version = GetUnicodeVersion() size = len( code_points ) table = { 'original': { 'output': StringIO(), 'size': 0, 'converter': CppChar }, 'normal': { 'output': StringIO(), 'size': 0, 'converter': CppChar }, 'folded_case': { 'output': StringIO(), 'size': 0, 'converter': CppChar }, 'swapped_case': { 'output': StringIO(), 'size': 0, 'converter': CppChar }, 'is_letter': { 'output': StringIO(), 'converter': CppBool }, 'is_punctuation': { 'output': StringIO(), 'converter': CppBool }, 'is_uppercase': { 'output': StringIO(), 'converter': CppBool }, 'break_property': { 'output': StringIO(), 'converter': str }, 'combining_class': { 'output': StringIO(), 'converter': str }, } for d in table.values(): d[ 'output' ].write( '{{' ) for code_point in code_points: for t, d in table.items(): cp = code_point[ t ] d[ 'output' ].write( d[ 'converter' ]( cp ) ) d[ 'output' ].write( ',' ) if d[ 'converter' ] == CppChar: d[ 'size' ] = max( CppLength( cp ), d[ 'size' ] ) for t, d in table.items(): if t == 'combining_class': d[ 'output' ] = d[ 'output' ].getvalue().rstrip( ',' ) + '}}' else: d[ 'output' ] = d[ 'output' ].getvalue().rstrip( ',' ) + '}},' code_points = '\n'.join( [ table[ 'original' ][ 'output' ], table[ 'normal' ][ 'output' ], table[ 'folded_case' ][ 'output' ], table[ 'swapped_case' ][ 'output' ], table[ 'is_letter' ][ 'output' ], table[ 'is_punctuation' ][ 'output' ], table[ 'is_uppercase' ][ 'output' ], table[ 'break_property' ][ 'output' ], table[ 'combining_class' ][ 'output' ] ] ) contents = UNICODE_TABLE_TEMPLATE.format( unicode_version = unicode_version, size = size, original_size = table[ 'original' ][ 'size' ], normal_size = table[ 'normal' ][ 'size' ], folded_case_size = table[ 'folded_case' ][ 'size' ], swapped_case_size = table[ 'swapped_case' ][ 'size' ], code_points = code_points ) with open( header_path, 'w', newline = '\n', encoding='utf8' ) as header_file: header_file.write( contents ) def GenerateNormalizationTestCases( output_file ): test_contents = Download( 'https://unicode.org/Public/UCD/latest/ucd/NormalizationTest.txt' ) hex_codepoint = '(?:[A-F0-9]{4,} ?)+' pattern = f'(?:{ hex_codepoint };){{5}}' pattern = re.compile( pattern ) res = [] for line in test_contents: m = pattern.match( line ) if m: captures = m[ 0 ].split( ';' ) res.append( '{"' + JoinUnicodeToUtf8( captures[ 0 ].split() ) + '","' + JoinUnicodeToUtf8( captures[ 1 ].split() ) + '","' + JoinUnicodeToUtf8( captures[ 2 ].split() ) + '","' + JoinUnicodeToUtf8( captures[ 3 ].split() ) + '","' + JoinUnicodeToUtf8( captures[ 4 ].split() ) + '"},\n' ) res[ -1 ] = res[ -1 ].rstrip( ',\n' ) with open( output_file, 'w' ) as f: f.writelines( res ) def GenerateGraphemeBreakTestCases( output_file ): test_contents = Download( 'https://www.unicode.org/' 'Public/UCD/latest/ucd/auxiliary/GraphemeBreakTest.txt' ) res = [] for line in test_contents: if line.startswith( '÷' ): data = line.split( '#' )[ 0 ].rstrip().strip( '÷' ).strip() all_data = data.replace( ' × ', ' ÷ ' ).replace( '÷ ', '' ) all_data = JoinUnicodeToUtf8( all_data.split() ) split_data = data.replace( '× ', '' ).split( ' ÷ ' ) for i, e in enumerate( split_data ): split_data[ i ] = JoinUnicodeToUtf8( e.split() ) res.append( '{"' + all_data + '",{' + ''.join( [ '"' + x + '",' for x in split_data ] ).rstrip( ',' ) + '}},\n' ) res[ -1 ] = res[ -1 ].rstrip( ',\n' ) with open( output_file, 'w' ) as f: f.writelines( res ) def Main(): code_points = GetCodePoints() table_path = p.join( DIR_OF_CPP_SOURCES, 'UnicodeTable.inc' ) GenerateUnicodeTable( table_path, code_points ) cpp_tests_path = p.join( DIR_OF_CPP_SOURCES, 'tests' ) normalization_cases_path = p.join( cpp_tests_path, 'NormalizationCases.inc' ) GenerateNormalizationTestCases( normalization_cases_path ) grapheme_break_cases_path = p.join( cpp_tests_path, 'GraphemeBreakCases.inc' ) GenerateGraphemeBreakTestCases( grapheme_break_cases_path ) if __name__ == '__main__': Main() �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/valgrind.suppressions�������������������������������������������������0000664�0000000�0000000�00000003172�13746324601�0021447�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Invalid conditional jumps { https://github.com/python/cpython/blob/v3.9.0/Objects/unicodeobject.c#L3443 Memcheck:Cond fun:PyUnicode_Decode fun:PyUnicode_Decode } { https://github.com/python/cpython/blob/v3.9.0/Objects/unicodeobject.c#L3731 Memcheck:Cond fun:PyUnicode_AsEncodedString fun:PyUnicode_AsEncodedString } # Definite leaks detected { Pybind11 leak to prevent crashes due to CPython bug. Fixed in 3.9.1 Memcheck:Leak match-leak-kinds: definite fun:_Znwm fun:_ZN8pybind1112cpp_function18initialize_genericEPNS_6detail15function_recordEPKcPKPKSt9type_infom } { <insert_a_suppression_name_here> Memcheck:Leak match-leak-kinds: definite fun:malloc fun:_dl_map_object_deps fun:dl_open_worker fun:_dl_catch_exception fun:_dl_open fun:dlopen_doit fun:_dl_catch_exception fun:_dl_catch_error fun:_dlerror_run fun:dlopen@@GLIBC_2.2.5 fun:_PyImport_FindSharedFuncptr fun:_PyImport_LoadDynamicModuleWithSpec } { https://bugs.llvm.org/show_bug.cgi?id=47832 Memcheck:Leak match-leak-kinds: definite fun:_ZnwmRKSt9nothrow_t fun:_ZN4llvm20WritableMemoryBuffer21getNewUninitMemBufferEmRKNS_5TwineE fun:_ZN4llvm12MemoryBuffer16getMemBufferCopyENS_9StringRefERKNS_5TwineE fun:_ZL31clang_parseTranslationUnit_ImplPvPKcPKS1_iN4llvm8ArrayRefI13CXUnsavedFileEEjPP21CXTranslationUnitImpl fun:_ZN4llvm12function_refIFvvEE11callback_fnIZ35clang_parseTranslationUnit2FullArgvEUlvE_EEvl fun:_ZN4llvm20CrashRecoveryContext9RunSafelyENS_12function_refIFvvEEE fun:_ZL26RunSafelyOnThread_DispatchPv fun:_ZL14threadFuncSyncPv fun:start_thread fun:clone } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/�����������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0016073�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/__init__.py������������������������������������������������������0000664�0000000�0000000�00000000000�13746324601�0020172�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/__main__.py������������������������������������������������������0000664�0000000�0000000�00000014655�13746324601�0020200�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2013-2020 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 <http://www.gnu.org/licenses/>. import sys import os if sys.version_info[ 0 ] < 3: sys.exit( 8 ) sys.path.insert( 0, os.path.dirname( os.path.abspath( __file__ ) ) ) from server_utils import SetUpPythonPath SetUpPythonPath() import atexit import sys import logging import json import argparse import signal import os import base64 from ycmd import extra_conf_store, user_options_store, utils from ycmd.hmac_plugin import HmacPlugin from ycmd.utils import ( ImportAndCheckCore, OpenForStdHandle, ReadFile, ToBytes ) from ycmd.wsgi_server import StoppableWSGIServer def YcmCoreSanityCheck(): if 'ycm_core' in sys.modules: raise RuntimeError( 'ycm_core already imported, ycmd has a bug!' ) # We manually call sys.exit() on SIGTERM and SIGINT so that atexit handlers are # properly executed. def SetUpSignalHandler(): def SignalHandler( signum, frame ): sys.exit() for sig in [ signal.SIGTERM, signal.SIGINT ]: signal.signal( sig, SignalHandler ) def CleanUpLogfiles( stdout, stderr, keep_logfiles ): # We reset stderr & stdout, just in case something tries to use them if stderr: tmp = sys.stderr sys.stderr = sys.__stderr__ tmp.close() if stdout: tmp = sys.stdout sys.stdout = sys.__stdout__ tmp.close() if not keep_logfiles: if stderr: utils.RemoveIfExists( stderr ) if stdout: utils.RemoveIfExists( stdout ) def PossiblyDetachFromTerminal(): # If not on windows, detach from controlling terminal to prevent # SIGINT from killing us. if not utils.OnWindows(): try: os.setsid() # setsid() can fail if the user started ycmd directly from a shell. except OSError: pass def ParseArguments(): parser = argparse.ArgumentParser() # Not using 'localhost' on purpose; see #987 and #1130 parser.add_argument( '--host', type = str, default = '127.0.0.1', help = 'server hostname' ) # Default of 0 will make the OS pick a free port for us parser.add_argument( '--port', type = int, default = 0, help = 'server port' ) parser.add_argument( '--log', type = str, default = 'info', help = 'log level, one of ' '[debug|info|warning|error|critical]' ) parser.add_argument( '--idle_suicide_seconds', type = int, default = 0, help = 'num idle seconds before server shuts down' ) parser.add_argument( '--check_interval_seconds', type = int, default = 600, help = 'interval in seconds to check server ' 'inactivity and keep subservers alive' ) parser.add_argument( '--options_file', type = str, required = True, help = 'file with user options, in JSON format' ) parser.add_argument( '--stdout', type = str, default = None, help = 'optional file to use for stdout' ) parser.add_argument( '--stderr', type = str, default = None, help = 'optional file to use for stderr' ) parser.add_argument( '--keep_logfiles', action = 'store_true', default = None, help = 'retain logfiles after the server exits' ) return parser.parse_args() def SetupLogging( log_level ): numeric_level = getattr( logging, log_level.upper(), None ) if not isinstance( numeric_level, int ): raise ValueError( 'Invalid log level: %s' % log_level ) # Has to be called before any call to logging.getLogger() logging.basicConfig( format = '%(asctime)s - %(levelname)s - %(message)s', level = numeric_level ) def SetupOptions( options_file ): options = user_options_store.DefaultOptions() user_options = json.loads( ReadFile( options_file ) ) options.update( user_options ) utils.RemoveIfExists( options_file ) hmac_secret = ToBytes( base64.b64decode( options[ 'hmac_secret' ] ) ) del options[ 'hmac_secret' ] user_options_store.SetAll( options ) return options, hmac_secret def CloseStdin(): sys.stdin.close() os.close( 0 ) def Main(): args = ParseArguments() if args.stdout is not None: sys.stdout = OpenForStdHandle( args.stdout ) if args.stderr is not None: sys.stderr = OpenForStdHandle( args.stderr ) SetupLogging( args.log ) options, hmac_secret = SetupOptions( args.options_file ) # This ensures that ycm_core is not loaded before extra conf # preload was run. YcmCoreSanityCheck() extra_conf_store.CallGlobalExtraConfYcmCorePreloadIfExists() code = ImportAndCheckCore() if code: sys.exit( code ) PossiblyDetachFromTerminal() # These can't be top-level imports because they transitively import # ycm_core which we want to be imported ONLY after extra conf # preload has executed. from ycmd import handlers from ycmd.watchdog_plugin import WatchdogPlugin handlers.UpdateUserOptions( options ) handlers.SetHmacSecret( hmac_secret ) handlers.KeepSubserversAlive( args.check_interval_seconds ) SetUpSignalHandler() # Functions registered by the atexit module are called at program termination # in last in, first out order. atexit.register( CleanUpLogfiles, args.stdout, args.stderr, args.keep_logfiles ) atexit.register( handlers.ServerCleanup ) handlers.app.install( WatchdogPlugin( args.idle_suicide_seconds, args.check_interval_seconds ) ) handlers.app.install( HmacPlugin( hmac_secret ) ) CloseStdin() handlers.wsgi_server = StoppableWSGIServer( handlers.app, host = args.host, port = args.port, threads = 30 ) handlers.wsgi_server.Run() if __name__ == "__main__": Main() �����������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0020250�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/__init__.py�������������������������������������������0000664�0000000�0000000�00000000000�13746324601�0022347�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/all/��������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0021020�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/all/__init__.py���������������������������������������0000664�0000000�0000000�00000000000�13746324601�0023117�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/all/identifier_completer.py���������������������������0000664�0000000�0000000�00000020453�13746324601�0025572�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2011-2020 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 <http://www.gnu.org/licenses/>. import os from collections import defaultdict from ycmd.completers.general_completer import GeneralCompleter from ycmd import identifier_utils from ycmd.utils import ImportCore, LOGGER, SplitLines from ycmd import responses ycm_core = ImportCore() SYNTAX_FILENAME = 'YCM_PLACEHOLDER_FOR_SYNTAX' class IdentifierCompleter( GeneralCompleter ): def __init__( self, user_options ): super().__init__( user_options ) self._completer = ycm_core.IdentifierCompleter() self._tags_file_last_mtime = defaultdict( int ) self._max_candidates = user_options[ 'max_num_identifier_candidates' ] def ShouldUseNow( self, request_data ): return self.QueryLengthAboveMinThreshold( request_data ) def ComputeCandidates( self, request_data ): if not self.ShouldUseNow( request_data ): return [] completions = self._completer.CandidatesForQueryAndType( _SanitizeQuery( request_data[ 'query' ] ), request_data[ 'first_filetype' ], self._max_candidates ) completions = _RemoveSmallCandidates( completions, self.user_options[ 'min_num_identifier_candidate_chars' ] ) def ConvertCompletionData( x ): return responses.BuildCompletionData( insertion_text = x, extra_menu_info='[ID]' ) return [ ConvertCompletionData( x ) for x in completions ] def _AddIdentifier( self, identifier, request_data ): filetype = request_data[ 'first_filetype' ] filepath = request_data[ 'filepath' ] if not filetype or not filepath or not identifier: return vector = ycm_core.StringVector() vector.append( identifier ) LOGGER.info( 'Adding ONE buffer identifier for file: %s', filepath ) self._completer.AddIdentifiersToDatabase( vector, filetype, filepath ) def _AddPreviousIdentifier( self, request_data ): self._AddIdentifier( _PreviousIdentifier( self.user_options[ 'min_num_of_chars_for_completion' ], self.user_options[ 'collect_identifiers_from_comments_and_strings' ], request_data ), request_data ) def _AddIdentifierUnderCursor( self, request_data ): self._AddIdentifier( _GetCursorIdentifier( self.user_options[ 'collect_identifiers_from_comments_and_strings' ], request_data ), request_data ) def _AddBufferIdentifiers( self, request_data ): filetype = request_data[ 'first_filetype' ] filepath = request_data[ 'filepath' ] if not filetype or not filepath: return collect_from_comments_and_strings = bool( self.user_options[ 'collect_identifiers_from_comments_and_strings' ] ) text = request_data[ 'file_data' ][ filepath ][ 'contents' ] LOGGER.info( 'Adding buffer identifiers for file: %s', filepath ) self._completer.ClearForFileAndAddIdentifiersToDatabase( _IdentifiersFromBuffer( text, filetype, collect_from_comments_and_strings ), filetype, filepath ) def _FilterUnchangedTagFiles( self, tag_files ): for tag_file in tag_files: try: current_mtime = os.path.getmtime( tag_file ) except Exception: LOGGER.exception( 'Error while getting %s last modification time', tag_file ) continue last_mtime = self._tags_file_last_mtime[ tag_file ] # We don't want to repeatedly process the same file over and over; we only # process if it's changed since the last time we looked at it if current_mtime <= last_mtime: continue self._tags_file_last_mtime[ tag_file ] = current_mtime yield tag_file def _AddIdentifiersFromTagFiles( self, tag_files ): absolute_paths_to_tag_files = ycm_core.StringVector() for tag_file in self._FilterUnchangedTagFiles( tag_files ): absolute_paths_to_tag_files.append( tag_file ) if not absolute_paths_to_tag_files: return self._completer.AddIdentifiersToDatabaseFromTagFiles( absolute_paths_to_tag_files ) def _AddIdentifiersFromSyntax( self, keyword_list, filetype ): keyword_vector = ycm_core.StringVector() for keyword in keyword_list: keyword_vector.append( keyword ) filepath = SYNTAX_FILENAME + filetype self._completer.AddIdentifiersToDatabase( keyword_vector, filetype, filepath ) def OnFileReadyToParse( self, request_data ): self._AddBufferIdentifiers( request_data ) if 'tag_files' in request_data: self._AddIdentifiersFromTagFiles( request_data[ 'tag_files' ] ) if 'syntax_keywords' in request_data: self._AddIdentifiersFromSyntax( request_data[ 'syntax_keywords' ], request_data[ 'first_filetype' ] ) def OnInsertLeave( self, request_data ): self._AddIdentifierUnderCursor( request_data ) def OnCurrentIdentifierFinished( self, request_data ): self._AddPreviousIdentifier( request_data ) # This looks for the previous identifier and returns it; this might mean looking # at last identifier on the previous line if a new line has just been created. def _PreviousIdentifier( min_num_candidate_size_chars, collect_from_comments_and_strings, request_data ): def PreviousIdentifierOnLine( line, column, filetype ): nearest_ident = '' for match in identifier_utils.IdentifierRegexForFiletype( filetype ).finditer( line ): if match.end() <= column: nearest_ident = match.group() return nearest_ident line_num = request_data[ 'line_num' ] - 1 column_num = request_data[ 'column_codepoint' ] - 1 filepath = request_data[ 'filepath' ] filetype = request_data[ 'first_filetype' ] contents = request_data[ 'file_data' ][ filepath ][ 'contents' ] if not collect_from_comments_and_strings: contents = identifier_utils.RemoveIdentifierFreeText( contents, filetype ) contents_per_line = SplitLines( contents ) ident = PreviousIdentifierOnLine( contents_per_line[ line_num ], column_num, filetype ) if ident: if len( ident ) < min_num_candidate_size_chars: return '' return ident line_num = line_num - 1 if line_num < 0: return '' prev_line = contents_per_line[ line_num ] ident = PreviousIdentifierOnLine( prev_line, len( prev_line ), filetype ) if len( ident ) < min_num_candidate_size_chars: return '' return ident def _RemoveSmallCandidates( candidates, min_num_candidate_size_chars ): if min_num_candidate_size_chars == 0: return candidates return [ x for x in candidates if len( x ) >= min_num_candidate_size_chars ] def _GetCursorIdentifier( collect_from_comments_and_strings, request_data ): filepath = request_data[ 'filepath' ] contents = request_data[ 'file_data' ][ filepath ][ 'contents' ] filetype = request_data[ 'first_filetype' ] if not collect_from_comments_and_strings: contents = identifier_utils.RemoveIdentifierFreeText( contents, filetype ) contents_per_line = SplitLines( contents ) line = contents_per_line[ request_data[ 'line_num' ] - 1 ] return identifier_utils.IdentifierAtIndex( line, request_data[ 'column_codepoint' ] - 1, filetype ) def _IdentifiersFromBuffer( text, filetype, collect_from_comments_and_strings ): if not collect_from_comments_and_strings: text = identifier_utils.RemoveIdentifierFreeText( text, filetype ) idents = identifier_utils.ExtractIdentifiersFromText( text, filetype ) vector = ycm_core.StringVector() for ident in idents: vector.append( ident ) return vector def _SanitizeQuery( query ): return query.strip() ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/c/����������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0020472�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/c/__init__.py�����������������������������������������0000664�0000000�0000000�00000000000�13746324601�0022571�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/c/hook.py���������������������������������������������0000664�0000000�0000000�00000002223�13746324601�0022003�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from ycmd.completers.cpp.clang_completer import ClangCompleter from ycmd.completers.cpp.clangd_completer import ( ShouldEnableClangdCompleter, ClangdCompleter ) from ycmd.utils import ImportCore ycm_core = ImportCore() def GetCompleter( user_options ): if ShouldEnableClangdCompleter( user_options ): return ClangdCompleter( user_options ) if ycm_core.HasClangSupport(): return ClangCompleter( user_options ) return None �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/completer.py������������������������������������������0000664�0000000�0000000�00000053142�13746324601�0022621�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2011-2020 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 <http://www.gnu.org/licenses/>. import abc import threading from ycmd import extra_conf_store from ycmd.completers import completer_utils from ycmd.responses import NoDiagnosticSupport, SignatureHelpAvailalability from ycmd.utils import LOGGER NO_USER_COMMANDS = 'This completer does not define any commands.' # Number of seconds to block before returning True in PollForMessages MESSAGE_POLL_TIMEOUT = 10 class CompletionsChanged( Exception ): pass # pragma: no cover class Completer( metaclass = abc.ABCMeta ): """A base class for all Completers in YCM. Here's several important things you need to know if you're writing a custom Completer. The following are functions that the Vim part of YCM will be calling on your Completer: *Important note about unicode and byte offsets* Useful background: http://utf8everywhere.org Internally, all Python strings are unicode string objects, unless otherwise converted to 'bytes' using ToBytes. In particular, the line_value and file_data.contents entries in the request_data are unicode strings. However, offsets in the API (such as column_num and start_column) are *byte* offsets into a utf-8 encoded version of the contents of the line or buffer. Therefore it is *never* safe to perform 'character' arithmetic (such as '-1' to get the previous 'character') using these byte offsets, and they cannot *ever* be used to index into line_value or buffer contents unicode strings. It is therefore important to ensure that you use the right type of offsets for the right type of calculation: - use codepoint offsets and a unicode string for 'character' calculations - use byte offsets and utf-8 encoded bytes for all other manipulations ycmd provides the following ways of accessing the source data and offsets: For working with utf-8 encoded bytes: - request_data[ 'line_bytes' ] - the line as utf-8 encoded bytes. - request_data[ 'start_column' ] and request_data[ 'column_num' ]. For working with 'character' manipulations (unicode strings and codepoint offsets): - request_data[ 'line_value' ] - the line as a unicode string. - request_data[ 'start_codepoint' ] and request_data[ 'column_codepoint' ]. For converting between the two: - utils.ToBytes - utils.ByteOffsetToCodepointOffset - utils.ToUnicode - utils.CodepointOffsetToByteOffset Note: The above use of codepoints for 'character' manipulations is not strictly correct. There are unicode 'characters' which consume multiple codepoints. However, it is currently considered viable to use a single codepoint = a single character until such a time as we improve support for unicode identifiers. The purpose of the above rule is to prevent crashes and random encoding exceptions, not to fully support unicode identifiers. *END: Important note about unicode and byte offsets* ShouldUseNow() is called with the start column of where a potential completion string should start and the current line (string) the cursor is on. For instance, if the user's input is 'foo.bar' and the cursor is on the 'r' in 'bar', start_column will be the 1-based byte index of 'b' in the line. Your implementation of ShouldUseNow() should return True if your semantic completer should be used and False otherwise. This is important to get right. You want to return False if you can't provide completions because then the identifier completer will kick in, and that's better than nothing. Note that it's HIGHLY likely that you want to override the ShouldUseNowInner() function instead of ShouldUseNow() directly (although chances are that you probably won't have any need to override either). ShouldUseNow() will call your *Inner version of the function and will also make sure that the completion cache is taken into account. You'll see this pattern repeated throughout the Completer API; YCM calls the "main" version of the function and that function calls the *Inner version while taking into account the cache. The cache is important and is a nice performance boost. When the user types in "foo.", your completer will return a list of all member functions and variables that can be accessed on the "foo" object. The Completer API caches this list. The user will then continue typing, let's say "foo.ba". On every keystroke after the dot, the Completer API will take the cache into account and will NOT re-query your completer but will in fact provide fuzzy-search on the candidate strings that were stored in the cache. ComputeCandidates() is the main entry point when the user types. For "foo.bar", the user query is "bar" and completions matching this string should be shown. It should return the list of candidates. The format of the result can be a list of strings or a more complicated list of dictionaries. Use ycmd.responses.BuildCompletionData to build the detailed response. See clang_completer.py to see how its used in practice. Again, you probably want to override ComputeCandidatesInner(). If computing the fields of the candidates is costly, you should consider building only the "insertion_text" field in ComputeCandidatesInner() then fill the remaining fields in DetailCandidates() which is called after the filtering is done. See python_completer.py for an example. You can also support 'delayed' detailing of candidates. The way this works is that you must add a 'resolve' key to the candidate's 'extra_data' which will be round-tripped to the client. The client passes the 'resolve' key back to us in the /resolve_completion request and your completer must override the DetailSingleCandidate() method, which is passed the full list of candidates from the cache and the resolve key supplied. This method must return the candidate, fully detailed. See the LanguageServerCompleter for an example. If the completer wants to use extra confs, it should implement Language() function as well, which returns a string that identifies the language in user's .ycmd_extra_conf.py file. You also need to implement the SupportedFiletypes() function which should return a list of strings, where the strings are Vim filetypes your completer supports. clang_completer.py is a good example of a "complicated" completer. A good example of a simple completer is ultisnips_completer.py. The On* functions are provided for your convenience. They are called when their specific events occur. For instance, the identifier completer collects all the identifiers in the file in OnFileReadyToParse() which gets called when the user stops typing for 2 seconds (Vim's CursorHold and CursorHoldI events). One special function is OnUserCommand. It is called when the user uses the command :YcmCompleter and is passed all extra arguments used on command invocation (e.g. OnUserCommand(['first argument', 'second'])). This can be used for completer-specific commands such as reloading external configuration. Do not override this function. Instead, you need to implement the GetSubcommandsMap method. It should return a map between the user commands and the methods of your completer. See the documentation of this method for more information on how to implement it. Override the Shutdown() member function if your Completer subclass needs to do custom cleanup logic on server shutdown. If the completer server provides unsolicited messages, such as used in Language Server Protocol, then you can override the PollForMessagesInner method. This method is called by the client in the "long poll" fashion to receive unsolicited messages. The method should block until a message is available and return a message response when one becomes available, or True if no message becomes available before the timeout. The return value must be one of the following: - a list of messages to send to the client - True if a timeout occurred, and the poll should be restarted - False if an error occurred, and no further polling should be attempted If your completer uses an external server process, then it can be useful to implement the ServerIsHealthy member function to handle the /healthy request. This is very useful for the test suite. If your server is based on the Language Server Protocol (LSP), take a look at language_server/language_server_completer, which provides most of the work necessary to get a LSP-based completion engine up and running. If your completer supports signature help, then you need to implemment: - SignatureHelpAvailable - something which calls self.SetSignatureHelpTriggers() - ComputeSignaturesInner See the language_server_completer or Python completers for examples. If your server returns lists of "code actions" that need to be resolved, instead of returning FixIts right away, you should override ResolveFixit. """ def __init__( self, user_options ): self.user_options = user_options self.min_num_chars = user_options[ 'min_num_of_chars_for_completion' ] self.max_diagnostics_to_display = user_options[ 'max_diagnostics_to_display' ] self.completion_triggers = ( completer_utils.PreparedTriggers( user_trigger_map = user_options[ 'semantic_triggers' ], filetype_set = set( self.SupportedFiletypes() ) ) if user_options[ 'auto_trigger' ] else None ) self._signature_triggers = ( completer_utils.PreparedTriggers( user_trigger_map = {}, # user triggers not supported for signature help filetype_set = set( self.SupportedFiletypes() ), default_triggers = {} ) if not user_options[ 'disable_signature_help' ] else None ) self._completions_cache = CompletionsCache() self._max_candidates = user_options[ 'max_num_candidates' ] self._max_candidates_to_detail = user_options[ 'max_num_candidates_to_detail' ] LOGGER.info( f"Completion config: { self._max_candidates }, detailing " f"{ self._max_candidates_to_detail } candiates" ) # It's highly likely you DON'T want to override this function but the *Inner # version of it. def ShouldUseNow( self, request_data ): if not self.ShouldUseNowInner( request_data ): self._completions_cache.Invalidate() return False # We have to do the cache valid check and get the completions as part of one # call because we have to ensure a different thread doesn't change the cache # data. cache_completions = self._completions_cache.GetCompletionsIfCacheValid( request_data ) # If None, then the cache isn't valid and we know we should return true if cache_completions is None: return True else: previous_results_were_valid = bool( cache_completions ) return previous_results_were_valid def ShouldUseNowInner( self, request_data ): if not self.completion_triggers: return False current_line = request_data[ 'line_value' ] start_codepoint = request_data[ 'start_codepoint' ] - 1 column_codepoint = request_data[ 'column_codepoint' ] - 1 filetype = self._CurrentFiletype( request_data[ 'filetypes' ] ) return self.completion_triggers.MatchesForFiletype( current_line, start_codepoint, column_codepoint, filetype ) def ShouldUseSignatureHelpNow( self, request_data ): if self.user_options[ 'disable_signature_help' ]: return False state = request_data.get( 'signature_help_state', 'INACTIVE' ) current_line = request_data[ 'line_value' ] # Note: We use the cursor column for all triggering of signature help, not # the calculated "start" codepoint. This is because start_codepoint is based # on the completion triggers, not the signature_triggers. column_codepoint = request_data[ 'column_codepoint' ] - 1 filetype = self._CurrentFiletype( request_data[ 'filetypes' ] ) if state == 'ACTIVE': # Signature help is already active (the menu is displayed), always # re-trigger until we return no signatures (and the client thus closes # the menu and returns state 'INACTIVE'). return True return self._signature_triggers.MatchesForFiletype( current_line, column_codepoint, column_codepoint, filetype ) def SetSignatureHelpTriggers( self, trigger_characters ): if self._signature_triggers is None: return self._signature_triggers.SetServerSemanticTriggers( trigger_characters ) def QueryLengthAboveMinThreshold( self, request_data ): # Note: calculation in 'characters' not bytes. query_length = ( request_data[ 'column_codepoint' ] - request_data[ 'start_codepoint' ] ) return query_length >= self.min_num_chars # It's highly likely you DON'T want to override this function but the *Inner # version of it. def ComputeCandidates( self, request_data ): if ( not request_data[ 'force_semantic' ] and not self.ShouldUseNow( request_data ) ): return [] candidates = self._GetCandidatesFromSubclass( request_data ) candidates = self.FilterAndSortCandidates( candidates, request_data[ 'query' ] ) return self.DetailCandidates( request_data, candidates ) def ShouldDetailCandidateList( self, candidates ): if self._max_candidates_to_detail < 0: return True if len( candidates ) < self._max_candidates_to_detail: return True return False def ResolveCompletionItem( self, request_data ): candidates = self._completions_cache.GetCompletionsIfCacheValid( request_data ) if not candidates: raise CompletionsChanged( 'Resolve request must not change request data' ) return self.DetailSingleCandidate( request_data, candidates, request_data[ 'resolve' ] ) def _GetCandidatesFromSubclass( self, request_data ): cache_completions = self._completions_cache.GetCompletionsIfCacheValid( request_data ) if cache_completions: return cache_completions raw_completions = self.ComputeCandidatesInner( request_data ) self._completions_cache.Update( request_data, raw_completions ) return raw_completions def DetailSingleCandidate( self, request_data, candidates, to_resolve ): # pragma: no cover raise RuntimeError( "Delayed detail candidate not implemented" ) def DetailCandidates( self, request_data, candidates ): return candidates def ComputeCandidatesInner( self, request_data ): return [] # pragma: no cover def ComputeSignatures( self, request_data ): if not self.ShouldUseSignatureHelpNow( request_data ): return {} return self.ComputeSignaturesInner( request_data ) def ComputeSignaturesInner( self, request_data ): return {} def DefinedSubcommands( self ): subcommands = sorted( self.GetSubcommandsMap().keys() ) try: # We don't want expose this subcommand because it is not really needed # for the user but it is useful in tests for tearing down the server subcommands.remove( 'StopServer' ) except ValueError: pass return subcommands def GetSubcommandsMap( self ): """This method should return a dictionary where each key represents the completer command name and its value is a lambda function of this form: ( self, request_data, args ) -> method where "method" is the call to the completer method with corresponding parameters. See the already implemented completers for examples. Arguments: - request_data : the request data supplied by the client - args: any additional command arguments (after the command name). Usually empty. """ return {} def ResolveFixit( self, request_data ): return { 'fixits': [ request_data[ 'fixit' ] ] } def UserCommandsHelpMessage( self ): subcommands = self.DefinedSubcommands() if subcommands: return ( 'Supported commands are:\n' + '\n'.join( subcommands ) + '\nSee the docs for information on what they do.' ) else: return 'This Completer has no supported subcommands.' def FilterAndSortCandidates( self, candidates, query ): if not candidates: return [] # We need to handle both an omni_completer style completer and a server # style completer if isinstance( candidates, dict ) and 'words' in candidates: candidates = candidates[ 'words' ] sort_property = '' if isinstance( candidates[ 0 ], dict ): if 'word' in candidates[ 0 ]: sort_property = 'word' elif 'insertion_text' in candidates[ 0 ]: sort_property = 'insertion_text' return self.FilterAndSortCandidatesInner( candidates, sort_property, query ) def FilterAndSortCandidatesInner( self, candidates, sort_property, query ): return completer_utils.FilterAndSortCandidatesWrap( candidates, sort_property, query, self._max_candidates ) def OnFileReadyToParse( self, request_data ): pass # pragma: no cover def OnFileSave( self, request_data ): pass # pragma: no cover def OnBufferVisit( self, request_data ): pass # pragma: no cover def OnBufferUnload( self, request_data ): pass # pragma: no cover def OnInsertLeave( self, request_data ): pass # pragma: no cover def Langauge( self ): pass # pragma: no cover def OnUserCommand( self, arguments, request_data ): if not arguments: raise ValueError( self.UserCommandsHelpMessage() ) command_map = self.GetSubcommandsMap() try: command = command_map[ arguments[ 0 ] ] except KeyError: raise ValueError( self.UserCommandsHelpMessage() ) return command( self, request_data, arguments[ 1: ] ) def OnCurrentIdentifierFinished( self, request_data ): pass # pragma: no cover def GetDiagnosticsForCurrentFile( self, request_data ): raise NoDiagnosticSupport def GetDetailedDiagnostic( self, request_data ): raise NoDiagnosticSupport def _CurrentFiletype( self, filetypes ): supported = self.SupportedFiletypes() for filetype in filetypes: if filetype in supported: return filetype return filetypes[ 0 ] @abc.abstractmethod def SupportedFiletypes( self ): return set() def DebugInfo( self, request_data ): return '' def Shutdown( self ): pass # pragma: no cover def ServerIsReady( self ): return self.ServerIsHealthy() def SignatureHelpAvailable( self ): return SignatureHelpAvailalability.NOT_AVAILABLE def ServerIsHealthy( self ): """Called by the /healthy handler to check if the underlying completion server is started and ready to receive requests. Returns bool.""" return True def PollForMessages( self, request_data ): return self.PollForMessagesInner( request_data, MESSAGE_POLL_TIMEOUT ) def PollForMessagesInner( self, request_data, timeout ): # Most completers don't implement this. It's only required where unsolicited # messages or diagnostics are supported, such as in the Language Server # Protocol. As such, the default implementation just returns False, meaning # that unsolicited messages are not supported for this filetype. return False def AdditionalFormattingOptions( self, request_data ): module = extra_conf_store.ModuleForSourceFile( request_data[ 'filepath' ] ) try: settings = self.GetSettings( module, request_data ) return settings.get( 'formatting_options', {} ) except AttributeError: return {} def GetSettings( self, module, request_data ): if hasattr( module, 'Settings' ): settings = module.Settings( filename = request_data[ 'filepath' ], language = self.Language(), client_data = request_data[ 'extra_conf_data' ] ) if settings is not None: return settings LOGGER.debug( 'No Settings function defined in %s', module.__file__ ) return {} class CompletionsCache: """Cache of computed completions for a particular request.""" def __init__( self ): self._access_lock = threading.Lock() self.Invalidate() def Invalidate( self ): with self._access_lock: self.InvalidateNoLock() def InvalidateNoLock( self ): self._request_data = None self._completions = None def Update( self, request_data, completions ): with self._access_lock: self.UpdateNoLock( request_data, completions ) def UpdateNoLock( self, request_data, completions ): self._request_data = request_data self._completions = completions def GetCompletionsIfCacheValid( self, request_data ): with self._access_lock: return self.GetCompletionsIfCacheValidNoLock( request_data ) def GetCompletionsIfCacheValidNoLock( self, request_data ): if self._request_data and self._request_data == request_data: return self._completions return None ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/completer_utils.py������������������������������������0000664�0000000�0000000�00000020700�13746324601�0024033�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2013-2020 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 <http://www.gnu.org/licenses/>. # Must not import ycm_core here! Vim imports completer, which imports this file. # We don't want ycm_core inside Vim. from collections import defaultdict from ycmd.utils import LOGGER, ToUnicode, re, ReadFile, SplitLines class PreparedTriggers: def __init__( self, user_trigger_map = None, filetype_set = None, default_triggers = None ): self._user_trigger_map = user_trigger_map self._server_trigger_map = None self._filetype_set = filetype_set self._default_triggers = ( default_triggers if default_triggers is not None else PREPARED_DEFAULT_FILETYPE_TRIGGERS ) self._CombineTriggers() def _CombineTriggers( self ): user_prepared_triggers = ( _FiletypeTriggerDictFromSpec( dict( self._user_trigger_map ) ) if self._user_trigger_map else defaultdict( set ) ) server_prepared_triggers = ( _FiletypeTriggerDictFromSpec( dict( self._server_trigger_map ) ) if self._server_trigger_map else defaultdict( set ) ) # Combine all of the defaults, server-supplied and user-supplied triggers final_triggers = _FiletypeDictUnion( self._default_triggers, server_prepared_triggers, user_prepared_triggers ) if self._filetype_set: final_triggers = { k: v for k, v in final_triggers.items() if k in self._filetype_set } self._filetype_to_prepared_triggers = final_triggers def SetServerSemanticTriggers( self, server_trigger_characters ): self._server_trigger_map = { ','.join( self._filetype_set ): server_trigger_characters } self._CombineTriggers() def MatchingTriggerForFiletype( self, current_line, start_codepoint, column_codepoint, filetype ): try: triggers = self._filetype_to_prepared_triggers[ filetype ] except KeyError: return None return _MatchingSemanticTrigger( current_line, start_codepoint, column_codepoint, triggers ) def MatchesForFiletype( self, current_line, start_codepoint, column_codepoint, filetype ): return self.MatchingTriggerForFiletype( current_line, start_codepoint, column_codepoint, filetype ) is not None def _FiletypeTriggerDictFromSpec( trigger_dict_spec ): triggers_for_filetype = defaultdict( set ) for key, triggers in trigger_dict_spec.items(): filetypes = key.split( ',' ) for filetype in filetypes: regexes = [ _PrepareTrigger( x ) for x in triggers ] triggers_for_filetype[ filetype ].update( regexes ) return triggers_for_filetype def _FiletypeDictUnion( *args ): """Returns a new filetype dict that's a union of the provided two dicts. Dict params are supposed to be type defaultdict(set).""" def UpdateDict( first, second ): for key, value in second.items(): first[ key ].update( value ) final_dict = defaultdict( set ) for d in args: UpdateDict( final_dict, d ) return final_dict # start_codepoint and column_codepoint are codepoint offsets in the unicode # string line_value. def _RegexTriggerMatches( trigger, line_value, start_codepoint, column_codepoint ): for match in trigger.finditer( line_value ): # By definition of 'start_codepoint', we know that the character just before # 'start_codepoint' is not an identifier character but all characters # between 'start_codepoint' and 'column_codepoint' are. This means that if # our trigger ends with an identifier character, its tail must match between # 'start_codepoint' and 'column_codepoint', 'start_codepoint' excluded. But # if it doesn't, its tail must match exactly at 'start_codepoint'. Both # cases are mutually exclusive hence the following condition. if start_codepoint <= match.end() and match.end() <= column_codepoint: return True return False # start_codepoint and column_codepoint are 0-based and are codepoint offsets # into the unicode string line_value. def _MatchingSemanticTrigger( line_value, start_codepoint, column_codepoint, trigger_list ): if start_codepoint < 0 or column_codepoint < 0: return None line_length = len( line_value ) if not line_length or start_codepoint > line_length: return None # Ignore characters after user's caret column line_value = line_value[ : column_codepoint ] for trigger in trigger_list: if _RegexTriggerMatches( trigger, line_value, start_codepoint, column_codepoint ): return trigger return None def _MatchesSemanticTrigger( line_value, start_codepoint, column_codepoint, trigger_list ): return _MatchingSemanticTrigger( line_value, start_codepoint, column_codepoint, trigger_list ) is not None def _PrepareTrigger( trigger ): trigger = ToUnicode( trigger ) if trigger.startswith( TRIGGER_REGEX_PREFIX ): return re.compile( trigger[ len( TRIGGER_REGEX_PREFIX ) : ], re.UNICODE ) return re.compile( re.escape( trigger ), re.UNICODE ) def FilterAndSortCandidatesWrap( candidates, sort_property, query, max_candidates ): from ycm_core import FilterAndSortCandidates return FilterAndSortCandidates( candidates, sort_property, query, max_candidates ) TRIGGER_REGEX_PREFIX = 're!' DEFAULT_FILETYPE_TRIGGERS = { 'c' : [ '->', '.' ], 'objc,objcpp' : [ '->', '.', r're!\[[_a-zA-Z]+\w*\s', # bracketed calls r're!^\s*[^\W\d]\w*\s', # bracketless calls r're!\[.*\]\s', # method composition ], 'ocaml' : [ '.', '#' ], 'cpp,cuda,objcpp,cs' : [ '->', '.', '::' ], 'perl' : [ '->' ], 'php' : [ '->', '::' ], ( 'd,' 'elixir,' 'go,' 'gdscript,' 'groovy,' 'java,' 'javascript,' 'javascriptreact,' 'julia,' 'perl6,' 'python,' 'scala,' 'typescript,' 'typescriptreact,' 'vb' ) : [ '.' ], 'ruby,rust' : [ '.', '::' ], 'lua' : [ '.', ':' ], 'erlang' : [ ':' ], } PREPARED_DEFAULT_FILETYPE_TRIGGERS = _FiletypeTriggerDictFromSpec( DEFAULT_FILETYPE_TRIGGERS ) def GetFileContents( request_data, filename ): """Returns the contents of the absolute path |filename| as a unicode string. If the file contents exist in |request_data| (i.e. it is open and potentially modified/dirty in the user's editor), then it is returned, otherwise the file is read from disk (assuming a UTF-8 encoding) and its contents returned.""" file_data = request_data[ 'file_data' ] if filename in file_data: return ToUnicode( file_data[ filename ][ 'contents' ] ) try: return ToUnicode( ReadFile( filename ) ) except ( OSError, UnicodeError ): LOGGER.exception( 'Error reading file %s', filename ) return '' def GetFileLines( request_data, filename ): """Like GetFileContents but return the contents as a list of lines. Avoid splitting the lines if they have already been split for the current file.""" if filename == request_data[ 'filepath' ]: return request_data[ 'lines' ] return SplitLines( GetFileContents( request_data, filename ) ) ����������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/cpp/��������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0021032�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/cpp/__init__.py���������������������������������������0000664�0000000�0000000�00000000000�13746324601�0023131�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/cpp/clang_completer.py��������������������������������0000664�0000000�0000000�00000056534�13746324601�0024557�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2011-2020 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 <http://www.gnu.org/licenses/>. from collections import defaultdict import os.path import textwrap import xml.etree.ElementTree from xml.etree.ElementTree import ParseError as XmlParseError from ycmd import responses from ycmd.utils import ImportCore, PathLeftSplit, re, ToBytes, ToUnicode from ycmd.completers.completer import Completer from ycmd.completers.cpp.flags import ( Flags, PrepareFlagsForClang, UserIncludePaths ) from ycmd.completers.cpp.ephemeral_values_set import EphemeralValuesSet from ycmd.completers.cpp.include_cache import IncludeCache, IncludeList from ycmd.responses import NoExtraConfDetected, UnknownExtraConf ycm_core = ImportCore() CLANG_FILETYPES = { 'c', 'cpp', 'cuda', 'objc', 'objcpp' } PARSING_FILE_MESSAGE = 'Still parsing file.' NO_COMPILE_FLAGS_MESSAGE = 'Still no compile flags.' NO_COMPLETIONS_MESSAGE = 'No completions found; errors in the file?' NO_DIAGNOSTIC_MESSAGE = 'No diagnostic for current line!' PRAGMA_DIAG_TEXT_TO_IGNORE = '#pragma once in main file' TOO_MANY_ERRORS_DIAG_TEXT_TO_IGNORE = 'too many errors emitted, stopping now' NO_DOCUMENTATION_MESSAGE = 'No documentation available for current context' INCLUDE_REGEX = re.compile( '(\\s*#\\s*(?:include|import)\\s*)(?:"[^"]*|<[^>]*)' ) class ClangCompleter( Completer ): def __init__( self, user_options ): super().__init__( user_options ) self._completer = ycm_core.ClangCompleter() self._flags = Flags() self._include_cache = IncludeCache() self._diagnostic_store = None self._files_being_compiled = EphemeralValuesSet() def SupportedFiletypes( self ): return CLANG_FILETYPES def GetUnsavedFilesVector( self, request_data ): files = ycm_core.UnsavedFileVector() for filename, file_data in request_data[ 'file_data' ].items(): if not ClangAvailableForFiletypes( file_data[ 'filetypes' ] ): continue contents = file_data[ 'contents' ] if not contents or not filename: continue unsaved_file = ycm_core.UnsavedFile() utf8_contents = ToBytes( contents ) unsaved_file.contents_ = utf8_contents unsaved_file.length_ = len( utf8_contents ) unsaved_file.filename_ = filename files.append( unsaved_file ) return files def ShouldCompleteIncludeStatement( self, request_data ): column_codepoint = request_data[ 'column_codepoint' ] - 1 current_line = request_data[ 'line_value' ] return INCLUDE_REGEX.match( current_line[ : column_codepoint ] ) def ShouldUseNowInner( self, request_data ): if self.ShouldCompleteIncludeStatement( request_data ): return True return super().ShouldUseNowInner( request_data ) def GetIncludePaths( self, request_data ): column_codepoint = request_data[ 'column_codepoint' ] - 1 current_line = request_data[ 'line_value' ] line = current_line[ : column_codepoint ] path_dir, quoted_include, start_codepoint = ( GetIncompleteIncludeValue( line ) ) if start_codepoint is None: return None request_data[ 'start_codepoint' ] = start_codepoint # We do what GCC does for <> versus "": # http://gcc.gnu.org/onlinedocs/cpp/Include-Syntax.html flags, filepath = self._FlagsForRequest( request_data ) ( quoted_include_paths, include_paths, framework_paths ) = UserIncludePaths( flags, filepath ) if quoted_include: include_paths.extend( quoted_include_paths ) includes = IncludeList() for include_path in include_paths: unicode_path = ToUnicode( os.path.join( include_path, path_dir ) ) includes.AddIncludes( self._include_cache.GetIncludes( unicode_path ) ) if framework_paths: if path_dir: head, tail = PathLeftSplit( path_dir ) path_dir = os.path.join( head + '.framework', 'Headers', tail ) for framework_path in framework_paths: unicode_path = ToUnicode( os.path.join( framework_path, path_dir ) ) includes.AddIncludes( self._include_cache.GetIncludes( unicode_path, is_framework = not path_dir ) ) return includes.GetIncludes() def ComputeCandidatesInner( self, request_data ): flags, filename = self._FlagsForRequest( request_data ) if not flags: raise RuntimeError( NO_COMPILE_FLAGS_MESSAGE ) includes = self.GetIncludePaths( request_data ) if includes is not None: return includes if self._completer.UpdatingTranslationUnit( filename ): raise RuntimeError( PARSING_FILE_MESSAGE ) files = self.GetUnsavedFilesVector( request_data ) line = request_data[ 'line_num' ] column = request_data[ 'start_column' ] with self._files_being_compiled.GetExclusive( filename ): results = self._completer.CandidatesForLocationInFile( filename, request_data[ 'filepath' ], line, column, files, flags ) if not results: raise RuntimeError( NO_COMPLETIONS_MESSAGE ) return [ ConvertCompletionData( x ) for x in results ] def GetSubcommandsMap( self ): return { 'GoToDefinition' : ( lambda self, request_data, args: self._GoToDefinition( request_data ) ), 'GoToDeclaration' : ( lambda self, request_data, args: self._GoToDeclaration( request_data ) ), 'GoTo' : ( lambda self, request_data, args: self._GoTo( request_data ) ), 'GoToImprecise' : ( lambda self, request_data, args: self._GoToImprecise( request_data ) ), 'GoToInclude' : ( lambda self, request_data, args: self._GoToInclude( request_data ) ), 'ClearCompilationFlagCache': ( lambda self, request_data, args: self._ClearCompilationFlagCache() ), 'GetType' : ( lambda self, request_data, args: self._GetSemanticInfo( request_data, func = 'GetTypeAtLocation' ) ), 'GetTypeImprecise' : ( lambda self, request_data, args: self._GetSemanticInfo( request_data, func = 'GetTypeAtLocation', reparse = False ) ), 'GetParent' : ( lambda self, request_data, args: self._GetSemanticInfo( request_data, func = 'GetEnclosingFunctionAtLocation' ) ), 'FixIt' : ( lambda self, request_data, args: self._FixIt( request_data ) ), 'GetDoc' : ( lambda self, request_data, args: self._GetSemanticInfo( request_data, reparse = True, func = 'GetDocsForLocationInFile', response_builder = _BuildGetDocResponse ) ), 'GetDocImprecise' : ( lambda self, request_data, args: self._GetSemanticInfo( request_data, reparse = False, func = 'GetDocsForLocationInFile', response_builder = _BuildGetDocResponse ) ), } def _LocationForGoTo( self, goto_function, request_data, reparse = True ): flags, filename = self._FlagsForRequest( request_data ) if not flags: raise ValueError( NO_COMPILE_FLAGS_MESSAGE ) if self._completer.UpdatingTranslationUnit( filename ): raise RuntimeError( PARSING_FILE_MESSAGE ) files = self.GetUnsavedFilesVector( request_data ) line = request_data[ 'line_num' ] column = request_data[ 'column_num' ] return getattr( self._completer, goto_function )( filename, request_data[ 'filepath' ], line, column, files, flags, reparse ) def _GoToDefinition( self, request_data ): location = self._LocationForGoTo( 'GetDefinitionLocation', request_data ) if not location or not location.IsValid(): raise RuntimeError( 'Can\'t jump to definition.' ) return _ResponseForLocation( location ) def _GoToDeclaration( self, request_data ): location = self._LocationForGoTo( 'GetDeclarationLocation', request_data ) if not location or not location.IsValid(): raise RuntimeError( 'Can\'t jump to declaration.' ) return _ResponseForLocation( location ) def _GoTo( self, request_data ): include_response = self._ResponseForInclude( request_data ) if include_response: return include_response location = self._LocationForGoTo( 'GetDefinitionOrDeclarationLocation', request_data ) if not location or not location.IsValid(): raise RuntimeError( 'Can\'t jump to definition or declaration.' ) return _ResponseForLocation( location ) def _GoToImprecise( self, request_data ): include_response = self._ResponseForInclude( request_data ) if include_response: return include_response location = self._LocationForGoTo( 'GetDefinitionOrDeclarationLocation', request_data, reparse = False ) if not location or not location.IsValid(): raise RuntimeError( 'Can\'t jump to definition or declaration.' ) return _ResponseForLocation( location ) def _ResponseForInclude( self, request_data ): """Returns response for include file location if cursor is on the include statement, None otherwise. Throws RuntimeError if cursor is on include statement and corresponding include file not found.""" current_line = request_data[ 'line_value' ] include_file_name, quoted_include = GetFullIncludeValue( current_line ) if not include_file_name: return None flags, current_file_path = self._FlagsForRequest( request_data ) ( quoted_include_paths, include_paths, framework_paths ) = UserIncludePaths( flags, current_file_path ) include_file_path = None if quoted_include: include_file_path = _GetAbsolutePath( include_file_name, quoted_include_paths ) if not include_file_path: include_file_path = _GetAbsolutePath( include_file_name, include_paths ) if not include_file_path and framework_paths: head, tail = PathLeftSplit( include_file_name ) include_file_name = os.path.join( head + '.framework', 'Headers', tail ) include_file_path = _GetAbsolutePath( include_file_name, framework_paths ) if include_file_path: return responses.BuildGoToResponse( include_file_path, line_num = 1, column_num = 1 ) raise RuntimeError( 'Include file not found.' ) def _GoToInclude( self, request_data ): include_response = self._ResponseForInclude( request_data ) if not include_response: raise RuntimeError( 'Not an include/import line.' ) return include_response def _GetSemanticInfo( self, request_data, func, response_builder = responses.BuildDisplayMessageResponse, reparse = True ): flags, filename = self._FlagsForRequest( request_data ) if not flags: raise ValueError( NO_COMPILE_FLAGS_MESSAGE ) if self._completer.UpdatingTranslationUnit( filename ): raise RuntimeError( PARSING_FILE_MESSAGE ) files = self.GetUnsavedFilesVector( request_data ) line = request_data[ 'line_num' ] column = request_data[ 'column_num' ] message = getattr( self._completer, func )( filename, request_data[ 'filepath' ], line, column, files, flags, reparse ) if not message: message = "No semantic information available" return response_builder( message ) def _ClearCompilationFlagCache( self ): self._flags.Clear() def _FixIt( self, request_data ): flags, filename = self._FlagsForRequest( request_data ) if not flags: raise ValueError( NO_COMPILE_FLAGS_MESSAGE ) if self._completer.UpdatingTranslationUnit( filename ): raise RuntimeError( PARSING_FILE_MESSAGE ) files = self.GetUnsavedFilesVector( request_data ) line = request_data[ 'line_num' ] column = request_data[ 'column_num' ] fixits = getattr( self._completer, "GetFixItsForLocationInFile" )( filename, request_data[ 'filepath' ], line, column, files, flags, True ) # don't raise an error if not fixits: - leave that to the client to respond # in a nice way return responses.BuildFixItResponse( fixits ) def OnFileReadyToParse( self, request_data ): flags, filename = self._FlagsForRequest( request_data ) if not flags: raise ValueError( NO_COMPILE_FLAGS_MESSAGE ) with self._files_being_compiled.GetExclusive( filename ): diagnostics = self._completer.UpdateTranslationUnit( filename, self.GetUnsavedFilesVector( request_data ), flags ) diagnostics = _FilterDiagnostics( diagnostics ) self._diagnostic_store = DiagnosticsToDiagStructure( diagnostics ) return responses.BuildDiagnosticResponse( diagnostics, request_data[ 'filepath' ], self.max_diagnostics_to_display ) def OnBufferUnload( self, request_data ): # FIXME: The filepath here is (possibly) wrong when overriding the # translation unit filename. If the buffer that the user closed is not the # "translation unit" filename, then we won't close the unit. It would # require the user to open the translation unit file, and close that. # Incidentally, doing so would flush the unit for any _other_ open files # which use that translation unit. # # Solving this would require remembering the graph of files to translation # units and only closing a unit when there are no files open which use it. self._completer.DeleteCachesForFile( request_data[ 'filepath' ] ) def GetDetailedDiagnostic( self, request_data ): current_line = request_data[ 'line_num' ] current_column = request_data[ 'column_num' ] current_file = request_data[ 'filepath' ] if not self._diagnostic_store: raise ValueError( NO_DIAGNOSTIC_MESSAGE ) diagnostics = self._diagnostic_store[ current_file ][ current_line ] if not diagnostics: raise ValueError( NO_DIAGNOSTIC_MESSAGE ) closest_diagnostic = None distance_to_closest_diagnostic = 999 # FIXME: all of these calculations are currently working with byte # offsets, which are technically incorrect. We should be working with # codepoint offsets, as we want the nearest character-wise diagnostic for diagnostic in diagnostics: distance = abs( current_column - diagnostic.location_.column_number_ ) if distance < distance_to_closest_diagnostic: distance_to_closest_diagnostic = distance closest_diagnostic = diagnostic return responses.BuildDisplayMessageResponse( closest_diagnostic.long_formatted_text_ ) def DebugInfo( self, request_data ): try: # Note that it only raises NoExtraConfDetected: # - when extra_conf is None and, # - there is no compilation database flags, filename = self._FlagsForRequest( request_data ) or [] except ( NoExtraConfDetected, UnknownExtraConf ): # If _FlagsForRequest returns None or raises, we use an empty list in # practice. flags = [] filename = request_data[ 'filepath' ] database = self._flags.LoadCompilationDatabase( filename ) database_directory = database.database_directory if database else None database_item = responses.DebugInfoItem( key = 'compilation database path', value = '{0}'.format( database_directory ) ) flags_item = responses.DebugInfoItem( key = 'flags', value = '{0}'.format( list( flags ) ) ) filename_item = responses.DebugInfoItem( key = 'translation unit', value = filename ) return responses.BuildDebugInfoResponse( name = 'C-family', items = [ database_item, flags_item, filename_item ] ) def _FlagsForRequest( self, request_data ): filename = request_data[ 'filepath' ] if 'compilation_flags' in request_data: # Not supporting specifying the translation unit using this method as it # is only used by the tests. return ( PrepareFlagsForClang( request_data[ 'compilation_flags' ], filename ), filename ) client_data = request_data[ 'extra_conf_data' ] return self._flags.FlagsForFile( filename, client_data = client_data ) def BuildExtraData( completion_data ): extra_data = {} fixit = completion_data.fixit_ if fixit.chunks: extra_data.update( responses.BuildFixItResponse( [ fixit ] ) ) if completion_data.DocString(): extra_data[ 'doc_string' ] = completion_data.DocString() return extra_data def ConvertCompletionData( completion_data ): return responses.BuildCompletionData( insertion_text = completion_data.TextToInsertInBuffer(), menu_text = completion_data.MainCompletionText(), extra_menu_info = completion_data.ExtraMenuInfo(), kind = completion_data.kind_.name, detailed_info = completion_data.DetailedInfoForPreviewWindow(), extra_data = BuildExtraData( completion_data ) ) def DiagnosticsToDiagStructure( diagnostics ): structure = defaultdict( lambda : defaultdict( list ) ) for diagnostic in diagnostics: structure[ diagnostic.location_.filename_ ][ diagnostic.location_.line_number_ ].append( diagnostic ) return structure def ClangAvailableForFiletypes( filetypes ): return any( filetype in CLANG_FILETYPES for filetype in filetypes ) def _FilterDiagnostics( diagnostics ): # Clang has an annoying warning that shows up when we try to compile header # files if the header has "#pragma once" inside it. The error is not # legitimate because it shows up because libclang thinks we are compiling a # source file instead of a header file. # # See our issue #216 and upstream bug: # http://llvm.org/bugs/show_bug.cgi?id=16686 # # The second thing we want to filter out are those incredibly annoying "too # many errors emitted" diagnostics that are utterly useless. return [ x for x in diagnostics if x.text_ != PRAGMA_DIAG_TEXT_TO_IGNORE and x.text_ != TOO_MANY_ERRORS_DIAG_TEXT_TO_IGNORE ] def _ResponseForLocation( location ): return responses.BuildGoToResponse( location.filename_, location.line_number_, location.column_number_ ) # Strips the following leading strings from the raw comment: # - <whitespace>/// # - <whitespace>///< # - <whitespace>//< # - <whitespace>//! # - <whitespace>/** # - <whitespace>/*! # - <whitespace>/*< # - <whitespace>/* # - <whitespace>* # - <whitespace>*/ # - etc. # That is: # - 2 or 3 '/' followed by '<' or '!' # - '/' then 1 or 2 '*' followed by optional '<' or '!' # - '*' followed by optional '/' STRIP_LEADING_COMMENT = re.compile( '^[ \t]*(/{2,3}[<!]?|/\\*{1,2}[<!]?|\\*/?)' ) # And the following trailing strings # - <whitespace>*/ # - <whitespace> STRIP_TRAILING_COMMENT = re.compile( '[ \t]*\\*/[ \t]*$|[ \t]*$' ) def _FormatRawComment( comment ): """Strips leading indentation and comment markers from the comment string""" return textwrap.dedent( '\n'.join( [ re.sub( STRIP_TRAILING_COMMENT, '', re.sub( STRIP_LEADING_COMMENT, '', line ) ) for line in ToUnicode( comment ).splitlines() ] ) ) def _BuildGetDocResponse( doc_data ): """Builds a "DetailedInfoResponse" for a GetDoc request. doc_data is a DocumentationData object returned from the ClangCompleter""" # Parse the XML, as this is the only way to get the declaration text out of # libclang. It seems quite wasteful, but while the contents of the XML # provide fully parsed doxygen documentation tree, we actually don't want to # ever lose any information from the comment, so we just want display # the stripped comment. Arguably we could skip all of this XML generation and # parsing, but having the raw declaration text is likely one of the most # useful pieces of documentation available to the developer. Perhaps in # future, we can use this XML for more interesting things. try: root = xml.etree.ElementTree.fromstring( doc_data.comment_xml ) except XmlParseError: raise ValueError( NO_DOCUMENTATION_MESSAGE ) # Note: declaration is False-y if it has no child elements, hence the below # (wordy) if not declaration is None declaration = root.find( "Declaration" ) return responses.BuildDetailedInfoResponse( '{0}\n{1}\nType: {2}\nName: {3}\n---\n{4}'.format( ToUnicode( declaration.text ) if declaration is not None else "", ToUnicode( doc_data.brief_comment ), ToUnicode( doc_data.canonical_type ), ToUnicode( doc_data.display_name ), ToUnicode( _FormatRawComment( doc_data.raw_comment ) ) ) ) def _GetAbsolutePath( include_file_name, include_paths ): for path in include_paths: include_file_path = os.path.join( path, include_file_name ) if os.path.isfile( include_file_path ): return include_file_path return None def GetIncompleteIncludeValue( line ): """Returns the tuple |include_value|, |quoted_include|, and |start_codepoint| where: - |include_value| is the string starting from the opening quote or bracket of the include statement in |line|. None if no include statement is found; - |quoted_include| is True if the statement is a quoted include, False otherwise; - |start_column| is the 1-based column where the completion should start (i.e. at the last path separator '/' or at the opening quote or bracket). None if no include statement is matched.""" match = INCLUDE_REGEX.match( line ) if not match: return None, False, None include_start = match.end( 1 ) + 1 quoted_include = ( line[ include_start - 1 ] == '"' ) separator_char = '/' separator_char_pos = line.rfind( separator_char, match.end( 1 ) ) if separator_char_pos == -1: return '', quoted_include, include_start + 1 return ( line[ include_start : separator_char_pos + 1 ], quoted_include, separator_char_pos + 2 ) def GetFullIncludeValue( line ): """Returns the tuple |include_value| and |quoted_include| where: - |include_value| is the whole string inside the quotes or brackets of the include statement in |line|. None if no include statement is found; - |quoted_include| is True if the statement is a quoted include, False otherwise.""" match = INCLUDE_REGEX.match( line ) if not match: return None, False include_start = match.end( 1 ) + 1 quoted_include = ( line[ include_start - 1 ] == '"' ) close_char = '"' if quoted_include else '>' close_char_pos = line.find( close_char, match.end() ) if close_char_pos == -1: return None, quoted_include return line[ include_start : close_char_pos ], quoted_include ��������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/cpp/clang_helpers.py����������������������������������0000664�0000000�0000000�00000001453�13746324601�0024215�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. # Provided for backwards compatibility with old ycm_extra_conf files. def PrepareClangFlags( flags, filename ): return flags ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/cpp/clangd_completer.py�������������������������������0000664�0000000�0000000�00000034104�13746324601�0024710�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2018-2020 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 <http://www.gnu.org/licenses/>. import logging import os import subprocess from ycmd import extra_conf_store, responses from ycmd.completers.cpp.flags import ( AddMacIncludePaths, RemoveUnusedFlags, ShouldAllowWinStyleFlags ) from ycmd.completers.language_server import language_server_completer from ycmd.completers.language_server import language_server_protocol as lsp from ycmd.utils import ( CLANG_RESOURCE_DIR, GetExecutable, ExpandVariablesInPath, FindExecutable, LOGGER, OnMac, PathsToAllParentFolders, re ) MIN_SUPPORTED_VERSION = ( 10, 0, 0 ) INCLUDE_REGEX = re.compile( '(\\s*#\\s*(?:include|import)\\s*)(?:"[^"]*|<[^>]*)' ) NOT_CACHED = 'NOT_CACHED' CLANGD_COMMAND = NOT_CACHED PRE_BUILT_CLANGD_DIR = os.path.abspath( os.path.join( os.path.dirname( __file__ ), '..', '..', '..', 'third_party', 'clangd', 'output', 'bin' ) ) PRE_BUILT_CLANDG_PATH = os.path.join( PRE_BUILT_CLANGD_DIR, 'clangd' ) def ParseClangdVersion( version_str ): version_regexp = r'(\d+)\.(\d+)\.(\d+)' m = re.search( version_regexp, version_str ) try: version = tuple( int( x ) for x in m.groups() ) except AttributeError: # Custom builds might have different versioning info. version = None return version def GetVersion( clangd_path ): args = [ clangd_path, '--version' ] stdout, _ = subprocess.Popen( args, stdout=subprocess.PIPE ).communicate() return ParseClangdVersion( stdout.decode() ) def CheckClangdVersion( clangd_path ): version = GetVersion( clangd_path ) if version and version < MIN_SUPPORTED_VERSION: return False return True def GetThirdPartyClangd(): pre_built_clangd = GetExecutable( PRE_BUILT_CLANDG_PATH ) if not pre_built_clangd: LOGGER.info( 'No Clangd executable found in %s', PRE_BUILT_CLANGD_DIR ) return None if not CheckClangdVersion( pre_built_clangd ): LOGGER.error( 'Clangd executable at %s is out-of-date', pre_built_clangd ) return None LOGGER.info( 'Clangd executable found at %s and up to date', PRE_BUILT_CLANGD_DIR ) return pre_built_clangd def GetClangdExecutableAndResourceDir( user_options ): """Return the Clangd binary from the path specified in the 'clangd_binary_path' option. Let the binary find its resource directory in that case. If no binary is found or if it's out-of-date, return nothing. If 'clangd_binary_path' is empty, return the third-party Clangd and its resource directory if the user downloaded it and if it's up to date. Otherwise, return nothing.""" clangd = user_options[ 'clangd_binary_path' ] resource_dir = None if clangd: clangd = FindExecutable( ExpandVariablesInPath( clangd ) ) if not clangd: LOGGER.error( 'No Clangd executable found at %s', user_options[ 'clangd_binary_path' ] ) return None, None if not CheckClangdVersion( clangd ): LOGGER.error( 'Clangd at %s is out-of-date', clangd ) return None, None # Try looking for the pre-built binary. else: third_party_clangd = GetThirdPartyClangd() if not third_party_clangd: return None, None clangd = third_party_clangd resource_dir = CLANG_RESOURCE_DIR LOGGER.info( 'Using Clangd from %s', clangd ) return clangd, resource_dir def GetClangdCommand( user_options ): global CLANGD_COMMAND # None stands for we tried to fetch command and failed, therefore it is not # the default. if CLANGD_COMMAND != NOT_CACHED: LOGGER.info( 'Returning cached Clangd command: %s', CLANGD_COMMAND ) return CLANGD_COMMAND CLANGD_COMMAND = None installed_clangd, resource_dir = GetClangdExecutableAndResourceDir( user_options ) if not installed_clangd: return None CLANGD_COMMAND = [ installed_clangd ] clangd_args = user_options[ 'clangd_args' ] put_resource_dir = False put_limit_results = False put_header_insertion_decorators = False put_log = False for arg in clangd_args: CLANGD_COMMAND.append( arg ) put_resource_dir = put_resource_dir or arg.startswith( '-resource-dir' ) put_limit_results = put_limit_results or arg.startswith( '-limit-results' ) put_header_insertion_decorators = ( put_header_insertion_decorators or arg.startswith( '-header-insertion-decorators' ) ) put_log = put_log or arg.startswith( '-log' ) if not put_header_insertion_decorators: CLANGD_COMMAND.append( '-header-insertion-decorators=0' ) if resource_dir and not put_resource_dir: CLANGD_COMMAND.append( '-resource-dir=' + resource_dir ) if user_options[ 'clangd_uses_ycmd_caching' ] and not put_limit_results: CLANGD_COMMAND.append( '-limit-results=500' ) if LOGGER.isEnabledFor( logging.DEBUG ) and not put_log: CLANGD_COMMAND.append( '-log=verbose' ) return CLANGD_COMMAND def ShouldEnableClangdCompleter( user_options ): """Checks whether clangd should be enabled or not. - Returns True if an up-to-date binary exists either in `clangd_binary_path` or in third party folder and `use_clangd` is not set to `0`. """ # User disabled clangd explicitly. if not user_options[ 'use_clangd' ]: return False clangd_command = GetClangdCommand( user_options ) if not clangd_command: return False LOGGER.info( 'Computed Clangd command: %s', clangd_command ) return True def PrependCompilerToFlags( flags, enable_windows_style_flags ): """Removes everything before the first flag and returns the remaining flags prepended with clang-tool.""" for index, flag in enumerate( flags ): if ( flag.startswith( '-' ) or ( enable_windows_style_flags and flag.startswith( '/' ) and not os.path.exists( flag ) ) ): flags = flags[ index: ] break return [ 'clang-tool' ] + flags def BuildCompilationCommand( flags, filepath ): """Returns a compilation command from a list of flags and a file.""" enable_windows_style_flags = ShouldAllowWinStyleFlags( flags ) flags = PrependCompilerToFlags( flags, enable_windows_style_flags ) flags = RemoveUnusedFlags( flags, filepath, enable_windows_style_flags ) if OnMac(): flags = AddMacIncludePaths( flags ) return flags + [ filepath ] class ClangdCompleter( language_server_completer.LanguageServerCompleter ): """A LSP-based completer for C-family languages, powered by Clangd. Supported features: * Code completion * Diagnostics and apply FixIts * Go to definition """ def __init__( self, user_options ): super().__init__( user_options ) self._clangd_command = GetClangdCommand( user_options ) self._use_ycmd_caching = user_options[ 'clangd_uses_ycmd_caching' ] self._compilation_commands = {} self.RegisterOnFileReadyToParse( lambda self, request_data: self._SendFlagsFromExtraConf( request_data ) ) def _Reset( self ): super()._Reset() self._compilation_commands = {} def GetCompleterName( self ): return 'C-family' def GetServerName( self ): return 'Clangd' def GetCommandLine( self ): return self._clangd_command def Language( self ): return 'cfamily' def SupportedFiletypes( self ): return ( 'c', 'cpp', 'objc', 'objcpp', 'cuda' ) def GetType( self, request_data ): try: hover_value = self.GetHoverResponse( request_data )[ 'value' ] # Last "paragraph" contains the signature/declaration - i.e. type info. type_info = hover_value.split( '\n\n' )[ -1 ] # The first line might contain the info of enclosing scope. if type_info.startswith( '// In' ): comment, signature = type_info.split( '\n', 1 ) type_info = signature + '; ' + comment # Condense multi-line function declarations into one line. type_info = re.sub( r'\s+', ' ', type_info ) return responses.BuildDisplayMessageResponse( type_info ) except language_server_completer.NoHoverInfoException: raise RuntimeError( 'Unknown type.' ) def GetDoc( self, request_data ): try: # Just pull `value` out of the textDocument/hover response return responses.BuildDetailedInfoResponse( self.GetHoverResponse( request_data )[ 'value' ] ) except language_server_completer.NoHoverInfoException: raise RuntimeError( 'No documentation available.' ) def GetTriggerCharacters( self, server_trigger_characters ): # The trigger characters supplied by clangd are worse than ycmd's own # semantic triggers which are more sophisticated (regex-based). So we # ignore them. return [] def GetCustomSubcommands( self ): return { 'GetTypeImprecise': ( lambda self, request_data, args: self.GetType( request_data ) ), # NOTE: these two commands are only kept for backward compatibility with # the libclang completer. 'GoToImprecise': ( lambda self, request_data, args: self.GoTo( request_data, [ 'Definition' ] ) ), 'GoToInclude': ( lambda self, request_data, args: self.GoTo( request_data, [ 'Definition' ] ) ), 'GetDocImprecise': ( lambda self, request_data, args: self.GetDoc( request_data ) ), # To handle the commands below we need extensions to LSP. One way to # provide those could be to use workspace/executeCommand requset. # 'GetParent': ( # lambda self, request_data, args: self.GetType( request_data ) # ) } def ShouldCompleteIncludeStatement( self, request_data ): column_codepoint = request_data[ 'column_codepoint' ] - 1 current_line = request_data[ 'line_value' ] return bool( INCLUDE_REGEX.match( current_line[ : column_codepoint ] ) ) def ShouldUseNowInner( self, request_data ): return ( self.ServerIsReady() and ( super().ShouldUseNowInner( request_data ) or self.ShouldCompleteIncludeStatement( request_data ) ) ) def ShouldUseNow( self, request_data ): """Overridden to use Clangd filtering and sorting when ycmd caching is disabled.""" # Clangd should be able to provide completions in any context. # FIXME: Empty queries provide spammy results, fix this in Clangd. if self._use_ycmd_caching: return super().ShouldUseNow( request_data ) return ( request_data[ 'query' ] != '' or super().ShouldUseNowInner( request_data ) ) def ComputeCandidates( self, request_data ): """Overridden to bypass ycmd cache if disabled.""" # Caching results means resorting them, and ycmd has fewer signals. if self._use_ycmd_caching: return super().ComputeCandidates( request_data ) codepoint = request_data[ 'column_codepoint' ] candidates, _ = super().ComputeCandidatesInner( request_data, codepoint ) return candidates def _SendFlagsFromExtraConf( self, request_data ): """Reads the flags from the extra conf of the given request and sends them to Clangd as an entry of a compilation database using the 'compilationDatabaseChanges' configuration.""" filepath = request_data[ 'filepath' ] with self._server_info_mutex: # Replicate the logic from flags.py _GetFlagsFromCompilationDatabase: # - if there's a local extra conf, use it # - otherwise if there's no database, try and use a global extra conf module = extra_conf_store.ModuleForSourceFile( filepath ) if not module: # No extra conf and no global extra conf. Just let clangd handle it. return if ( extra_conf_store.IsGlobalExtraConfModule( module ) and CompilationDatabaseExists( filepath ) ): # No local extra conf, database exists: use database (i.e. clangd) return # Use our module (either local extra conf or global extra conf when no # database is found) settings = self.GetSettings( module, request_data ) if 'flags' not in settings: # No flags returned. Let Clangd find the flags. return if ( settings.get( 'do_cache', True ) and filepath in self._compilation_commands ): # Flags for this file have already been sent to Clangd. return flags = BuildCompilationCommand( settings[ 'flags' ], filepath ) self.GetConnection().SendNotification( lsp.DidChangeConfiguration( { 'compilationDatabaseChanges': { filepath: { 'compilationCommand': flags, 'workingDirectory': settings.get( 'include_paths_relative_to_dir', self._project_directory ) } } } ) ) self._compilation_commands[ filepath ] = flags def ExtraDebugItems( self, request_data ): return [ responses.DebugInfoItem( 'Compilation Command', self._compilation_commands.get( request_data[ 'filepath' ], False ) ) ] def OnBufferVisit( self, request_data ): # In case a header has been changed, we need to make clangd reparse the TU. file_state = self._server_file_state[ request_data[ 'filepath' ] ] if file_state.state == lsp.ServerFileState.OPEN: msg = lsp.DidChangeTextDocument( file_state, None ) self.GetConnection().SendNotification( msg ) def CompilationDatabaseExists( file_dir ): for folder in PathsToAllParentFolders( file_dir ): if os.path.exists( os.path.join( folder, 'compile_commands.json' ) ): return True return False ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/cpp/ephemeral_values_set.py���������������������������0000664�0000000�0000000�00000004044�13746324601�0025602�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import threading ALREADY_PARSING_MESSAGE = 'File already being parsed.' # Holds a set of values in a python set. Trying to get an exclusive hold on a # provided value results in a context manager that manages the lifetime of the # value. # For the context manager, trying to enter it if the value is already held by a # different caller results in a RuntimeError. Otherwise an exclusive hold is # provided until the context manager is exited. # # Example usage: # paths = EphemeralValuesSet() # ... # with path as paths.GetExclusive('/foo'): # ... class EphemeralValuesSet: def __init__( self ): self._values = set() self._values_lock = threading.Lock() def GetExclusive( self, value ): return EphemeralValue( value, self._values, self._values_lock ) # Implements the Python context manager API. class EphemeralValue: def __init__( self, value, parent_set, parent_lock ): self._value = value self._parent_set = parent_set self._parent_lock = parent_lock def __enter__( self ): with self._parent_lock: if self._value in self._parent_set: # This also prevents execution of __exit__ raise RuntimeError( ALREADY_PARSING_MESSAGE ) self._parent_set.add( self._value ) return self._value def __exit__( self, *unused_args ): with self._parent_lock: self._parent_set.remove( self._value ) return False ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/cpp/flags.py������������������������������������������0000664�0000000�0000000�00000063040�13746324601�0022503�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2011-2020 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 <http://www.gnu.org/licenses/>. import os import inspect from ycmd import extra_conf_store from ycmd.utils import ( AbsolutePath, ImportCore, OnMac, OnWindows, PathsToAllParentFolders, re, ToUnicode, CLANG_RESOURCE_DIR ) from ycmd.responses import NoExtraConfDetected ycm_core = ImportCore() # -include-pch and --sysroot= must be listed before -include and --sysroot # respectively because the latter is a prefix of the former (and the algorithm # checks prefixes). INCLUDE_FLAGS = [ '-isystem', '-I', '-iquote', '-isysroot', '--sysroot', '-gcc-toolchain', '-include-pch', '-include', '-iframework', '-F', '-imacros', '-idirafter', '-B' ] INCLUDE_FLAGS_WIN_STYLE = [ '/I' ] PATH_FLAGS = [ '--sysroot=' ] + INCLUDE_FLAGS # We need to remove --fcolor-diagnostics because it will cause shell escape # sequences to show up in editors, which is bad. See Valloric/YouCompleteMe#1421 STATE_FLAGS_TO_SKIP = { '-c', '-MP', '-MD', '-MMD', '--fcolor-diagnostics' } STATE_FLAGS_TO_SKIP_WIN_STYLE = { '/c' } # The -M* flags spec: # https://gcc.gnu.org/onlinedocs/gcc-4.9.0/gcc/Preprocessor-Options.html FILE_FLAGS_TO_SKIP = { '-MF', '-MT', '-MQ', '-o', '--serialize-diagnostics' } # Use a regex to correctly detect c++/c language for both versioned and # non-versioned compiler executable names suffixes # (e.g., c++, g++, clang++, g++-4.9, clang++-3.7, c++-10.2 etc). # See Valloric/ycmd#266 CPP_COMPILER_REGEX = re.compile( r'\+\+(-\d+(\.\d+){0,2})?$' ) # Use a regex to match all the possible forms of clang-cl or cl compiler CL_COMPILER_REGEX = re.compile( r'(?:cl|clang-cl)(.exe)?$', re.IGNORECASE ) # List of file extensions to be considered "header" files and thus not present # in the compilation database. The logic will try and find an associated # "source" file (see SOURCE_EXTENSIONS below) and use the flags for that. HEADER_EXTENSIONS = [ '.h', '.hxx', '.hpp', '.hh', '.cuh' ] # List of file extensions which are considered "source" files for the purposes # of heuristically locating the flags for a header file. SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.cu', '.m', '.mm' ] EMPTY_FLAGS = { 'flags': [], } MAC_XCODE_TOOLCHAIN_DIR = ( '/Applications/Xcode.app/Contents/Developer/Toolchains' '/XcodeDefault.xctoolchain' ) MAC_COMMAND_LINE_TOOLCHAIN_DIR = '/Library/Developer/CommandLineTools' MAC_XCODE_SYSROOT = ( '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform' '/Developer/SDKs/MacOSX.sdk' ) MAC_COMMAND_LINE_SYSROOT = ( '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk' ) MAC_FOUNDATION_HEADERS_RELATIVE_DIR = ( 'System/Library/Frameworks/Foundation.framework/Headers' ) class Flags: """Keeps track of the flags necessary to compile a file. The flags are loaded from user-created python files (hereafter referred to as 'modules') that contain a method Settings( **kwargs ).""" def __init__( self ): # We cache the flags by a tuple of filename and client data. self.flags_for_file = {} self.no_extra_conf_file_warning_posted = False # We cache the compilation database for any given source directory # Keys are directory names and values are ycm_core.CompilationDatabase # instances or None. Value is None when it is known there is no compilation # database to be found for the directory. self.compilation_database_dir_map = {} def FlagsForFile( self, filename, add_extra_clang_flags = True, client_data = None ): """Returns a tuple describing the compiler invocation required to parse the file |filename|. The tuple contains 2 entries: 1. A list of the compiler flags to use, 2. The name of the translation unit to parse. Note that the second argument might not be the same as the |filename| argument to this method in the event that the extra conf file overrides the translation unit, e.g. in the case of a "unity" build.""" # The try-catch here is to avoid a synchronisation primitive. This method # may be called from multiple threads, and python gives us # 1-python-statement synchronisation for "free" (via the GIL) try: return self.flags_for_file[ filename, client_data ] except KeyError: pass results = self._GetFlagsFromExtraConfOrDatabase( filename, client_data ) if not results.get( 'flags_ready', True ): return [], filename return self._ParseFlagsFromExtraConfOrDatabase( filename, results, add_extra_clang_flags, client_data ) def _ParseFlagsFromExtraConfOrDatabase( self, filename, results, add_extra_clang_flags, client_data ): if 'override_filename' in results: filename = results[ 'override_filename' ] or filename flags = _ExtractFlagsList( results ) if not flags: return [], filename sanitized_flags = PrepareFlagsForClang( flags, filename, add_extra_clang_flags, ShouldAllowWinStyleFlags( flags ) ) if results.get( 'do_cache', True ): self.flags_for_file[ filename, client_data ] = sanitized_flags, filename return sanitized_flags, filename def _GetFlagsFromExtraConfOrDatabase( self, filename, client_data ): # Load the flags from the extra conf file if one is found and is not global. module = extra_conf_store.ModuleForSourceFile( filename ) if module and not extra_conf_store.IsGlobalExtraConfModule( module ): return _CallExtraConfFlagsForFile( module, filename, client_data ) # Load the flags from the compilation database if any. database = self.LoadCompilationDatabase( filename ) if database: return self._GetFlagsFromCompilationDatabase( database, filename ) # Load the flags from the global extra conf if set. if module: return _CallExtraConfFlagsForFile( module, filename, client_data ) # No compilation database and no extra conf found. Warn the user if not # already warned. if not self.no_extra_conf_file_warning_posted: self.no_extra_conf_file_warning_posted = True raise NoExtraConfDetected return EMPTY_FLAGS def Clear( self ): self.flags_for_file.clear() self.compilation_database_dir_map.clear() def _GetFlagsFromCompilationDatabase( self, database, file_name ): _, file_extension = os.path.splitext( file_name ) compilation_info = _GetCompilationInfoForFile( database, file_name, file_extension ) if not compilation_info: # No flags for this file in the database. return EMPTY_FLAGS return { 'flags': _MakeRelativePathsInFlagsAbsolute( compilation_info.compiler_flags_, compilation_info.compiler_working_dir_ ), } # Return a compilation database object for the supplied path or None if no # compilation database is found. def LoadCompilationDatabase( self, file_dir ): # We search up the directory hierarchy, to first see if we have a # compilation database already for that path, or if a compile_commands.json # file exists in that directory. for folder in PathsToAllParentFolders( file_dir ): # Try/catch to synchronise access to cache try: return self.compilation_database_dir_map[ folder ] except KeyError: pass compile_commands = os.path.join( folder, 'compile_commands.json' ) if os.path.exists( compile_commands ): database = ycm_core.CompilationDatabase( folder ) if database.DatabaseSuccessfullyLoaded(): self.compilation_database_dir_map[ folder ] = database return database # Nothing was found. No compilation flags are available. # Note: we cache the fact that none was found for this folder to speed up # subsequent searches. self.compilation_database_dir_map[ file_dir ] = None return None def _ExtractFlagsList( flags_for_file_output ): return [ ToUnicode( x ) for x in flags_for_file_output[ 'flags' ] ] def ShouldAllowWinStyleFlags( flags ): if OnWindows(): # Iterate in reverse because we only care # about the last occurrence of --driver-mode flag. for flag in reversed( flags ): if flag.startswith( '--driver-mode' ): return flag == '--driver-mode=cl' # If there was no --driver-mode flag, # check if we are using a compiler like clang-cl. return bool( CL_COMPILER_REGEX.search( flags[ 0 ] ) ) return False def _CallExtraConfFlagsForFile( module, filename, client_data ): filename = ToUnicode( filename ) if hasattr( module, 'Settings' ): results = module.Settings( language = 'cfamily', filename = filename, client_data = client_data ) # For the sake of backwards compatibility, we need to first check whether the # FlagsForFile function in the extra conf module even allows keyword args. elif inspect.getfullargspec( module.FlagsForFile ).varkw: results = module.FlagsForFile( filename, client_data = client_data ) else: results = module.FlagsForFile( filename ) if not isinstance( results, dict ) or 'flags' not in results: return EMPTY_FLAGS results[ 'flags' ] = _MakeRelativePathsInFlagsAbsolute( results[ 'flags' ], results.get( 'include_paths_relative_to_dir' ) ) return results def PrepareFlagsForClang( flags, filename, add_extra_clang_flags = True, enable_windows_style_flags = False ): flags = _AddLanguageFlagWhenAppropriate( flags, enable_windows_style_flags ) flags = _RemoveXclangFlags( flags ) flags = RemoveUnusedFlags( flags, filename, enable_windows_style_flags ) if add_extra_clang_flags: # This flag tells libclang where to find the builtin includes. flags.append( '-resource-dir=' + CLANG_RESOURCE_DIR ) # On Windows, parsing of templates is delayed until instantiation time. # This makes GetType and GetParent commands fail to return the expected # result when the cursor is in a template. # Using the -fno-delayed-template-parsing flag disables this behavior. See # http://clang.llvm.org/extra/PassByValueTransform.html#note-about-delayed-template-parsing # noqa # for an explanation of the flag and # https://code.google.com/p/include-what-you-use/source/detail?r=566 # for a similar issue. if OnWindows(): flags.append( '-fno-delayed-template-parsing' ) if OnMac(): flags = AddMacIncludePaths( flags ) flags = _EnableTypoCorrection( flags ) vector = ycm_core.StringVector() for flag in flags: vector.append( flag ) return vector def _RemoveXclangFlags( flags ): """Drops -Xclang flags. These are typically used to pass in options to clang cc1 which are not used in the front-end, so they are not needed for code completion.""" sanitized_flags = [] saw_xclang = False for flag in flags: if flag == '-Xclang': saw_xclang = True continue elif saw_xclang: saw_xclang = False continue sanitized_flags.append( flag ) return sanitized_flags def _RemoveFlagsPrecedingCompiler( flags, enable_windows_style_flags ): """Assuming that the flag just before the first flag (looks like a flag, not like a file path) is the compiler path, removes all flags preceding it.""" for index, flag in enumerate( flags ): if ( flag.startswith( '-' ) or ( enable_windows_style_flags and flag.startswith( '/' ) and not os.path.exists( flag ) ) ): return ( flags[ index - 1: ] if index > 1 else flags ) return flags[ :-1 ] def _AddLanguageFlagWhenAppropriate( flags, enable_windows_style_flags ): """When flags come from the compile_commands.json file, the flag preceding the first flag starting with a dash is usually the path to the compiler that should be invoked. Since LibClang does not deduce the language from the compiler name, we explicitly set the language to C++ if the compiler is a C++ one (g++, clang++, etc.). We also set the language to CUDA if any of the source files has a .cu or .cuh extension. Otherwise, we let LibClang guess the language from the file extension. This handles the case where the .h extension is used for C++ headers.""" flags = _RemoveFlagsPrecedingCompiler( flags, enable_windows_style_flags ) # First flag is now the compiler path, a flag starting with a dash or # a flag starting with a forward slash if enable_windows_style_flags is True. first_flag = flags[ 0 ] # Because of _RemoveFlagsPrecedingCompiler called above, irrelevant of # enable_windows_style_flags. the first flag is either the compiler # (path or executable), a Windows style flag or starts with a dash. if first_flag.startswith( '-' ): return flags # Explicitly set the language to CUDA to avoid setting it to C++ when # compiling CUDA source files with a C++ compiler if any( fl.endswith( '.cu' ) or fl.endswith( '.cuh' ) for fl in reversed( flags ) ): return [ first_flag, '-x', 'cuda' ] + flags[ 1: ] # NOTE: This is intentionally NOT checking for enable_windows_style_flags. # # The first flag is now either an absolute path, a Windows style flag or a # C++ compiler executable from $PATH. # If it starts with a forward slash the flag can either be an absolute # flag or a Windows style flag. # If it matches the regex, it is safe to assume the flag is a compiler # path. # If it does not match the regex, it could still be a Windows style # path or an absolute path. - This is determined in _RemoveUnusedFlags() # and cleaned properly. # If the flag starts with anything else (i.e. not a '-' or a '/'), the flag # is a stray file path and shall be gotten rid of in _RemoveUnusedFlags(). if CPP_COMPILER_REGEX.search( first_flag ): return [ first_flag, '-x', 'c++' ] + flags[ 1: ] return flags def RemoveUnusedFlags( flags, filename, enable_windows_style_flags ): """Given an iterable object that produces strings (flags for Clang), removes the '-c' and '-o' options that Clang does not like to see when it's producing completions for a file. Same for '-MD' etc. We also try to remove any stray filenames in the flags that aren't include dirs.""" new_flags = [] # When flags come from the compile_commands.json file, the first flag is # usually the path to the compiler that should be invoked. Directly move it to # the new_flags list so it doesn't get stripped of in the loop below. if not flags[ 0 ].startswith( '-' ): new_flags = flags[ :1 ] flags = flags[ 1: ] skip_next = False current_flag = flags[ 0 ] filename = os.path.realpath( filename ) for flag in flags: previous_flag = current_flag current_flag = flag if skip_next: skip_next = False continue if ( flag in STATE_FLAGS_TO_SKIP or ( enable_windows_style_flags and flag in STATE_FLAGS_TO_SKIP_WIN_STYLE ) ): continue if flag in FILE_FLAGS_TO_SKIP: skip_next = True continue if os.path.realpath( flag ) == filename: continue # We want to make sure that we don't have any stray filenames in our flags; # filenames that are part of include flags are ok, but others are not. This # solves the case where we ask the compilation database for flags for # "foo.cpp" when we are compiling "foo.h" because the comp db doesn't have # flags for headers. The returned flags include "foo.cpp" and we need to # remove that. if _SkipStrayFilenameFlag( current_flag, previous_flag, enable_windows_style_flags ): continue new_flags.append( flag ) return new_flags def _SkipStrayFilenameFlag( current_flag, previous_flag, enable_windows_style_flags ): current_flag_starts_with_slash = current_flag.startswith( '/' ) previous_flag_starts_with_slash = previous_flag.startswith( '/' ) current_flag_starts_with_dash = current_flag.startswith( '-' ) previous_flag_starts_with_dash = previous_flag.startswith( '-' ) previous_flag_is_include = ( previous_flag in INCLUDE_FLAGS or ( enable_windows_style_flags and previous_flag in INCLUDE_FLAGS_WIN_STYLE ) ) current_flag_may_be_path = ( '/' in current_flag or ( enable_windows_style_flags and '\\' in current_flag ) ) return ( not ( current_flag_starts_with_dash or ( enable_windows_style_flags and current_flag_starts_with_slash ) ) and ( not ( previous_flag_starts_with_dash or ( enable_windows_style_flags and previous_flag_starts_with_slash ) ) or ( not previous_flag_is_include and current_flag_may_be_path ) ) ) def _GetMacSysRoot(): # Since macOS 10.14, the root framework directories do not contain the # headers. Instead of relying on the macOS version, check if the Headers # directory of a common framework (Foundation) exists. If it does, return the # base directory as the default sysroot. for sysroot in [ '/', MAC_XCODE_SYSROOT, MAC_COMMAND_LINE_SYSROOT ]: if os.path.exists( os.path.join( sysroot, MAC_FOUNDATION_HEADERS_RELATIVE_DIR ) ): return sysroot # No headers found. Use the root directory anyway. return '/' def _ExtractInfoForMacIncludePaths( flags ): language = 'c++' use_libcpp = True sysroot = _GetMacSysRoot() isysroot = None previous_flag = None for current_flag in flags: if previous_flag == '-x': language = current_flag if current_flag.startswith( '-x' ): language = current_flag[ 2: ] if current_flag.startswith( '-stdlib=' ): use_libcpp = current_flag[ 8: ] == 'libc++' if previous_flag == '--sysroot': sysroot = current_flag if current_flag.startswith( '--sysroot=' ): sysroot = current_flag[ 10: ] if previous_flag == '-isysroot': isysroot = current_flag if current_flag.startswith( '-isysroot' ): isysroot = current_flag[ 9: ] previous_flag = current_flag # -isysroot takes precedence over --sysroot. if isysroot: sysroot = isysroot language_is_cpp = language in { 'c++', 'objective-c++' } return language_is_cpp, use_libcpp, sysroot def _FindMacToolchain(): for toolchain in [ MAC_XCODE_TOOLCHAIN_DIR, MAC_COMMAND_LINE_TOOLCHAIN_DIR ]: if os.path.exists( toolchain ): return toolchain return None # We can't rely on upstream libclang to find the system headers on macOS 10.14 # as it's unable to locate the framework headers without setting the sysroot if # Command Line Tools is not installed. So, we try to reproduce the logic used by # Apple Clang to find the system headers by looking at the output of the command # # clang++ -x c++ -E -v - # # which prints the list of system header directories and by reading the source # code of upstream Clang: # https://github.com/llvm-mirror/clang/blob/2709c8b804eb38dbdc8ae05b8fcf4f95c01b4102/lib/Frontend/InitHeaderSearch.cpp#L453-L510 # This has also the benefit of allowing completion of system header paths and # navigation to these headers when the cursor is on an include statement. def AddMacIncludePaths( flags ): use_standard_cpp_includes = '-nostdinc++' not in flags use_standard_system_includes = '-nostdinc' not in flags use_builtin_includes = '-nobuiltininc' not in flags language_is_cpp, use_libcpp, sysroot = _ExtractInfoForMacIncludePaths( flags ) toolchain = _FindMacToolchain() if ( language_is_cpp and use_standard_cpp_includes and use_standard_system_includes and use_libcpp ): if toolchain: flags.extend( [ '-isystem', os.path.join( toolchain, 'usr/include/c++/v1' ) ] ) flags.extend( [ '-isystem', os.path.join( sysroot, 'usr/include/c++/v1' ) ] ) if use_standard_system_includes: flags.extend( [ '-isystem', os.path.join( sysroot, 'usr/local/include' ) ] ) # Apple Clang always adds /usr/local/include to the list of system header # directories even if sysroot is not the root directory. if sysroot != '/': flags.extend( [ '-isystem', '/usr/local/include' ] ) if use_builtin_includes: flags.extend( [ '-isystem', os.path.join( CLANG_RESOURCE_DIR, 'include' ) ] ) if use_standard_system_includes: if toolchain: flags.extend( [ '-isystem', os.path.join( toolchain, 'usr/include' ) ] ) flags.extend( [ '-isystem', os.path.join( sysroot, 'usr/include' ), '-iframework', os.path.join( sysroot, 'System/Library/Frameworks' ), '-iframework', os.path.join( sysroot, 'Library/Frameworks' ) ] ) return flags def _EnableTypoCorrection( flags ): """Adds the -fspell-checking flag if the -fno-spell-checking flag is not present""" # "Typo correction" (aka spell checking) in clang allows it to produce # hints (in the form of fix-its) in the case of certain diagnostics. A common # example is "no type named 'strng' in namespace 'std'; Did you mean # 'string'? (FixIt)". This is enabled by default in the clang driver (i.e. the # 'clang' binary), but is not when using libclang (as we do). It's a useful # enough feature that we just always turn it on unless the user explicitly # turned it off in their flags (with -fno-spell-checking). if '-fno-spell-checking' in flags: return flags flags.append( '-fspell-checking' ) return flags def _MakeRelativePathsInFlagsAbsolute( flags, working_directory ): if not working_directory: return list( flags ) new_flags = [] make_next_absolute = False path_flags = ( PATH_FLAGS + INCLUDE_FLAGS_WIN_STYLE if ShouldAllowWinStyleFlags( flags ) else PATH_FLAGS ) for flag in flags: new_flag = flag if make_next_absolute: make_next_absolute = False new_flag = AbsolutePath( flag, working_directory ) else: for path_flag in path_flags: # Single dash argument alone, e.g. -isysroot <path> if flag == path_flag: make_next_absolute = True break # Single dash argument with inbuilt path, e.g. -isysroot<path> # or double-dash argument, e.g. --isysroot=<path> if flag.startswith( path_flag ): path = flag[ len( path_flag ): ] path = AbsolutePath( path, working_directory ) new_flag = f'{ path_flag }{ path }' break if new_flag: new_flags.append( new_flag ) return new_flags # Find the compilation info structure from the supplied database for the # supplied file. If the source file is a header, try and find an appropriate # source file and return the compilation_info for that. def _GetCompilationInfoForFile( database, file_name, file_extension ): # Ask the database for the flags. compilation_info = database.GetCompilationInfoForFile( file_name ) if compilation_info.compiler_flags_: return compilation_info return None def UserIncludePaths( user_flags, filename ): """ Returns a tuple ( quoted_include_paths, include_paths ) quoted_include_paths is a list of include paths that are only suitable for quoted include statement. include_paths is a list of include paths that can be used for angle bracketed and quoted include statement. """ quoted_include_paths = [ ToUnicode( os.path.dirname( filename ) ) ] include_paths = [] framework_paths = [] if user_flags: include_flags = { '-iquote': quoted_include_paths, '-I': include_paths, '-isystem': include_paths, '-F': framework_paths, '-iframework': framework_paths } if ShouldAllowWinStyleFlags( user_flags ): include_flags[ '/I' ] = include_paths try: it = iter( user_flags ) for user_flag in it: user_flag_len = len( user_flag ) for flag in include_flags: if user_flag.startswith( flag ): flag_len = len( flag ) include_path = ( next( it ) if user_flag_len == flag_len else user_flag[ flag_len: ] ) if include_path: container = include_flags[ flag ] container.append( ToUnicode( include_path ) ) break except StopIteration: pass return quoted_include_paths, include_paths, framework_paths ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/cpp/hook.py�������������������������������������������0000664�0000000�0000000�00000002223�13746324601�0022343�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from ycmd.completers.cpp.clang_completer import ClangCompleter from ycmd.completers.cpp.clangd_completer import ( ShouldEnableClangdCompleter, ClangdCompleter ) from ycmd.utils import ImportCore ycm_core = ImportCore() def GetCompleter( user_options ): if ShouldEnableClangdCompleter( user_options ): return ClangdCompleter( user_options ) if ycm_core.HasClangSupport(): return ClangCompleter( user_options ) return None �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/cpp/include_cache.py����������������������������������0000664�0000000�0000000�00000007367�13746324601�0024167�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2017 Davit Samvelyan davitsamvelyan@gmail.com # Synopsys. # 2020 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 <http://www.gnu.org/licenses/>. import os import threading from collections import defaultdict, namedtuple from ycmd import responses from ycmd.completers.general.filename_completer import ( GetPathType, GetPathTypeName ) from ycmd.utils import GetModificationTime, ListDirectory """ Represents single include completion candidate. name is the name/string of the completion candidate, entry_type is an integer indicating whether the candidate is a 'File', 'Dir' or both (See EXTRA_INFO_MAP in filename_completer). """ IncludeEntry = namedtuple( 'IncludeEntry', [ 'name', 'entry_type' ] ) class IncludeList: """ Helper class for combining include completion candidates from several include paths. self._includes is a dictionary whose keys are IncludeEntry `name`s and values are IncludeEntry `entry_type`s. """ def __init__( self ): self._includes = defaultdict( int ) def AddIncludes( self, includes ): for include in includes: self._includes[ include.name ] |= include.entry_type def GetIncludes( self ): includes = [] for name, include_type in self._includes.items(): includes.append( responses.BuildCompletionData( name, GetPathTypeName( include_type ) ) ) return includes class IncludeCache: """ Holds a dictionary representing the include path cache. Dictionary keys are the include path directories. Dictionary values are tuples whose first object represents `mtime` of the dictionary key and the other object is an IncludeList. """ def __init__( self ): self._cache = {} self._cache_lock = threading.Lock() def GetIncludes( self, path, is_framework = False ): includes = self._GetCached( path, is_framework ) if includes is None: includes = self._ListIncludes( path, is_framework ) self._AddToCache( path, includes ) return includes def _AddToCache( self, path, includes, mtime = None ): if not mtime: mtime = GetModificationTime( path ) # mtime of 0 is "a magic value" to represent inaccessible directory mtime. if mtime: with self._cache_lock: self._cache[ path ] = { 'mtime': mtime, 'includes': includes } def _GetCached( self, path, is_framework ): includes = None with self._cache_lock: cache_entry = self._cache.get( path ) if cache_entry: mtime = GetModificationTime( path ) if mtime > cache_entry[ 'mtime' ]: includes = self._ListIncludes( path, is_framework ) self._AddToCache( path, includes, mtime ) else: includes = cache_entry[ 'includes' ] return includes def _ListIncludes( self, path, is_framework ): includes = [] for name in ListDirectory( path ): if is_framework: if not name.endswith( '.framework' ): continue name = name[ : -len( '.framework' ) ] inc_path = os.path.join( path, name ) entry_type = GetPathType( inc_path, is_framework ) includes.append( IncludeEntry( name, entry_type ) ) return includes �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/cs/���������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0020655�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/cs/__init__.py����������������������������������������0000664�0000000�0000000�00000000000�13746324601�0022754�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/cs/cs_completer.py������������������������������������0000664�0000000�0000000�00000101146�13746324601�0023711�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2011-2020 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 <http://www.gnu.org/licenses/>. from collections import defaultdict import os import errno import time import requests import threading from urllib.parse import urljoin from ycmd.completers.completer import Completer from ycmd.completers.completer_utils import GetFileLines from ycmd.completers.cs import solutiondetection from ycmd.utils import ( ByteOffsetToCodepointOffset, CodepointOffsetToByteOffset, FindExecutable, FindExecutableWithFallback, LOGGER ) from ycmd import responses from ycmd import utils SERVER_NOT_FOUND_MSG = ( 'OmniSharp server binary not found at {0}. ' + 'Did you compile it? You can do so by running ' + '"./install.py --cs-completer".' ) INVALID_FILE_MESSAGE = 'File is invalid.' NO_DIAGNOSTIC_MESSAGE = 'No diagnostic for current line!' PATH_TO_ROSLYN_OMNISHARP = os.path.join( os.path.abspath( os.path.dirname( __file__ ) ), '..', '..', '..', 'third_party', 'omnisharp-roslyn' ) PATH_TO_OMNISHARP_ROSLYN_BINARY = os.path.join( PATH_TO_ROSLYN_OMNISHARP, 'Omnisharp.exe' ) if ( not os.path.isfile( PATH_TO_OMNISHARP_ROSLYN_BINARY ) and os.path.isfile( os.path.join( PATH_TO_ROSLYN_OMNISHARP, 'omnisharp', 'OmniSharp.exe' ) ) ): PATH_TO_OMNISHARP_ROSLYN_BINARY = ( os.path.join( PATH_TO_ROSLYN_OMNISHARP, 'omnisharp', 'OmniSharp.exe' ) ) LOGFILE_FORMAT = 'omnisharp_{port}_{sln}_{std}_' def ShouldEnableCsCompleter( user_options ): user_roslyn_path = user_options[ 'roslyn_binary_path' ] if user_roslyn_path and not os.path.isfile( user_roslyn_path ): LOGGER.error( 'No omnisharp-roslyn executable at %s', user_roslyn_path ) # We should trust the user who specifically asked for a custom path. return False if os.path.isfile( user_roslyn_path ): roslyn = user_roslyn_path else: roslyn = PATH_TO_OMNISHARP_ROSLYN_BINARY mono = FindExecutableWithFallback( user_options[ 'mono_binary_path' ], FindExecutable( 'mono' ) ) if roslyn and ( mono or utils.OnWindows() ): return True LOGGER.info( 'No mono executable at %s', mono ) return False class CsharpCompleter( Completer ): """ A Completer that uses the Omnisharp server as completion engine. """ def __init__( self, user_options ): super().__init__( user_options ) self._solution_for_file = {} self._completer_per_solution = {} self._diagnostic_store = None self._solution_state_lock = threading.Lock() self.SetSignatureHelpTriggers( [ '(', ',' ] ) if os.path.isfile( user_options[ 'roslyn_binary_path' ] ): self._roslyn_path = user_options[ 'roslyn_binary_path' ] else: self._roslyn_path = PATH_TO_OMNISHARP_ROSLYN_BINARY self._mono_path = FindExecutableWithFallback( user_options[ 'mono_binary_path' ], FindExecutable( 'mono' ) ) def Shutdown( self ): if self.user_options[ 'auto_stop_csharp_server' ]: for solutioncompleter in self._completer_per_solution.values(): solutioncompleter._StopServer() def SupportedFiletypes( self ): """ Just csharp """ return [ 'cs' ] def _GetSolutionCompleter( self, request_data ): """ Get the solution completer or create a new one if it does not already exist. Use a lock to avoid creating the same solution completer multiple times.""" solution = self._GetSolutionFile( request_data[ "filepath" ] ) with self._solution_state_lock: if solution not in self._completer_per_solution: keep_logfiles = self.user_options[ 'server_keep_logfiles' ] desired_omnisharp_port = self.user_options.get( 'csharp_server_port' ) completer = CsharpSolutionCompleter( solution, keep_logfiles, desired_omnisharp_port, self._roslyn_path, self._mono_path ) self._completer_per_solution[ solution ] = completer return self._completer_per_solution[ solution ] def SignatureHelpAvailable( self ): if not self.ServerIsHealthy(): return responses.SignatureHelpAvailalability.PENDING return responses.SignatureHelpAvailalability.AVAILABLE def ComputeSignaturesInner( self, request_data ): response = self._SolutionSubcommand( request_data, '_SignatureHelp' ) if response is None: return {} signatures = response[ 'Signatures' ] def MakeSignature( s ): sig_label = s[ 'Label' ] end = 0 parameters = [] for arg in s[ 'Parameters' ]: arg_label = arg[ 'Label' ] begin = sig_label.find( arg_label, end ) end = begin + len( arg_label ) parameters.append( { 'label': [ CodepointOffsetToByteOffset( sig_label, begin ), CodepointOffsetToByteOffset( sig_label, end ) ] } ) return { 'label': sig_label, 'parameters': parameters } return { 'activeSignature': response[ 'ActiveSignature' ], 'activeParameter': response[ 'ActiveParameter' ], 'signatures': [ MakeSignature( s ) for s in signatures ] } def ResolveFixit( self, request_data ): return self._SolutionSubcommand( request_data, '_ResolveFixIt' ) def ComputeCandidatesInner( self, request_data ): solutioncompleter = self._GetSolutionCompleter( request_data ) return [ responses.BuildCompletionData( completion[ 'CompletionText' ], completion[ 'DisplayText' ], completion[ 'Description' ], None, completion[ 'Kind' ] ) for completion in solutioncompleter._GetCompletions( request_data ) ] def GetSubcommandsMap( self ): return { 'StopServer' : ( lambda self, request_data, args: self._SolutionSubcommand( request_data, method = '_StopServer', no_request_data = True ) ), 'RestartServer' : ( lambda self, request_data, args: self._SolutionSubcommand( request_data, method = '_RestartServer', no_request_data = True ) ), 'GoToDefinition' : ( lambda self, request_data, args: self._SolutionSubcommand( request_data, method = '_GoToDefinition' ) ), 'GoToDeclaration' : ( lambda self, request_data, args: self._SolutionSubcommand( request_data, method = '_GoToDefinition' ) ), 'GoTo' : ( lambda self, request_data, args: self._SolutionSubcommand( request_data, method = '_GoToImplementation', fallback_to_declaration = True ) ), 'GoToDefinitionElseDeclaration' : ( lambda self, request_data, args: self._SolutionSubcommand( request_data, method = '_GoToDefinition' ) ), 'GoToReferences' : ( lambda self, request_data, args: self._SolutionSubcommand( request_data, method = '_GoToReferences' ) ), 'GoToImplementation' : ( lambda self, request_data, args: self._SolutionSubcommand( request_data, method = '_GoToImplementation', fallback_to_declaration = False ) ), 'GoToImplementationElseDeclaration': ( lambda self, request_data, args: self._SolutionSubcommand( request_data, method = '_GoToImplementation', fallback_to_declaration = True ) ), 'GetType' : ( lambda self, request_data, args: self._SolutionSubcommand( request_data, method = '_GetType' ) ), 'Format' : ( lambda self, request_data, args: self._SolutionSubcommand( request_data, method = '_Format' ) ), 'FixIt' : ( lambda self, request_data, args: self._SolutionSubcommand( request_data, method = '_FixIt' ) ), 'GetDoc' : ( lambda self, request_data, args: self._SolutionSubcommand( request_data, method = '_GetDoc' ) ), 'GoToSymbol' : ( lambda self, request_data, args: self._SolutionSubcommand( request_data, method = '_GoToSymbol', args = args ) ), 'RefactorRename' : ( lambda self, request_data, args: self._SolutionSubcommand( request_data, method = '_RefactorRename', args = args ) ), } def _SolutionSubcommand( self, request_data, method, no_request_data = False, **kwargs ): solutioncompleter = self._GetSolutionCompleter( request_data ) if not no_request_data: kwargs[ 'request_data' ] = request_data return getattr( solutioncompleter, method )( **kwargs ) def OnFileReadyToParse( self, request_data ): solutioncompleter = self._GetSolutionCompleter( request_data ) # Only start the server associated to this solution if the option to # automatically start one is set and no server process is already running. if ( self.user_options[ 'auto_start_csharp_server' ] and not solutioncompleter._ServerIsRunning() ): solutioncompleter._StartServer() return # Bail out if the server is unresponsive. We don't start or restart the # server in this case because current one may still be warming up. if not solutioncompleter.ServerIsHealthy(): return errors = solutioncompleter.CodeCheck( request_data ) diagnostics = [ self._QuickFixToDiagnostic( request_data, x ) for x in errors[ "QuickFixes" ] ] self._diagnostic_store = DiagnosticsToDiagStructure( diagnostics ) return responses.BuildDiagnosticResponse( diagnostics, request_data[ 'filepath' ], self.max_diagnostics_to_display ) def _QuickFixToDiagnostic( self, request_data, quick_fix ): filename = quick_fix[ "FileName" ] # NOTE: end of diagnostic range returned by the OmniSharp server is not # included. location = _BuildLocation( request_data, filename, quick_fix[ 'Line' ], quick_fix[ 'Column' ] ) location_end = _BuildLocation( request_data, filename, quick_fix[ 'EndLine' ], quick_fix[ 'EndColumn' ] ) if not location_end: location_end = location location_extent = responses.Range( location, location_end ) return responses.Diagnostic( [], location, location_extent, quick_fix[ 'Text' ], quick_fix[ 'LogLevel' ].upper() ) def GetDetailedDiagnostic( self, request_data ): current_line = request_data[ 'line_num' ] current_column = request_data[ 'column_num' ] current_file = request_data[ 'filepath' ] if not self._diagnostic_store: raise ValueError( NO_DIAGNOSTIC_MESSAGE ) diagnostics = self._diagnostic_store[ current_file ][ current_line ] if not diagnostics: raise ValueError( NO_DIAGNOSTIC_MESSAGE ) closest_diagnostic = None distance_to_closest_diagnostic = 999 # FIXME: all of these calculations are currently working with byte # offsets, which are technically incorrect. We should be working with # codepoint offsets, as we want the nearest character-wise diagnostic for diagnostic in diagnostics: distance = abs( current_column - diagnostic.location_.column_number_ ) if distance < distance_to_closest_diagnostic: distance_to_closest_diagnostic = distance closest_diagnostic = diagnostic return responses.BuildDisplayMessageResponse( closest_diagnostic.text_ ) def DebugInfo( self, request_data ): try: completer = self._GetSolutionCompleter( request_data ) except RuntimeError: omnisharp_server = responses.DebugInfoServer( name = 'OmniSharp', handle = None, executable = self._roslyn_path ) return responses.BuildDebugInfoResponse( name = 'C#', servers = [ omnisharp_server ] ) with completer._server_state_lock: solution_item = responses.DebugInfoItem( key = 'solution', value = completer._solution_path ) omnisharp_server = responses.DebugInfoServer( name = 'OmniSharp', handle = completer._omnisharp_phandle, executable = PATH_TO_ROSLYN_OMNISHARP, address = 'localhost', port = completer._omnisharp_port, logfiles = [ completer._filename_stdout, completer._filename_stderr ], extras = [ solution_item ] ) return responses.BuildDebugInfoResponse( name = 'C#', servers = [ omnisharp_server ] ) def ServerIsHealthy( self ): """ Check if our OmniSharp server is healthy (up and serving). """ return self._CheckAllRunning( lambda i: i.ServerIsHealthy() ) def ServerIsReady( self ): """ Check if our OmniSharp server is ready (loaded solution file).""" return self._CheckAllRunning( lambda i: i.ServerIsReady() ) def _CheckAllRunning( self, action ): solutioncompleters = self._completer_per_solution.values() return all( action( completer ) for completer in solutioncompleters if completer._ServerIsRunning() ) def _GetSolutionFile( self, filepath ): if filepath not in self._solution_for_file: # NOTE: detection could throw an exception if an extra_conf_store needs # to be confirmed path_to_solutionfile = solutiondetection.FindSolutionPath( filepath ) if not path_to_solutionfile: raise RuntimeError( 'Autodetection of solution file failed.' ) self._solution_for_file[ filepath ] = path_to_solutionfile return self._solution_for_file[ filepath ] class CsharpSolutionCompleter( object ): def __init__( self, solution_path, keep_logfiles, desired_omnisharp_port, roslyn_path, mono_path ): self._solution_path = solution_path self._keep_logfiles = keep_logfiles self._filename_stderr = None self._filename_stdout = None self._omnisharp_port = None self._omnisharp_phandle = None self._desired_omnisharp_port = desired_omnisharp_port self._server_state_lock = threading.Lock() self._roslyn_path = roslyn_path self._mono_path = mono_path def CodeCheck( self, request_data ): filename = request_data[ 'filepath' ] if not filename: raise ValueError( INVALID_FILE_MESSAGE ) return self._GetResponse( '/codecheck', self._DefaultParameters( request_data ) ) def _StartServer( self ): with self._server_state_lock: return self._StartServerNoLock() def _StartServerNoLock( self ): """ Start the OmniSharp server if not already running. Use a lock to avoid starting the server multiple times for the same solution. """ if self._ServerIsRunning(): return LOGGER.info( 'Starting OmniSharp server' ) LOGGER.info( 'Loading solution file %s', self._solution_path ) self._ChooseOmnisharpPort() command = [ PATH_TO_OMNISHARP_ROSLYN_BINARY, '-p', str( self._omnisharp_port ), '-s', str( self._solution_path ) ] if ( not utils.OnWindows() and self._roslyn_path.endswith( '.exe' ) ): command.insert( 0, self._mono_path ) LOGGER.info( 'Starting OmniSharp server with: %s', command ) solutionfile = os.path.basename( self._solution_path ) self._filename_stdout = utils.CreateLogfile( LOGFILE_FORMAT.format( port = self._omnisharp_port, sln = solutionfile, std = 'stdout' ) ) self._filename_stderr = utils.CreateLogfile( LOGFILE_FORMAT.format( port = self._omnisharp_port, sln = solutionfile, std = 'stderr' ) ) with utils.OpenForStdHandle( self._filename_stderr ) as fstderr: with utils.OpenForStdHandle( self._filename_stdout ) as fstdout: self._omnisharp_phandle = utils.SafePopen( command, stdout = fstdout, stderr = fstderr ) LOGGER.info( 'Started OmniSharp server' ) def _StopServer( self ): with self._server_state_lock: return self._StopServerNoLock() def _StopServerNoLock( self ): """ Stop the OmniSharp server using a lock. """ if self._ServerIsRunning(): LOGGER.info( 'Stopping OmniSharp server with PID %s', self._omnisharp_phandle.pid ) try: self._TryToStopServer() self._ForceStopServer() utils.WaitUntilProcessIsTerminated( self._omnisharp_phandle, timeout = 5 ) LOGGER.info( 'OmniSharp server stopped' ) except Exception: LOGGER.exception( 'Error while stopping OmniSharp server' ) self._CleanUp() def _TryToStopServer( self ): for _ in range( 5 ): try: self._GetResponse( '/stopserver', timeout = .5 ) except Exception: pass for _ in range( 10 ): if not self._ServerIsRunning(): return time.sleep( .1 ) def _ForceStopServer( self ): # Kill it if it's still up phandle = self._omnisharp_phandle if phandle is not None: LOGGER.info( 'Killing OmniSharp server' ) for stream in [ phandle.stderr, phandle.stdout ]: if stream is not None: stream.close() try: phandle.kill() except OSError as e: if e.errno == errno.ESRCH: # No such process pass else: raise def _CleanUp( self ): self._omnisharp_port = None self._omnisharp_phandle = None if not self._keep_logfiles: if self._filename_stdout: utils.RemoveIfExists( self._filename_stdout ) self._filename_stdout = None if self._filename_stderr: utils.RemoveIfExists( self._filename_stderr ) self._filename_stderr = None def _RestartServer( self ): """ Restarts the OmniSharp server using a lock. """ with self._server_state_lock: self._StopServerNoLock() return self._StartServerNoLock() def _GetCompletions( self, request_data ): """ Ask server for completions """ parameters = self._DefaultParameters( request_data ) parameters[ 'WantSnippet' ] = False parameters[ 'WantKind' ] = True parameters[ 'WantReturnType' ] = False parameters[ 'WantDocumentationForEveryCompletionResult' ] = True completions = self._GetResponse( '/autocomplete', parameters ) return completions if completions is not None else [] def _GoToDefinition( self, request_data ): """ Jump to definition of identifier under cursor """ definition = self._GetResponse( '/gotodefinition', self._DefaultParameters( request_data ) ) if definition[ 'FileName' ] is not None: filepath = definition[ 'FileName' ] return responses.BuildGoToResponseFromLocation( _BuildLocation( request_data, filepath, definition[ 'Line' ], definition[ 'Column' ] ) ) else: raise RuntimeError( 'Can\'t jump to definition' ) def _GoToImplementation( self, request_data, fallback_to_declaration ): """ Jump to implementation of identifier under cursor """ try: implementation = self._GetResponse( '/findimplementations', self._DefaultParameters( request_data ) ) except ValueError: implementation = { 'QuickFixes': None } quickfixes = implementation[ 'QuickFixes' ] if quickfixes: if len( quickfixes ) == 1: impl = quickfixes[ 0 ] return responses.BuildGoToResponseFromLocation( _BuildLocation( request_data, impl[ 'FileName' ], impl[ 'Line' ], impl[ 'Column' ] ) ) else: return [ responses.BuildGoToResponseFromLocation( _BuildLocation( request_data, x[ 'FileName' ], x[ 'Line' ], x[ 'Column' ] ) ) for x in quickfixes ] else: if ( fallback_to_declaration ): return self._GoToDefinition( request_data ) elif quickfixes is None: raise RuntimeError( 'Can\'t jump to implementation' ) else: raise RuntimeError( 'No implementations found' ) def _SignatureHelp( self, request_data ): request = self._DefaultParameters( request_data ) return self._GetResponse( '/signatureHelp', request ) def _RefactorRename( self, request_data, args ): request = self._DefaultParameters( request_data ) if len( args ) != 1: raise ValueError( 'Please specify a new name to rename it to.\n' 'Usage: RefactorRename <new name>' ) request[ 'RenameTo' ] = args[ 0 ] request[ 'WantsTextChanges' ] = True response = self._GetResponse( '/rename', request ) fixit = _ModifiedFilesToFixIt( response[ 'Changes' ], request_data ) return responses.BuildFixItResponse( [ fixit ] ) def _GoToSymbol( self, request_data, args ): request = self._DefaultParameters( request_data ) request.update( { 'Language': 'C#', 'Filter': args[ 0 ] } ) response = self._GetResponse( '/findsymbols', request ) quickfixes = response[ 'QuickFixes' ] if quickfixes: if len( quickfixes ) == 1: ref = quickfixes[ 0 ] ref_file = ref[ 'FileName' ] ref_line = ref[ 'Line' ] lines = GetFileLines( request_data, ref_file ) line = lines[ min( len( lines ), ref_line - 1 ) ] return responses.BuildGoToResponseFromLocation( _BuildLocation( request_data, ref_file, ref_line, ref[ 'Column' ] ), line ) else: goto_locations = [] for ref in quickfixes: ref_file = ref[ 'FileName' ] ref_line = ref[ 'Line' ] lines = GetFileLines( request_data, ref_file ) line = lines[ min( len( lines ), ref_line - 1 ) ] goto_locations.append( responses.BuildGoToResponseFromLocation( _BuildLocation( request_data, ref_file, ref_line, ref[ 'Column' ] ), line ) ) return goto_locations else: raise RuntimeError( 'No symbols found' ) def _GoToReferences( self, request_data ): """ Jump to references of identifier under cursor """ # _GetResponse can throw. Original code by @mispencer # wrapped it in a try/except and set `reference` to `{ 'QuickFixes': None }` # After being unable to hit that case with tests, # that code path was thrown away. reference = self._GetResponse( '/findusages', self._DefaultParameters( request_data ) ) quickfixes = reference[ 'QuickFixes' ] if quickfixes: if len( quickfixes ) == 1: ref = quickfixes[ 0 ] return responses.BuildGoToResponseFromLocation( _BuildLocation( request_data, ref[ 'FileName' ], ref[ 'Line' ], ref[ 'Column' ] ) ) else: return [ responses.BuildGoToResponseFromLocation( _BuildLocation( request_data, ref[ 'FileName' ], ref[ 'Line' ], ref[ 'Column' ] ) ) for ref in quickfixes ] else: raise RuntimeError( 'No references found' ) def _GetType( self, request_data ): request = self._DefaultParameters( request_data ) request[ "IncludeDocumentation" ] = False result = self._GetResponse( '/typelookup', request ) message = result[ "Type" ] if not message: raise RuntimeError( 'No type info available.' ) return responses.BuildDisplayMessageResponse( message ) def _Format( self, request_data ): request = self._DefaultParameters( request_data ) request[ 'WantsTextChanges' ] = True if 'range' in request_data: lines = request_data[ 'lines' ] start = request_data[ 'range' ][ 'start' ] start_line_num = start[ 'line_num' ] start_line_value = lines[ start_line_num ] start_codepoint = ByteOffsetToCodepointOffset( start_line_value, start[ 'column_num' ] ) end = request_data[ 'range' ][ 'end' ] end_line_num = end[ 'line_num' ] end_line_value = lines[ end_line_num ] end_codepoint = ByteOffsetToCodepointOffset( end_line_value, end[ 'column_num' ] ) request.update( { 'line': start_line_num, 'column': start_codepoint, 'EndLine': end_line_num, 'EndColumn': end_codepoint } ) result = self._GetResponse( '/formatRange', request ) else: result = self._GetResponse( '/codeformat', request ) fixit = responses.FixIt( _BuildLocation( request_data, request_data[ 'filepath' ], request_data[ 'line_num' ], request_data[ 'column_codepoint' ] ), _LinePositionSpanTextChangeToFixItChunks( result[ 'Changes' ], request_data[ 'filepath' ], request_data ) ) return responses.BuildFixItResponse( [ fixit ] ) def _FixIt( self, request_data ): request = self._DefaultParameters( request_data ) request[ 'WantsTextChanges' ] = True result = self._GetResponse( '/getcodeactions', request ) fixits = [] for i, code_action_name in enumerate( result[ 'CodeActions' ] ): fixit = responses.UnresolvedFixIt( { 'index': i }, code_action_name ) fixits.append( fixit ) if len( fixits ) == 1: fixit = fixits[ 0 ] fixit = { 'command': fixit.command, 'resolve': fixit.resolve } return self._ResolveFixIt( request_data, fixit ) return responses.BuildFixItResponse( fixits ) def _ResolveFixIt( self, request_data, unresolved_fixit = None ): fixit = unresolved_fixit if unresolved_fixit else request_data[ 'fixit' ] if not fixit[ 'resolve' ]: return { 'fixits': [ fixit ] } fixit = fixit[ 'command' ] code_action = fixit[ 'index' ] request = self._DefaultParameters( request_data ) request.update( { 'CodeAction': code_action, 'WantsTextChanges': True, } ) response = self._GetResponse( '/runcodeaction', request ) fixit = responses.FixIt( _BuildLocation( request_data, request_data[ 'filepath' ], request_data[ 'line_num' ], request_data[ 'column_codepoint' ] ), _LinePositionSpanTextChangeToFixItChunks( response[ 'Changes' ], request_data[ 'filepath' ], request_data ), response[ 'Text' ] ) # The sort is necessary to keep the tests stable. # Python's sort() is stable, so it won't mess up the order within a file. fixit.chunks.sort( key = lambda c: c.range.start_.filename_ ) return responses.BuildFixItResponse( [ fixit ] ) def _GetDoc( self, request_data ): request = self._DefaultParameters( request_data ) request[ "IncludeDocumentation" ] = True result = self._GetResponse( '/typelookup', request ) message = result.get( 'Type' ) or '' if ( result[ "Documentation" ] ): message += "\n" + result[ "Documentation" ] if not message: raise RuntimeError( 'No documentation available.' ) return responses.BuildDetailedInfoResponse( message.strip() ) def _DefaultParameters( self, request_data ): """ Some very common request parameters """ parameters = {} parameters[ 'line' ] = request_data[ 'line_num' ] parameters[ 'column' ] = request_data[ 'column_codepoint' ] filepath = request_data[ 'filepath' ] parameters[ 'buffer' ] = ( request_data[ 'file_data' ][ filepath ][ 'contents' ] ) parameters[ 'filename' ] = filepath return parameters def _ServerIsRunning( self ): """ Check if our OmniSharp server is running (process is up).""" return utils.ProcessIsRunning( self._omnisharp_phandle ) def ServerIsHealthy( self ): """ Check if our OmniSharp server is healthy (up and serving).""" if not self._ServerIsRunning(): return False try: return self._GetResponse( '/checkalivestatus', timeout = 3 ) except Exception: return False def ServerIsReady( self ): """ Check if our OmniSharp server is ready (loaded solution file).""" if not self._ServerIsRunning(): return False try: return self._GetResponse( '/checkreadystatus', timeout = .2 ) except Exception: return False def _ServerLocation( self ): # We cannot use 127.0.0.1 like we do in other places because OmniSharp # server only listens on localhost. return 'http://localhost:' + str( self._omnisharp_port ) def _GetResponse( self, handler, parameters = {}, timeout = None ): """ Handle communication with server """ target = urljoin( self._ServerLocation(), handler ) LOGGER.debug( 'TX (%s): %s', handler, parameters ) response = requests.post( target, json = parameters, timeout = timeout ) LOGGER.debug( 'RX: %s', response.json() ) return response.json() def _ChooseOmnisharpPort( self ): if not self._omnisharp_port: if self._desired_omnisharp_port: self._omnisharp_port = int( self._desired_omnisharp_port ) else: self._omnisharp_port = utils.GetUnusedLocalhostPort() LOGGER.info( 'using port %s', self._omnisharp_port ) def DiagnosticsToDiagStructure( diagnostics ): structure = defaultdict( lambda : defaultdict( list ) ) for diagnostic in diagnostics: structure[ diagnostic.location_.filename_ ][ diagnostic.location_.line_number_ ].append( diagnostic ) return structure def _BuildLocation( request_data, filename, line_num, column_num ): if line_num <= 0: return None # OmniSharp sometimes incorrectly returns 0 for the column number. Assume the # column is 1 in that case. if column_num <= 0: column_num = 1 contents = GetFileLines( request_data, filename ) line_value = contents[ min( len( contents ), line_num - 1 ) ] return responses.Location( line_num, CodepointOffsetToByteOffset( line_value, column_num ), filename ) def _LinePositionSpanTextChangeToFixItChunks( chunks, filename, request_data ): return [ responses.FixItChunk( chunk[ 'NewText' ], responses.Range( _BuildLocation( request_data, filename, chunk[ 'StartLine' ], chunk[ 'StartColumn' ] ), _BuildLocation( request_data, filename, chunk[ 'EndLine' ], chunk[ 'EndColumn' ] ) ) ) for chunk in chunks ] def _ModifiedFilesToFixIt( changes, request_data ): chunks = [] for change in changes: chunks.extend( _LinePositionSpanTextChangeToFixItChunks( change[ 'Changes' ], change[ 'FileName' ], request_data ) ) # The sort is necessary to keep the tests stable. # Python's sort() is stable, so it won't mess up the order within a file. chunks.sort( key = lambda c: c.range.start_.filename_ ) return responses.FixIt( _BuildLocation( request_data, request_data[ 'filepath' ], request_data[ 'line_num' ], request_data[ 'column_codepoint' ] ), chunks ) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/cs/hook.py��������������������������������������������0000664�0000000�0000000�00000001701�13746324601�0022166�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from ycmd.completers.cs.cs_completer import ( CsharpCompleter, ShouldEnableCsCompleter ) def GetCompleter( user_options ): if not ShouldEnableCsCompleter( user_options ): return None return CsharpCompleter( user_options ) ���������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/cs/solutiondetection.py�������������������������������0000664�0000000�0000000�00000010726�13746324601�0025010�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2013-2020 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 <http://www.gnu.org/licenses/>. import os import glob from inspect import getfile from ycmd import extra_conf_store from ycmd.utils import LOGGER def FindSolutionPath( filepath ): """Try to find suitable solution file given a source file path using all available information sources""" # try to load ycm_extra_conf # if it needs to be verified, abort here and try again later module = extra_conf_store.ModuleForSourceFile( filepath ) path_to_solutionfile = PollModule( module, filepath ) if not path_to_solutionfile: # ycm_extra_conf not available or did not provide a solution file path_to_solutionfile = GuessFile( filepath ) return path_to_solutionfile def PollModule( module, filepath ): """ Try to use passed module in the selection process by calling CSharpSolutionFile on it """ path_to_solutionfile = None module_hint = None if module: try: module_hint = module.CSharpSolutionFile( filepath ) LOGGER.info( 'extra_conf_store suggests %s as solution file', module_hint ) if module_hint: # received a full path or one relative to the config's location? candidates = [ module_hint, os.path.join( os.path.dirname( getfile( module ) ), module_hint ) ] # try the assumptions for path in candidates: if os.path.isfile( path ): # path seems to point to a solution path_to_solutionfile = path LOGGER.info( 'Using solution file %s selected by extra_conf_store', path_to_solutionfile ) break except AttributeError: # the config script might not provide solution file locations LOGGER.exception( 'Could not retrieve solution for %s' 'from extra_conf_store', filepath ) return path_to_solutionfile def GuessFile( filepath ): """ Find solution files by searching upwards in the file tree """ tokens = _PathComponents( filepath ) for i in reversed( range( len( tokens ) - 1 ) ): path = os.path.join( *tokens[ : i + 1 ] ) candidates = glob.glob1( path, '*.sln' ) if len( candidates ) > 0: # do the whole procedure only for the first solution file(s) you find return _SolutionTestCheckHeuristics( candidates, tokens, i ) return None def _SolutionTestCheckHeuristics( candidates, tokens, i ): """ Test if one of the candidate files stands out """ path = os.path.join( *tokens[ : i + 1 ] ) selection = None # if there is just one file here, use that if len( candidates ) == 1 : selection = os.path.join( path, candidates[ 0 ] ) LOGGER.info( 'Selected solution file %s as it is the first one found', selection ) # there is more than one file, try some hints to decide # 1. is there a solution named just like the subdirectory with the source? if ( not selection and i < len( tokens ) - 1 and f'{ tokens[ i + 1 ] }.sln' in candidates ): selection = os.path.join( path, f'{ tokens[ i + 1] }.sln' ) LOGGER.info( 'Selected solution file %s as it matches source subfolder', selection ) # 2. is there a solution named just like the directory containing the # solution? if not selection and f'{ tokens[ i ] }.sln' in candidates : selection = os.path.join( path, f'{ tokens[ i ] }.sln' ) LOGGER.info( 'Selected solution file %s as it matches containing folder', selection ) if not selection: LOGGER.error( 'Could not decide between multiple solution files:\n%s', candidates ) return selection def _PathComponents( path ): path_components = [] while True: path, folder = os.path.split( path ) if folder: path_components.append( folder ) else: if path: path_components.append( path ) break path_components.reverse() return path_components ������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/cuda/�������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0021164�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/cuda/__init__.py��������������������������������������0000664�0000000�0000000�00000000000�13746324601�0023263�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/cuda/hook.py������������������������������������������0000664�0000000�0000000�00000002223�13746324601�0022475�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from ycmd.completers.cpp.clang_completer import ClangCompleter from ycmd.completers.cpp.clangd_completer import ( ShouldEnableClangdCompleter, ClangdCompleter ) from ycmd.utils import ImportCore ycm_core = ImportCore() def GetCompleter( user_options ): if ShouldEnableClangdCompleter( user_options ): return ClangdCompleter( user_options ) if ycm_core.HasClangSupport(): return ClangCompleter( user_options ) return None �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/general/����������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0021665�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/general/__init__.py�����������������������������������0000664�0000000�0000000�00000000000�13746324601�0023764�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/general/filename_completer.py�������������������������0000664�0000000�0000000�00000022414�13746324601�0026074�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2013-2020 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 <http://www.gnu.org/licenses/>. import os from ycmd.completers.completer import Completer from ycmd.utils import ( ExpandVariablesInPath, GetCurrentDirectory, GetModificationTime, ListDirectory, OnWindows, re, ToUnicode ) from ycmd import responses FILE = 1 DIR = 2 FRAMEWORK = 4 # This mapping is also used for the #include completion. Entries can # simultaneously be a file, a directory, and/or a framework. EXTRA_INFO_MAP = { FILE: '[File]', DIR: '[Dir]', 3: '[File&Dir]', FRAMEWORK: '[Framework]', 5: '[File&Framework]', 6: '[Dir&Framework]', 7: '[File&Dir&Framework]' } PATH_SEPARATORS_PATTERN = '([{seps}][^{seps}]*|[{seps}]$)' HEAD_PATH_PATTERN_UNIX = """ # Current and previous directories \\.{1,2}| # Home directory ~| # UNIX environment variables \\$[^$]+ """ HEAD_PATH_PATTERN_WINDOWS = HEAD_PATH_PATTERN_UNIX + """| # Drive letters [A-Za-z]:| # Windows environment variables %[^%]+% """ class FilenameCompleter( Completer ): """ General completer that provides filename and filepath completions. """ def __init__( self, user_options ): super().__init__( user_options ) if OnWindows(): self._path_separators = r'/\\' self._head_path_pattern = HEAD_PATH_PATTERN_WINDOWS else: self._path_separators = '/' self._head_path_pattern = HEAD_PATH_PATTERN_UNIX self._path_separators_regex = re.compile( PATH_SEPARATORS_PATTERN.format( seps = self._path_separators ) ) self._head_path_for_directory = {} self._candidates_for_directory = {} def CurrentFiletypeCompletionDisabled( self, request_data ): disabled_filetypes = self.user_options[ 'filepath_blacklist' ] filetypes = request_data[ 'filetypes' ] return ( '*' in disabled_filetypes or any( x in disabled_filetypes for x in filetypes ) ) def GetWorkingDirectory( self, request_data ): if self.user_options[ 'filepath_completion_use_working_dir' ]: # Return paths relative to the working directory of the client, if # supplied, otherwise relative to the current working directory of this # process. return request_data.get( 'working_dir' ) or GetCurrentDirectory() # Return paths relative to the file. return os.path.dirname( request_data[ 'filepath' ] ) def GetCompiledHeadRegexForDirectory( self, directory ): mtime = GetModificationTime( directory ) try: head_regex = self._head_path_for_directory[ directory ] if mtime and mtime <= head_regex[ 'mtime' ]: return head_regex[ 'regex' ] except KeyError: pass current_paths = ListDirectory( directory ) current_paths_pattern = '|'.join( [ re.escape( path ) for path in current_paths ] ) head_pattern = ( '(' + self._head_path_pattern + '|' + current_paths_pattern + ')$' ) head_regex = re.compile( head_pattern, re.VERBOSE ) if mtime: self._head_path_for_directory[ directory ] = { 'regex': head_regex, 'mtime': mtime } return head_regex def SearchPath( self, request_data ): """Return the tuple (|path|, |start_column|) where |path| is a path that could be completed on the current line before the cursor and |start_column| is the column where the completion should start. (None, None) is returned if no suitable path is found.""" # Find all path separators on the current line before the cursor. Return # early if no separators are found. current_line = request_data[ 'prefix' ] matches = list( self._path_separators_regex.finditer( current_line ) ) if not matches: return None, None working_dir = self.GetWorkingDirectory( request_data ) head_regex = self.GetCompiledHeadRegexForDirectory( working_dir ) last_match = matches[ -1 ] last_match_start = last_match.start( 1 ) # Go through all path separators from left to right. for match in matches: # Check if ".", "..", "~", an environment variable, one of the current # directories, or a drive letter on Windows match just before the # separator. If so, extract the path from the start of the match to the # latest path separator. Expand "~" and the environment variables in the # path. If the path is relative, convert it to an absolute path relative # to the working directory. If the resulting path exists, return it and # the column just after the latest path separator as the starting column. head_match = head_regex.search( current_line[ : match.start() ] ) if head_match: path = current_line[ head_match.start( 1 ) : last_match_start ] path = ExpandVariablesInPath( path + os.path.sep ) if not os.path.isabs( path ): path = os.path.join( working_dir, path ) if os.path.exists( path ): # +2 because last_match_start is the 0-indexed position just before # the latest path separator whose length is 1 on all platforms we # support. return path, last_match_start + 2 # Otherwise, the path may start with "/" (or "\" on Windows). Extract the # path from the current path separator to the latest one. If the path is # not empty and does not only consist of path separators, expand "~" and # the environment variables in the path. If the resulting path exists, # return it and the column just after the latest path separator as the # starting column. path = current_line[ match.start() : last_match_start ] if path.strip( self._path_separators ): path = ExpandVariablesInPath( path + os.path.sep ) if os.path.exists( path ): return path, last_match_start + 2 # No suitable paths have been found after going through all separators. The # path could be exactly "/" (or "\" on Windows). Only return the path if # there are no other path separators on the line. This prevents always # completing the root directory if nothing is matched. # TODO: completion on a single "/" or "\" is not really desirable in # languages where such characters are part of special constructs like # comments in C/C++ or closing tags in HTML. This behavior could be improved # by using rules that depend on the filetype. if len( matches ) == 1: return os.path.sep, last_match_start + 2 return None, None def ShouldUseNow( self, request_data ): if self.CurrentFiletypeCompletionDisabled( request_data ): return False return bool( self.SearchPath( request_data )[ 0 ] ) def SupportedFiletypes( self ): return [] def GetCandidatesForDirectory( self, directory ): mtime = GetModificationTime( directory ) try: candidates = self._candidates_for_directory[ directory ] if mtime and mtime <= candidates[ 'mtime' ]: return candidates[ 'candidates' ] except KeyError: pass candidates = _GeneratePathCompletionCandidates( directory ) if mtime: self._candidates_for_directory[ directory ] = { 'candidates': candidates, 'mtime': mtime } return candidates def ComputeCandidates( self, request_data ): if not self.ShouldUseNow( request_data ): return [] # Calling this function seems inefficient when it's already been called in # ShouldUseNow for that request but its execution time is so low once the # head regex is cached that it doesn't matter. directory, start_codepoint = self.SearchPath( request_data ) old_start_codepoint = request_data[ 'start_codepoint' ] request_data[ 'start_codepoint' ] = start_codepoint candidates = self.GetCandidatesForDirectory( directory ) candidates = self.FilterAndSortCandidates( candidates, request_data[ 'query' ] ) if not candidates: # No candidates were matched. Reset the start column for the identifier # completer. request_data[ 'start_codepoint' ] = old_start_codepoint return candidates def _GeneratePathCompletionCandidates( path_dir ): completions = [] unicode_path = ToUnicode( path_dir ) for rel_path in ListDirectory( unicode_path ): absolute_path = os.path.join( unicode_path, rel_path ) path_type = GetPathTypeName( GetPathType( absolute_path ) ) completions.append( responses.BuildCompletionData( rel_path, path_type ) ) return completions def GetPathType( path, is_framework = False ): if is_framework: return FRAMEWORK if os.path.isdir( path ): return DIR return FILE def GetPathTypeName( path_type ): return EXTRA_INFO_MAP[ path_type ] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/general/general_completer_store.py��������������������0000664�0000000�0000000�00000006213�13746324601�0027144�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2013-2020 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 <http://www.gnu.org/licenses/>. from ycmd.completers.completer import Completer from ycmd.completers.all.identifier_completer import IdentifierCompleter from ycmd.completers.general.filename_completer import FilenameCompleter from ycmd.completers.general.ultisnips_completer import UltiSnipsCompleter class GeneralCompleterStore( Completer ): """ Holds a list of completers that can be used in all filetypes. It overrides all Completer API methods so that specific calls to GeneralCompleterStore are passed to all general completers. """ def __init__( self, user_options ): super().__init__( user_options ) self._identifier_completer = IdentifierCompleter( user_options ) self._filename_completer = FilenameCompleter( user_options ) self._ultisnips_completer = UltiSnipsCompleter( user_options ) self._non_filename_completers = [ self._identifier_completer ] if user_options.get( 'use_ultisnips_completer', True ): self._non_filename_completers.append( self._ultisnips_completer ) self._all_completers = [ self._identifier_completer, self._filename_completer, self._ultisnips_completer ] def SupportedFiletypes( self ): return set() def GetIdentifierCompleter( self ): return self._identifier_completer def ComputeCandidates( self, request_data ): candidates = self._filename_completer.ComputeCandidates( request_data ) if candidates: return candidates for completer in self._non_filename_completers: candidates += completer.ComputeCandidates( request_data ) return candidates def OnFileReadyToParse( self, request_data ): for completer in self._all_completers: completer.OnFileReadyToParse( request_data ) def OnBufferVisit( self, request_data ): for completer in self._all_completers: completer.OnBufferVisit( request_data ) def OnBufferUnload( self, request_data ): for completer in self._all_completers: completer.OnBufferUnload( request_data ) def OnInsertLeave( self, request_data ): for completer in self._all_completers: completer.OnInsertLeave( request_data ) def OnCurrentIdentifierFinished( self, request_data ): for completer in self._all_completers: completer.OnCurrentIdentifierFinished( request_data ) def GettingCompletions( self ): for completer in self._all_completers: completer.GettingCompletions() def Shutdown( self ): for completer in self._all_completers: completer.Shutdown() �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/general/ultisnips_completer.py������������������������0000664�0000000�0000000�00000003167�13746324601�0026352�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from ycmd.completers.general_completer import GeneralCompleter from ycmd import responses class UltiSnipsCompleter( GeneralCompleter ): """ General completer that provides UltiSnips snippet names in completions. """ def __init__( self, user_options ): super().__init__( user_options ) self._candidates = None self._filtered_candidates = None def ShouldUseNow( self, request_data ): return self.QueryLengthAboveMinThreshold( request_data ) def ComputeCandidates( self, request_data ): if not self.ShouldUseNow( request_data ): return [] return self.FilterAndSortCandidates( self._candidates, request_data[ 'query' ] ) def OnBufferVisit( self, request_data ): raw_candidates = request_data.get( 'ultisnips_snippets', [] ) self._candidates = [ responses.BuildCompletionData( snip[ 'trigger' ], '<snip> ' + snip[ 'description' ] ) for snip in raw_candidates ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/general_completer.py����������������������������������0000664�0000000�0000000�00000002246�13746324601�0024315�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from ycmd.completers.completer import Completer class GeneralCompleter( Completer ): """ A base class for General completers in YCM. A general completer is used in all filetypes. Because this is a subclass of Completer class, you should refer to the Completer class documentation. Do NOT use this class for semantic completers! Subclass Completer directly. """ def __init__( self, user_options ): super().__init__( user_options ) def SupportedFiletypes( self ): return set() ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/go/���������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0020655�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/go/__init__.py����������������������������������������0000664�0000000�0000000�00000000000�13746324601�0022754�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/go/go_completer.py������������������������������������0000664�0000000�0000000�00000006763�13746324601�0023722�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import json import logging import os from ycmd import responses from ycmd import utils from ycmd.completers.language_server import language_server_completer PATH_TO_GOPLS = os.path.abspath( os.path.join( os.path.dirname( __file__ ), '..', '..', '..', 'third_party', 'go', 'bin', utils.ExecutableName( 'gopls' ) ) ) def ShouldEnableGoCompleter( user_options ): server_exists = utils.FindExecutableWithFallback( user_options[ 'gopls_binary_path' ], PATH_TO_GOPLS ) if server_exists: return True utils.LOGGER.info( 'No gopls executable at %s.', PATH_TO_GOPLS ) return False class GoCompleter( language_server_completer.LanguageServerCompleter ): def __init__( self, user_options ): super().__init__( user_options ) self._user_supplied_gopls_args = user_options[ 'gopls_args' ] self._gopls_path = utils.FindExecutableWithFallback( user_options[ 'gopls_binary_path' ], PATH_TO_GOPLS ) def GetServerName( self ): return 'gopls' def GetProjectRootFiles( self ): # Without LSP workspaces support, GOPLS relies on the rootUri to detect a # project. # TODO: add support for LSP workspaces to allow users to change project # without having to restart GOPLS. return [ 'go.mod' ] def GetCommandLine( self ): cmdline = [ self._gopls_path ] + self._user_supplied_gopls_args + [ '-logfile', self._stderr_file ] if utils.LOGGER.isEnabledFor( logging.DEBUG ): cmdline.append( '-rpc.trace' ) return cmdline def SupportedFiletypes( self ): return [ 'go' ] def GetDoc( self, request_data ): assert self._settings[ 'ls' ][ 'hoverKind' ] == 'Structured' try: result = json.loads( self.GetHoverResponse( request_data )[ 'value' ] ) docs = result[ 'signature' ] + '\n' + result[ 'fullDocumentation' ] return responses.BuildDetailedInfoResponse( docs.strip() ) except language_server_completer.NoHoverInfoException: raise RuntimeError( 'No documentation available.' ) def GetType( self, request_data ): try: result = json.loads( self.GetHoverResponse( request_data )[ 'value' ] )[ 'signature' ] return responses.BuildDisplayMessageResponse( result ) except language_server_completer.NoHoverInfoException: raise RuntimeError( 'Unknown type.' ) def DefaultSettings( self, request_data ): return { 'hoverKind': 'Structured' } def ExtraCapabilities( self ): return { 'workspace': { 'configuration': True } } def WorkspaceConfigurationResponse( self, request ): # Returns the same settings for each "section", since gopls requests # settings for each open project, but ycmd only has a single settings # object per LSP completer. return [ self._settings.get( 'ls', {} ) for i in request[ 'params' ][ 'items' ] ] �������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/go/hook.py��������������������������������������������0000664�0000000�0000000�00000001610�13746324601�0022165�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from ycmd.completers.go.go_completer import GoCompleter, ShouldEnableGoCompleter def GetCompleter( user_options ): if not ShouldEnableGoCompleter( user_options ): return None return GoCompleter( user_options ) ������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/java/�������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0021171�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/java/__init__.py��������������������������������������0000664�0000000�0000000�00000000000�13746324601�0023270�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/java/hook.py������������������������������������������0000664�0000000�0000000�00000001632�13746324601�0022505�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from ycmd.completers.java.java_completer import ( ShouldEnableJavaCompleter, JavaCompleter ) def GetCompleter( user_options ): if not ShouldEnableJavaCompleter( user_options ): return None return JavaCompleter( user_options ) ������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/java/java_completer.py��������������������������������0000664�0000000�0000000�00000052233�13746324601�0024543�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2017-2020 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 <http://www.gnu.org/licenses/>. import glob import hashlib import json import os import shutil import tempfile import threading from ycmd import responses, utils from ycmd.completers.language_server import language_server_protocol as lsp from ycmd.completers.language_server import language_server_completer from ycmd.utils import LOGGER NO_DOCUMENTATION_MESSAGE = 'No documentation available for current context' LANGUAGE_SERVER_HOME = os.path.abspath( os.path.join( os.path.dirname( __file__ ), '..', '..', '..', 'third_party', 'eclipse.jdt.ls', 'target', 'repository' ) ) PATH_TO_JAVA = None PROJECT_FILE_TAILS = [ '.project', 'pom.xml', 'build.gradle' ] DEFAULT_WORKSPACE_ROOT_PATH = os.path.abspath( os.path.join( os.path.dirname( __file__ ), '..', '..', '..', 'third_party', 'eclipse.jdt.ls', 'workspace' ) ) DEFAULT_EXTENSION_PATH = os.path.abspath( os.path.join( os.path.dirname( __file__ ), '..', '..', '..', 'third_party', 'eclipse.jdt.ls', 'extensions' ) ) # The authors of jdt.ls say that we should re-use workspaces. They also say that # occasionally, the workspace becomes corrupt, and has to be deleted. This is # frustrating. # # Pros for re-use: # - Startup time is significantly improved. This could be very meaningful on # larger projects # # Cons: # - A little more complexity (we hash the project path to create the workspace # directory) # - It breaks our tests which expect the logs to be deleted # - It can lead to multiple jdt.ls instances using the same workspace (BAD) # - It breaks our tests which do exactly that # # So: # - By _default_ we use a clean workspace (see default_settings.json) on each # ycmd instance # - An option is available to re-use workspaces CLEAN_WORKSPACE_OPTION = 'java_jdtls_use_clean_workspace' # jdt.ls workspace areas are mutable and written by the server. Putting them # underneath the ycmd installation, even in their own directory makes it # impossible to use a shared installation of ycmd. In order to allow that, we # expose another (hidden) option which moves the workspace root dirdectory # somewhere else, such as the user's home directory. WORKSPACE_ROOT_PATH_OPTION = 'java_jdtls_workspace_root_path' # jdt.ls supports extensions that are loaded on startup bu passing a list of jar # files to load. The following list option is a list of paths to scan for # directories containing extensions in the same format as expected by the # vscode-java extension. EXTENSION_PATH_OPTION = 'java_jdtls_extension_path' def ShouldEnableJavaCompleter( user_options ): LOGGER.info( 'Looking for jdt.ls' ) global PATH_TO_JAVA PATH_TO_JAVA = utils.FindExecutableWithFallback( user_options[ 'java_binary_path' ], utils.FindExecutable( 'java' ) ) if not PATH_TO_JAVA: LOGGER.warning( "Not enabling java completion: Couldn't find java 11" ) return False if not os.path.exists( LANGUAGE_SERVER_HOME ): LOGGER.warning( 'Not using java completion: jdt.ls is not installed' ) return False if not _PathToLauncherJar(): LOGGER.warning( 'Not using java completion: jdt.ls is not built' ) return False return True def _PathToLauncherJar(): # The file name changes between version of eclipse, so we use a glob as # recommended by the language server developers. There should only be one. launcher_jars = glob.glob( os.path.abspath( os.path.join( LANGUAGE_SERVER_HOME, 'plugins', 'org.eclipse.equinox.launcher_*.jar' ) ) ) LOGGER.debug( 'Found launchers: %s', launcher_jars ) if not launcher_jars: return None return launcher_jars[ 0 ] def _CollectExtensionBundles( extension_path ): extension_bundles = [] for extension_dir in extension_path: if not os.path.isdir( extension_dir ): LOGGER.info( f'extension directory does not exist: { extension_dir }' ) continue for path in os.listdir( extension_dir ): path = os.path.join( extension_dir, path ) manifest_file = os.path.join( path, 'package.json' ) if not os.path.isdir( path ) or not os.path.isfile( manifest_file ): LOGGER.debug( f'{ path } is not an extension directory' ) continue manifest_json = utils.ReadFile( manifest_file ) try: manifest = json.loads( manifest_json ) except ValueError: LOGGER.exception( f'Could not load bundle { manifest_file }' ) continue if ( 'contributes' not in manifest or 'javaExtensions' not in manifest[ 'contributes' ] or not isinstance( manifest[ 'contributes' ][ 'javaExtensions' ], list ) ): LOGGER.info( f'Bundle { manifest_file } is not a java extension' ) continue LOGGER.info( f'Found bundle: { manifest_file }' ) extension_bundles.extend( [ os.path.join( path, p ) for p in manifest[ 'contributes' ][ 'javaExtensions' ] ] ) return extension_bundles def _LauncherConfiguration( workspace_root, wipe_config ): if utils.OnMac(): config = 'config_mac' elif utils.OnWindows(): config = 'config_win' else: config = 'config_linux' CONFIG_FILENAME = 'config.ini' # The 'config' directory is a bit of a misnomer. It is really a working area # for eclipse to store things that eclipse feels entitled to store, # that are not specific to a particular project or workspace. # Importantly, the server writes to this directory, which means that in order # to allow installations of ycmd on readonly filesystems (or shared # installations of ycmd), we have to make it somehow unique at least per user, # and possibly per ycmd instance. # # To allow this, we let the client specify the workspace root and we always # put the (mutable) config directory under the workspace root path. The config # directory is simply a writable directory with the config.ini in it. # # Note that we must re-copy the config when it changes. Otherwise, eclipse # just won't start. As the file is generated part of the jdt.ls build, we just # always copy and overwrite it. working_config = os.path.abspath( os.path.join( workspace_root, config ) ) working_config_file = os.path.join( working_config, CONFIG_FILENAME ) base_config_file = os.path.abspath( os.path.join( LANGUAGE_SERVER_HOME, config, CONFIG_FILENAME ) ) if os.path.isdir( working_config ): if wipe_config: shutil.rmtree( working_config ) os.makedirs( working_config ) elif os.path.isfile( working_config_file ): os.remove( working_config_file ) else: os.makedirs( working_config ) shutil.copy2( base_config_file, working_config_file ) return working_config def _MakeProjectFilesForPath( path ): for tail in PROJECT_FILE_TAILS: yield os.path.join( path, tail ), tail def _FindProjectDir( starting_dir ): project_path = starting_dir project_type = None for folder in utils.PathsToAllParentFolders( starting_dir ): for project_file, tail in _MakeProjectFilesForPath( folder ): if os.path.isfile( project_file ): project_path = folder project_type = tail break if project_type: break if project_type: # We've found a project marker file (like build.gradle). Search parent # directories for that same project type file and find the topmost one as # the project root. LOGGER.debug( 'Found %s style project in %s. Searching for ' 'project root:', project_type, project_path ) for folder in utils.PathsToAllParentFolders( os.path.join( project_path, '..' ) ): if os.path.isfile( os.path.join( folder, project_type ) ): LOGGER.debug( ' %s is a parent project dir', folder ) project_path = folder else: break LOGGER.debug( ' Project root is %s', project_path ) return project_path def _WorkspaceDirForProject( workspace_root_path, project_dir, use_clean_workspace ): if use_clean_workspace: temp_path = os.path.join( workspace_root_path, 'temp' ) try: os.makedirs( temp_path ) except OSError: pass return tempfile.mkdtemp( dir=temp_path ) project_dir_hash = hashlib.sha256( utils.ToBytes( project_dir ) ) return os.path.join( workspace_root_path, utils.ToUnicode( project_dir_hash.hexdigest() ) ) class JavaCompleter( language_server_completer.LanguageServerCompleter ): def __init__( self, user_options ): self._workspace_path = None super().__init__( user_options ) self._server_keep_logfiles = user_options[ 'server_keep_logfiles' ] self._use_clean_workspace = user_options[ CLEAN_WORKSPACE_OPTION ] self._workspace_root_path = user_options[ WORKSPACE_ROOT_PATH_OPTION ] self._extension_path = user_options[ EXTENSION_PATH_OPTION ] if not self._workspace_root_path: self._workspace_root_path = DEFAULT_WORKSPACE_ROOT_PATH if not isinstance( self._extension_path, list ): raise ValueError( f'{ EXTENSION_PATH_OPTION } option must be a list' ) if not self._extension_path: self._extension_path = [ DEFAULT_EXTENSION_PATH ] else: self._extension_path.append( DEFAULT_EXTENSION_PATH ) self._bundles = ( _CollectExtensionBundles( self._extension_path ) if self._extension_path else [] ) self._connection = None self._server_handle = None self._stderr_file = None self._Reset() self._command = [] def DefaultSettings( self, request_data ): return { 'bundles': self._bundles } def SupportedFiletypes( self ): return [ 'java' ] def GetSignatureTriggerCharacters( self, server_trigger_characters ): return server_trigger_characters + [ ',' ] def GetCustomSubcommands( self ): return { 'OrganizeImports': ( lambda self, request_data, args: self.OrganizeImports( request_data ) ), 'OpenProject': ( lambda self, request_data, args: self._OpenProject( request_data, args ) ), 'WipeWorkspace': ( lambda self, request_data, args: self._WipeWorkspace( request_data, args ) ), } def AdditionalLogFiles( self ): if self._workspace_path: return [ os.path.join( self._workspace_path, '.metadata', '.log' ) ] return [] def ExtraDebugItems( self, request_data ): items = [ responses.DebugInfoItem( 'Startup Status', self._server_init_status ), responses.DebugInfoItem( 'Java Path', PATH_TO_JAVA ), ] if self._launcher_config: items.append( responses.DebugInfoItem( 'Launcher Config.', self._launcher_config ) ) if self._workspace_path: items.append( responses.DebugInfoItem( 'Workspace Path', self._workspace_path ) ) items.append( responses.DebugInfoItem( 'Extension Path', self._extension_path ) ) return items def ServerIsReady( self ): return ( self.ServerIsHealthy() and self._received_ready_message.is_set() and super().ServerIsReady() ) def GetProjectDirectory( self, *args, **kwargs ): return self._java_project_dir def _WipeWorkspace( self, request_data, args ): with_config = False if len( args ) > 0 and '--with-config' in args: with_config = True self._RestartServer( request_data, wipe_workspace = True, wipe_config = with_config ) def _OpenProject( self, request_data, args ): if len( args ) != 1: raise ValueError( "Usage: OpenProject <project directory>" ) project_directory = args[ 0 ] # If the dir is not absolute, calculate it relative to the working dir of # the client (if supplied). if not os.path.isabs( project_directory ): if 'working_dir' not in request_data: raise ValueError( "Project directory must be absolute" ) project_directory = os.path.normpath( os.path.join( request_data[ 'working_dir' ], project_directory ) ) self._RestartServer( request_data, project_directory = project_directory ) def _Reset( self ): if self._workspace_path and self._use_clean_workspace: try: shutil.rmtree( self._workspace_path ) except OSError: LOGGER.exception( 'Failed to clean up workspace dir %s', self._workspace_path ) self._launcher_path = _PathToLauncherJar() self._launcher_config = None self._workspace_path = None self._java_project_dir = None self._received_ready_message = threading.Event() self._server_init_status = 'Not started' self._started_message_sent = False super()._Reset() def StartServer( self, request_data, project_directory = None, wipe_workspace = False, wipe_config = False ): try: with self._server_info_mutex: LOGGER.info( 'Starting jdt.ls Language Server...' ) if project_directory: self._java_project_dir = project_directory elif 'project_directory' in self._settings: self._java_project_dir = utils.AbsolutePath( self._settings[ 'project_directory' ], self._extra_conf_dir ) else: self._java_project_dir = _FindProjectDir( os.path.dirname( request_data[ 'filepath' ] ) ) self._workspace_path = _WorkspaceDirForProject( self._workspace_root_path, self._java_project_dir, self._use_clean_workspace ) if not self._use_clean_workspace and wipe_workspace: if os.path.isdir( self._workspace_path ): LOGGER.info( f'Wiping out workspace { self._workspace_path }' ) shutil.rmtree( self._workspace_path ) self._launcher_config = _LauncherConfiguration( self._workspace_root_path, wipe_config ) self._command = [ PATH_TO_JAVA, '-Dfile.encoding=UTF-8', '-Declipse.application=org.eclipse.jdt.ls.core.id1', '-Dosgi.bundles.defaultStartLevel=4', '-Declipse.product=org.eclipse.jdt.ls.core.product', '-Dlog.level=ALL', '-jar', self._launcher_path, '-configuration', self._launcher_config, '-data', self._workspace_path, ] return super( JavaCompleter, self )._StartServerNoLock( request_data ) except language_server_completer.LanguageServerConnectionTimeout: LOGGER.error( '%s failed to start, or did not connect successfully', self.GetServerName() ) self.Shutdown() return False def GetCodepointForCompletionRequest( self, request_data ): """Returns the 1-based codepoint offset on the current line at which to make the completion request""" # When the user forces semantic completion, we pass the actual cursor # position to jdt.ls. # At the top level (i.e. without a semantic trigger), there are always way # too many possible candidates for jdt.ls to return anything useful. This is # because we don't send the currently typed characters to jdt.ls. The # general idea is that we apply our own post-filter and sort. However, in # practice we never get a full set of possibilities at the top-level. So, as # a compromise, we allow the user to force us to send the "query" to the # semantic engine, and thus get good completion results at the top level, # even if this means the "filtering and sorting" is not 100% ycmd flavor. if request_data[ 'force_semantic' ]: return request_data[ 'column_codepoint' ] return super().GetCodepointForCompletionRequest( request_data ) def HandleNotificationInPollThread( self, notification ): if notification[ 'method' ] == 'language/status': message_type = notification[ 'params' ][ 'type' ] if message_type == 'Started': LOGGER.info( 'jdt.ls initialized successfully' ) self._server_init_status = notification[ 'params' ][ 'message' ] self._received_ready_message.set() elif not self._received_ready_message.is_set(): self._server_init_status = notification[ 'params' ][ 'message' ] super().HandleNotificationInPollThread( notification ) def ConvertNotificationToMessage( self, request_data, notification ): if notification[ 'method' ] == 'language/status': message = notification[ 'params' ][ 'message' ] if notification[ 'params' ][ 'type' ] == 'Started': self._started_message_sent = True return responses.BuildDisplayMessageResponse( f'Initializing Java completer: { message }' ) if not self._started_message_sent: return responses.BuildDisplayMessageResponse( f'Initializing Java completer: { message }' ) return super().ConvertNotificationToMessage( request_data, notification ) def GetType( self, request_data ): hover_response = self.GetHoverResponse( request_data ) # The LSP defines the hover response as either: # - a string # - a list of strings # - an object with keys language, value # - a list of objects with keys language, value # - an object with keys kind, value # That's right. All of the above. # However it would appear that jdt.ls only ever returns useful data when it # is a list of objects-with-keys-language-value, and the type information is # always in the first such list element, so we only handle that case and # throw any other time. # Strictly we seem to receive: # - "" # when there really is no documentation or type info available # - {language:java, value:<type info>} # when there only the type information is available # - [{language:java, value:<type info>}, # 'doc line 1', # 'doc line 2', # ...] # when there is type and documentation information available. if not hover_response: raise RuntimeError( 'Unknown type' ) if isinstance( hover_response, list ): hover_response = hover_response[ 0 ] if ( not isinstance( hover_response, dict ) or hover_response.get( 'language' ) != 'java' or 'value' not in hover_response ): raise RuntimeError( 'Unknown type' ) return responses.BuildDisplayMessageResponse( hover_response[ 'value' ] ) def GetDoc( self, request_data ): hover_response = self.GetHoverResponse( request_data ) # The LSP defines the hover response as either: # - a string # - a list of strings # - an object with keys language, value # - a list of objects with keys language, value # - an object with keys kind, value # That's right. All of the above. # However it would appear that jdt.ls only ever returns useful data when it # is a list of objects-with-keys-language-value, so we only handle that case # and throw any other time. # Strictly we seem to receive: # - "" # when there really is no documentation or type info available # - {language:java, value:<type info>} # when there only the type information is available # - [{language:java, value:<type info>}, # 'doc line 1', # 'doc line 2', # ...] # when there is type and documentation information available. documentation = '' if isinstance( hover_response, list ): for item in hover_response: if isinstance( item, str ): documentation += item + '\n' documentation = documentation.rstrip() if not documentation: raise RuntimeError( NO_DOCUMENTATION_MESSAGE ) return responses.BuildDetailedInfoResponse( documentation ) def OrganizeImports( self, request_data ): fixit = { 'resolve': True, 'command': { 'title': 'Organize Imports', 'command': 'java.edit.organizeImports', 'arguments': [ lsp.FilePathToUri( request_data[ 'filepath' ] ) ] } } return self._ResolveFixit( request_data, fixit ) def CodeActionCommandToFixIt( self, request_data, command ): # JDT wants us to special case `java.apply.workspaceEdit` # https://github.com/eclipse/eclipse.jdt.ls/issues/376 if command[ 'command' ][ 'command' ] == 'java.apply.workspaceEdit': command[ 'edit' ] = command.pop( 'command' )[ 'arguments' ][ 0 ] return super().CodeActionLiteralToFixIt( request_data, command ) return super().CodeActionCommandToFixIt( request_data, command ) def GetServerName( self ): return 'jdt.ls' def GetCommandLine( self ): return self._command ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/javascript/�������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0022416�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/javascript/__init__.py��������������������������������0000664�0000000�0000000�00000000000�13746324601�0024515�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/javascript/hook.py������������������������������������0000664�0000000�0000000�00000002151�13746324601�0023727�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from ycmd.completers.javascript.tern_completer import ( ShouldEnableTernCompleter, TernCompleter ) from ycmd.completers.typescript.typescript_completer import ( ShouldEnableTypeScriptCompleter, TypeScriptCompleter ) def GetCompleter( user_options ): if ShouldEnableTernCompleter(): return TernCompleter( user_options ) if ShouldEnableTypeScriptCompleter( user_options ): return TypeScriptCompleter( user_options ) return None �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/javascript/tern_completer.py��������������������������0000664�0000000�0000000�00000055464�13746324601�0026030�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2015-2020 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 <http://www.gnu.org/licenses/>. import logging import os import requests import threading from subprocess import PIPE from ycmd import utils, responses from ycmd.completers.completer import Completer from ycmd.completers.completer_utils import GetFileLines from ycmd.utils import LOGGER PATH_TO_TERN_BINARY = os.path.abspath( os.path.join( os.path.dirname( __file__ ), '..', '..', '..', 'third_party', 'tern_runtime', 'node_modules', 'tern', 'bin', 'tern' ) ) PATH_TO_NODE = utils.FindExecutable( 'node' ) # host name/address on which the tern server should listen # note: we use 127.0.0.1 rather than localhost because on some platforms # localhost might not be correctly configured as an alias for the loopback # address. (ahem: Windows) SERVER_HOST = '127.0.0.1' LOGFILE_FORMAT = 'tern_{port}_{std}_' def ShouldEnableTernCompleter(): """Returns whether or not the tern completer is 'installed'. That is whether or not the tern submodule has a 'node_modules' directory. This is pretty much the only way we can know if the user added '--js-completer' on install or manually ran 'npm install' in the tern submodule directory.""" if not PATH_TO_NODE: LOGGER.warning( 'Not using Tern completer: unable to find node' ) return False LOGGER.info( 'Using node binary from: %s', PATH_TO_NODE ) installed = os.path.exists( PATH_TO_TERN_BINARY ) if not installed: LOGGER.info( 'Not using Tern completer: not installed at %s', PATH_TO_TERN_BINARY ) return False return True def GlobalConfigExists( tern_config ): """Returns whether or not the global config file with the supplied path exists. This method primarily exists to allow testability and simply returns whether the supplied file exists.""" return os.path.exists( tern_config ) def FindTernProjectFile( starting_directory ): """Finds the path to either a Tern project file or the user's global Tern configuration file. If found, a tuple is returned containing the path and a boolean indicating if the path is to a .tern-project file. If not found, returns ( None, False ).""" for folder in utils.PathsToAllParentFolders( starting_directory ): tern_project = os.path.join( folder, '.tern-project' ) if os.path.exists( tern_project ): return tern_project, True # As described here: http://ternjs.net/doc/manual.html#server a global # .tern-config file is also supported for the Tern server. This can provide # meaningful defaults (for libs, and possibly also for require paths), so # don't warn if we find one. The point is that if the user has a .tern-config # set up, then she has deliberately done so and a ycmd warning is unlikely # to be anything other than annoying. tern_config = os.path.join( os.path.expanduser( '~' ), '.tern-config' ) if GlobalConfigExists( tern_config ): return tern_config, False return None, False class TernCompleter( Completer ): """Completer for JavaScript using tern.js: http://ternjs.net. The protocol is defined here: http://ternjs.net/doc/manual.html#protocol""" def __init__( self, user_options ): super().__init__( user_options ) self._server_keep_logfiles = user_options[ 'server_keep_logfiles' ] # Used to ensure that starting/stopping of the server is synchronised self._server_state_mutex = threading.Lock() self._do_tern_project_check = False self._server_handle = None self._server_port = None self._server_stdout = None self._server_stderr = None self._server_started = False self._server_working_dir = None self._server_project_file = None def _WarnIfMissingTernProject( self, request_data ): # The Tern server will operate without a .tern-project file. However, it # does not operate optimally, and will likely lead to issues reported that # JavaScript completion is not working properly. So we raise a warning if we # aren't able to detect some semblance of manual Tern configuration. # We do this check after the server has started because the server does # have nonzero use without a project file, however limited. We only do this # check once, though because the server can only handle one project at a # time. if not self._ServerIsRunning() or not self._do_tern_project_check: return self._do_tern_project_check = False filepath = request_data[ 'filepath' ] project_file, _ = FindTernProjectFile( filepath ) if not project_file: raise RuntimeError( 'Warning: Unable to detect a .tern-project file ' 'in the hierarchy before ' + filepath + ' and no global .tern-config file was found. ' 'This is required for accurate JavaScript ' 'completion. Please see the User Guide for ' 'details.' ) def _GetServerAddress( self ): return 'http://' + SERVER_HOST + ':' + str( self._server_port ) def ComputeCandidatesInner( self, request_data ): query = { 'type': 'completions', 'types': True, 'docs': True, 'filter': False, 'caseInsensitive': True, 'guess': False, 'sort': False, 'includeKeywords': False, 'expandWordForward': False, 'omitObjectPrototype': False } response = self._GetResponse( query, request_data[ 'start_codepoint' ], request_data ) completions = response.get( 'completions', [] ) tern_start_codepoint = response[ 'start' ][ 'ch' ] # Tern returns the range of the word in the file which it is replacing. This # may not be the same range that our "completion start column" calculation # decided (i.e. it might not strictly be an identifier according to our # rules). For example, when completing: # # require( '| # # with the cursor on |, tern returns something like 'test' (i.e. including # the single-quotes). Single-quotes are not a JavaScript identifier, so # should not normally be considered an identifier character, but by using # our own start_codepoint calculation, the inserted string would be: # # require( ''test' # # which is clearly incorrect. It should be: # # require( 'test' # # So, we use the start position that tern tells us to use. # We add 1 because tern offsets are 0-based and ycmd offsets are 1-based request_data[ 'start_codepoint' ] = tern_start_codepoint + 1 def BuildDoc( completion ): doc = completion.get( 'type', 'Unknown type' ) if 'doc' in completion: doc = doc + '\n' + completion[ 'doc' ] return doc return [ responses.BuildCompletionData( completion[ 'name' ], completion.get( 'type', '?' ), BuildDoc( completion ) ) for completion in completions ] def OnFileReadyToParse( self, request_data ): self._StartServer( request_data ) self._WarnIfMissingTernProject( request_data ) # Keep tern server up to date with the file data. We do this by sending an # empty request just containing the file data try: self._PostRequest( {}, request_data ) except Exception: # The server might not be ready yet or the server might not be running. # in any case, just ignore this we'll hopefully get another parse request # soon. pass def GetSubcommandsMap( self ): return { 'RestartServer': ( lambda self, request_data, args: self._RestartServer( request_data ) ), 'StopServer': ( lambda self, request_data, args: self._StopServer() ), 'GoToDefinition': ( lambda self, request_data, args: self._GoToDefinition( request_data ) ), 'GoTo': ( lambda self, request_data, args: self._GoToDefinition( request_data ) ), 'GoToReferences': ( lambda self, request_data, args: self._GoToReferences( request_data ) ), 'GetType': ( lambda self, request_data, args: self._GetType( request_data ) ), 'GetDoc': ( lambda self, request_data, args: self._GetDoc( request_data ) ), 'RefactorRename': ( lambda self, request_data, args: self._Rename( request_data, args ) ), } def SupportedFiletypes( self ): return [ 'javascript' ] def DebugInfo( self, request_data ): with self._server_state_mutex: extras = [ responses.DebugInfoItem( key = 'configuration file', value = self._server_project_file ), responses.DebugInfoItem( key = 'working directory', value = self._server_working_dir ) ] tern_server = responses.DebugInfoServer( name = 'Tern', handle = self._server_handle, executable = PATH_TO_TERN_BINARY, address = SERVER_HOST, port = self._server_port, logfiles = [ self._server_stdout, self._server_stderr ], extras = extras ) return responses.BuildDebugInfoResponse( name = 'JavaScript', servers = [ tern_server ] ) def Shutdown( self ): LOGGER.debug( 'Shutting down Tern server' ) self._StopServer() def ServerIsHealthy( self ): if not self._ServerIsRunning(): return False try: target = self._GetServerAddress() + '/ping' response = requests.get( target ) return response.status_code == requests.codes.ok except requests.ConnectionError: return False def _PostRequest( self, request, request_data ): """Send a raw request with the supplied request block, and return the server's response. If the server is not running, it is started. This method is useful where the query block is not supplied, i.e. where just the files are being updated. The request block should contain the optional query block only. The file data are added automatically.""" if not self._ServerIsRunning(): raise ValueError( 'Not connected to server' ) def MakeIncompleteFile( name, file_data ): return { 'type': 'full', 'name': name, 'text': file_data[ 'contents' ], } file_data = request_data.get( 'file_data', {} ) full_request = { 'files': [ MakeIncompleteFile( x, file_data[ x ] ) for x in file_data.keys() if 'javascript' in file_data[ x ][ 'filetypes' ] ], } full_request.update( request ) response = requests.post( self._GetServerAddress(), json = full_request ) if response.status_code != requests.codes.ok: raise RuntimeError( response.text ) return response.json() def _GetResponse( self, query, codepoint, request_data ): """Send a standard file/line request with the supplied query block, and return the server's response. If the server is not running, it is started. This method should be used for almost all requests. The exception is when just updating file data in which case _PostRequest should be used directly. The query block should contain the type and any parameters. The files, position, etc. are added automatically. NOTE: the |codepoint| parameter is usually the current cursor position, though it should be the "completion start column" codepoint for completion requests.""" def MakeTernLocation( request_data ): return { 'line': request_data[ 'line_num' ] - 1, 'ch': codepoint - 1 } full_query = { 'file': request_data[ 'filepath' ], 'end': MakeTernLocation( request_data ), 'lineCharPositions': True, } full_query.update( query ) return self._PostRequest( { 'query': full_query }, request_data ) def _ServerPathToAbsolute( self, path ): """Given a path returned from the tern server, return it as an absolute path. In particular, if the path is a relative path, return an absolute path assuming that it is relative to the working directory of the Tern server (which is the location of the .tern-project file if there is one).""" if os.path.isabs( path ): return path return os.path.join( self._server_working_dir, path ) def _SetServerProjectFileAndWorkingDirectory( self, request_data ): filepath = request_data[ 'filepath' ] self._server_project_file, is_project = FindTernProjectFile( filepath ) working_dir = request_data.get( 'working_dir', utils.GetCurrentDirectory() ) if not self._server_project_file: LOGGER.warning( 'No .tern-project file detected: %s', filepath ) self._server_working_dir = working_dir else: LOGGER.info( 'Detected Tern configuration file at: %s', self._server_project_file ) self._server_working_dir = ( os.path.dirname( self._server_project_file ) if is_project else working_dir ) LOGGER.info( 'Tern paths are relative to: %s', self._server_working_dir ) def _StartServer( self, request_data ): with self._server_state_mutex: return self._StartServerNoLock( request_data ) def _StartServerNoLock( self, request_data ): if self._server_started: return self._server_started = True LOGGER.info( 'Starting Tern server...' ) self._SetServerProjectFileAndWorkingDirectory( request_data ) self._server_port = utils.GetUnusedLocalhostPort() command = [ PATH_TO_NODE, PATH_TO_TERN_BINARY, '--port', str( self._server_port ), '--host', SERVER_HOST, '--persistent', '--no-port-file' ] if LOGGER.isEnabledFor( logging.DEBUG ): command.append( '--verbose' ) LOGGER.debug( 'Starting tern with the following command: %s', command ) self._server_stdout = utils.CreateLogfile( LOGFILE_FORMAT.format( port = self._server_port, std = 'stdout' ) ) self._server_stderr = utils.CreateLogfile( LOGFILE_FORMAT.format( port = self._server_port, std = 'stderr' ) ) # We need to open a pipe to stdin or the Tern server is killed. # See https://github.com/ternjs/tern/issues/740#issuecomment-203979749 # For unknown reasons, this is only needed on Windows and for Python # 3.4+ on other platforms. with utils.OpenForStdHandle( self._server_stdout ) as stdout: with utils.OpenForStdHandle( self._server_stderr ) as stderr: self._server_handle = utils.SafePopen( command, stdin = PIPE, stdout = stdout, stderr = stderr, cwd = self._server_working_dir ) if self._ServerIsRunning(): LOGGER.info( 'Tern Server started with pid %d listening on port %d', self._server_handle.pid, self._server_port ) LOGGER.info( 'Tern Server log files are %s and %s', self._server_stdout, self._server_stderr ) self._do_tern_project_check = True else: LOGGER.warning( 'Tern server did not start successfully' ) def _RestartServer( self, request_data ): with self._server_state_mutex: self._StopServerNoLock() self._StartServerNoLock( request_data ) def _StopServer( self ): with self._server_state_mutex: return self._StopServerNoLock() def _StopServerNoLock( self ): if self._ServerIsRunning(): LOGGER.info( 'Stopping Tern server with PID %s', self._server_handle.pid ) self._server_handle.terminate() try: utils.WaitUntilProcessIsTerminated( self._server_handle, timeout = 5 ) LOGGER.info( 'Tern server stopped' ) except RuntimeError: LOGGER.exception( 'Error while stopping Tern server' ) self._CleanUp() def _CleanUp( self ): utils.CloseStandardStreams( self._server_handle ) self._do_tern_project_check = False self._server_handle = None self._server_port = None if not self._server_keep_logfiles: if self._server_stdout: utils.RemoveIfExists( self._server_stdout ) self._server_stdout = None if self._server_stderr: utils.RemoveIfExists( self._server_stderr ) self._server_stderr = None self._server_started = False self._server_working_dir = None self._server_project_file = None def _ServerIsRunning( self ): return utils.ProcessIsRunning( self._server_handle ) def _GetType( self, request_data ): query = { 'type': 'type', } response = self._GetResponse( query, request_data[ 'column_codepoint' ], request_data ) return responses.BuildDisplayMessageResponse( response[ 'type' ] ) def _GetDoc( self, request_data ): # Note: we use the 'type' request because this is the best # way to get the name, type and doc string. The 'documentation' request # doesn't return the 'name' (strangely), whereas the 'type' request returns # the same docs with extra info. query = { 'type': 'type', 'docFormat': 'full', 'types': True } response = self._GetResponse( query, request_data[ 'column_codepoint' ], request_data ) doc_string = 'Name: {name}\nType: {type}\n\n{doc}'.format( name = response.get( 'name', 'Unknown' ), type = response.get( 'type', 'Unknown' ), doc = response.get( 'doc', 'No documentation available' ) ) return responses.BuildDetailedInfoResponse( doc_string ) def _GoToDefinition( self, request_data ): query = { 'type': 'definition', } response = self._GetResponse( query, request_data[ 'column_codepoint' ], request_data ) filepath = self._ServerPathToAbsolute( response[ 'file' ] ) return responses.BuildGoToResponseFromLocation( _BuildLocation( GetFileLines( request_data, filepath ), filepath, response[ 'start' ][ 'line' ], response[ 'start' ][ 'ch' ] ) ) def _GoToReferences( self, request_data ): query = { 'type': 'refs', } response = self._GetResponse( query, request_data[ 'column_codepoint' ], request_data ) def BuildRefResponse( ref ): filepath = self._ServerPathToAbsolute( ref[ 'file' ] ) return responses.BuildGoToResponseFromLocation( _BuildLocation( GetFileLines( request_data, filepath ), filepath, ref[ 'start' ][ 'line' ], ref[ 'start' ][ 'ch' ] ) ) return [ BuildRefResponse( ref ) for ref in response[ 'refs' ] ] def _Rename( self, request_data, args ): if len( args ) != 1: raise ValueError( 'Please specify a new name to rename it to.\n' 'Usage: RefactorRename <new name>' ) query = { 'type': 'rename', 'newName': args[ 0 ], } response = self._GetResponse( query, request_data[ 'column_codepoint' ], request_data ) # Tern response format: # 'changes': [ # { # 'file' (potentially relative path) # 'start' { # 'line' # 'ch' (codepoint offset) # } # 'end' { # 'line' # 'ch' (codepoint offset) # } # 'text' # } # ] # ycmd response format: # # { # 'fixits': [ # 'chunks': (list<Chunk>) [ # { # 'replacement_text', # 'range' (Range) { # 'start_' (Location): { # 'line_number_', # 'column_number_', (byte offset) # 'filename_' (note: absolute path!) # }, # 'end_' (Location): { # 'line_number_', # 'column_number_', (byte offset) # 'filename_' (note: absolute path!) # } # } # } # ], # 'location' (Location) { # 'line_number_', # 'column_number_', # 'filename_' (note: absolute path!) # } # # ] # } def BuildRange( file_contents, filename, start, end ): return responses.Range( _BuildLocation( file_contents, filename, start[ 'line' ], start[ 'ch' ] ), _BuildLocation( file_contents, filename, end[ 'line' ], end[ 'ch' ] ) ) def BuildFixItChunk( change ): filepath = self._ServerPathToAbsolute( change[ 'file' ] ) file_contents = GetFileLines( request_data, filepath ) return responses.FixItChunk( change[ 'text' ], BuildRange( file_contents, filepath, change[ 'start' ], change[ 'end' ] ) ) # From an API perspective, Refactor and FixIt are the same thing - it just # applies a set of changes to a set of files. So we re-use all of the # existing FixIt infrastructure. return responses.BuildFixItResponse( [ responses.FixIt( responses.Location( request_data[ 'line_num' ], request_data[ 'column_num' ], request_data[ 'filepath' ] ), [ BuildFixItChunk( x ) for x in response[ 'changes' ] ] ) ] ) def _BuildLocation( file_contents, filename, line, ch ): # tern returns codepoint offsets, but we need byte offsets, so we must # convert return responses.Location( line = line + 1, column = utils.CodepointOffsetToByteOffset( file_contents[ line ], ch + 1 ), filename = os.path.realpath( filename ) ) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/javascriptreact/��������������������������������������0000775�0000000�0000000�00000000000�13746324601�0023435�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/javascriptreact/hook.py�������������������������������0000664�0000000�0000000�00000001700�13746324601�0024745�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from ycmd.completers.typescript.typescript_completer import ( ShouldEnableTypeScriptCompleter, TypeScriptCompleter ) def GetCompleter( user_options ): if not ShouldEnableTypeScriptCompleter( user_options ): return None return TypeScriptCompleter( user_options ) ����������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/language_server/��������������������������������������0000775�0000000�0000000�00000000000�13746324601�0023421�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/language_server/__init__.py���������������������������0000664�0000000�0000000�00000000000�13746324601�0025520�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/language_server/generic_lsp_completer.py��������������0000664�0000000�0000000�00000007214�13746324601�0030343�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import string from ycmd import responses, utils from ycmd.completers.language_server import language_server_completer class GenericLSPCompleter( language_server_completer.LanguageServerCompleter ): def __init__( self, user_options, server_settings ): self._name = server_settings[ 'name' ] self._supported_filetypes = server_settings[ 'filetypes' ] self._project_root_files = server_settings.get( 'project_root_files', [] ) self._capabilities = server_settings.get( 'capabilities', {} ) self._command_line = server_settings.get( 'cmdline' ) self._port = server_settings.get( 'port' ) if self._port: connection_type = 'tcp' if self._port == '*': self._port = utils.GetUnusedLocalhostPort() else: connection_type = 'stdio' if self._command_line: self._command_line[ 0 ] = utils.FindExecutable( self._command_line[ 0 ] ) for idx in range( len( self._command_line ) ): self._command_line[ idx ] = string.Template( self._command_line[ idx ] ).safe_substitute( { 'port': self._port } ) super().__init__( user_options, connection_type ) def GetProjectRootFiles( self ): return self._project_root_files def Language( self ): return self._name def GetServerName( self ): return self._name + 'Completer' def GetCommandLine( self ): return self._command_line def GetCustomSubcommands( self ): return { 'GetHover': lambda self, request_data, args: self._GetHover( request_data ) } def _GetHover( self, request_data ): raw_hover = self.GetHoverResponse( request_data ) if isinstance( raw_hover, dict ): # Both MarkedString and MarkupContent contain 'value' key. # MarkupContent is the only one not deprecated. return responses.BuildDetailedInfoResponse( raw_hover[ 'value' ] ) if isinstance( raw_hover, str ): # MarkedString might be just a string. return responses.BuildDetailedInfoResponse( raw_hover ) # If we got this far, this is a list of MarkedString objects. lines = [] for marked_string in raw_hover: if isinstance( marked_string, str ): lines.append( marked_string ) else: lines.append( marked_string[ 'value' ] ) return responses.BuildDetailedInfoResponse( '\n'.join( lines ) ) def GetCodepointForCompletionRequest( self, request_data ): if request_data[ 'force_semantic' ]: return request_data[ 'column_codepoint' ] return super().GetCodepointForCompletionRequest( request_data ) def SupportedFiletypes( self ): return self._supported_filetypes def ExtraCapabilities( self ): return self._capabilities def WorkspaceConfigurationResponse( self, request ): if self._capabilities.get( 'workspace', {} ).get( 'configuration' ): sections_to_config_map = self._settings.get( 'config_sections', {} ) return [ sections_to_config_map.get( item.get( 'section', '' ) ) for item in request[ 'params' ][ 'items' ] ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/language_server/language_server_completer.py����������0000664�0000000�0000000�00000352232�13746324601�0031225�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2017-2020 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 <http://www.gnu.org/licenses/>. from functools import partial import abc import collections import contextlib import json import logging import os import socket import time import queue import subprocess import threading from watchdog.events import PatternMatchingEventHandler from watchdog.observers import Observer from ycmd import extra_conf_store, responses, utils from ycmd.completers.completer import Completer, CompletionsCache from ycmd.completers.completer_utils import GetFileContents, GetFileLines from ycmd.utils import LOGGER from ycmd.completers.language_server import language_server_protocol as lsp NO_HOVER_INFORMATION = 'No hover information.' # All timeout values are in seconds REQUEST_TIMEOUT_COMPLETION = 5 REQUEST_TIMEOUT_INITIALISE = 30 REQUEST_TIMEOUT_COMMAND = 30 CONNECTION_TIMEOUT = 5 # Size of the notification ring buffer MAX_QUEUED_MESSAGES = 250 PROVIDERS_MAP = { 'codeActionProvider': ( lambda self, request_data, args: self.GetCodeActions( request_data, args ) ), 'declarationProvider': ( lambda self, request_data, args: self.GoTo( request_data, [ 'Declaration' ] ) ), 'definitionProvider': ( lambda self, request_data, args: self.GoTo( request_data, [ 'Definition' ] ) ), ( 'definitionProvider', 'declarationProvider' ): ( lambda self, request_data, args: self.GoTo( request_data, [ 'Definition', 'Declaration' ] ) ), 'documentFormattingProvider': ( lambda self, request_data, args: self.Format( request_data ) ), 'executeCommandProvider': ( lambda self, request_data, args: self.ExecuteCommand( request_data, args ) ), 'implementationProvider': ( lambda self, request_data, args: self.GoTo( request_data, [ 'Implementation' ] ) ), 'referencesProvider': ( lambda self, request_data, args: self.GoTo( request_data, [ 'References' ] ) ), 'renameProvider': ( lambda self, request_data, args: self.RefactorRename( request_data, args ) ), 'typeDefinitionProvider': ( lambda self, request_data, args: self.GoTo( request_data, [ 'TypeDefinition' ] ) ), 'workspaceSymbolProvider': ( lambda self, request_data, args: self.GoToSymbol( request_data, args ) ), } # Each command is mapped to a list of providers. This allows a command to use # another provider if the LSP server doesn't support the main one. For instance, # GoToDeclaration is mapped to the same provider as GoToDefinition if there is # no declaration provider. A tuple of providers is also allowed for commands # like GoTo where it's convenient to jump to the declaration if already on the # definition and vice versa. DEFAULT_SUBCOMMANDS_MAP = { 'ExecuteCommand': [ 'executeCommandProvider' ], 'FixIt': [ 'codeActionProvider' ], 'GoToDefinition': [ 'definitionProvider' ], 'GoToDeclaration': [ 'declarationProvider', 'definitionProvider' ], 'GoTo': [ ( 'definitionProvider', 'declarationProvider' ), 'definitionProvider' ], 'GoToType': [ 'typeDefinitionProvider' ], 'GoToImplementation': [ 'implementationProvider' ], 'GoToReferences': [ 'referencesProvider' ], 'RefactorRename': [ 'renameProvider' ], 'Format': [ 'documentFormattingProvider' ], 'GoToSymbol': [ 'workspaceSymbolProvider' ], } class NoHoverInfoException( Exception ): """ Raised instead of RuntimeError for empty hover responses, to allow completers to easily distinguish empty hover from other errors.""" pass # pragma: no cover class ResponseTimeoutException( Exception ): """Raised by LanguageServerConnection if a request exceeds the supplied time-to-live.""" pass # pragma: no cover class ResponseAbortedException( Exception ): """Raised by LanguageServerConnection if a request is canceled due to the server shutting down.""" pass # pragma: no cover class ResponseFailedException( Exception ): """Raised by LanguageServerConnection if a request returns an error""" pass # pragma: no cover class IncompatibleCompletionException( Exception ): """Internal exception returned when a completion item is encountered which is not supported by ycmd, or where the completion item is invalid.""" pass # pragma: no cover class LanguageServerConnectionTimeout( Exception ): """Raised by LanguageServerConnection if the connection to the server is not established with the specified timeout.""" pass # pragma: no cover class LanguageServerConnectionStopped( Exception ): """Internal exception raised by LanguageServerConnection when the server is successfully shut down according to user request.""" pass # pragma: no cover class Response: """Represents a blocking pending request. LanguageServerCompleter handles create an instance of this class for each request that expects a response and wait for its response synchronously by calling |AwaitResponse|. The LanguageServerConnection message pump thread calls |ResponseReceived| when the associated response is read, which triggers the |AwaitResponse| method to handle the actual response""" def __init__( self, response_callback=None ): """In order to receive a callback in the message pump thread context, supply a method taking ( response, message ) in |response_callback|. Note that |response| is _this object_, not the calling object, and message is the message that was received. NOTE: This should not normally be used. Instead users should synchronously wait on AwaitResponse.""" self._event = threading.Event() self._message = None self._response_callback = response_callback def ResponseReceived( self, message ): """Called by the message pump thread when the response with corresponding ID is received from the server. Triggers the message received event and calls any configured message-pump-thread callback.""" self._message = message self._event.set() if self._response_callback: self._response_callback( self, message ) def Abort( self ): """Called when the server is shutting down.""" self.ResponseReceived( None ) def AwaitResponse( self, timeout ): """Called by clients to wait synchronously for either a response to be received or for |timeout| seconds to have passed. Returns the message, or: - throws ResponseFailedException if the request fails - throws ResponseTimeoutException in case of timeout - throws ResponseAbortedException in case the server is shut down.""" self._event.wait( timeout ) if not self._event.is_set(): raise ResponseTimeoutException( 'Response Timeout' ) if self._message is None: raise ResponseAbortedException( 'Response Aborted' ) if 'error' in self._message: error = self._message[ 'error' ] raise ResponseFailedException( 'Request failed: ' f'{ error.get( "code" ) or 0 }' ': ' f'{ error.get( "message" ) or "No message" }' ) return self._message class LanguageServerConnection( threading.Thread ): """ Abstract language server communication object. This connection runs as a thread and is generally only used directly by LanguageServerCompleter, but is instantiated, started and stopped by concrete LanguageServerCompleter implementations. Implementations of this class are required to provide the following methods: - TryServerConnectionBlocking: Connect to the server and return when the connection is established - Shutdown: Close any sockets or channels prior to the thread exit - IsConnected: Whether the socket is connected - WriteData: Write some data to the server - ReadData: Read some data from the server, blocking until some data is available Threads: LSP is by its nature an asynchronous protocol. There are request-reply like requests and unsolicited notifications. Receipt of the latter is mandatory, so we cannot rely on there being a bottle thread executing a client request. So we need a message pump and dispatch thread. This is actually the LanguageServerConnection, which implements Thread. It's main method simply listens on the socket/stream and dispatches complete messages to the LanguageServerCompleter. It does this: - For requests: Using python event objects, wrapped in the Response class - For notifications: via a synchronized Queue. NOTE: Some handling is done in the dispatch thread. There are certain notifications which we have to handle when we get them, such as: - Initialization messages - Diagnostics In these cases, we allow some code to be executed inline within the dispatch thread, as there is no other thread guaranteed to execute. These are handled by callback functions and mutexes. Using this class in concrete LanguageServerCompleter implementations: Startup - Call Start() and AwaitServerConnection() - AwaitServerConnection() throws LanguageServerConnectionTimeout if the server fails to connect in a reasonable time. Shutdown - Call Stop() prior to shutting down the downstream server (see LanguageServerCompleter.ShutdownServer to do that part) - Call Close() to close any remaining streams. Do this in a request thread. DO NOT CALL THIS FROM THE DISPATCH THREAD. That is, Close() must not be called from a callback supplied to GetResponseAsync, or in any callback or method with a name like "*InPollThread". The result would be a deadlock. Footnote: Why does this interface exist? Language servers are at liberty to provide their communication interface over any transport. Typically, this is either stdio or a socket (though some servers require multiple sockets). This interface abstracts the implementation detail of the communication from the transport, allowing concrete completers to choose the right transport according to the downstream server (i.e. Whatever works best). If in doubt, use the StandardIOLanguageServerConnection as that is the simplest. Socket-based connections often require the server to connect back to us, which can lead to complexity and possibly blocking. """ @abc.abstractmethod def TryServerConnectionBlocking( self ): pass # pragma: no cover def _CancelWatchdogThreads( self ): for observer in self._observers: observer.stop() observer.join() def Shutdown( self ): self._CancelWatchdogThreads() @abc.abstractmethod def IsConnected( self ): pass @abc.abstractmethod def WriteData( self, data ): pass # pragma: no cover @abc.abstractmethod def ReadData( self, size=-1 ): pass # pragma: no cover def __init__( self, project_directory, watchdog_factory, workspace_conf_handler, notification_handler = None ): super().__init__() self._watchdog_factory = watchdog_factory self._workspace_conf_handler = workspace_conf_handler self._project_directory = project_directory self._last_id = 0 self._responses = {} self._response_mutex = threading.Lock() self._notifications = queue.Queue( maxsize=MAX_QUEUED_MESSAGES ) self._connection_event = threading.Event() self._stop_event = threading.Event() self._notification_handler = notification_handler self._collector = RejectCollector() self._observers = [] @contextlib.contextmanager def CollectApplyEdits( self, collector ): old_collector = self._collector self._collector = collector try: yield finally: self._collector = old_collector def run( self ): try: # Wait for the connection to fully establish (this runs in the thread # context, so we block until a connection is received or there is a # timeout, which throws an exception) self.TryServerConnectionBlocking() self._connection_event.set() # Blocking loop which reads whole messages and calls _DispatchMessage self._ReadMessages() except LanguageServerConnectionStopped: # Abort any outstanding requests with self._response_mutex: for _, response in self._responses.items(): response.Abort() self._responses.clear() LOGGER.debug( 'Connection was closed cleanly' ) except Exception: LOGGER.exception( 'The language server communication channel closed ' 'unexpectedly. Issue a RestartServer command to ' 'recover.' ) # Abort any outstanding requests with self._response_mutex: for _, response in self._responses.items(): response.Abort() self._responses.clear() # Close any remaining sockets or files self.Shutdown() def Start( self ): # Wraps the fact that this class inherits (privately, in a sense) from # Thread. self.start() def Stop( self ): self._stop_event.set() def Close( self ): self.Shutdown() try: self.join() except RuntimeError: LOGGER.exception( "Shutting down dispatch thread while it isn't active" ) # This actually isn't a problem in practice. def IsStopped( self ): return self._stop_event.is_set() def NextRequestId( self ): with self._response_mutex: self._last_id += 1 return self._last_id def GetResponseAsync( self, request_id, message, response_callback=None ): """Issue a request to the server and return immediately. If a response needs to be handled, supply a method taking ( response, message ) in response_callback. Note |response| is the instance of Response and message is the message received from the server. Returns the Response instance created.""" response = Response( response_callback ) with self._response_mutex: assert request_id not in self._responses self._responses[ request_id ] = response LOGGER.debug( 'TX: Sending message: %r', message ) self.WriteData( message ) return response def GetResponse( self, request_id, message, timeout ): """Issue a request to the server and await the response. See Response.AwaitResponse for return values and exceptions.""" response = self.GetResponseAsync( request_id, message ) return response.AwaitResponse( timeout ) def SendNotification( self, message ): """Issue a notification to the server. A notification is "fire and forget"; no response will be received and nothing is returned.""" LOGGER.debug( 'TX: Sending notification: %r', message ) self.WriteData( message ) def SendResponse( self, message ): """Send a response message. This is a message which is not a notification, but still requires no further response from the server.""" LOGGER.debug( 'TX: Sending response: %r', message ) self.WriteData( message ) def AwaitServerConnection( self ): """Language server completer implementations should call this after starting the server and the message pump (Start()) to await successful connection to the server being established. Returns no meaningful value, but may throw LanguageServerConnectionTimeout in the event that the server does not connect promptly. In that case, clients should shut down their server and reset their state.""" self._connection_event.wait( timeout = CONNECTION_TIMEOUT ) if not self._connection_event.is_set(): raise LanguageServerConnectionTimeout( 'Timed out waiting for server to connect' ) def _ReadMessages( self ): """Main message pump. Within the message pump thread context, reads messages from the socket/stream by calling self.ReadData in a loop and dispatch complete messages by calling self._DispatchMessage. When the server is shut down cleanly, raises LanguageServerConnectionStopped""" data = bytes( b'' ) while True: data, read_bytes, headers = self._ReadHeaders( data ) if 'Content-Length' not in headers: # FIXME: We could try and recover this, but actually the message pump # just fails. raise ValueError( "Missing 'Content-Length' header" ) content_length = int( headers[ 'Content-Length' ] ) # We need to read content_length bytes for the payload of this message. # This may be in the remainder of `data`, but equally we may need to read # more data from the socket. content = bytes( b'' ) content_read = 0 if read_bytes < len( data ): # There are bytes left in data, use them data = data[ read_bytes: ] # Read up to content_length bytes from data content_to_read = min( content_length, len( data ) ) content += data[ : content_to_read ] content_read += len( content ) read_bytes = content_to_read while content_read < content_length: # There is more content to read, but data is exhausted - read more from # the socket data = self.ReadData( content_length - content_read ) content_to_read = min( content_length - content_read, len( data ) ) content += data[ : content_to_read ] content_read += len( content ) read_bytes = content_to_read LOGGER.debug( 'RX: Received message: %r', content ) # lsp will convert content to Unicode self._DispatchMessage( lsp.Parse( content ) ) # We only consumed len( content ) of data. If there is more, we start # again with the remainder and look for headers data = data[ read_bytes : ] def _ReadHeaders( self, data ): """Starting with the data in |data| read headers from the stream/socket until a full set of headers has been consumed. Returns a tuple ( - data: any remaining unused data from |data| or the socket - read_bytes: the number of bytes of returned data that have been consumed - headers: a dictionary whose keys are the header names and whose values are the header values )""" # LSP defines only 2 headers, of which only 1 is useful (Content-Length). # Headers end with an empty line, and there is no guarantee that a single # socket or stream read will contain only a single message, or even a whole # message. headers_complete = False prefix = bytes( b'' ) headers = {} while not headers_complete: read_bytes = 0 last_line = 0 if len( data ) == 0: data = self.ReadData() while read_bytes < len( data ): if utils.ToUnicode( data[ read_bytes: ] )[ 0 ] == '\n': line = prefix + data[ last_line : read_bytes ].strip() prefix = bytes( b'' ) last_line = read_bytes if not line.strip(): headers_complete = True read_bytes += 1 break else: try: key, value = utils.ToUnicode( line ).split( ':', 1 ) headers[ key.strip() ] = value.strip() except Exception: LOGGER.exception( 'Received invalid protocol data from server: ' + str( line ) ) raise read_bytes += 1 if not headers_complete: prefix = data[ last_line : ] data = bytes( b'' ) return data, read_bytes, headers def _HandleDynamicRegistrations( self, request ): for reg in request[ 'params' ][ 'registrations' ]: if reg[ 'method' ] == 'workspace/didChangeWatchedFiles': globs = [] for watcher in reg[ 'registerOptions' ][ 'watchers' ]: # TODO: Take care of watcher kinds. Not everything needs # to be watched for create, modify *and* delete actions. pattern = os.path.join( self._project_directory, watcher[ 'globPattern' ] ) if os.path.isdir( pattern ): pattern = os.path.join( pattern, '**' ) globs.append( pattern ) observer = Observer() observer.schedule( self._watchdog_factory( globs ), self._project_directory, recursive = True ) observer.start() self._observers.append( observer ) self.SendResponse( lsp.Void( request ) ) def _ServerToClientRequest( self, request ): method = request[ 'method' ] if method == 'workspace/applyEdit': self._collector.CollectApplyEdit( request, self ) elif method == 'workspace/configuration': response = self._workspace_conf_handler( request ) if response is not None: self.SendResponse( lsp.Accept( request, response ) ) else: self.SendResponse( lsp.Reject( request, lsp.Errors.MethodNotFound ) ) elif method == 'client/registerCapability': self._HandleDynamicRegistrations( request ) elif method == 'client/unregisterCapability': for reg in request[ 'params' ][ 'unregisterations' ]: if reg[ 'method' ] == 'workspace/didChangeWatchedFiles': self._CancelWatchdogThreads() self.SendResponse( lsp.Void( request ) ) else: # Reject the request self.SendResponse( lsp.Reject( request, lsp.Errors.MethodNotFound ) ) def _DispatchMessage( self, message ): """Called in the message pump thread context when a complete message was read. For responses, calls the Response object's ResponseReceived method, or for notifications (unsolicited messages from the server), simply accumulates them in a Queue which is polled by the long-polling mechanism in LanguageServerCompleter.""" if 'id' in message: message_id = message[ 'id' ] if message_id is None: return if 'method' in message: # This is a server->client request, which requires a response. self._ServerToClientRequest( message ) else: # This is a response to the message with id message[ 'id' ] with self._response_mutex: assert message_id in self._responses self._responses[ message_id ].ResponseReceived( message ) del self._responses[ message_id ] else: # This is a notification self._AddNotificationToQueue( message ) # If there is an immediate (in-message-pump-thread) handler configured, # call it. if self._notification_handler: try: self._notification_handler( self, message ) except Exception: LOGGER.exception( 'Handling message in poll thread failed: %s', message ) def _AddNotificationToQueue( self, message ): while True: try: self._notifications.put_nowait( message ) return except queue.Full: pass # The queue (ring buffer) is full. This indicates either a slow # consumer or the message poll is not running. In any case, rather than # infinitely queueing, discard the oldest message and try again. try: self._notifications.get_nowait() except queue.Empty: # This is only a theoretical possibility to prevent this thread # blocking in the unlikely event that all elements are removed from # the queue between put_nowait and get_nowait. Unfortunately, this # isn't testable without a debugger, so coverage will show up red. pass # pragma: no cover class StandardIOLanguageServerConnection( LanguageServerConnection ): """Concrete language server connection using stdin/stdout to communicate with the server. This should be the default choice for concrete completers.""" def __init__( self, project_directory, watchdog_factory, server_stdin, server_stdout, workspace_conf_handler, notification_handler = None ): super().__init__( project_directory, watchdog_factory, workspace_conf_handler, notification_handler ) self._server_stdin = server_stdin self._server_stdout = server_stdout # NOTE: All access to the stdin/out objects must be synchronised due to the # long-running `read` operations that are done on stdout, and how our # shutdown request will come from another (arbitrary) thread. It is not # legal in Python to close a stdio file while there is a pending read. This # can lead to IOErrors due to "concurrent operations' on files. # See https://stackoverflow.com/q/29890603/2327209 self._stdin_lock = threading.Lock() self._stdout_lock = threading.Lock() def TryServerConnectionBlocking( self ): # standard in/out don't need to wait for the server to connect to us return True def IsConnected( self ): # TODO ? self._server_stdin.closed / self._server_stdout.closed? return True def Shutdown( self ): super().Shutdown() with self._stdin_lock: if not self._server_stdin.closed: self._server_stdin.close() with self._stdout_lock: if not self._server_stdout.closed: self._server_stdout.close() def WriteData( self, data ): with self._stdin_lock: self._server_stdin.write( data ) self._server_stdin.flush() def ReadData( self, size=-1 ): data = None with self._stdout_lock: if not self._server_stdout.closed: if size > -1: data = self._server_stdout.read( size ) else: data = self._server_stdout.readline() if not data: # No data means the connection was severed. Connection severed when (not # self.IsStopped()) means the server died unexpectedly. if self.IsStopped(): raise LanguageServerConnectionStopped() raise RuntimeError( "Connection to server died" ) return data class TCPSingleStreamConnection( LanguageServerConnection ): # Connection timeout in seconds TCP_CONNECT_TIMEOUT = 10 def __init__( self, project_directory, watchdog_factory, port, workspace_conf_handler, notification_handler = None ): super().__init__( project_directory, watchdog_factory, workspace_conf_handler, notification_handler ) self.port = port self._client_socket = None def TryServerConnectionBlocking( self ): LOGGER.info( "Connecting to localhost:%s", self.port ) expiration = time.time() + TCPSingleStreamConnection.TCP_CONNECT_TIMEOUT reason = RuntimeError( f"Timeout connecting to port { self.port }" ) while True: if time.time() > expiration: LOGGER.error( "Timed out after %s seconds connecting to port %s", TCPSingleStreamConnection.TCP_CONNECT_TIMEOUT, self.port ) raise reason try: self._client_socket = socket.create_connection( ( '127.0.0.1', self.port ) ) LOGGER.info( "Language server connection successful on port %s", self.port ) return True except IOError as e: reason = e time.sleep( 0.1 ) def IsConnected( self ): return bool( self._client_socket ) def Shutdown( self ): super().Shutdown() self._client_socket.close() def WriteData( self, data ): assert self._connection_event.isSet() assert self._client_socket total_sent = 0 while total_sent < len( data ): try: sent = self._client_socket.send( data[ total_sent: ] ) except OSError: sent = 0 if sent == 0: raise RuntimeError( 'Socket was closed when writing' ) total_sent += sent def ReadData( self, size=-1 ): assert self._connection_event.isSet() assert self._client_socket chunks = [] bytes_read = 0 while bytes_read < size or size < 0: try: if size < 0: chunk = self._client_socket.recv( 2048 ) else: chunk = self._client_socket.recv( min( size - bytes_read , 2048 ) ) except OSError: chunk = '' if chunk == '': # The socket was closed if self.IsStopped(): raise LanguageServerConnectionStopped() raise RuntimeError( 'Scoket closed unexpectedly when reading' ) if size < 0: # We just return whatever we read return chunk # Otherwise, keep reading if there's more data requested chunks.append( chunk ) bytes_read += len( chunk ) return b''.join( chunks ) class LanguageServerCompleter( Completer ): """ Abstract completer implementation for Language Server Protocol. Concrete implementations are required to: - Handle downstream server state and create a LanguageServerConnection, returning it in GetConnection - Set its notification handler to self.GetDefaultNotificationHandler() - See below for Startup/Shutdown instructions - Optionally handle server-specific command responses in HandleServerCommandResponse - Optionally override GetCustomSubcommands to return subcommand handlers that cannot be detected from the capabilities response. - Optionally override AdditionalLogFiles for logs other than stderr - Optionally override ExtraDebugItems for anything that should be in the /debug_info response, that isn't covered by default - Optionally override GetServerEnvironment if the server needs to be run with specific environment variables. - Implement the following Completer abstract methods: - GetServerName - GetCommandLine - SupportedFiletypes - DebugInfo - Shutdown - ServerIsHealthy : Return True if the server is _running_ - StartServer : Return True if the server was started. - Optionally override methods to customise behavior: - ConvertNotificationToMessage - GetCompleterName - GetProjectDirectory - GetProjectRootFiles - GetTriggerCharacters - GetDefaultNotificationHandler - HandleNotificationInPollThread - Language Startup - Startup is initiated for you in OnFileReadyToParse - The StartServer method is only called once (reset with ServerReset) - See also LanguageServerConnection requirements Shutdown - Call ShutdownServer and wait for the downstream server to exit - Call ServerReset to clear down state - See also LanguageServerConnection requirements Completions - The implementation should not require any code to support completions - (optional) Override GetCodepointForCompletionRequest if you wish to change the completion position (e.g. if you want to pass the "query" to the server) Diagnostics - The implementation should not require any code to support diagnostics Sub-commands - The sub-commands map is bespoke to the implementation, but generally, this class attempts to provide all of the pieces where it can generically. - By default, the subcommands are detected from the server's capabilities. The logic for this is in DEFAULT_SUBCOMMANDS_MAP (and implemented by _DiscoverSubcommandSupport). - By default FixIt should work, but for example, jdt.ls doesn't implement CodeActions correctly and forces clients to handle it differently. For these cases, completers can override any of: - CodeActionLiteralToFixIt - CodeActionCommandToFixIt - CommandToFixIt - Other commands not covered by DEFAULT_SUBCOMMANDS_MAP are bespoke to the completer and should be returned by GetCustomSubcommands: - GetType/GetDoc are bespoke to the downstream server, though this class provides GetHoverResponse which is useful in this context. GetCustomSubcommands needs not contain GetType/GetDoc if the member functions implementing GetType/GetDoc are named GetType/GetDoc. """ def GetConnection( self ): """Method that can be implemented by derived classes to return an instance of LanguageServerConnection appropriate for the language server in question""" return self._connection def HandleServerCommandResponse( self, request_data, edits, command_response ): pass # pragma: no cover # Resolve all completion items up-front RESOLVE_ALL = True # Don't resolve any completion items, but prepare them for resolve RESOLVE_NONE = False def __init__( self, user_options, connection_type = 'stdio' ): super().__init__( user_options ) self._connection_type = connection_type # _server_info_mutex synchronises access to the state of the # LanguageServerCompleter object. There are a number of threads at play # here which might want to change properties of this object: # - Each client request (handled by concrete completers) executes in a # separate thread and might call methods requiring us to synchronise the # server's view of file state with our own. We protect from clobbering # by doing all server-file-state operations under this mutex. # - There are certain events that we handle in the message pump thread, # like some parts of initialization. We must protect against concurrent # access to our internal state (such as the server file state, and # stored data about the server itself) when we are calling methods on # this object from the message pump). We synchronise on this mutex for # that. # - We need to make sure that multiple client requests don't try to start # or stop the server simultaneously, so we also do all server # start/stop/etc. operations under this mutex # - Acquiring this mutex from the poll thread can lead to deadlocks. # Currently, this is avoided by using _latest_diagnostics_mutex to # access _latest_diagnostics, as that is the only resource shared with # the poll thread. self._server_info_mutex = threading.Lock() self.ServerReset() # LSP allows servers to return an incomplete list of completions. The cache # cannot be used in that case and the current column must be sent to the # language server for the subsequent completion requests; otherwise, the # server will return the same incomplete list. When that list is complete, # two cases are considered: # - the starting column was sent to the server: cache is valid for the # whole completion; # - the current column was sent to the server: cache stays valid while the # cached query is a prefix of the subsequent queries. self._completions_cache = LanguageServerCompletionsCache() self._completer_name = self.__class__.__name__.replace( 'Completer', '' ) self._language = self._completer_name.lower() self._on_file_ready_to_parse_handlers = [] self.RegisterOnFileReadyToParse( lambda self, request_data: self._UpdateServerWithFileContents( request_data ) ) self._signature_help_disabled = user_options[ 'disable_signature_help' ] self._server_keep_logfiles = user_options[ 'server_keep_logfiles' ] self._stdout_file = None self._stderr_file = None self._server_started = False self._Reset() def _Reset( self ): self.ServerReset() self._connection = None self._server_handle = None if not self._server_keep_logfiles and self._stdout_file: utils.RemoveIfExists( self._stdout_file ) self._stdout_file = None if not self._server_keep_logfiles and self._stderr_file: utils.RemoveIfExists( self._stderr_file ) self._stderr_file = None def ServerReset( self ): """Clean up internal state related to the running server instance. Implementations are required to call this after disconnection and killing the downstream server.""" self._server_file_state = lsp.ServerFileStateStore() self._latest_diagnostics_mutex = threading.Lock() self._latest_diagnostics = collections.defaultdict( list ) self._sync_type = 'Full' self._initialize_response = None self._initialize_event = threading.Event() self._on_initialize_complete_handlers = [] self._server_capabilities = None self._is_completion_provider = False self._resolve_completion_items = False self._project_directory = None self._settings = {} self._extra_conf_dir = None def GetCompleterName( self ): return self._completer_name def Language( self ): return self._language def StartServer( self, request_data ): try: with self._server_info_mutex: return self._StartServerNoLock( request_data ) except LanguageServerConnectionTimeout: LOGGER.error( '%s failed to start, or did not connect successfully', self.GetServerName() ) self.Shutdown() return False def _StartServerNoLock( self, request_data ): LOGGER.info( 'Starting %s: %s', self.GetServerName(), self.GetCommandLine() ) self._project_directory = self.GetProjectDirectory( request_data ) if self._connection_type == 'tcp': if self.GetCommandLine(): self._stderr_file = utils.CreateLogfile( f'{ utils.MakeSafeFileNameString( self.GetServerName() ) }_stderr' ) self._stdout_file = utils.CreateLogfile( f'{ utils.MakeSafeFileNameString( self.GetServerName() ) }_stdout' ) with utils.OpenForStdHandle( self._stderr_file ) as stderr: with utils.OpenForStdHandle( self._stdout_file ) as stdout: self._server_handle = utils.SafePopen( self.GetCommandLine(), stdin = subprocess.PIPE, stdout = stdout, stderr = stderr, env = self.GetServerEnvironment() ) self._connection = TCPSingleStreamConnection( self._project_directory, lambda globs: WatchdogHandler( self, globs ), self._port, lambda request: self.WorkspaceConfigurationResponse( request ), self.GetDefaultNotificationHandler() ) else: self._stderr_file = utils.CreateLogfile( f'{ utils.MakeSafeFileNameString( self.GetServerName() ) }_stderr' ) with utils.OpenForStdHandle( self._stderr_file ) as stderr: self._server_handle = utils.SafePopen( self.GetCommandLine(), stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = stderr, env = self.GetServerEnvironment() ) self._connection = ( StandardIOLanguageServerConnection( self._project_directory, lambda globs: WatchdogHandler( self, globs ), self._server_handle.stdin, self._server_handle.stdout, lambda request: self.WorkspaceConfigurationResponse( request ), self.GetDefaultNotificationHandler() ) ) self._connection.Start() self._connection.AwaitServerConnection() if self._server_handle: LOGGER.info( '%s started with PID %s', self.GetServerName(), self._server_handle.pid ) return True def Shutdown( self ): with self._server_info_mutex: LOGGER.info( 'Shutting down %s...', self.GetServerName() ) # Tell the connection to expect the server to disconnect if self._connection: self._connection.Stop() if not self.ServerIsHealthy(): LOGGER.info( '%s is not running', self.GetServerName() ) self._Reset() return if self._server_handle: LOGGER.info( 'Stopping %s with PID %s', self.GetServerName(), self._server_handle.pid ) try: with self._server_info_mutex: self.ShutdownServer() # By this point, the server should have shut down and terminated. To # ensure that isn't blocked, we close all of our connections and wait # for the process to exit. # # If, after a small delay, the server has not shut down we do NOT kill # it; we expect that it will shut itself down eventually. This is # predominantly due to strange process behaviour on Windows. # NOTE: While waiting for the connection to close, we must _not_ hold any # locks (in fact, we must not hold locks that might be needed when # processing messages in the poll thread - i.e. notifications). # This is crucial, as the server closing (asynchronously) might # involve _other activities_ if there are messages in the queue (e.g. on # the socket) and we need to store/handle them in the message pump # (such as notifications) or even the initialise response. if self._connection: # Actually this sits around waiting for the connection thraed to exit self._connection.Close() if self._server_handle: for stream in [ self._server_handle.stdout, self._server_handle.stdin ]: if stream and not stream.closed: stream.close() with self._server_info_mutex: utils.WaitUntilProcessIsTerminated( self._server_handle, timeout = 30 ) LOGGER.info( '%s stopped', self.GetServerName() ) except Exception: LOGGER.exception( 'Error while stopping %s', self.GetServerName() ) # We leave the process running. Hopefully it will eventually die of its # own accord. with self._server_info_mutex: # Tidy up our internal state, even if the completer server didn't close # down cleanly. self._Reset() def ShutdownServer( self ): """Send the shutdown and possibly exit request to the server. Implementations must call this prior to closing the LanguageServerConnection or killing the downstream server.""" # Language server protocol requires orderly shutdown of the downstream # server by first sending a shutdown request, and on its completion sending # and exit notification (which does not receive a response). Some buggy # servers exit on receipt of the shutdown request, so we handle that too. if self._ServerIsInitialized(): request_id = self.GetConnection().NextRequestId() msg = lsp.Shutdown( request_id ) try: self.GetConnection().GetResponse( request_id, msg, REQUEST_TIMEOUT_INITIALISE ) except ResponseAbortedException: # When the language server (heinously) dies handling the shutdown # request, it is aborted. Just return - we're done. return except Exception: # Ignore other exceptions from the server and send the exit request # anyway LOGGER.exception( 'Shutdown request failed. Ignoring' ) if self.ServerIsHealthy(): self.GetConnection().SendNotification( lsp.Exit() ) # If any threads are waiting for the initialize exchange to complete, # release them, as there is no chance of getting a response now. if ( self._initialize_response is not None and not self._initialize_event.is_set() ): self._initialize_response = None self._initialize_event.set() def _RestartServer( self, request_data, *args, **kwargs ): self.Shutdown() self._StartAndInitializeServer( request_data, *args, **kwargs ) def _ServerIsInitialized( self ): """Returns True if the server is running and the initialization exchange has completed successfully. Implementations must not issue requests until this method returns True.""" if not self.ServerIsHealthy(): return False if self._initialize_event.is_set(): # We already got the initialize response return True if self._initialize_response is None: # We never sent the initialize response return False # Initialize request in progress. Will be handled asynchronously. return False def ServerIsHealthy( self ): if not self.GetCommandLine(): return self._connection and self._connection.IsConnected() else: return utils.ProcessIsRunning( self._server_handle ) def ServerIsReady( self ): return self._ServerIsInitialized() def ShouldUseNowInner( self, request_data ): # We should only do _anything_ after the initialize exchange has completed. return ( self.ServerIsReady() and super().ShouldUseNowInner( request_data ) ) def GetCodepointForCompletionRequest( self, request_data ): """Returns the 1-based codepoint offset on the current line at which to make the completion request""" return self._completions_cache.GetCodepointForCompletionRequest( request_data ) def ComputeCandidatesInner( self, request_data, codepoint ): if not self._is_completion_provider: return None, False self._UpdateServerWithFileContents( request_data ) request_id = self.GetConnection().NextRequestId() msg = lsp.Completion( request_id, request_data, codepoint ) response = self.GetConnection().GetResponse( request_id, msg, REQUEST_TIMEOUT_COMPLETION ) result = response.get( 'result' ) or [] if isinstance( result, list ): items = result is_incomplete = False else: items = result[ 'items' ] is_incomplete = result[ 'isIncomplete' ] # Note: _CandidatesFromCompletionItems does a lot of work on the actual # completion text to ensure that the returned text and start_codepoint are # applicable to our model of a single start column. # # Unfortunately (perhaps) we have to do this both here and in # DetailCandidates when resolve is required. This is because the filtering # should be based on ycmd's version of the insertion_text. Fortunately it's # likely much quicker to do the simple calculations inline rather than a # series of potentially many blocking server round trips. return ( self._CandidatesFromCompletionItems( items, LanguageServerCompleter.RESOLVE_NONE, request_data ), is_incomplete ) def _GetCandidatesFromSubclass( self, request_data ): cache_completions = self._completions_cache.GetCompletionsIfCacheValid( request_data ) if cache_completions: return cache_completions codepoint = self.GetCodepointForCompletionRequest( request_data ) raw_completions, is_incomplete = self.ComputeCandidatesInner( request_data, codepoint ) self._completions_cache.Update( request_data, raw_completions, is_incomplete ) return raw_completions def DetailCandidates( self, request_data, completions ): if not self._resolve_completion_items: return completions if not self.ShouldDetailCandidateList( completions ): return completions # Note: _CandidatesFromCompletionItems does a lot of work on the actual # completion text to ensure that the returned text and start_codepoint are # applicable to our model of a single start column. # # While we did this before, this time round we will have much better data to # do it on, and the new calculated value is dependent on the set of filtered # data, possibly leading to significantly smaller overlap with existing # text. See the fixup algorithm for more details on that. return self._CandidatesFromCompletionItems( [ c[ 'extra_data' ][ 'item' ] for c in completions ], LanguageServerCompleter.RESOLVE_ALL, request_data ) def DetailSingleCandidate( self, request_data, completions, to_resolve ): completion = completions[ to_resolve ] if not self._resolve_completion_items: return completion return self._CandidatesFromCompletionItems( [ completion[ 'extra_data' ][ 'item' ] ], LanguageServerCompleter.RESOLVE_ALL, request_data )[ 0 ] def _ResolveCompletionItem( self, item ): try: resolve_id = self.GetConnection().NextRequestId() resolve = lsp.ResolveCompletion( resolve_id, item ) response = self.GetConnection().GetResponse( resolve_id, resolve, REQUEST_TIMEOUT_COMPLETION ) item.clear() item.update( response[ 'result' ] ) except ResponseFailedException: LOGGER.exception( 'A completion item could not be resolved. Using ' 'basic data' ) return item def _ShouldResolveCompletionItems( self ): # We might not actually need to issue the resolve request if the server # claims that it doesn't support it. However, we still might need to fix up # the completion items. return ( self._server_capabilities.get( 'completionProvider' ) or {} ).get( 'resolveProvider', False ) def _CandidatesFromCompletionItems( self, items, resolve_completions, request_data ): """Issue the resolve request for each completion item in |items|, then fix up the items such that a single start codepoint is used.""" # # Important note on the following logic: # # Language server protocol requires that clients support textEdits in # completion items. It imposes some restrictions on the textEdit, namely: # * the edit range must cover at least the original requested position, # * and that it is on a single line. # # We only get textEdits (usually) for items which were successfully # resolved. Otherwise we just get insertion text, which might overlap the # existing text. # # Importantly there is no restriction that all edits start and end at the # same point. # # ycmd protocol only supports a single start column, so we must post-process # the completion items to work out a single start column to use, as follows: # * read all completion items text and start codepoint and store them # * store the minimum start codepoint encountered # * go back through the completion items and modify them so that they # contain enough text to start from the minimum start codepoint # * set the completion start codepoint to the minimum start point # # The last part involves reading the original source text and padding out # completion items so that they all start at the same point. # # This is neither particularly pretty nor efficient, but it is necessary. # Significant completions, such as imports, do not work without it in # jdt.ls. # completions = [] start_codepoints = [] unique_start_codepoints = [] min_start_codepoint = request_data[ 'start_codepoint' ] # First generate all of the completion items and store their # start_codepoints. Then, we fix-up the completion texts to use the # earliest start_codepoint by borrowing text from the original line. for idx, item in enumerate( items ): this_tem_is_resolved = item.get( '_resolved', False ) if ( resolve_completions and not this_tem_is_resolved and self._resolve_completion_items ): self._ResolveCompletionItem( item ) item[ '_resolved' ] = True this_tem_is_resolved = True try: insertion_text, extra_data, start_codepoint = ( _InsertionTextForItem( request_data, item ) ) except IncompatibleCompletionException: LOGGER.exception( 'Ignoring incompatible completion suggestion %s', item ) continue if not resolve_completions and self._resolve_completion_items: extra_data = {} if extra_data is None else extra_data # Deferred resolve - the client must read this and send the # /resolve_completion request to update the candidate set extra_data[ 'resolve' ] = idx # Store the actual item in the extra_data area of the completion item. # We'll use this later to do the resolve. extra_data[ 'item' ] = item min_start_codepoint = min( min_start_codepoint, start_codepoint ) # Build a ycmd-compatible completion for the text as we received it. Later # we might modify insertion_text should we see a lower start codepoint. completions.append( _CompletionItemToCompletionData( insertion_text, item, extra_data ) ) start_codepoints.append( start_codepoint ) if start_codepoint not in unique_start_codepoints: unique_start_codepoints.append( start_codepoint ) if ( len( completions ) > 1 and len( unique_start_codepoints ) > 1 and min_start_codepoint != request_data[ 'start_codepoint' ] ): # We need to fix up the completions, go do that return _FixUpCompletionPrefixes( completions, start_codepoints, request_data, min_start_codepoint ) request_data[ 'start_codepoint' ] = min_start_codepoint return completions def SignatureHelpAvailable( self ): if self._signature_help_disabled: return responses.SignatureHelpAvailalability.NOT_AVAILABLE if not self.ServerIsReady(): return responses.SignatureHelpAvailalability.PENDING if bool( self._server_capabilities.get( 'signatureHelpProvider' ) ): return responses.SignatureHelpAvailalability.AVAILABLE else: return responses.SignatureHelpAvailalability.NOT_AVAILABLE def ComputeSignaturesInner( self, request_data ): if not self.ServerIsReady(): return {} if not self._server_capabilities.get( 'signatureHelpProvider' ): return {} self._UpdateServerWithFileContents( request_data ) request_id = self.GetConnection().NextRequestId() msg = lsp.SignatureHelp( request_id, request_data ) response = self.GetConnection().GetResponse( request_id, msg, REQUEST_TIMEOUT_COMPLETION ) result = response[ 'result' ] if result is None: return {} for sig in result[ 'signatures' ]: sig_label = sig[ 'label' ] end = 0 if sig.get( 'parameters' ) is None: sig[ 'parameters' ] = [] for arg in sig[ 'parameters' ]: arg_label = arg[ 'label' ] if not isinstance( arg_label, list ): begin = sig[ 'label' ].find( arg_label, end ) end = begin + len( arg_label ) else: begin, end = arg_label arg[ 'label' ] = [ utils.CodepointOffsetToByteOffset( sig_label, begin ), utils.CodepointOffsetToByteOffset( sig_label, end ) ] result.setdefault( 'activeParameter', 0 ) result.setdefault( 'activeSignature', 0 ) return result def GetDetailedDiagnostic( self, request_data ): self._UpdateServerWithFileContents( request_data ) current_line_lsp = request_data[ 'line_num' ] - 1 current_file = request_data[ 'filepath' ] if not self._latest_diagnostics: return responses.BuildDisplayMessageResponse( 'Diagnostics are not ready yet.' ) with self._latest_diagnostics_mutex: diagnostics = list( self._latest_diagnostics[ lsp.FilePathToUri( current_file ) ] ) if not diagnostics: return responses.BuildDisplayMessageResponse( 'No diagnostics for current file.' ) current_column = lsp.CodepointsToUTF16CodeUnits( GetFileLines( request_data, current_file )[ current_line_lsp ], request_data[ 'column_codepoint' ] ) minimum_distance = None message = 'No diagnostics for current line.' for diagnostic in diagnostics: start = diagnostic[ 'range' ][ 'start' ] end = diagnostic[ 'range' ][ 'end' ] if current_line_lsp < start[ 'line' ] or end[ 'line' ] < current_line_lsp: continue point = { 'line': current_line_lsp, 'character': current_column } distance = _DistanceOfPointToRange( point, diagnostic[ 'range' ] ) if minimum_distance is None or distance < minimum_distance: message = diagnostic[ 'message' ] if distance == 0: break minimum_distance = distance return responses.BuildDisplayMessageResponse( message ) @abc.abstractmethod def GetServerName( self ): """ A string representing a human readable name of the server.""" pass # pragma: no cover def GetServerEnvironment( self ): """ None or a dictionary containing the environment variables. """ return None @abc.abstractmethod def GetCommandLine( self ): """ An override in a concrete class needs to return a list of cli arguments for starting the LSP server.""" pass # pragma: no cover def WorkspaceConfigurationResponse( self, request ): """If the concrete completer wants to respond to workspace/configuration requests, it should override this method.""" return None def ExtraCapabilities( self ): """ If the server is a special snowflake that need special attention, override this to supply special snowflake capabilities.""" return {} def AdditionalLogFiles( self ): """ Returns the list of server logs other than stderr. """ return [] def ExtraDebugItems( self, request_data ): """ A list of DebugInfoItems """ return [] def DebugInfo( self, request_data ): with self._server_info_mutex: extras = self.CommonDebugItems() + self.ExtraDebugItems( request_data ) logfiles = [ self._stdout_file, self._stderr_file ] + self.AdditionalLogFiles() server = responses.DebugInfoServer( name = self.GetServerName(), handle = self._server_handle, executable = self.GetCommandLine(), port = self._port if self._connection_type == 'tcp' else None, logfiles = logfiles, extras = extras ) return responses.BuildDebugInfoResponse( name = self.GetCompleterName(), servers = [ server ] ) def GetCustomSubcommands( self ): """Return a list of subcommand definitions to be used in conjunction with the subcommands detected by _DiscoverSubcommandSupport. The return is a dict whose keys are the subcommand and whose values are either: - a callable, as compatible with GetSubcommandsMap, or - a dict, compatible with DEFAULT_SUBCOMMANDS_MAP including a checker and a callable. If there are no custom subcommands, an empty dict should be returned.""" return {} def GetSubcommandsMap( self ): commands = {} commands.update( DEFAULT_SUBCOMMANDS_MAP ) commands.update( { 'StopServer': ( lambda self, request_data, args: self.Shutdown() ), 'RestartServer': ( lambda self, request_data, args: self._RestartServer( request_data ) ), } ) if hasattr( self, 'GetDoc' ): commands[ 'GetDoc' ] = ( lambda self, request_data, args: self.GetDoc( request_data ) ) if hasattr( self, 'GetType' ): commands[ 'GetType' ] = ( lambda self, request_data, args: self.GetType( request_data ) ) commands.update( self.GetCustomSubcommands() ) return self._DiscoverSubcommandSupport( commands ) def _GetSubcommandProvider( self, provider_list ): if not self._server_capabilities: LOGGER.warning( "Can't determine subcommands: not initialized yet" ) capabilities = {} else: capabilities = self._server_capabilities for providers in provider_list: if isinstance( providers, tuple ): if all( capabilities.get( provider ) for provider in providers ): return providers if capabilities.get( providers ): return providers return None def _DiscoverSubcommandSupport( self, commands ): subcommands_map = {} for command, handler in commands.items(): if isinstance( handler, list ): provider = self._GetSubcommandProvider( handler ) if provider: LOGGER.info( 'Found %s support for command %s in %s', provider, command, self.Language() ) subcommands_map[ command ] = PROVIDERS_MAP[ provider ] else: LOGGER.info( 'No support for %s command in server for %s', command, self.Language() ) else: LOGGER.info( 'Always supporting %s for %s', command, self.Language() ) subcommands_map[ command ] = handler return subcommands_map def DefaultSettings( self, request_data ): return {} def _GetSettingsFromExtraConf( self, request_data ): # The DefaultSettings method returns only the 'language server" ('ls') # settings, but self._settings is a wider dict containing a 'ls' key and any # other keys that we might want to add (e.g. 'project_directory', # 'capabilities', etc.) merged_ls_settings = self.DefaultSettings( request_data ) # If there is no extra-conf, the total settings are just the defaults: self._settings = { 'ls': merged_ls_settings } module = extra_conf_store.ModuleForSourceFile( request_data[ 'filepath' ] ) if module: # The user-defined settings may contain a 'ls' key, which override (merge # with) the DefaultSettings, and any other keys we specify generically for # all LSP-based completers (such as 'project_directory'). user_settings = self.GetSettings( module, request_data ) # Merge any user-supplied 'ls' settings with the defaults if 'ls' in user_settings: merged_ls_settings.update( user_settings[ 'ls' ] ) user_settings[ 'ls' ] = merged_ls_settings self._settings = user_settings # Only return the dir if it was found in the paths; we don't want to use # the path of the global extra conf as a project root dir. if not extra_conf_store.IsGlobalExtraConfModule( module ): LOGGER.debug( 'Using path %s for extra_conf_dir', os.path.dirname( module.__file__ ) ) return os.path.dirname( module.__file__ ) # No local extra conf return None def _StartAndInitializeServer( self, request_data, *args, **kwargs ): """Starts the server and sends the initialize request, assuming the start is successful. |args| and |kwargs| are passed through to the underlying call to StartServer. In general, completers don't need to call this as it is called automatically in OnFileReadyToParse, but this may be used in completer subcommands that require restarting the underlying server.""" self._server_started = False self._extra_conf_dir = self._GetSettingsFromExtraConf( request_data ) # Only attempt to start the server once. Set this after above call as it may # throw an exception self._server_started = True if self.StartServer( request_data, *args, **kwargs ): self._SendInitialize( request_data ) def OnFileReadyToParse( self, request_data ): if not self.ServerIsHealthy() and not self._server_started: # We have to get the settings before starting the server, as this call # might throw UnknownExtraConf. self._StartAndInitializeServer( request_data ) if not self.ServerIsHealthy(): return # If we haven't finished initializing yet, we need to queue up all functions # registered on the FileReadyToParse event and in particular # _UpdateServerWithFileContents in reverse order of registration. This # ensures that the server is up to date as soon as we are able to send more # messages. This is important because server start up can be quite slow and # we must not block the user, while we must keep the server synchronized. if not self._initialize_event.is_set(): for handler in reversed( self._on_file_ready_to_parse_handlers ): self._OnInitializeComplete( partial( handler, request_data = request_data ) ) return for handler in reversed( self._on_file_ready_to_parse_handlers ): handler( self, request_data ) # Return the latest diagnostics that we have received. # # NOTE: We also return diagnostics asynchronously via the long-polling # mechanism to avoid timing issues with the servers asynchronous publication # of diagnostics. # # However, we _also_ return them here to refresh diagnostics after, say # changing the active file in the editor, or for clients not supporting the # polling mechanism. filepath = request_data[ 'filepath' ] uri = lsp.FilePathToUri( filepath ) contents = GetFileLines( request_data, filepath ) with self._latest_diagnostics_mutex: if uri in self._latest_diagnostics: diagnostics = [ _BuildDiagnostic( contents, uri, diag ) for diag in self._latest_diagnostics[ uri ] ] return responses.BuildDiagnosticResponse( diagnostics, filepath, self.max_diagnostics_to_display ) def PollForMessagesInner( self, request_data, timeout ): # If there are messages pending in the queue, return them immediately messages = self._GetPendingMessages( request_data ) if messages: return messages # Otherwise, block until we get one or we hit the timeout. return self._AwaitServerMessages( request_data, timeout ) def _GetPendingMessages( self, request_data ): """Convert any pending notifications to messages and return them in a list. If there are no messages pending, returns an empty list. Returns False if an error occurred and no further polling should be attempted.""" messages = [] if not self._initialize_event.is_set(): # The request came before we started up, there cannot be any messages # pending, and in any case they will be handled later. return messages try: while True: if not self.GetConnection(): # The server isn't running or something. Don't re-poll. return False notification = self.GetConnection()._notifications.get_nowait() message = self.ConvertNotificationToMessage( request_data, notification ) if message: messages.append( message ) except queue.Empty: # We drained the queue pass return messages def _AwaitServerMessages( self, request_data, timeout ): """Block until either we receive a notification, or a timeout occurs. Returns one of the following: - a list containing a single message - True if a timeout occurred, and the poll should be restarted - False if an error occurred, and no further polling should be attempted """ try: while True: if not self._initialize_event.is_set(): # The request came before we started up, wait for startup to complete, # then tell the client to re-send the request. Note, we perform this # check on every iteration, as the server may be legitimately # restarted while this loop is running. self._initialize_event.wait( timeout=timeout ) # If the timeout is hit waiting for the server to be ready, after we # tried to start the server, we return False and kill the message # poll. return not self._server_started or self._initialize_event.is_set() if not self.GetConnection(): # The server isn't running or something. Don't re-poll, as this will # just cause errors. return False notification = self.GetConnection()._notifications.get( timeout = timeout ) message = self.ConvertNotificationToMessage( request_data, notification ) if message: return [ message ] except queue.Empty: return True def GetDefaultNotificationHandler( self ): """Return a notification handler method suitable for passing to LanguageServerConnection constructor""" def handler( server, notification ): self.HandleNotificationInPollThread( notification ) return handler def HandleNotificationInPollThread( self, notification ): """Called by the LanguageServerConnection in its message pump context when a notification message arrives.""" if notification[ 'method' ] == 'textDocument/publishDiagnostics': # Some clients might not use a message poll, so we must store the # diagnostics and return them in OnFileReadyToParse. We also need these # for correct FixIt handling, as they are part of the FixIt context. params = notification[ 'params' ] # Since percent-encoded strings are not canonical, they can choose to use # upper case or lower case letters, also there are some characters that # can be encoded or not. Therefore, we convert them back and forth # according to our implementation to make sure they are in a canonical # form for access later on. try: uri = lsp.FilePathToUri( lsp.UriToFilePath( params[ 'uri' ] ) ) except lsp.InvalidUriException: # Ignore diagnostics for URIs we don't recognise LOGGER.exception( 'Ignoring diagnostics for unrecognized URI' ) return with self._latest_diagnostics_mutex: self._latest_diagnostics[ uri ] = params[ 'diagnostics' ] def ConvertNotificationToMessage( self, request_data, notification ): """Convert the supplied server notification to a ycmd message. Returns None if the notification should be ignored. Implementations may override this method to handle custom notifications, but must always call the base implementation for unrecognized notifications.""" if notification[ 'method' ] == 'window/showMessage': return responses.BuildDisplayMessageResponse( notification[ 'params' ][ 'message' ] ) if notification[ 'method' ] == 'textDocument/publishDiagnostics': params = notification[ 'params' ] uri = params[ 'uri' ] try: filepath = lsp.UriToFilePath( uri ) except lsp.InvalidUriException: LOGGER.exception( 'Ignoring diagnostics for unrecognized URI' ) return None with self._server_info_mutex: if filepath in self._server_file_state: contents = utils.SplitLines( self._server_file_state[ filepath ].contents ) else: contents = GetFileLines( request_data, filepath ) diagnostics = [ _BuildDiagnostic( contents, uri, x ) for x in params[ 'diagnostics' ] ] return { 'diagnostics': responses.BuildDiagnosticResponse( diagnostics, filepath, self.max_diagnostics_to_display ), 'filepath': filepath } if notification[ 'method' ] == 'window/logMessage': log_level = [ None, # 1-based enum from LSP logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG, ] params = notification[ 'params' ] LOGGER.log( log_level[ int( params[ 'type' ] ) ], 'Server reported: %s', params[ 'message' ] ) return None def _AnySupportedFileType( self, file_types ): for supported in self.SupportedFiletypes(): if supported in file_types: return True return False def _UpdateServerWithFileContents( self, request_data ): """Update the server with the current contents of all open buffers, and close any buffers no longer open. This method should be called frequently and in any event before a synchronous operation.""" with self._server_info_mutex: self._UpdateDirtyFilesUnderLock( request_data ) files_to_purge = self._UpdateSavedFilesUnderLock( request_data ) self._PurgeMissingFilesUnderLock( files_to_purge ) def _UpdateDirtyFilesUnderLock( self, request_data ): for file_name, file_data in request_data[ 'file_data' ].items(): if not self._AnySupportedFileType( file_data[ 'filetypes' ] ): LOGGER.debug( 'Not updating file %s, it is not a supported filetype: ' '%s not in %s', file_name, file_data[ 'filetypes' ], self.SupportedFiletypes() ) continue file_state = self._server_file_state[ file_name ] action = file_state.GetDirtyFileAction( file_data[ 'contents' ] ) LOGGER.debug( 'Refreshing file %s: State is %s/action %s', file_name, file_state.state, action ) if action == lsp.ServerFileState.OPEN_FILE: msg = lsp.DidOpenTextDocument( file_state, file_data[ 'filetypes' ], file_data[ 'contents' ] ) self.GetConnection().SendNotification( msg ) elif action == lsp.ServerFileState.CHANGE_FILE: # FIXME: DidChangeTextDocument doesn't actually do anything # different from DidOpenTextDocument other than send the right # message, because we don't actually have a mechanism for generating # the diffs. This isn't strictly necessary, but might lead to # performance problems. msg = lsp.DidChangeTextDocument( file_state, file_data[ 'contents' ] ) self.GetConnection().SendNotification( msg ) def _UpdateSavedFilesUnderLock( self, request_data ): files_to_purge = [] for file_name, file_state in self._server_file_state.items(): if file_name in request_data[ 'file_data' ]: continue # We also need to tell the server the contents of any files we have said # are open, but are not 'dirty' in the editor. This is because after # sending a didOpen notification, we own the contents of the file. # # So for any file that is in the server map, and open, but not supplied in # the request, we check to see if its on-disk contents match the latest in # the server. If they don't, we send an update. # # FIXME: This is really inefficient currently, as it reads the entire file # on every update. It might actually be better to close files which have # been saved and are no longer "dirty", though that would likely be less # efficient for downstream servers which cache e.g. AST. try: contents = GetFileContents( request_data, file_name ) except IOError: LOGGER.exception( 'Error getting contents for open file: %s', file_name ) # The file no longer exists (it might have been a temporary file name) # or it is no longer accessible, so we should state that it is closed. # If it were still open it would have been in the request_data. # # We have to do this in a separate loop because we can't change # self._server_file_state while iterating it. files_to_purge.append( file_name ) continue action = file_state.GetSavedFileAction( contents ) if action == lsp.ServerFileState.CHANGE_FILE: msg = lsp.DidChangeTextDocument( file_state, contents ) self.GetConnection().SendNotification( msg ) return files_to_purge def _PurgeMissingFilesUnderLock( self, files_to_purge ): # ycmd clients only send buffers which have changed, and are required to # send BufferUnload autocommand when files are closed. for file_name in files_to_purge: self._PurgeFileFromServer( file_name ) def OnFileSave( self, request_data ): if not self.ServerIsReady(): return if 'textDocumentSync' in self._server_capabilities: sync = self._server_capabilities[ 'textDocumentSync' ] if isinstance( sync, dict ) and sync.get( 'save' ) not in [ None, False ]: save = sync[ 'save' ] file_name = request_data[ 'filepath' ] contents = None if isinstance( save, dict ) and save.get( 'includeText' ): contents = request_data[ 'file_data' ][ file_name ][ 'contents' ] file_state = self._server_file_state[ file_name ] msg = lsp.DidSaveTextDocument( file_state, contents ) self.GetConnection().SendNotification( msg ) def OnBufferUnload( self, request_data ): if not self.ServerIsHealthy(): return # If we haven't finished initializing yet, we need to queue up a call to # _PurgeFileFromServer. This ensures that the server is up to date # as soon as we are able to send more messages. This is important because # server start up can be quite slow and we must not block the user, while we # must keep the server synchronized. if not self._initialize_event.is_set(): self._OnInitializeComplete( lambda self: self._PurgeFileFromServer( request_data[ 'filepath' ] ) ) return self._PurgeFileFromServer( request_data[ 'filepath' ] ) def _PurgeFileFromServer( self, file_path ): file_state = self._server_file_state[ file_path ] action = file_state.GetFileCloseAction() if action == lsp.ServerFileState.CLOSE_FILE: msg = lsp.DidCloseTextDocument( file_state ) self.GetConnection().SendNotification( msg ) del self._server_file_state[ file_state.filename ] def GetProjectRootFiles( self ): """Returns a list of files that indicate the root of the project. It should be easier to override just this method than the whole GetProjectDirectory.""" return [] def GetProjectDirectory( self, request_data ): """Return the directory in which the server should operate. Language server protocol and most servers have a concept of a 'project directory'. Where a concrete completer can detect this better, it should override this method, but otherwise, we default as follows: - If the user specified 'project_directory' in their extra conf 'Settings', use that. - try to find files from GetProjectRootFiles and use the first directory from there - if there's an extra_conf file, use that directory - otherwise if we know the client's cwd, use that - otherwise use the directory of the file that we just opened Note: None of these are ideal. Ycmd doesn't really have a notion of project directory and therefore neither do any of our clients. NOTE: Must be called _after_ _GetSettingsFromExtraConf, as it uses self._settings and self._extra_conf_dir """ if 'project_directory' in self._settings: return utils.AbsolutePath( self._settings[ 'project_directory' ], self._extra_conf_dir ) project_root_files = self.GetProjectRootFiles() if project_root_files: for folder in utils.PathsToAllParentFolders( request_data[ 'filepath' ] ): for root_file in project_root_files: if os.path.isfile( os.path.join( folder, root_file ) ): return folder if self._extra_conf_dir: return self._extra_conf_dir if 'working_dir' in request_data: return request_data[ 'working_dir' ] return os.path.dirname( request_data[ 'filepath' ] ) def _SendInitialize( self, request_data ): """Sends the initialize request asynchronously. This must be called immediately after establishing the connection with the language server. Implementations must not issue further requests to the server until the initialize exchange has completed. This can be detected by calling this class's implementation of _ServerIsInitialized. _GetSettingsFromExtraConf must be called before calling this method, as this method release on self._extra_conf_dir. It is called before starting the server in OnFileReadyToParse.""" with self._server_info_mutex: assert not self._initialize_response request_id = self.GetConnection().NextRequestId() # FIXME: According to the discussion on # https://github.com/Microsoft/language-server-protocol/issues/567 # the settings on the Initialize request are somehow subtly different from # the settings supplied in didChangeConfiguration, though it's not exactly # clear how/where that is specified. msg = lsp.Initialize( request_id, self._project_directory, self.ExtraCapabilities(), self._settings.get( 'ls', {} ) ) def response_handler( response, message ): if message is None: return self._HandleInitializeInPollThread( message ) self._initialize_response = self.GetConnection().GetResponseAsync( request_id, msg, response_handler ) def GetTriggerCharacters( self, server_trigger_characters ): """Given the server trigger characters supplied in the initialize response, returns the trigger characters to merge with the ycmd-defined ones. By default, all server trigger characters are merged in. Note this might not be appropriate in all cases as ycmd's own triggering mechanism is more sophisticated (regex based) than LSP's (single character). If the server-supplied single-character triggers are not useful, override this method to return an empty list or None.""" return server_trigger_characters def GetSignatureTriggerCharacters( self, server_trigger_characters ): """Same as _GetTriggerCharacters but for signature help.""" return server_trigger_characters def _HandleInitializeInPollThread( self, response ): """Called within the context of the LanguageServerConnection's message pump when the initialize request receives a response.""" with self._server_info_mutex: self._server_capabilities = response[ 'result' ][ 'capabilities' ] self._resolve_completion_items = self._ShouldResolveCompletionItems() if self._resolve_completion_items: LOGGER.info( '%s: Language server requires resolve request', self.Langauge() ) else: LOGGER.info( '%s: Language server does not require resolve request', self.Langauge() ) self._is_completion_provider = ( 'completionProvider' in self._server_capabilities ) if 'textDocumentSync' in self._server_capabilities: sync = self._server_capabilities[ 'textDocumentSync' ] SYNC_TYPE = [ 'None', 'Full', 'Incremental' ] # The sync type can either be a number or an object. Because it's # important to make things difficult. if isinstance( sync, dict ): if 'change' in sync: sync = sync[ 'change' ] else: sync = 1 self._sync_type = SYNC_TYPE[ sync ] LOGGER.info( '%s: Language server requires sync type of %s', self.Langauge(), self._sync_type ) # Update our semantic triggers if they are supplied by the server if self.completion_triggers is not None: server_trigger_characters = ( ( self._server_capabilities.get( 'completionProvider' ) or {} ) .get( 'triggerCharacters' ) or [] ) LOGGER.debug( '%s: Server declares trigger characters: %s', self.Language(), server_trigger_characters ) trigger_characters = self.GetTriggerCharacters( server_trigger_characters ) if trigger_characters: LOGGER.info( '%s: Using trigger characters for semantic triggers: %s', self.Language(), ','.join( trigger_characters ) ) self.completion_triggers.SetServerSemanticTriggers( trigger_characters ) if self._signature_triggers is not None: server_trigger_characters = ( ( self._server_capabilities.get( 'signatureHelpProvider' ) or {} ) .get( 'triggerCharacters' ) or [] ) LOGGER.debug( '%s: Server declares signature trigger characters: %s', self.Language(), server_trigger_characters ) trigger_characters = self.GetSignatureTriggerCharacters( server_trigger_characters ) if trigger_characters: LOGGER.info( '%s: Using characters for signature triggers: %s', self.Language(), ','.join( trigger_characters ) ) self.SetSignatureHelpTriggers( trigger_characters ) # We must notify the server that we received the initialize response (for # no apparent reason, other than that's what the protocol says). self.GetConnection().SendNotification( lsp.Initialized() ) # Some language servers require the use of didChangeConfiguration event, # even though it is not clear in the specification that it is mandatory, # nor when it should be sent. VSCode sends it immediately after # initialized notification, so we do the same. # FIXME: According to # https://github.com/Microsoft/language-server-protocol/issues/567 the # configuration should be send in response to a workspace/configuration # request? self.GetConnection().SendNotification( lsp.DidChangeConfiguration( self._settings.get( 'ls', {} ) ) ) # Notify the other threads that we have completed the initialize exchange. self._initialize_response = None self._initialize_event.set() # Fire any events that are pending on the completion of the initialize # exchange. Typically, this will be calls to _UpdateServerWithFileContents # or something that occurred while we were waiting. for handler in self._on_initialize_complete_handlers: handler( self ) self._on_initialize_complete_handlers = [] def _OnInitializeComplete( self, handler ): """Register a function to be called when the initialize exchange completes. The function |handler| will be called on successful completion of the initialize exchange with a single argument |self|, which is the |self| passed to this method. If the server is shut down or reset, the callback is not called.""" self._on_initialize_complete_handlers.append( handler ) def RegisterOnFileReadyToParse( self, handler ): self._on_file_ready_to_parse_handlers.append( handler ) def GetHoverResponse( self, request_data ): """Return the raw LSP response to the hover request for the supplied context. Implementations can use this for e.g. GetDoc and GetType requests, depending on the particular server response.""" if not self._ServerIsInitialized(): raise RuntimeError( 'Server is initializing. Please wait.' ) self._UpdateServerWithFileContents( request_data ) request_id = self.GetConnection().NextRequestId() response = self.GetConnection().GetResponse( request_id, lsp.Hover( request_id, request_data ), REQUEST_TIMEOUT_COMMAND ) result = response[ 'result' ] if result: return result[ 'contents' ] raise NoHoverInfoException( NO_HOVER_INFORMATION ) def _GoToRequest( self, request_data, handler ): request_id = self.GetConnection().NextRequestId() result = self.GetConnection().GetResponse( request_id, getattr( lsp, handler )( request_id, request_data ), REQUEST_TIMEOUT_COMMAND )[ 'result' ] if not result: raise RuntimeError( 'Cannot jump to location' ) if not isinstance( result, list ): return [ result ] return result def GoTo( self, request_data, handlers ): """Issues a GoTo request for each handler in |handlers| until it returns multiple locations or a location the cursor does not belong since the user wants to jump somewhere else. If that's the last handler, the location is returned anyway.""" if not self.ServerIsReady(): raise RuntimeError( 'Server is initializing. Please wait.' ) self._UpdateServerWithFileContents( request_data ) if len( handlers ) == 1: result = self._GoToRequest( request_data, handlers[ 0 ] ) else: for handler in handlers: result = self._GoToRequest( request_data, handler ) if len( result ) > 1 or not _CursorInsideLocation( request_data, result[ 0 ] ): break return _LocationListToGoTo( request_data, result ) def GoToSymbol( self, request_data, args ): if not self.ServerIsReady(): raise RuntimeError( 'Server is initializing. Please wait.' ) self._UpdateServerWithFileContents( request_data ) if len( args ) < 1: raise RuntimeError( 'Must specify something to search for' ) query = args[ 0 ] request_id = self.GetConnection().NextRequestId() response = self.GetConnection().GetResponse( request_id, lsp.WorkspaceSymbol( request_id, query ), REQUEST_TIMEOUT_COMMAND ) result = response.get( 'result' ) or [] locations = [ responses.BuildGoToResponseFromLocation( _PositionToLocationAndDescription( request_data, symbol_info[ 'location' ] )[ 0 ], symbol_info[ 'name' ] ) for symbol_info in result ] if not locations: raise RuntimeError( "Symbol not found" ) elif len( locations ) == 1: return locations[ 0 ] else: return locations def GetCodeActions( self, request_data, args ): """Performs the codeAction request and returns the result as a FixIt response.""" if not self.ServerIsReady(): raise RuntimeError( 'Server is initializing. Please wait.' ) self._UpdateServerWithFileContents( request_data ) request_id = self.GetConnection().NextRequestId() cursor_range_ls = lsp.Range( request_data ) with self._latest_diagnostics_mutex: # _latest_diagnostics contains LSP rnages, _not_ YCM ranges file_diagnostics = list( self._latest_diagnostics[ lsp.FilePathToUri( request_data[ 'filepath' ] ) ] ) matched_diagnostics = [ d for d in file_diagnostics if lsp.RangesOverlap( d[ 'range' ], cursor_range_ls ) ] # If we didn't find any overlapping the strict range/character. Find any # that overlap line of the cursor. if not matched_diagnostics and 'range' not in request_data: matched_diagnostics = [ d for d in file_diagnostics if lsp.RangesOverlapLines( d[ 'range' ], cursor_range_ls ) ] code_actions = self.GetConnection().GetResponse( request_id, lsp.CodeAction( request_id, request_data, cursor_range_ls, matched_diagnostics ), REQUEST_TIMEOUT_COMMAND ) return self.CodeActionResponseToFixIts( request_data, code_actions[ 'result' ] ) def CodeActionResponseToFixIts( self, request_data, code_actions ): if code_actions is None: return responses.BuildFixItResponse( [] ) fixits = [] for code_action in code_actions: if 'edit' in code_action: # TODO: Start supporting a mix of WorkspaceEdits and Commands # once there's a need for such assert 'command' not in code_action # This is a WorkspaceEdit literal fixits.append( self.CodeActionLiteralToFixIt( request_data, code_action ) ) continue # Either a CodeAction or a Command assert 'command' in code_action action_command = code_action[ 'command' ] if isinstance( action_command, dict ): # CodeAction with a 'command' rather than 'edit' fixits.append( self.CodeActionCommandToFixIt( request_data, code_action ) ) continue # It is a Command fixits.append( self.CommandToFixIt( request_data, code_action ) ) # Show a list of actions to the user to select which one to apply. # This is (probably) a more common workflow for "code action". result = [ r for r in fixits if r ] if len( result ) == 1: fixit = result[ 0 ] if hasattr( fixit, 'resolve' ): # Be nice and resolve the fixit to save on roundtrips unresolved_fixit = { 'command': fixit.command, 'text': fixit.text, 'resolve': fixit.resolve } return self._ResolveFixit( request_data, unresolved_fixit ) return responses.BuildFixItResponse( result ) def CodeActionLiteralToFixIt( self, request_data, code_action_literal ): return WorkspaceEditToFixIt( request_data, code_action_literal[ 'edit' ], code_action_literal[ 'title' ], code_action_literal.get( 'kind' ) ) def CodeActionCommandToFixIt( self, request_data, code_action_command ): command = code_action_command[ 'command' ] return self.CommandToFixIt( request_data, command, code_action_command.get( 'kind' ) ) def CommandToFixIt( self, request_data, command, kind = None ): return responses.UnresolvedFixIt( command, command[ 'title' ], kind ) def RefactorRename( self, request_data, args ): """Issues the rename request and returns the result as a FixIt response.""" if not self.ServerIsReady(): raise RuntimeError( 'Server is initializing. Please wait.' ) if len( args ) != 1: raise ValueError( 'Please specify a new name to rename it to.\n' 'Usage: RefactorRename <new name>' ) self._UpdateServerWithFileContents( request_data ) new_name = args[ 0 ] request_id = self.GetConnection().NextRequestId() response = self.GetConnection().GetResponse( request_id, lsp.Rename( request_id, request_data, new_name ), REQUEST_TIMEOUT_COMMAND ) fixit = WorkspaceEditToFixIt( request_data, response[ 'result' ] ) if not fixit: raise RuntimeError( 'Cannot rename the symbol under cursor.' ) return responses.BuildFixItResponse( [ fixit ] ) def Format( self, request_data ): """Issues the formatting or rangeFormatting request (depending on the presence of a range) and returns the result as a FixIt response.""" if not self.ServerIsReady(): raise RuntimeError( 'Server is initializing. Please wait.' ) self._UpdateServerWithFileContents( request_data ) request_data[ 'options' ].update( self.AdditionalFormattingOptions( request_data ) ) request_id = self.GetConnection().NextRequestId() if 'range' in request_data: message = lsp.RangeFormatting( request_id, request_data ) else: message = lsp.Formatting( request_id, request_data ) response = self.GetConnection().GetResponse( request_id, message, REQUEST_TIMEOUT_COMMAND ) filepath = request_data[ 'filepath' ] contents = GetFileLines( request_data, filepath ) chunks = [ responses.FixItChunk( text_edit[ 'newText' ], _BuildRange( contents, filepath, text_edit[ 'range' ] ) ) for text_edit in response[ 'result' ] or [] ] return responses.BuildFixItResponse( [ responses.FixIt( responses.Location( request_data[ 'line_num' ], request_data[ 'column_num' ], request_data[ 'filepath' ] ), chunks ) ] ) def _ResolveFixit( self, request_data, fixit ): if not fixit[ 'resolve' ]: return { 'fixits': [ fixit ] } unresolved_fixit = fixit[ 'command' ] collector = EditCollector() with self.GetConnection().CollectApplyEdits( collector ): self.GetCommandResponse( request_data, unresolved_fixit[ 'command' ], unresolved_fixit[ 'arguments' ] ) # Return a ycmd fixit response = collector.requests assert len( response ) < 2 if not response: return responses.BuildFixItResponse( [ responses.FixIt( responses.Location( request_data[ 'line_num' ], request_data[ 'column_num' ], request_data[ 'filepath' ] ), [] ) ] ) fixit = WorkspaceEditToFixIt( request_data, response[ 0 ][ 'edit' ], unresolved_fixit[ 'title' ] ) return responses.BuildFixItResponse( [ fixit ] ) def ResolveFixit( self, request_data ): return self._ResolveFixit( request_data, request_data[ 'fixit' ] ) def ExecuteCommand( self, request_data, args ): if not args: raise ValueError( 'Must specify a command to execute' ) # We don't have any actual knowledge of the responses here. Unfortunately, # the LSP "commands" require client/server specific understanding of the # commands. collector = EditCollector() with self.GetConnection().CollectApplyEdits( collector ): command_response = self.GetCommandResponse( request_data, args[ 0 ], args[ 1: ] ) edits = collector.requests response = self.HandleServerCommandResponse( request_data, edits, command_response ) if response is not None: return response if len( edits ): fixits = [ WorkspaceEditToFixIt( request_data, e[ 'edit' ], '' ) for e in edits ] return responses.BuildFixItResponse( fixits ) return responses.BuildDetailedInfoResponse( json.dumps( command_response, indent = 2 ) ) def GetCommandResponse( self, request_data, command, arguments ): if not self.ServerIsReady(): raise RuntimeError( 'Server is initializing. Please wait.' ) self._UpdateServerWithFileContents( request_data ) request_id = self.GetConnection().NextRequestId() message = lsp.ExecuteCommand( request_id, command, arguments ) response = self.GetConnection().GetResponse( request_id, message, REQUEST_TIMEOUT_COMMAND ) return response[ 'result' ] def CommonDebugItems( self ): def ServerStateDescription(): if not self.ServerIsHealthy(): return 'Dead' if not self._ServerIsInitialized(): return 'Starting...' return 'Initialized' return [ responses.DebugInfoItem( 'Server State', ServerStateDescription() ), responses.DebugInfoItem( 'Project Directory', self._project_directory ), responses.DebugInfoItem( 'Settings', json.dumps( self._settings.get( 'ls', {} ), indent = 2, sort_keys = True ) ) ] def _DistanceOfPointToRange( point, range ): """Calculate the distance from a point to a range. Assumes point is covered by lines in the range. Returns 0 if point is already inside range. """ start = range[ 'start' ] end = range[ 'end' ] # Single-line range. if start[ 'line' ] == end[ 'line' ]: # 0 if point is within range, otherwise distance from start/end. return max( 0, point[ 'character' ] - end[ 'character' ], start[ 'character' ] - point[ 'character' ] ) if start[ 'line' ] == point[ 'line' ]: return max( 0, start[ 'character' ] - point[ 'character' ] ) if end[ 'line' ] == point[ 'line' ]: return max( 0, point[ 'character' ] - end[ 'character' ] ) # If not on the first or last line, then point is within range for sure. return 0 def _CompletionItemToCompletionData( insertion_text, item, fixits ): # Since we send completionItemKind capabilities, we guarantee to handle # values outside our value set and fall back to a default. try: kind = lsp.ITEM_KIND[ item.get( 'kind' ) or 0 ] except IndexError: kind = lsp.ITEM_KIND[ 0 ] # Fallback to None for unsupported kinds. documentation = item.get( 'documentation' ) or '' if isinstance( documentation, dict ): documentation = documentation[ 'value' ] return responses.BuildCompletionData( insertion_text, extra_menu_info = item.get( 'detail' ), detailed_info = item[ 'label' ] + '\n\n' + documentation, menu_text = item[ 'label' ], kind = kind, extra_data = fixits ) def _FixUpCompletionPrefixes( completions, start_codepoints, request_data, min_start_codepoint ): """Fix up the insertion texts so they share the same start_codepoint by borrowing text from the source.""" line = request_data[ 'line_value' ] for completion, start_codepoint in zip( completions, start_codepoints ): to_borrow = start_codepoint - min_start_codepoint if to_borrow > 0: borrow = line[ start_codepoint - to_borrow - 1 : start_codepoint - 1 ] new_insertion_text = borrow + completion[ 'insertion_text' ] completion[ 'insertion_text' ] = new_insertion_text # Finally, remove any common prefix common_prefix_len = len( os.path.commonprefix( [ c[ 'insertion_text' ] for c in completions ] ) ) for completion in completions: completion[ 'insertion_text' ] = completion[ 'insertion_text' ][ common_prefix_len : ] # The start column is the earliest start point that we fixed up plus the # length of the common prefix that we subsequently removed. # # Phew! That was hard work. request_data[ 'start_codepoint' ] = min_start_codepoint + common_prefix_len return completions def _InsertionTextForItem( request_data, item ): """Determines the insertion text for the completion item |item|, and any additional FixIts that need to be applied when selecting it. Returns a tuple ( - insertion_text = the text to insert - fixits = ycmd fixit which needs to be applied additionally when selecting this completion - start_codepoint = the start column at which the text should be inserted )""" # We do not support completion types of "Snippet". This is implicit in that we # don't say it is a "capability" in the initialize request. # Abort this request if the server is buggy and ignores us. assert lsp.INSERT_TEXT_FORMAT[ item.get( 'insertTextFormat' ) or 1 ] == 'PlainText' fixits = None start_codepoint = request_data[ 'start_codepoint' ] # We will always have one of insertText or label if 'insertText' in item and item[ 'insertText' ]: insertion_text = item[ 'insertText' ] else: insertion_text = item[ 'label' ] additional_text_edits = [] # Per the protocol, textEdit takes precedence over insertText, and must be # on the same line (and containing) the originally requested position. These # are a pain, and require fixing up later in some cases, as most of our # clients won't be able to apply arbitrary edits (only 'completion', as # opposed to 'content assist'). if 'textEdit' in item and item[ 'textEdit' ]: text_edit = item[ 'textEdit' ] start_codepoint = _GetCompletionItemStartCodepointOrReject( text_edit, request_data ) insertion_text = text_edit[ 'newText' ] if '\n' in insertion_text: # jdt.ls can return completions which generate code, such as # getters/setters and entire anonymous classes. # # In order to support this we would need to do something like: # - invent some insertion_text based on label/insertText (or perhaps # '<snippet>' # - insert a textEdit in additionalTextEdits which deletes this # insertion # - or perhaps just modify this textEdit to undo that change? # - or perhaps somehow support insertion_text of '' (this doesn't work # because of filtering/sorting, etc.). # - insert this textEdit in additionalTextEdits # # These textEdits would need a lot of fixing up and is currently out of # scope. # # These sorts of completions aren't really in the spirit of ycmd at the # moment anyway. So for now, we just ignore this candidate. raise IncompatibleCompletionException( insertion_text ) else: # Calculate the start codepoint based on the overlapping text in the # insertion text and the existing text. This is the behavior of Visual # Studio Code and therefore de-facto undocumented required behavior of LSP # clients. start_codepoint -= FindOverlapLength( request_data[ 'prefix' ], insertion_text ) additional_text_edits.extend( item.get( 'additionalTextEdits' ) or [] ) if additional_text_edits: filepath = request_data[ 'filepath' ] contents = GetFileLines( request_data, filepath ) chunks = [ responses.FixItChunk( e[ 'newText' ], _BuildRange( contents, filepath, e[ 'range' ] ) ) for e in additional_text_edits ] fixits = responses.BuildFixItResponse( [ responses.FixIt( chunks[ 0 ].range.start_, chunks ) ] ) return insertion_text, fixits, start_codepoint def FindOverlapLength( line_value, insertion_text ): """Return the length of the longest suffix of |line_value| which is a prefix of |insertion_text|""" # Credit: https://neil.fraser.name/news/2010/11/04/ # Example of what this does: # line_value: import com. # insertion_text: com.youcompleteme.test # Overlap: ^..^ # Overlap Len: 4 # Calculated as follows: # - truncate: # line_value = import com. # insertion_text = com.youcomp # - assume overlap length 1 # overlap_text = "." # position = 3 # overlap set to be 4 # com. compared with com.: longest_overlap = 4 # - assume overlap length 5 # overlap_text = " com." # position = -1 # return 4 (from previous iteration) # More complex example: 'Some CoCo' vs 'CoCo Bean' # No truncation # Iter 1 (overlap = 1): p('o') = 1, overlap = 2, Co==Co, best = 2 (++) # Iter 2 (overlap = 3): p('oCo') = 1 overlap = 4, CoCo==CoCo, best = 4 (++) # Iter 3 (overlap = 5): p(' CoCo') = -1, return 4 # And the non-overlap case "aaab" "caab": # Iter 1 (overlap = 1): p('b') = 3, overlap = 4, aaab!=caab, return 0 line_value_len = len( line_value ) insertion_text_len = len( insertion_text ) # Bail early if either are empty if line_value_len == 0 or insertion_text_len == 0: return 0 # Truncate so that they are the same length. Keep the overlapping sections # (suffix of line_value, prefix of insertion_text). if line_value_len > insertion_text_len: line_value = line_value[ -insertion_text_len : ] elif insertion_text_len > line_value_len: insertion_text = insertion_text[ : line_value_len ] # Worst case is full overlap, but that's trivial to check. if insertion_text == line_value: return min( line_value_len, insertion_text_len ) longest_matching_overlap = 0 # Assume a single-character of overlap, and find where this appears (if at # all) in the insertion_text overlap = 1 while True: # Find the position of the overlap-length suffix of line_value within # insertion_text overlap_text = line_value[ -overlap : ] position = insertion_text.find( overlap_text ) # If it isn't found, then we're done, return the last known overlap length. if position == -1: return longest_matching_overlap # Assume that all of the characters up to where this suffix was found # overlap. If they do, assume 1 more character of overlap, and continue. # Otherwise, we're done. overlap += position # If the overlap section matches, then we know this is the longest overlap # we've seen so far. if line_value[ -overlap : ] == insertion_text[ : overlap ]: longest_matching_overlap = overlap overlap += 1 def _GetCompletionItemStartCodepointOrReject( text_edit, request_data ): edit_range = text_edit[ 'range' ] # Conservatively rejecting candidates that breach the protocol if edit_range[ 'start' ][ 'line' ] != edit_range[ 'end' ][ 'line' ]: new_text = text_edit[ 'newText' ] raise IncompatibleCompletionException( f"The TextEdit '{ new_text }' spans multiple lines" ) file_contents = GetFileLines( request_data, request_data[ 'filepath' ] ) line_value = file_contents[ edit_range[ 'start' ][ 'line' ] ] start_codepoint = lsp.UTF16CodeUnitsToCodepoints( line_value, edit_range[ 'start' ][ 'character' ] + 1 ) if start_codepoint > request_data[ 'start_codepoint' ]: new_text = text_edit[ 'newText' ] raise IncompatibleCompletionException( f"The TextEdit '{ new_text }' starts after the start position" ) return start_codepoint def _LocationListToGoTo( request_data, positions ): """Convert a LSP list of locations to a ycmd GoTo response.""" try: if len( positions ) > 1: return [ responses.BuildGoToResponseFromLocation( *_PositionToLocationAndDescription( request_data, position ) ) for position in positions ] return responses.BuildGoToResponseFromLocation( *_PositionToLocationAndDescription( request_data, positions[ 0 ] ) ) except ( IndexError, KeyError ): raise RuntimeError( 'Cannot jump to location' ) def _PositionToLocationAndDescription( request_data, position ): """Convert a LSP position to a ycmd location.""" try: filename = lsp.UriToFilePath( position[ 'uri' ] ) file_contents = GetFileLines( request_data, filename ) except lsp.InvalidUriException: LOGGER.debug( 'Invalid URI, file contents not available in GoTo' ) filename = '' file_contents = [] except IOError: # It's possible to receive positions for files which no longer exist (due to # race condition). UriToFilePath doesn't throw IOError, so we can assume # that filename is already set. LOGGER.exception( 'A file could not be found when determining a ' 'GoTo location' ) file_contents = [] return _BuildLocationAndDescription( filename, file_contents, position[ 'range' ][ 'start' ] ) def _LspToYcmdLocation( file_contents, location ): """Converts a LSP location to a ycmd one. Returns a tuple of ( - the contents of the line of |location| - the line number of |location| - the byte offset converted from the UTF-16 offset of |location| )""" line_num = location[ 'line' ] + 1 try: line_value = file_contents[ location[ 'line' ] ] return line_value, line_num, utils.CodepointOffsetToByteOffset( line_value, lsp.UTF16CodeUnitsToCodepoints( line_value, location[ 'character' ] + 1 ) ) except IndexError: # This can happen when there are stale diagnostics in OnFileReadyToParse, # just return the value as-is. return '', line_num, location[ 'character' ] + 1 def _CursorInsideLocation( request_data, location ): try: filepath = lsp.UriToFilePath( location[ 'uri' ] ) except lsp.InvalidUriException: LOGGER.debug( 'Invalid URI, assume cursor is not inside the location' ) return False if request_data[ 'filepath' ] != filepath: return False line = request_data[ 'line_num' ] column = request_data[ 'column_num' ] file_contents = GetFileLines( request_data, filepath ) lsp_range = location[ 'range' ] _, start_line, start_column = _LspToYcmdLocation( file_contents, lsp_range[ 'start' ] ) if ( line < start_line or ( line == start_line and column < start_column ) ): return False _, end_line, end_column = _LspToYcmdLocation( file_contents, lsp_range[ 'end' ] ) if ( line > end_line or ( line == end_line and column > end_column ) ): return False return True def _BuildLocationAndDescription( filename, file_contents, location ): """Returns a tuple of ( - ycmd Location for the supplied filename and LSP location - contents of the line at that location ) Importantly, converts from LSP Unicode offset to ycmd byte offset.""" line_value, line, column = _LspToYcmdLocation( file_contents, location ) return responses.Location( line, column, filename = filename ), line_value def _BuildRange( contents, filename, r ): """Returns a ycmd range from a LSP range |r|.""" return responses.Range( _BuildLocationAndDescription( filename, contents, r[ 'start' ] )[ 0 ], _BuildLocationAndDescription( filename, contents, r[ 'end' ] )[ 0 ] ) def _BuildDiagnostic( contents, uri, diag ): """Return a ycmd diagnostic from a LSP diagnostic.""" try: filename = lsp.UriToFilePath( uri ) except lsp.InvalidUriException: LOGGER.debug( 'Invalid URI received for diagnostic' ) filename = '' r = _BuildRange( contents, filename, diag[ 'range' ] ) diag_text = diag[ 'message' ] try: code = diag[ 'code' ] diag_text += " [" + str( code ) + "]" except KeyError: # code field doesn't exist. pass return responses.Diagnostic( ranges = [ r ], location = r.start_, location_extent = r, text = diag_text, kind = lsp.SEVERITY[ diag[ 'severity' ] ].upper() ) def TextEditToChunks( request_data, uri, text_edit ): """Returns a list of FixItChunks from a LSP textEdit.""" try: filepath = lsp.UriToFilePath( uri ) except lsp.InvalidUriException: LOGGER.debug( 'Invalid filepath received in TextEdit' ) filepath = '' contents = GetFileLines( request_data, filepath ) return [ responses.FixItChunk( change[ 'newText' ], _BuildRange( contents, filepath, change[ 'range' ] ) ) for change in text_edit ] def WorkspaceEditToFixIt( request_data, workspace_edit, text='', kind = None ): """Converts a LSP workspace edit to a ycmd FixIt suitable for passing to responses.BuildFixItResponse.""" if not workspace_edit: return None if 'changes' in workspace_edit: chunks = [] # We sort the filenames to make the response stable. Edits are applied in # strict sequence within a file, but apply to files in arbitrary order. # However, it's important for the response to be stable for the tests. for uri in sorted( workspace_edit[ 'changes' ].keys() ): chunks.extend( TextEditToChunks( request_data, uri, workspace_edit[ 'changes' ][ uri ] ) ) else: chunks = [] for text_document_edit in workspace_edit[ 'documentChanges' ]: uri = text_document_edit[ 'textDocument' ][ 'uri' ] edits = text_document_edit[ 'edits' ] chunks.extend( TextEditToChunks( request_data, uri, edits ) ) return responses.FixIt( responses.Location( request_data[ 'line_num' ], request_data[ 'column_num' ], request_data[ 'filepath' ] ), chunks, text, kind ) class LanguageServerCompletionsCache( CompletionsCache ): """Cache of computed LSP completions for a particular request.""" def Invalidate( self ): with self._access_lock: super().InvalidateNoLock() self._is_incomplete = False self._use_start_column = True def Update( self, request_data, completions, is_incomplete ): with self._access_lock: super().UpdateNoLock( request_data, completions ) self._is_incomplete = is_incomplete if is_incomplete: self._use_start_column = False def GetCodepointForCompletionRequest( self, request_data ): with self._access_lock: if self._use_start_column: return request_data[ 'start_codepoint' ] return request_data[ 'column_codepoint' ] # Must be called under the lock. def _IsQueryPrefix( self, request_data ): return request_data[ 'query' ].startswith( self._request_data[ 'query' ] ) def GetCompletionsIfCacheValid( self, request_data ): with self._access_lock: if ( not self._is_incomplete and ( self._use_start_column or self._IsQueryPrefix( request_data ) ) ): return super().GetCompletionsIfCacheValidNoLock( request_data ) return None class RejectCollector: def CollectApplyEdit( self, request, connection ): connection.SendResponse( lsp.ApplyEditResponse( request, False ) ) class EditCollector: def __init__( self ): self.requests = [] def CollectApplyEdit( self, request, connection ): self.requests.append( request[ 'params' ] ) connection.SendResponse( lsp.ApplyEditResponse( request, True ) ) class WatchdogHandler( PatternMatchingEventHandler ): def __init__( self, server, patterns ): super().__init__( patterns ) self._server = server def on_created( self, event ): if self._server.ServerIsReady(): with self._server._server_info_mutex: msg = lsp.DidChangeWatchedFiles( event.src_path, 'create' ) self._server.GetConnection().SendNotification( msg ) def on_modified( self, event ): if self._server.ServerIsReady(): with self._server._server_info_mutex: msg = lsp.DidChangeWatchedFiles( event.src_path, 'modify' ) self._server.GetConnection().SendNotification( msg ) def on_deleted( self, event ): if self._server.ServerIsReady(): with self._server._server_info_mutex: msg = lsp.DidChangeWatchedFiles( event.src_path, 'delete' ) self._server.GetConnection().SendNotification( msg ) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/language_server/language_server_protocol.py�����������0000664�0000000�0000000�00000051307�13746324601�0031073�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2017-2020 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 <http://www.gnu.org/licenses/>. import collections import os import json import hashlib from urllib.parse import urljoin, urlparse, unquote from urllib.request import pathname2url, url2pathname from ycmd.utils import ( ByteOffsetToCodepointOffset, ToBytes, ToUnicode, UpdateDict ) Error = collections.namedtuple( 'RequestError', [ 'code', 'reason' ] ) class Errors: # From # https://microsoft.github.io/language-server-protocol/specification#response-message # # JSON RPC ParseError = Error( -32700, "Parse error" ) InvalidRequest = Error( -32600, "Invalid request" ) MethodNotFound = Error( -32601, "Method not found" ) InvalidParams = Error( -32602, "Invalid parameters" ) InternalError = Error( -32603, "Internal error" ) # The following sentinel values represent the range of errors for "user # defined" server errors. We don't define them as actual errors, as they are # just representing a valid range. # # export const serverErrorStart: number = -32099; # export const serverErrorEnd: number = -32000; # LSP defines the following custom server errors ServerNotInitialized = Error( -32002, "Server not initialized" ) UnknownErrorCode = Error( -32001, "Unknown error code" ) # LSP request errors RequestCancelled = Error( -32800, "The request was canceled" ) ContentModified = Error( -32801, "Content was modified" ) INSERT_TEXT_FORMAT = [ None, # 1-based 'PlainText', 'Snippet' ] ITEM_KIND = [ None, # 1-based 'Text', 'Method', 'Function', 'Constructor', 'Field', 'Variable', 'Class', 'Interface', 'Module', 'Property', 'Unit', 'Value', 'Enum', 'Keyword', 'Snippet', 'Color', 'File', 'Reference', 'Folder', 'EnumMember', 'Constant', 'Struct', 'Event', 'Operator', 'TypeParameter', ] SEVERITY = [ None, 'Error', 'Warning', 'Information', 'Hint', ] FILE_EVENT_KIND = { 'create': 1, 'modify': 2, 'delete': 3 } SYMBOL_KIND = [ None, 'File', 'Module', 'Namespace', 'Package', 'Class', 'Method', 'Property', 'Field', 'Constructor', 'Enum', 'Interface', 'Function', 'Variable', 'Constant', 'String', 'Number', 'Boolean', 'Array', 'Object', 'Key', 'Null', 'EnumMember', 'Struct', 'Event', 'Operator', 'TypeParameter', ] class InvalidUriException( Exception ): """Raised when trying to convert a server URI to a file path but the scheme was not supported. Only the file: scheme is supported""" pass class ServerFileStateStore( dict ): """Trivial default-dict-like class to hold ServerFileState for a given filepath. Language server clients must maintain one of these for each language server connection.""" def __missing__( self, key ): self[ key ] = ServerFileState( key ) return self[ key ] class ServerFileState: """State machine for a particular file from the server's perspective, including version.""" # States OPEN = 'Open' CLOSED = 'Closed' # Actions CLOSE_FILE = 'Close' NO_ACTION = 'None' OPEN_FILE = 'Open' CHANGE_FILE = 'Change' def __init__( self, filename ): self.filename = filename self.version = 0 self.state = ServerFileState.CLOSED self.checksum = None self.contents = '' def GetDirtyFileAction( self, contents ): """Progress the state for a file to be updated due to being supplied in the dirty buffers list. Returns any one of the Actions to perform.""" new_checksum = self._CalculateCheckSum( contents ) if ( self.state == ServerFileState.OPEN and self.checksum.digest() == new_checksum.digest() ): return ServerFileState.NO_ACTION elif self.state == ServerFileState.CLOSED: self.version = 0 action = ServerFileState.OPEN_FILE else: action = ServerFileState.CHANGE_FILE return self._SendNewVersion( new_checksum, action, contents ) def GetSavedFileAction( self, contents ): """Progress the state for a file to be updated due to having previously been opened, but no longer supplied in the dirty buffers list. Returns one of the Actions to perform: either NO_ACTION or CHANGE_FILE.""" # We only need to update if the server state is open if self.state != ServerFileState.OPEN: return ServerFileState.NO_ACTION new_checksum = self._CalculateCheckSum( contents ) if self.checksum.digest() == new_checksum.digest(): return ServerFileState.NO_ACTION return self._SendNewVersion( new_checksum, ServerFileState.CHANGE_FILE, contents ) def GetFileCloseAction( self ): """Progress the state for a file which was closed in the client. Returns one of the actions to perform: either NO_ACTION or CLOSE_FILE.""" if self.state == ServerFileState.OPEN: self.state = ServerFileState.CLOSED return ServerFileState.CLOSE_FILE self.state = ServerFileState.CLOSED return ServerFileState.NO_ACTION def _SendNewVersion( self, new_checksum, action, contents ): self.checksum = new_checksum self.version = self.version + 1 self.state = ServerFileState.OPEN self.contents = contents return action def _CalculateCheckSum( self, contents ): return hashlib.sha1( ToBytes( contents ) ) def BuildRequest( request_id, method, parameters ): """Builds a JSON RPC request message with the supplied ID, method and method parameters""" return _BuildMessageData( { 'id': request_id, 'method': method, 'params': parameters, } ) def BuildNotification( method, parameters ): """Builds a JSON RPC notification message with the supplied method and method parameters""" return _BuildMessageData( { 'method': method, 'params': parameters, } ) def BuildResponse( request, parameters ): """Builds a JSON RPC response message to respond to the supplied |request| message. |parameters| should contain either 'error' or 'result'""" message = { 'id': request[ 'id' ] } message.update( parameters ) return _BuildMessageData( message ) def Initialize( request_id, project_directory, extra_capabilities, settings ): """Build the Language Server initialize request""" capabilities = { 'workspace': { 'applyEdit': True, 'didChangeWatchedFiles': { 'dynamicRegistration': True }, 'workspaceEdit': { 'documentChanges': True, }, 'symbol': { 'symbolKind': { 'valueSet': list( range( 1, len( SYMBOL_KIND ) ) ), } } }, 'textDocument': { 'codeAction': { 'codeActionLiteralSupport': { 'codeActionKind': { 'valueSet': [ '', 'quickfix', 'refactor', 'refactor.extract', 'refactor.inline', 'refactor.rewrite', 'source', 'source.organizeImports' ] } } }, 'completion': { 'completionItemKind': { # ITEM_KIND list is 1-based. # valueSet is a list of the indices of items supported 'valueSet': list( range( 1, len( ITEM_KIND ) ) ), }, 'completionItem': { 'documentationFormat': [ 'plaintext', 'markdown' ], }, }, 'hover': { 'contentFormat': [ 'plaintext', 'markdown' ] }, 'signatureHelp': { 'signatureInformation': { 'parameterInformation': { 'labelOffsetSupport': True, }, 'documentationFormat': [ 'plaintext', 'markdown' ], }, }, 'synchronization': { 'didSave': True }, }, } return BuildRequest( request_id, 'initialize', { 'processId': os.getpid(), 'rootPath': project_directory, 'rootUri': FilePathToUri( project_directory ), 'initializationOptions': settings, 'capabilities': UpdateDict( capabilities, extra_capabilities ), } ) def Initialized(): return BuildNotification( 'initialized', {} ) def Shutdown( request_id ): return BuildRequest( request_id, 'shutdown', None ) def Exit(): return BuildNotification( 'exit', None ) def Void( request ): return Accept( request, None ) def Reject( request, request_error, data = None ): msg = { 'error': { 'code': request_error.code, 'message': request_error.reason, } } if data is not None: msg[ 'error' ][ 'data' ] = data return BuildResponse( request, msg ) def Accept( request, result ): msg = { 'result': result } return BuildResponse( request, msg ) def ApplyEditResponse( request, applied ): msg = { 'applied': applied } return Accept( request, msg ) def DidChangeWatchedFiles( path, kind ): return BuildNotification( 'workspace/didChangeWatchedFiles', { 'changes': [ { 'uri': FilePathToUri( path ), 'type': FILE_EVENT_KIND[ kind ] } ] } ) def DidChangeConfiguration( config ): return BuildNotification( 'workspace/didChangeConfiguration', { 'settings': config, } ) def DidOpenTextDocument( file_state, file_types, file_contents ): return BuildNotification( 'textDocument/didOpen', { 'textDocument': { 'uri': FilePathToUri( file_state.filename ), 'languageId': '/'.join( file_types ), 'version': file_state.version, 'text': file_contents } } ) def DidChangeTextDocument( file_state, file_contents ): # NOTE: Passing `None` for the second argument will send an empty # textDocument/didChange notification. It is useful when a LSP server # needs to be forced to reparse a file without sending all the changes. # More specifically, clangd completer relies on this. return BuildNotification( 'textDocument/didChange', { 'textDocument': { 'uri': FilePathToUri( file_state.filename ), 'version': file_state.version, }, 'contentChanges': [ { 'text': file_contents }, ] if file_contents is not None else [], } ) def DidSaveTextDocument( file_state, file_contents ): params = { 'textDocument': { 'uri': FilePathToUri( file_state.filename ), 'version': file_state.version, }, } if file_contents is not None: params.update( { 'text': file_contents } ) return BuildNotification( 'textDocument/didSave', params ) def DidCloseTextDocument( file_state ): return BuildNotification( 'textDocument/didClose', { 'textDocument': { 'uri': FilePathToUri( file_state.filename ), 'version': file_state.version, }, } ) def Completion( request_id, request_data, codepoint ): return BuildRequest( request_id, 'textDocument/completion', { 'textDocument': { 'uri': FilePathToUri( request_data[ 'filepath' ] ), }, 'position': Position( request_data[ 'line_num' ], request_data[ 'line_value' ], codepoint ), } ) def ResolveCompletion( request_id, completion ): return BuildRequest( request_id, 'completionItem/resolve', completion ) def SignatureHelp( request_id, request_data ): return BuildRequest( request_id, 'textDocument/signatureHelp', BuildTextDocumentPositionParams( request_data ) ) def Hover( request_id, request_data ): return BuildRequest( request_id, 'textDocument/hover', BuildTextDocumentPositionParams( request_data ) ) def Definition( request_id, request_data ): return BuildRequest( request_id, 'textDocument/definition', BuildTextDocumentPositionParams( request_data ) ) def Declaration( request_id, request_data ): return BuildRequest( request_id, 'textDocument/declaration', BuildTextDocumentPositionParams( request_data ) ) def TypeDefinition( request_id, request_data ): return BuildRequest( request_id, 'textDocument/typeDefinition', BuildTextDocumentPositionParams( request_data ) ) def Implementation( request_id, request_data ): return BuildRequest( request_id, 'textDocument/implementation', BuildTextDocumentPositionParams( request_data ) ) def CodeAction( request_id, request_data, best_match_range, diagnostics ): return BuildRequest( request_id, 'textDocument/codeAction', { 'textDocument': { 'uri': FilePathToUri( request_data[ 'filepath' ] ), }, 'range': best_match_range, 'context': { 'diagnostics': diagnostics, }, } ) def Rename( request_id, request_data, new_name ): return BuildRequest( request_id, 'textDocument/rename', { 'textDocument': { 'uri': FilePathToUri( request_data[ 'filepath' ] ), }, 'newName': new_name, 'position': Position( request_data[ 'line_num' ], request_data[ 'line_value' ], request_data[ 'column_codepoint' ] ) } ) def WorkspaceSymbol( request_id, query ): return BuildRequest( request_id, 'workspace/symbol', { 'query': query, } ) def BuildTextDocumentPositionParams( request_data ): return { 'textDocument': { 'uri': FilePathToUri( request_data[ 'filepath' ] ), }, 'position': Position( request_data[ 'line_num' ], request_data[ 'line_value' ], request_data[ 'column_codepoint' ] ) } def References( request_id, request_data ): request = BuildTextDocumentPositionParams( request_data ) request[ 'context' ] = { 'includeDeclaration': True } return BuildRequest( request_id, 'textDocument/references', request ) def Position( line_num, line_value, column_codepoint ): # The API requires 0-based line number and 0-based UTF-16 offset. return { 'line': line_num - 1, 'character': CodepointsToUTF16CodeUnits( line_value, column_codepoint ) - 1 } def Formatting( request_id, request_data ): return BuildRequest( request_id, 'textDocument/formatting', { 'textDocument': { 'uri': FilePathToUri( request_data[ 'filepath' ] ), }, 'options': FormattingOptions( request_data ) } ) def RangeFormatting( request_id, request_data ): return BuildRequest( request_id, 'textDocument/rangeFormatting', { 'textDocument': { 'uri': FilePathToUri( request_data[ 'filepath' ] ), }, 'range': Range( request_data ), 'options': FormattingOptions( request_data ) } ) def FormattingOptions( request_data ): options = request_data[ 'options' ] format_options = { 'tabSize': options.pop( 'tab_size' ), 'insertSpaces': options.pop( 'insert_spaces' ) } format_options.update( options ) return format_options def Range( request_data ): lines = request_data[ 'lines' ] if 'range' not in request_data: start_codepoint = request_data[ 'start_codepoint' ] start_line_num = request_data[ 'line_num' ] start_line_value = request_data[ 'line_value' ] end_codepoint = start_codepoint + 1 end_line_num = start_line_num end_line_value = start_line_value else: start = request_data[ 'range' ][ 'start' ] start_line_num = start[ 'line_num' ] end = request_data[ 'range' ][ 'end' ] end_line_num = end[ 'line_num' ] try: start_line_value = lines[ start_line_num - 1 ] start_codepoint = ByteOffsetToCodepointOffset( start_line_value, start[ 'column_num' ] ) end_line_value = lines[ end_line_num - 1 ] end_codepoint = ByteOffsetToCodepointOffset( end_line_value, end[ 'column_num' ] ) except IndexError: raise RuntimeError( "Invalid range" ) # LSP requires to use the start of the next line as the end position for a # range that ends with a newline. if end_codepoint >= len( end_line_value ): end_line_num += 1 end_line_value = '' end_codepoint = 1 return { 'start': Position( start_line_num, start_line_value, start_codepoint ), 'end': Position( end_line_num, end_line_value, end_codepoint ) } def ExecuteCommand( request_id, command, arguments ): return BuildRequest( request_id, 'workspace/executeCommand', { 'command': command, 'arguments': arguments } ) def FilePathToUri( file_name ): return urljoin( 'file:', pathname2url( file_name ) ) def UriToFilePath( uri ): parsed_uri = urlparse( uri ) if parsed_uri.scheme != 'file': raise InvalidUriException( uri ) # url2pathname doesn't work as expected when uri.path is percent-encoded and # is a windows path for ex: # url2pathname('/C%3a/') == 'C:\\C:' # whereas # url2pathname('/C:/') == 'C:\\' # Therefore first unquote pathname. pathname = unquote( parsed_uri.path ) return os.path.abspath( url2pathname( pathname ) ) def _BuildMessageData( message ): message[ 'jsonrpc' ] = '2.0' # NOTE: sort_keys=True is needed to workaround a 'limitation' of clangd where # it requires keys to be in a specific order, due to a somewhat naive # JSON/YAML parser. data = ToBytes( json.dumps( message, separators = ( ',', ':' ), sort_keys=True ) ) packet = ToBytes( f'Content-Length: { len( data ) }\r\n\r\n' ) + data return packet def Parse( data ): """Reads the raw language server message payload into a Python dictionary""" return json.loads( ToUnicode( data ) ) def CodepointsToUTF16CodeUnits( line_value, codepoint_offset ): """Return the 1-based UTF-16 code unit offset equivalent to the 1-based unicode codepoint offset |codepoint_offset| in the Unicode string |line_value|""" # Language server protocol requires offsets to be in utf16 code _units_. # Each code unit is 2 bytes. # So we re-encode the line as utf-16 and divide the length in bytes by 2. # # Of course, this is a terrible API, but until all the servers support any # change out of # https://github.com/Microsoft/language-server-protocol/issues/376 then we # have to jump through hoops. if codepoint_offset > len( line_value ): return ( len( line_value.encode( 'utf-16-le' ) ) + 2 ) // 2 value_as_utf16 = line_value[ : codepoint_offset ].encode( 'utf-16-le' ) return len( value_as_utf16 ) // 2 def UTF16CodeUnitsToCodepoints( line_value, code_unit_offset ): """Return the 1-based codepoint offset into the unicode string |line_value| equivalent to the 1-based UTF-16 code unit offset |code_unit_offset| into a UTF-16 encoded version of |line_value|""" # As above, LSP returns offsets in utf16 code units. So we convert the line to # UTF16, snip everything up to the code_unit_offset * 2 bytes (each code unit # is 2 bytes), then re-encode as unicode and return the length (in # codepoints). value_as_utf16_bytes = ToBytes( line_value.encode( 'utf-16-le' ) ) byte_offset_utf16 = code_unit_offset * 2 if byte_offset_utf16 > len( value_as_utf16_bytes ): # If the offset points off the end of the string, then the codepoint offset # is one-past-the-end of the string in unicode codepoints return len( line_value ) + 1 bytes_included = value_as_utf16_bytes[ : code_unit_offset * 2 ] return len( bytes_included.decode( 'utf-16-le' ) ) def ComparePositions( a, b ): """Returns < 0 if a is before b, 0 if a and b are equal and > 0 if a is after b. a and b are both LSP positions.""" # If they are on the same line, compare character pos if a[ 'line' ] == b[ 'line' ]: return a[ 'character' ] - b[ 'character' ] # otherwise, just compare the lines return a[ 'line' ] - b[ 'line' ] def RangesOverlap( a, b ): """Returns true if the LSP ranges a and b strictly overlap""" # if the diag ends before the start of the cursor, it's not overlapping if ComparePositions( a[ 'end' ], b[ 'start' ] ) < 0: return False # if the diag starts after the end of the cursor, it's not overlapping if ComparePositions( a[ 'start' ], b[ 'end' ] ) > 0: return False # Otherwise it overaps in at least 1 char return True def RangesOverlapLines( a, b ): """Returns true if the LSP ranges a and b share any lines""" if a[ 'end' ][ 'line' ] < b[ 'start' ][ 'line' ]: return False if a[ 'start' ][ 'line' ] > b[ 'end' ][ 'line' ]: return False return True �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/objc/�������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0021165�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/objc/__init__.py��������������������������������������0000664�0000000�0000000�00000000000�13746324601�0023264�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/objc/hook.py������������������������������������������0000664�0000000�0000000�00000002223�13746324601�0022476�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from ycmd.completers.cpp.clang_completer import ClangCompleter from ycmd.completers.cpp.clangd_completer import ( ShouldEnableClangdCompleter, ClangdCompleter ) from ycmd.utils import ImportCore ycm_core = ImportCore() def GetCompleter( user_options ): if ShouldEnableClangdCompleter( user_options ): return ClangdCompleter( user_options ) if ycm_core.HasClangSupport(): return ClangCompleter( user_options ) return None �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/objcpp/�����������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0021525�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/objcpp/__init__.py������������������������������������0000664�0000000�0000000�00000000000�13746324601�0023624�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/objcpp/hook.py����������������������������������������0000664�0000000�0000000�00000002223�13746324601�0023036�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from ycmd.completers.cpp.clang_completer import ClangCompleter from ycmd.completers.cpp.clangd_completer import ( ShouldEnableClangdCompleter, ClangdCompleter ) from ycmd.utils import ImportCore ycm_core = ImportCore() def GetCompleter( user_options ): if ShouldEnableClangdCompleter( user_options ): return ClangdCompleter( user_options ) if ycm_core.HasClangSupport(): return ClangCompleter( user_options ) return None �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/python/�����������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0021571�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/python/__init__.py������������������������������������0000664�0000000�0000000�00000000000�13746324601�0023670�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/python/hook.py����������������������������������������0000664�0000000�0000000�00000001474�13746324601�0023111�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from ycmd.completers.python.python_completer import PythonCompleter def GetCompleter( user_options ): return PythonCompleter( user_options ) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/python/python_completer.py����������������������������0000664�0000000�0000000�00000055160�13746324601�0025545�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2011-2020 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 <http://www.gnu.org/licenses/>. from ycmd import extra_conf_store, responses from ycmd.completers.completer import Completer, SignatureHelpAvailalability from ycmd.utils import ( CodepointOffsetToByteOffset, ExpandVariablesInPath, FindExecutable, LOGGER ) import difflib import itertools import jedi import os import parso from threading import Lock class PythonCompleter( Completer ): """ A completer for the Python language using the Jedi semantic engine: https://jedi.readthedocs.org/en/latest/ """ def __init__( self, user_options ): super().__init__( user_options ) self._jedi_lock = Lock() self._settings_for_file = {} self._environment_for_file = {} self._environment_for_interpreter_path = {} self._jedi_project_for_file = {} self.SetSignatureHelpTriggers( [ '(', ',' ] ) def SupportedFiletypes( self ): return [ 'python' ] def OnFileReadyToParse( self, request_data ): # This is implicitly loading the extra conf file and caching the Jedi # environment and Python path. environment = self._EnvironmentForRequest( request_data ) self._JediProjectForFile( request_data, environment ) def _SettingsForRequest( self, request_data ): filepath = request_data[ 'filepath' ] client_data = request_data[ 'extra_conf_data' ] try: return self._settings_for_file[ filepath, client_data ] except KeyError: pass module = extra_conf_store.ModuleForSourceFile( filepath ) settings = self._GetSettings( module, filepath, client_data ) self._settings_for_file[ filepath, client_data ] = settings return settings def _GetSettings( self, module, filepath, client_data ): # We don't warn the user if no extra conf file is found. if module: if hasattr( module, 'Settings' ): settings = module.Settings( language = 'python', filename = filepath, client_data = client_data ) if settings is not None: return settings LOGGER.debug( 'No Settings function defined in %s', module.__file__ ) return { # NOTE: this option is only kept for backward compatibility. Setting the # Python interpreter path through the extra conf file is preferred. 'interpreter_path': self.user_options[ 'python_binary_path' ] } def _EnvironmentForInterpreterPath( self, interpreter_path ): if interpreter_path: resolved_interpreter_path = FindExecutable( ExpandVariablesInPath( interpreter_path ) ) if not resolved_interpreter_path: raise RuntimeError( 'Cannot find Python interpreter path ' f'{ interpreter_path }.' ) interpreter_path = os.path.normpath( resolved_interpreter_path ) try: return self._environment_for_interpreter_path[ interpreter_path ] except KeyError: pass # Assume paths specified by the user are safe. environment = ( jedi.get_default_environment() if not interpreter_path else jedi.create_environment( interpreter_path, safe = False ) ) self._environment_for_interpreter_path[ interpreter_path ] = environment return environment def _EnvironmentForRequest( self, request_data ): filepath = request_data[ 'filepath' ] client_data = request_data[ 'extra_conf_data' ] try: return self._environment_for_file[ filepath, client_data ] except KeyError: pass settings = self._SettingsForRequest( request_data ) interpreter_path = settings.get( 'interpreter_path' ) environment = self._EnvironmentForInterpreterPath( interpreter_path ) self._environment_for_file[ filepath, client_data ] = environment return environment def _GetJediProject( self, request_data, environment ): settings = { 'sys_path': [] } settings.update( self._SettingsForRequest( request_data ) ) settings[ 'interpreter_path' ] = environment.executable settings[ 'sys_path' ].extend( environment.get_sys_path() ) filepath = request_data[ 'filepath' ] module = extra_conf_store.ModuleForSourceFile( filepath ) # We don't warn the user if no extra conf file is found. if module: if hasattr( module, 'PythonSysPath' ): settings[ 'sys_path' ] = module.PythonSysPath( **settings ) LOGGER.debug( 'No PythonSysPath function defined in %s', module.__file__ ) project_directory = settings.get( 'project_directory' ) if not project_directory: default_project = jedi.get_default_project( os.path.dirname( request_data[ 'filepath' ] ) ) project_directory = default_project._path return jedi.Project( project_directory, sys_path = settings[ 'sys_path' ], environment_path = settings[ 'interpreter_path' ] ) def _JediProjectForFile( self, request_data, environment ): filepath = request_data[ 'filepath' ] client_data = request_data[ 'extra_conf_data' ] try: return self._jedi_project_for_file[ filepath, client_data ] except KeyError: pass jedi_project = self._GetJediProject( request_data, environment ) self._jedi_project_for_file[ filepath, client_data ] = jedi_project return jedi_project def _GetJediScript( self, request_data ): path = request_data[ 'filepath' ] source = request_data[ 'file_data' ][ path ][ 'contents' ] environment = self._EnvironmentForRequest( request_data ) jedi_project = self._JediProjectForFile( request_data, environment ) return jedi.Script( source, path = path, project = jedi_project, environment = environment ) # This method must be called under Jedi's lock. def _GetExtraData( self, completion ): if completion.module_path and completion.line and completion.column: return { 'location': { 'filepath': completion.module_path, 'line_num': completion.line, 'column_num': completion.column + 1 } } return {} def ComputeCandidatesInner( self, request_data ): with self._jedi_lock: line = request_data[ 'line_num' ] # Jedi expects columns to start at 0, not 1, and for them to be Unicode # codepoint offsets. column = request_data[ 'start_codepoint' ] - 1 completions = self._GetJediScript( request_data ).complete( line, column ) return [ responses.BuildCompletionData( insertion_text = completion.complete, # We store the Completion object returned by Jedi in the extra_data # field to detail the candidates once the filtering is done. extra_data = completion ) for completion in completions ] def SignatureHelpAvailable( self ): return SignatureHelpAvailalability.AVAILABLE def ComputeSignaturesInner( self, request_data ): with self._jedi_lock: line = request_data[ 'line_num' ] # Jedi expects columns to start at 0, not 1, and for them to be Unicode # codepoint offsets. column = request_data[ 'start_codepoint' ] - 1 signatures = self._GetJediScript( request_data ).get_signatures( line, column ) # Sorting by the number or arguments makes the order stable for the tests # and isn't harmful. The order returned by jedi seems to be arbitrary. signatures.sort( key=lambda s: len( s.params ) ) active_signature = 0 active_parameter = 0 for index, signature in enumerate( signatures ): if signature.index is not None: active_signature = index active_parameter = signature.index break def MakeSignature( s ): label = s.description + '( ' parameters = [] for index, p in enumerate( s.params ): # We remove 'param ' from the start of each parameter (hence the 6:) param = p.description[ 6: ] start = len( label ) end = start + len( param ) label += param if index < len( s.params ) - 1: label += ', ' parameters.append( { 'label': [ CodepointOffsetToByteOffset( label, start ), CodepointOffsetToByteOffset( label, end ) ] } ) label += ' )' return { 'label': label, 'parameters': parameters, } return { 'activeSignature': active_signature, 'activeParameter': active_parameter, 'signatures': [ MakeSignature( s ) for s in signatures ], } def DetailCandidates( self, request_data, candidates ): with self._jedi_lock: for candidate in candidates: if isinstance( candidate[ 'extra_data' ], dict ): # This candidate is already detailed. continue completion = candidate[ 'extra_data' ] candidate[ 'extra_menu_info' ] = self._BuildTypeInfo( completion ) candidate[ 'detailed_info' ] = completion.docstring() candidate[ 'kind' ] = completion.type candidate[ 'extra_data' ] = self._GetExtraData( completion ) return candidates def GetSubcommandsMap( self ): return { 'GoTo' : ( lambda self, request_data, args: self._GoToDefinition( request_data ) ), 'GoToDefinition' : ( lambda self, request_data, args: self._GoToDefinition( request_data ) ), 'GoToDeclaration': ( lambda self, request_data, args: self._GoToDefinition( request_data ) ), 'GoToReferences' : ( lambda self, request_data, args: self._GoToReferences( request_data ) ), 'GoToSymbol' : ( lambda self, request_data, args: self._GoToSymbol( request_data, args ) ), 'GoToType' : ( lambda self, request_data, args: self._GoToType( request_data ) ), 'GetType' : ( lambda self, request_data, args: self._GetType( request_data ) ), 'GetDoc' : ( lambda self, request_data, args: self._GetDoc( request_data ) ), 'RefactorRename' : ( lambda self, request_data, args: self._RefactorRename( request_data, args ) ), } def _BuildGoToResponse( self, definitions, request_data ): if len( definitions ) == 1: definition = definitions[ 0 ] column = 1 if all( x is None for x in [ definition.column, definition.line, definition.module_path ] ): return None if definition.column is not None: column += definition.column filepath = definition.module_path or request_data[ 'filepath' ] return responses.BuildGoToResponse( filepath, definition.line, column ) gotos = [] for definition in definitions: column = 1 if all( x is None for x in [ definition.column, definition.line, definition.module_path ] ): continue if definition.column is not None: column += definition.column filepath = definition.module_path or request_data[ 'filepath' ] gotos.append( responses.BuildGoToResponse( filepath, definition.line, column, definition.description ) ) return gotos def _GoToType( self, request_data ): with self._jedi_lock: line = request_data[ 'line_num' ] # Jedi expects columns to start at 0, not 1, and for them to be Unicode # codepoint offsets. column = request_data[ 'start_codepoint' ] - 1 script = self._GetJediScript( request_data ) definitions = script.infer( line, column ) if definitions: type_def = self._BuildGoToResponse( definitions, request_data ) if type_def is not None: return type_def raise RuntimeError( 'Can\'t jump to type definition.' ) def _GoToDefinition( self, request_data ): with self._jedi_lock: line = request_data[ 'line_num' ] # Jedi expects columns to start at 0, not 1, and for them to be Unicode # codepoint offsets. column = request_data[ 'start_codepoint' ] - 1 script = self._GetJediScript( request_data ) definitions = script.goto( line, column ) if definitions: definitions = self._BuildGoToResponse( definitions, request_data ) if definitions is not None: return definitions raise RuntimeError( 'Can\'t jump to definition.' ) def _GoToReferences( self, request_data ): with self._jedi_lock: line = request_data[ 'line_num' ] # Jedi expects columns to start at 0, not 1, and for them to be Unicode # codepoint offsets. column = request_data[ 'start_codepoint' ] - 1 definitions = self._GetJediScript( request_data ).get_references( line, column ) if definitions: references = self._BuildGoToResponse( definitions, request_data ) if references is not None: return references raise RuntimeError( 'Can\'t find references.' ) def _GoToSymbol( self, request_data, args ): if len( args ) < 1: raise RuntimeError( 'Must specify something to search for' ) query = args[ 0 ] # Jedi docs say: # Searches a name in the whole project. If the project is very big, at # some point Jedi will stop searching. However it’s also very much # recommended to not exhaust the generator. Just display the first ten # results to the user. MAX_RESULTS = 10 with self._jedi_lock: environent = self._EnvironmentForRequest( request_data ) project = self._JediProjectForFile( request_data, environent ) definitions = list( itertools.islice( project.complete_search( query ), MAX_RESULTS ) ) if definitions: definitions = self._BuildGoToResponse( definitions, request_data ) if definitions is not None: return definitions raise RuntimeError( 'Symbol not found' ) # This method must be called under Jedi's lock. def _BuildTypeInfo( self, definition ): type_info = definition.description # Jedi doesn't return the signature in the description. Build the signature # from the params field. try: # Remove the "param " prefix from the description. parameters = definition.get_signatures()[ 0 ].params type_info += '(' + ', '.join( [ param.description[ 6: ] for param in parameters ] ) + ')' except IndexError: pass return type_info def _GetType( self, request_data ): with self._jedi_lock: line = request_data[ 'line_num' ] # Jedi expects columns to start at 0, not 1, and for them to be Unicode # codepoint offsets. column = request_data[ 'start_codepoint' ] - 1 definitions = self._GetJediScript( request_data ).infer( line, column ) type_info = [ self._BuildTypeInfo( definition ) for definition in definitions ] type_info = ', '.join( type_info ) if type_info: return responses.BuildDisplayMessageResponse( type_info ) raise RuntimeError( 'No type information available.' ) def _GetDoc( self, request_data ): with self._jedi_lock: line = request_data[ 'line_num' ] # Jedi expects columns to start at 0, not 1, and for them to be Unicode # codepoint offsets. column = request_data[ 'start_codepoint' ] - 1 definitions = self._GetJediScript( request_data ).goto( line, column ) documentation = [ definition.docstring().strip() for definition in definitions ] documentation = '\n---\n'.join( [ d for d in documentation if d ] ) if documentation: return responses.BuildDetailedInfoResponse( documentation ) raise RuntimeError( 'No documentation available.' ) def _RefactorRename( self, request_data, args ): if len( args ) < 1: raise RuntimeError( 'Must specify a new name' ) new_name = args[ 0 ] with self._jedi_lock: refactoring = self._GetJediScript( request_data ).rename( line = request_data[ 'line_num' ], column = request_data[ 'column_codepoint' ] - 1, new_name = new_name ) return responses.BuildFixItResponse( [ _RefactoringToFixIt( refactoring ) ] ) # Jedi has the following refactorings: # - renmae (RefactorRename) # - inline variable # - extract variable (requires argument) # - extract function (requires argument) # # We could add inline variable via FixIt, but for the others we have no way to # ask for the argument on "resolve" of the FixIt. We could add # Refactor Inline ... but that would be inconsistent. def DebugInfo( self, request_data ): environment = self._EnvironmentForRequest( request_data ) python_interpreter = responses.DebugInfoItem( key = 'Python interpreter', value = environment.executable ) python_root = responses.DebugInfoItem( key = 'Python root', value = str( self._JediProjectForFile( request_data, environment )._path ) ) python_path = responses.DebugInfoItem( key = 'Python path', value = str( self._JediProjectForFile( request_data, environment )._sys_path ) ) python_version = responses.DebugInfoItem( key = 'Python version', value = '.'.join( str( item ) for item in environment.version_info ) ) jedi_version = responses.DebugInfoItem( key = 'Jedi version', value = jedi.__version__ ) parso_version = responses.DebugInfoItem( key = 'Parso version', value = parso.__version__ ) return responses.BuildDebugInfoResponse( name = 'Python', items = [ python_interpreter, python_root, python_path, python_version, jedi_version, parso_version ] ) def _RefactoringToFixIt( refactoring ): """Converts a Jedi Refactoring instance to a single responses.FixIt.""" # FIXME: refactorings can rename files (apparently). ycmd API doesn't have any # interface for that, so we just ignore them. changes = refactoring.get_changed_files() chunks = [] # We sort the files to ensure the tests are stable for filename in sorted( changes.keys() ): changed_file = changes[ filename ] # NOTE: This is an internal API. We _could_ use GetFileContents( filename ) # here, but using Jedi's representation means that it is always consistent # with get_new_code() old_text = changed_file._module_node.get_code() new_text = changed_file.get_new_code() # Cache the offsets of all the newlines in the file. These are used to # calculate the line/column values from the offsets retuned by the diff # scanner newlines = [ i for i, c in enumerate( old_text ) if c == '\n' ] newlines.append( len( old_text ) ) sequence_matcher = difflib.SequenceMatcher( a = old_text, b = new_text, autojunk = False ) for ( operation, old_start, old_end, new_start, new_end ) in sequence_matcher.get_opcodes(): # Tag of equal means the range is identical, so nothing to do. if operation == 'equal': continue # operation can be 'insert', 'replace' or 'delete', the offsets actually # already cover that in our FixIt API (a delete has an empty new_text, an # insert has an empty range), so we just encode the line/column offset and # the replacement text extracted from new_text chunks.append( responses.FixItChunk( new_text[ new_start : new_end ], # FIXME: new_end must be equal to or after new_start, so we should make # OffsetToPosition take 2 offsets and return them rather than repeating # work responses.Range( _OffsetToPosition( old_start, filename, old_text, newlines ), _OffsetToPosition( old_end, filename, old_text, newlines ) ) ) ) return responses.FixIt( responses.Location( 1, 1, 'none' ), chunks, '', kind = responses.FixIt.Kind.REFACTOR ) def _OffsetToPosition( offset, filename, text, newlines ): """Convert the 0-based codepoint offset |offset| to a position (line/col) in |text|. |filename| is the full path of the file containing |text| and |newlines| is a cache of the 0-based character offsets of all the \n characters in |text| (plus one extra). Returns responses.Position.""" for index, newline in enumerate( newlines ): if newline >= offset: start_of_line = newlines[ index - 1 ] + 1 if index > 0 else 0 column = offset - start_of_line line_value = text[ start_of_line : newline ] return responses.Location( index + 1, CodepointOffsetToByteOffset( line_value, column + 1 ), filename ) # Invalid position - it's outside of the text. Just return the last # position in the text. This is an internal error. LOGGER.error( "Invalid offset %s in file %s with text %s and newlines %s", offset, filename, text, newlines ) raise RuntimeError( "Invalid file offset in diff" ) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/rust/�������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0021245�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/rust/__init__.py��������������������������������������0000664�0000000�0000000�00000000000�13746324601�0023344�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/rust/hook.py������������������������������������������0000664�0000000�0000000�00000001716�13746324601�0022564�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2015-2020 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 <http://www.gnu.org/licenses/>. from ycmd.completers.rust.rust_completer import ( ShouldEnableRustCompleter, RustCompleter ) def GetCompleter( user_options ): if not ShouldEnableRustCompleter( user_options ): return None return RustCompleter( user_options ) ��������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/rust/rust_completer.py��������������������������������0000664�0000000�0000000�00000016140�13746324601�0024670�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2015-2020 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 <http://www.gnu.org/licenses/>. import logging import os from subprocess import PIPE from ycmd import responses, utils from ycmd.completers.language_server import language_server_completer from ycmd.utils import LOGGER, re LOGFILE_FORMAT = 'ra_' RUST_ROOT = os.path.abspath( os.path.join( os.path.dirname( __file__ ), '..', '..', '..', 'third_party', 'rust-analyzer' ) ) RA_BIN_DIR = os.path.join( RUST_ROOT, 'bin' ) RUSTC_EXECUTABLE = utils.FindExecutable( os.path.join( RA_BIN_DIR, 'rustc' ) ) RA_EXECUTABLE = utils.FindExecutable( os.path.join( RA_BIN_DIR, 'rust-analyzer' ) ) RA_VERSION_REGEX = re.compile( r'^rust-analyzer (?P<version>.*)$' ) def _GetCommandOutput( command ): return utils.ToUnicode( utils.SafePopen( command, stdin_windows = PIPE, stdout = PIPE, stderr = PIPE ).communicate()[ 0 ].rstrip() ) def _GetRAVersion( ra_path ): ra_version = _GetCommandOutput( [ ra_path, '--version' ] ) match = RA_VERSION_REGEX.match( ra_version ) if not match: LOGGER.error( 'Cannot parse Rust Language Server version: %s', ra_version ) return None return match.group( 'version' ) def ShouldEnableRustCompleter( user_options ): if ( 'rls_binary_path' in user_options and not user_options[ 'rust_toolchain_root' ] ): LOGGER.warning( 'rls_binary_path detected. ' 'Did you mean rust_toolchain_root?' ) if user_options[ 'rust_toolchain_root' ]: # Listen to what the user wanted to use ra = os.path.join( user_options[ 'rust_toolchain_root' ], 'bin', 'rust-analyzer' ) if not utils.FindExecutable( ra ): LOGGER.error( 'Not using Rust completer: no rust-analyzer ' 'executable found at %s', ra ) return False else: return True else: return bool( utils.FindExecutable( RA_EXECUTABLE ) ) class RustCompleter( language_server_completer.LanguageServerCompleter ): def __init__( self, user_options ): super().__init__( user_options ) if user_options[ 'rust_toolchain_root' ]: self._rust_root = user_options[ 'rust_toolchain_root' ] else: self._rust_root = os.path.dirname( os.path.dirname( RA_EXECUTABLE ) ) self._ra_path = utils.FindExecutable( os.path.join( self._rust_root, 'bin', 'rust-analyzer' ) ) def _Reset( self ): self._server_progress = 'Not started' super()._Reset() def GetServerName( self ): return 'Rust Language Server' def GetCommandLine( self ): return [ self._ra_path ] def GetServerEnvironment( self ): env = os.environ.copy() old_path = env[ 'PATH' ] ra_bin_dir = os.path.join( self._rust_root, 'bin' ) env[ 'PATH' ] = ra_bin_dir + os.pathsep + old_path if LOGGER.isEnabledFor( logging.DEBUG ): env[ 'RA_LOG' ] = 'rust_analyzer=trace' return env def GetProjectRootFiles( self ): # Without LSP workspaces support, RA relies on the rootUri to detect a # project. # TODO: add support for LSP workspaces to allow users to change project # without having to restart RA. return [ 'Cargo.toml' ] def ServerIsReady( self ): return ( super().ServerIsReady() and self._server_progress in [ 'invalid', 'ready' ] ) def SupportedFiletypes( self ): return [ 'rust' ] def GetTriggerCharacters( self, server_trigger_characters ): # The trigger characters supplied by RA ('.' and ':') are worse than ycmd's # own semantic triggers ('.' and '::') so we ignore them. return [] def ExtraDebugItems( self, request_data ): return [ responses.DebugInfoItem( 'Project State', self._server_progress ), responses.DebugInfoItem( 'Version', _GetRAVersion( self._ra_path ) ), responses.DebugInfoItem( 'Rust Root', self._rust_root ) ] def HandleNotificationInPollThread( self, notification ): if notification[ 'method' ] == 'rust-analyzer/status': if self._server_progress not in [ 'invalid', 'ready' ]: self._server_progress = notification[ 'params' ][ 'status' ] if notification[ 'method' ] == 'window/showMessage': if ( notification[ 'params' ][ 'message' ] == 'rust-analyzer failed to discover workspace' ): self._server_progress = 'invalid' super().HandleNotificationInPollThread( notification ) def ConvertNotificationToMessage( self, request_data, notification ): if notification[ 'method' ] == 'rust-analyzer/status': message = notification[ 'params' ] if message != 'invalid': # RA produces a better message for `invalid` return responses.BuildDisplayMessageResponse( f'Initializing Rust completer: { message }' ) return super().ConvertNotificationToMessage( request_data, notification ) def GetType( self, request_data ): try: hover_response = self.GetHoverResponse( request_data )[ 'value' ] except language_server_completer.NoHoverInfoException: raise RuntimeError( 'Unknown type.' ) # rust-analyzer's hover format looks like this: # # ```rust # namespace # ``` # # ```rust # type info # ``` # # --- # docstring # # To extract the type info, we take everything up to `---` line, # then find the last occurence of "```" as the end index and "```rust" # as the start index and return the slice. hover_response = hover_response.split( '\n---\n', 2 )[ 0 ] start = hover_response.rfind( '```rust\n' ) + len( '```rust\n' ) end = hover_response.rfind( '\n```' ) return responses.BuildDisplayMessageResponse( hover_response[ start:end ] ) def GetDoc( self, request_data ): try: hover_response = self.GetHoverResponse( request_data ) except language_server_completer.NoHoverInfoException: raise RuntimeError( 'No documentation available.' ) # Strips all empty lines and lines starting with "```" to make the hover # response look like plain text. For the format, see the comment in GetType. lines = hover_response[ 'value' ].split( '\n' ) documentation = '\n'.join( line for line in lines if line and not line.startswith( '```' ) ).strip() return responses.BuildDetailedInfoResponse( documentation ) def ExtraCapabilities( self ): return { 'experimental': { 'statusNotification': True }, 'workspace': { 'configuration': True } } def WorkspaceConfigurationResponse( self, request ): assert len( request[ 'params' ][ 'items' ] ) == 1 return [ self._settings.get( 'ls', {} ).get( 'rust-analyzer' ) ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/typescript/�������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0022456�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/typescript/__init__.py��������������������������������0000664�0000000�0000000�00000000000�13746324601�0024555�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/typescript/hook.py������������������������������������0000664�0000000�0000000�00000001700�13746324601�0023766�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from ycmd.completers.typescript.typescript_completer import ( ShouldEnableTypeScriptCompleter, TypeScriptCompleter ) def GetCompleter( user_options ): if not ShouldEnableTypeScriptCompleter( user_options ): return None return TypeScriptCompleter( user_options ) ����������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/typescript/typescript_completer.py��������������������0000664�0000000�0000000�00000114320�13746324601�0027311�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2015-2020 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 <http://www.gnu.org/licenses/>. import json import logging import os import subprocess import itertools import threading from collections import defaultdict from functools import partial from tempfile import NamedTemporaryFile from ycmd import extra_conf_store from ycmd import responses from ycmd import utils from ycmd.completers.completer import Completer from ycmd.completers.completer_utils import GetFileLines from ycmd.utils import LOGGER, re SERVER_NOT_RUNNING_MESSAGE = 'TSServer is not running.' NO_DIAGNOSTIC_MESSAGE = 'No diagnostic for current line!' RESPONSE_TIMEOUT_SECONDS = 20 TSSERVER_DIR = os.path.abspath( os.path.join( os.path.dirname( __file__ ), '..', '..', '..', 'third_party', 'tsserver' ) ) LOGFILE_FORMAT = 'tsserver_' class DeferredResponse: """ A deferred that resolves to a response from TSServer. """ def __init__( self, timeout = RESPONSE_TIMEOUT_SECONDS ): self._event = threading.Event() self._message = None self._timeout = timeout def resolve( self, message ): self._message = message self._event.set() def result( self ): self._event.wait( timeout = self._timeout ) if not self._event.isSet(): raise RuntimeError( 'Response Timeout' ) message = self._message if not message[ 'success' ]: raise RuntimeError( message[ 'message' ] ) if 'body' in message: return self._message[ 'body' ] def FindTSServer( user_options_path ): tsserver = utils.FindExecutableWithFallback( user_options_path , None ) if tsserver and os.path.isfile( tsserver ): return tsserver # The TSServer executable is installed at the root directory on Windows while # it's installed in the bin folder on other platforms. for executable in [ os.path.join( TSSERVER_DIR, 'node_modules', 'typescript', 'bin', 'tsserver' ), 'tsserver' ]: tsserver = utils.FindExecutable( executable ) if tsserver: return tsserver return None def ShouldEnableTypeScriptCompleter( user_options ): tsserver = FindTSServer( user_options[ 'tsserver_binary_path' ] ) if not tsserver: LOGGER.error( 'Not using TypeScript completer: TSServer not installed ' 'in %s', TSSERVER_DIR ) return False LOGGER.info( 'Using TypeScript completer with %s', tsserver ) return True def IsLineInTsDiagnosticRange( line, ts_diagnostic ): ts_start_line = ts_diagnostic[ 'startLocation' ][ 'line' ] ts_end_line = ts_diagnostic[ 'endLocation' ][ 'line' ] return ts_start_line <= line and ts_end_line >= line def GetByteOffsetDistanceFromTsDiagnosticRange( byte_offset, line_value, ts_diagnostic ): ts_start_offset = ts_diagnostic[ 'startLocation' ][ 'offset' ] ts_end_offset = ts_diagnostic[ 'endLocation' ][ 'offset' ] codepoint_offset = utils.ByteOffsetToCodepointOffset( line_value, byte_offset ) start_difference = codepoint_offset - ts_start_offset end_difference = codepoint_offset - ( ts_end_offset - 1 ) if start_difference >= 0 and end_difference <= 0: return 0 return min( abs( start_difference ), abs( end_difference ) ) class TypeScriptCompleter( Completer ): """ Completer for TypeScript. It uses TSServer which is bundled with TypeScript 1.5 See the protocol here: https://github.com/microsoft/TypeScript/blob/master/src/server/protocol.ts """ def __init__( self, user_options ): super().__init__( user_options ) self._logfile = None self._tsserver_lock = threading.Lock() self._tsserver_handle = None self._tsserver_version = None self._tsserver_executable = FindTSServer( user_options[ 'tsserver_binary_path' ] ) # Used to read response only if TSServer is running. self._tsserver_is_running = threading.Event() # Used to prevent threads from concurrently writing to # the tsserver process's stdin self._write_lock = threading.Lock() # Each request sent to tsserver must have a sequence id. # Responses contain the id sent in the corresponding request. self._sequenceid = itertools.count() # Used to prevent threads from concurrently accessing the sequence counter self._sequenceid_lock = threading.Lock() # Start a thread to read response from TSServer. utils.StartThread( self._ReaderLoop ) # Used to map sequence id's to their corresponding DeferredResponse # objects. The reader loop uses this to hand out responses. self._pending = {} # Used to prevent threads from concurrently reading and writing to # the pending response dictionary self._pending_lock = threading.Lock() self._StartServer() self._latest_diagnostics_for_file_lock = threading.Lock() self._latest_diagnostics_for_file = defaultdict( list ) # There's something in the API that lists the trigger characters, but # there is no way to request that from the server, so we just hard-code # the signature triggers. self.SetSignatureHelpTriggers( [ '(', ',', '<' ] ) LOGGER.info( 'Enabling TypeScript completion' ) def _SetServerVersion( self ): version = self._SendRequest( 'status' )[ 'version' ] with self._tsserver_lock: self._tsserver_version = version def _StartServer( self ): with self._tsserver_lock: self._StartServerNoLock() def _StartServerNoLock( self ): if self._ServerIsRunning(): return self._logfile = utils.CreateLogfile( LOGFILE_FORMAT ) tsserver_log = f'-file { self._logfile } -level {_LogLevel()}' # TSServer gets the configuration for the log file through the # environment variable 'TSS_LOG'. This seems to be undocumented but # looking at the source code it seems like this is the way: # https://github.com/Microsoft/TypeScript/blob/8a93b489454fdcbdf544edef05f73a913449be1d/src/server/server.ts#L136 environ = os.environ.copy() environ[ 'TSS_LOG' ] = tsserver_log LOGGER.info( 'TSServer log file: %s', self._logfile ) # We need to redirect the error stream to the output one on Windows. self._tsserver_handle = utils.SafePopen( self._tsserver_executable, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.STDOUT, env = environ ) LOGGER.info( "TSServer started with PID %s", self._tsserver_handle.pid ) self._tsserver_is_running.set() utils.StartThread( self._SetServerVersion ) def _ReaderLoop( self ): """ Read responses from TSServer and use them to resolve the DeferredResponse instances. """ while True: self._tsserver_is_running.wait() try: message = self._ReadMessage() except ( RuntimeError, ValueError ): LOGGER.exception( 'Error while reading message from server' ) if not self._ServerIsRunning(): self._tsserver_is_running.clear() continue # We ignore events for now since we don't have a use for them. msgtype = message[ 'type' ] if msgtype == 'event': eventname = message[ 'event' ] LOGGER.info( 'Received %s event from TSServer', eventname ) continue if msgtype != 'response': LOGGER.error( 'Unsupported message type', msgtype ) continue seq = message[ 'request_seq' ] with self._pending_lock: if seq in self._pending: self._pending[ seq ].resolve( message ) del self._pending[ seq ] def _ReadMessage( self ): """Read a response message from TSServer.""" # The headers are pretty similar to HTTP. # At the time of writing, 'Content-Length' is the only supplied header. headers = {} while True: headerline = self._tsserver_handle.stdout.readline().strip() if not headerline: break key, value = utils.ToUnicode( headerline ).split( ':', 1 ) headers[ key.strip() ] = value.strip() # The response message is a JSON object which comes back on one line. # Since this might change in the future, we use the 'Content-Length' # header. if 'Content-Length' not in headers: raise RuntimeError( "Missing 'Content-Length' header" ) content_length = int( headers[ 'Content-Length' ] ) # TSServer adds a newline at the end of the response message and counts it # as one character (\n) towards the content length. However, newlines are # two characters on Windows (\r\n), so we need to take care of that. See # issue https://github.com/Microsoft/TypeScript/issues/3403 content = self._tsserver_handle.stdout.read( content_length ) if utils.OnWindows() and content.endswith( b'\r' ): content += self._tsserver_handle.stdout.read( 1 ) return json.loads( utils.ToUnicode( content ) ) def _BuildRequest( self, command, arguments = None ): """Build TSServer request object.""" with self._sequenceid_lock: seq = next( self._sequenceid ) request = { 'seq': seq, 'type': 'request', 'command': command } if arguments: request[ 'arguments' ] = arguments return request def _WriteRequest( self, request ): """Write a request to TSServer stdin.""" serialized_request = utils.ToBytes( json.dumps( request, separators = ( ',', ':' ) ) + '\n' ) with self._write_lock: try: self._tsserver_handle.stdin.write( serialized_request ) self._tsserver_handle.stdin.flush() # IOError is an alias of OSError in Python 3. except ( AttributeError, IOError ): LOGGER.exception( SERVER_NOT_RUNNING_MESSAGE ) raise RuntimeError( SERVER_NOT_RUNNING_MESSAGE ) def _SendCommand( self, command, arguments = None ): """ Send a request message to TSServer but don't wait for the response. This function is to be used when we don't care about the response to the message that is sent. """ request = self._BuildRequest( command, arguments ) self._WriteRequest( request ) def _SendRequest( self, command, arguments = None ): """ Send a request message to TSServer and wait for the response. """ request = self._BuildRequest( command, arguments ) deferred = DeferredResponse() with self._pending_lock: seq = request[ 'seq' ] self._pending[ seq ] = deferred self._WriteRequest( request ) return deferred.result() def _Reload( self, request_data ): """ Synchronize TSServer's view of the file to the contents of the unsaved buffer. """ filename = request_data[ 'filepath' ] contents = request_data[ 'file_data' ][ filename ][ 'contents' ] tmpfile = NamedTemporaryFile( delete = False ) tmpfile.write( utils.ToBytes( contents ) ) tmpfile.close() self._SendRequest( 'reload', { 'file': filename, 'tmpfile': tmpfile.name } ) utils.RemoveIfExists( tmpfile.name ) def _ServerIsRunning( self ): return utils.ProcessIsRunning( self._tsserver_handle ) def Language( self ): return 'typescript' def ServerIsHealthy( self ): return self._ServerIsRunning() def SupportedFiletypes( self ): return [ 'javascript', 'typescript', 'typescriptreact', 'javascriptreact' ] def SignatureHelpAvailable( self ): return responses.SignatureHelpAvailalability.AVAILABLE def ComputeCandidatesInner( self, request_data ): self._Reload( request_data ) entries = self._SendRequest( 'completionInfo', { 'file': request_data[ 'filepath' ], 'line': request_data[ 'line_num' ], 'offset': request_data[ 'start_codepoint' ], 'includeExternalModuleExports': True } )[ 'entries' ] # Ignore entries with the "warning" kind. They are identifiers from the # current file that TSServer returns sometimes in JavaScript. return [ responses.BuildCompletionData( insertion_text = entry[ 'name' ], # We store the entries returned by TSServer in the extra_data field to # detail the candidates once the filtering is done. extra_data = entry ) for entry in entries if entry[ 'kind' ] != 'warning' ] def DetailCandidates( self, request_data, candidates ): undetailed_entries = [] map_entries_to_candidates = {} for candidate in candidates: undetailed_entry = candidate[ 'extra_data' ] if 'name' not in undetailed_entry: # This candidate is already detailed. continue map_entries_to_candidates[ undetailed_entry[ 'name' ] ] = candidate undetailed_entries.append( undetailed_entry ) if not undetailed_entries: # All candidates are already detailed. return candidates detailed_entries = self._SendRequest( 'completionEntryDetails', { 'file': request_data[ 'filepath' ], 'line': request_data[ 'line_num' ], 'offset': request_data[ 'start_codepoint' ], 'entryNames': undetailed_entries } ) for entry in detailed_entries: candidate = map_entries_to_candidates[ entry[ 'name' ] ] extra_menu_info, detailed_info = _BuildCompletionExtraMenuAndDetailedInfo( request_data, entry ) if extra_menu_info: candidate[ 'extra_menu_info' ] = extra_menu_info if detailed_info: candidate[ 'detailed_info' ] = detailed_info candidate[ 'kind' ] = entry[ 'kind' ] candidate[ 'extra_data' ] = _BuildCompletionFixIts( request_data, entry ) return candidates def GetSubcommandsMap( self ): return { 'RestartServer' : ( lambda self, request_data, args: self._RestartServer( request_data ) ), 'StopServer' : ( lambda self, request_data, args: self._StopServer() ), 'GoTo' : ( lambda self, request_data, args: self._GoToDefinition( request_data ) ), 'GoToDefinition' : ( lambda self, request_data, args: self._GoToDefinition( request_data ) ), 'GoToDeclaration' : ( lambda self, request_data, args: self._GoToDefinition( request_data ) ), 'GoToImplementation': ( lambda self, request_data, args: self._GoToImplementation( request_data ) ), 'GoToReferences' : ( lambda self, request_data, args: self._GoToReferences( request_data ) ), 'GoToType' : ( lambda self, request_data, args: self._GoToType( request_data ) ), 'GoToSymbol' : ( lambda self, request_data, args: self._GoToSymbol( request_data, args ) ), 'GetType' : ( lambda self, request_data, args: self._GetType( request_data ) ), 'GetDoc' : ( lambda self, request_data, args: self._GetDoc( request_data ) ), 'FixIt' : ( lambda self, request_data, args: self._FixIt( request_data, args ) ), 'OrganizeImports' : ( lambda self, request_data, args: self._OrganizeImports( request_data ) ), 'RefactorRename' : ( lambda self, request_data, args: self._RefactorRename( request_data, args ) ), 'Format' : ( lambda self, request_data, args: self._Format( request_data ) ), } def OnBufferVisit( self, request_data ): filename = request_data[ 'filepath' ] self._SendCommand( 'open', { 'file': filename } ) def OnBufferUnload( self, request_data ): filename = request_data[ 'filepath' ] self._SendCommand( 'close', { 'file': filename } ) def OnFileReadyToParse( self, request_data ): # Only load the extra conf. We don't need it for anything but Format. extra_conf_store.ModuleFileForSourceFile( request_data[ 'filepath' ] ) self._Reload( request_data ) diagnostics = self.GetDiagnosticsForCurrentFile( request_data ) filepath = request_data[ 'filepath' ] with self._latest_diagnostics_for_file_lock: self._latest_diagnostics_for_file[ filepath ] = diagnostics return responses.BuildDiagnosticResponse( diagnostics, filepath, self.max_diagnostics_to_display ) def GetTsDiagnosticsForCurrentFile( self, request_data ): # This returns the data the TypeScript server responded with. # Note that its "offset" values represent codepoint offsets, # not byte offsets, which are required by the ycmd API. filepath = request_data[ 'filepath' ] ts_diagnostics = list( itertools.chain( self._GetSemanticDiagnostics( filepath ), self._GetSyntacticDiagnostics( filepath ) ) ) return ts_diagnostics def _TsDiagnosticToYcmdDiagnostic( self, request_data, ts_diagnostic ): filepath = request_data[ 'filepath' ] ts_fixes = self._SendRequest( 'getCodeFixes', { 'file': filepath, 'startLine': ts_diagnostic[ 'startLocation' ][ 'line' ], 'startOffset': ts_diagnostic[ 'startLocation' ][ 'offset' ], 'endLine': ts_diagnostic[ 'endLocation' ][ 'line' ], 'endOffset': ts_diagnostic[ 'endLocation' ][ 'offset' ], 'errorCodes': [ ts_diagnostic[ 'code' ] ] } ) location = responses.Location( request_data[ 'line_num' ], request_data[ 'column_num' ], filepath ) fixits = [] for fix in ts_fixes: description = fix[ 'description' ] # TSServer returns these fixits for every error in JavaScript files. # Ignore them since they are not useful. if description in [ 'Ignore this error message', 'Disable checking for this file' ]: continue fixit = responses.FixIt( location, _BuildFixItForChanges( request_data, fix[ 'changes' ] ), description ) fixits.append( fixit ) contents = GetFileLines( request_data, filepath ) ts_start_location = ts_diagnostic[ 'startLocation' ] ts_start_line = ts_start_location[ 'line' ] start_offset = utils.CodepointOffsetToByteOffset( contents[ ts_start_line - 1 ], ts_start_location[ 'offset' ] ) ts_end_location = ts_diagnostic[ 'endLocation' ] ts_end_line = ts_end_location[ 'line' ] end_offset = utils.CodepointOffsetToByteOffset( contents[ ts_end_line - 1 ], ts_end_location[ 'offset' ] ) location_start = responses.Location( ts_start_line, start_offset, filepath ) location_end = responses.Location( ts_end_line, end_offset, filepath ) location_extent = responses.Range( location_start, location_end ) return responses.Diagnostic( [ location_extent ], location_start, location_extent, ts_diagnostic[ 'message' ], 'ERROR', fixits = fixits ) def GetDiagnosticsForCurrentFile( self, request_data ): ts_diagnostics = self.GetTsDiagnosticsForCurrentFile( request_data ) return [ self._TsDiagnosticToYcmdDiagnostic( request_data, x ) for x in ts_diagnostics ] def GetDetailedDiagnostic( self, request_data ): ts_diagnostics = self.GetTsDiagnosticsForCurrentFile( request_data ) ts_diagnostics_on_line = list( filter( partial( IsLineInTsDiagnosticRange, request_data[ 'line_num' ] ), ts_diagnostics ) ) if not ts_diagnostics_on_line: raise ValueError( NO_DIAGNOSTIC_MESSAGE ) closest_ts_diagnostic = None distance_to_closest_ts_diagnostic = None line_value = request_data[ 'line_value' ] current_byte_offset = request_data[ 'column_num' ] for ts_diagnostic in ts_diagnostics_on_line: distance = GetByteOffsetDistanceFromTsDiagnosticRange( current_byte_offset, line_value, ts_diagnostic ) if ( not closest_ts_diagnostic or distance < distance_to_closest_ts_diagnostic ): distance_to_closest_ts_diagnostic = distance closest_ts_diagnostic = ts_diagnostic closest_diagnostic = self._TsDiagnosticToYcmdDiagnostic( request_data, closest_ts_diagnostic ) return responses.BuildDisplayMessageResponse( closest_diagnostic.text_ ) def ComputeSignaturesInner( self, request_data ): self._Reload( request_data ) try: items = self._SendRequest( 'signatureHelp', { 'file': request_data[ 'filepath' ], 'line': request_data[ 'line_num' ], 'offset': request_data[ 'start_codepoint' ], # triggerReason - opitonal and tricky to populate } ) except RuntimeError: # We get an exception when there are no results, so squash it if LOGGER.isEnabledFor( logging.DEBUG ): LOGGER.exception( "No signatures from tsserver" ) return {} def MakeSignature( s ): label = _DisplayPartsToString( s[ 'prefixDisplayParts' ] ) parameters = [] sep = _DisplayPartsToString( s[ 'separatorDisplayParts' ] ) for index, p in enumerate( s[ 'parameters' ] ): param = _DisplayPartsToString( p[ 'displayParts' ] ) start = len( label ) end = start + len( param ) label += param if index < len( s[ 'parameters' ] ) - 1: label += sep parameters.append( { 'label': [ utils.CodepointOffsetToByteOffset( label, start ), utils.CodepointOffsetToByteOffset( label, end ) ] } ) label += _DisplayPartsToString( s[ 'suffixDisplayParts' ] ) return { 'label': label, 'parameters': parameters } return { 'activeSignature': items[ 'selectedItemIndex' ], 'activeParameter': items[ 'argumentIndex' ], 'signatures': [ MakeSignature( s ) for s in items[ 'items' ] ] } def _GetSemanticDiagnostics( self, filename ): return self._SendRequest( 'semanticDiagnosticsSync', { 'file': filename, 'includeLinePosition': True } ) def _GetSyntacticDiagnostics( self, filename ): return self._SendRequest( 'syntacticDiagnosticsSync', { 'file': filename, 'includeLinePosition': True } ) def _GoToDefinition( self, request_data ): self._Reload( request_data ) filespans = self._SendRequest( 'definition', { 'file': request_data[ 'filepath' ], 'line': request_data[ 'line_num' ], 'offset': request_data[ 'column_codepoint' ] } ) if not filespans: raise RuntimeError( 'Could not find definition.' ) span = filespans[ 0 ] return responses.BuildGoToResponseFromLocation( _BuildLocation( GetFileLines( request_data, span[ 'file' ] ), span[ 'file' ], span[ 'start' ][ 'line' ], span[ 'start' ][ 'offset' ] ) ) def _GoToImplementation( self, request_data ): self._Reload( request_data ) try: filespans = self._SendRequest( 'implementation', { 'file': request_data[ 'filepath' ], 'line': request_data[ 'line_num' ], 'offset': request_data[ 'column_codepoint' ] } ) except RuntimeError: raise RuntimeError( 'No implementation found.' ) results = [] for span in filespans: filename = span[ 'file' ] start = span[ 'start' ] lines = GetFileLines( request_data, span[ 'file' ] ) line_num = start[ 'line' ] results.append( responses.BuildGoToResponseFromLocation( _BuildLocation( lines, filename, line_num, start[ 'offset' ] ), lines[ line_num - 1 ] ) ) return results def _GoToReferences( self, request_data ): self._Reload( request_data ) response = self._SendRequest( 'references', { 'file': request_data[ 'filepath' ], 'line': request_data[ 'line_num' ], 'offset': request_data[ 'column_codepoint' ] } ) return [ responses.BuildGoToResponseFromLocation( _BuildLocation( GetFileLines( request_data, ref[ 'file' ] ), ref[ 'file' ], ref[ 'start' ][ 'line' ], ref[ 'start' ][ 'offset' ] ), ref[ 'lineText' ] ) for ref in response[ 'refs' ] ] def _GoToType( self, request_data ): self._Reload( request_data ) try: filespans = self._SendRequest( 'typeDefinition', { 'file': request_data[ 'filepath' ], 'line': request_data[ 'line_num' ], 'offset': request_data[ 'column_num' ] } ) except RuntimeError: raise RuntimeError( 'Could not find type definition.' ) if not filespans: raise RuntimeError( 'Could not find type definition.' ) span = filespans[ 0 ] return responses.BuildGoToResponse( filepath = span[ 'file' ], line_num = span[ 'start' ][ 'line' ], column_num = span[ 'start' ][ 'offset' ] ) def _GoToSymbol( self, request_data, args ): if len( args ) < 1: raise RuntimeError( 'Must specify something to search for' ) query = args[ 0 ] self._Reload( request_data ) filespans = self._SendRequest( 'navto', { 'searchValue': query, 'file': request_data[ 'filepath' ] } ) if not filespans: raise RuntimeError( 'Symbol not found' ) results = [ responses.BuildGoToResponseFromLocation( _BuildLocation( GetFileLines( request_data, fs[ 'file' ] ), fs[ 'file' ], fs[ 'start' ][ 'line' ], fs[ 'start' ][ 'offset' ] ), fs[ 'name' ] ) for fs in filespans ] if len( results ) == 1: return results[ 0 ] return results def _GetType( self, request_data ): self._Reload( request_data ) info = self._SendRequest( 'quickinfo', { 'file': request_data[ 'filepath' ], 'line': request_data[ 'line_num' ], 'offset': request_data[ 'column_codepoint' ] } ) return responses.BuildDisplayMessageResponse( info[ 'displayString' ] ) def _GetDoc( self, request_data ): self._Reload( request_data ) info = self._SendRequest( 'quickinfo', { 'file': request_data[ 'filepath' ], 'line': request_data[ 'line_num' ], 'offset': request_data[ 'column_codepoint' ] } ) message = f'{ info[ "displayString" ] }\n\n{info[ "documentation" ]}' return responses.BuildDetailedInfoResponse( message ) def _FixIt( self, request_data, args ): self._Reload( request_data ) filepath = request_data[ 'filepath' ] line_num = request_data[ 'line_num' ] fixits = [] with self._latest_diagnostics_for_file_lock: for diagnostic in self._latest_diagnostics_for_file[ filepath ]: if diagnostic.location_.line_number_ != line_num: continue fixits.extend( diagnostic.fixits_ ) return responses.BuildFixItResponse( fixits ) def _OrganizeImports( self, request_data ): self._Reload( request_data ) filepath = request_data[ 'filepath' ] changes = self._SendRequest( 'organizeImports', { 'scope': { 'type': 'file', 'args': { 'file': filepath } } } ) location = responses.Location( request_data[ 'line_num' ], request_data[ 'column_num' ], filepath ) return responses.BuildFixItResponse( [ responses.FixIt( location, _BuildFixItForChanges( request_data, changes ) ) ] ) def _RefactorRename( self, request_data, args ): if len( args ) != 1: raise ValueError( 'Please specify a new name to rename it to.\n' 'Usage: RefactorRename <new name>' ) self._Reload( request_data ) response = self._SendRequest( 'rename', { 'file': request_data[ 'filepath' ], 'line': request_data[ 'line_num' ], 'offset': request_data[ 'column_codepoint' ], 'findInComments': False, 'findInStrings': False, } ) if not response[ 'info' ][ 'canRename' ]: raise RuntimeError( 'Value cannot be renamed: ' f'{ response[ "info" ][ "localizedErrorMessage" ] }' ) # The format of the response is: # # body { # info { # ... # triggerSpan: { # length: original_length # } # } # # locs [ { # file: file_path # locs: [ # start: { # line: line_num # offset: offset # } # end { # line: line_num # offset: offset # } # ] } # ] # } # new_name = args[ 0 ] location = responses.Location( request_data[ 'line_num' ], request_data[ 'column_num' ], request_data[ 'filepath' ] ) chunks = [] for file_replacement in response[ 'locs' ]: chunks.extend( _BuildFixItChunksForFile( request_data, new_name, file_replacement ) ) return responses.BuildFixItResponse( [ responses.FixIt( location, chunks ) ] ) def _Format( self, request_data ): filepath = request_data[ 'filepath' ] self._Reload( request_data ) # TODO: support all formatting options. See # https://github.com/Microsoft/TypeScript/blob/72e92a055823f1ade97d03d7526dbab8be405dde/lib/protocol.d.ts#L2060-L2077 # for the list of options. While not standard, a way to support these # options, which is already adopted by a number of clients, would be to read # the "formatOptions" field in the tsconfig.json file. options = dict( request_data[ 'options' ] ) options[ 'tabSize' ] = options.pop( 'tab_size' ) options[ 'indentSize' ] = options[ 'tabSize' ] options[ 'convertTabsToSpaces' ] = options.pop( 'insert_spaces' ) options.update( self.AdditionalFormattingOptions( request_data ) ) self._SendRequest( 'configure', { 'file': filepath, 'formatOptions': options } ) response = self._SendRequest( 'format', _BuildTsFormatRange( request_data ) ) contents = GetFileLines( request_data, filepath ) chunks = [ _BuildFixItChunkForRange( text_edit[ 'newText' ], contents, filepath, text_edit ) for text_edit in response ] location = responses.Location( request_data[ 'line_num' ], request_data[ 'column_num' ], filepath ) return responses.BuildFixItResponse( [ responses.FixIt( location, chunks ) ] ) def _RestartServer( self, request_data ): with self._tsserver_lock: self._StopServerNoLock() self._StartServerNoLock() # This is needed because after we restart the TSServer it would lose all # the information about the files we were working on. This means that the # newly started TSServer will know nothing about the buffer we're working # on after restarting the server. So if we restart the server and right # after that ask for completion in the buffer, the server will timeout. # So we notify the server that we're working on the current buffer. self.OnBufferVisit( request_data ) def _StopServer( self ): with self._tsserver_lock: self._StopServerNoLock() def _StopServerNoLock( self ): if self._ServerIsRunning(): LOGGER.info( 'Stopping TSServer with PID %s', self._tsserver_handle.pid ) try: self._SendCommand( 'exit' ) utils.WaitUntilProcessIsTerminated( self._tsserver_handle, timeout = 5 ) LOGGER.info( 'TSServer stopped' ) except Exception: LOGGER.exception( 'Error while stopping TSServer' ) self._CleanUp() def _CleanUp( self ): utils.CloseStandardStreams( self._tsserver_handle ) self._tsserver_handle = None self._latest_diagnostics_for_file = defaultdict( list ) if not self.user_options[ 'server_keep_logfiles' ] and self._logfile: utils.RemoveIfExists( self._logfile ) self._logfile = None def Shutdown( self ): self._StopServer() def DebugInfo( self, request_data ): with self._tsserver_lock: item_version = responses.DebugInfoItem( 'version', self._tsserver_version ) tsserver = responses.DebugInfoServer( name = 'TSServer', handle = self._tsserver_handle, executable = self._tsserver_executable, logfiles = [ self._logfile ], extras = [ item_version ] ) return responses.BuildDebugInfoResponse( name = 'TypeScript', servers = [ tsserver ] ) def _LogLevel(): return 'verbose' if LOGGER.isEnabledFor( logging.DEBUG ) else 'normal' def _BuildCompletionExtraMenuAndDetailedInfo( request_data, entry ): signature = _DisplayPartsToString( entry[ 'displayParts' ] ) if entry[ 'name' ] == signature: extra_menu_info = None detailed_info = [] else: # Strip new lines and indentation from the signature to display it on one # line. extra_menu_info = re.sub( '\\s+', ' ', signature ) detailed_info = [ signature ] docs = entry.get( 'documentation', [] ) detailed_info += [ doc[ 'text' ].strip() for doc in docs if doc ] detailed_info = '\n\n'.join( detailed_info ) return extra_menu_info, detailed_info def _BuildCompletionFixIts( request_data, entry ): if 'codeActions' in entry: location = responses.Location( request_data[ 'line_num' ], request_data[ 'column_num' ], request_data[ 'filepath' ] ) return responses.BuildFixItResponse( [ responses.FixIt( location, _BuildFixItForChanges( request_data, action[ 'changes' ] ), action[ 'description' ] ) for action in entry[ 'codeActions' ] ] ) return {} def _BuildFixItChunkForRange( new_name, file_contents, file_name, source_range ): """Returns list FixItChunk for a tsserver source range.""" return responses.FixItChunk( new_name, responses.Range( start = _BuildLocation( file_contents, file_name, source_range[ 'start' ][ 'line' ], source_range[ 'start' ][ 'offset' ] ), end = _BuildLocation( file_contents, file_name, source_range[ 'end' ][ 'line' ], source_range[ 'end' ][ 'offset' ] ) ) ) def _BuildFixItChunksForFile( request_data, new_name, file_replacement ): """Returns a list of FixItChunk for each replacement range for the supplied file.""" # On Windows, TSServer annoyingly returns file path as C:/blah/blah, # whereas all other paths in Python are of the C:\\blah\\blah form. We use # normpath to have python do the conversion for us. file_path = os.path.normpath( file_replacement[ 'file' ] ) file_contents = GetFileLines( request_data, file_path ) return [ _BuildFixItChunkForRange( new_name, file_contents, file_path, r ) for r in file_replacement[ 'locs' ] ] def _BuildFixItForChanges( request_data, changes ): """Returns a list of FixItChunk given a list of TSServer changes.""" chunks = [] for change in changes: # On Windows, TSServer annoyingly returns file path as C:/blah/blah, # whereas all other paths in Python are of the C:\\blah\\blah form. We use # normpath to have python do the conversion for us. file_path = os.path.normpath( change[ 'fileName' ] ) file_contents = GetFileLines( request_data, file_path ) for text_change in change[ 'textChanges' ]: chunks.append( _BuildFixItChunkForRange( text_change[ 'newText' ], file_contents, file_path, text_change ) ) return chunks def _BuildLocation( file_contents, filename, line, offset ): return responses.Location( line = line, # TSServer returns codepoint offsets, but we need byte offsets, so we must # convert. column = utils.CodepointOffsetToByteOffset( file_contents[ line - 1 ], offset ), filename = filename ) def _BuildTsFormatRange( request_data ): filepath = request_data[ 'filepath' ] lines = GetFileLines( request_data, filepath ) if 'range' not in request_data: return { 'file': filepath, 'line': 1, 'offset': 1, 'endLine': len( lines ), 'endOffset': len( lines[ - 1 ] ) + 1 } start = request_data[ 'range' ][ 'start' ] start_line_num = start[ 'line_num' ] start_line_value = lines[ start_line_num - 1 ] start_codepoint = utils.ByteOffsetToCodepointOffset( start_line_value, start[ 'column_num' ] ) end = request_data[ 'range' ][ 'end' ] end_line_num = end[ 'line_num' ] end_line_value = lines[ end_line_num - 1 ] end_codepoint = utils.ByteOffsetToCodepointOffset( end_line_value, end[ 'column_num' ] ) return { 'file': filepath, 'line': start_line_num, 'offset': start_codepoint, 'endLine': end_line_num, 'endOffset': end_codepoint } def _DisplayPartsToString( parts ): return ''.join( [ p[ 'text' ] for p in parts ] ) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/typescriptreact/��������������������������������������0000775�0000000�0000000�00000000000�13746324601�0023475�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/completers/typescriptreact/hook.py�������������������������������0000664�0000000�0000000�00000001700�13746324601�0025005�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from ycmd.completers.typescript.typescript_completer import ( ShouldEnableTypeScriptCompleter, TypeScriptCompleter ) def GetCompleter( user_options ): if not ShouldEnableTypeScriptCompleter( user_options ): return None return TypeScriptCompleter( user_options ) ����������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/default_settings.json��������������������������������������������0000664�0000000�0000000�00000002357�13746324601�0022341�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "filepath_completion_use_working_dir": 0, "auto_trigger": 1, "min_num_of_chars_for_completion": 2, "min_num_identifier_candidate_chars": 0, "semantic_triggers": {}, "filetype_specific_completion_to_disable": { "gitcommit": 1 }, "collect_identifiers_from_comments_and_strings": 0, "max_num_identifier_candidates": 10, "max_num_candidates": 50, "max_num_candidates_to_detail": -1, "extra_conf_globlist": [], "global_ycm_extra_conf": "", "confirm_extra_conf": 1, "max_diagnostics_to_display": 30, "filepath_blacklist": { "html": 1, "jsx": 1, "xml": 1 }, "auto_start_csharp_server": 1, "auto_stop_csharp_server": 1, "use_ultisnips_completer": 1, "csharp_server_port": 0, "hmac_secret": "", "server_keep_logfiles": 0, "python_binary_path": "", "language_server": [], "java_jdtls_use_clean_workspace": 1, "java_jdtls_workspace_root_path": "", "java_jdtls_extension_path": [], "use_clangd": 1, "clangd_binary_path": "", "clangd_args": [], "clangd_uses_ycmd_caching": 1, "disable_signature_help": 0, "gopls_binary_path": "", "gopls_args": [], "rust_toolchain_root": "", "tsserver_binary_path": "", "roslyn_binary_path": "", "mono_binary_path": "", "java_binary_path": "" } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/extra_conf_store.py����������������������������������������������0000664�0000000�0000000�00000017641�13746324601�0022022�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2011-2020 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 <http://www.gnu.org/licenses/>. # NOTE: This module is used as a Singleton import os import random import string import sys from threading import Lock from ycmd import user_options_store from ycmd.responses import UnknownExtraConf, YCM_EXTRA_CONF_FILENAME from ycmd.utils import ( ExpandVariablesInPath, LoadPythonSource, LOGGER, PathsToAllParentFolders ) from fnmatch import fnmatch # Singleton variables _module_for_module_file = {} _module_for_module_file_lock = Lock() _module_file_for_source_file = {} _module_file_for_source_file_lock = Lock() def Get(): return _module_for_module_file, _module_file_for_source_file def Set( state ): global _module_for_module_file, _module_file_for_source_file _module_for_module_file, _module_file_for_source_file = state def Reset(): global _module_for_module_file, _module_file_for_source_file _module_for_module_file = {} _module_file_for_source_file = {} def ModuleForSourceFile( filename ): return Load( ModuleFileForSourceFile( filename ) ) def ModuleFileForSourceFile( filename ): """This will try all files returned by _ExtraConfModuleSourceFilesForFile in order and return the filename of the first module that was allowed to load. If no module was found or allowed to load, None is returned.""" with _module_file_for_source_file_lock: if filename not in _module_file_for_source_file: for module_file in _ExtraConfModuleSourceFilesForFile( filename ): if Load( module_file ): _module_file_for_source_file[ filename ] = module_file break return _module_file_for_source_file.setdefault( filename ) def CallGlobalExtraConfYcmCorePreloadIfExists(): _CallGlobalExtraConfMethod( 'YcmCorePreload' ) def Shutdown(): # VimClose is for the sake of backwards compatibility; it's a no-op when it # doesn't exist. _CallGlobalExtraConfMethod( 'VimClose' ) _CallGlobalExtraConfMethod( 'Shutdown' ) def _CallGlobalExtraConfMethod( function_name ): global_ycm_extra_conf = _GlobalYcmExtraConfFileLocation() if not ( global_ycm_extra_conf and os.path.exists( global_ycm_extra_conf ) ): LOGGER.debug( 'No global extra conf, not calling method %s', function_name ) return try: module = Load( global_ycm_extra_conf, force = True ) except Exception: LOGGER.exception( 'Error occurred while loading global extra conf %s', global_ycm_extra_conf ) return if not module or not hasattr( module, function_name ): LOGGER.debug( 'Global extra conf not loaded or no function %s', function_name ) return try: LOGGER.info( 'Calling global extra conf method %s on conf file %s', function_name, global_ycm_extra_conf ) getattr( module, function_name )() except Exception: LOGGER.exception( 'Error occurred while calling global extra conf method %s ' 'on conf file %s', function_name, global_ycm_extra_conf ) def Disable( module_file ): """Disables the loading of a module for the current session.""" with _module_for_module_file_lock: _module_for_module_file[ module_file ] = None def _ShouldLoad( module_file, is_global ): """Checks if a module is safe to be loaded. By default this will try to decide using a white-/blacklist and ask the user for confirmation as a fallback.""" if is_global or not user_options_store.Value( 'confirm_extra_conf' ): return True globlist = user_options_store.Value( 'extra_conf_globlist' ) for glob in globlist: is_blacklisted = glob[ 0 ] == '!' if _MatchesGlobPattern( module_file, glob.lstrip( '!' ) ): return not is_blacklisted raise UnknownExtraConf( module_file ) def Load( module_file, force = False ): """Load and return the module contained in a file. Using force = True the module will be loaded regardless of the criteria in _ShouldLoad. This will return None if the module was not allowed to be loaded.""" if not module_file: return None with _module_for_module_file_lock: if module_file in _module_for_module_file: return _module_for_module_file[ module_file ] is_global = module_file == _GlobalYcmExtraConfFileLocation() if not force and not _ShouldLoad( module_file, is_global ): Disable( module_file ) return None # This has to be here because a long time ago, the ycm_extra_conf.py files # used to import clang_helpers.py from the cpp folder. This is not needed # anymore, but there are a lot of old ycm_extra_conf.py files that we don't # want to break. sys.path.insert( 0, _PathToCppCompleterFolder() ) # By default, the Python interpreter compiles source files into bytecode to # load them faster next time they are run. These *.pyc files are generated # along the source files prior to Python 3.2 or in a __pycache__ folder for # newer versions. We disable the generation of these files when loading # ycm_extra_conf.py files as users do not want them inside their projects. # The drawback is negligible since ycm_extra_conf.py files are generally small # files thus really fast to compile and only loaded once by editing session. old_dont_write_bytecode = sys.dont_write_bytecode sys.dont_write_bytecode = True try: module = LoadPythonSource( _RandomName(), module_file ) module.is_global_ycm_extra_conf = is_global finally: sys.dont_write_bytecode = old_dont_write_bytecode del sys.path[ 0 ] with _module_for_module_file_lock: _module_for_module_file[ module_file ] = module return module def _MatchesGlobPattern( filename, glob ): """Returns true if a filename matches a given pattern. Environment variables and a '~' in glob will be expanded and checking will be performed using absolute paths with symlinks resolved (except on Windows). See the documentation of fnmatch for the supported patterns.""" # NOTE: os.path.realpath does not resolve symlinks on Windows. # See https://bugs.python.org/issue9949 realpath = os.path.realpath( filename ) return fnmatch( realpath, os.path.realpath( ExpandVariablesInPath( glob ) ) ) def _ExtraConfModuleSourceFilesForFile( filename ): """For a given filename, search all parent folders for YCM_EXTRA_CONF_FILENAME files that will compute the flags necessary to compile the file. If _GlobalYcmExtraConfFileLocation() exists it is returned as a fallback.""" for folder in PathsToAllParentFolders( filename ): candidate = os.path.join( folder, YCM_EXTRA_CONF_FILENAME ) if os.path.exists( candidate ): yield candidate global_ycm_extra_conf = _GlobalYcmExtraConfFileLocation() if ( global_ycm_extra_conf and os.path.exists( global_ycm_extra_conf ) ): yield global_ycm_extra_conf def _PathToCppCompleterFolder(): """Returns the path to the 'cpp' completer folder. This is necessary because ycm_extra_conf files need it on the path.""" return os.path.join( _DirectoryOfThisScript(), 'completers', 'cpp' ) def _DirectoryOfThisScript(): return os.path.dirname( os.path.abspath( __file__ ) ) def _RandomName(): """Generates a random module name.""" return ''.join( random.choice( string.ascii_lowercase ) for x in range( 15 ) ) def _GlobalYcmExtraConfFileLocation(): return ExpandVariablesInPath( user_options_store.Value( 'global_ycm_extra_conf' ) ) def IsGlobalExtraConfModule( module ): return module.is_global_ycm_extra_conf �����������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/handlers.py������������������������������������������������������0000664�0000000�0000000�00000031545�13746324601�0020255�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2013-2020 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 <http://www.gnu.org/licenses/>. import bottle import json import platform import sys import time import traceback from bottle import request from ycmd import extra_conf_store, hmac_plugin, server_state, user_options_store from ycmd.responses import ( BuildExceptionResponse, BuildCompletionResponse, BuildResolveCompletionResponse, BuildSignatureHelpResponse, BuildSignatureHelpAvailableResponse, SignatureHelpAvailalability, UnknownExtraConf ) from ycmd.request_wrap import RequestWrap from ycmd.completers.completer_utils import FilterAndSortCandidatesWrap from ycmd.utils import LOGGER, StartThread, ImportCore ycm_core = ImportCore() # num bytes for the request body buffer; request.json only works if the request # size is less than this bottle.Request.MEMFILE_MAX = 10 * 1024 * 1024 _server_state = None _hmac_secret = bytes() app = bottle.Bottle() wsgi_server = None @app.post( '/event_notification' ) def EventNotification(): LOGGER.info( 'Received event notification' ) request_data = RequestWrap( request.json ) event_name = request_data[ 'event_name' ] LOGGER.debug( 'Event name: %s', event_name ) event_handler = 'On' + event_name getattr( _server_state.GetGeneralCompleter(), event_handler )( request_data ) filetypes = request_data[ 'filetypes' ] response_data = None if _server_state.FiletypeCompletionUsable( filetypes ): response_data = getattr( _server_state.GetFiletypeCompleter( filetypes ), event_handler )( request_data ) if response_data: return _JsonResponse( response_data ) return _JsonResponse( {} ) @app.get( '/signature_help_available' ) def GetSignatureHelpAvailable(): LOGGER.info( 'Received signature help available request' ) if request.query.subserver: filetype = request.query.subserver try: completer = _server_state.GetFiletypeCompleter( [ filetype ] ) except ValueError: return _JsonResponse( BuildSignatureHelpAvailableResponse( SignatureHelpAvailalability.NOT_AVAILABLE ) ) value = completer.SignatureHelpAvailable() return _JsonResponse( BuildSignatureHelpAvailableResponse( value ) ) else: raise RuntimeError( 'Subserver not specified' ) @app.post( '/run_completer_command' ) def RunCompleterCommand(): LOGGER.info( 'Received command request' ) request_data = RequestWrap( request.json ) completer = _GetCompleterForRequestData( request_data ) return _JsonResponse( completer.OnUserCommand( request_data[ 'command_arguments' ], request_data ) ) @app.post( '/resolve_fixit' ) def ResolveFixit(): LOGGER.info( 'Received resolve_fixit request' ) request_data = RequestWrap( request.json ) completer = _GetCompleterForRequestData( request_data ) return _JsonResponse( completer.ResolveFixit( request_data ) ) @app.post( '/completions' ) def GetCompletions(): LOGGER.info( 'Received completion request' ) request_data = RequestWrap( request.json ) do_filetype_completion = _server_state.ShouldUseFiletypeCompleter( request_data ) LOGGER.debug( 'Using filetype completion: %s', do_filetype_completion ) errors = None completions = None if do_filetype_completion: try: filetype_completer = _server_state.GetFiletypeCompleter( request_data[ 'filetypes' ] ) completions = filetype_completer.ComputeCandidates( request_data ) except Exception as exception: if request_data[ 'force_semantic' ]: # user explicitly asked for semantic completion, so just pass the error # back raise # store the error to be returned with results from the identifier # completer LOGGER.exception( 'Exception from semantic completer (using general)' ) stack = traceback.format_exc() errors = [ BuildExceptionResponse( exception, stack ) ] if not completions and not request_data[ 'force_semantic' ]: completions = _server_state.GetGeneralCompleter().ComputeCandidates( request_data ) return _JsonResponse( BuildCompletionResponse( completions if completions else [], request_data[ 'start_column' ], errors = errors ) ) @app.post( '/resolve_completion' ) def ResolveCompletionItem(): LOGGER.info( "Received resolve request" ) request_data = RequestWrap( request.json ) completer = _GetCompleterForRequestData( request_data ) errors = None completion = None try: completion = completer.ResolveCompletionItem( request_data ) except Exception as e: errors = [ BuildExceptionResponse( e, traceback.format_exc() ) ] return _JsonResponse( BuildResolveCompletionResponse( completion, errors ) ) @app.post( '/signature_help' ) def GetSignatureHelp(): LOGGER.info( 'Received signature help request' ) request_data = RequestWrap( request.json ) if not _server_state.FiletypeCompletionUsable( request_data[ 'filetypes' ], silent = True ): return _JsonResponse( BuildSignatureHelpResponse( None ) ) errors = None signature_info = None try: filetype_completer = _server_state.GetFiletypeCompleter( request_data[ 'filetypes' ] ) signature_info = filetype_completer.ComputeSignatures( request_data ) except Exception as exception: LOGGER.exception( 'Exception from semantic completer during sig help' ) errors = [ BuildExceptionResponse( exception, traceback.format_exc() ) ] # No fallback for signature help. The general completer is unlikely to be able # to offer anything of for that here. return _JsonResponse( BuildSignatureHelpResponse( signature_info, errors = errors ) ) @app.post( '/filter_and_sort_candidates' ) def FilterAndSortCandidates(): LOGGER.info( 'Received filter & sort request' ) # Not using RequestWrap because no need and the requests coming in aren't like # the usual requests we handle. request_data = request.json return _JsonResponse( FilterAndSortCandidatesWrap( request_data[ 'candidates' ], request_data[ 'sort_property' ], request_data[ 'query' ], _server_state.user_options[ 'max_num_candidates' ] ) ) @app.get( '/healthy' ) def GetHealthy(): LOGGER.info( 'Received health request' ) if request.query.subserver: filetype = request.query.subserver completer = _server_state.GetFiletypeCompleter( [ filetype ] ) return _JsonResponse( completer.ServerIsHealthy() ) return _JsonResponse( True ) @app.get( '/ready' ) def GetReady(): LOGGER.info( 'Received ready request' ) if request.query.subserver: filetype = request.query.subserver completer = _server_state.GetFiletypeCompleter( [ filetype ] ) return _JsonResponse( completer.ServerIsReady() ) return _JsonResponse( True ) @app.post( '/semantic_completion_available' ) def FiletypeCompletionAvailable(): LOGGER.info( 'Received filetype completion available request' ) return _JsonResponse( _server_state.FiletypeCompletionAvailable( RequestWrap( request.json )[ 'filetypes' ] ) ) @app.post( '/defined_subcommands' ) def DefinedSubcommands(): LOGGER.info( 'Received defined subcommands request' ) completer = _GetCompleterForRequestData( RequestWrap( request.json ) ) return _JsonResponse( completer.DefinedSubcommands() ) @app.post( '/detailed_diagnostic' ) def GetDetailedDiagnostic(): LOGGER.info( 'Received detailed diagnostic request' ) request_data = RequestWrap( request.json ) completer = _GetCompleterForRequestData( request_data ) return _JsonResponse( completer.GetDetailedDiagnostic( request_data ) ) @app.post( '/load_extra_conf_file' ) def LoadExtraConfFile(): LOGGER.info( 'Received extra conf load request' ) request_data = RequestWrap( request.json, validate = False ) extra_conf_store.Load( request_data[ 'filepath' ], force = True ) return _JsonResponse( True ) @app.post( '/ignore_extra_conf_file' ) def IgnoreExtraConfFile(): LOGGER.info( 'Received extra conf ignore request' ) request_data = RequestWrap( request.json, validate = False ) extra_conf_store.Disable( request_data[ 'filepath' ] ) return _JsonResponse( True ) @app.post( '/debug_info' ) def DebugInfo(): LOGGER.info( 'Received debug info request' ) request_data = RequestWrap( request.json ) has_clang_support = ycm_core.HasClangSupport() clang_version = ycm_core.ClangVersion() if has_clang_support else None filepath = request_data[ 'filepath' ] try: extra_conf_path = extra_conf_store.ModuleFileForSourceFile( filepath ) is_loaded = bool( extra_conf_path ) except UnknownExtraConf as error: extra_conf_path = error.extra_conf_file is_loaded = False response = { 'python': { 'executable': sys.executable, 'version': platform.python_version() }, 'clang': { 'has_support': has_clang_support, 'version': clang_version }, 'extra_conf': { 'path': extra_conf_path, 'is_loaded': is_loaded }, 'completer': None } try: response[ 'completer' ] = _GetCompleterForRequestData( request_data ).DebugInfo( request_data ) except Exception: LOGGER.exception( 'Error retrieving completer debug info' ) return _JsonResponse( response ) @app.post( '/shutdown' ) def Shutdown(): LOGGER.info( 'Received shutdown request' ) ServerShutdown() return _JsonResponse( True ) @app.post( '/receive_messages' ) def ReceiveMessages(): # Receive messages is a "long-poll" handler. # The client makes the request with a long timeout (1 hour). # When we have data to send, we send it and close the socket. # The client then sends a new request. request_data = RequestWrap( request.json ) try: completer = _GetCompleterForRequestData( request_data ) except Exception: # No semantic completer for this filetype, don't requery. This is not an # error. return _JsonResponse( False ) return _JsonResponse( completer.PollForMessages( request_data ) ) # The type of the param is Bottle.HTTPError def ErrorHandler( httperror ): body = _JsonResponse( BuildExceptionResponse( httperror.exception, httperror.traceback ) ) hmac_plugin.SetHmacHeader( body, _hmac_secret ) return body # For every error Bottle encounters it will use this as the default handler app.default_error_handler = ErrorHandler def _JsonResponse( data ): bottle.response.set_header( 'Content-Type', 'application/json' ) return json.dumps( data, separators = ( ',', ':' ), default = _UniversalSerialize ) def _UniversalSerialize( obj ): try: serialized = obj.__dict__.copy() serialized[ 'TYPE' ] = type( obj ).__name__ return serialized except AttributeError: return str( obj ) def _GetCompleterForRequestData( request_data ): completer_target = request_data.get( 'completer_target', None ) if completer_target == 'identifier': return _server_state.GetGeneralCompleter().GetIdentifierCompleter() elif completer_target == 'filetype_default' or not completer_target: return _server_state.GetFiletypeCompleter( request_data[ 'filetypes' ] ) else: return _server_state.GetFiletypeCompleter( [ completer_target ] ) def ServerShutdown(): def Terminator(): if wsgi_server: wsgi_server.Shutdown() # Use a separate thread to let the server send the response before shutting # down. StartThread( Terminator ) def ServerCleanup(): if _server_state: _server_state.Shutdown() extra_conf_store.Shutdown() def SetHmacSecret( hmac_secret ): global _hmac_secret _hmac_secret = hmac_secret def UpdateUserOptions( options ): global _server_state if not options: return # This should never be passed in, but let's try to remove it just in case. options.pop( 'hmac_secret', None ) user_options_store.SetAll( options ) _server_state = server_state.ServerState( options ) def KeepSubserversAlive( check_interval_seconds ): def Keepalive( check_interval_seconds ): while True: time.sleep( check_interval_seconds ) LOGGER.debug( 'Keeping subservers alive' ) loaded_completers = _server_state.GetLoadedFiletypeCompleters() for completer in loaded_completers: completer.ServerIsHealthy() StartThread( Keepalive, check_interval_seconds ) �����������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/hmac_plugin.py���������������������������������������������������0000664�0000000�0000000�00000006056�13746324601�0020742�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2014-2020 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 <http://www.gnu.org/licenses/>. import requests from base64 import b64decode, b64encode from bottle import request, abort, response from hmac import compare_digest from urllib.parse import urlparse from ycmd import hmac_utils from ycmd.utils import LOGGER, ToBytes, ToUnicode _HMAC_HEADER = 'x-ycm-hmac' _HOST_HEADER = 'host' # This class implements the Bottle plugin API: # http://bottlepy.org/docs/dev/plugindev.html # # We want to ensure that every request coming in has a valid HMAC set in the # x-ycm-hmac header and that every response coming out sets such a valid header. # This is to prevent security issues with possible remote code execution. # The x-ycm-hmac value is encoded as base64 during transport instead of sent raw # because https://tools.ietf.org/html/rfc5987 says header values must be in the # ISO-8859-1 character set. class HmacPlugin: name = 'hmac' api = 2 def __init__( self, hmac_secret ): self._hmac_secret = hmac_secret def __call__( self, callback ): def wrapper( *args, **kwargs ): if not HostHeaderCorrect( request ): LOGGER.info( 'Dropping request with bad Host header' ) abort( requests.codes.unauthorized, 'Unauthorized, received bad Host header.' ) return body = ToBytes( request.body.read() ) if not RequestAuthenticated( request.method, request.path, body, self._hmac_secret ): LOGGER.info( 'Dropping request with bad HMAC' ) abort( requests.codes.unauthorized, 'Unauthorized, received bad HMAC.' ) return body = callback( *args, **kwargs ) SetHmacHeader( body, self._hmac_secret ) return body return wrapper def HostHeaderCorrect( request ): host = urlparse( 'http://' + request.headers[ _HOST_HEADER ] ).hostname return host == '127.0.0.1' or host == 'localhost' def RequestAuthenticated( method, path, body, hmac_secret ): if _HMAC_HEADER not in request.headers: return False return compare_digest( hmac_utils.CreateRequestHmac( ToBytes( method ), ToBytes( path ), ToBytes( body ), ToBytes( hmac_secret ) ), ToBytes( b64decode( request.headers[ _HMAC_HEADER ] ) ) ) def SetHmacHeader( body, hmac_secret ): value = b64encode( hmac_utils.CreateHmac( ToBytes( body ), ToBytes( hmac_secret ) ) ) response.set_header( _HMAC_HEADER, ToUnicode( value ) ) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/hmac_utils.py����������������������������������������������������0000664�0000000�0000000�00000003611�13746324601�0020576�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from builtins import bytes import hmac import hashlib def CreateHmac( content, hmac_secret ): if not isinstance( content, bytes ): raise TypeError( 'content was not of bytes type; you have a bug!' ) if not isinstance( hmac_secret, bytes ): raise TypeError( 'hmac_secret was not of bytes type; you have a bug!' ) return bytes( hmac.new( hmac_secret, msg = content, digestmod = hashlib.sha256 ).digest() ) def CreateRequestHmac( method, path, body, hmac_secret ): if not isinstance( body, bytes ): raise TypeError( 'body was not of bytes type; you have a bug!' ) if not isinstance( hmac_secret, bytes ): raise TypeError( 'hmac_secret was not of bytes type; you have a bug!' ) if not isinstance( method, bytes ): raise TypeError( 'method was not of bytes type; you have a bug!' ) if not isinstance( path, bytes ): raise TypeError( 'path was not of bytes type; you have a bug!' ) method_hmac = CreateHmac( method, hmac_secret ) path_hmac = CreateHmac( path, hmac_secret ) body_hmac = CreateHmac( body, hmac_secret ) joined_hmac_input = bytes().join( ( method_hmac, path_hmac, body_hmac ) ) return CreateHmac( joined_hmac_input, hmac_secret ) �����������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/identifier_utils.py����������������������������������������������0000664�0000000�0000000�00000020505�13746324601�0022011�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2014-2020 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 <http://www.gnu.org/licenses/>. from ycmd.utils import re, SplitLines C_STYLE_COMMENT = '/\\*(?:\n|.)*?\\*/' CPP_STYLE_COMMENT = '//.*?$' PYTHON_STYLE_COMMENT = '#.*?$' # Anything inside single quotes, '...', but mind: # 1. that the starting single quote is not escaped # 2. the escaped slash (\\) # 3. the escaped single quote inside the string SINGLE_QUOTE_STRING = r"(?<!\\)'(?:\\\\|\\'|.)*?'" # Anything inside double quotes, "...", but mind: # 1. that the starting double quote is not escaped # 2. the escaped slash (\\) # 3. the escaped double quote inside the string DOUBLE_QUOTE_STRING = r'(?<!\\)"(?:\\\\|\\"|.)*?"' # Anything inside back quotes, `...`, but mind: # 1. that the starting back quote is not escaped # 2. the escaped slash (\\) # 3. the escaped back quote inside the string BACK_QUOTE_STRING = r'(?<!\\)`(?:\\\\|\\`|.)*?`' # Python-style multiline single-quote string MULTILINE_SINGLE_QUOTE_STRING = "'''(?:\n|.)*?'''" # Python-style multiline double-quote string MULTILINE_DOUBLE_QUOTE_STRING = '"""(?:\n|.)*?"""' DEFAULT_COMMENT_AND_STRING_REGEX = re.compile( "|".join( [ C_STYLE_COMMENT, CPP_STYLE_COMMENT, PYTHON_STYLE_COMMENT, MULTILINE_SINGLE_QUOTE_STRING, MULTILINE_DOUBLE_QUOTE_STRING, SINGLE_QUOTE_STRING, DOUBLE_QUOTE_STRING ] ), re.MULTILINE ) FILETYPE_TO_COMMENT_AND_STRING_REGEX = { # Spec: # http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3690.pdf 'cpp': re.compile( "|".join( [ C_STYLE_COMMENT, CPP_STYLE_COMMENT, SINGLE_QUOTE_STRING, DOUBLE_QUOTE_STRING ] ), re.MULTILINE ), # Spec: # https://golang.org/ref/spec#Comments # https://golang.org/ref/spec#String_literals # https://golang.org/ref/spec#Rune_literals 'go': re.compile( "|".join( [ C_STYLE_COMMENT, CPP_STYLE_COMMENT, SINGLE_QUOTE_STRING, DOUBLE_QUOTE_STRING, BACK_QUOTE_STRING ] ), re.MULTILINE ), # Spec: # https://docs.python.org/3.6/reference/lexical_analysis.html#comments # https://docs.python.org/3.6/reference/lexical_analysis.html#literals 'python': re.compile( "|".join( [ PYTHON_STYLE_COMMENT, MULTILINE_SINGLE_QUOTE_STRING, MULTILINE_DOUBLE_QUOTE_STRING, SINGLE_QUOTE_STRING, DOUBLE_QUOTE_STRING ] ), re.MULTILINE ), # Spec: # https://doc.rust-lang.org/reference.html#comments # https://doc.rust-lang.org/reference.html#character-and-string-literals 'rust': re.compile( "|".join( [ CPP_STYLE_COMMENT, SINGLE_QUOTE_STRING, DOUBLE_QUOTE_STRING ] ), re.MULTILINE ) } for filetype in [ 'c', 'cuda', 'objc', 'objcpp', 'javascript', 'typescript' ]: FILETYPE_TO_COMMENT_AND_STRING_REGEX[ filetype ] = ( FILETYPE_TO_COMMENT_AND_STRING_REGEX[ 'cpp' ] ) # At least c++ and javascript support unicode identifiers, and identifiers may # start with unicode character, e.g. ålpha. So we need to accept any identifier # starting with an 'alpha' character or underscore. i.e. not starting with a # 'digit'. The following regex will match: # - A character which is alpha or _. That is a character which is NOT: # - a digit (\d) # - non-alphanumeric # - not an underscore # (The latter two come from \W which is the negation of \w) # - Followed by any alphanumeric or _ characters DEFAULT_IDENTIFIER_REGEX = re.compile( r"[^\W\d]\w*", re.UNICODE ) FILETYPE_TO_IDENTIFIER_REGEX = { # Spec: # http://www.ecma-international.org/ecma-262/6.0/#sec-names-and-keywords # Default identifier plus the dollar sign. 'javascript': re.compile( r"(?:[^\W\d]|\$)[\w$]*", re.UNICODE ), # Spec: https://www.w3.org/TR/css-syntax-3/#ident-token-diagram 'css': re.compile( r"-?[^\W\d][\w-]*", re.UNICODE ), # Spec: http://www.w3.org/TR/html5/syntax.html#tag-name-state # But not quite since not everything we want to pull out is a tag name. We # also want attribute names (and probably unquoted attribute values). # And we also want to ignore common template chars like `}` and `{`. 'html': re.compile( r"[a-zA-Z][^\s/>='\"}{\.]*", re.UNICODE ), # Spec: http://cran.r-project.org/doc/manuals/r-release/R-lang.pdf # Section 10.3.2. # Can be any sequence of '.', '_' and alphanum BUT can't start with: # - '.' followed by digit # - digit # - '_' 'r': re.compile( r"(?!(?:\.\d|\d|_))[\.\w]+", re.UNICODE ), # Spec: http://clojure.org/reader # Section: Symbols 'clojure': re.compile( r"[-\*\+!_\?:\.a-zA-Z][-\*\+!_\?:\.\w]*/?[-\*\+!_\?:\.\w]*", re.UNICODE ), # Spec: http://www.haskell.org/onlinereport/lexemes.html # Section 2.4 'haskell': re.compile( r"[_a-zA-Z][\w']+", re.UNICODE ), # Spec: ? # Colons are often used in labels (e.g. \label{fig:foobar}) so we accept # them in the middle of an identifier but not at its extremities. We also # accept dashes for compound words. 'tex': re.compile( r"[^\W\d](?:[\w:-]*\w)?", re.UNICODE ), # Spec: http://doc.perl6.org/language/syntax 'perl6': re.compile( r"[_a-zA-Z](?:\w|[-'](?=[_a-zA-Z]))*", re.UNICODE ), # https://www.scheme.com/tspl4/grammar.html#grammar:symbols 'scheme': re.compile( r"\+|\-|\.\.\.|" r"(?:->|(:?\\x[0-9A-Fa-f]+;|[!$%&*/:<=>?~^]|[^\W\d]))" r"(?:\\x[0-9A-Fa-f]+;|[-+.@!$%&*/:<=>?~^\w])*", re.UNICODE ), } FILETYPE_TO_IDENTIFIER_REGEX[ 'typescript' ] = ( FILETYPE_TO_IDENTIFIER_REGEX[ 'javascript' ] ) FILETYPE_TO_IDENTIFIER_REGEX[ 'scss' ] = FILETYPE_TO_IDENTIFIER_REGEX[ 'css' ] FILETYPE_TO_IDENTIFIER_REGEX[ 'sass' ] = FILETYPE_TO_IDENTIFIER_REGEX[ 'css' ] FILETYPE_TO_IDENTIFIER_REGEX[ 'less' ] = FILETYPE_TO_IDENTIFIER_REGEX[ 'css' ] FILETYPE_TO_IDENTIFIER_REGEX[ 'elisp' ] = ( FILETYPE_TO_IDENTIFIER_REGEX[ 'clojure' ] ) FILETYPE_TO_IDENTIFIER_REGEX[ 'lisp' ] = ( FILETYPE_TO_IDENTIFIER_REGEX[ 'clojure' ] ) def CommentAndStringRegexForFiletype( filetype ): return FILETYPE_TO_COMMENT_AND_STRING_REGEX.get( filetype, DEFAULT_COMMENT_AND_STRING_REGEX ) def IdentifierRegexForFiletype( filetype ): return FILETYPE_TO_IDENTIFIER_REGEX.get( filetype, DEFAULT_IDENTIFIER_REGEX ) def ReplaceWithEmptyLines( regex_match ): return '\n' * ( len( SplitLines( regex_match.group( 0 ) ) ) - 1 ) def RemoveIdentifierFreeText( text, filetype = None ): return CommentAndStringRegexForFiletype( filetype ).sub( ReplaceWithEmptyLines, text ) def ExtractIdentifiersFromText( text, filetype = None ): return re.findall( IdentifierRegexForFiletype( filetype ), text ) def IsIdentifier( text, filetype = None ): if not text: return False regex = IdentifierRegexForFiletype( filetype ) match = regex.match( text ) return match and match.end() == len( text ) # index is 0-based and EXCLUSIVE, so ("foo.", 3) -> 0 # Works with both unicode and str objects. # Returns the index on bad input. def StartOfLongestIdentifierEndingAtIndex( text, index, filetype = None ): if not text or index < 1 or index > len( text ): return index for i in range( index ): if IsIdentifier( text[ i : index ], filetype ): return i return index # If the index is not on a valid identifier, it searches forward until a valid # identifier is found. Returns the identifier. def IdentifierAtIndex( text, index, filetype = None ): if index > len( text ): return '' for match in IdentifierRegexForFiletype( filetype ).finditer( text ): if match.end() > index: return match.group() return '' �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/request_validation.py��������������������������������������������0000664�0000000�0000000�00000004445�13746324601�0022356�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from ycmd.responses import ServerError # Throws an exception if request doesn't have all the required fields. # TODO: Accept a request_type param so that we can also verify missing # command_arguments and completer_target fields if necessary. def EnsureRequestValid( request_json ): required_fields = { 'line_num', 'column_num', 'filepath', 'file_data' } missing = { x for x in required_fields if x not in request_json } if 'filepath' not in missing and 'file_data' not in missing: missing.update( _MissingFieldsForFileData( request_json ) ) if not missing: return True message = '\n'.join( _FieldMissingMessage( field ) for field in missing ) raise ServerError( message ) def _FieldMissingMessage( field ): return f'Request missing required field: { field }' def _FilepathInFileDataSpec( request_json ): filepath = request_json[ 'filepath' ] return f'file_data[ "{ filepath }" ]' def _SingleFileDataFieldSpec( request_json, field ): return f'{ _FilepathInFileDataSpec( request_json ) }[ "{ field }" ]' def _MissingFieldsForFileData( request_json ): missing = set() data_for_file = request_json[ 'file_data' ].get( request_json[ 'filepath' ] ) if data_for_file: required_data = [ 'filetypes', 'contents' ] for required in required_data: if required not in data_for_file: missing.add( _SingleFileDataFieldSpec( request_json, required ) ) filetypes = data_for_file.get( 'filetypes', [] ) if not filetypes: missing.add( f'{ _SingleFileDataFieldSpec( request_json, "filetypes" ) }[ 0 ]' ) else: missing.add( _FilepathInFileDataSpec( request_json ) ) return missing ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/request_wrap.py��������������������������������������������������0000664�0000000�0000000�00000024170�13746324601�0021172�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2014-2020 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 <http://www.gnu.org/licenses/>. from ycmd.utils import ( ByteOffsetToCodepointOffset, CodepointOffsetToByteOffset, HashableDict, LOGGER, ToUnicode, ToBytes, SplitLines ) from ycmd.identifier_utils import StartOfLongestIdentifierEndingAtIndex from ycmd.request_validation import EnsureRequestValid # TODO: Change the custom computed (and other) keys to be actual properties on # the object. class RequestWrap: def __init__( self, request, validate = True ): if validate: EnsureRequestValid( request ) self._request = request # Maps the keys returned by this objects __getitem__ to a # tuple of # ( getter_method, setter_method ). Values computed by getter_method (or set # by setter_method) are cached in _cached_computed. setter_method may be # None for read-only items. self._computed_key = { # Unicode string representation of the current line. If the line requested # is not in the file, returns ''. 'line_value': ( self._CurrentLine, None ), # The calculated start column, as a codepoint offset into the # unicode string line_value 'start_codepoint': ( self._GetCompletionStartCodepoint, self._SetCompletionStartCodepoint ), # The 'column_num' as a unicode codepoint offset 'column_codepoint': ( lambda: ByteOffsetToCodepointOffset( self[ 'line_bytes' ], self[ 'column_num' ] ), None ), # Bytes string representation of the current line 'line_bytes': ( lambda: ToBytes( self[ 'line_value' ] ), None ), # The calculated start column, as a byte offset into the UTF-8 encoded # bytes returned by line_bytes 'start_column': ( self._GetCompletionStartColumn, self._SetCompletionStartColumn ), # Note: column_num is the byte offset into the UTF-8 encoded bytes # returned by line_bytes # unicode string representation of the 'query' after the beginning # of the identifier to be completed 'query': ( self._Query, None ), # Unicode string representation of the line value up to the character # before the start of 'query' 'prefix': ( self._Prefix, None ), 'filetypes': ( self._Filetypes, None ), 'first_filetype': ( self._FirstFiletype, None ), 'force_semantic': ( self._GetForceSemantic, None ), 'lines': ( self._CurrentLines, None ), 'extra_conf_data': ( self._GetExtraConfData, None ), } self._cached_computed = {} def __getitem__( self, key ): if key in self._cached_computed: return self._cached_computed[ key ] if key in self._computed_key: getter, _ = self._computed_key[ key ] value = getter() self._cached_computed[ key ] = value return value return self._request[ key ] def __setitem__( self, key, value ): if key in self._computed_key: _, setter = self._computed_key[ key ] if setter: setter( value ) return raise ValueError( f'Key "{ key }" is read-only' ) def __contains__( self, key ): return key in self._computed_key or key in self._request def __eq__( self, other ): if ( self[ 'filepath' ] != other[ 'filepath' ] or self[ 'filetypes' ] != other[ 'filetypes' ] or self[ 'line_num' ] != other[ 'line_num' ] or self[ 'start_column' ] != other[ 'start_column' ] or self[ 'prefix' ] != other[ 'prefix' ] or self[ 'force_semantic' ] != other[ 'force_semantic' ] or self[ 'extra_conf_data' ] != other[ 'extra_conf_data' ] or len( self[ 'file_data' ] ) != len( other[ 'file_data' ] ) ): return False for filename, file_data in self[ 'file_data' ].items(): if filename == self[ 'filepath' ]: lines = self[ 'lines' ] other_lines = other[ 'lines' ] if len( lines ) != len( other_lines ): return False line_num = self[ 'line_num' ] if ( lines[ : line_num - 1 ] != other_lines[ : line_num - 1 ] or lines[ line_num : ] != other_lines[ line_num : ] ): return False elif ( filename not in other[ 'file_data' ] or file_data != other[ 'file_data' ][ filename ] ): return False return True def get( self, key, default = None ): try: return self[ key ] except KeyError: return default def _CurrentLines( self ): current_file = self[ 'filepath' ] contents = self[ 'file_data' ][ current_file ][ 'contents' ] return SplitLines( contents ) def _CurrentLine( self ): try: return self[ 'lines' ][ self[ 'line_num' ] - 1 ] except IndexError: LOGGER.exception( 'Client returned invalid line number %s ' 'for file %s. Assuming empty', self[ 'line_num' ], self[ 'filepath' ] ) return '' def _GetCompletionStartColumn( self ): return CompletionStartColumn( self[ 'line_value' ], self[ 'column_num' ], self[ 'first_filetype' ] ) def _SetCompletionStartColumn( self, column_num ): self._cached_computed[ 'start_column' ] = column_num # Note: We must pre-compute (and cache) the codepoint equivalent. This is # because the value calculated by the getter (_GetCompletionStartCodepoint) # would be based on self[ 'column_codepoint' ] which would be incorrect; it # does not know that the user has forced this value to be independent of the # column. self._cached_computed[ 'start_codepoint' ] = ByteOffsetToCodepointOffset( self[ 'line_value' ], column_num ) # The same applies to the 'prefix' (the bit before the start column) and the # 'query' (the bit after the start column up to the cursor column). They are # dependent on the 'start_codepoint' so we must reset them. self._cached_computed.pop( 'prefix', None ) self._cached_computed.pop( 'query', None ) def _GetCompletionStartCodepoint( self ): return CompletionStartCodepoint( self[ 'line_value' ], self[ 'column_num' ], self[ 'first_filetype' ] ) def _SetCompletionStartCodepoint( self, codepoint_offset ): self._cached_computed[ 'start_codepoint' ] = codepoint_offset # Note: We must pre-compute (and cache) the byte equivalent. This is because # the value calculated by the getter (_GetCompletionStartColumn) would be # based on self[ 'column_num' ], which would be incorrect; it does not know # that the user has forced this value to be independent of the column. self._cached_computed[ 'start_column' ] = CodepointOffsetToByteOffset( self[ 'line_value' ], codepoint_offset ) # The same applies to the 'prefix' (the bit before the start column) and the # 'query' (the bit after the start column up to the cursor column). They are # dependent on the 'start_codepoint' so we must reset them. self._cached_computed.pop( 'prefix', None ) self._cached_computed.pop( 'query', None ) def _Query( self ): return self[ 'line_value' ][ self[ 'start_codepoint' ] - 1 : self[ 'column_codepoint' ] - 1 ] def _Prefix( self ): return self[ 'line_value' ][ : ( self[ 'start_codepoint' ] - 1 ) ] def _FirstFiletype( self ): try: return self[ 'filetypes' ][ 0 ] except ( KeyError, IndexError ): return None def _Filetypes( self ): path = self[ 'filepath' ] return self[ 'file_data' ][ path ][ 'filetypes' ] def _GetForceSemantic( self ): return bool( self._request.get( 'force_semantic', False ) ) def _GetExtraConfData( self ): return HashableDict( self._request.get( 'extra_conf_data', {} ) ) def CompletionStartColumn( line_value, column_num, filetype ): """Returns the 1-based byte index where the completion query should start. So if the user enters: foo.bar^ with the cursor being at the location of the caret (so the character *AFTER* 'r'), then the starting column would be the index of the letter 'b'. NOTE: if the line contains multi-byte characters, then the result is not the 'character' index (see CompletionStartCodepoint for that), and therefore it is not safe to perform any character-relevant arithmetic on the result of this method.""" return CodepointOffsetToByteOffset( ToUnicode( line_value ), CompletionStartCodepoint( line_value, column_num, filetype ) ) def CompletionStartCodepoint( line_value, column_num, filetype ): """Returns the 1-based codepoint index where the completion query should start. So if the user enters: ƒøø.∫å®^ with the cursor being at the location of the caret (so the character *AFTER* '®'), then the starting column would be the index of the character '∫' (i.e. 5, not its byte index).""" # NOTE: column_num and other numbers on the wire are byte indices, but we need # to walk codepoints for identifier checks. codepoint_column_num = ByteOffsetToCodepointOffset( line_value, column_num ) unicode_line_value = ToUnicode( line_value ) # -1 and then +1 to account for difference between 0-based and 1-based # indices/columns codepoint_start_column = StartOfLongestIdentifierEndingAtIndex( unicode_line_value, codepoint_column_num - 1, filetype ) + 1 return codepoint_start_column ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/responses.py�����������������������������������������������������0000664�0000000�0000000�00000027727�13746324601�0020505�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2013-2020 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 <http://www.gnu.org/licenses/>. import os from ycmd.utils import ProcessIsRunning YCM_EXTRA_CONF_FILENAME = '.ycm_extra_conf.py' CONFIRM_CONF_FILE_MESSAGE = ( 'Found {0}. Load? \n\n(Question can be turned ' 'off with options, see YCM docs)' ) NO_EXTRA_CONF_FILENAME_MESSAGE = ( f'No { YCM_EXTRA_CONF_FILENAME } file ' 'detected, so no compile flags are available. Thus no semantic support for ' 'C/C++/ObjC/ObjC++. Go READ THE ' 'DOCS *NOW*, DON\'T file a bug report.' ) NO_DIAGNOSTIC_SUPPORT_MESSAGE = ( 'YCM has no diagnostics support for this ' 'filetype; refer to Syntastic docs if using Syntastic.' ) EMPTY_SIGNATURE_INFO = { 'activeSignature': 0, 'activeParameter': 0, 'signatures': [], } class SignatureHelpAvailalability: AVAILABLE = 'YES' NOT_AVAILABLE = 'NO' PENDING = 'PENDING' class ServerError( Exception ): def __init__( self, message ): super().__init__( message ) class UnknownExtraConf( ServerError ): def __init__( self, extra_conf_file ): message = CONFIRM_CONF_FILE_MESSAGE.format( extra_conf_file ) super().__init__( message ) self.extra_conf_file = extra_conf_file class NoExtraConfDetected( ServerError ): def __init__( self ): super().__init__( NO_EXTRA_CONF_FILENAME_MESSAGE ) class NoDiagnosticSupport( ServerError ): def __init__( self ): super().__init__( NO_DIAGNOSTIC_SUPPORT_MESSAGE ) # column_num is a byte offset def BuildGoToResponse( filepath, line_num, column_num, description = None ): return BuildGoToResponseFromLocation( Location( line = line_num, column = column_num, filename = filepath ), description ) def BuildGoToResponseFromLocation( location, description = None ): """Build a GoTo response from a responses.Location object.""" response = BuildLocationData( location ) if description: response[ 'description' ] = description return response def BuildDescriptionOnlyGoToResponse( text ): return { 'description': text, } def BuildDisplayMessageResponse( text ): return { 'message': text } def BuildDetailedInfoResponse( text ): """ Returns the response object for displaying detailed information about types and usage, such as within a preview window""" return { 'detailed_info': text } def BuildCompletionData( insertion_text, extra_menu_info = None, detailed_info = None, menu_text = None, kind = None, extra_data = None ): completion_data = { 'insertion_text': insertion_text } if extra_menu_info: completion_data[ 'extra_menu_info' ] = extra_menu_info if menu_text: completion_data[ 'menu_text' ] = menu_text if detailed_info: completion_data[ 'detailed_info' ] = detailed_info if kind: completion_data[ 'kind' ] = kind if extra_data: completion_data[ 'extra_data' ] = extra_data return completion_data # start_column is a byte offset def BuildCompletionResponse( completions, start_column, errors=None ): return { 'completions': completions, 'completion_start_column': start_column, 'errors': errors if errors else [], } def BuildResolveCompletionResponse( completion, errors ): return { 'completion': completion, 'errors': errors if errors else [], } def BuildSignatureHelpResponse( signature_info, errors = None ): return { 'signature_help': signature_info if signature_info else EMPTY_SIGNATURE_INFO, 'errors': errors if errors else [], } # location.column_number_ is a byte offset def BuildLocationData( location ): return { 'line_num': location.line_number_, 'column_num': location.column_number_, 'filepath': ( os.path.normpath( location.filename_ ) if location.filename_ else '' ), } def BuildRangeData( source_range ): return { 'start': BuildLocationData( source_range.start_ ), 'end': BuildLocationData( source_range.end_ ), } class Diagnostic: def __init__( self, ranges, location, location_extent, text, kind, fixits = [] ): self.ranges_ = ranges self.location_ = location self.location_extent_ = location_extent self.text_ = text self.kind_ = kind self.fixits_ = fixits class UnresolvedFixIt: def __init__( self, command, text, kind = None ): self.command = command self.text = text self.resolve = True self.kind = kind class FixIt: """A set of replacements (of type FixItChunk) to be applied to fix a single diagnostic. This can be used for any type of refactoring command, not just quick fixes. The individual chunks may span multiple files. NOTE: All offsets supplied in both |location| and (the members of) |chunks| must be byte offsets into the UTF-8 encoded version of the appropriate buffer. """ class Kind: """These are LSP kinds that we use outside of LSP completers.""" REFACTOR = 'refactor' def __init__( self, location, chunks, text = '', kind = None ): """location of type Location, chunks of type list<FixItChunk>""" self.location = location self.chunks = chunks self.text = text self.kind = kind class FixItChunk: """An individual replacement within a FixIt (aka Refactor)""" def __init__( self, replacement_text, range ): """replacement_text of type string, range of type Range""" self.replacement_text = replacement_text self.range = range class Range: """Source code range relating to a diagnostic or FixIt (aka Refactor).""" def __init__( self, start, end ): "start of type Location, end of type Location""" self.start_ = start self.end_ = end class Location: """Source code location for a diagnostic or FixIt (aka Refactor).""" def __init__( self, line, column, filename ): """Line is 1-based line, column is 1-based column byte offset, filename is absolute path of the file""" self.line_number_ = line self.column_number_ = column if filename: self.filename_ = os.path.abspath( filename ) else: # When the filename passed (e.g. by a server) can't be recognized or # parsed, we send an empty filename. This at least allows the client to # know there _is_ a reference, but not exactly where it is. This can # happen with the Java completer which sometimes returns references using # a custom/undocumented URI scheme. Typically, such URIs point to .class # files or other binary data which clients can't display anyway. # FIXME: Sending a location with an empty filename could be considered a # strict breach of our own protocol. Perhaps completers should be required # to simply skip such a location. self.filename_ = filename def BuildDiagnosticData( diagnostic ): kind = ( diagnostic.kind_.name if hasattr( diagnostic.kind_, 'name' ) else diagnostic.kind_ ) return { 'ranges': [ BuildRangeData( x ) for x in diagnostic.ranges_ ], 'location': BuildLocationData( diagnostic.location_ ), 'location_extent': BuildRangeData( diagnostic.location_extent_ ), 'text': diagnostic.text_, 'kind': kind, 'fixit_available': len( diagnostic.fixits_ ) > 0, } def BuildDiagnosticResponse( diagnostics, filename, max_diagnostics_to_display ): if ( max_diagnostics_to_display and len( diagnostics ) > max_diagnostics_to_display ): diagnostics = diagnostics[ : max_diagnostics_to_display ] location = Location( 1, 1, filename ) location_extent = Range( location, location ) diagnostics.append( Diagnostic( [ location_extent ], location, location_extent, 'Maximum number of diagnostics exceeded.', 'ERROR' ) ) return [ BuildDiagnosticData( diagnostic ) for diagnostic in diagnostics ] def BuildFixItResponse( fixits ): """Build a response from a list of FixIt (aka Refactor) objects. This response can be used to apply arbitrary changes to arbitrary files and is suitable for both quick fix and refactor operations""" def BuildFixitChunkData( chunk ): return { 'replacement_text': chunk.replacement_text, 'range': BuildRangeData( chunk.range ), } def BuildFixItData( fixit ): if hasattr( fixit, 'resolve' ): result = { 'command': fixit.command, 'text': fixit.text, 'kind': fixit.kind, 'resolve': fixit.resolve } else: result = { 'location': BuildLocationData( fixit.location ), 'chunks' : [ BuildFixitChunkData( x ) for x in fixit.chunks ], 'text': fixit.text, 'kind': fixit.kind, 'resolve': False } if result[ 'kind' ] is None: result.pop( 'kind' ) return result return { 'fixits' : [ BuildFixItData( x ) for x in fixits ] } def BuildExceptionResponse( exception, traceback ): return { 'exception': exception, 'message': str( exception ), 'traceback': traceback } class DebugInfoServer: """Store debugging information on a server: - name: the server name; - is_running: True if the server process is alive, False otherwise; - executable: path of the executable used to start the server; - address: if applicable, the address on which the server is listening. None otherwise; - port: if applicable, the port on which the server is listening. None otherwise; - pid: the process identifier of the server. None if the server is not running; - logfiles: a list of logging files used by the server; - extras: a list of DebugInfoItem objects for additional information on the server.""" def __init__( self, name, handle, executable, address = None, port = None, logfiles = [], extras = [] ): self.name = name self.is_running = ProcessIsRunning( handle ) self.executable = executable self.address = address self.port = port self.pid = handle.pid if self.is_running else None # Remove undefined logfiles from the list. self.logfiles = [ logfile for logfile in logfiles if logfile ] self.extras = extras class DebugInfoItem: def __init__( self, key, value ): self.key = key self.value = value def BuildDebugInfoResponse( name, servers = [], items = [] ): """Build a response containing debugging information on a semantic completer: - name: the completer name; - servers: a list of DebugInfoServer objects representing the servers used by the completer; - items: a list of DebugInfoItem objects for additional information on the completer.""" def BuildItemData( item ): return { 'key': item.key, 'value': item.value } def BuildServerData( server ): return { 'name': server.name, 'is_running': server.is_running, 'executable': server.executable, 'address': server.address, 'port': server.port, 'pid': server.pid, 'logfiles': server.logfiles, 'extras': [ BuildItemData( item ) for item in server.extras ] } return { 'name': name, 'servers': [ BuildServerData( server ) for server in servers ], 'items': [ BuildItemData( item ) for item in items ] } def BuildSignatureHelpAvailableResponse( value ): return { 'available': value } �����������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/server_state.py��������������������������������������������������0000664�0000000�0000000�00000012211�13746324601�0021150�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2013-2020 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 <http://www.gnu.org/licenses/>. import threading from importlib import import_module from ycmd.completers.general.general_completer_store import ( GeneralCompleterStore ) from ycmd.completers.language_server import generic_lsp_completer from ycmd.utils import LOGGER def _GetGenericLSPCompleter( user_options, filetype ): custom_lsp = user_options[ 'language_server' ] for server_settings in custom_lsp: if filetype in server_settings[ 'filetypes' ]: return generic_lsp_completer.GenericLSPCompleter( user_options, server_settings ) return None class ServerState: def __init__( self, user_options ): self._user_options = user_options self._filetype_completers = {} self._filetype_completers_lock = threading.Lock() self._gencomp = GeneralCompleterStore( self._user_options ) @property def user_options( self ): return self._user_options def Shutdown( self ): with self._filetype_completers_lock: for completer in self._filetype_completers.values(): if completer: completer.Shutdown() self._gencomp.Shutdown() def _GetFiletypeCompleterForFiletype( self, filetype ): with self._filetype_completers_lock: try: return self._filetype_completers[ filetype ] except KeyError: pass try: module = import_module( f'ycmd.completers.{ filetype }.hook' ) completer = module.GetCompleter( self._user_options ) except ImportError: completer = None if completer is None: completer = _GetGenericLSPCompleter( self._user_options, filetype ) supported_filetypes = { filetype } if completer: supported_filetypes.update( completer.SupportedFiletypes() ) for supported_filetype in supported_filetypes: if supported_filetype not in self._filetype_completers: self._filetype_completers[ supported_filetype ] = completer return completer def GetFiletypeCompleter( self, current_filetypes ): completers = [ self._GetFiletypeCompleterForFiletype( filetype ) for filetype in current_filetypes ] for completer in completers: if completer: return completer raise ValueError( f'No semantic completer exists for filetypes: { current_filetypes }' ) def GetLoadedFiletypeCompleters( self ): with self._filetype_completers_lock: return { completer for completer in self._filetype_completers.values() if completer } def FiletypeCompletionAvailable( self, filetypes, silent = False ): """Returns True if there is a ycmd semantic completer defined for any filetype in the list |filetypes|. Otherwise, returns False and prints an error to the log file, unless silent = True.""" try: self.GetFiletypeCompleter( filetypes ) return True except Exception: if not silent: LOGGER.exception( 'Semantic completion not available for %s', filetypes ) return False def FiletypeCompletionUsable( self, filetypes, silent = False ): """Return True if ycmd supports semantic compltion for any filetype in the list |filetypes| and those filetypes are not disabled by user options.""" return ( self.CurrentFiletypeCompletionEnabled( filetypes ) and self.FiletypeCompletionAvailable( filetypes, silent ) ) def ShouldUseFiletypeCompleter( self, request_data ): """Determines whether or not the semantic completion should be called for completion request.""" filetypes = request_data[ 'filetypes' ] if not self.FiletypeCompletionUsable( filetypes ): # don't use semantic, ignore whether or not the user requested forced # completion as that's not relevant to signatures. return False if request_data[ 'force_semantic' ]: # use semantic, and it was forced return True filetype_completer = self.GetFiletypeCompleter( filetypes ) # was not forced. check the conditions for triggering return filetype_completer.ShouldUseNow( request_data ) def GetGeneralCompleter( self ): return self._gencomp def CurrentFiletypeCompletionEnabled( self, current_filetypes ): """Return False if all filetypes in the list |current_filetypes| are disabled by the user option 'filetype_specific_completion_to_disable'.""" filetype_to_disable = self._user_options[ 'filetype_specific_completion_to_disable' ] if '*' in filetype_to_disable: return False else: return not all( x in filetype_to_disable for x in current_filetypes ) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/server_utils.py��������������������������������������������������0000664�0000000�0000000�00000004733�13746324601�0021202�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2013-2020 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 <http://www.gnu.org/licenses/>. from __future__ import unicode_literals from __future__ import print_function from __future__ import division from __future__ import absolute_import # No other imports from `future` because this module is loaded before we have # put our submodules in sys.path import os.path as p import re import sys ROOT_DIR = p.normpath( p.join( p.dirname( __file__ ), '..' ) ) DIR_OF_THIRD_PARTY = p.join( ROOT_DIR, 'third_party' ) DIR_OF_WATCHDOG_DEPS = p.join( DIR_OF_THIRD_PARTY, 'watchdog_deps' ) PYTHON_STDLIB_ZIP_REGEX = re.compile( 'python3[0-9]\\.zip' ) def SetUpPythonPath(): sys.path[ 0:0 ] = [ p.join( ROOT_DIR ), p.join( DIR_OF_THIRD_PARTY, 'bottle' ), p.join( DIR_OF_THIRD_PARTY, 'regex-build' ), p.join( DIR_OF_THIRD_PARTY, 'frozendict' ), p.join( DIR_OF_THIRD_PARTY, 'jedi_deps', 'jedi' ), 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', '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, 'requests_deps', 'urllib3', 'src' ), p.join( DIR_OF_WATCHDOG_DEPS, 'watchdog', 'build', 'lib3' ), p.join( DIR_OF_WATCHDOG_DEPS, 'pathtools' ), p.join( DIR_OF_THIRD_PARTY, 'waitress' ) ] sys.path.append( p.join( DIR_OF_THIRD_PARTY, 'jedi_deps', 'numpydoc' ) ) �������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/�����������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0017235�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/__init__.py������������������������������������������������0000664�0000000�0000000�00000001615�13746324601�0021351�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2016-2020 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 <http://www.gnu.org/licenses/>. import os from ycmd.tests.conftest import * # noqa def PathToTestFile( *args ): dir_of_current_script = os.path.dirname( os.path.abspath( __file__ ) ) return os.path.join( dir_of_current_script, 'testdata', *args ) �������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/bindings/��������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0021032�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/bindings/__init__.py���������������������������������������0000664�0000000�0000000�00000001544�13746324601�0023147�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import os.path def PathToTestFile( *args ): dir_of_current_script = os.path.dirname( os.path.abspath( __file__ ) ) return os.path.join( dir_of_current_script, 'testdata', *args ) ������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/bindings/cpp_bindings_general_test.py����������������������0000664�0000000�0000000�00000040661�13746324601�0026606�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from ycmd.completers.cpp.clang_completer import ConvertCompletionData from ycmd.responses import BuildDiagnosticData from ycmd.tests.bindings import PathToTestFile from ycmd.tests.test_utils import ( ClangOnly, TemporaryTestDir, TemporaryClangProject ) from ycmd.utils import ImportCore from hamcrest import ( assert_that, contains_exactly, contains_inanyorder, contains_string, equal_to, has_entries, has_properties ) ycm_core = ImportCore() import os def CppBindings_FilterAndSortCandidates_test(): candidates = [ 'foo1', 'foo2', 'foo3' ] query = 'oo' candidate_property = '' result_full = ycm_core.FilterAndSortCandidates( candidates, candidate_property, query ) result_2 = ycm_core.FilterAndSortCandidates( candidates, candidate_property, query, 2 ) del candidates del query del candidate_property assert_that( result_full, contains_exactly( 'foo1', 'foo2', 'foo3' ) ) assert_that( result_2, contains_exactly( 'foo1', 'foo2' ) ) def CppBindings_IdentifierCompleter_test(): identifier_completer = ycm_core.IdentifierCompleter() identifiers = ycm_core.StringVector() identifiers.append( 'foo' ) identifiers.append( 'bar' ) identifiers.append( 'baz' ) identifier_completer.AddIdentifiersToDatabase( identifiers, 'foo', 'file' ) del identifiers query_fo_10 = identifier_completer.CandidatesForQueryAndType( 'fo', 'foo', 10 ) query_fo = identifier_completer.CandidatesForQueryAndType( 'fo', 'foo' ) query_a = identifier_completer.CandidatesForQueryAndType( 'a', 'foo' ) assert_that( query_fo_10, contains_exactly( 'foo' ) ) assert_that( query_fo, contains_exactly( 'foo' ) ) assert_that( query_a, contains_exactly( 'bar', 'baz' ) ) identifiers = ycm_core.StringVector() identifiers.append( 'oof' ) identifiers.append( 'rab' ) identifiers.append( 'zab' ) identifier_completer.ClearForFileAndAddIdentifiersToDatabase( identifiers, 'foo', 'file' ) query_a_10 = identifier_completer.CandidatesForQueryAndType( 'a', 'foo' ) assert_that( query_a_10, contains_exactly( 'rab', 'zab' ) ) @ClangOnly def CppBindings_UnsavedFile_test(): unsaved_file = ycm_core.UnsavedFile() filename = 'foo' contents = 'bar\\n' length = len( contents ) unsaved_file.filename_ = filename unsaved_file.contents_ = contents unsaved_file.length_ = length del filename del contents del length assert_that( unsaved_file, has_properties( { 'filename_': 'foo', 'contents_': 'bar\\n', 'length_': len( 'bar\\n' ) } ) ) @ClangOnly def CppBindings_DeclarationLocation_test(): translation_unit = PathToTestFile( 'foo.c' ) filename = PathToTestFile( 'foo.c' ) line = 9 column = 17 unsaved_file_vector = ycm_core.UnsavedFileVector() flags = ycm_core.StringVector() flags.append( '-xc++' ) reparse = True clang_completer = ycm_core.ClangCompleter() location = clang_completer.GetDeclarationLocation( translation_unit, filename, line, column, unsaved_file_vector, flags, reparse ) del translation_unit del filename del line del column del unsaved_file_vector del flags del clang_completer del reparse assert_that( location, has_properties( { 'line_number_': 2, 'column_number_': 5, 'filename_': PathToTestFile( 'foo.c' ) } ) ) @ClangOnly def CppBindings_DefinitionOrDeclarationLocation_test(): translation_unit = PathToTestFile( 'foo.c' ) filename = PathToTestFile( 'foo.c' ) line = 9 column = 17 unsaved_file_vector = ycm_core.UnsavedFileVector() flags = ycm_core.StringVector() flags.append( '-xc++' ) reparse = True clang_completer = ycm_core.ClangCompleter() location = ( clang_completer. GetDefinitionOrDeclarationLocation( translation_unit, filename, line, column, unsaved_file_vector, flags, reparse ) ) del translation_unit del filename del line del column del unsaved_file_vector del flags del clang_completer del reparse assert_that( location, has_properties( { 'line_number_': 2, 'column_number_': 5, 'filename_': PathToTestFile( 'foo.c' ) } ) ) @ClangOnly def CppBindings_DefinitionLocation_test(): translation_unit = PathToTestFile( 'foo.c' ) filename = PathToTestFile( 'foo.c' ) line = 9 column = 17 unsaved_file_vector = ycm_core.UnsavedFileVector() flags = ycm_core.StringVector() flags.append( '-xc++' ) reparse = True clang_completer = ycm_core.ClangCompleter() location = clang_completer.GetDefinitionLocation( translation_unit, filename, line, column, unsaved_file_vector, flags, reparse ) del translation_unit del filename del line del column del unsaved_file_vector del flags del clang_completer del reparse assert_that( location, has_properties( { 'line_number_': 2, 'column_number_': 5, 'filename_': PathToTestFile( 'foo.c' ) } ) ) @ClangOnly def CppBindings_Candidates_test(): translation_unit = PathToTestFile( 'foo.c' ) filename = PathToTestFile( 'foo.c' ) line = 11 column = 6 unsaved_file_vector = ycm_core.UnsavedFileVector() flags = ycm_core.StringVector() flags.append( '-xc' ) reparse = True clang_completer = ycm_core.ClangCompleter() candidates = ( clang_completer .CandidatesForLocationInFile( translation_unit, filename, line, column, unsaved_file_vector, flags ) ) del translation_unit del filename del line del column del unsaved_file_vector del flags del clang_completer del reparse candidates = [ ConvertCompletionData( x ) for x in candidates ] assert_that( candidates, contains_inanyorder( has_entries( { 'detailed_info': 'float b\n', 'extra_menu_info': 'float', 'insertion_text': 'b', 'kind': 'MEMBER', 'menu_text': 'b' } ), has_entries( { 'detailed_info': 'int a\n', 'extra_menu_info': 'int', 'insertion_text': 'a', 'kind': 'MEMBER', 'menu_text': 'a' } ) ) ) @ClangOnly def CppBindings_GetType_test(): translation_unit = PathToTestFile( 'foo.c' ) filename = PathToTestFile( 'foo.c' ) line = 9 column = 17 unsaved_file_vector = ycm_core.UnsavedFileVector() flags = ycm_core.StringVector() flags.append( '-xc++' ) reparse = True clang_completer = ycm_core.ClangCompleter() type_at_cursor = clang_completer.GetTypeAtLocation( translation_unit, filename, line, column, unsaved_file_vector, flags, reparse ) del translation_unit del filename del line del column del unsaved_file_vector del flags del clang_completer del reparse assert_that( 'int ()', equal_to( type_at_cursor ) ) @ClangOnly def CppBindings_GetParent_test(): translation_unit = PathToTestFile( 'foo.c' ) filename = PathToTestFile( 'foo.c' ) line = 9 column = 17 unsaved_file_vector = ycm_core.UnsavedFileVector() flags = ycm_core.StringVector() flags.append( '-xc++' ) reparse = True clang_completer = ycm_core.ClangCompleter() enclosing_function = ( clang_completer .GetEnclosingFunctionAtLocation( translation_unit, filename, line, column, unsaved_file_vector, flags, reparse ) ) del translation_unit del filename del line del column del unsaved_file_vector del flags del clang_completer del reparse assert_that( 'bar', equal_to( enclosing_function ) ) @ClangOnly def CppBindings_FixIt_test(): translation_unit = PathToTestFile( 'foo.c' ) filename = PathToTestFile( 'foo.c' ) line = 3 column = 5 unsaved_file_vector = ycm_core.UnsavedFileVector() flags = ycm_core.StringVector() flags.append( '-xc++' ) reparse = True clang_completer = ycm_core.ClangCompleter() fixits = clang_completer.GetFixItsForLocationInFile( translation_unit, filename, line, column, unsaved_file_vector, flags, reparse ) del translation_unit del filename del line del column del unsaved_file_vector del flags del clang_completer del reparse assert_that( fixits, contains_exactly( has_properties( { 'text': ( PathToTestFile( 'foo.c' ) + ':3:16: error: expected \';\' at end of declaration' ), 'location': has_properties( { 'line_number_': 3, 'column_number_': 16, 'filename_': PathToTestFile( 'foo.c' ) } ), 'chunks': contains_exactly( has_properties( { 'replacement_text': ';', 'range': has_properties( { 'start_': has_properties( { 'line_number_': 3, 'column_number_': 16, } ), 'end_': has_properties( { 'line_number_': 3, 'column_number_': 16, } ), } ) } ) ), 'kind': None, } ) ) ) @ClangOnly def CppBindings_Docs_test(): translation_unit = PathToTestFile( 'foo.c' ) filename = PathToTestFile( 'foo.c' ) line = 9 column = 16 unsaved_file_vector = ycm_core.UnsavedFileVector() flags = ycm_core.StringVector() flags.append( '-xc++' ) reparse = True clang_completer = ycm_core.ClangCompleter() docs = clang_completer.GetDocsForLocationInFile( translation_unit, filename, line, column, unsaved_file_vector, flags, reparse ) del translation_unit del filename del line del column del unsaved_file_vector del flags del clang_completer del reparse assert_that( docs, has_properties( { 'comment_xml': '<Function file="' + PathToTestFile( 'foo.c' ) + '"' ' line="2" column="5"><Name>foooo</Name><USR>c:@F@foooo#' '</USR><Declaration>int foooo()</Declaration><Abstract>' '<Para> Foo</Para></Abstract></Function>', 'brief_comment': 'Foo', 'raw_comment': '/// Foo', 'canonical_type': 'int ()', 'display_name': 'foooo' } ) ) @ClangOnly def CppBindings_Diags_test(): filename = PathToTestFile( 'foo.c' ) unsaved_file_vector = ycm_core.UnsavedFileVector() flags = ycm_core.StringVector() flags.append( '-xc++' ) reparse = True clang_completer = ycm_core.ClangCompleter() diag_vector = clang_completer.UpdateTranslationUnit( filename, unsaved_file_vector, flags ) diags = [ BuildDiagnosticData( x ) for x in diag_vector ] del diag_vector del filename del unsaved_file_vector del flags del clang_completer del reparse assert_that( diags, contains_exactly( has_entries( { 'kind': 'ERROR', 'text': contains_string( 'expected \';\' at end of declaration' ), 'ranges': contains_exactly(), 'location': has_entries( { 'line_num': 3, 'column_num': 16, } ), 'location_extent': has_entries( { 'start': has_entries( { 'line_num': 3, 'column_num': 16, } ), 'end': has_entries( { 'line_num': 3, 'column_num': 16, } ), } ), } ) ) ) @ClangOnly def CppBindings_CompilationDatabase_test(): with TemporaryTestDir() as tmp_dir: compile_commands = [ { 'directory': tmp_dir, 'command': 'clang++ -x c++ -I. -I/absolute/path -Wall', 'file': os.path.join( tmp_dir, 'test.cc' ), }, ] with TemporaryClangProject( tmp_dir, compile_commands ): db = ycm_core.CompilationDatabase( tmp_dir ) db_successful = db.DatabaseSuccessfullyLoaded() db_busy = db.AlreadyGettingFlags() db_dir = db.database_directory compilation_info = db.GetCompilationInfoForFile( compile_commands[ 0 ][ 'file' ] ) del db del compile_commands assert_that( db_successful, equal_to( True ) ) assert_that( db_busy, equal_to( False ) ) assert_that( db_dir, equal_to( tmp_dir ) ) assert_that( compilation_info, has_properties( { 'compiler_working_dir_': tmp_dir, 'compiler_flags_': contains_exactly( 'clang++', '--driver-mode=g++', '-x', 'c++', '-I.', '-I/absolute/path', '-Wall' ) } ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/bindings/cpp_bindings_raises_exception_test.py�������������0000664�0000000�0000000�00000014003�13746324601�0030524�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from ycmd.utils import ImportCore from ycmd.tests.test_utils import ClangOnly from hamcrest import assert_that, calling, raises ycm_core = ImportCore() READONLY_MESSAGE = 'can\'t set attribute' @ClangOnly def CppBindings_ReadOnly_test(): assert_that( calling( ycm_core.CompletionData().__setattr__ ) .with_args( 'kind_', ycm_core.CompletionData().kind_ ), raises( AttributeError, READONLY_MESSAGE ) ) assert_that( calling( ycm_core.Location().__setattr__ ) .with_args( 'line_number_', 1 ), raises( AttributeError, READONLY_MESSAGE ) ) assert_that( calling( ycm_core.Location().__setattr__ ) .with_args( 'column_number_', 1 ), raises( AttributeError, READONLY_MESSAGE ) ) assert_that( calling( ycm_core.Location().__setattr__ ) .with_args( 'filename_', 'foo' ), raises( AttributeError, READONLY_MESSAGE ) ) assert_that( calling( ycm_core.Range().__setattr__ ) .with_args( 'end_', ycm_core.Range().end_ ), raises( AttributeError, READONLY_MESSAGE ) ) assert_that( calling( ycm_core.Range().__setattr__ ) .with_args( 'start_', ycm_core.Range().start_ ), raises( AttributeError, READONLY_MESSAGE ) ) assert_that( calling( ycm_core.FixItChunk().__setattr__ ) .with_args( 'range', ycm_core.FixItChunk().range ), raises( AttributeError, READONLY_MESSAGE ) ) assert_that( calling( ycm_core.FixItChunk().__setattr__ ) .with_args( 'replacement_text', 'foo' ), raises( AttributeError, READONLY_MESSAGE ) ) assert_that( calling( ycm_core.FixIt().__setattr__ ) .with_args( 'chunks', ycm_core.FixIt().chunks ), raises( AttributeError, READONLY_MESSAGE ) ) assert_that( calling( ycm_core.FixIt().__setattr__ ) .with_args( 'location', ycm_core.FixIt().location ), raises( AttributeError, READONLY_MESSAGE ) ) assert_that( calling( ycm_core.FixIt().__setattr__ ) .with_args( 'text', 'foo' ), raises( AttributeError, READONLY_MESSAGE ) ) assert_that( calling( ycm_core.Diagnostic().__setattr__ ) .with_args( 'ranges_', ycm_core.Diagnostic().ranges_ ), raises( AttributeError, READONLY_MESSAGE ) ) assert_that( calling( ycm_core.Diagnostic().__setattr__ ) .with_args( 'location_', ycm_core.Diagnostic().location_ ), raises( AttributeError, READONLY_MESSAGE ) ) assert_that( calling( ycm_core.Diagnostic().__setattr__ ) .with_args( 'location_extent_', ycm_core.Diagnostic().location_extent_ ), raises( AttributeError, READONLY_MESSAGE ) ) assert_that( calling( ycm_core.Diagnostic().__setattr__ ) .with_args( 'fixits_', ycm_core.Diagnostic().fixits_ ), raises( AttributeError, READONLY_MESSAGE ) ) assert_that( calling( ycm_core.Diagnostic().__setattr__ ) .with_args( 'text_', 'foo' ), raises( AttributeError, READONLY_MESSAGE ) ) assert_that( calling( ycm_core.Diagnostic().__setattr__ ) .with_args( 'long_formatted_text_', 'foo' ), raises( AttributeError, READONLY_MESSAGE ) ) assert_that( calling( ycm_core.Diagnostic().__setattr__ ) .with_args( 'kind_', ycm_core.Diagnostic().kind_.WARNING ), raises( AttributeError, READONLY_MESSAGE ) ) assert_that( calling( ycm_core.DocumentationData().__setattr__ ) .with_args( 'raw_comment', 'foo' ), raises( AttributeError, READONLY_MESSAGE ) ) assert_that( calling( ycm_core.DocumentationData().__setattr__ ) .with_args( 'brief_comment', 'foo' ), raises( AttributeError, READONLY_MESSAGE ) ) assert_that( calling( ycm_core.DocumentationData().__setattr__ ) .with_args( 'canonical_type', 'foo' ), raises( AttributeError, READONLY_MESSAGE ) ) assert_that( calling( ycm_core.DocumentationData().__setattr__ ) .with_args( 'display_name', 'foo' ), raises( AttributeError, READONLY_MESSAGE ) ) assert_that( calling( ycm_core.DocumentationData().__setattr__ ) .with_args( 'comment_xml', 'foo' ), raises( AttributeError, READONLY_MESSAGE ) ) db = ycm_core.CompilationDatabase( 'foo' ) assert_that( calling( db.__setattr__ ) .with_args( 'database_directory', 'foo' ), raises( AttributeError, READONLY_MESSAGE ) ) compilation_info = db.GetCompilationInfoForFile( 'foo.c' ) assert_that( calling( compilation_info.__setattr__ ) .with_args( 'compiler_working_dir_', 'foo' ), raises( AttributeError, READONLY_MESSAGE ) ) assert_that( calling( compilation_info.__setattr__ ) .with_args( 'compiler_flags_', ycm_core.StringVector() ), raises( AttributeError, READONLY_MESSAGE ) ) @ClangOnly def CppBindings_CompilationInfo_NoInit_test(): assert_that( calling( ycm_core.CompilationInfoForFile ), raises( TypeError, 'ycm_core.CompilationInfoForFile:' ' No constructor defined!' ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/bindings/cpp_bindings_vectors_test.py����������������������0000664�0000000�0000000�00000030555�13746324601�0026657�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from ycmd.completers.cpp.clang_completer import ConvertCompletionData from ycmd.responses import BuildDiagnosticData from ycmd.tests.bindings import PathToTestFile from ycmd.tests.test_utils import ClangOnly from ycmd.utils import ImportCore from hamcrest import ( assert_that, contains_exactly, contains_inanyorder, contains_string, has_entries, has_properties ) ycm_core = ImportCore() def EmplaceBack( vector, element ): vector.append( element ) def CppBindings_StringVector_test(): str1 = 'foo' str2 = 'bar' str3 = 'baz' string_vector = ycm_core.StringVector() string_vector.append( str1 ) EmplaceBack( string_vector, str2 ) string_vector.append( str3 ) del str1 del str2 del str3 assert_that( string_vector, contains_exactly( 'foo', 'bar', 'baz' ) ) @ClangOnly def CppBindings_UnsavedFileVector_test(): unsaved_file_vector = ycm_core.UnsavedFileVector() unsaved_file = ycm_core.UnsavedFile() unsaved_file.filename_ = 'foo' unsaved_file.contents_ = 'bar' unsaved_file.length_ = 3 unsaved_file_vector.append( unsaved_file ) EmplaceBack( unsaved_file_vector, unsaved_file ) del unsaved_file assert_that( unsaved_file_vector, contains_exactly( has_properties( { 'filename_': 'foo', 'contents_': 'bar', 'length_': len( 'bar' ) } ), has_properties( { 'filename_': 'foo', 'contents_': 'bar', 'length_': len( 'bar' ) } ) ) ) @ClangOnly def CppBindings_FixItVector_test(): flags = ycm_core.StringVector() flags.append( '-xc++' ) clang_completer = ycm_core.ClangCompleter() translation_unit = PathToTestFile( 'foo.c' ) filename = PathToTestFile( 'foo.c' ) fixits = ( clang_completer .GetFixItsForLocationInFile( translation_unit, filename, 3, 5, ycm_core.UnsavedFileVector(), flags, True ) ) fixits = fixits[ 0:1 ] EmplaceBack( fixits, fixits[ 0 ] ) del translation_unit del flags del filename del clang_completer assert_that( fixits, contains_exactly( has_properties( { 'text': ( PathToTestFile( 'foo.c' ) + ':3:16: error: expected \';\' at end of declaration' ), 'location': has_properties( { 'line_number_': 3, 'column_number_': 16, 'filename_': PathToTestFile( 'foo.c' ) } ), 'chunks': contains_exactly( has_properties( { 'replacement_text': ';', 'range': has_properties( { 'start_': has_properties( { 'line_number_': 3, 'column_number_': 16, } ), 'end_': has_properties( { 'line_number_': 3, 'column_number_': 16, } ), } ) } ) ), } ), has_properties( { 'text': ( PathToTestFile( 'foo.c' ) + ':3:16: error: expected \';\' at end of declaration' ), 'location': has_properties( { 'line_number_': 3, 'column_number_': 16, 'filename_': PathToTestFile( 'foo.c' ) } ), 'chunks': contains_exactly( has_properties( { 'replacement_text': ';', 'range': has_properties( { 'start_': has_properties( { 'line_number_': 3, 'column_number_': 16, } ), 'end_': has_properties( { 'line_number_': 3, 'column_number_': 16, } ), } ) } ) ), } ) ) ) @ClangOnly def CppBindings_FixItChunkVector_test(): flags = ycm_core.StringVector() flags.append( '-xc++' ) clang_completer = ycm_core.ClangCompleter() translation_unit = PathToTestFile( 'foo.c' ) filename = PathToTestFile( 'foo.c' ) fixits = ( clang_completer .GetFixItsForLocationInFile( translation_unit, filename, 3, 5, ycm_core.UnsavedFileVector(), flags, True ) ) fixit_chunks = fixits[ 0 ].chunks[ 0:1 ] EmplaceBack( fixit_chunks, fixit_chunks[ 0 ] ) del translation_unit del flags del filename del clang_completer del fixits assert_that( fixit_chunks, contains_exactly( has_properties( { 'replacement_text': ';', 'range': has_properties( { 'start_': has_properties( { 'line_number_': 3, 'column_number_': 16, } ), 'end_': has_properties( { 'line_number_': 3, 'column_number_': 16, } ), } ), } ), has_properties( { 'replacement_text': ';', 'range': has_properties( { 'start_': has_properties( { 'line_number_': 3, 'column_number_': 16, } ), 'end_': has_properties( { 'line_number_': 3, 'column_number_': 16, } ), } ), } ) ) ) @ClangOnly def CppBindings_RangeVector_test(): flags = ycm_core.StringVector() flags.append( '-xc++' ) clang_completer = ycm_core.ClangCompleter() translation_unit = PathToTestFile( 'foo.c' ) filename = PathToTestFile( 'foo.c' ) fixits = ( clang_completer .GetFixItsForLocationInFile( translation_unit, filename, 3, 5, ycm_core.UnsavedFileVector(), flags, True ) ) fixit_range = fixits[ 0 ].chunks[ 0 ].range ranges = ycm_core.RangeVector() ranges.append( fixit_range ) EmplaceBack( ranges, fixit_range ) del flags del translation_unit del filename del clang_completer del fixits del fixit_range assert_that( ranges, contains_exactly( has_properties( { 'start_': has_properties( { 'line_number_': 3, 'column_number_': 16, } ), 'end_': has_properties( { 'line_number_': 3, 'column_number_': 16, } ), } ), has_properties( { 'start_': has_properties( { 'line_number_': 3, 'column_number_': 16, } ), 'end_': has_properties( { 'line_number_': 3, 'column_number_': 16, } ), } ), ) ) @ClangOnly def CppBindings_DiagnosticVector_test(): filename = PathToTestFile( 'foo.c' ) unsaved_file_vector = ycm_core.UnsavedFileVector() flags = ycm_core.StringVector() flags.append( '-xc++' ) clang_completer = ycm_core.ClangCompleter() diag_vector = clang_completer.UpdateTranslationUnit( filename, unsaved_file_vector, flags ) del filename del unsaved_file_vector del flags del clang_completer diag_vector = diag_vector[ 0:1 ] EmplaceBack( diag_vector, diag_vector[ 0 ] ) diags = [ BuildDiagnosticData( x ) for x in diag_vector ] del diag_vector assert_that( diags, contains_exactly( has_entries( { 'kind': 'ERROR', 'text': contains_string( 'expected \';\' at end of declaration' ), 'ranges': contains_exactly(), 'location': has_entries( { 'line_num': 3, 'column_num': 16, } ), 'location_extent': has_entries( { 'start': has_entries( { 'line_num': 3, 'column_num': 16, } ), 'end': has_entries( { 'line_num': 3, 'column_num': 16, } ), } ), } ), has_entries( { 'kind': 'ERROR', 'text': contains_string( 'expected \';\' at end of declaration' ), 'ranges': contains_exactly(), 'location': has_entries( { 'line_num': 3, 'column_num': 16, } ), 'location_extent': has_entries( { 'start': has_entries( { 'line_num': 3, 'column_num': 16, } ), 'end': has_entries( { 'line_num': 3, 'column_num': 16, } ), } ), } ), ) ) @ClangOnly def CppBindings_CompletionDataVector_test(): translation_unit = PathToTestFile( 'foo.c' ) filename = PathToTestFile( 'foo.c' ) line = 11 column = 6 unsaved_file_vector = ycm_core.UnsavedFileVector() flags = ycm_core.StringVector() flags.append( '-xc' ) clang_completer = ycm_core.ClangCompleter() candidates = ( clang_completer .CandidatesForLocationInFile( translation_unit, filename, line, column, unsaved_file_vector, flags ) ) if candidates[ 0 ].TextToInsertInBuffer() == 'a': candidate = candidates[ 0 ] else: candidate = candidates[ 1 ] candidates = ycm_core.CompletionVector() candidates.append( candidate ) EmplaceBack( candidates, candidate ) del translation_unit del filename del candidate del clang_completer del line del column del flags del unsaved_file_vector candidates = [ ConvertCompletionData( x ) for x in candidates ] assert_that( candidates, contains_inanyorder( has_entries( { 'detailed_info': 'int a\n', 'extra_menu_info': 'int', 'insertion_text': 'a', 'kind': 'MEMBER', 'menu_text': 'a' } ), has_entries( { 'detailed_info': 'int a\n', 'extra_menu_info': 'int', 'insertion_text': 'a', 'kind': 'MEMBER', 'menu_text': 'a' } ) ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ���������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/bindings/testdata/�����������������������������������������0000775�0000000�0000000�00000000000�13746324601�0022643�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/bindings/testdata/foo.c������������������������������������0000664�0000000�0000000�00000000255�13746324601�0023574�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/// Foo int foooo(){ int aaaaaaaaa return aaaaaaaaa; } int main(int argc, char *argv[]) { int bar = foooo(); struct { int a; float b; } a; a.b = 5; return 0; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/�����������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0020321�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/__init__.py������������������������������������������0000664�0000000�0000000�00000002747�13746324601�0022444�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import os from ycmd.tests.clang.conftest import * # noqa shared_app = None def PathToTestFile( *args ): dir_of_current_script = os.path.dirname( os.path.abspath( __file__ ) ) return os.path.join( dir_of_current_script, 'testdata', *args ) # A mock of ycm_core.ClangCompleter with translation units still being parsed. class MockCoreClangCompleter: def GetDefinitionLocation( self, *args ): pass def GetDeclarationLocation( self, *args ): pass def GetDefinitionOrDeclarationLocation( self, *args ): pass def GetTypeAtLocation( self, *args ): pass def GetEnclosingFunctionAtLocation( self, *args ): pass def GetDocsForLocationInFile( self, *args ): pass def GetFixItsForLocationInFile( self, *args ): pass def UpdatingTranslationUnit( self, filename ): return True �������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/comment_strip_test.py��������������������������������0000664�0000000�0000000�00000011545�13746324601�0024623�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. """This tests the comment "sanitisation" which is done on C/C++/ObjC method/variable,etc. headers in order to remove non-data-ink from the raw comment""" # flake8: noqa from hamcrest import assert_that, equal_to from ycmd.completers.cpp import clang_completer def _Check_FormatRawComment( comment, expected ): try: result = clang_completer._FormatRawComment( comment ) assert_that( result, equal_to( expected ) ) except: print( "Failed while parsing:\n" "'" + comment + "'\n" "Expecting:\n" "'" + expected + "'\n" "But found:\n" "'" + result + "'" ) raise def ClangCompleter_FormatRawComment_SingleLine_Doxygen_test(): # - <whitespace>/// # Indent + /// + _Check_FormatRawComment( ' /// Single line comment', 'Single line comment' ) # No indent, Internal indent removed, trailing whitespace removed _Check_FormatRawComment( '/// Single line comment ', 'Single line comment' ) # Extra / prevents initial indent being removed _Check_FormatRawComment( '//// Single line comment ', '/ Single line comment' ) _Check_FormatRawComment( '////* Test ', '/* Test' ) def ClangCompleter_FormatRawComment_SingleLine_InlineDoxygen_test(): # Inline-style comments with and without leading/trailing tokens # - <whitespace>///< _Check_FormatRawComment( '///<Test', 'Test' ) _Check_FormatRawComment( ' ///<Test */', 'Test' ) _Check_FormatRawComment( '///<Test ', 'Test' ) _Check_FormatRawComment( '///< Test ', 'Test' ) _Check_FormatRawComment( ' ///<< Test ', '< Test' ) _Check_FormatRawComment( ' ///<! Test ', '! Test' ) def ClangCompleter_FormatRawComment_SingleLine_InlineShort_test(): # - <whitespace>//< _Check_FormatRawComment( '//<Test', 'Test' ) _Check_FormatRawComment( ' //<Test', 'Test' ) _Check_FormatRawComment( '//<Test', 'Test' ) _Check_FormatRawComment( '//< Test', 'Test' ) _Check_FormatRawComment( '//<< Test ', '< Test' ) def ClangCompleter_FormatRawComment_SingleLine_InlineShortBang_test(): # - <whitespace>//! _Check_FormatRawComment( '//!Test', 'Test' ) _Check_FormatRawComment( ' //<Test */ ', 'Test' ) _Check_FormatRawComment( '//!Test ', 'Test' ) _Check_FormatRawComment( '//! Test ', 'Test' ) _Check_FormatRawComment( '//!! Test ', '! Test' ) def ClangCompleter_FormatRawComment_SingleLine_JavaDoc_test(): # - <whitespace>/* # - <whitespace>/** # - <whitespace>*/ _Check_FormatRawComment( '/*Test', 'Test' ) _Check_FormatRawComment( ' /** Test */ ', 'Test' ) _Check_FormatRawComment( '/*** Test', '* Test' ) def ClangCompleter_FormatRawComment_MultiOneLine_JavaDoc_test(): # sic: This one isn't ideal, but it is (probably) uncommon _Check_FormatRawComment( '/** Test */ /** Test2 */', 'Test */ /** Test2' ) def ClangCompleter_FormatRawComment_MultiLine_Doxygen_Inbalance_test(): # The dedenting only applies to consistent indent # Note trailing whitespace is intentional _Check_FormatRawComment( """ /// This is /// a ///Multi-line /// Comment /// With many different */ ///< Doxygen-like ///! comment ****/ Entries """, """ This is a Multi-line Comment With many different Doxygen-like comment ****/ Entries """ ) # noqa def ClangCompleter_FormatRawComment_MultiLine_JavaDoc_Inconsistent_test(): # The dedenting only applies to consistent indent, and leaves any subsequent # indent intact # Note trailing whitespace is intentional _Check_FormatRawComment( """ /** All of the * Lines in this Comment consistently * * Have a 2-space indent */ """, """ All of the Lines in this Comment consistently * Have a 2-space indent """ ) # noqa def ClangCompleter_FormatRawComment_ZeroLine_test(): _Check_FormatRawComment( '', '' ) def ClangCompleter_FormatRawComment_MultiLine_empty_test(): # Note trailing whitespace is intentional _Check_FormatRawComment( """ * /// */ """, """ """ ) # noqa def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �����������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/conftest.py������������������������������������������0000664�0000000�0000000�00000005731�13746324601�0022526�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import pytest from ycmd.tests.test_utils import ( ClearCompletionsCache, IsolatedApp, SetUpApp ) shared_app = None @pytest.fixture( scope='module', autouse=True ) def set_up_shared_app(): global shared_app shared_app = SetUpApp( { 'use_clangd': 0 } ) @pytest.fixture def app( request ): which = request.param[ 0 ] assert which == 'isolated' or which == 'shared' if which == 'isolated': custom_options = request.param[ 1 ] custom_options.update( { 'use_clangd': 0 } ) with IsolatedApp( custom_options ) as app: yield app else: global shared_app ClearCompletionsCache() yield shared_app """Defines a decorator to be attached to tests of this package. This decorator passes the shared ycmd application as a parameter.""" SharedYcmd = pytest.mark.parametrize( # Name of the fixture/function argument 'app', # Fixture parameters, passed to app() as request.param [ ( 'shared', ) ], # Non-empty ids makes fixture parameters visible in pytest verbose output ids = [ '' ], # Execute the fixture, instead of passing parameters directly to the # function argument indirect = True ) def IsolatedYcmd( custom_options = {} ): """Defines a decorator to be attached to tests of this package. This decorator passes a unique ycmd application as a parameter. It should be used on tests that change the server state in a irreversible way (ex: a semantic subserver is stopped or restarted) or expect a clean state (ex: no semantic subserver started, no .ycm_extra_conf.py loaded, etc). Use the optional parameter |custom_options| to give additional options and/or override the default ones. Example usage: from ycmd.tests.python import IsolatedYcmd @IsolatedYcmd( { 'python_binary_path': '/some/path' } ) def CustomPythonBinaryPath_test( app ): ... """ return pytest.mark.parametrize( # Name of the fixture/function argument 'app', # Fixture parameters, passed to app() as request.param [ ( 'isolated', custom_options ) ], # Non-empty ids makes fixture parameters visible in pytest verbose output ids = [ '' ], # Execute the fixture, instead of passing parameters directly to the # function argument indirect = True ) ���������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/debug_info_test.py�����������������������������������0000664�0000000�0000000�00000022372�13746324601�0024041�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2016-2020 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 <http://www.gnu.org/licenses/>. import os from hamcrest import ( assert_that, contains_exactly, empty, has_entries, has_entry, instance_of, matches_regexp ) from ycmd.tests.clang import IsolatedYcmd, PathToTestFile, SharedYcmd from ycmd.tests.test_utils import ( BuildRequest, TemporaryTestDir, TemporaryClangProject ) @SharedYcmd def DebugInfo_FlagsWhenExtraConfLoadedAndNoCompilationDatabase_test( app ): app.post_json( '/load_extra_conf_file', { 'filepath': PathToTestFile( '.ycm_extra_conf.py' ) } ) request_data = BuildRequest( filepath = PathToTestFile( 'basic.cpp' ), filetype = 'cpp' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'C-family', 'servers': empty(), 'items': contains_exactly( has_entries( { 'key': 'compilation database path', 'value': 'None' } ), has_entries( { 'key': 'flags', 'value': matches_regexp( "\\['-x', 'c\\+\\+', .*\\]" ) } ), has_entries( { 'key': 'translation unit', 'value': PathToTestFile( 'basic.cpp' ) } ) ) } ) ) ) @SharedYcmd def DebugInfo_FlagsWhenNoExtraConfAndNoCompilationDatabase_test( app ): request_data = BuildRequest( filetype = 'cpp' ) # First request, FlagsForFile raises a NoExtraConfDetected exception. assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'C-family', 'servers': empty(), 'items': contains_exactly( has_entries( { 'key': 'compilation database path', 'value': 'None' } ), has_entries( { 'key': 'flags', 'value': '[]' } ), has_entries( { 'key': 'translation unit', 'value': instance_of( str ) } ) ) } ) ) ) # Second request, FlagsForFile returns None. assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'C-family', 'servers': empty(), 'items': contains_exactly( has_entries( { 'key': 'compilation database path', 'value': 'None' } ), has_entries( { 'key': 'flags', 'value': '[]' } ), has_entries( { 'key': 'translation unit', 'value': instance_of( str ) } ) ) } ) ) ) @IsolatedYcmd() def DebugInfo_FlagsWhenExtraConfNotLoadedAndNoCompilationDatabase_test( app ): request_data = BuildRequest( filepath = PathToTestFile( 'basic.cpp' ), filetype = 'cpp' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'C-family', 'servers': empty(), 'items': contains_exactly( has_entries( { 'key': 'compilation database path', 'value': 'None' } ), has_entries( { 'key': 'flags', 'value': '[]' } ), has_entries( { 'key': 'translation unit', 'value': PathToTestFile( 'basic.cpp' ) } ) ) } ) ) ) @IsolatedYcmd() def DebugInfo_FlagsWhenNoExtraConfAndCompilationDatabaseLoaded_test( app ): with TemporaryTestDir() as tmp_dir: compile_commands = [ { 'directory': tmp_dir, 'command': 'clang++ -I. -I/absolute/path -Wall', 'file': os.path.join( tmp_dir, 'test.cc' ), }, ] with TemporaryClangProject( tmp_dir, compile_commands ): request_data = BuildRequest( filepath = os.path.join( tmp_dir, 'test.cc' ), filetype = 'cpp' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'C-family', 'servers': empty(), 'items': contains_exactly( has_entries( { 'key': 'compilation database path', 'value': instance_of( str ) } ), has_entries( { 'key': 'flags', 'value': matches_regexp( "\\['clang\\+\\+', '-x', 'c\\+\\+', .*, '-Wall', .*\\]" ) } ), has_entries( { 'key': 'translation unit', 'value': os.path.join( tmp_dir, 'test.cc' ), } ) ) } ) ) ) @IsolatedYcmd() def DebugInfo_FlagsWhenNoExtraConfAndInvalidCompilationDatabase_test( app ): with TemporaryTestDir() as tmp_dir: compile_commands = 'garbage' with TemporaryClangProject( tmp_dir, compile_commands ): request_data = BuildRequest( filepath = os.path.join( tmp_dir, 'test.cc' ), filetype = 'cpp' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'C-family', 'servers': empty(), 'items': contains_exactly( has_entries( { 'key': 'compilation database path', 'value': 'None' } ), has_entries( { 'key': 'flags', 'value': '[]' } ), has_entries( { 'key': 'translation unit', 'value': os.path.join( tmp_dir, 'test.cc' ) } ) ) } ) ) ) @IsolatedYcmd( { 'global_ycm_extra_conf': PathToTestFile( '.ycm_extra_conf.py' ) } ) def DebugInfo_FlagsWhenGlobalExtraConfAndCompilationDatabaseLoaded_test( app ): with TemporaryTestDir() as tmp_dir: compile_commands = [ { 'directory': tmp_dir, 'command': 'clang++ -I. -I/absolute/path -Wall', 'file': os.path.join( tmp_dir, 'test.cc' ), }, ] with TemporaryClangProject( tmp_dir, compile_commands ): request_data = BuildRequest( filepath = os.path.join( tmp_dir, 'test.cc' ), filetype = 'cpp' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'C-family', 'servers': empty(), 'items': contains_exactly( has_entries( { 'key': 'compilation database path', 'value': instance_of( str ) } ), has_entries( { 'key': 'flags', 'value': matches_regexp( "\\['clang\\+\\+', '-x', 'c\\+\\+', .*, '-Wall', .*\\]" ) } ), has_entries( { 'key': 'translation unit', 'value': os.path.join( tmp_dir, 'test.cc' ), } ) ) } ) ) ) @IsolatedYcmd( { 'global_ycm_extra_conf': PathToTestFile( '.ycm_extra_conf.py' ) } ) def DebugInfo_FlagsWhenGlobalExtraConfAndNoCompilationDatabase_test( app ): request_data = BuildRequest( filepath = PathToTestFile( 'basic.cpp' ), filetype = 'cpp' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'C-family', 'servers': empty(), 'items': contains_exactly( has_entries( { 'key': 'compilation database path', 'value': 'None' } ), has_entries( { 'key': 'flags', 'value': matches_regexp( "\\['-x', 'c\\+\\+', .*\\]" ) } ), has_entries( { 'key': 'translation unit', 'value': PathToTestFile( 'basic.cpp' ) } ) ) } ) ) ) @SharedYcmd def DebugInfo_Unity_test( app ): # Main TU app.post_json( '/load_extra_conf_file', { 'filepath': PathToTestFile( '.ycm_extra_conf.py' ) } ) for filename in [ 'unity.cc', 'unity.h', 'unitya.cc' ]: request_data = BuildRequest( filepath = PathToTestFile( filename ), filetype = 'cpp' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'C-family', 'servers': empty(), 'items': contains_exactly( has_entries( { 'key': 'compilation database path', 'value': 'None' } ), has_entries( { 'key': 'flags', 'value': matches_regexp( "\\['-x', 'c\\+\\+', .*\\]" ) } ), has_entries( { 'key': 'translation unit', 'value': PathToTestFile( 'unity.cc' ) } ) ) } ) ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/diagnostics_test.py����������������������������������0000664�0000000�0000000�00000037035�13746324601�0024251�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_exactly, contains_inanyorder, contains_string, has_entries, has_entry, has_items, empty, equal_to ) from pprint import pprint from ycmd.tests.clang import SharedYcmd, IsolatedYcmd, PathToTestFile from ycmd.tests.test_utils import BuildRequest, LocationMatcher, RangeMatcher from ycmd.utils import ReadFile @IsolatedYcmd() def Diagnostics_ZeroBasedLineAndColumn_test( app ): contents = """ void foo() { double baz = "foo"; } // Padding to 5 lines // Padding to 5 lines """ event_data = BuildRequest( compilation_flags = [ '-x', 'c++' ], event_name = 'FileReadyToParse', contents = contents, filepath = 'foo', filetype = 'cpp' ) results = app.post_json( '/event_notification', event_data ).json assert_that( results, contains_exactly( has_entries( { 'kind': equal_to( 'ERROR' ), 'text': contains_string( 'cannot initialize' ), 'ranges': contains_exactly( RangeMatcher( 'foo', ( 3, 16 ), ( 3, 21 ) ) ), 'location': LocationMatcher( 'foo', 3, 10 ), 'location_extent': RangeMatcher( 'foo', ( 3, 10 ), ( 3, 13 ) ) } ) ) ) @IsolatedYcmd() def Diagnostics_SimpleLocationExtent_test( app ): contents = """ void foo() { baz = 5; } // Padding to 5 lines // Padding to 5 lines """ event_data = BuildRequest( compilation_flags = [ '-x', 'c++' ], event_name = 'FileReadyToParse', contents = contents, filepath = 'foo', filetype = 'cpp' ) results = app.post_json( '/event_notification', event_data ).json assert_that( results, contains_exactly( has_entries( { 'location_extent': RangeMatcher( 'foo', ( 3, 3 ), ( 3, 6 ) ) } ) ) ) @IsolatedYcmd() def Diagnostics_PragmaOnceWarningIgnored_test( app ): contents = """ #pragma once struct Foo { int x; int y; int c; int d; }; """ event_data = BuildRequest( compilation_flags = [ '-x', 'c++' ], event_name = 'FileReadyToParse', contents = contents, filepath = '/foo.h', filetype = 'cpp' ) response = app.post_json( '/event_notification', event_data ).json assert_that( response, empty() ) @IsolatedYcmd() def Diagnostics_Works_test( app ): contents = """ struct Foo { int x // semicolon missing here! int y; int c; int d; }; """ diag_data = BuildRequest( compilation_flags = [ '-x', 'c++' ], line_num = 3, contents = contents, filetype = 'cpp' ) event_data = diag_data.copy() event_data.update( { 'event_name': 'FileReadyToParse', } ) app.post_json( '/event_notification', event_data ) results = app.post_json( '/detailed_diagnostic', diag_data ).json assert_that( results, has_entry( 'message', contains_string( "expected ';'" ) ) ) @IsolatedYcmd() def Diagnostics_Multiline_test( app ): contents = """ struct Foo { Foo(int z) {} }; int main() { Foo foo("goo"); } """ diag_data = BuildRequest( compilation_flags = [ '-x', 'c++' ], line_num = 7, contents = contents, filetype = 'cpp' ) event_data = diag_data.copy() event_data.update( { 'event_name': 'FileReadyToParse', } ) app.post_json( '/event_notification', event_data ) results = app.post_json( '/detailed_diagnostic', diag_data ).json assert_that( results, has_entry( 'message', contains_string( "\n" ) ) ) @IsolatedYcmd() def Diagnostics_FixIt_Available_test( app ): filepath = PathToTestFile( 'FixIt_Clang_cpp11.cpp' ) event_data = BuildRequest( contents = ReadFile( filepath ), event_name = 'FileReadyToParse', filepath = filepath, filetype = 'cpp', compilation_flags = [ '-x', 'c++', '-std=c++03', '-Wall', '-Wextra', '-pedantic' ] ) response = app.post_json( '/event_notification', event_data ).json pprint( response ) assert_that( response, has_items( has_entries( { 'location': LocationMatcher( filepath, 16, 3 ), 'text': equal_to( 'switch condition type \'A\' ' 'requires explicit conversion to \'int\'' ), 'fixit_available': True } ), has_entries( { 'location': LocationMatcher( filepath, 11, 3 ), 'text': equal_to( 'explicit conversion functions are a C++11 extension' ), 'fixit_available': False } ), ) ) @IsolatedYcmd() def Diagnostics_MultipleMissingIncludes_test( app ): filepath = PathToTestFile( 'multiple_missing_includes.cc' ) event_data = BuildRequest( contents = ReadFile( filepath ), event_name = 'FileReadyToParse', filepath = filepath, filetype = 'cpp', compilation_flags = [ '-x', 'c++' ] ) response = app.post_json( '/event_notification', event_data ).json pprint( response ) assert_that( response, has_items( has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 1, 10 ), 'text': equal_to( "'first_missing_include' file not found" ), 'fixit_available': False } ), has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 2, 10 ), 'text': equal_to( "'second_missing_include' file not found" ), 'fixit_available': False } ), ) ) @IsolatedYcmd() def Diagnostics_LocationExtent_MissingSemicolon_test( app ): filepath = PathToTestFile( 'location_extent.cc' ) event_data = BuildRequest( contents = ReadFile( filepath ), event_name = 'FileReadyToParse', filepath = filepath, filetype = 'cpp', compilation_flags = [ '-x', 'c++' ] ) response = app.post_json( '/event_notification', event_data ).json pprint( response ) assert_that( response, contains_exactly( has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 2, 9 ), 'location_extent': RangeMatcher( filepath, ( 2, 9 ), ( 2, 9 ) ), 'ranges': empty(), 'text': equal_to( "expected ';' at end of declaration list" ), 'fixit_available': True } ), has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 5, 1 ), 'location_extent': RangeMatcher( filepath, ( 5, 1 ), ( 6, 11 ) ), 'ranges': empty(), 'text': equal_to( "unknown type name 'multiline_identifier'" ), 'fixit_available': False } ), has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 8, 7 ), 'location_extent': RangeMatcher( filepath, ( 8, 7 ), ( 8, 11 ) ), 'ranges': contains_exactly( # FIXME: empty ranges from libclang should be ignored. RangeMatcher( '', ( 0, 0 ), ( 0, 0 ) ), RangeMatcher( filepath, ( 8, 7 ), ( 8, 11 ) ) ), 'text': equal_to( 'constructor cannot have a return type' ), 'fixit_available': False } ) ) ) @SharedYcmd def Diagnostics_Unity_test( app ): app.post_json( '/load_extra_conf_file', { 'filepath': PathToTestFile( '.ycm_extra_conf.py' ) } ) for filename in [ 'unity.cc', 'unity.h', 'unitya.cc' ]: contents = ReadFile( PathToTestFile( filename ) ) event_data = BuildRequest( filepath = PathToTestFile( filename ), contents = contents, event_name = 'FileReadyToParse', filetype = 'cpp' ) response = app.post_json( '/event_notification', event_data ).json pprint( response ) assert_that( response, contains_inanyorder( has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( PathToTestFile( 'unity.h' ), 4, 3 ), 'location_extent': RangeMatcher( PathToTestFile( 'unity.h' ), ( 4, 3 ), ( 4, 14 ) ), 'ranges': empty(), 'text': equal_to( "use of undeclared identifier 'fake_method'" ), 'fixit_available': False } ), has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( PathToTestFile( 'unitya.cc' ), 11, 18 ), 'location_extent': RangeMatcher( PathToTestFile( 'unitya.cc' ), ( 11, 18 ), ( 11, 18 ) ), 'ranges': empty(), 'text': equal_to( "expected ';' after expression" ), 'fixit_available': True } ), ) ) @SharedYcmd def Diagnostics_CUDA_Kernel_test( app ): filepath = PathToTestFile( 'cuda', 'kernel_call.cu' ) event_data = BuildRequest( filepath = filepath, contents = ReadFile( filepath ), event_name = 'FileReadyToParse', filetype = 'cuda', compilation_flags = [ '-x', 'cuda', '-nocudainc', '-nocudalib' ] ) response = app.post_json( '/event_notification', event_data ).json pprint( response ) assert_that( response, contains_inanyorder( has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 59, 5 ), 'location_extent': RangeMatcher( filepath, ( 59, 5 ), ( 59, 6 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 59, 3 ), ( 59, 5 ) ) ), 'text': equal_to( "call to global function 'g1' not configured" ), 'fixit_available': False } ), has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 60, 9 ), 'location_extent': RangeMatcher( filepath, ( 60, 9 ), ( 60, 12 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 60, 5 ), ( 60, 8 ) ) ), 'text': equal_to( 'too few execution configuration arguments to kernel function call, ' 'expected at least 2, have 1' ), 'fixit_available': False } ), has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 61, 20 ), 'location_extent': RangeMatcher( filepath, ( 61, 20 ), ( 61, 21 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 61, 5 ), ( 61, 8 ) ), RangeMatcher( filepath, ( 61, 20 ), ( 61, 21 ) ) ), 'text': equal_to( 'too many execution configuration arguments to kernel ' 'function call, expected at most 4, have 5' ), 'fixit_available': False } ), has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 65, 15 ), 'location_extent': RangeMatcher( filepath, ( 65, 15 ), ( 65, 16 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 65, 3 ), ( 65, 5 ) ) ), 'text': equal_to( "kernel call to non-global function 'h1'" ), 'fixit_available': False } ), has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 68, 15 ), 'location_extent': RangeMatcher( filepath, ( 68, 15 ), ( 68, 16 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 68, 3 ), ( 68, 5 ) ) ), 'text': equal_to( "kernel function type 'int (*)(int)' must have " "void return type" ), 'fixit_available': False } ), has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 70, 8 ), 'location_extent': RangeMatcher( filepath, ( 70, 8 ), ( 70, 18 ) ), 'ranges': empty(), 'text': equal_to( "use of undeclared identifier 'undeclared'" ), 'fixit_available': False } ), ) ) @IsolatedYcmd( { 'max_diagnostics_to_display': 1 } ) def Diagnostics_MaximumDiagnosticsNumberExceeded_test( app ): filepath = PathToTestFile( 'max_diagnostics.cc' ) contents = ReadFile( filepath ) event_data = BuildRequest( contents = contents, event_name = 'FileReadyToParse', filetype = 'cpp', filepath = filepath, compilation_flags = [ '-x', 'c++' ] ) response = app.post_json( '/event_notification', event_data ).json pprint( response ) assert_that( response, contains_exactly( has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 3, 9 ), 'location_extent': RangeMatcher( filepath, ( 3, 9 ), ( 3, 13 ) ), 'ranges': empty(), 'text': equal_to( "redefinition of 'test'" ), 'fixit_available': False } ), has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 1, 1 ), 'location_extent': RangeMatcher( filepath, ( 1, 1 ), ( 1, 1 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 1, 1 ), ( 1, 1 ) ) ), 'text': equal_to( 'Maximum number of diagnostics exceeded.' ), 'fixit_available': False } ) ) ) @IsolatedYcmd( { 'max_diagnostics_to_display': 0 } ) def Diagnostics_NoLimitToNumberOfDiagnostics_test( app ): filepath = PathToTestFile( 'max_diagnostics.cc' ) contents = ReadFile( filepath ) event_data = BuildRequest( contents = contents, event_name = 'FileReadyToParse', filetype = 'cpp', filepath = filepath, compilation_flags = [ '-x', 'c++' ] ) response = app.post_json( '/event_notification', event_data ).json pprint( response ) assert_that( response, contains_exactly( has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 3, 9 ), 'location_extent': RangeMatcher( filepath, ( 3, 9 ), ( 3, 13 ) ), 'ranges': empty(), 'text': equal_to( "redefinition of 'test'" ), 'fixit_available': False } ), has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 4, 9 ), 'location_extent': RangeMatcher( filepath, ( 4, 9 ), ( 4, 13 ) ), 'ranges': empty(), 'text': equal_to( "redefinition of 'test'" ), 'fixit_available': False } ) ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/flags_test.py����������������������������������������0000664�0000000�0000000�00000144511�13746324601�0023034�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2011-2020 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 <http://www.gnu.org/licenses/>. import contextlib import os import pytest from hamcrest import ( assert_that, calling, contains_exactly, empty, equal_to, raises ) from unittest.mock import patch, MagicMock from types import ModuleType from ycmd.completers.cpp import flags from ycmd.completers.cpp.flags import ShouldAllowWinStyleFlags, INCLUDE_FLAGS from ycmd.tests.test_utils import ( MacOnly, TemporaryTestDir, WindowsOnly, TemporaryClangProject ) from ycmd.utils import CLANG_RESOURCE_DIR from ycmd.responses import NoExtraConfDetected @contextlib.contextmanager def MockExtraConfModule( settings_function ): module = MagicMock( spec = ModuleType ) module.is_global_ycm_extra_conf = False setattr( module, settings_function.__name__, settings_function ) with patch( 'ycmd.extra_conf_store.ModuleForSourceFile', return_value = module ): yield def FlagsForFile_NothingReturned_test(): flags_object = flags.Flags() def Settings( **kwargs ): pass with MockExtraConfModule( Settings ): flags_list, filename = flags_object.FlagsForFile( '/foo' ) assert_that( flags_list, empty() ) assert_that( filename, equal_to( '/foo' ) ) def FlagsForFile_FlagsNotReady_test(): flags_object = flags.Flags() def Settings( **kwargs ): return { 'flags': [], 'flags_ready': False } with MockExtraConfModule( Settings ): flags_list, filename = flags_object.FlagsForFile( '/foo', False ) assert_that( list( flags_list ), equal_to( [] ) ) assert_that( filename, equal_to( '/foo' ) ) def FlagsForFile_BadNonUnicodeFlagsAreAlsoRemoved_test( *args ): flags_object = flags.Flags() def Settings( **kwargs ): return { 'flags': [ bytes( b'-c' ), '-c', bytes( b'-foo' ), '-bar' ] } with MockExtraConfModule( Settings ): flags_list, _ = flags_object.FlagsForFile( '/foo', False ) assert_that( list( flags_list ), equal_to( [ '-foo', '-bar' ] ) ) def FlagsForFile_FlagsCachedByDefault_test(): flags_object = flags.Flags() def Settings( **kwargs ): return { 'flags': [ '-x', 'c' ] } with MockExtraConfModule( Settings ): flags_list, _ = flags_object.FlagsForFile( '/foo', False ) assert_that( flags_list, contains_exactly( '-x', 'c' ) ) def Settings( **kwargs ): return { 'flags': [ '-x', 'c++' ] } with MockExtraConfModule( Settings ): flags_list, _ = flags_object.FlagsForFile( '/foo', False ) assert_that( flags_list, contains_exactly( '-x', 'c' ) ) def FlagsForFile_FlagsNotCachedWhenDoCacheIsFalse_test(): flags_object = flags.Flags() def Settings( **kwargs ): return { 'flags': [ '-x', 'c' ], 'do_cache': False } with MockExtraConfModule( Settings ): flags_list, _ = flags_object.FlagsForFile( '/foo', False ) assert_that( flags_list, contains_exactly( '-x', 'c' ) ) def Settings( **kwargs ): return { 'flags': [ '-x', 'c++' ] } with MockExtraConfModule( Settings ): flags_list, _ = flags_object.FlagsForFile( '/foo', False ) assert_that( flags_list, contains_exactly( '-x', 'c++' ) ) def FlagsForFile_FlagsCachedWhenDoCacheIsTrue_test(): flags_object = flags.Flags() def Settings( **kwargs ): return { 'flags': [ '-x', 'c' ], 'do_cache': True } with MockExtraConfModule( Settings ): flags_list, _ = flags_object.FlagsForFile( '/foo', False ) assert_that( flags_list, contains_exactly( '-x', 'c' ) ) def Settings( **kwargs ): return { 'flags': [ '-x', 'c++' ] } with MockExtraConfModule( Settings ): flags_list, _ = flags_object.FlagsForFile( '/foo', False ) assert_that( flags_list, contains_exactly( '-x', 'c' ) ) def FlagsForFile_DoNotMakeRelativePathsAbsoluteByDefault_test(): flags_object = flags.Flags() def Settings( **kwargs ): return { 'flags': [ '-x', 'c', '-I', 'header' ] } with MockExtraConfModule( Settings ): flags_list, _ = flags_object.FlagsForFile( '/foo', False ) assert_that( flags_list, contains_exactly( '-x', 'c', '-I', 'header' ) ) def FlagsForFile_MakeRelativePathsAbsoluteIfOptionSpecified_test(): flags_object = flags.Flags() def Settings( **kwargs ): return { 'flags': [ '-x', 'c', '-I', 'header' ], 'include_paths_relative_to_dir': '/working_dir/' } with MockExtraConfModule( Settings ): flags_list, _ = flags_object.FlagsForFile( '/foo', False ) assert_that( flags_list, contains_exactly( '-x', 'c', '-I', os.path.normpath( '/working_dir/header' ) ) ) @MacOnly @patch( 'os.path.exists', lambda path: path == '/System/Library/Frameworks/Foundation.framework/Headers' ) def FlagsForFile_AddMacIncludePaths_SysRoot_Default_test(): flags_object = flags.Flags() def Settings( **kwargs ): return { 'flags': [ '-Wall' ] } with MockExtraConfModule( Settings ): flags_list, _ = flags_object.FlagsForFile( '/foo' ) assert_that( flags_list, contains_exactly( '-Wall', '-resource-dir=' + CLANG_RESOURCE_DIR, '-isystem', '/usr/include/c++/v1', '-isystem', '/usr/local/include', '-isystem', os.path.join( CLANG_RESOURCE_DIR, 'include' ), '-isystem', '/usr/include', '-iframework', '/System/Library/Frameworks', '-iframework', '/Library/Frameworks', '-fspell-checking' ) ) @MacOnly @patch( 'os.path.exists', lambda path: path == '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform' '/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks' '/Foundation.framework/Headers' ) def FlagsForFile_AddMacIncludePaths_SysRoot_Xcode_test(): flags_object = flags.Flags() def Settings( **kwargs ): return { 'flags': [ '-Wall' ] } with MockExtraConfModule( Settings ): flags_list, _ = flags_object.FlagsForFile( '/foo' ) assert_that( flags_list, contains_exactly( '-Wall', '-resource-dir=' + CLANG_RESOURCE_DIR, '-isystem', '/Applications/Xcode.app/Contents/Developer/Platforms' '/MacOSX.platform/Developer/SDKs/MacOSX.sdk' '/usr/include/c++/v1', '-isystem', '/Applications/Xcode.app/Contents/Developer/Platforms' '/MacOSX.platform/Developer/SDKs/MacOSX.sdk' '/usr/local/include', '-isystem', '/usr/local/include', '-isystem', os.path.join( CLANG_RESOURCE_DIR, 'include' ), '-isystem', '/Applications/Xcode.app/Contents/Developer/Platforms' '/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include', '-iframework', '/Applications/Xcode.app/Contents/Developer/Platforms' '/MacOSX.platform/Developer/SDKs/MacOSX.sdk' '/System/Library/Frameworks', '-iframework', '/Applications/Xcode.app/Contents/Developer/Platforms' '/MacOSX.platform/Developer/SDKs/MacOSX.sdk' '/Library/Frameworks', '-fspell-checking' ) ) @MacOnly @patch( 'os.path.exists', lambda path: path == '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk' '/System/Library/Frameworks/Foundation.framework/Headers' ) def FlagsForFile_AddMacIncludePaths_SysRoot_CommandLine_test(): flags_object = flags.Flags() def Settings( **kwargs ): return { 'flags': [ '-Wall' ] } with MockExtraConfModule( Settings ): flags_list, _ = flags_object.FlagsForFile( '/foo' ) assert_that( flags_list, contains_exactly( '-Wall', '-resource-dir=' + CLANG_RESOURCE_DIR, '-isystem', '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk' '/usr/include/c++/v1', '-isystem', '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk' '/usr/local/include', '-isystem', '/usr/local/include', '-isystem', os.path.join( CLANG_RESOURCE_DIR, 'include' ), '-isystem', '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk' '/usr/include', '-iframework', '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk' '/System/Library/Frameworks', '-iframework', '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk' '/Library/Frameworks', '-fspell-checking' ) ) @MacOnly @patch( 'os.path.exists', lambda path: False ) def FlagsForFile_AddMacIncludePaths_Sysroot_Custom_test(): flags_object = flags.Flags() def Settings( **kwargs ): return { 'flags': [ '-Wall', '-isysroot/path/to/first/sys/root', '-isysroot', '/path/to/second/sys/root/', '--sysroot=/path/to/third/sys/root', '--sysroot', '/path/to/fourth/sys/root' ] } with MockExtraConfModule( Settings ): flags_list, _ = flags_object.FlagsForFile( '/foo' ) assert_that( flags_list, contains_exactly( '-Wall', '-isysroot/path/to/first/sys/root', '-isysroot', '/path/to/second/sys/root/', '--sysroot=/path/to/third/sys/root', '--sysroot', '/path/to/fourth/sys/root', '-resource-dir=' + CLANG_RESOURCE_DIR, '-isystem', '/path/to/second/sys/root/usr/include/c++/v1', '-isystem', '/path/to/second/sys/root/usr/local/include', '-isystem', '/usr/local/include', '-isystem', os.path.join( CLANG_RESOURCE_DIR, 'include' ), '-isystem', '/path/to/second/sys/root/usr/include', '-iframework', '/path/to/second/sys/root/System/Library/Frameworks', '-iframework', '/path/to/second/sys/root/Library/Frameworks', '-fspell-checking' ) ) @MacOnly @patch( 'os.path.exists', lambda path: path == '/Applications/Xcode.app/Contents/Developer/Toolchains/' 'XcodeDefault.xctoolchain' ) def FlagsForFile_AddMacIncludePaths_Toolchain_Xcode_test(): flags_object = flags.Flags() def Settings( **kwargs ): return { 'flags': [ '-Wall' ] } with MockExtraConfModule( Settings ): flags_list, _ = flags_object.FlagsForFile( '/foo' ) assert_that( flags_list, contains_exactly( '-Wall', '-resource-dir=' + CLANG_RESOURCE_DIR, '-isystem', '/Applications/Xcode.app/Contents/Developer/Toolchains' '/XcodeDefault.xctoolchain/usr/include/c++/v1', '-isystem', '/usr/include/c++/v1', '-isystem', '/usr/local/include', '-isystem', os.path.join( CLANG_RESOURCE_DIR, 'include' ), '-isystem', '/Applications/Xcode.app/Contents/Developer/Toolchains' '/XcodeDefault.xctoolchain/usr/include', '-isystem', '/usr/include', '-iframework', '/System/Library/Frameworks', '-iframework', '/Library/Frameworks', '-fspell-checking' ) ) @MacOnly @patch( 'os.path.exists', lambda path: path == '/Library/Developer/CommandLineTools' ) def FlagsForFile_AddMacIncludePaths_Toolchain_CommandLine_test(): flags_object = flags.Flags() def Settings( **kwargs ): return { 'flags': [ '-Wall' ] } with MockExtraConfModule( Settings ): flags_list, _ = flags_object.FlagsForFile( '/foo' ) assert_that( flags_list, contains_exactly( '-Wall', '-resource-dir=' + CLANG_RESOURCE_DIR, '-isystem', '/Library/Developer/CommandLineTools/usr/include/c++/v1', '-isystem', '/usr/include/c++/v1', '-isystem', '/usr/local/include', '-isystem', os.path.join( CLANG_RESOURCE_DIR, 'include' ), '-isystem', '/Library/Developer/CommandLineTools/usr/include', '-isystem', '/usr/include', '-iframework', '/System/Library/Frameworks', '-iframework', '/Library/Frameworks', '-fspell-checking' ) ) @MacOnly @patch( 'os.path.exists', lambda path: False ) def FlagsForFile_AddMacIncludePaths_ObjCppLanguage_test(): flags_object = flags.Flags() def Settings( **kwargs ): return { 'flags': [ '-Wall', '-x', 'c', '-xobjective-c++' ] } with MockExtraConfModule( Settings ): flags_list, _ = flags_object.FlagsForFile( '/foo' ) assert_that( flags_list, contains_exactly( '-Wall', '-x', 'c', '-xobjective-c++', '-resource-dir=' + CLANG_RESOURCE_DIR, '-isystem', '/usr/include/c++/v1', '-isystem', '/usr/local/include', '-isystem', os.path.join( CLANG_RESOURCE_DIR, 'include' ), '-isystem', '/usr/include', '-iframework', '/System/Library/Frameworks', '-iframework', '/Library/Frameworks', '-fspell-checking' ) ) @MacOnly @patch( 'os.path.exists', lambda path: False ) def FlagsForFile_AddMacIncludePaths_CppLanguage_test(): flags_object = flags.Flags() def Settings( **kwargs ): return { 'flags': [ '-Wall', '-x', 'c', '-xc++' ] } with MockExtraConfModule( Settings ): flags_list, _ = flags_object.FlagsForFile( '/foo' ) assert_that( flags_list, contains_exactly( '-Wall', '-x', 'c', '-xc++', '-resource-dir=' + CLANG_RESOURCE_DIR, '-isystem', '/usr/include/c++/v1', '-isystem', '/usr/local/include', '-isystem', os.path.join( CLANG_RESOURCE_DIR, 'include' ), '-isystem', '/usr/include', '-iframework', '/System/Library/Frameworks', '-iframework', '/Library/Frameworks', '-fspell-checking' ) ) @MacOnly @patch( 'os.path.exists', lambda path: False ) def FlagsForFile_AddMacIncludePaths_CLanguage_test(): flags_object = flags.Flags() def Settings( **kwargs ): return { 'flags': [ '-Wall', '-xc++', '-xc' ] } with MockExtraConfModule( Settings ): flags_list, _ = flags_object.FlagsForFile( '/foo' ) assert_that( flags_list, contains_exactly( '-Wall', '-xc++', '-xc', '-resource-dir=' + CLANG_RESOURCE_DIR, '-isystem', '/usr/local/include', '-isystem', os.path.join( CLANG_RESOURCE_DIR, 'include' ), '-isystem', '/usr/include', '-iframework', '/System/Library/Frameworks', '-iframework', '/Library/Frameworks', '-fspell-checking' ) ) @MacOnly @patch( 'os.path.exists', lambda path: False ) def FlagsForFile_AddMacIncludePaths_NoLibCpp_test(): flags_object = flags.Flags() def Settings( **kwargs ): return { 'flags': [ '-Wall', '-stdlib=libc++', '-stdlib=libstdc++' ] } with MockExtraConfModule( Settings ): flags_list, _ = flags_object.FlagsForFile( '/foo' ) assert_that( flags_list, contains_exactly( '-Wall', '-stdlib=libc++', '-stdlib=libstdc++', '-resource-dir=' + CLANG_RESOURCE_DIR, '-isystem', '/usr/local/include', '-isystem', os.path.join( CLANG_RESOURCE_DIR, 'include' ), '-isystem', '/usr/include', '-iframework', '/System/Library/Frameworks', '-iframework', '/Library/Frameworks', '-fspell-checking' ) ) @MacOnly @patch( 'os.path.exists', lambda path: False ) def FlagsForFile_AddMacIncludePaths_NoStandardCppIncludes_test(): flags_object = flags.Flags() def Settings( **kwargs ): return { 'flags': [ '-Wall', '-nostdinc++' ] } with MockExtraConfModule( Settings ): flags_list, _ = flags_object.FlagsForFile( '/foo' ) assert_that( flags_list, contains_exactly( '-Wall', '-nostdinc++', '-resource-dir=' + CLANG_RESOURCE_DIR, '-isystem', '/usr/local/include', '-isystem', os.path.join( CLANG_RESOURCE_DIR, 'include' ), '-isystem', '/usr/include', '-iframework', '/System/Library/Frameworks', '-iframework', '/Library/Frameworks', '-fspell-checking' ) ) @MacOnly @patch( 'os.path.exists', lambda path: False ) def FlagsForFile_AddMacIncludePaths_NoStandardSystemIncludes_test(): flags_object = flags.Flags() def Settings( **kwargs ): return { 'flags': [ '-Wall', '-nostdinc' ] } with MockExtraConfModule( Settings ): flags_list, _ = flags_object.FlagsForFile( '/foo' ) assert_that( flags_list, contains_exactly( '-Wall', '-nostdinc', '-resource-dir=' + CLANG_RESOURCE_DIR, '-isystem', os.path.join( CLANG_RESOURCE_DIR, 'include' ), '-fspell-checking' ) ) @MacOnly @patch( 'os.path.exists', lambda path: False ) def FlagsForFile_AddMacIncludePaths_NoBuiltinIncludes_test(): flags_object = flags.Flags() def Settings( **kwargs ): return { 'flags': [ '-Wall', '-nobuiltininc' ] } with MockExtraConfModule( Settings ): flags_list, _ = flags_object.FlagsForFile( '/foo' ) assert_that( flags_list, contains_exactly( '-Wall', '-nobuiltininc', '-resource-dir=' + CLANG_RESOURCE_DIR, '-isystem', '/usr/include/c++/v1', '-isystem', '/usr/local/include', '-isystem', '/usr/include', '-iframework', '/System/Library/Frameworks', '-iframework', '/Library/Frameworks', '-fspell-checking' ) ) def FlagsForFile_OverrideTranslationUnit_test(): flags_object = flags.Flags() def Settings( **kwargs ): return { 'flags': [], 'override_filename': 'changed:' + kwargs[ 'filename' ] } with MockExtraConfModule( Settings ): flags_list, filename = flags_object.FlagsForFile( '/foo' ) assert_that( flags_list, contains_exactly() ) assert_that( filename, equal_to( 'changed:/foo' ) ) def Settings( **kwargs ): return { 'flags': [], 'override_filename': kwargs[ 'filename' ] } with MockExtraConfModule( Settings ): flags_list, filename = flags_object.FlagsForFile( '/foo' ) assert_that( flags_list, contains_exactly() ) assert_that( filename, equal_to( '/foo' ) ) def Settings( **kwargs ): return { 'flags': [], 'override_filename': None } with MockExtraConfModule( Settings ): flags_list, filename = flags_object.FlagsForFile( '/foo' ) assert_that( flags_list, contains_exactly() ) assert_that( filename, equal_to( '/foo' ) ) def Settings( **kwargs ): return { 'flags': [], } with MockExtraConfModule( Settings ): flags_list, filename = flags_object.FlagsForFile( '/foo' ) assert_that( flags_list, contains_exactly() ) assert_that( filename, equal_to( '/foo' ) ) def Settings( **kwargs ): return { 'flags': [], 'override_filename': '' } with MockExtraConfModule( Settings ): flags_list, filename = flags_object.FlagsForFile( '/foo' ) assert_that( flags_list, contains_exactly() ) assert_that( filename, equal_to( '/foo' ) ) def Settings( **kwargs ): return { 'flags': [], 'override_filename': '0' } with MockExtraConfModule( Settings ): flags_list, filename = flags_object.FlagsForFile( '/foo' ) assert_that( flags_list, contains_exactly() ) assert_that( filename, equal_to( '0' ) ) def FlagsForFile_Compatibility_KeywordArguments_test(): flags_object = flags.Flags() def FlagsForFile( filename, **kwargs ): return { 'flags': [ '-x', 'c' ] } with MockExtraConfModule( FlagsForFile ): flags_list, _ = flags_object.FlagsForFile( '/foo', False ) assert_that( flags_list, contains_exactly( '-x', 'c' ) ) def FlagsForFile_Compatibility_NoKeywordArguments_test(): flags_object = flags.Flags() def FlagsForFile( filename ): return { 'flags': [ '-x', 'c' ] } with MockExtraConfModule( FlagsForFile ): flags_list, _ = flags_object.FlagsForFile( '/foo', False ) assert_that( flags_list, contains_exactly( '-x', 'c' ) ) def RemoveUnusedFlags_Passthrough_test(): compiler_flags = [ '-foo', '-bar' ] assert_that( flags.RemoveUnusedFlags( compiler_flags, 'file', ShouldAllowWinStyleFlags( compiler_flags ) ), contains_exactly( '-foo', '-bar' ) ) def RemoveUnusedFlags_RemoveDashC_test(): expected = [ '-foo', '-bar' ] to_remove = [ '-c' ] filename = 'file' assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected + to_remove, filename, ShouldAllowWinStyleFlags( expected + to_remove ) ) ) ) assert_that( expected, equal_to( flags.RemoveUnusedFlags( to_remove + expected, filename, ShouldAllowWinStyleFlags( to_remove + expected ) ) ) ) assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected[ :1 ] + to_remove + expected[ -1: ], filename, ShouldAllowWinStyleFlags( expected[ :1 ] + to_remove + expected[ -1: ] ) ) ) ) def RemoveUnusedFlags_RemoveColor_test(): expected = [ '-foo', '-bar' ] to_remove = [ '--fcolor-diagnostics' ] filename = 'file' assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected + to_remove, filename, ShouldAllowWinStyleFlags( expected + to_remove ) ) ) ) assert_that( expected, equal_to( flags.RemoveUnusedFlags( to_remove + expected, filename, ShouldAllowWinStyleFlags( to_remove + expected ) ) ) ) assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected[ :1 ] + to_remove + expected[ -1: ], filename, ShouldAllowWinStyleFlags( expected[ :1 ] + to_remove + expected[ -1: ] ) ) ) ) def RemoveUnusedFlags_RemoveDashO_test(): expected = [ '-foo', '-bar' ] to_remove = [ '-o', 'output_name' ] filename = 'file' assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected + to_remove, filename, ShouldAllowWinStyleFlags( expected + to_remove ) ) ) ) assert_that( expected, equal_to( flags.RemoveUnusedFlags( to_remove + expected, filename, ShouldAllowWinStyleFlags( to_remove + expected ) ) ) ) assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected[ :1 ] + to_remove + expected[ -1: ], filename, ShouldAllowWinStyleFlags( expected[ :1 ] + to_remove + expected[ -1: ] ) ) ) ) def RemoveUnusedFlags_RemoveMP_test(): expected = [ '-foo', '-bar' ] to_remove = [ '-MP' ] filename = 'file' assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected + to_remove, filename, ShouldAllowWinStyleFlags( expected + to_remove ) ) ) ) assert_that( expected, equal_to( flags.RemoveUnusedFlags( to_remove + expected, filename, ShouldAllowWinStyleFlags( to_remove + expected ) ) ) ) assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected[ :1 ] + to_remove + expected[ -1: ], filename, ShouldAllowWinStyleFlags( expected[ :1 ] + to_remove + expected[ -1: ] ) ) ) ) def RemoveUnusedFlags_RemoveFilename_test(): expected = [ 'foo', '-bar' ] to_remove = [ 'file' ] filename = 'file' assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected + to_remove, filename, ShouldAllowWinStyleFlags( expected + to_remove ) ) ) ) assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected[ :1 ] + to_remove + expected[ 1: ], filename, ShouldAllowWinStyleFlags( expected[ :1 ] + to_remove + expected[ 1: ] ) ) ) ) assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected[ :1 ] + to_remove + expected[ -1: ], filename, ShouldAllowWinStyleFlags( expected[ :1 ] + to_remove + expected[ -1: ] ) ) ) ) def RemoveUnusedFlags_RemoveFlagWithoutPrecedingDashFlag_test(): expected = [ 'g++', '-foo', '-x', 'c++', '-bar', 'include_dir' ] to_remove = [ 'unrelated_file' ] filename = 'file' assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected + to_remove, filename, ShouldAllowWinStyleFlags( expected + to_remove ) ) ) ) assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected[ :1 ] + to_remove + expected[ 1: ], filename, ShouldAllowWinStyleFlags( expected[ :1 ] + to_remove + expected[ 1: ] ) ) ) ) @WindowsOnly def RemoveUnusedFlags_RemoveStrayFilenames_CLDriver_test(): # Only --driver-mode=cl specified. expected = [ 'g++', '-foo', '--driver-mode=cl', '-xc++', '-bar', 'include_dir', '/I', 'include_dir_other' ] to_remove = [ '..' ] filename = 'file' assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected + to_remove, filename, ShouldAllowWinStyleFlags( expected + to_remove ) ) ) ) assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected[ :1 ] + to_remove + expected[ 1: ], filename, ShouldAllowWinStyleFlags( expected[ :1 ] + to_remove + expected[ 1: ] ) ) ) ) # clang-cl and --driver-mode=cl expected = [ 'clang-cl.exe', '-foo', '--driver-mode=cl', '-xc++', '-bar', 'include_dir', '/I', 'include_dir_other' ] to_remove = [ 'unrelated_file' ] filename = 'file' assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected + to_remove, filename, ShouldAllowWinStyleFlags( expected + to_remove ) ) ) ) assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected[ :1 ] + to_remove + expected[ 1: ], filename, ShouldAllowWinStyleFlags( expected[ :1 ] + to_remove + expected[ 1: ] ) ) ) ) # clang-cl only expected = [ 'clang-cl.exe', '-foo', '-xc++', '-bar', 'include_dir', '/I', 'include_dir_other' ] to_remove = [ 'unrelated_file' ] filename = 'file' assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected + to_remove, filename, ShouldAllowWinStyleFlags( expected + to_remove ) ) ) ) assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected[ :1 ] + to_remove + expected[ 1: ], filename, ShouldAllowWinStyleFlags( expected[ :1 ] + to_remove + expected[ 1: ] ) ) ) ) # clang-cl and --driver-mode=gcc expected = [ 'clang-cl', '-foo', '-xc++', '--driver-mode=gcc', '-bar', 'include_dir' ] to_remove = [ 'unrelated_file', '/I', 'include_dir_other' ] filename = 'file' assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected + to_remove, filename, ShouldAllowWinStyleFlags( expected + to_remove ) ) ) ) assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected[ :1 ] + to_remove + expected[ 1: ], filename, ShouldAllowWinStyleFlags( expected[ :1 ] + to_remove + expected[ 1: ] ) ) ) ) # cl only with extension expected = [ 'cl.EXE', '-foo', '-xc++', '-bar', 'include_dir' ] to_remove = [ '-c', 'path\\to\\unrelated_file' ] filename = 'file' assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected + to_remove, filename, ShouldAllowWinStyleFlags( expected + to_remove ) ) ) ) assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected[ :1 ] + to_remove + expected[ 1: ], filename, ShouldAllowWinStyleFlags( expected[ :1 ] + to_remove + expected[ 1: ] ) ) ) ) # cl path with Windows separators expected = [ 'path\\to\\cl', '-foo', '-xc++', '/I', 'path\\to\\include\\dir' ] to_remove = [ '-c', 'path\\to\\unrelated_file' ] filename = 'file' assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected + to_remove, filename, ShouldAllowWinStyleFlags( expected + to_remove ) ) ) ) assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected[ :1 ] + to_remove + expected[ 1: ], filename, ShouldAllowWinStyleFlags( expected[ :1 ] + to_remove + expected[ 1: ] ) ) ) ) @WindowsOnly def RemoveUnusedFlags_MultipleDriverModeFlagsWindows_test(): expected = [ 'g++', '--driver-mode=cl', '/Zi', '-foo', '--driver-mode=gcc', '--driver-mode=cl', 'include_dir' ] to_remove = [ 'unrelated_file', '/c' ] filename = 'file' assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected + to_remove, filename, ShouldAllowWinStyleFlags( expected + to_remove ) ) ) ) assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected[ :1 ] + to_remove + expected[ 1: ], filename, ShouldAllowWinStyleFlags( expected[ :1 ] + to_remove + expected[ 1: ] ) ) ) ) flags_expected = [ '/usr/bin/g++', '--driver-mode=cl', '--driver-mode=gcc' ] flags_all = [ '/usr/bin/g++', '/Zi', '--driver-mode=cl', '/foo', '--driver-mode=gcc' ] filename = 'file' assert_that( flags_expected, equal_to( flags.RemoveUnusedFlags( flags_all, filename, ShouldAllowWinStyleFlags( flags_all ) ) ) ) def RemoveUnusedFlags_Depfiles_test(): full_flags = [ '/bin/clang', '-x', 'objective-c', '-arch', 'armv7', '-MMD', '-MT', 'dependencies', '-MF', 'file', '--serialize-diagnostics', 'diagnostics' ] expected = [ '/bin/clang', '-x', 'objective-c', '-arch', 'armv7', ] assert_that( flags.RemoveUnusedFlags( full_flags, 'test.m', ShouldAllowWinStyleFlags( full_flags ) ), contains_exactly( *expected ) ) def EnableTypoCorrection_Empty_test(): assert_that( flags._EnableTypoCorrection( [] ), equal_to( [ '-fspell-checking' ] ) ) def EnableTypoCorrection_Trivial_test(): assert_that( flags._EnableTypoCorrection( [ '-x', 'c++' ] ), equal_to( [ '-x', 'c++', '-fspell-checking' ] ) ) def EnableTypoCorrection_Reciprocal_test(): assert_that( flags._EnableTypoCorrection( [ '-fno-spell-checking' ] ), equal_to( [ '-fno-spell-checking' ] ) ) def EnableTypoCorrection_ReciprocalOthers_test(): compile_flags = [ '-x', 'c++', '-fno-spell-checking' ] assert_that( flags._EnableTypoCorrection( compile_flags ), equal_to( compile_flags ) ) @pytest.mark.parametrize( 'flag', INCLUDE_FLAGS ) def RemoveUnusedFlags_RemoveFilenameWithoutPrecedingInclude_test( flag ): to_remove = [ '/moo/boo' ] filename = 'file' expected = [ 'clang', flag, '/foo/bar', '-isystem/zoo/goo' ] assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected + to_remove, filename, ShouldAllowWinStyleFlags( expected + to_remove ) ) ) ) assert_that( expected, equal_to( flags.RemoveUnusedFlags( expected[ :1 ] + to_remove + expected[ 1: ], filename, ShouldAllowWinStyleFlags( expected[ :1 ] + to_remove + expected[ 1: ] ) ) ) ) assert_that( expected + expected[ 1: ], equal_to( flags.RemoveUnusedFlags( expected + to_remove + expected[ 1: ], filename, ShouldAllowWinStyleFlags( expected + to_remove + expected[ 1: ] ) ) ) ) def RemoveXclangFlags_test(): expected = [ '-I', '/foo/bar', '-DMACRO=Value' ] to_remove = [ '-Xclang', 'load', '-Xclang', 'libplugin.so', '-Xclang', '-add-plugin', '-Xclang', 'plugin-name' ] assert_that( expected, equal_to( flags._RemoveXclangFlags( expected + to_remove ) ) ) assert_that( expected, equal_to( flags._RemoveXclangFlags( to_remove + expected ) ) ) assert_that( expected + expected, equal_to( flags._RemoveXclangFlags( expected + to_remove + expected ) ) ) def AddLanguageFlagWhenAppropriate_Passthrough_test(): compiler_flags = [ '-foo', '-bar' ] assert_that( flags._AddLanguageFlagWhenAppropriate( compiler_flags, ShouldAllowWinStyleFlags( compiler_flags ) ), contains_exactly( '-foo', '-bar' ) ) @WindowsOnly def AddLanguageFlagWhenAppropriate_CLDriver_Passthrough_test(): compiler_flags = [ '-foo', '-bar', '--driver-mode=cl' ] assert_that( flags._AddLanguageFlagWhenAppropriate( compiler_flags, ShouldAllowWinStyleFlags( compiler_flags ) ), contains_exactly( '-foo', '-bar', '--driver-mode=cl' ) ) def _AddLanguageFlagWhenAppropriateTester( compiler, language_flag = [] ): to_removes = [ [], [ '/usr/bin/ccache' ], [ 'some_command', 'another_command' ] ] expected = [ '-foo', '-bar' ] for to_remove in to_removes: assert_that( [ compiler ] + language_flag + expected, equal_to( flags._AddLanguageFlagWhenAppropriate( to_remove + [ compiler ] + expected, ShouldAllowWinStyleFlags( to_remove + [ compiler ] + expected ) ) ) ) @pytest.mark.parametrize( 'compiler', [ 'cc', 'gcc', 'clang', '/usr/bin/cc', '/some/other/path', 'some_command' ] ) def AddLanguageFlagWhenAppropriate_CCompiler_test( compiler ): _AddLanguageFlagWhenAppropriateTester( compiler ) @pytest.mark.parametrize( 'compiler', [ 'c++', 'g++', 'clang++', '/usr/bin/c++', '/some/other/path++', 'some_command++', 'c++-5', 'g++-5.1', 'clang++-3.7.3', '/usr/bin/c++-5', 'c++-5.11', 'g++-50.1.49', 'clang++-3.12.3', '/usr/bin/c++-10', '/some/other/path++-4.9.3', 'some_command++-5.1', '/some/other/path++-4.9.31', 'some_command++-5.10' ] ) def AddLanguageFlagWhenAppropriate_CppCompiler_test( compiler ): _AddLanguageFlagWhenAppropriateTester( compiler, [ '-x', 'c++' ] ) def CompilationDatabase_NoDatabase_test(): with TemporaryTestDir() as tmp_dir: assert_that( calling( flags.Flags().FlagsForFile ).with_args( os.path.join( tmp_dir, 'test.cc' ) ), raises( NoExtraConfDetected ) ) def CompilationDatabase_FileNotInDatabase_test(): compile_commands = [] with TemporaryTestDir() as tmp_dir: with TemporaryClangProject( tmp_dir, compile_commands ): assert_that( flags.Flags().FlagsForFile( os.path.join( tmp_dir, 'test.cc' ) ), equal_to( ( [], os.path.join( tmp_dir, 'test.cc' ) ) ) ) def CompilationDatabase_InvalidDatabase_test(): with TemporaryTestDir() as tmp_dir: with TemporaryClangProject( tmp_dir, 'this is junk' ): assert_that( calling( flags.Flags().FlagsForFile ).with_args( os.path.join( tmp_dir, 'test.cc' ) ), raises( NoExtraConfDetected ) ) def CompilationDatabase_UseFlagsFromDatabase_test(): with TemporaryTestDir() as tmp_dir: compile_commands = [ { 'directory': tmp_dir, 'command': 'clang++ -x c++ -I. -I/absolute/path -Wall', 'file': os.path.join( tmp_dir, 'test.cc' ), }, ] with TemporaryClangProject( tmp_dir, compile_commands ): assert_that( flags.Flags().FlagsForFile( os.path.join( tmp_dir, 'test.cc' ), add_extra_clang_flags = False )[ 0 ], contains_exactly( 'clang++', '-x', 'c++', '--driver-mode=g++', '-x', 'c++', '-I' + os.path.normpath( tmp_dir ), '-I' + os.path.normpath( '/absolute/path' ), '-Wall' ) ) def CompilationDatabase_UseFlagsFromSameDir_test(): with TemporaryTestDir() as tmp_dir: compile_commands = [ { 'directory': tmp_dir, 'command': 'clang++ -x c++ -Wall', 'file': os.path.join( tmp_dir, 'test.cc' ), }, ] with TemporaryClangProject( tmp_dir, compile_commands ): f = flags.Flags() # If we ask for a file that is not in the DB but is in the same directory # of another file present in the DB, we get its flags. assert_that( f.FlagsForFile( os.path.join( tmp_dir, 'test1.cc' ), add_extra_clang_flags = False ), contains_exactly( contains_exactly( 'clang++', '-x', 'c++', '--driver-mode=g++', '-Wall' ), os.path.join( tmp_dir, 'test1.cc' ) ) ) # If we ask for a file that is not in the DB but in a subdirectory # of another file present in the DB, we get its flags. assert_that( f.FlagsForFile( os.path.join( tmp_dir, 'some_dir', 'test1.cc' ), add_extra_clang_flags = False ), contains_exactly( contains_exactly( 'clang++', '-x', 'c++', '--driver-mode=g++', '-Wall' ), os.path.join( tmp_dir, 'some_dir', 'test1.cc' ) ) ) def CompilationDatabase_HeaderFile_SameNameAsSourceFile_test(): with TemporaryTestDir() as tmp_dir: compile_commands = [ { 'directory': tmp_dir, 'command': 'clang++ -x c++ -Wall', 'file': os.path.join( tmp_dir, 'test.cc' ), }, ] with TemporaryClangProject( tmp_dir, compile_commands ): # If we ask for a header file with the same name as a source file, it # returns the flags of that cc file (and a special language flag for C++ # headers). assert_that( flags.Flags().FlagsForFile( os.path.join( tmp_dir, 'test.h' ), add_extra_clang_flags = False )[ 0 ], contains_exactly( 'clang++', '-x', 'c++', '--driver-mode=g++', '-Wall', '-x', 'c++-header' ) ) def CompilationDatabase_HeaderFile_DifferentNameFromSourceFile_test(): with TemporaryTestDir() as tmp_dir: compile_commands = [ { 'directory': tmp_dir, 'command': 'clang++ -x c++ -Wall', 'file': os.path.join( tmp_dir, 'test.cc' ), }, ] with TemporaryClangProject( tmp_dir, compile_commands ): # Even if we ask for a header file with a different name than the source # file, it still returns the flags from the cc file (and a special # language flag for C++ headers). assert_that( flags.Flags().FlagsForFile( os.path.join( tmp_dir, 'not_in_the_db.h' ), add_extra_clang_flags = False )[ 0 ], contains_exactly( 'clang++', '-x', 'c++', '--driver-mode=g++', '-Wall', '-x', 'c++-header' ) ) def CompilationDatabase_ExplicitHeaderFileEntry_test(): with TemporaryTestDir() as tmp_dir: # Have an explicit header file entry which should take priority over the # corresponding source file compile_commands = [ { 'directory': tmp_dir, 'command': 'clang++ -x c++ -I. -I/absolute/path -Wall', 'file': os.path.join( tmp_dir, 'test.cc' ), }, { 'directory': tmp_dir, 'command': 'clang++ -I/absolute/path -Wall', 'file': os.path.join( tmp_dir, 'test.h' ), }, ] with TemporaryClangProject( tmp_dir, compile_commands ): assert_that( flags.Flags().FlagsForFile( os.path.join( tmp_dir, 'test.h' ), add_extra_clang_flags = False )[ 0 ], contains_exactly( 'clang++', '-x', 'c++', '--driver-mode=g++', '-I' + os.path.normpath( '/absolute/path' ), '-Wall' ) ) def CompilationDatabase_CUDALanguageFlags_test(): with TemporaryTestDir() as tmp_dir: compile_commands = [ { 'directory': tmp_dir, 'command': 'clang++ -Wall ./test.cu', 'file': os.path.join( tmp_dir, 'test.cu' ), }, ] with TemporaryClangProject( tmp_dir, compile_commands ): # If we ask for a header file, it returns the equivalent cu file assert_that( flags.Flags().FlagsForFile( os.path.join( tmp_dir, 'test.cuh' ), add_extra_clang_flags = False )[ 0 ], contains_exactly( 'clang++', '-x', 'cuda', '--driver-mode=g++', '-Wall' ) ) def _MakeRelativePathsInFlagsAbsoluteTest( test ): wd = test[ 'wd' ] if 'wd' in test else '/not_test' assert_that( flags._MakeRelativePathsInFlagsAbsolute( test[ 'flags' ], wd ), contains_exactly( *test[ 'expect' ] ) ) @pytest.mark.parametrize( 'test', [ # Already absolute, positional arguments { 'flags': [ '-isystem', '/test' ], 'expect': [ '-isystem', os.path.normpath( '/test' ) ], }, { 'flags': [ '-I', '/test' ], 'expect': [ '-I', os.path.normpath( '/test' ) ], }, { 'flags': [ '-iquote', '/test' ], 'expect': [ '-iquote', os.path.normpath( '/test' ) ], }, { 'flags': [ '-isysroot', '/test' ], 'expect': [ '-isysroot', os.path.normpath( '/test' ) ], }, { 'flags': [ '-include-pch', '/test' ], 'expect': [ '-include-pch', os.path.normpath( '/test' ) ], }, { 'flags': [ '-idirafter', '/test' ], 'expect': [ '-idirafter', os.path.normpath( '/test' ) ], }, # Already absolute, single arguments { 'flags': [ '-isystem/test' ], 'expect': [ '-isystem' + os.path.normpath( '/test' ) ], }, { 'flags': [ '-I/test' ], 'expect': [ '-I' + os.path.normpath( '/test' ) ], }, { 'flags': [ '-iquote/test' ], 'expect': [ '-iquote' + os.path.normpath( '/test' ) ], }, { 'flags': [ '-isysroot/test' ], 'expect': [ '-isysroot' + os.path.normpath( '/test' ) ], }, { 'flags': [ '-include-pch/test' ], 'expect': [ '-include-pch' + os.path.normpath( '/test' ) ], }, { 'flags': [ '-idirafter/test' ], 'expect': [ '-idirafter' + os.path.normpath( '/test' ) ], }, # Already absolute, double-dash arguments { 'flags': [ '--isystem=/test' ], 'expect': [ '--isystem=/test' ], }, { 'flags': [ '--I=/test' ], 'expect': [ '--I=/test' ], }, { 'flags': [ '--iquote=/test' ], 'expect': [ '--iquote=/test' ], }, { 'flags': [ '--sysroot=/test' ], 'expect': [ '--sysroot=' + os.path.normpath( '/test' ) ], }, { 'flags': [ '--include-pch=/test' ], 'expect': [ '--include-pch=/test' ], }, { 'flags': [ '--idirafter=/test' ], 'expect': [ '--idirafter=/test' ], }, # Relative, positional arguments { 'flags': [ '-isystem', 'test' ], 'expect': [ '-isystem', os.path.normpath( '/test/test' ) ], 'wd': '/test', }, { 'flags': [ '-I', 'test' ], 'expect': [ '-I', os.path.normpath( '/test/test' ) ], 'wd': '/test', }, { 'flags': [ '-iquote', 'test' ], 'expect': [ '-iquote', os.path.normpath( '/test/test' ) ], 'wd': '/test', }, { 'flags': [ '-isysroot', 'test' ], 'expect': [ '-isysroot', os.path.normpath( '/test/test' ) ], 'wd': '/test', }, { 'flags': [ '-include-pch', 'test' ], 'expect': [ '-include-pch', os.path.normpath( '/test/test' ) ], 'wd': '/test', }, { 'flags': [ '-idirafter', 'test' ], 'expect': [ '-idirafter', os.path.normpath( '/test/test' ) ], 'wd': '/test', }, # Relative, single arguments { 'flags': [ '-isystemtest' ], 'expect': [ '-isystem' + os.path.normpath( '/test/test' ) ], 'wd': '/test', }, { 'flags': [ '-Itest' ], 'expect': [ '-I' + os.path.normpath( '/test/test' ) ], 'wd': '/test', }, { 'flags': [ '-iquotetest' ], 'expect': [ '-iquote' + os.path.normpath( '/test/test' ) ], 'wd': '/test', }, { 'flags': [ '-isysroottest' ], 'expect': [ '-isysroot' + os.path.normpath( '/test/test' ) ], 'wd': '/test', }, { 'flags': [ '-include-pchtest' ], 'expect': [ '-include-pch' + os.path.normpath( '/test/test' ) ], 'wd': '/test', }, { 'flags': [ '-idiraftertest' ], 'expect': [ '-idirafter' + os.path.normpath( '/test/test' ) ], 'wd': '/test', }, # Already absolute, double-dash arguments { 'flags': [ '--isystem=test' ], 'expect': [ '--isystem=test' ], 'wd': '/test', }, { 'flags': [ '--I=test' ], 'expect': [ '--I=test' ], 'wd': '/test', }, { 'flags': [ '--iquote=test' ], 'expect': [ '--iquote=test' ], 'wd': '/test', }, { 'flags': [ '--sysroot=test' ], 'expect': [ '--sysroot=' + os.path.normpath( '/test/test' ) ], 'wd': '/test', }, { 'flags': [ '--include-pch=test' ], 'expect': [ '--include-pch=test' ], 'wd': '/test', }, { 'flags': [ '--idirafter=test' ], 'expect': [ '--idirafter=test' ], 'wd': '/test', }, ] ) def MakeRelativePathsInFlagsAbsolute_test( test ): _MakeRelativePathsInFlagsAbsoluteTest( test ) @pytest.mark.parametrize( 'test', [ { 'flags': [ 'ignored', '-isystem', '/test', '-ignored', '-I', '/test', '--ignored=ignored' ], 'expect': [ 'ignored', '-isystem', os.path.normpath( '/test' ), '-ignored', '-I', os.path.normpath( '/test' ), '--ignored=ignored' ] }, { 'flags': [ 'ignored', '-isystem/test', '-ignored', '-I/test', '--ignored=ignored' ], 'expect': [ 'ignored', '-isystem' + os.path.normpath( '/test' ), '-ignored', '-I' + os.path.normpath( '/test/' ), '--ignored=ignored' ] }, { 'flags': [ 'ignored', '--isystem=/test', '-ignored', '--I=/test', '--ignored=ignored' ], 'expect': [ 'ignored', '--isystem=/test', '-ignored', '--I=/test', '--ignored=ignored' ] }, { 'flags': [ 'ignored', '-isystem', 'test', '-ignored', '-I', 'test', '--ignored=ignored' ], 'expect': [ 'ignored', '-isystem', os.path.normpath( '/test/test' ), '-ignored', '-I', os.path.normpath( '/test/test' ), '--ignored=ignored' ], 'wd': '/test', }, { 'flags': [ 'ignored', '-isystemtest', '-ignored', '-Itest', '--ignored=ignored' ], 'expect': [ 'ignored', '-isystem' + os.path.normpath( '/test/test' ), '-ignored', '-I' + os.path.normpath( '/test/test' ), '--ignored=ignored' ], 'wd': '/test', }, { 'flags': [ 'ignored', '--isystem=test', '-ignored', '--I=test', '--ignored=ignored', '--sysroot=test' ], 'expect': [ 'ignored', '--isystem=test', '-ignored', '--I=test', '--ignored=ignored', '--sysroot=' + os.path.normpath( '/test/test' ), ], 'wd': '/test', }, ] ) def MakeRelativePathsInFlagsAbsolute_IgnoreUnknown_test( test ): _MakeRelativePathsInFlagsAbsoluteTest( test ) def MakeRelativePathsInFlagsAbsolute_NoWorkingDir_test(): _MakeRelativePathsInFlagsAbsoluteTest( { 'flags': [ 'list', 'of', 'flags', 'not', 'changed', '-Itest' ], 'expect': [ 'list', 'of', 'flags', 'not', 'changed', '-Itest' ], 'wd': '' } ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/get_completions_test.py������������������������������0000664�0000000�0000000�00000142306�13746324601�0025133�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2015-2020 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 <http://www.gnu.org/licenses/>. import json import requests from unittest.mock import patch from hamcrest import ( all_of, assert_that, contains_exactly, contains_inanyorder, empty, equal_to, has_item, has_items, has_entry, has_entries, is_not ) from ycmd import handlers from ycmd.completers.cpp.clang_completer import ( NO_COMPLETIONS_MESSAGE, NO_COMPILE_FLAGS_MESSAGE, PARSING_FILE_MESSAGE ) from ycmd.responses import UnknownExtraConf, NoExtraConfDetected from ycmd.tests.clang import ( IsolatedYcmd, MockCoreClangCompleter, PathToTestFile, SharedYcmd ) from ycmd.tests.test_utils import ( BuildRequest, ChunkMatcher, CombineRequest, CompletionEntryMatcher, ErrorMatcher, LocationMatcher, WindowsOnly ) from ycmd.utils import ImportCore, ReadFile ycm_core = ImportCore() NO_COMPLETIONS_ERROR = ErrorMatcher( RuntimeError, NO_COMPLETIONS_MESSAGE ) def RunTest( app, test ): """ Method to run a simple completion test and verify the result Note: by default uses the .ycm_extra_conf from general_fallback/ which: - supports cpp, c and objc - requires extra_conf_data containing 'filetype&' = the filetype This should be sufficient for many standard test cases. If not, specify a path (as a list of path items) in 'extra_conf' member of |test|. test is a dictionary containing: 'request': kwargs for BuildRequest 'expect': { 'response': server response code (e.g. requests.codes.ok) 'data': matcher for the server response json } 'extra_conf': [ optional list of path items to extra conf file ] """ extra_conf = ( test[ 'extra_conf' ] if 'extra_conf' in test else [ 'general_fallback', '.ycm_extra_conf.py' ] ) app.post_json( '/load_extra_conf_file', { 'filepath': PathToTestFile( *extra_conf ) } ) request = test[ 'request' ] contents = ( request[ 'contents' ] if 'contents' in request else ReadFile( request[ 'filepath' ] ) ) # Because we aren't testing this command, we *always* ignore errors. This # is mainly because we (may) want to test scenarios where the completer # throws an exception and the easiest way to do that is to throw from # within the Settings function. app.post_json( '/event_notification', CombineRequest( request, { 'event_name': 'FileReadyToParse', 'contents': contents, } ), expect_errors = True ) # We also ignore errors here, but then we check the response code ourself. # This is to allow testing of requests returning errors. response = app.post_json( '/completions', CombineRequest( request, { 'contents': contents } ), expect_errors = True ) assert_that( response.status_code, equal_to( test[ 'expect' ][ 'response' ] ) ) print( f'Completer response: { json.dumps( response.json, indent = 2 ) }' ) assert_that( response.json, test[ 'expect' ][ 'data' ] ) @SharedYcmd def GetCompletions_ForcedWithNoTrigger_test( app ): RunTest( app, { 'description': 'semantic completion with force query=DO_SO', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'general_fallback', 'lang_cpp.cc' ), 'line_num' : 54, 'column_num': 8, 'extra_conf_data': { '&filetype': 'cpp' }, 'force_semantic': True, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_exactly( CompletionEntryMatcher( 'DO_SOMETHING_TO', 'void' ), CompletionEntryMatcher( 'DO_SOMETHING_WITH', 'void' ), ), 'errors': empty(), } ) }, } ) # This test is isolated to make sure we trigger c hook for clangd, instead of # fetching completer from cache. @IsolatedYcmd() def GetCompletions_Fallback_NoSuggestions_test( app ): RunTest( app, { 'description': 'Triggered, fallback but no query so no completions', 'request': { 'filetype' : 'c', 'filepath' : PathToTestFile( 'general_fallback', 'lang_c.c' ), 'line_num' : 29, 'column_num': 21, 'extra_conf_data': { '&filetype': 'c' }, 'force_semantic': False, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': empty(), 'errors': has_item( NO_COMPLETIONS_ERROR ), } ) }, } ) @SharedYcmd def GetCompletions_Fallback_NoSuggestions_MinimumCharacters_test( app ): RunTest( app, { 'description': 'fallback general completion obeys min chars setting ' ' (query="a")', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'general_fallback', 'lang_c.c' ), 'line_num' : 29, 'column_num': 22, 'extra_conf_data': { '&filetype': 'c' }, 'force_semantic': False, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': empty(), 'errors': has_item( NO_COMPLETIONS_ERROR ), } ) }, } ) @SharedYcmd def GetCompletions_Fallback_Suggestions_test( app ): RunTest( app, { 'description': '. after macro with some query text (.a_)', 'request': { 'filetype' : 'c', 'filepath' : PathToTestFile( 'general_fallback', 'lang_c.c' ), 'line_num' : 29, 'column_num': 23, 'extra_conf_data': { '&filetype': 'c' }, 'force_semantic': False, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': has_item( CompletionEntryMatcher( 'a_parameter', '[ID]' ) ), 'errors': has_item( NO_COMPLETIONS_ERROR ), } ) }, } ) @SharedYcmd def GetCompletions_Fallback_Exception_test( app ): # extra conf throws exception RunTest( app, { 'description': '. on struct returns identifier because of error', 'request': { 'filetype' : 'c', 'filepath' : PathToTestFile( 'general_fallback', 'lang_c.c' ), 'line_num' : 62, 'column_num': 20, 'extra_conf_data': { '&filetype': 'c', 'throw': 'testy' }, 'force_semantic': False, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_exactly( CompletionEntryMatcher( 'a_parameter', '[ID]' ), CompletionEntryMatcher( 'another_parameter', '[ID]' ), ), 'errors': has_item( ErrorMatcher( ValueError, 'testy' ) ) } ) }, } ) @SharedYcmd def GetCompletions_Forced_NoFallback_test( app ): RunTest( app, { 'description': '-> after macro with forced semantic', 'request': { 'filetype' : 'c', 'filepath' : PathToTestFile( 'general_fallback', 'lang_c.c' ), 'line_num' : 41, 'column_num': 30, 'extra_conf_data': { '&filetype': 'c' }, 'force_semantic': True, }, 'expect': { 'response': requests.codes.internal_server_error, 'data': NO_COMPLETIONS_ERROR, }, } ) @SharedYcmd def GetCompletions_FilteredNoResults_Fallback_test( app ): # no errors because the semantic completer returned results, but they # were filtered out by the query, so this is considered working OK # (whereas no completions from the semantic engine is considered an # error) RunTest( app, { 'description': '. on struct returns IDs after query=do_', 'request': { 'filetype': 'c', 'filepath': PathToTestFile( 'general_fallback', 'lang_c.c' ), 'line_num': 71, 'column_num': 18, 'extra_conf_data': { '&filetype': 'c' }, 'force_semantic': False, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_inanyorder( # do_ is an identifier because it is already in the file when we # load it CompletionEntryMatcher( 'do_', '[ID]' ), CompletionEntryMatcher( 'do_something', '[ID]' ), CompletionEntryMatcher( 'do_another_thing', '[ID]' ), CompletionEntryMatcher( 'DO_SOMETHING_TO', '[ID]' ), CompletionEntryMatcher( 'DO_SOMETHING_VIA', '[ID]' ) ), 'errors': empty() } ) }, } ) @IsolatedYcmd() def GetCompletions_WorksWithExplicitFlags_test( app ): app.post_json( '/ignore_extra_conf_file', { 'filepath': PathToTestFile( '.ycm_extra_conf.py' ) } ) contents = """ struct Foo { int x; int y; char c; }; int main() { Foo foo; foo. } """ completion_data = BuildRequest( filepath = '/foo.cpp', filetype = 'cpp', contents = contents, line_num = 11, column_num = 7, compilation_flags = [ '-x', 'c++' ] ) response_data = app.post_json( '/completions', completion_data ).json assert_that( response_data[ 'completions' ], has_items( CompletionEntryMatcher( 'c' ), CompletionEntryMatcher( 'x' ), CompletionEntryMatcher( 'y' ) ) ) assert_that( 7, equal_to( response_data[ 'completion_start_column' ] ) ) @IsolatedYcmd( { 'auto_trigger': 0 } ) def GetCompletions_NoCompletionsWhenAutoTriggerOff_test( app ): app.post_json( '/ignore_extra_conf_file', { 'filepath': PathToTestFile( '.ycm_extra_conf.py' ) } ) contents = """ struct Foo { int x; int y; char c; }; int main() { Foo foo; foo. } """ completion_data = BuildRequest( filepath = '/foo.cpp', filetype = 'cpp', contents = contents, line_num = 11, column_num = 7, compilation_flags = [ '-x', 'c++' ] ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, empty() ) @IsolatedYcmd() def GetCompletions_UnknownExtraConfException_test( app ): filepath = PathToTestFile( 'basic.cpp' ) completion_data = BuildRequest( filepath = filepath, filetype = 'cpp', contents = ReadFile( filepath ), line_num = 11, column_num = 7, force_semantic = True ) response = app.post_json( '/completions', completion_data, expect_errors = True ) assert_that( response.status_code, equal_to( requests.codes.internal_server_error ) ) assert_that( response.json, has_entry( 'exception', has_entry( 'TYPE', UnknownExtraConf.__name__ ) ) ) app.post_json( '/ignore_extra_conf_file', { 'filepath': PathToTestFile( '.ycm_extra_conf.py' ) } ) response = app.post_json( '/completions', completion_data, expect_errors = True ) assert_that( response.status_code, equal_to( requests.codes.internal_server_error ) ) assert_that( response.json, has_entry( 'exception', has_entry( 'TYPE', NoExtraConfDetected.__name__ ) ) ) @IsolatedYcmd() def GetCompletions_WorksWhenExtraConfExplicitlyAllowed_test( app ): app.post_json( '/load_extra_conf_file', { 'filepath': PathToTestFile( '.ycm_extra_conf.py' ) } ) filepath = PathToTestFile( 'basic.cpp' ) completion_data = BuildRequest( filepath = filepath, filetype = 'cpp', contents = ReadFile( filepath ), line_num = 11, column_num = 7 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'c' ), CompletionEntryMatcher( 'x' ), CompletionEntryMatcher( 'y' ) ) ) @SharedYcmd def GetCompletions_ExceptionWhenNoFlagsFromExtraConf_test( app ): app.post_json( '/load_extra_conf_file', { 'filepath': PathToTestFile( 'noflags', '.ycm_extra_conf.py' ) } ) filepath = PathToTestFile( 'noflags', 'basic.cpp' ) completion_data = BuildRequest( filepath = filepath, filetype = 'cpp', contents = ReadFile( filepath ), line_num = 11, column_num = 7, force_semantic = True ) response = app.post_json( '/completions', completion_data, expect_errors = True ) assert_that( response.status_code, equal_to( requests.codes.internal_server_error ) ) assert_that( response.json, has_entry( 'exception', has_entry( 'TYPE', RuntimeError.__name__ ) ) ) @SharedYcmd def GetCompletions_ForceSemantic_OnlyFilteredCompletions_test( app ): contents = """ int main() { int foobar; int floozar; int gooboo; int bleble; fooar } """ completion_data = BuildRequest( filepath = '/foo.cpp', filetype = 'cpp', force_semantic = True, contents = contents, line_num = 9, column_num = 8, compilation_flags = [ '-x', 'c++' ] ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, contains_inanyorder( CompletionEntryMatcher( 'foobar' ), CompletionEntryMatcher( 'floozar' ) ) ) @SharedYcmd def GetCompletions_DocStringsAreIncluded_test( app ): filepath = PathToTestFile( 'completion_docstring.cc' ) completion_data = BuildRequest( filepath = filepath, filetype = 'cpp', contents = ReadFile( filepath ), line_num = 5, column_num = 7, compilation_flags = [ '-x', 'c++' ], force_semantic = True ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_item( has_entries( { 'insertion_text': 'func', 'extra_data': has_entry( 'doc_string', 'This is a docstring.' ) } ) ) ) @SharedYcmd def GetCompletions_PublicAndProtectedMembersAvailableInDerivedClass_test( app ): filepath = PathToTestFile( 'completion_availability.cc' ) completion_data = BuildRequest( filepath = filepath, filetype = 'cpp', contents = ReadFile( filepath ), line_num = 14, column_num = 5, compilation_flags = [ '-x', 'c++' ], force_semantic = True ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, all_of( has_items( CompletionEntryMatcher( 'public_member' ), CompletionEntryMatcher( 'protected_member' ) ), is_not( has_item( CompletionEntryMatcher( 'private_member' ) ) ) ) ) @SharedYcmd def GetCompletions_ClientDataGivenToExtraConf_test( app ): app.post_json( '/load_extra_conf_file', { 'filepath': PathToTestFile( 'client_data', '.ycm_extra_conf.py' ) } ) filepath = PathToTestFile( 'client_data', 'main.cpp' ) completion_data = BuildRequest( filepath = filepath, filetype = 'cpp', contents = ReadFile( filepath ), line_num = 9, column_num = 7, extra_conf_data = { 'flags': [ '-x', 'c++' ] } ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_item( CompletionEntryMatcher( 'x' ) ) ) @IsolatedYcmd( { 'max_num_candidates': 0 } ) def GetCompletions_ClientDataGivenToExtraConf_Include_test( app ): app.post_json( '/load_extra_conf_file', { 'filepath': PathToTestFile( 'client_data', '.ycm_extra_conf.py' ) } ) filepath = PathToTestFile( 'client_data', 'include.cpp' ) completion_data = BuildRequest( filepath = filepath, filetype = 'cpp', contents = ReadFile( filepath ), line_num = 1, column_num = 11, extra_conf_data = { 'flags': [ '-x', 'c++' ] } ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_item( CompletionEntryMatcher( 'include.hpp', extra_menu_info = '[File]' ) ) ) @IsolatedYcmd() def GetCompletions_ClientDataGivenToExtraConf_Cache_test( app ): app.post_json( '/load_extra_conf_file', { 'filepath': PathToTestFile( 'client_data', '.ycm_extra_conf.py' ) } ) filepath = PathToTestFile( 'client_data', 'macro.cpp' ) contents = ReadFile( filepath ) request = { 'filetype' : 'cpp', 'filepath' : filepath, 'contents' : contents, 'line_num' : 11, 'column_num': 8 } # Complete with flags from the client. completion_request = CombineRequest( request, { 'extra_conf_data': { 'flags': [ '-DSOME_MACRO' ] } } ) assert_that( app.post_json( '/completions', completion_request ).json, has_entries( { 'completions': has_item( CompletionEntryMatcher( 'macro_defined' ) ), 'errors': empty() } ) ) # Complete at the same position but for a different set of flags from the # client. completion_request = CombineRequest( request, { 'extra_conf_data': { 'flags': [ '-Wall' ] } } ) assert_that( app.post_json( '/completions', completion_request ).json, has_entries( { 'completions': has_item( CompletionEntryMatcher( 'macro_not_defined' ) ), 'errors': empty() } ) ) # Finally, complete once again at the same position but no flags are given by # the client. An empty list of flags is returned by the extra conf file in # that case. completion_request = CombineRequest( request, {} ) assert_that( app.post_json( '/completions', completion_request ).json, has_entries( { 'completions': empty(), 'errors': contains_exactly( ErrorMatcher( RuntimeError, NO_COMPILE_FLAGS_MESSAGE ) ) } ) ) @SharedYcmd @WindowsOnly def GetCompletions_ClangCLDriverFlag_SimpleCompletion_test( app ): RunTest( app, { 'description': 'basic completion with --driver-mode=cl', 'extra_conf': [ 'driver_mode_cl', 'flag', '.ycm_extra_conf.py' ], 'request': { 'filetype': 'cpp', 'filepath': PathToTestFile( 'driver_mode_cl', 'flag', 'driver_mode_cl.cpp' ), 'line_num': 8, 'column_num': 18, 'force_semantic': True, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 3, 'completions': contains_inanyorder( CompletionEntryMatcher( 'driver_mode_cl_include_func', 'void' ), CompletionEntryMatcher( 'driver_mode_cl_include_int', 'int' ), ), 'errors': empty(), } ) } } ) @SharedYcmd @WindowsOnly def GetCompletions_ClangCLDriverExec_SimpleCompletion_test( app ): RunTest( app, { 'description': 'basic completion with --driver-mode=cl', 'extra_conf': [ 'driver_mode_cl', 'executable', '.ycm_extra_conf.py' ], 'request': { 'filetype': 'cpp', 'filepath': PathToTestFile( 'driver_mode_cl', 'executable', 'driver_mode_cl.cpp' ), 'line_num': 8, 'column_num': 18, 'force_semantic': True, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 3, 'completions': contains_inanyorder( CompletionEntryMatcher( 'driver_mode_cl_include_func', 'void' ), CompletionEntryMatcher( 'driver_mode_cl_include_int', 'int' ), ), 'errors': empty(), } ) } } ) @SharedYcmd @WindowsOnly def GetCompletions_ClangCLDriverFlag_IncludeStatementCandidate_test( app ): RunTest( app, { 'description': 'Completion inside include statement with CL driver', 'extra_conf': [ 'driver_mode_cl', 'flag', '.ycm_extra_conf.py' ], 'request': { 'filetype': 'cpp', 'filepath': PathToTestFile( 'driver_mode_cl', 'flag', 'driver_mode_cl.cpp' ), 'line_num': 1, 'column_num': 34, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 11, 'completions': contains_inanyorder( CompletionEntryMatcher( 'driver_mode_cl_include.h', '[File]' ), ), 'errors': empty(), } ) } } ) @SharedYcmd @WindowsOnly def GetCompletions_ClangCLDriverExec_IncludeStatementCandidate_test( app ): RunTest( app, { 'description': 'Completion inside include statement with CL driver', 'extra_conf': [ 'driver_mode_cl', 'executable', '.ycm_extra_conf.py' ], 'request': { 'filetype': 'cpp', 'filepath': PathToTestFile( 'driver_mode_cl', 'executable', 'driver_mode_cl.cpp' ), 'line_num': 1, 'column_num': 34, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 11, 'completions': contains_inanyorder( CompletionEntryMatcher( 'driver_mode_cl_include.h', '[File]' ), ), 'errors': empty(), } ) } } ) @SharedYcmd def GetCompletions_UnicodeInLine_test( app ): RunTest( app, { 'description': 'member completion with a unicode identifier', 'extra_conf': [ '.ycm_extra_conf.py' ], 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'unicode.cc' ), 'line_num' : 9, 'column_num': 8, 'extra_conf_data': { '&filetype': 'cpp' }, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 8, 'completions': contains_inanyorder( CompletionEntryMatcher( 'member_with_å_unicøde', 'int' ), CompletionEntryMatcher( '~MyStruct', 'void' ), CompletionEntryMatcher( 'operator=', 'MyStruct &' ), CompletionEntryMatcher( 'MyStruct::', '' ), ), 'errors': empty(), } ) }, } ) @SharedYcmd def GetCompletions_UnicodeInLineFilter_test( app ): RunTest( app, { 'description': 'member completion with a unicode identifier', 'extra_conf': [ '.ycm_extra_conf.py' ], 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'unicode.cc' ), 'line_num' : 9, 'column_num': 10, 'extra_conf_data': { '&filetype': 'cpp' }, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 8, 'completions': contains_inanyorder( CompletionEntryMatcher( 'member_with_å_unicøde', 'int' ), ), 'errors': empty(), } ) }, } ) @SharedYcmd def GetCompletions_QuotedInclude_AtStart_test( app ): RunTest( app, { 'description': 'completion of #include "', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 11, 'column_num': 11, 'compilation_flags': [ '-x', 'c++', '-nostdinc', '-nobuiltininc' ] }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 11, 'completions': contains_exactly( CompletionEntryMatcher( '.ycm_extra_conf.py', '[File]' ), CompletionEntryMatcher( 'a.hpp', '[File]' ), CompletionEntryMatcher( 'dir with spaces', '[Dir]' ), CompletionEntryMatcher( 'main.cpp', '[File]' ), CompletionEntryMatcher( 'quote', '[Dir]' ), CompletionEntryMatcher( 'system', '[Dir]' ), CompletionEntryMatcher( 'Frameworks', '[Dir]' ) ), 'errors': empty(), } ) }, } ) @SharedYcmd def GetCompletions_QuotedInclude_UserIncludeFlag_test( app ): RunTest( app, { 'description': 'completion of #include " with a -I flag', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 11, 'column_num': 11, 'compilation_flags': [ '-x', 'c++', '-nostdinc', '-nobuiltininc', '-I', PathToTestFile( 'test-include', 'system' ) ] }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 11, 'completions': contains_exactly( CompletionEntryMatcher( '.ycm_extra_conf.py', '[File]' ), CompletionEntryMatcher( 'a.hpp', '[File]' ), CompletionEntryMatcher( 'c.hpp', '[File]' ), CompletionEntryMatcher( 'common', '[Dir]' ), CompletionEntryMatcher( 'dir with spaces', '[Dir]' ), CompletionEntryMatcher( 'main.cpp', '[File]' ), CompletionEntryMatcher( 'quote', '[Dir]' ), CompletionEntryMatcher( 'system', '[Dir]' ), CompletionEntryMatcher( 'Frameworks', '[Dir]' ) ), 'errors': empty(), } ) }, } ) @SharedYcmd def GetCompletions_QuotedInclude_SystemIncludeFlag_test( app ): RunTest( app, { 'description': 'completion of #include " with a -isystem flag', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 11, 'column_num': 11, 'compilation_flags': [ '-x', 'c++', '-nostdinc', '-nobuiltininc', '-isystem', PathToTestFile( 'test-include', 'system' ) ] }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 11, 'completions': contains_exactly( CompletionEntryMatcher( '.ycm_extra_conf.py', '[File]' ), CompletionEntryMatcher( 'a.hpp', '[File]' ), CompletionEntryMatcher( 'c.hpp', '[File]' ), CompletionEntryMatcher( 'common', '[Dir]' ), CompletionEntryMatcher( 'dir with spaces', '[Dir]' ), CompletionEntryMatcher( 'main.cpp', '[File]' ), CompletionEntryMatcher( 'quote', '[Dir]' ), CompletionEntryMatcher( 'system', '[Dir]' ), CompletionEntryMatcher( 'Frameworks', '[Dir]' ) ), 'errors': empty(), } ) }, } ) @SharedYcmd def GetCompletions_QuotedInclude_QuoteIncludeFlag_test( app ): RunTest( app, { 'description': 'completion of #include " with a -iquote flag', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 11, 'column_num': 11, 'compilation_flags': [ '-x', 'c++', '-nostdinc', '-nobuiltininc', '-iquote', PathToTestFile( 'test-include', 'quote' ) ] }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 11, 'completions': contains_exactly( CompletionEntryMatcher( '.ycm_extra_conf.py', '[File]' ), CompletionEntryMatcher( 'a.hpp', '[File]' ), CompletionEntryMatcher( 'b.hpp', '[File]' ), CompletionEntryMatcher( 'dir with spaces', '[Dir]' ), CompletionEntryMatcher( 'main.cpp', '[File]' ), CompletionEntryMatcher( 'quote', '[Dir]' ), CompletionEntryMatcher( 'system', '[Dir]' ), CompletionEntryMatcher( 'Frameworks', '[Dir]' ) ), 'errors': empty(), } ) }, } ) @SharedYcmd def GetCompletions_QuotedInclude_MultipleIncludeFlags_test( app ): RunTest( app, { 'description': 'completion of #include " with multiple -I flags', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 11, 'column_num': 11, 'compilation_flags': [ '-x', 'c++', '-nostdinc', '-nobuiltininc', '-I', PathToTestFile( 'test-include', 'dir with spaces' ), '-I', PathToTestFile( 'test-include', 'quote' ), '-I', PathToTestFile( 'test-include', 'system' ) ] }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 11, 'completions': contains_exactly( CompletionEntryMatcher( '.ycm_extra_conf.py', '[File]' ), CompletionEntryMatcher( 'a.hpp', '[File]' ), CompletionEntryMatcher( 'b.hpp', '[File]' ), CompletionEntryMatcher( 'c.hpp', '[File]' ), CompletionEntryMatcher( 'common', '[Dir]' ), CompletionEntryMatcher( 'd.hpp', '[File]' ), CompletionEntryMatcher( 'dir with spaces', '[Dir]' ), CompletionEntryMatcher( 'main.cpp', '[File]' ), CompletionEntryMatcher( 'quote', '[Dir]' ), CompletionEntryMatcher( 'system', '[Dir]' ), CompletionEntryMatcher( 'Frameworks', '[Dir]' ) ), 'errors': empty(), } ) }, } ) @SharedYcmd def GetCompletions_QuotedInclude_AfterDirectorySeparator_test( app ): RunTest( app, { 'description': 'completion of #include "quote/', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 11, 'column_num': 27, 'compilation_flags': [ '-x', 'c++', '-nostdinc', '-nobuiltininc' ] }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 27, 'completions': contains_exactly( CompletionEntryMatcher( 'd.hpp', '[File]' ) ), 'errors': empty(), } ) }, } ) @SharedYcmd def GetCompletions_QuotedInclude_AfterDot_test( app ): RunTest( app, { 'description': 'completion of #include "quote/b.', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 11, 'column_num': 28, 'compilation_flags': [ '-x', 'c++', '-nostdinc', '-nobuiltininc' ] }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 27, 'completions': contains_exactly( CompletionEntryMatcher( 'd.hpp', '[File]' ) ), 'errors': empty(), } ) }, } ) @SharedYcmd def GetCompletions_QuotedInclude_AfterSpace_test( app ): RunTest( app, { 'description': 'completion of #include "dir with ', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 11, 'column_num': 20, 'compilation_flags': [ '-x', 'c++', '-nostdinc', '-nobuiltininc' ] }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 11, 'completions': contains_exactly( CompletionEntryMatcher( 'dir with spaces', '[Dir]' ) ), 'errors': empty(), } ) }, } ) @SharedYcmd def GetCompletions_QuotedInclude_Invalid_test( app ): RunTest( app, { 'description': 'completion of an invalid include statement', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 13, 'column_num': 12, 'compilation_flags': [ '-x', 'c++', '-nostdinc', '-nobuiltininc' ] }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 12, 'completions': empty(), 'errors': empty(), } ) }, } ) @SharedYcmd def GetCompletions_QuotedInclude_FrameworkHeader_test( app ): RunTest( app, { 'description': 'completion of #include "OpenGL/', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 14, 'column_num': 18, 'compilation_flags': [ '-x', 'c++', '-nostdinc', '-nobuiltininc', '-iframework', PathToTestFile( 'test-include', 'Frameworks' ) ] }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 18, 'completions': contains_exactly( CompletionEntryMatcher( 'gl.h', '[File]' ) ), 'errors': empty() } ) }, } ) @SharedYcmd def GetCompletions_BracketInclude_AtStart_test( app ): RunTest( app, { 'description': 'completion of #include <', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 12, 'column_num': 11, 'compilation_flags': [ '-x', 'c++', '-nostdinc', '-nobuiltininc' ] }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 11, 'completions': empty(), 'errors': empty(), } ) }, } ) @SharedYcmd def GetCompletions_BracketInclude_UserIncludeFlag_test( app ): RunTest( app, { 'description': 'completion of #include < with a -I flag', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 12, 'column_num': 11, 'compilation_flags': [ '-x', 'c++', '-nostdinc', '-nobuiltininc', '-I', PathToTestFile( 'test-include', 'system' ) ] }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 11, 'completions': contains_exactly( CompletionEntryMatcher( 'a.hpp', '[File]' ), CompletionEntryMatcher( 'c.hpp', '[File]' ), CompletionEntryMatcher( 'common', '[Dir]' ) ), 'errors': empty(), } ) }, } ) @SharedYcmd def GetCompletions_BracketInclude_SystemIncludeFlag_test( app ): RunTest( app, { 'description': 'completion of #include < with a -isystem flag', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 12, 'column_num': 11, 'compilation_flags': [ '-x', 'c++', '-nostdinc', '-nobuiltininc', '-isystem', PathToTestFile( 'test-include', 'system' ) ] }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 11, 'completions': contains_exactly( CompletionEntryMatcher( 'a.hpp', '[File]' ), CompletionEntryMatcher( 'c.hpp', '[File]' ), CompletionEntryMatcher( 'common', '[Dir]' ) ), 'errors': empty(), } ) }, } ) @SharedYcmd def GetCompletions_BracketInclude_QuoteIncludeFlag_test( app ): RunTest( app, { 'description': 'completion of #include < with a -iquote flag', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 12, 'column_num': 11, 'compilation_flags': [ '-x', 'c++', '-nostdinc', '-nobuiltininc', '-iquote', PathToTestFile( 'test-include', 'quote' ) ] }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 11, 'completions': empty(), 'errors': empty(), } ) }, } ) @SharedYcmd def GetCompletions_BracketInclude_MultipleIncludeFlags_test( app ): RunTest( app, { 'description': 'completion of #include < with multiple -I flags', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 12, 'column_num': 11, 'compilation_flags': [ '-x', 'c++', '-nostdinc', '-nobuiltininc', '-I', PathToTestFile( 'test-include', 'dir with spaces' ), '-I', PathToTestFile( 'test-include', 'quote' ), '-I', PathToTestFile( 'test-include', 'system' ) ] }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 11, 'completions': contains_exactly( CompletionEntryMatcher( 'a.hpp', '[File]' ), CompletionEntryMatcher( 'b.hpp', '[File]' ), CompletionEntryMatcher( 'c.hpp', '[File]' ), CompletionEntryMatcher( 'common', '[Dir]' ), CompletionEntryMatcher( 'd.hpp', '[File]' ) ), 'errors': empty(), } ) }, } ) @SharedYcmd def GetCompletions_BracketInclude_AtDirectorySeparator_test( app ): RunTest( app, { 'description': 'completion of #include <system/', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 12, 'column_num': 18, 'compilation_flags': [ '-x', 'c++', '-nostdinc', '-nobuiltininc' ], # NOTE: when not forcing semantic, it falls back to the filename # completer and returns the root folder entries. 'force_semantic': True }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 18, 'completions': empty(), 'errors': empty(), } ) }, } ) @SharedYcmd def GetCompletions_BracketInclude_FrameworkHeader_test( app ): RunTest( app, { 'description': 'completion of #include <OpenGL/', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 15, 'column_num': 18, 'compilation_flags': [ '-x', 'c++', '-nostdinc', '-nobuiltininc', '-iframework', PathToTestFile( 'test-include', 'Frameworks' ) ] }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 18, 'completions': contains_exactly( CompletionEntryMatcher( 'gl.h', '[File]' ) ), 'errors': empty() } ) }, } ) @SharedYcmd def GetCompletions_BracketInclude_FileAndDirectory_test( app ): RunTest( app, { 'description': 'suggestion can simultaneously be a file ' 'and a directory', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 12, 'column_num': 11, 'compilation_flags': [ '-x', 'c++', '-nostdinc', '-nobuiltininc', '-isystem', PathToTestFile( 'test-include', 'system' ), '-isystem', PathToTestFile( 'test-include', 'system', 'common' ) ] }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 11, 'completions': contains_exactly( CompletionEntryMatcher( 'a.hpp', '[File]' ), CompletionEntryMatcher( 'c.hpp', '[File]' ), CompletionEntryMatcher( 'common', '[File&Dir]' ) ), 'errors': empty() } ) }, } ) @SharedYcmd def GetCompletions_BracketInclude_FileAndFramework_test( app ): RunTest( app, { 'description': 'suggestion can simultaneously be a file and a framework', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 12, 'column_num': 11, 'compilation_flags': [ '-x', 'c++', '-nostdinc', '-nobuiltininc', '-iframework', PathToTestFile( 'test-include', 'Frameworks' ), '-isystem', PathToTestFile( 'test-include', 'system', 'common' ) ] }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 11, 'completions': contains_exactly( CompletionEntryMatcher( 'common', '[File&Framework]' ), CompletionEntryMatcher( 'OpenGL', '[Framework]' ) ), 'errors': empty() } ) }, } ) @SharedYcmd def GetCompletions_BracketInclude_DirectoryAndFramework_test( app ): RunTest( app, { 'description': 'suggestion can simultaneously be a directory ' 'and a framework', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 12, 'column_num': 11, 'compilation_flags': [ '-x', 'c++', '-nostdinc', '-nobuiltininc', '-iframework', PathToTestFile( 'test-include', 'Frameworks' ), '-isystem', PathToTestFile( 'test-include', 'system' ) ] }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 11, 'completions': contains_exactly( CompletionEntryMatcher( 'a.hpp', '[File]' ), CompletionEntryMatcher( 'c.hpp', '[File]' ), CompletionEntryMatcher( 'common', '[Dir&Framework]' ), CompletionEntryMatcher( 'OpenGL', '[Framework]' ) ), 'errors': empty() } ) }, } ) @SharedYcmd def GetCompletions_BracketInclude_FileAndDirectoryAndFramework_test( app ): RunTest( app, { 'description': 'suggestion can simultaneously be a file, a directory, ' 'and a framework', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 12, 'column_num': 11, 'compilation_flags': [ '-x', 'c++', '-nostdinc', '-nobuiltininc', '-iframework', PathToTestFile( 'test-include', 'Frameworks' ), '-isystem', PathToTestFile( 'test-include', 'system' ), '-isystem', PathToTestFile( 'test-include', 'system', 'common' ) ] }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 11, 'completions': contains_exactly( CompletionEntryMatcher( 'a.hpp', '[File]' ), CompletionEntryMatcher( 'c.hpp', '[File]' ), CompletionEntryMatcher( 'common', '[File&Dir&Framework]' ), CompletionEntryMatcher( 'OpenGL', '[Framework]' ) ), 'errors': empty() } ) }, } ) @SharedYcmd def GetCompletions_TranslateClangExceptionToPython_test( app ): RunTest( app, { 'description': 'The ClangParseError C++ exception is properly translated ' 'to a Python exception', 'extra_conf': [ '.ycm_extra_conf.py' ], 'request': { 'filetype' : 'cpp', # libclang fails to parse a file that doesn't exist. 'filepath' : PathToTestFile( 'non_existing_file' ), 'contents' : '', 'force_semantic': True }, 'expect': { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( ycm_core.ClangParseError, "Failed to parse the translation unit." ) }, } ) @SharedYcmd def GetCompletions_Unity_test( app ): RunTest( app, { 'description': 'Completion returns from file included in TU, but not in ' 'opened file', 'extra_conf': [ '.ycm_extra_conf.py' ], 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'unitya.cc' ), 'line_num' : 10, 'column_num': 24, 'force_semantic': True, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 20, 'completions': contains_exactly( CompletionEntryMatcher( 'this_is_an_it', 'int' ), ), 'errors': empty(), } ) } } ) @SharedYcmd def GetCompletions_UnityInclude_test( app ): RunTest( app, { 'description': 'Completion returns for includes in unity setup', 'extra_conf': [ '.ycm_extra_conf.py' ], 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'unitya.cc' ), 'line_num' : 1, 'column_num': 17, 'force_semantic': True, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 11, 'completions': has_items( CompletionEntryMatcher( 'unity.h', '[File]' ), CompletionEntryMatcher( 'unity.cc', '[File]' ), CompletionEntryMatcher( 'unitya.cc', '[File]' ), ), 'errors': empty(), } ) } } ) # This test is isolated to make sure we trigger c hook for clangd, instead of # fetching completer from cache. @IsolatedYcmd() def GetCompletions_cuda_test( app ): RunTest( app, { 'description': 'Completion of CUDA files', 'extra_conf': [ '.ycm_extra_conf.py' ], 'request': { 'filetype' : 'cuda', 'filepath' : PathToTestFile( 'cuda', 'completion_test.cu' ), 'line_num' : 16, 'column_num': 29, 'force_semantic': True, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 29, # NOTE: libclang also returns strange completions like 'cudaStream::', # 'dim3::', 'and Kernels::'. 'completions': has_item( CompletionEntryMatcher( 'do_something', 'void' ), ), 'errors': empty(), } ) } } ) @SharedYcmd def GetCompletions_StillParsingError_test( app ): completer = handlers._server_state.GetFiletypeCompleter( [ 'cpp' ] ) with patch.object( completer, '_completer', MockCoreClangCompleter() ): RunTest( app, { 'description': 'raise an appropriate error if translation unit is still ' 'being parsed.', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test.cpp' ), 'contents' : '', 'compilation_flags': [ '-x', 'c++' ], 'force_semantic' : True }, 'expect': { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( RuntimeError, PARSING_FILE_MESSAGE ) }, } ) @SharedYcmd def GetCompletions_FixIt_test( app ): filepath = PathToTestFile( 'completion_fixit.cc' ) RunTest( app, { 'description': 'member completion has a fixit that change "." into "->"', 'extra_conf': [ '.ycm_extra_conf.py' ], 'request': { 'filetype': 'cpp', 'filepath': filepath, 'line_num': 7, 'column_num': 8, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': has_item( has_entries( { 'insertion_text': 'bar', 'extra_menu_info': 'int', 'menu_text': 'bar', 'detailed_info': 'int bar\n', 'kind': 'MEMBER', 'extra_data': has_entries( { 'fixits': contains_inanyorder( has_entries( { 'text': '', 'chunks': contains_exactly( ChunkMatcher( '->', LocationMatcher( filepath, 7, 6 ), LocationMatcher( filepath, 7, 7 ) ) ), 'location': LocationMatcher( '', 0, 0 ) } ) ) } ) } ) ) } ) } } ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/include_cache_test.py��������������������������������0000664�0000000�0000000�00000015110�13746324601�0024476�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2017 Davit Samvelyan davitsamvelyan@gmail.com # Synopsys. # 2020 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 <http://www.gnu.org/licenses/>. import os from time import sleep from hamcrest import ( assert_that, contains_exactly, contains_inanyorder, equal_to, has_entries, has_entry, has_properties, not_ ) from ycmd.completers.cpp.include_cache import IncludeCache from ycmd.tests.clang import PathToTestFile from ycmd.tests.test_utils import TemporaryTestDir def IncludeCache_NotCached_DirInaccessible_test(): include_cache = IncludeCache() assert_that( include_cache._cache, equal_to( {} ) ) includes = include_cache.GetIncludes( PathToTestFile( 'unknown_dir' ) ) assert_that( includes, equal_to( [] ) ) assert_that( include_cache._cache, equal_to( {} ) ) def IncludeCache_NotCached_DirAccessible_test(): include_cache = IncludeCache() assert_that( include_cache._cache, equal_to( {} ) ) includes = include_cache.GetIncludes( PathToTestFile( 'cache_test' ) ) mtime = os.path.getmtime( PathToTestFile( 'cache_test' ) ) assert_that( includes, contains_exactly( has_properties( { 'name': 'foo.h', 'entry_type': 1 } ) ) ) assert_that( include_cache._cache, has_entry( PathToTestFile( 'cache_test' ), has_entries( { 'mtime': mtime, 'includes': contains_exactly( has_properties( { 'name': 'foo.h', 'entry_type': 1 } ) ) } ) ) ) def IncludeCache_Cached_NoNewMtime_test(): include_cache = IncludeCache() assert_that( include_cache._cache, equal_to( {} ) ) old_includes = include_cache.GetIncludes( PathToTestFile( 'cache_test' ) ) old_mtime = os.path.getmtime( PathToTestFile( 'cache_test' ) ) assert_that( old_includes, contains_exactly( has_properties( { 'name': 'foo.h', 'entry_type': 1 } ) ) ) assert_that( include_cache._cache, has_entry( PathToTestFile( 'cache_test' ), has_entries( { 'mtime': old_mtime, 'includes': contains_exactly( has_properties( { 'name': 'foo.h', 'entry_type': 1 } ) ) } ) ) ) new_includes = include_cache.GetIncludes( PathToTestFile( 'cache_test' ) ) new_mtime = os.path.getmtime( PathToTestFile( 'cache_test' ) ) assert_that( new_mtime, equal_to( old_mtime ) ) assert_that( new_includes, contains_exactly( has_properties( { 'name': 'foo.h', 'entry_type': 1 } ) ) ) assert_that( include_cache._cache, has_entry( PathToTestFile( 'cache_test' ), has_entries( { 'mtime': new_mtime, 'includes': contains_exactly( has_properties( { 'name': 'foo.h', 'entry_type': 1 } ) ) } ) ) ) def IncludeCache_Cached_NewMtime_test(): with TemporaryTestDir() as tmp_dir: include_cache = IncludeCache() assert_that( include_cache._cache, equal_to( {} ) ) foo_path = os.path.join( tmp_dir, 'foo' ) with open( foo_path, 'w' ) as foo_file: foo_file.write( 'foo' ) old_includes = include_cache.GetIncludes( tmp_dir ) old_mtime = os.path.getmtime( tmp_dir ) assert_that( old_includes, contains_exactly( has_properties( { 'name': 'foo', 'entry_type': 1 } ) ) ) assert_that( include_cache._cache, has_entry( tmp_dir, has_entries( { 'mtime': old_mtime, 'includes': contains_exactly( has_properties( { 'name': 'foo', 'entry_type': 1 } ) ) } ) ) ) sleep( 2 ) bar_path = os.path.join( tmp_dir, 'bar' ) with open( bar_path, 'w' ) as bar_file: bar_file.write( 'bar' ) new_includes = include_cache.GetIncludes( tmp_dir ) new_mtime = os.path.getmtime( tmp_dir ) assert_that( old_mtime, not_( equal_to( new_mtime ) ) ) assert_that( new_includes, contains_inanyorder( has_properties( { 'name': 'foo', 'entry_type': 1 } ), has_properties( { 'name': 'bar', 'entry_type': 1 } ) ) ) assert_that( include_cache._cache, has_entry( tmp_dir, has_entries( { 'mtime': new_mtime, 'includes': contains_inanyorder( has_properties( { 'name': 'foo', 'entry_type': 1 } ), has_properties( { 'name': 'bar', 'entry_type': 1 } ) ) } ) ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/signature_help_test.py�������������������������������0000664�0000000�0000000�00000005622�13746324601�0024750�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2015-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, empty, has_entries, has_items ) from ycmd.utils import ReadFile from ycmd.tests.clang import PathToTestFile, SharedYcmd from ycmd.tests.test_utils import ( EMPTY_SIGNATURE_HELP, BuildRequest, CompletionEntryMatcher ) @SharedYcmd def SignatureHelp_NotImplemented_test( app ): app.post_json( '/load_extra_conf_file', { 'filepath': PathToTestFile( '.ycm_extra_conf.py' ) } ) filepath = PathToTestFile( 'unity.cc' ) contents = ReadFile( filepath ) app.post_json( '/event_notification', BuildRequest( filepath = filepath, contents = contents, filetype = 'cpp', event_name = 'FileReadyToParse' ) ) # Doing a completion proves that we have semantic parsing working response_data = app.post_json( '/completions', BuildRequest( filepath = filepath, contents = contents, filetype = 'cpp', line_num = 27, column_num = 11, force_semantic = True ) ).json assert_that( response_data[ 'completions' ], has_items( CompletionEntryMatcher( 'an_int' ), CompletionEntryMatcher( 'a_char' ) ) ) # Signature help request always returns nothing # FIXME: A method to say "don't bother sending more signature help request" response_data = app.post_json( '/signature_help', BuildRequest( filepath = filepath, contents = contents, filetype = 'cpp', line_num = 24, column_num = 19 ) ).json assert_that( response_data, has_entries( { 'errors': empty(), 'signature_help': EMPTY_SIGNATURE_HELP } ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ��������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/subcommands_test.py����������������������������������0000664�0000000�0000000�00000160366�13746324601�0024261�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2015-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, calling, contains_exactly, contains_inanyorder, contains_string, empty, equal_to, has_entry, has_entries, raises, matches_regexp ) from unittest.mock import patch from pprint import pprint from webtest import AppError import pytest import requests import os.path from ycmd import handlers from ycmd.completers.cpp.clang_completer import ( NO_DOCUMENTATION_MESSAGE, PARSING_FILE_MESSAGE ) from ycmd.tests.clang import ( MockCoreClangCompleter, PathToTestFile, SharedYcmd ) from ycmd.tests.test_utils import ( BuildRequest, ErrorMatcher, ChunkMatcher, LocationMatcher, LineColMatcher ) from ycmd.utils import ReadFile @SharedYcmd def Subcommands_DefinedSubcommands_test( app ): subcommands_data = BuildRequest( completer_target = 'cpp' ) assert_that( app.post_json( '/defined_subcommands', subcommands_data ).json, contains_inanyorder( 'ClearCompilationFlagCache', 'FixIt', 'GetDoc', 'GetDocImprecise', 'GetParent', 'GetType', 'GetTypeImprecise', 'GoTo', 'GoToDeclaration', 'GoToDefinition', 'GoToImprecise', 'GoToInclude' ) ) @SharedYcmd def Subcommands_GoTo_ZeroBasedLineAndColumn_test( app ): contents = ReadFile( PathToTestFile( 'GoTo_Clang_ZeroBasedLineAndColumn_test.cc' ) ) goto_data = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'GoToDefinition' ], compilation_flags = [ '-x', 'c++' ], line_num = 10, column_num = 3, contents = contents, filetype = 'cpp' ) assert_that( app.post_json( '/run_completer_command', goto_data ).json, LocationMatcher( os.path.abspath( '/foo' ), 2, 8 ) ) @SharedYcmd def Subcommands_GoTo_CUDA_test( app ): filepath = PathToTestFile( 'cuda', 'basic.cu' ) contents = ReadFile( filepath ) goto_data = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'GoToDefinition' ], compilation_flags = [ '-x', 'cuda' ], line_num = 8, column_num = 3, filepath = filepath, contents = contents, filetype = 'cuda' ) assert_that( app.post_json( '/run_completer_command', goto_data ).json, LocationMatcher( filepath, 4, 17 ) ) def RunGoToTest_all( app, filename, command, test ): contents = ReadFile( PathToTestFile( filename ) ) common_request = { 'completer_target' : 'filetype_default', 'filepath' : PathToTestFile( filename ), 'command_arguments': command, 'compilation_flags': [ '-x', 'c++' ], 'line_num' : 10, 'column_num' : 3, 'contents' : contents, 'filetype' : 'cpp' } common_response = { 'filepath': os.path.abspath( PathToTestFile( filename ) ), } if 'extra_conf' in test: common_request.pop( 'compilation_flags' ) app.post_json( '/load_extra_conf_file', { 'filepath': PathToTestFile( *test[ 'extra_conf' ] ) } ) request = common_request request.update( { 'line_num' : test[ 'request' ][ 0 ], 'column_num': test[ 'request' ][ 1 ], } ) response = common_response response.update( { 'line_num' : test[ 'response' ][ 0 ], 'column_num': test[ 'response' ][ 1 ], } ) if len( test[ 'response' ] ) > 2: response.update( { 'filepath': PathToTestFile( test[ 'response' ][ 2 ] ) } ) goto_data = BuildRequest( **request ) actual_response = app.post_json( '/run_completer_command', goto_data ).json pprint( actual_response ) assert_that( response, equal_to( actual_response ) ) @SharedYcmd def Subcommands_GoTo_all_test( app ): # GoToDeclaration tests = [ # Local::x -> definition/declaration of x { 'request': [ 23, 21 ], 'response': [ 4, 9 ] }, # Local::in_line -> definition/declaration of Local::in_line { 'request': [ 24, 26 ], 'response': [ 6, 10 ] }, # Local -> definition/declaration of Local { 'request': [ 24, 16 ], 'response': [ 2, 11 ] }, # Local::out_of_line -> declaration of Local::out_of_line { 'request': [ 25, 27 ], 'response': [ 11, 10 ] }, # GoToDeclaration on definition of out_of_line moves to declaration { 'request': [ 14, 13 ], 'response': [ 11, 10 ] }, # main -> declaration of main { 'request': [ 21, 7 ], 'response': [ 19, 5 ] }, # Unicøde { 'request': [ 34, 8 ], 'response': [ 32, 26 ] }, # Another_Unicøde { 'request': [ 36, 25 ], 'response': [ 32, 54 ] }, { 'request': [ 38, 3 ], 'response': [ 36, 28 ] }, ] for test in tests: RunGoToTest_all( app, 'GoTo_all_Clang_test.cc', [ 'GoToDeclaration' ], test ) # GoToDefinition tests = [ # Local::x -> declaration/definition of x { 'request': [ 23, 21 ], 'response': [ 4, 9 ] }, # Local::in_line -> declaration/definition of Local::in_line { 'request': [ 24, 26 ], 'response': [ 6, 10 ] }, # Local -> declaration/definition of Local { 'request': [ 24, 16 ], 'response': [ 2, 11 ] }, # Local::out_of_line -> definition of Local::out_of_line { 'request': [ 25, 27 ], 'response': [ 14, 13 ] }, # GoToDefinition on definition of out_of_line moves to itself { 'request': [ 14, 13 ], 'response': [ 14, 13 ] }, # main -> definition of main (not declaration) { 'request': [ 21, 7 ], 'response': [ 21, 5 ] }, # Unicøde { 'request': [ 34, 8 ], 'response': [ 32, 26 ] }, ] for test in tests: RunGoToTest_all( app, 'GoTo_all_Clang_test.cc', [ 'GoToDefinition' ], test ) # GoTo tests = [ # Local::x -> declaration/definition of x { 'request': [ 23, 21 ], 'response': [ 4, 9 ] }, # Local::in_line -> declaration/definition of Local::in_line { 'request': [ 24, 26 ], 'response': [ 6, 10 ] }, # Local -> declaration/definition of Local { 'request': [ 24, 16 ], 'response': [ 2, 11 ] }, # Local::out_of_line -> definition of Local::out_of_line { 'request': [ 25, 27 ], 'response': [ 14, 13 ] }, # GoTo on definition of out_of_line moves to declaration { 'request': [ 14, 13 ], 'response': [ 11, 10 ] }, # GoTo on declaration of out_of_line moves to definition { 'request': [ 11, 17 ], 'response': [ 14, 13 ] }, # main -> definition of main { 'request': [ 21, 7 ], 'response': [ 19, 5 ] }, # Unicøde { 'request': [ 34, 8 ], 'response': [ 32, 26 ] }, # Another_Unicøde { 'request': [ 36, 25 ], 'response': [ 32, 54 ] }, { 'request': [ 38, 3 ], 'response': [ 36, 28 ] }, ] for test in tests: RunGoToTest_all( app, 'GoTo_all_Clang_test.cc', [ 'GoTo' ], test ) # GoToImprecise - identical to GoTo tests = [ # Local::x -> declaration/definition of x { 'request': [ 23, 21 ], 'response': [ 4, 9 ] }, # Local::in_line -> declaration/definition of Local::in_line { 'request': [ 24, 26 ], 'response': [ 6, 10 ] }, # Local -> declaration/definition of Local { 'request': [ 24, 16 ], 'response': [ 2, 11 ] }, # Local::out_of_line -> definition of Local::out_of_line { 'request': [ 25, 27 ], 'response': [ 14, 13 ] }, # GoToImprecise on definition of out_of_line moves to declaration { 'request': [ 14, 13 ], 'response': [ 11, 10 ] }, # GoToImprecise on declaration of out_of_line moves to definition { 'request': [ 11, 17 ], 'response': [ 14, 13 ] }, # main -> definition of main { 'request': [ 21, 7 ], 'response': [ 19, 5 ] }, # Unicøde { 'request': [ 34, 8 ], 'response': [ 32, 26 ] }, # Another_Unicøde { 'request': [ 36, 25 ], 'response': [ 32, 54 ] }, { 'request': [ 38, 3 ], 'response': [ 36, 28 ] }, ] for test in tests: RunGoToTest_all( app, 'GoTo_all_Clang_test.cc', [ 'GoToImprecise' ], test ) @SharedYcmd def Subcommands_GoTo_all_Fail_test( app ): cursor_on_nothing = { 'request': [ 13, 1 ], 'response': [ 1, 1 ] } cursor_on_another_unicode = { 'request': [ 36, 17 ], 'response': [ 1, 1 ] } cursor_on_keyword = { 'request': [ 16, 6 ], 'response': [ 1, 1 ] } # GoToDeclaration assert_that( calling( RunGoToTest_all ).with_args( app, 'GoTo_all_Clang_test.cc', [ 'GoToDeclaration' ], cursor_on_nothing ), raises( AppError, r'Can\\\'t jump to declaration.' ) ) assert_that( calling( RunGoToTest_all ).with_args( app, 'GoTo_all_Clang_test.cc', [ 'GoToDeclaration' ], cursor_on_keyword ), raises( AppError, r'Can\\\'t jump to declaration.' ) ) # GoToDefinition assert_that( calling( RunGoToTest_all ).with_args( app, 'GoTo_all_Clang_test.cc', [ 'GoToDefinition' ], cursor_on_nothing ), raises( AppError, r'Can\\\'t jump to definition.' ) ) assert_that( calling( RunGoToTest_all ).with_args( app, 'GoTo_all_Clang_test.cc', [ 'GoToDefinition' ], cursor_on_another_unicode ), raises( AppError, r'Can\\\'t jump to definition.' ) ) # GoTo assert_that( calling( RunGoToTest_all ).with_args( app, 'GoTo_all_Clang_test.cc', [ 'GoTo' ], cursor_on_nothing ), raises( AppError, r'Can\\\'t jump to definition or declaration.' ) ) assert_that( calling( RunGoToTest_all ).with_args( app, 'GoTo_all_Clang_test.cc', [ 'GoTo' ], cursor_on_keyword ), raises( AppError, r'Can\\\'t jump to definition or declaration.' ) ) # GoToImprecise assert_that( calling( RunGoToTest_all ).with_args( app, 'GoTo_all_Clang_test.cc', [ 'GoToImprecise' ], cursor_on_nothing ), raises( AppError, r'Can\\\'t jump to definition or declaration.' ) ) assert_that( calling( RunGoToTest_all ).with_args( app, 'GoTo_all_Clang_test.cc', [ 'GoToImprecise' ], cursor_on_keyword ), raises( AppError, r'Can\\\'t jump to definition or declaration.' ) ) def RunGoToIncludeTest( app, command, test ): app.post_json( '/load_extra_conf_file', { 'filepath': PathToTestFile( 'test-include', '.ycm_extra_conf.py' ) } ) filepath = PathToTestFile( 'test-include', 'main.cpp' ) goto_data = BuildRequest( filepath = filepath, filetype = 'cpp', contents = ReadFile( filepath ), command_arguments = [ command ], line_num = test[ 'request' ][ 0 ], column_num = test[ 'request' ][ 1 ] ) response = { 'filepath' : PathToTestFile( 'test-include', test[ 'response' ] ), 'line_num' : 1, 'column_num' : 1, } actual_response = app.post_json( '/run_completer_command', goto_data ).json pprint( actual_response ) assert_that( response, equal_to( actual_response ) ) @pytest.mark.parametrize( 'cmd', [ 'GoToInclude', 'GoTo', 'GoToImprecise' ] ) @pytest.mark.parametrize( 'test', [ { 'request': [ 1, 1 ], 'response': 'a.hpp' }, { 'request': [ 2, 1 ], 'response': os.path.join( 'system', 'a.hpp' ) }, { 'request': [ 3, 1 ], 'response': os.path.join( 'quote', 'b.hpp' ) }, { 'request': [ 5, 1 ], 'response': os.path.join( 'system', 'c.hpp' ) }, { 'request': [ 6, 1 ], 'response': os.path.join( 'system', 'c.hpp' ) }, { 'request': [ 7, 1 ], 'response': os.path.join( 'Frameworks', 'OpenGL.framework', 'Headers', 'gl.h' ) }, { 'request': [ 8, 1 ], 'response': os.path.join( 'Frameworks', 'OpenGL.framework', 'Headers', 'gl.h' ) }, ] ) @SharedYcmd def Subcommands_GoToInclude_test( app, cmd, test ): RunGoToIncludeTest( app, cmd, test ) @SharedYcmd def Subcommands_GoToInclude_Fail_test( app ): test = { 'request': [ 4, 1 ], 'response': '' } assert_that( calling( RunGoToIncludeTest ).with_args( app, 'GoToInclude', test ), raises( AppError, 'Include file not found.' ) ) assert_that( calling( RunGoToIncludeTest ).with_args( app, 'GoTo', test ), raises( AppError, 'Include file not found.' ) ) assert_that( calling( RunGoToIncludeTest ).with_args( app, 'GoToImprecise', test ), raises( AppError, 'Include file not found.' ) ) test = { 'request': [ 9, 1 ], 'response': '' } assert_that( calling( RunGoToIncludeTest ).with_args( app, 'GoToInclude', test ), raises( AppError, 'Not an include/import line.' ) ) assert_that( calling( RunGoToIncludeTest ).with_args( app, 'GoTo', test ), raises( AppError, r'Can\\\'t jump to definition or declaration.' ) ) assert_that( calling( RunGoToIncludeTest ).with_args( app, 'GoToImprecise', test ), raises( AppError, r'Can\\\'t jump to definition or declaration.' ) ) # Unclosed #include statement. test = { 'request': [ 12, 13 ], 'response': '' } assert_that( calling( RunGoToIncludeTest ).with_args( app, 'GoToInclude', test ), raises( AppError, 'Not an include/import line.' ) ) assert_that( calling( RunGoToIncludeTest ).with_args( app, 'GoTo', test ), raises( AppError, r'Can\\\'t jump to definition or declaration.' ) ) assert_that( calling( RunGoToIncludeTest ).with_args( app, 'GoToImprecise', test ), raises( AppError, r'Can\\\'t jump to definition or declaration.' ) ) @SharedYcmd def Subcommands_GoTo_Unity_test( app ): RunGoToTest_all( app, 'unitya.cc', [ 'GoToDeclaration' ], { 'request': [ 8, 21 ], 'response': [ 1, 8, 'unity.cc' ], 'extra_conf': [ '.ycm_extra_conf.py' ], } ) RunGoToTest_all( app, 'unitya.cc', [ 'GoToInclude' ], { 'request': [ 1, 14 ], 'response': [ 1, 1, 'unity.h' ], 'extra_conf': [ '.ycm_extra_conf.py' ], } ) def RunGetSemanticTest( app, filepath, filetype, test, command ): contents = ReadFile( filepath ) language = { 'cpp': 'c++', 'cuda': 'cuda' } # We use the -fno-delayed-template-parsing flag to not delay # parsing of templates on Windows. This is the default on # other platforms. See the _ExtraClangFlags function in # ycmd/completers/cpp/flags.py file for more information. common_args = { 'completer_target' : 'filetype_default', 'command_arguments': command, 'compilation_flags': [ '-x', language[ filetype ], # C++11 flag is needed for lambda functions '-std=c++11', '-fno-delayed-template-parsing' ], 'line_num' : 10, 'column_num' : 3, 'filepath' : filepath, 'contents' : contents, 'filetype' : filetype } args = test[ 0 ] expected = test[ 1 ] if 'extra_conf' in args: common_args.pop( 'compilation_flags' ) app.post_json( '/load_extra_conf_file', { 'filepath': PathToTestFile( *args[ 'extra_conf' ] ) } ) args.pop( 'extra_conf' ) request = common_args request.update( args ) request_data = BuildRequest( **request ) response = app.post_json( '/run_completer_command', request_data ).json pprint( response ) assert_that( response, has_entry( 'message', expected ) ) @pytest.mark.parametrize( 'test', [ # Basic pod types [ { 'line_num': 24, 'column_num': 3 }, 'Foo' ], [ { 'line_num': 1, 'column_num': 1 }, 'Internal error: ' 'cursor not valid' ], [ { 'line_num': 12, 'column_num': 2 }, 'Foo' ], [ { 'line_num': 12, 'column_num': 8 }, 'Foo' ], [ { 'line_num': 12, 'column_num': 9 }, 'Foo' ], [ { 'line_num': 12, 'column_num': 10 }, 'Foo' ], [ { 'line_num': 13, 'column_num': 3 }, 'int' ], [ { 'line_num': 13, 'column_num': 7 }, 'int' ], [ { 'line_num': 15, 'column_num': 7 }, 'char' ], # Function [ { 'line_num': 22, 'column_num': 2 }, 'int ()' ], [ { 'line_num': 22, 'column_num': 6 }, 'int ()' ], # Declared and canonical type # On Ns:: (Unknown) [ { 'line_num': 25, 'column_num': 3 }, 'Unknown type' ], # sic # On Type (Type) [ { 'line_num': 25, 'column_num': 8 }, 'Ns::Type => Ns::BasicType<char>' ], # On "a" (Ns::Type) [ { 'line_num': 25, 'column_num': 15 }, 'Ns::Type => Ns::BasicType<char>' ], [ { 'line_num': 26, 'column_num': 13 }, 'Ns::Type => Ns::BasicType<char>' ], # Cursor on decl for refs & pointers [ { 'line_num': 39, 'column_num': 3 }, 'Foo' ], [ { 'line_num': 39, 'column_num': 11 }, 'Foo &' ], [ { 'line_num': 39, 'column_num': 15 }, 'Foo' ], [ { 'line_num': 40, 'column_num': 3 }, 'Foo' ], [ { 'line_num': 40, 'column_num': 11 }, 'Foo *' ], [ { 'line_num': 40, 'column_num': 18 }, 'Foo' ], [ { 'line_num': 42, 'column_num': 3 }, 'const Foo &' ], [ { 'line_num': 42, 'column_num': 16 }, 'const Foo &' ], [ { 'line_num': 43, 'column_num': 3 }, 'const Foo *' ], [ { 'line_num': 43, 'column_num': 16 }, 'const Foo *' ], # Cursor on usage [ { 'line_num': 45, 'column_num': 13 }, 'const Foo' ], [ { 'line_num': 45, 'column_num': 19 }, 'const int' ], [ { 'line_num': 46, 'column_num': 13 }, 'const Foo *' ], [ { 'line_num': 46, 'column_num': 20 }, 'const int' ], [ { 'line_num': 47, 'column_num': 12 }, 'Foo' ], [ { 'line_num': 47, 'column_num': 17 }, 'int' ], [ { 'line_num': 48, 'column_num': 12 }, 'Foo *' ], [ { 'line_num': 48, 'column_num': 18 }, 'int' ], # Auto in declaration [ { 'line_num': 28, 'column_num': 3 }, 'Foo &' ], [ { 'line_num': 28, 'column_num': 11 }, 'Foo &' ], [ { 'line_num': 28, 'column_num': 18 }, 'Foo' ], [ { 'line_num': 29, 'column_num': 3 }, 'Foo *' ], [ { 'line_num': 29, 'column_num': 11 }, 'Foo *' ], [ { 'line_num': 29, 'column_num': 18 }, 'Foo' ], [ { 'line_num': 31, 'column_num': 3 }, 'const Foo &' ], [ { 'line_num': 31, 'column_num': 16 }, 'const Foo &' ], [ { 'line_num': 32, 'column_num': 3 }, 'const Foo *' ], [ { 'line_num': 32, 'column_num': 16 }, 'const Foo *' ], # Auto in usage [ { 'line_num': 34, 'column_num': 14 }, 'const Foo' ], [ { 'line_num': 34, 'column_num': 21 }, 'const int' ], [ { 'line_num': 35, 'column_num': 14 }, 'const Foo *' ], [ { 'line_num': 35, 'column_num': 22 }, 'const int' ], [ { 'line_num': 36, 'column_num': 13 }, 'Foo' ], [ { 'line_num': 36, 'column_num': 19 }, 'int' ], [ { 'line_num': 37, 'column_num': 13 }, 'Foo *' ], [ { 'line_num': 37, 'column_num': 20 }, 'int' ], # Unicode [ { 'line_num': 51, 'column_num': 13 }, 'Unicøde *' ], # Bound methods # On Win32, methods pick up an __attribute__((thiscall)) to annotate their # calling convention. This shows up in the type, which isn't ideal, but # also prohibitively complex to try and strip out. [ { 'line_num': 53, 'column_num': 15 }, matches_regexp( r'int \(int\)(?: __attribute__\(\(thiscall\)\))?' ) ], [ { 'line_num': 54, 'column_num': 18 }, matches_regexp( r'int \(int\)(?: __attribute__\(\(thiscall\)\))?' ) ], ] ) @pytest.mark.parametrize( 'cmd', [ 'GetType', 'GoToImprecise' ] ) @SharedYcmd def Subcommands_GetType_test( app, cmd, test ): RunGetSemanticTest( app, PathToTestFile( 'GetType_Clang_test.cc' ), 'cpp', test, [ 'GetType' ] ) @SharedYcmd def SubCommands_GetType_CUDA_test( app ): test = [ { 'line_num': 8, 'column_num': 3, }, 'void ()' ] RunGetSemanticTest( app, PathToTestFile( 'cuda', 'basic.cu' ), 'cuda', test, [ 'GetType' ] ) @SharedYcmd def SubCommands_GetType_Unity_test( app ): test = [ { 'line_num': 10, 'column_num': 25, 'extra_conf': [ '.ycm_extra_conf.py' ] }, 'int' ] RunGetSemanticTest( app, PathToTestFile( 'unitya.cc' ), 'cpp', test, [ 'GetType' ] ) @pytest.mark.parametrize( 'test', [ [ { 'line_num': 1, 'column_num': 1 }, 'Internal error: ' 'cursor not valid' ], [ { 'line_num': 2, 'column_num': 8 }, PathToTestFile( 'GetParent_Clang_test.cc' ) ], # The reported scope does not include parents [ { 'line_num': 3, 'column_num': 11 }, 'A' ], [ { 'line_num': 4, 'column_num': 13 }, 'B' ], [ { 'line_num': 5, 'column_num': 13 }, 'B' ], [ { 'line_num': 9, 'column_num': 17 }, 'do_z_inline()' ], [ { 'line_num': 15, 'column_num': 22 }, 'do_anything(T &)' ], [ { 'line_num': 19, 'column_num': 9 }, 'A' ], [ { 'line_num': 20, 'column_num': 9 }, 'A' ], [ { 'line_num': 22, 'column_num': 12 }, 'A' ], [ { 'line_num': 23, 'column_num': 5 }, 'do_Z_inline()' ], [ { 'line_num': 24, 'column_num': 12 }, 'do_Z_inline()' ], [ { 'line_num': 28, 'column_num': 14 }, 'A' ], [ { 'line_num': 34, 'column_num': 1 }, 'do_anything(T &)' ], [ { 'line_num': 39, 'column_num': 1 }, 'do_x()' ], [ { 'line_num': 44, 'column_num': 1 }, 'do_y()' ], [ { 'line_num': 49, 'column_num': 1 }, 'main()' ], # Lambdas report the name of the variable [ { 'line_num': 49, 'column_num': 14 }, 'l' ], [ { 'line_num': 50, 'column_num': 19 }, 'l' ], [ { 'line_num': 51, 'column_num': 16 }, 'main()' ], ] ) @SharedYcmd def Subcommands_GetParent_test( app, test ): RunGetSemanticTest( app, PathToTestFile( 'GetParent_Clang_test.cc' ), 'cpp', test, [ 'GetParent' ] ) def RunFixItTest( app, line, column, lang, file_path, check ): contents = ReadFile( file_path ) language_options = { 'cpp11': { 'compilation_flags': [ '-x', 'c++', '-std=c++11', '-Wall', '-Wextra', '-pedantic' ], 'filetype' : 'cpp', }, 'cuda': { 'compilation_flags': [ '-x', 'cuda', '-std=c++11', '-Wall', '-Wextra', '-pedantic' ], 'filetype' : 'cuda', }, 'objective-c': { 'compilation_flags': [ '-x', 'objective-c', '-Wall', '-Wextra' ], 'filetype' : 'objc', }, } # Build the command arguments from the standard ones and the language-specific # arguments. args = { 'completer_target' : 'filetype_default', 'contents' : contents, 'filepath' : file_path, 'command_arguments': [ 'FixIt' ], 'line_num' : line, 'column_num' : column, } args.update( language_options[ lang ] ) # Get the fixes for the file. event_data = BuildRequest( **args ) results = app.post_json( '/run_completer_command', event_data ).json pprint( results ) check( results ) def FixIt_Check_cpp11_Ins( results ): # First fixit # switch(A()) { // expected-error{{explicit conversion to}} assert_that( results, has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( has_entries( { 'replacement_text': equal_to( 'static_cast<int>(' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 16, 'column_num': 10 } ), 'end' : has_entries( { 'line_num': 16, 'column_num': 10 } ), } ), } ), has_entries( { 'replacement_text': equal_to( ')' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 16, 'column_num': 13 } ), 'end' : has_entries( { 'line_num': 16, 'column_num': 13 } ), } ), } ) ), 'location': has_entries( { 'line_num': 16, 'column_num': 3 } ) } ) ) } ) ) def FixIt_Check_cpp11_InsMultiLine( results ): # Similar to FixIt_Check_cpp11_1 but inserts split across lines # assert_that( results, has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( has_entries( { 'replacement_text': equal_to( 'static_cast<int>(' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 26, 'column_num': 7 } ), 'end' : has_entries( { 'line_num': 26, 'column_num': 7 } ), } ), } ), has_entries( { 'replacement_text': equal_to( ')' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 28, 'column_num': 2 } ), 'end' : has_entries( { 'line_num': 28, 'column_num': 2 } ), } ), } ) ), 'location': has_entries( { 'line_num': 25, 'column_num': 3 } ) } ) ) } ) ) def FixIt_Check_cpp11_Del( results ): # Removal of :: assert_that( results, has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( has_entries( { 'replacement_text': equal_to( '' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 35, 'column_num': 7 } ), 'end' : has_entries( { 'line_num': 35, 'column_num': 9 } ), } ), } ) ), 'location': has_entries( { 'line_num': 35, 'column_num': 7 } ) } ) ) } ) ) def FixIt_Check_cpp11_Repl( results ): assert_that( results, has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( has_entries( { 'replacement_text': equal_to( 'foo' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 40, 'column_num': 6 } ), 'end' : has_entries( { 'line_num': 40, 'column_num': 9 } ), } ), } ) ), 'location': has_entries( { 'line_num': 40, 'column_num': 6 } ) } ) ) } ) ) def FixIt_Check_cpp11_DelAdd( results ): assert_that( results, has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( has_entries( { 'replacement_text': equal_to( '' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 48, 'column_num': 3 } ), 'end' : has_entries( { 'line_num': 48, 'column_num': 4 } ), } ), } ), has_entries( { 'replacement_text': equal_to( '~' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 48, 'column_num': 9 } ), 'end' : has_entries( { 'line_num': 48, 'column_num': 9 } ), } ), } ), ), 'location': has_entries( { 'line_num': 48, 'column_num': 3 } ) } ) ) } ) ) def FixIt_Check_objc( results ): assert_that( results, has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( has_entries( { 'replacement_text': equal_to( 'id' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 5, 'column_num': 3 } ), 'end' : has_entries( { 'line_num': 5, 'column_num': 3 } ), } ), } ) ), 'location': has_entries( { 'line_num': 5, 'column_num': 3 } ) } ) ) } ) ) def FixIt_Check_objc_NoFixIt( results ): # and finally, a warning with no fixits assert_that( results, equal_to( { 'fixits': [] } ) ) def FixIt_Check_cpp11_MultiFirst( results ): assert_that( results, has_entries( { 'fixits': contains_exactly( # first fix-it at 54,16 has_entries( { 'chunks': contains_exactly( has_entries( { 'replacement_text': equal_to( 'foo' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 54, 'column_num': 16 } ), 'end' : has_entries( { 'line_num': 54, 'column_num': 19 } ), } ), } ) ), 'location': has_entries( { 'line_num': 54, 'column_num': 16 } ) } ), # second fix-it at 54,52 has_entries( { 'chunks': contains_exactly( has_entries( { 'replacement_text': equal_to( '' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 54, 'column_num': 52 } ), 'end' : has_entries( { 'line_num': 54, 'column_num': 53 } ), } ), } ), has_entries( { 'replacement_text': equal_to( '~' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 54, 'column_num': 58 } ), 'end' : has_entries( { 'line_num': 54, 'column_num': 58 } ), } ), } ), ), 'location': has_entries( { 'line_num': 54, 'column_num': 52 } ) } ) ) } ) ) def FixIt_Check_cpp11_MultiSecond( results ): assert_that( results, has_entries( { 'fixits': contains_exactly( # second fix-it at 54,52 has_entries( { 'chunks': contains_exactly( has_entries( { 'replacement_text': equal_to( '' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 54, 'column_num': 52 } ), 'end' : has_entries( { 'line_num': 54, 'column_num': 53 } ), } ), } ), has_entries( { 'replacement_text': equal_to( '~' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 54, 'column_num': 58 } ), 'end' : has_entries( { 'line_num': 54, 'column_num': 58 } ), } ), } ), ), 'location': has_entries( { 'line_num': 54, 'column_num': 52 } ) } ), # first fix-it at 54,16 has_entries( { 'chunks': contains_exactly( has_entries( { 'replacement_text': equal_to( 'foo' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 54, 'column_num': 16 } ), 'end' : has_entries( { 'line_num': 54, 'column_num': 19 } ), } ), } ) ), 'location': has_entries( { 'line_num': 54, 'column_num': 16 } ) } ) ) } ) ) def FixIt_Check_unicode_Ins( results ): assert_that( results, has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( has_entries( { 'replacement_text': equal_to( ';' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 21, 'column_num': 39 } ), 'end' : has_entries( { 'line_num': 21, 'column_num': 39 } ), } ), } ) ), 'location': has_entries( { 'line_num': 21, 'column_num': 39 } ) } ) ) } ) ) def FixIt_Check_cpp11_Note( results ): assert_that( results, has_entries( { 'fixits': contains_exactly( # First note: put parens around it has_entries( { 'text': contains_string( 'parentheses around the assignment' ), 'chunks': contains_exactly( ChunkMatcher( '(', LineColMatcher( 59, 8 ), LineColMatcher( 59, 8 ) ), ChunkMatcher( ')', LineColMatcher( 61, 12 ), LineColMatcher( 61, 12 ) ) ), 'location': LineColMatcher( 60, 8 ), } ), # Second note: change to == has_entries( { 'text': contains_string( '==' ), 'chunks': contains_exactly( ChunkMatcher( '==', LineColMatcher( 60, 8 ), LineColMatcher( 60, 9 ) ) ), 'location': LineColMatcher( 60, 8 ), } ) ) } ) ) def FixIt_Check_cpp11_SpellCheck( results ): assert_that( results, has_entries( { 'fixits': contains_exactly( # Change to SpellingIsNotMyStrongPoint has_entries( { 'text': contains_string( "did you mean 'SpellingIsNotMyStrongPoint'" ), 'chunks': contains_exactly( ChunkMatcher( 'SpellingIsNotMyStrongPoint', LineColMatcher( 72, 9 ), LineColMatcher( 72, 35 ) ) ), 'location': LineColMatcher( 72, 9 ), } ) ) } ) ) def FixIt_Check_cuda( results ): assert_that( results, has_entries( { 'fixits': contains_exactly( has_entries( { 'text': contains_string( "error: kernel function type 'int ()' must have void " ), 'chunks': contains_exactly( ChunkMatcher( 'void', LineColMatcher( 3, 12 ), LineColMatcher( 3, 15 ) ) ), 'location': LineColMatcher( 3, 12 ), } ) ) } ) ) @pytest.mark.parametrize( 'test', [ # L # i C # n o # e l Lang File, Checker [ 16, 0, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), FixIt_Check_cpp11_Ins ], [ 16, 1, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), FixIt_Check_cpp11_Ins ], [ 16, 10, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), FixIt_Check_cpp11_Ins ], [ 25, 14, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), FixIt_Check_cpp11_InsMultiLine ], [ 25, 0, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), FixIt_Check_cpp11_InsMultiLine ], [ 35, 7, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), FixIt_Check_cpp11_Del ], [ 40, 6, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), FixIt_Check_cpp11_Repl ], [ 48, 3, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), FixIt_Check_cpp11_DelAdd ], [ 5, 3, 'objective-c', PathToTestFile( 'FixIt_Clang_objc.m' ), FixIt_Check_objc ], [ 7, 1, 'objective-c', PathToTestFile( 'FixIt_Clang_objc.m' ), FixIt_Check_objc_NoFixIt ], [ 3, 12, 'cuda', PathToTestFile( 'cuda', 'fixit_test.cu' ), FixIt_Check_cuda ], # multiple errors on a single line; both with fixits [ 54, 15, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), FixIt_Check_cpp11_MultiFirst ], [ 54, 16, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), FixIt_Check_cpp11_MultiFirst ], [ 54, 16, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), FixIt_Check_cpp11_MultiFirst ], [ 54, 17, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), FixIt_Check_cpp11_MultiFirst ], [ 54, 18, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), FixIt_Check_cpp11_MultiFirst ], # should put closest fix-it first? [ 54, 51, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), FixIt_Check_cpp11_MultiSecond ], [ 54, 52, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), FixIt_Check_cpp11_MultiSecond ], [ 54, 53, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), FixIt_Check_cpp11_MultiSecond ], # unicode in line for fixit [ 21, 16, 'cpp11', PathToTestFile( 'unicode.cc' ), FixIt_Check_unicode_Ins ], # FixIt attached to a "child" diagnostic (i.e. a Note) [ 60, 1, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), FixIt_Check_cpp11_Note ], # FixIt due to forced spell checking [ 72, 9, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), FixIt_Check_cpp11_SpellCheck ], ] ) @SharedYcmd def Subcommands_FixIt_all_test( app, test ): RunFixItTest( app, test[ 0 ], test[ 1 ], test[ 2 ], test[ 3 ], test[ 4 ] ) @SharedYcmd def Subcommands_FixIt_Unity_test( app ): file_path = PathToTestFile( 'unitya.cc' ) args = { 'filetype' : 'cpp', 'completer_target' : 'filetype_default', 'contents' : ReadFile( file_path ), 'filepath' : file_path, 'command_arguments': [ 'FixIt' ], 'line_num' : 11, 'column_num' : 17, } app.post_json( '/load_extra_conf_file', { 'filepath': PathToTestFile( '.ycm_extra_conf.py' ), } ) # Get the fixes for the file. event_data = BuildRequest( **args ) results = app.post_json( '/run_completer_command', event_data ).json pprint( results ) assert_that( results, has_entries( { 'fixits': contains_exactly( has_entries( { 'text': contains_string( "expected ';' after expression" ), 'chunks': contains_exactly( ChunkMatcher( ';', LocationMatcher( file_path, 11, 18 ), LocationMatcher( file_path, 11, 18 ) ), ), 'location': LocationMatcher( file_path, 11, 18 ), } ) ) } ) ) @SharedYcmd def Subcommands_FixIt_UnityDifferentFile_test( app ): # This checks that we only return FixIt for the requested file, not a fixit on # the same line in a different file file_path = PathToTestFile( 'unity.cc' ) args = { 'filetype' : 'cpp', 'completer_target' : 'filetype_default', 'contents' : ReadFile( file_path ), 'filepath' : file_path, 'command_arguments': [ 'FixIt' ], 'line_num' : 11, 'column_num' : 17, } app.post_json( '/load_extra_conf_file', { 'filepath': PathToTestFile( '.ycm_extra_conf.py' ), } ) # Get the fixes for the file. event_data = BuildRequest( **args ) results = app.post_json( '/run_completer_command', event_data ).json pprint( results ) assert_that( results, has_entries( { 'fixits': empty() } ) ) @SharedYcmd def Subcommands_FixIt_NonExistingFile_test( app ): # This checks that FixIt is working for a non-existing file and that the path # is properly normalized ('.' and '..' are removed from the path). file_path = PathToTestFile( 'non_existing_dir', '..', '.', 'non_existing.cc' ) normal_file_path = PathToTestFile( 'non_existing.cc' ) args = { 'filetype' : 'cpp', 'completer_target' : 'filetype_default', 'contents' : 'int test', 'filepath' : file_path, 'command_arguments': [ 'FixIt' ], 'line_num' : 1, 'column_num' : 1, } app.post_json( '/load_extra_conf_file', { 'filepath': PathToTestFile( '.ycm_extra_conf.py' ), } ) # Get the fixes for the file. event_data = BuildRequest( **args ) results = app.post_json( '/run_completer_command', event_data ).json pprint( results ) assert_that( results, has_entries( { 'fixits': contains_exactly( has_entries( { 'text': contains_string( "expected ';' after top level declarator" ), 'chunks': contains_exactly( ChunkMatcher( ';', LocationMatcher( normal_file_path, 1, 9 ), LocationMatcher( normal_file_path, 1, 9 ) ), ), 'location': LocationMatcher( normal_file_path, 1, 9 ), } ) ) } ) ) @SharedYcmd def Subcommands_GetDoc_Variable_test( app ): filepath = PathToTestFile( 'GetDoc_Clang.cc' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'cpp', compilation_flags = [ '-x', 'c++' ], line_num = 70, column_num = 24, contents = contents, command_arguments = [ 'GetDoc' ] ) response = app.post_json( '/run_completer_command', event_data ).json pprint( response ) assert_that( response, has_entry( 'detailed_info', """\ char a_global_variable This really is a global variable. Type: char Name: a_global_variable --- This really is a global variable. The first line of comment is the brief.""" ) ) @SharedYcmd def Subcommands_GetDoc_Method_test( app ): filepath = PathToTestFile( 'GetDoc_Clang.cc' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'cpp', compilation_flags = [ '-x', 'c++' ], line_num = 22, column_num = 13, contents = contents, command_arguments = [ 'GetDoc' ] ) response = app.post_json( '/run_completer_command', event_data ).json pprint( response ) assert_that( response, has_entry( 'detailed_info', """\ char with_brief() brevity is for suckers Type: char () Name: with_brief --- This is not the brief. \\brief brevity is for suckers This is more information """ ) ) @SharedYcmd def Subcommands_GetDoc_Namespace_test( app ): filepath = PathToTestFile( 'GetDoc_Clang.cc' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'cpp', compilation_flags = [ '-x', 'c++' ], line_num = 65, column_num = 14, contents = contents, command_arguments = [ 'GetDoc' ] ) response = app.post_json( '/run_completer_command', event_data ).json pprint( response ) assert_that( response, has_entry( 'detailed_info', """\ namespace Test {} This is a test namespace Type: Name: Test --- This is a test namespace""" ) ) # noqa @SharedYcmd def Subcommands_GetDoc_Undocumented_test( app ): filepath = PathToTestFile( 'GetDoc_Clang.cc' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'cpp', compilation_flags = [ '-x', 'c++' ], line_num = 81, column_num = 17, contents = contents, command_arguments = [ 'GetDoc' ] ) response = app.post_json( '/run_completer_command', event_data, expect_errors = True ) assert_that( response.status_code, equal_to( requests.codes.internal_server_error ) ) assert_that( response.json, ErrorMatcher( ValueError, NO_DOCUMENTATION_MESSAGE ) ) @SharedYcmd def Subcommands_GetDoc_NoCursor_test( app ): filepath = PathToTestFile( 'GetDoc_Clang.cc' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'cpp', compilation_flags = [ '-x', 'c++' ], line_num = 1, column_num = 1, contents = contents, command_arguments = [ 'GetDoc' ] ) response = app.post_json( '/run_completer_command', event_data, expect_errors = True ) assert_that( response.status_code, equal_to( requests.codes.internal_server_error ) ) assert_that( response.json, ErrorMatcher( ValueError, NO_DOCUMENTATION_MESSAGE ) ) @SharedYcmd def Subcommands_GetDoc_SystemHeaders_test( app ): app.post_json( '/load_extra_conf_file', { 'filepath': PathToTestFile( 'get_doc', '.ycm_extra_conf.py' ) } ) filepath = PathToTestFile( 'get_doc', 'test.cpp' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'cpp', line_num = 4, column_num = 7, contents = contents, command_arguments = [ 'GetDoc' ] ) response = app.post_json( '/run_completer_command', event_data ).json assert_that( response, has_entry( 'detailed_info', """\ int test() This is a function. Type: int () Name: test --- \\brief This is a function. This function is defined in a system header. """ ) ) # Following tests repeat the tests above, but without re-parsing the file @SharedYcmd def Subcommands_GetDocImprecise_Variable_test( app ): filepath = PathToTestFile( 'GetDoc_Clang.cc' ) contents = ReadFile( filepath ) app.post_json( '/event_notification', BuildRequest( filepath = filepath, filetype = 'cpp', compilation_flags = [ '-x', 'c++' ], contents = contents, event_name = 'FileReadyToParse' ) ) event_data = BuildRequest( filepath = filepath, filetype = 'cpp', compilation_flags = [ '-x', 'c++' ], line_num = 70, column_num = 24, contents = contents, command_arguments = [ 'GetDocImprecise' ] ) response = app.post_json( '/run_completer_command', event_data ).json pprint( response ) assert_that( response, has_entry( 'detailed_info', """\ char a_global_variable This really is a global variable. Type: char Name: a_global_variable --- This really is a global variable. The first line of comment is the brief.""" ) ) @SharedYcmd def Subcommands_GetDocImprecise_Method_test( app ): filepath = PathToTestFile( 'GetDoc_Clang.cc' ) contents = ReadFile( filepath ) app.post_json( '/event_notification', BuildRequest( filepath = filepath, filetype = 'cpp', compilation_flags = [ '-x', 'c++' ], contents = contents, event_name = 'FileReadyToParse' ) ) event_data = BuildRequest( filepath = filepath, filetype = 'cpp', compilation_flags = [ '-x', 'c++' ], line_num = 22, column_num = 13, contents = contents, command_arguments = [ 'GetDocImprecise' ] ) response = app.post_json( '/run_completer_command', event_data ).json pprint( response ) assert_that( response, has_entry( 'detailed_info', """\ char with_brief() brevity is for suckers Type: char () Name: with_brief --- This is not the brief. \\brief brevity is for suckers This is more information """ ) ) @SharedYcmd def Subcommands_GetDocImprecise_Namespace_test( app ): filepath = PathToTestFile( 'GetDoc_Clang.cc' ) contents = ReadFile( filepath ) app.post_json( '/event_notification', BuildRequest( filepath = filepath, filetype = 'cpp', compilation_flags = [ '-x', 'c++' ], contents = contents, event_name = 'FileReadyToParse' ) ) event_data = BuildRequest( filepath = filepath, filetype = 'cpp', compilation_flags = [ '-x', 'c++' ], line_num = 65, column_num = 14, contents = contents, command_arguments = [ 'GetDocImprecise' ] ) response = app.post_json( '/run_completer_command', event_data ).json pprint( response ) assert_that( response, has_entry( 'detailed_info', """\ namespace Test {} This is a test namespace Type: Name: Test --- This is a test namespace""" ) ) # noqa @SharedYcmd def Subcommands_GetDocImprecise_Undocumented_test( app ): filepath = PathToTestFile( 'GetDoc_Clang.cc' ) contents = ReadFile( filepath ) app.post_json( '/event_notification', BuildRequest( filepath = filepath, filetype = 'cpp', compilation_flags = [ '-x', 'c++' ], contents = contents, event_name = 'FileReadyToParse' ) ) event_data = BuildRequest( filepath = filepath, filetype = 'cpp', compilation_flags = [ '-x', 'c++' ], line_num = 81, column_num = 17, contents = contents, command_arguments = [ 'GetDocImprecise' ] ) response = app.post_json( '/run_completer_command', event_data, expect_errors = True ) assert_that( response.status_code, equal_to( requests.codes.internal_server_error ) ) assert_that( response.json, ErrorMatcher( ValueError, NO_DOCUMENTATION_MESSAGE ) ) @SharedYcmd def Subcommands_GetDocImprecise_NoCursor_test( app ): filepath = PathToTestFile( 'GetDoc_Clang.cc' ) contents = ReadFile( filepath ) app.post_json( '/event_notification', BuildRequest( filepath = filepath, filetype = 'cpp', compilation_flags = [ '-x', 'c++' ], contents = contents, event_name = 'FileReadyToParse' ) ) event_data = BuildRequest( filepath = filepath, filetype = 'cpp', compilation_flags = [ '-x', 'c++' ], line_num = 1, column_num = 1, contents = contents, command_arguments = [ 'GetDocImprecise' ] ) response = app.post_json( '/run_completer_command', event_data, expect_errors = True ) assert_that( response.status_code, equal_to( requests.codes.internal_server_error ) ) assert_that( response.json, ErrorMatcher( ValueError, NO_DOCUMENTATION_MESSAGE ) ) @SharedYcmd def Subcommands_GetDocImprecise_NoReadyToParse_test( app ): filepath = PathToTestFile( 'GetDoc_Clang.cc' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'cpp', compilation_flags = [ '-x', 'c++' ], line_num = 11, column_num = 18, contents = contents, command_arguments = [ 'GetDocImprecise' ] ) response = app.post_json( '/run_completer_command', event_data ).json assert_that( response, has_entry( 'detailed_info', """\ int get_a_global_variable(bool test) This is a method which is only pretend global Type: int (bool) Name: get_a_global_variable --- This is a method which is only pretend global @param test Set this to true. Do it.""" ) ) @SharedYcmd def Subcommands_GetDocImprecise_SystemHeaders_test( app ): app.post_json( '/load_extra_conf_file', { 'filepath': PathToTestFile( 'get_doc', '.ycm_extra_conf.py' ) } ) filepath = PathToTestFile( 'get_doc', 'test.cpp' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'cpp', line_num = 4, column_num = 7, contents = contents, command_arguments = [ 'GetDocImprecise' ] ) response = app.post_json( '/run_completer_command', event_data ).json assert_that( response, has_entry( 'detailed_info', """\ int test() This is a function. Type: int () Name: test --- \\brief This is a function. This function is defined in a system header. """ ) ) @SharedYcmd def Subcommands_GetDoc_Unicode_test( app ): filepath = PathToTestFile( 'unicode.cc' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'cpp', compilation_flags = [ '-x', 'c++' ], line_num = 21, column_num = 16, contents = contents, command_arguments = [ 'GetDoc' ], completer_target = 'filetype_default' ) response = app.post_json( '/run_completer_command', event_data ).json pprint( response ) assert_that( response, has_entry( 'detailed_info', """\ int member_with_å_unicøde This method has unicøde in it Type: int Name: member_with_å_unicøde --- This method has unicøde in it """ ) ) @SharedYcmd def Subcommands_GetDoc_CUDA_test( app ): filepath = PathToTestFile( 'cuda', 'basic.cu' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'cuda', compilation_flags = [ '-x', 'cuda' ], line_num = 8, column_num = 3, contents = contents, command_arguments = [ 'GetDoc' ], completer_target = 'filetype_default' ) response = app.post_json( '/run_completer_command', event_data ).json pprint( response ) assert_that( response, has_entry( 'detailed_info', """\ void kernel() This is a test kernel Type: void () Name: kernel --- This is a test kernel""" ) ) def Subcommands_StillParsingError( app, command ): filepath = PathToTestFile( 'test.cpp' ) data = BuildRequest( command_arguments = [ command ], compilation_flags = [ '-x', 'c++' ], line_num = 1, column_num = 1, filepath = filepath, contents = '', filetype = 'cpp' ) response = app.post_json( '/run_completer_command', data, expect_errors = True ) assert_that( response.status_code, equal_to( requests.codes.internal_server_error ) ) pprint( response.json ) assert_that( response.json, ErrorMatcher( RuntimeError, PARSING_FILE_MESSAGE ) ) @SharedYcmd def Subcommands_StillParsingError_test( app ): completer = handlers._server_state.GetFiletypeCompleter( [ 'cpp' ] ) with patch.object( completer, '_completer', MockCoreClangCompleter() ): Subcommands_StillParsingError( app, 'FixIt' ) Subcommands_StillParsingError( app, 'GetDoc' ) Subcommands_StillParsingError( app, 'GetDocImprecise' ) Subcommands_StillParsingError( app, 'GetParent' ) Subcommands_StillParsingError( app, 'GetType' ) Subcommands_StillParsingError( app, 'GetTypeImprecise' ) Subcommands_StillParsingError( app, 'GoTo' ) Subcommands_StillParsingError( app, 'GoToDeclaration' ) Subcommands_StillParsingError( app, 'GoToDefinition' ) Subcommands_StillParsingError( app, 'GoToImprecise' ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/��������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0022132�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/.ycm_extra_conf.py��������������������������0000664�0000000�0000000�00000001024�13746324601�0025557�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import os # These files are all a single TU 'unity.cc' unity_files = [ 'unitya.cc', 'unityb.cc', 'unity.h', ] def Settings( **kwargs ): filename = kwargs[ 'filename' ] if os.path.basename( filename ) in unity_files: return { 'flags': [ '-x', 'c++', '-I', '.' ], 'override_filename': os.path.join( os.path.dirname( filename ), 'unity.cc' ), 'include_paths_relative_to_dir': os.path.dirname( filename ), } return { 'flags': ['-x', 'c++', '-I', '.'] } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/FixIt_Clang_cpp11.cpp�����������������������0000664�0000000�0000000�00000003410�13746324601�0025767�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // A selection of the tests from llvm/tools/clang/test/FixIt/fixit-cxx0x.cpp // // Modified to test fixits across multiple lines and ensure that the number of // diagnostics doesn't hit the compile threshold. // /* This is a test of the various code modification hints that only apply in C++0x. */ struct A { explicit operator int(); // expected-note{{conversion to integral type}} }; // _FixIt_Check_cpp11_Ins (2 inserts on one line) void x() { switch(A()) { // expected-error{{explicit conversion to}} } } // _FixIt_Check_cpp11_InsMultiLine // same as above, except the 2 inserts split over multiple lines, neiher of // which are the *same* line as the diagnostic report (diagnostic on line // "switch", inserts on following line and later line void y() { switch( // diag A // insert: static_cast<int>( ( ) // insert: ) ) { } } // _FixIt_Check_cpp11_Del using ::T = void; // expected-error {{name defined in alias declaration must be an identifier}} namespace dtor_fixit { class foo { // _FixIt_Check_cpp11_Repl ~bar() { } // expected-error {{expected the class name after '~' to name a destructor}} // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:6-[[@LINE-1]]:9}:"foo" }; class bar { ~bar(); }; // _FixIt_Check_cpp11_DelAdd ~bar::bar() {} // expected-error {{'~' in destructor name should be after nested name specifier}} // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:4}:"" // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:9-[[@LINE-2]]:9}:"~" } namespace test_fixit_multiple { class foo { ~bar() { } }; class bar { ~bar(); }; ~bar::bar() { } } void z() { bool x; if ( x = true ) { } } namespace Typo { struct SpellingIsNotMyStrongPoint; } void typo() { Typo::SpellingIsNotMyStringPiont *p; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/FixIt_Clang_objc.m��������������������������0000664�0000000�0000000�00000000372�13746324601�0025436�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������@protocol X; // _FixIt_Check_objc, _FixIt_Check_objc_NoFixIt void foo() { <X> *P; // expected-warning{{protocol has no object type specified; defaults to qualified 'id'}} char *x = nullptr; // no fix-it for this error (nullptr undefinied) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/GetDoc_Clang.cc�����������������������������0000664�0000000�0000000�00000003600�13746324601�0024711�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "GetDoc_Clang.h" /// This is a test namespace namespace Test { /// This is a global variable, that isn't really all that global. int a_global_variable; /// This is a method which is only pretend global /// @param test Set this to true. Do it. int get_a_global_variable( bool test ); char postfix_comment(); ///< Method postfix /** * This is not the brief. * * \brief brevity is for suckers * * This is more information */ char with_brief(); /** The * indentation * of this comment * is * all messed up. */ int messed_up(); } /// This really is a global variable. /// /// The first line of comment is the brief. char a_global_variable; /** JavaDoc-style */ bool javadoc_style; //! Qt-style bool qt_style; /// This method has lots of lines of text in its comment. /// /// That's important because the preview window /// /// Has /// /// Limited Space. char get_a_global_variable( ); bool postfix_comment; ///< The brief follows the declaration. typedef TestClass AnotherClass; // Double slash is not considered attached to declaration bool double_slash; /* slash asterisk is also not considered attached to declaration */ bool slash_asterisk; /** Main */ int main() { char c = get_a_global_variable( ); int i = Test::get_a_global_variable( true ); typedef int(*FUNCTION_POINTER)( bool ); FUNCTION_POINTER fp = &Test::get_a_global_variable; bool b = c == a_global_variable && i == Test::a_global_variable && javadoc_style && qt_style && postfix_comment && double_slash && slash_asterisk; TestClass tc; const AnotherClass& ac = tc; tc.a_public_method( tc.a_public_var ); tc.an_undocumented_method(); Test::postfix_comment(); Test::messed_up(); return b ? 0 : 1; } ��������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/GetParent_Clang_test.cc���������������������0000664�0000000�0000000�00000001254�13746324601�0026477�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// this file is used in RunCompleterCommand_GetParent_Clang_test struct A { struct B { int x; char do_x(); char do_z_inline() { return 'z'; } template<typename T> char do_anything(T &t) { return t; } }; int y; char do_y(); char do_Z_inline() { return 'Z'; } template<typename T> char do_anything(T &t); }; template<typename T> char A::do_anything(T &t) { return t; } char A::B::do_x() { return 'x'; } char A::do_y() { return 'y'; } int main() { auto l = [](){ return "lambda"; }; l(); return 0; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/GetType_Clang_test.cc�����������������������0000664�0000000�0000000�00000001477�13746324601�0026176�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// This file is used in RunCompleterCommand_GetType_Clang_test namespace Ns { template< typename T > struct BasicType { BasicType( const T * ); }; typedef BasicType< char > Type; } struct Foo { int x; int y; char c; int bar(int i) { return i+1; } }; int main() { Foo foo; Ns::Type a = "hello"; Ns::Type b = a; auto &arFoo = foo; auto *apFoo = &foo; const auto &acrFoo = foo; const auto *acpFoo = &foo; int acry = acrFoo.y; int acpx = acpFoo->x; int ary = arFoo.y; int apx = apFoo->x; Foo &rFoo = foo; Foo *pFoo = &foo; const Foo &crFoo = foo; const Foo *cpFoo = &foo; int cry = crFoo.y; int cpx = cpFoo->x; int ry = rFoo.y; int px = pFoo->x; struct Unicøde; Unicøde *ø; int i = foo.bar(1); int j = apFoo->bar(1); return 0; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/GoTo_Clang_ZeroBasedLineAndColumn_test.cc���0000664�0000000�0000000�00000000246�13746324601�0032025�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// this file is used in RunCompleterCommand_GoTo_Clang_ZeroBasedLineAndColumn_test struct Foo { int x; int y; char c; }; int main() { Foo foo; return 0; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/GoTo_all_Clang_test.cc����������������������0000664�0000000�0000000�00000000764�13746324601�0026313�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// This file is used in RunCompleterCommand_GoTo_all_Clang_test namespace Local { int x; char in_line() { return 'y'; }; char out_of_line(); } char Local::out_of_line() { return 'x'; } int main(); int main() { int ix = Local::x; char cy = Local::in_line(); char cx = Local::out_of_line(); return 0; } void unicode() { /* †est ê */ struct Unicøde { int u; }; struct Another_Unicøde; Unicøde *ç; ç->u; Another_Unicøde *u; u; } ������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/basic.cpp�����������������������������������0000664�0000000�0000000�00000000201�13746324601�0023710�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������struct Foo { int x; int y; char c; }; int main() { Foo foo; // The location after the dot is line 11, col 7 foo. } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/cache_test/���������������������������������0000775�0000000�0000000�00000000000�13746324601�0024234�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/cache_test/foo.h����������������������������0000664�0000000�0000000�00000000000�13746324601�0025156�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/client_data/��������������������������������0000775�0000000�0000000�00000000000�13746324601�0024401�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/client_data/.ycm_extra_conf.py��������������0000664�0000000�0000000�00000000133�13746324601�0030026�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������def Settings( **kwargs ): return { 'flags': kwargs[ 'client_data' ].get( 'flags', [] ) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/client_data/include.cpp���������������������0000664�0000000�0000000�00000000013�13746324601�0026522�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include " ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/client_data/include.hpp���������������������0000664�0000000�0000000�00000000000�13746324601�0026523�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/client_data/macro.cpp�����������������������0000664�0000000�0000000�00000000203�13746324601�0026201�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������struct Test { #ifdef SOME_MACRO int macro_defined; #else int macro_not_defined; #endif }; int main() { Test test; test. } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/client_data/main.cpp������������������������0000664�0000000�0000000�00000000155�13746324601�0026032�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������struct Foo { int x; }; int main() { Foo foo; // The location after the dot is line 9, col 7 foo. } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/completion_availability.cc������������������0000664�0000000�0000000�00000000243�13746324601�0027343�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������class Base { public: int public_member; protected: int protected_member; private: int private_member; }; class Derived : Base { void method() { } }; �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/completion_docstring.cc���������������������0000664�0000000�0000000�00000000100�13746324601�0026655�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/// This is a docstring. void func(); int main() { func(); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/completion_fixit.cc�������������������������0000664�0000000�0000000�00000000077�13746324601�0026021�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������struct Foo { int bar; }; int main() { Foo* foo; foo.b } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/cuda/���������������������������������������0000775�0000000�0000000�00000000000�13746324601�0023046�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/cuda/basic.cu�������������������������������0000664�0000000�0000000�00000000173�13746324601�0024461�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "cuda.h" /// This is a test kernel __global__ void kernel() {} int main() { kernel<<<1, 1>>>(); return 0; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/cuda/completion_test.cu���������������������0000664�0000000�0000000�00000000447�13746324601�0026614�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "cuda.h" namespace Kernels { __device__ void do_something(float* a) {} } template<typename F, class ...Args> __global__ void launch(F& fn, Args args...) { fn(args...); } int main() { // The location after the colon is line 16, col 29 launch<<<1, 1>>>(Kernels:: return 0; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/cuda/cuda.h���������������������������������0000664�0000000�0000000�00000005613�13746324601�0024140�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Modified by ycmd contributors */ /* University of Illinois/NCSA Open Source License Copyright (c) 2007-2016 University of Illinois at Urbana-Champaign. All rights reserved. Developed by: LLVM Team University of Illinois at Urbana-Champaign http://llvm.org Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal with 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: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimers. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimers in the documentation and/or other materials provided with the distribution. * Neither the names of the LLVM Team, University of Illinois at Urbana-Champaign, nor the names of its contributors may be used to endorse or promote products derived from this Software without specific prior written permission. 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 CONTRIBUTORS 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 WITH THE SOFTWARE. */ /* Minimal declarations for CUDA support. Testing purposes only. */ typedef __SIZE_TYPE__ size_t; // Make this file work with nvcc, for testing compatibility. #ifndef __NVCC__ #define __constant__ __attribute__((constant)) #define __device__ __attribute__((device)) #define __global__ __attribute__((global)) #define __host__ __attribute__((host)) #define __shared__ __attribute__((shared)) #define __launch_bounds__(...) __attribute__((launch_bounds(__VA_ARGS__))) struct dim3 { unsigned x, y, z; __host__ __device__ dim3(unsigned x, unsigned y = 1, unsigned z = 1) : x(x), y(y), z(z) {} }; typedef struct cudaStream *cudaStream_t; int cudaConfigureCall(dim3 gridSize, dim3 blockSize, size_t sharedSize = 0, cudaStream_t stream = 0); // Host- and device-side placement new overloads. void *operator new(__SIZE_TYPE__, void *p) { return p; } void *operator new[](__SIZE_TYPE__, void *p) { return p; } __device__ void *operator new(__SIZE_TYPE__, void *p) { return p; } __device__ void *operator new[](__SIZE_TYPE__, void *p) { return p; } #endif // !__NVCC__ ���������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/cuda/fixit_test.cu��������������������������0000664�0000000�0000000�00000000136�13746324601�0025561�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "cuda.h" __global__ int kernel(); int main() { kernel<<<1, 1>>>(); return 0; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/cuda/kernel_call.cu�������������������������0000664�0000000�0000000�00000005120�13746324601�0025650�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Modified by ycmd contributors */ /* University of Illinois/NCSA Open Source License Copyright (c) 2007-2016 University of Illinois at Urbana-Champaign. All rights reserved. Developed by: LLVM Team University of Illinois at Urbana-Champaign http://llvm.org Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal with 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: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimers. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimers in the documentation and/or other materials provided with the distribution. * Neither the names of the LLVM Team, University of Illinois at Urbana-Champaign, nor the names of its contributors may be used to endorse or promote products derived from this Software without specific prior written permission. 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 CONTRIBUTORS 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 WITH THE SOFTWARE. */ #include "cuda.h" __global__ void g1(int x) {} template <typename T> void t1(T arg) { g1<<<arg, arg>>>(1); } void h1(int x) {} int h2(int x) { return 1; } int main(void) { g1<<<1, 1>>>(42); g1(42); // expected-error {{call to global function 'g1' not configured}} g1<<<1>>>(42); // expected-error {{too few execution configuration arguments to kernel function call}} g1<<<1, 1, 0, 0, 0>>>(42); // expected-error {{too many execution configuration arguments to kernel function call}} t1(1); h1<<<1, 1>>>(42); // expected-error {{kernel call to non-global function 'h1'}} int (*fp)(int) = h2; fp<<<1, 1>>>(42); // expected-error {{must have void return type}} g1<<<undeclared, 1>>>(42); // expected-error {{use of undeclared identifier 'undeclared'}} } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/driver_mode_cl/�����������������������������0000775�0000000�0000000�00000000000�13746324601�0025107�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/driver_mode_cl/executable/������������������0000775�0000000�0000000�00000000000�13746324601�0027230�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/driver_mode_cl/executable/.ycm_extra_conf.py0000664�0000000�0000000�00000000376�13746324601�0032666�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import os DIR_OF_THIS_SCRIPT = os.path.dirname( os.path.abspath( __file__ ) ) def Settings( **kwargs ): return { 'flags': [ 'clang-cl', '-xc++', '/c', '/I', 'driver_mode_cl_include' ], 'include_paths_relative_to_dir': DIR_OF_THIS_SCRIPT } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/driver_mode_cl/executable/driver_mode_cl.cpp0000664�0000000�0000000�00000000236�13746324601�0032712�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "driver_mode_cl_include.h" int main(void) { // Position after the last underscore: // line = 8 // column = 18 driver_mode_cl_ return 0; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������driver_mode_cl_include/�����������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0033631�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/driver_mode_cl/executable��������������������������������������������������������������������������������������driver_mode_cl_include.h����������������������������������������������������������������������������0000664�0000000�0000000�00000000262�13746324601�0040462�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/driver_mode_cl/executable/driver_mode_cl_include���������������������������������������������������������������#ifndef DRIVER_MODE_CL_INCLUDE_H #define DRIVER_MODE_CL_INCLUDE_H void driver_mode_cl_include_func(void); int driver_mode_cl_include_int; #endif /* DRIVER_MODE_CL_INCLUDE_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/driver_mode_cl/flag/������������������������0000775�0000000�0000000�00000000000�13746324601�0026020�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/driver_mode_cl/flag/.ycm_extra_conf.py������0000664�0000000�0000000�00000000415�13746324601�0031450�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import os DIR_OF_THIS_SCRIPT = os.path.dirname( os.path.abspath( __file__ ) ) def Settings( **kwargs ): return { 'flags': [ 'g++', '-xc++', '--driver-mode=cl', '/c', '/I', 'driver_mode_cl_include' ], 'include_paths_relative_to_dir': DIR_OF_THIS_SCRIPT } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/driver_mode_cl/flag/driver_mode_cl.cpp������0000664�0000000�0000000�00000000236�13746324601�0031502�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "driver_mode_cl_include.h" int main(void) { // Position after the last underscore: // line = 8 // column = 18 driver_mode_cl_ return 0; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/driver_mode_cl/flag/driver_mode_cl_include/�0000775�0000000�0000000�00000000000�13746324601�0032500�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������driver_mode_cl_include.h����������������������������������������������������������������������������0000664�0000000�0000000�00000000262�13746324601�0037252�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/driver_mode_cl/flag/driver_mode_cl_include���������������������������������������������������������������������#ifndef DRIVER_MODE_CL_INCLUDE_H #define DRIVER_MODE_CL_INCLUDE_H void driver_mode_cl_include_func(void); int driver_mode_cl_include_int; #endif /* DRIVER_MODE_CL_INCLUDE_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/general_fallback/���������������������������0000775�0000000�0000000�00000000000�13746324601�0025366�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/general_fallback/.ycm_extra_conf.py���������0000664�0000000�0000000�00000001106�13746324601�0031014�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ftopts = { 'cpp': [ '-Wall', '-Wextra', '-x', 'c++', '-std=c++11', ], 'c': [ '-Wall', '-Wextra', '-std=c99', '-x', 'c', '-I', '.', ], 'cuda': [ '-Wall', '-Wextra', '-x', 'cuda', '--std=c++11', ], 'objc': [ '-x', 'objective-c', '-I', '.', ], } def Settings(**kwargs): client_data = kwargs['client_data'] ft = client_data['&filetype'] try: opts = ftopts[ft] except: opts = ftopts['cpp'] if 'throw' in client_data: raise ValueError( client_data['throw'] ) return { 'flags': opts } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/general_fallback/lang_c.c�������������������0000664�0000000�0000000�00000005007�13746324601�0026757�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Identifier completion makes sense for duck-typing. // // The purpose of this test is to both demonstrate the use-case and test the // functionality. Completions of "A.a_parameter" using the identifier // completer feels natural, whereas offering no suggestions feels broken typedef struct { int a_parameter; int another_parameter; } TTest; typedef struct { int another_int; int and_a_final_int; } TTest_Which_Is_Not_TTest; static void do_something( int ); static void do_another_thing( int ); // TESTCASE1: use of . on macro parameter. Macros used this way are compile-time // duck-typing. Note this in this particular instance, libclang actually // offers semantic completions if there is only call to this macro // (e.g. DO_SOMETHING_TO( a_test ) - clever little shrew), so we don't call it /* 1 2 3 4 1234567890123456789012345678901234567890123456789 */ #define DO_SOMETHING_TO( A ) \ do { \ do_something( A.a_parameter ); \ do_another_thing( A.another_parameter ); \ } while (0) // TESTCASE2: use of -> on macro parameter. Macros used this way are // compile-time duck-typing /* 1 2 3 4 1234567890123456789012345678901234567890123456789 */ #define DO_SOMETHING_VIA( P ) \ do { \ do_something( P->a_parameter ); \ do_another_thing( P->another_parameter ); \ } while (0) int main( int argc, char ** argv ) { TTest a_test; // TESTCASE3: use of -> on struct. This is an error, but the user might // subsequently change a_test to be a pointer. Assumption: user knows what they // are doing /* 1 2 3 4 1234567890123456789012345678901234567890123456789 */ if ( a_test->anoth ) { (void) 0; } // TESTCASE4: use of . on struct. Gets semantic suggestions /* 1 2 3 4 1234567890123456789012345678901234567890123456789 */ if ( a_test.a_parameter ) { (void) 0; } // TESTCASE5: use of . on struct for non-matching text. Again, probably an // error, but assume the user knows best, and might change it later. /* 1 2 3 4 1234567890123456789012345678901234567890123456789 */ if ( a_test.do_ ) { (void) 0; } TTest *p_test = &a_test; // TESTCASE6: use of -> on pointer. Semantic completions /* 1 2 3 4 1234567890123456789012345678901234567890123456789 */ if ( p_test-> ) { } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/general_fallback/lang_cpp.cc����������������0000664�0000000�0000000�00000002450�13746324601�0027461�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������namespace { struct TTest { static void PrintDiagnostic(); int a_parameter; int another_parameter; }: void do_something(int); void do_another_thing(int); // TESTCASE1: use of . on template argument. Templates used this way are // compile-time duck typing template<typename T> void DO_SOMETHING_TO( T& t ) { /* 1 2 3 4 1234567890123456789012345678901234567890123456789 */ do_something( t.a_parameter ); } // TESTCASE2: use of -> on template argument. Templates used this way are // compile-time duck typing template<typename T> void DO_SOMETHING_WITH( T* t ) { /* 1 2 3 4 1234567890123456789012345678901234567890123456789 */ do_something( t->a_parameter ); } // TESTCASE3: use of :: on template argument. Templates used this way are // compile-time duck typing template<typename T> void PRINT_A_DIAGNOSTIC( ) { /* 1 2 3 4 1234567890123456789012345678901234567890123456789 */ T::PrintDiagnostic(); } } int main (int , char **) { // bonus test case (regression test) for identifier/forced semantic without // trigger TTest test; /* 1 2 3 4 1234567890123456789012345678901234567890123456789 */ DO_SOMETHING_TO(test); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/get_doc/������������������������������������0000775�0000000�0000000�00000000000�13746324601�0023536�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/get_doc/.ycm_extra_conf.py������������������0000664�0000000�0000000�00000000305�13746324601�0027164�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import os DIR_OF_THIS_SCRIPT = os.path.dirname( os.path.abspath( __file__ ) ) def Settings( **kwargs ): return { 'flags': [ '-isystem', os.path.join( DIR_OF_THIS_SCRIPT, 'include' ) ] } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/get_doc/include/����������������������������0000775�0000000�0000000�00000000000�13746324601�0025161�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/get_doc/include/test.hpp��������������������0000664�0000000�0000000�00000000145�13746324601�0026651�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * \brief This is a function. * * This function is defined in a system header. */ int test(); ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/get_doc/test.cpp����������������������������0000664�0000000�0000000�00000000060�13746324601�0025215�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "test.hpp" int main() { test(); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/location_extent.cc��������������������������0000664�0000000�0000000�00000000124�13746324601�0025635�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������class Test { Test()// ignore comment }; multiline_\ identifier Test::Test() { } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/max_diagnostics.cc��������������������������0000664�0000000�0000000�00000000071�13746324601�0025613�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������int main() { int test; int test; int test; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/multiple_missing_includes.cc����������������0000664�0000000�0000000�00000000103�13746324601�0027705�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "first_missing_include" #include "second_missing_include" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/noflags/������������������������������������0000775�0000000�0000000�00000000000�13746324601�0023563�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/noflags/.ycm_extra_conf.py������������������0000664�0000000�0000000�00000000063�13746324601�0027212�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������def Settings( **kwargs ): return { 'flags': [] } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/noflags/basic.cpp���������������������������0000664�0000000�0000000�00000000201�13746324601�0025341�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������struct Foo { int x; int y; char c; }; int main() { Foo foo; // The location after the dot is line 11, col 7 foo. } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/test-include/�������������������������������0000775�0000000�0000000�00000000000�13746324601�0024532�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/test-include/.ycm_extra_conf.py�������������0000664�0000000�0000000�00000000430�13746324601�0030157�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import os.path def Settings( **kwargs ): d = os.path.dirname( kwargs[ 'filename' ] ) return { 'flags': [ '-iquote', os.path.join( d, 'quote' ), '-I', os.path.join( d, 'system' ), '-iframework', os.path.join( d, 'Frameworks' ) ] } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/test-include/Frameworks/��������������������0000775�0000000�0000000�00000000000�13746324601�0026652�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/test-include/Frameworks/OpenGL.framework/���0000775�0000000�0000000�00000000000�13746324601�0031772�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Headers/��������������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0033266�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/test-include/Frameworks/OpenGL.framework�����������������������������������������������������������������������gl.h������������������������������������������������������������������������������������������������0000664�0000000�0000000�00000000000�13746324601�0034027�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/test-include/Frameworks/OpenGL.framework/Headers���������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/test-include/Frameworks/common.framework/���0000775�0000000�0000000�00000000000�13746324601�0032136�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Headers/��������������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0033432�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/test-include/Frameworks/common.framework�����������������������������������������������������������������������dummy.h���������������������������������������������������������������������������������������������0000664�0000000�0000000�00000000000�13746324601�0034724�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/test-include/Frameworks/common.framework/Headers���������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/test-include/a.hpp��������������������������0000664�0000000�0000000�00000000000�13746324601�0025451�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/test-include/dir with spaces/���������������0000775�0000000�0000000�00000000000�13746324601�0027503�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/test-include/dir with spaces/d.hpp����������0000664�0000000�0000000�00000000000�13746324601�0030425�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/test-include/main.cpp�����������������������0000664�0000000�0000000�00000000673�13746324601�0026170�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "a.hpp" // ./a.hpp #include <a.hpp> // system/a.hpp #include "b.hpp" // quote/b.hpp #include <b.hpp> // error #include "c.hpp" // system/c.hpp #include <c.hpp> // system/c.hpp #include "OpenGL/gl.h" // Frameworks/OpenGL.framework/Headers/gl.h #include <OpenGL/gl.h> // Frameworks/OpenGL.framework/Headers/gl.h // not an #include line #include "dir with spaces/d.hpp" #include <system/ #include :" #include "OpenGL/ #include <OpenGL/ ���������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/test-include/quote/�������������������������0000775�0000000�0000000�00000000000�13746324601�0025667�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/test-include/quote/b.hpp��������������������0000664�0000000�0000000�00000000000�13746324601�0026607�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/test-include/system/������������������������0000775�0000000�0000000�00000000000�13746324601�0026056�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/test-include/system/a.hpp�������������������0000664�0000000�0000000�00000000000�13746324601�0026775�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/test-include/system/c.hpp�������������������0000664�0000000�0000000�00000000000�13746324601�0026777�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/test-include/system/common/�����������������0000775�0000000�0000000�00000000000�13746324601�0027346�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/test-include/system/common/common�����������0000664�0000000�0000000�00000000000�13746324601�0030547�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/unicode.cc����������������������������������0000664�0000000�0000000�00000000513�13746324601�0024066�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ int main( int argc, char ** argv ) { int id_with_å_unicode_chars = 10; struct MyStruct { int member_with_å_unicøde; } myå; myå.w } int another_main() { struct MyStruct { /** * This method has unicøde in it */ int member_with_å_unicøde; } myå; int a = myå.member_with_å_unicøde } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/unity.cc������������������������������������0000664�0000000�0000000�00000000451�13746324601�0023611�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������struct Unity { int this_is_an_it; }; static void static_method() { } namespace { void unity_method( int p ); } #include "unitya.cc" namespace { void unity_method( int p ) { Unity u; UnityA a; extern_method( &u ); do_unity_A( &u ); if (a.an_int) { } } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/unity.h�������������������������������������0000664�0000000�0000000�00000000150�13746324601�0023447�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������void header_method() { unity_method( 1 ); fake_method(); } extern void extern_method( Unity * u ); ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clang/testdata/unitya.cc�����������������������������������0000664�0000000�0000000�00000000233�13746324601�0023750�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "unity.h" struct UnityA { char a_char; int an_int; }; void do_unity_A( Unity* u ) { unity_method( u->this_is_an_it ); static_method() } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/����������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0020465�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/__init__.py�����������������������������������������0000664�0000000�0000000�00000006254�13746324601�0022605�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2018-2020 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 <http://www.gnu.org/licenses/>. import json from hamcrest import assert_that, equal_to from ycmd.tests.clangd.conftest import * # noqa from ycmd.tests.test_utils import ( CombineRequest, WaitUntilCompleterServerReady ) from ycmd.utils import ReadFile shared_app = None def RunAfterInitialized( app, test ): """Performs initialization of clangd server for the file contents specified in the |test| and optionally can run a test and check for its response. Since LSP servers do not start until initialization we need to send a FileReadyToParse request prior to any other request we will make. |test| consists of two parts a 'request' to be made and an optional 'expect' to perform a check on server's response. Request part must contain either a 'content' or 'filepath' element which either contains or points to the source code that will be sent to the server. In addition to that, if |test| also contain a 'route' element, then a follow-up request will be made to the server, with the same file contents and response of that request will be returned. Expect part, if specified, must contain two elements named 'response' and 'data' which are used to check status code and data of the result received from server before returning them to the caller. Example usage: filepath = PathToTestFile( 'foo.cc' ) request = { 'filepath': filepath, 'filetype': 'cpp' } test = { 'request': request } RunAfterInitialized( app, test ) ... """ request = test[ 'request' ] contents = ( request[ 'contents' ] if 'contents' in request else ReadFile( request[ 'filepath' ] ) ) response = app.post_json( '/event_notification', CombineRequest( request, { 'event_name': 'FileReadyToParse', 'contents': contents, } ), expect_errors = True ) WaitUntilCompleterServerReady( app, 'cpp' ) if 'route' in test: expect_errors = 'expect' in test response = app.post_json( test[ 'route' ], CombineRequest( request, { 'contents': contents } ), expect_errors = expect_errors ) if 'expect' in test: print( f'Completer response: { json.dumps( response.json, indent = 2 ) }' ) assert_that( response.status_code, equal_to( test[ 'expect' ][ 'response' ] ) ) assert_that( response.json, test[ 'expect' ][ 'data' ] ) return response.json ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/conftest.py�����������������������������������������0000664�0000000�0000000�00000006642�13746324601�0022674�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import os import pytest from ycmd.tests.test_utils import ( ClearCompletionsCache, IgnoreExtraConfOutsideTestsFolder, IsolatedApp, SetUpApp, StopCompleterServer ) from ycmd.completers.cpp import clangd_completer shared_app = None @pytest.fixture( scope='module', autouse=True ) def set_up_shared_app(): global shared_app shared_app = SetUpApp() yield StopCompleterServer( shared_app, 'cpp' ) @pytest.fixture def app( request ): which = request.param[ 0 ] assert which == 'isolated' or which == 'shared' if which == 'isolated': with IsolatedApp( request.param[ 1 ] ) as app: clangd_completer.CLANGD_COMMAND = clangd_completer.NOT_CACHED yield app StopCompleterServer( app, 'cpp' ) else: global shared_app ClearCompletionsCache() with IgnoreExtraConfOutsideTestsFolder(): yield shared_app """Defines a decorator to be attached to tests of this package. This decorator passes the shared ycmd application as a parameter.""" SharedYcmd = pytest.mark.parametrize( # Name of the fixture/function argument 'app', # Fixture parameters, passed to app() as request.param [ ( 'shared', ) ], # Non-empty ids makes fixture parameters visible in pytest verbose output ids = [ '' ], # Execute the fixture, instead of passing parameters directly to the # function argument indirect = True ) def IsolatedYcmd( custom_options = {} ): """Defines a decorator to be attached to tests of this package. This decorator passes a unique ycmd application as a parameter. It should be used on tests that change the server state in a irreversible way (ex: a semantic subserver is stopped or restarted) or expect a clean state (ex: no semantic subserver started, no .ycm_extra_conf.py loaded, etc). Use the optional parameter |custom_options| to give additional options and/or override the default ones. Example usage: from ycmd.tests.python import IsolatedYcmd @IsolatedYcmd( { 'python_binary_path': '/some/path' } ) def CustomPythonBinaryPath_test( app ): ... """ return pytest.mark.parametrize( # Name of the fixture/function argument 'app', # Fixture parameters, passed to app() as request.param [ ( 'isolated', custom_options ) ], # Non-empty ids makes fixture parameters visible in pytest verbose output ids = [ '' ], # Execute the fixture, instead of passing parameters directly to the # function argument indirect = True ) def PathToTestFile( *args ): dir_of_current_script = os.path.dirname( os.path.abspath( __file__ ) ) return os.path.join( dir_of_current_script, 'testdata', *args ) ����������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/debug_info_test.py����������������������������������0000664�0000000�0000000�00000035121�13746324601�0024201�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2011-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_exactly, empty, has_entries, has_entry, has_items ) from ycmd.tests.clangd import ( IsolatedYcmd, PathToTestFile, SharedYcmd, RunAfterInitialized ) from ycmd.tests.test_utils import ( BuildRequest, TemporaryClangProject, TemporaryTestDir, MacOnly ) import os @IsolatedYcmd() def DebugInfo_NotInitialized_test( app ): request_data = BuildRequest( filepath = PathToTestFile( 'basic.cpp' ), filetype = 'cpp' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'C-family', 'servers': contains_exactly( has_entries( { 'name': 'Clangd', 'pid': None, 'is_running': False, 'extras': contains_exactly( has_entries( { 'key': 'Server State', 'value': 'Dead', } ), has_entries( { 'key': 'Project Directory', 'value': None, } ), has_entries( { 'key': 'Settings', 'value': '{}', } ), has_entries( { 'key': 'Compilation Command', 'value': False, } ), ), } ) ), 'items': empty(), } ) ) ) @SharedYcmd def DebugInfo_Initialized_test( app ): request_data = BuildRequest( filepath = PathToTestFile( 'basic.cpp' ), filetype = 'cpp' ) test = { 'request': request_data } RunAfterInitialized( app, test ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'C-family', 'servers': contains_exactly( has_entries( { 'name': 'Clangd', 'is_running': True, 'extras': contains_exactly( has_entries( { 'key': 'Server State', 'value': 'Initialized', } ), has_entries( { 'key': 'Project Directory', 'value': PathToTestFile(), } ), has_entries( { 'key': 'Settings', 'value': '{}', } ), has_entries( { 'key': 'Compilation Command', 'value': False, } ), ), } ) ), 'items': empty() } ) ) ) @IsolatedYcmd( { 'extra_conf_globlist': [ PathToTestFile( 'extra_conf', '.ycm_extra_conf.py' ) ] } ) def DebugInfo_ExtraConf_ReturningFlags_test( app ): request_data = BuildRequest( filepath = PathToTestFile( 'extra_conf', 'foo.cpp' ), filetype = 'cpp' ) test = { 'request': request_data } RunAfterInitialized( app, test ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'C-family', 'servers': contains_exactly( has_entries( { 'name': 'Clangd', 'is_running': True, 'extras': contains_exactly( has_entries( { 'key': 'Server State', 'value': 'Initialized', } ), has_entries( { 'key': 'Project Directory', 'value': PathToTestFile( 'extra_conf' ), } ), has_entries( { 'key': 'Settings', 'value': '{}', } ), has_entries( { 'key': 'Compilation Command', 'value': has_items( '-I', 'include', '-DFOO' ), } ), ), } ) ), 'items': empty() } ) ) ) @IsolatedYcmd( { 'extra_conf_globlist': [ PathToTestFile( 'extra_conf', '.ycm_extra_conf.py' ) ] } ) def DebugInfo_ExtraConf_NotReturningFlags_test( app ): request_data = BuildRequest( filepath = PathToTestFile( 'extra_conf', 'xyz.cpp' ), filetype = 'cpp' ) request_data[ 'contents' ] = '' test = { 'request': request_data } RunAfterInitialized( app, test ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'C-family', 'servers': contains_exactly( has_entries( { 'name': 'Clangd', 'is_running': True, 'extras': contains_exactly( has_entries( { 'key': 'Server State', 'value': 'Initialized', } ), has_entries( { 'key': 'Project Directory', 'value': PathToTestFile( 'extra_conf' ), } ), has_entries( { 'key': 'Settings', 'value': '{}', } ), has_entries( { 'key': 'Compilation Command', 'value': False } ), ), } ) ), 'items': empty() } ) ) ) @IsolatedYcmd( { 'global_ycm_extra_conf': PathToTestFile( 'extra_conf', 'global_extra_conf.py' ), } ) def DebugInfo_ExtraConf_Global_test( app ): request_data = BuildRequest( filepath = PathToTestFile( 'foo.cpp' ), contents = '', filetype = 'cpp' ) test = { 'request': request_data } request_data[ 'contents' ] = '' RunAfterInitialized( app, test ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'C-family', 'servers': contains_exactly( has_entries( { 'name': 'Clangd', 'is_running': True, 'extras': contains_exactly( has_entries( { 'key': 'Server State', 'value': 'Initialized', } ), has_entries( { 'key': 'Project Directory', 'value': PathToTestFile(), } ), has_entries( { 'key': 'Settings', 'value': '{}', } ), has_entries( { 'key': 'Compilation Command', 'value': has_items( '-I', 'test' ), } ), ), } ) ), 'items': empty() } ) ) ) @IsolatedYcmd( { 'global_ycm_extra_conf': PathToTestFile( 'extra_conf', 'global_extra_conf.py' ), 'extra_conf_globlist': [ PathToTestFile( 'extra_conf', '.ycm_extra_conf.py' ) ] } ) def DebugInfo_ExtraConf_LocalOverGlobal_test( app ): request_data = BuildRequest( filepath = PathToTestFile( 'extra_conf', 'foo.cpp' ), filetype = 'cpp' ) test = { 'request': request_data } RunAfterInitialized( app, test ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'C-family', 'servers': contains_exactly( has_entries( { 'name': 'Clangd', 'is_running': True, 'extras': contains_exactly( has_entries( { 'key': 'Server State', 'value': 'Initialized', } ), has_entries( { 'key': 'Project Directory', 'value': PathToTestFile( 'extra_conf' ), } ), has_entries( { 'key': 'Settings', 'value': '{}', } ), has_entries( { 'key': 'Compilation Command', 'value': has_items( '-I', 'include', '-DFOO' ), } ), ), } ) ), 'items': empty() } ) ) ) @IsolatedYcmd() def DebugInfo_ExtraConf_Database_test( app ): with TemporaryTestDir() as tmp_dir: database = [ { 'directory': tmp_dir, 'command': 'clang++ -x c++ -I test foo.cpp' , 'file': os.path.join( tmp_dir, 'foo.cpp' ), } ] with TemporaryClangProject( tmp_dir, database ): request_data = BuildRequest( filepath = os.path.join( tmp_dir, 'foo.cpp' ), filetype = 'cpp' ) request_data[ 'contents' ] = '' test = { 'request': request_data } RunAfterInitialized( app, test ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'C-family', 'servers': contains_exactly( has_entries( { 'name': 'Clangd', 'is_running': True, 'extras': contains_exactly( has_entries( { 'key': 'Server State', 'value': 'Initialized', } ), has_entries( { 'key': 'Project Directory', 'value': tmp_dir, } ), has_entries( { 'key': 'Settings', 'value': '{}', } ), has_entries( { 'key': 'Compilation Command', 'value': False } ), ), } ) ), 'items': empty() } ) ) ) @IsolatedYcmd( { 'confirm_extra_conf': 0 } ) def DebugInfo_ExtraConf_UseLocalOverDatabase_test( app ): with TemporaryTestDir() as tmp_dir: database = [ { 'directory': tmp_dir, 'command': 'clang++ -x c++ -I test foo.cpp' , 'file': os.path.join( tmp_dir, 'foo.cpp' ), } ] with TemporaryClangProject( tmp_dir, database ): extra_conf = os.path.join( tmp_dir, '.ycm_extra_conf.py' ) with open( extra_conf, 'w' ) as f: f.write( ''' def Settings( **kwargs ): return { 'flags': [ '-x', 'c++', '-I', 'ycm' ] } ''' ) try: request_data = BuildRequest( filepath = os.path.join( tmp_dir, 'foo.cpp' ), filetype = 'cpp' ) request_data[ 'contents' ] = '' test = { 'request': request_data } RunAfterInitialized( app, test ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'C-family', 'servers': contains_exactly( has_entries( { 'name': 'Clangd', 'is_running': True, 'extras': contains_exactly( has_entries( { 'key': 'Server State', 'value': 'Initialized', } ), has_entries( { 'key': 'Project Directory', 'value': tmp_dir, } ), has_entries( { 'key': 'Settings', 'value': '{}', } ), has_entries( { 'key': 'Compilation Command', 'value': has_items( '-x', 'c++', '-I', 'ycm' ) } ), ), } ) ), 'items': empty() } ) ) ) finally: os.remove( extra_conf ) @IsolatedYcmd( { 'global_ycm_extra_conf': PathToTestFile( 'extra_conf', 'global_extra_conf.py' ), } ) def DebugInfo_ExtraConf_UseDatabaseOverGlobal_test( app ): with TemporaryTestDir() as tmp_dir: database = [ { 'directory': tmp_dir, 'command': 'clang++ -x c++ -I test foo.cpp' , 'file': os.path.join( tmp_dir, 'foo.cpp' ), } ] with TemporaryClangProject( tmp_dir, database ): request_data = BuildRequest( filepath = os.path.join( tmp_dir, 'foo.cpp' ), filetype = 'cpp' ) request_data[ 'contents' ] = '' test = { 'request': request_data } RunAfterInitialized( app, test ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'C-family', 'servers': contains_exactly( has_entries( { 'name': 'Clangd', 'is_running': True, 'extras': contains_exactly( has_entries( { 'key': 'Server State', 'value': 'Initialized', } ), has_entries( { 'key': 'Project Directory', 'value': tmp_dir, } ), has_entries( { 'key': 'Settings', 'value': '{}', } ), has_entries( { 'key': 'Compilation Command', 'value': False } ), ), } ) ), 'items': empty() } ) ) ) @MacOnly @IsolatedYcmd( { 'extra_conf_globlist': [ PathToTestFile( 'extra_conf', '.ycm_extra_conf.py' ) ] } ) def DebugInfo_ExtraConf_MacIncludeFlags_test( app ): request_data = BuildRequest( filepath = PathToTestFile( 'extra_conf', 'foo.cpp' ), filetype = 'cpp' ) test = { 'request': request_data } RunAfterInitialized( app, test ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'C-family', 'servers': contains_exactly( has_entries( { 'name': 'Clangd', 'is_running': True, 'extras': contains_exactly( has_entries( { 'key': 'Server State', 'value': 'Initialized', } ), has_entries( { 'key': 'Project Directory', 'value': PathToTestFile( 'extra_conf' ), } ), has_entries( { 'key': 'Settings', 'value': '{}', } ), has_entries( { 'key': 'Compilation Command', 'value': has_items( '-isystem', '-iframework' ) } ), ), } ) ), 'items': empty() } ) ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/diagnostics_test.py���������������������������������0000664�0000000�0000000�00000044143�13746324601�0024413�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import os from pprint import pformat from hamcrest import ( assert_that, contains_exactly, contains_string, empty, equal_to, has_entries, has_entry, has_items ) from unittest.mock import patch from pprint import pprint from ycmd.tests.clangd import ( IsolatedYcmd, PathToTestFile, RunAfterInitialized ) from ycmd.tests.test_utils import ( BuildRequest, LocationMatcher, RangeMatcher, PollForMessages, TemporaryTestDir, UnixOnly ) from ycmd.utils import ReadFile from ycmd import handlers @IsolatedYcmd() def Diagnostics_ZeroBasedLineAndColumn_test( app ): contents = """ void foo() { double baz = "foo"; } // Padding to 5 lines // Padding to 5 lines """ filepath = PathToTestFile( 'foo.cc' ) request = { 'contents': contents, 'filepath': filepath, 'filetype': 'cpp' } test = { 'request': request, 'route': '/receive_messages' } results = RunAfterInitialized( app, test ) assert_that( results, contains_exactly( has_entries( { 'diagnostics': contains_exactly( has_entries( { 'kind': equal_to( 'ERROR' ), 'text': contains_string( 'Cannot initialize' ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 3, 10 ), ( 3, 13 ) ) ), 'location': LocationMatcher( filepath, 3, 10 ), 'location_extent': RangeMatcher( filepath, ( 3, 10 ), ( 3, 13 ) ) } ) ) } ) ) ) @IsolatedYcmd() def Diagnostics_SimpleLocationExtent_test( app ): contents = """ void foo() { baz = 5; } // Padding to 5 lines // Padding to 5 lines """ filepath = PathToTestFile( 'foo.cc' ) request = { 'contents': contents, 'filepath': filepath, 'filetype': 'cpp' } test = { 'request': request, 'route': '/receive_messages' } results = RunAfterInitialized( app, test ) assert_that( results, contains_exactly( has_entries( { 'diagnostics': contains_exactly( has_entries( { 'location_extent': RangeMatcher( filepath, ( 3, 3 ), ( 3, 6 ) ) } ) ) } ) ) ) @IsolatedYcmd() def Diagnostics_PragmaOnceWarningIgnored_test( app ): contents = """ #pragma once struct Foo { int x; int y; int c; int d; }; """ request = { 'contents': contents, 'filepath': PathToTestFile( 'foo.h' ), 'filetype': 'cpp' } test = { 'request': request, 'route': '/receive_messages' } response = RunAfterInitialized( app, test ) assert_that( response, contains_exactly( has_entries( { 'diagnostics': empty() } ) ) ) @IsolatedYcmd() def Diagnostics_Works_test( app ): contents = """ struct Foo { int x // semicolon missing here! int y; int c; int d; }; """ filepath = PathToTestFile( 'foo.cc' ) request = { 'contents': contents, 'filepath': filepath, 'filetype': 'cpp' } test = { 'request': request, 'route': '/receive_messages' } RunAfterInitialized( app, test ) diag_data = BuildRequest( line_num = 3, contents = contents, filepath = filepath, filetype = 'cpp' ) results = app.post_json( '/detailed_diagnostic', diag_data ).json assert_that( results, has_entry( 'message', contains_string( "Expected ';'" ) ) ) @IsolatedYcmd() def Diagnostics_Multiline_test( app ): contents = """ struct Foo { Foo(int z) {} }; int main() { Foo foo("goo"); } """ filepath = PathToTestFile( 'foo.cc' ) request = { 'contents': contents, 'filepath': filepath, 'filetype': 'cpp' } test = { 'request': request, 'route': '/receive_messages' } RunAfterInitialized( app, test ) diag_data = BuildRequest( line_num = 7, contents = contents, filepath = filepath, filetype = 'cpp' ) results = app.post_json( '/detailed_diagnostic', diag_data ).json assert_that( results, has_entry( 'message', contains_string( "\n" ) ) ) @IsolatedYcmd() def Diagnostics_FixIt_Available_test( app ): filepath = PathToTestFile( 'FixIt_Clang_cpp11.cpp' ) contents = ReadFile( filepath ) request = { 'contents': contents, 'filepath': filepath, 'filetype': 'cpp' } test = { 'request': request, 'route': '/receive_messages' } response = RunAfterInitialized( app, test ) pprint( response ) assert_that( response, has_items( has_entries( { 'diagnostics': has_items( has_entries( { 'location': LocationMatcher( filepath, 16, 3 ), 'text': contains_string( 'Switch condition type \'A\' ' 'requires explicit conversion to \'int\'' ), 'fixit_available': False } ) ) } ) ) ) @IsolatedYcmd() def Diagnostics_MultipleMissingIncludes_test( app ): filepath = PathToTestFile( 'multiple_missing_includes.cc' ) contents = ReadFile( filepath ) request = { 'contents': contents, 'filepath': filepath, 'filetype': 'cpp' } test = { 'request': request, 'route': '/receive_messages' } response = RunAfterInitialized( app, test ) pprint( response ) assert_that( response, has_items( has_entries( { 'diagnostics': has_items( has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 1, 10 ), 'text': equal_to( "'first_missing_include' file not found" " [pp_file_not_found]" ), 'fixit_available': False } ) ) } ) ) ) @IsolatedYcmd() def Diagnostics_LocationExtent_MissingSemicolon_test( app ): filepath = PathToTestFile( 'location_extent.cc' ) contents = ReadFile( filepath ) request = { 'contents': contents, 'filepath': filepath, 'filetype': 'cpp' } test = { 'request': request, 'route': '/receive_messages' } response = RunAfterInitialized( app, test ) pprint( response ) assert_that( response, contains_exactly( has_entries( { 'diagnostics': has_items( has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 2, 9 ), 'location_extent': RangeMatcher( filepath, ( 2, 9 ), ( 2, 9 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 2, 9 ), ( 2, 9 ) ) ), 'text': equal_to( "Expected ';' at end of declaration list (fix " "available) [expected_semi_decl_list]" ), 'fixit_available': False } ), has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 5, 1 ), 'location_extent': RangeMatcher( filepath, ( 5, 1 ), ( 6, 11 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 5, 1 ), ( 6, 11 ) ) ), 'text': equal_to( "Unknown type name 'multiline_identifier'" " [unknown_typename]" ), 'fixit_available': False } ), has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 8, 7 ), 'location_extent': RangeMatcher( filepath, ( 8, 7 ), ( 8, 11 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 8, 7 ), ( 8, 11 ) ) ), 'text': equal_to( 'Constructor cannot have a return type' ' [constructor_return_type]' ), 'fixit_available': False } ) ) } ) ) ) @IsolatedYcmd() def Diagnostics_CUDA_Kernel_test( app ): filepath = PathToTestFile( 'cuda', 'kernel_call.cu' ) contents = ReadFile( filepath ) request = { 'contents': contents, 'filepath': filepath, 'filetype': 'cuda' } test = { 'request': request, 'route': '/receive_messages' } response = RunAfterInitialized( app, test ) pprint( response ) assert_that( response, contains_exactly( has_entries( { 'diagnostics': has_items( has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 59, 5 ), 'location_extent': RangeMatcher( filepath, ( 59, 5 ), ( 59, 6 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 59, 5 ), ( 59, 6 ) ) ), 'text': equal_to( 'Call to global function \'g1\' not configured' ' [global_call_not_config]' ), 'fixit_available': False } ), has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 60, 9 ), 'location_extent': RangeMatcher( filepath, ( 60, 9 ), ( 60, 12 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 60, 9 ), ( 60, 12 ) ) ), 'text': equal_to( 'Too few execution configuration arguments to kernel ' 'function call, expected at least 2, have 1' ' [typecheck_call_too_few_args_at_least]' ), 'fixit_available': False } ), has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 61, 20 ), 'location_extent': RangeMatcher( filepath, ( 61, 20 ), ( 61, 21 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 61, 20 ), ( 61, 21 ) ) ), 'text': equal_to( 'Too many execution configuration arguments to ' 'kernel function call, expected at most 4, have 5' ' [typecheck_call_too_many_args_at_most]' ), 'fixit_available': False } ), has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 65, 15 ), 'location_extent': RangeMatcher( filepath, ( 65, 15 ), ( 65, 16 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 65, 15 ), ( 65, 16 ) ) ), 'text': equal_to( 'Kernel call to non-global function \'h1\'' ' [kern_call_not_global_function]' ), 'fixit_available': False } ), has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 68, 15 ), 'location_extent': RangeMatcher( filepath, ( 68, 15 ), ( 68, 16 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 68, 15 ), ( 68, 16 ) ) ), 'text': equal_to( "Kernel function type 'int (*)(int)' must have " "void return type [kern_type_not_void_return]" ), 'fixit_available': False } ), has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 70, 8 ), 'location_extent': RangeMatcher( filepath, ( 70, 8 ), ( 70, 18 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 70, 8 ), ( 70, 18 ) ) ), 'text': equal_to( "Use of undeclared identifier 'undeclared'" ' [undeclared_var_use]' ), 'fixit_available': False } ), ) } ) ) ) @IsolatedYcmd( { 'max_diagnostics_to_display': 1 } ) def Diagnostics_MaximumDiagnosticsNumberExceeded_test( app ): filepath = PathToTestFile( 'max_diagnostics.cc' ) contents = ReadFile( filepath ) request = { 'contents': contents, 'filepath': filepath, 'filetype': 'cpp' } test = { 'request': request, 'route': '/receive_messages' } response = RunAfterInitialized( app, test ) pprint( response ) assert_that( response, contains_exactly( has_entries( { 'diagnostics': has_items( has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 3, 9 ), 'location_extent': RangeMatcher( filepath, ( 3, 9 ), ( 3, 13 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 3, 9 ), ( 3, 13 ) ) ), 'text': contains_string( "Redefinition of 'test'" ), 'fixit_available': False } ), has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 1, 1 ), 'location_extent': RangeMatcher( filepath, ( 1, 1 ), ( 1, 1 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 1, 1 ), ( 1, 1 ) ) ), 'text': equal_to( 'Maximum number of diagnostics exceeded.' ), 'fixit_available': False } ) ) } ) ) ) @IsolatedYcmd( { 'max_diagnostics_to_display': 0 } ) def Diagnostics_NoLimitToNumberOfDiagnostics_test( app ): filepath = PathToTestFile( 'max_diagnostics.cc' ) contents = ReadFile( filepath ) request = { 'contents': contents, 'filepath': filepath, 'filetype': 'cpp' } test = { 'request': request, 'route': '/receive_messages' } response = RunAfterInitialized( app, test ) pprint( response ) assert_that( response, contains_exactly( has_entries( { 'diagnostics': has_items( has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 3, 9 ), 'location_extent': RangeMatcher( filepath, ( 3, 9 ), ( 3, 13 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 3, 9 ), ( 3, 13 ) ) ), 'text': contains_string( "Redefinition of 'test'" ), 'fixit_available': False } ), has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 4, 9 ), 'location_extent': RangeMatcher( filepath, ( 4, 9 ), ( 4, 13 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 4, 9 ), ( 4, 13 ) ) ), 'text': contains_string( "Redefinition of 'test'" ), 'fixit_available': False } ) ) } ) ) ) @IsolatedYcmd() def Diagnostics_DiagsNotReady_test( app ): completer = handlers._server_state.GetFiletypeCompleter( [ 'cpp' ] ) contents = """ struct Foo { int x // semicolon missing here! int y; int c; int d; }; """ filepath = PathToTestFile( 'foo.cc' ) request = { 'contents': contents, 'filepath': filepath, 'filetype': 'cpp' } test = { 'request': request, 'route': '/receive_messages' } RunAfterInitialized( app, test ) diag_data = BuildRequest( line_num = 3, contents = contents, filepath = filepath, filetype = 'cpp' ) with patch.object( completer, '_latest_diagnostics', None ): results = app.post_json( '/detailed_diagnostic', diag_data ).json assert_that( results, has_entry( 'message', contains_string( "are not ready yet" ) ) ) @UnixOnly @IsolatedYcmd() def Diagnostics_UpdatedOnBufferVisit_test( app ): with TemporaryTestDir() as tmp_dir: source_file = os.path.join( tmp_dir, 'source.cpp' ) source_contents = """#include "header.h" int main() {return S::h();} """ with open( source_file, 'w' ) as sf: sf.write( source_contents ) header_file = os.path.join( tmp_dir, 'header.h' ) old_header_content = """#pragma once struct S{static int h();}; """ with open( header_file, 'w' ) as hf: hf.write( old_header_content ) flags_file = os.path.join( tmp_dir, 'compile_flags.txt' ) flags_content = """-xc++""" with open( flags_file, 'w' ) as ff: ff.write( flags_content ) messages_request = { 'contents': source_contents, 'filepath': source_file, 'filetype': 'cpp' } test = { 'request': messages_request, 'route': '/receive_messages' } response = RunAfterInitialized( app, test ) assert_that( response, contains_exactly( has_entries( { 'diagnostics': empty() } ) ) ) # Overwrite header.cpp new_header_content = """#pragma once static int h(); """ with open( header_file, 'w' ) as f: f.write( new_header_content ) # Send BufferVisit notification buffer_visit_request = { "event_name": "BufferVisit", "filepath": source_file, "filetype": 'cpp' } app.post_json( '/event_notification', BuildRequest( **buffer_visit_request ) ) # Assert diagnostics for message in PollForMessages( app, messages_request ): if 'diagnostics' in message: assert_that( message, has_entries( { 'diagnostics': contains_exactly( has_entries( { 'kind': equal_to( 'ERROR' ), 'text': "Use of undeclared identifier 'S' [undeclared_var_use]", 'ranges': contains_exactly( RangeMatcher( contains_string( source_file ), ( 2, 20 ), ( 2, 21 ) ) ), 'location': LocationMatcher( contains_string( source_file ), 2, 20 ), 'location_extent': RangeMatcher( contains_string( source_file ), ( 2, 20 ), ( 2, 21 ) ) } ) ) } ) ) break # Restore original content with open( header_file, 'w' ) as f: f.write( old_header_content ) # Send BufferVisit notification app.post_json( '/event_notification', BuildRequest( **buffer_visit_request ) ) # Assert no diagnostics for message in PollForMessages( app, messages_request ): print( f'Message { pformat( message ) }' ) if 'diagnostics' in message: assert_that( message, has_entries( { 'diagnostics': empty() } ) ) break # Assert no dirty files with open( header_file, 'r' ) as f: assert_that( f.read(), equal_to( old_header_content ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/get_completions_test.py�����������������������������0000664�0000000�0000000�00000061155�13746324601�0025301�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2015-2020 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 <http://www.gnu.org/licenses/>. from time import sleep import json import requests from hamcrest import ( assert_that, contains_exactly, contains_inanyorder, empty, equal_to, has_item, has_items, has_entries ) from ycmd import handlers from ycmd.tests.clangd import IsolatedYcmd, PathToTestFile, SharedYcmd from ycmd.tests.test_utils import ( BuildRequest, CombineRequest, CompletionEntryMatcher, WaitUntilCompleterServerReady, WindowsOnly, WithRetry ) from ycmd.utils import ReadFile def RunTest( app, test ): """ Method to run a simple completion test and verify the result Note: Compile commands are extracted from a compile_flags.txt file by clangd by iteratively looking at the directory containing the source file and its ancestors. test is a dictionary containing: 'request': kwargs for BuildRequest 'expect': { 'response': server response code (e.g. requests.codes.ok) 'data': matcher for the server response json } """ request = test[ 'request' ] filetype = request.get( 'filetype', 'cpp' ) if 'contents' not in request: contents = ReadFile( request[ 'filepath' ] ) request[ 'contents' ] = contents request[ 'filetype' ] = filetype # Because we aren't testing this command, we *always* ignore errors. This # is mainly because we (may) want to test scenarios where the completer # throws an exception and the easiest way to do that is to throw from # within the Settings function. app.post_json( '/event_notification', CombineRequest( request, { 'event_name': 'FileReadyToParse', 'filetype': filetype } ), expect_errors = True ) WaitUntilCompleterServerReady( app, filetype ) for i in range( 10 ): try: # We also ignore errors here, but then we check the response code ourself. # This is to allow testing of requests returning errors. response = app.post_json( '/completions', BuildRequest( **request ), expect_errors = True ) assert_that( response.status_code, equal_to( test[ 'expect' ][ 'response' ] ) ) print( 'Completer response: ' f'{ json.dumps( response.json, indent = 2 ) }' ) assert_that( response.json, test[ 'expect' ][ 'data' ] ) break except Exception: if i == 9: raise else: completer = handlers._server_state.GetFiletypeCompleter( [ 'cpp' ] ) completer._completions_cache.Invalidate() sleep( 0.1 ) pass @IsolatedYcmd( { 'clangd_uses_ycmd_caching': 0 } ) def GetCompletions_ForcedWithNoTrigger_NoYcmdCaching_test( app ): RunTest( app, { 'description': 'semantic completion with force query=DO_SO', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'general_fallback', 'lang_cpp.cc' ), 'line_num' : 54, 'column_num': 8, 'force_semantic': True, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_exactly( CompletionEntryMatcher( 'DO_SOMETHING_TO', 'void' ), CompletionEntryMatcher( 'DO_SOMETHING_WITH', 'void' ), CompletionEntryMatcher( 'do_something', 'void' ), ), 'errors': empty(), } ) }, } ) @IsolatedYcmd( { 'clangd_uses_ycmd_caching': 0 } ) def GetCompletions_NotForced_NoYcmdCaching_test( app ): RunTest( app, { 'description': 'semantic completion with force query=DO_SO', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'general_fallback', 'lang_cpp.cc' ), 'line_num' : 54, 'column_num': 8, 'force_semantic': False, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_exactly( CompletionEntryMatcher( 'DO_SOMETHING_TO', 'void' ), CompletionEntryMatcher( 'DO_SOMETHING_WITH', 'void' ), CompletionEntryMatcher( 'do_something', 'void' ), ), 'errors': empty(), } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_ForcedWithNoTrigger_test( app ): RunTest( app, { 'description': 'semantic completion with force query=DO_SO', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'general_fallback', 'lang_cpp.cc' ), 'line_num' : 54, 'column_num': 8, 'force_semantic': True, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_exactly( CompletionEntryMatcher( 'DO_SOMETHING_TO', 'void' ), CompletionEntryMatcher( 'DO_SOMETHING_WITH', 'void' ), ), 'errors': empty(), } ) }, } ) # This test is isolated to make sure we trigger c hook for clangd, instead of # fetching completer from cache. @IsolatedYcmd() def GetCompletions_Fallback_NoSuggestions_test( app ): # TESTCASE1 (general_fallback/lang_c.c) RunTest( app, { 'description': 'Triggered, fallback but no query so no completions', 'request': { 'filetype' : 'c', 'filepath' : PathToTestFile( 'general_fallback', 'lang_c.c' ), 'line_num' : 29, 'column_num': 21, 'force_semantic': False, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': empty(), 'errors': empty(), } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_Fallback_NoSuggestions_MinimumCharaceters_test( app ): # TESTCASE1 (general_fallback/lang_cpp.cc) RunTest( app, { 'description': 'fallback general completion obeys min chars setting ' ' (query="a")', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'general_fallback', 'lang_cpp.cc' ), 'line_num' : 21, 'column_num': 22, 'force_semantic': False, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': empty(), 'errors': empty(), } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_Fallback_Suggestions_test( app ): # TESTCASE1 (general_fallback/lang_c.c) RunTest( app, { 'description': '. after macro with some query text (.a_)', 'request': { 'filetype' : 'c', 'filepath' : PathToTestFile( 'general_fallback', 'lang_c.c' ), 'line_num' : 29, 'column_num': 23, 'force_semantic': False, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': has_item( CompletionEntryMatcher( 'a_parameter', '[ID]' ) ), 'errors': empty(), } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_Fallback_Exception_test( app ): # TESTCASE4 (general_fallback/lang_c.c) # extra conf throws exception RunTest( app, { 'description': '. on struct returns identifier because of error', 'request': { 'filetype' : 'c', 'filepath' : PathToTestFile( 'general_fallback', 'lang_c.c' ), 'line_num' : 62, 'column_num': 20, 'force_semantic': False, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_exactly( CompletionEntryMatcher( 'a_parameter', 'int' ), CompletionEntryMatcher( 'another_parameter', 'int' ), ), 'errors': empty() } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_Forced_NoFallback_test( app ): # TESTCASE2 (general_fallback/lang_c.c) RunTest( app, { 'description': '-> after macro with forced semantic', 'request': { 'filetype' : 'c', 'filepath' : PathToTestFile( 'general_fallback', 'lang_c.c' ), 'line_num' : 41, 'column_num': 30, 'force_semantic': True, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': empty() } ), }, } ) @WithRetry @SharedYcmd def GetCompletions_FilteredNoResults_Fallback_test( app ): # no errors because the semantic completer returned results, but they # were filtered out by the query, so this is considered working OK # (whereas no completions from the semantic engine is considered an # error) # TESTCASE5 (general_fallback/lang_cpp.cc) RunTest( app, { 'description': '. on struct returns IDs after query=do_', 'request': { 'filetype': 'c', 'filepath': PathToTestFile( 'general_fallback', 'lang_c.c' ), 'line_num': 71, 'column_num': 18, 'force_semantic': False, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_inanyorder( # do_ is an identifier because it is already in the file when we # load it CompletionEntryMatcher( 'do_', '[ID]' ), CompletionEntryMatcher( 'do_something', '[ID]' ), CompletionEntryMatcher( 'do_another_thing', '[ID]' ), CompletionEntryMatcher( 'DO_SOMETHING_TO', '[ID]' ), CompletionEntryMatcher( 'DO_SOMETHING_VIA', '[ID]' ) ), 'errors': empty() } ) }, } ) @IsolatedYcmd( { 'auto_trigger': 0 } ) def GetCompletions_NoCompletionsWhenAutoTriggerOff_test( app ): RunTest( app, { 'description': 'no completions on . when auto trigger is off', 'request': { 'filetype': 'cpp', 'filepath': PathToTestFile( 'foo.cc' ), 'contents': """ struct Foo { int x; int y; char c; }; int main() { Foo foo; foo. } """, 'line_num': 11, 'column_num': 7 }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': empty(), 'errors': empty() } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_ForceSemantic_YcmdCache_test( app ): RunTest( app, { 'description': 'completions are returned when using ycmd filtering', 'request': { 'filetype': 'cpp', 'filepath': PathToTestFile( 'foo.cc' ), 'contents': """ int main() { int foobar; int floozar; int gooboo; int bleble; fooar } """, 'line_num': 9, 'column_num': 8, 'force_semantic': True }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_exactly( CompletionEntryMatcher( 'foobar' ), CompletionEntryMatcher( 'floozar' ) ), 'errors': empty() } ) }, } ) @IsolatedYcmd( { 'clangd_uses_ycmd_caching': 0 } ) def GetCompletions_ForceSemantic_NoYcmdCache_test( app ): RunTest( app, { 'description': 'no completions are returned when using Clangd filtering', 'request': { 'filetype': 'cpp', 'filepath': PathToTestFile( 'foo.cc' ), 'contents': """ int main() { int foobar; int floozar; int gooboo; int bleble; fooar } """, 'line_num': 9, 'column_num': 8, 'force_semantic': True }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': empty(), 'errors': empty() } ) }, } ) @WindowsOnly @WithRetry @SharedYcmd def GetCompletions_ClangCLDriverFlag_SimpleCompletion_test( app ): RunTest( app, { 'description': 'basic completion with --driver-mode=cl', 'request': { 'filetype': 'cpp', 'filepath': PathToTestFile( 'driver_mode_cl', 'flag', 'driver_mode_cl.cpp' ), 'line_num': 8, 'column_num': 18, 'force_semantic': True, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 3, 'completions': contains_inanyorder( CompletionEntryMatcher( 'driver_mode_cl_include_func', 'void\n"driver_mode_cl_include.h"' ), CompletionEntryMatcher( 'driver_mode_cl_include_int', 'int\n"driver_mode_cl_include.h"' ), ), 'errors': empty(), } ) } } ) @WindowsOnly @WithRetry @IsolatedYcmd() def GetCompletions_ClangCLDriverExec_SimpleCompletion_test( app ): RunTest( app, { 'description': 'basic completion with --driver-mode=cl', 'request': { 'filetype': 'cpp', 'filepath': PathToTestFile( 'driver_mode_cl', 'executable', 'driver_mode_cl.cpp' ), 'line_num': 8, 'column_num': 18, 'force_semantic': True, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 3, 'completions': contains_inanyorder( CompletionEntryMatcher( 'driver_mode_cl_include_func', 'void\n"driver_mode_cl_include.h"' ), CompletionEntryMatcher( 'driver_mode_cl_include_int', 'int\n"driver_mode_cl_include.h"' ), ), 'errors': empty(), } ) } } ) @WindowsOnly @WithRetry @SharedYcmd def GetCompletions_ClangCLDriverFlag_IncludeStatementCandidate_test( app ): RunTest( app, { 'description': 'Completion inside include statement with CL driver', 'request': { 'filetype': 'cpp', 'filepath': PathToTestFile( 'driver_mode_cl', 'flag', 'driver_mode_cl.cpp' ), 'line_num': 1, 'column_num': 34, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 11, 'completions': contains_inanyorder( CompletionEntryMatcher( 'driver_mode_cl_include.h\"' ), ), 'errors': empty(), } ) } } ) @WindowsOnly @WithRetry @SharedYcmd def GetCompletions_ClangCLDriverExec_IncludeStatementCandidate_test( app ): RunTest( app, { 'description': 'Completion inside include statement with CL driver', 'request': { 'filetype': 'cpp', 'filepath': PathToTestFile( 'driver_mode_cl', 'executable', 'driver_mode_cl.cpp' ), 'line_num': 1, 'column_num': 34, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 11, 'completions': contains_inanyorder( CompletionEntryMatcher( 'driver_mode_cl_include.h\"' ), ), 'errors': empty(), } ) } } ) @WithRetry @SharedYcmd def GetCompletions_UnicodeInLine_test( app ): RunTest( app, { 'description': 'member completion with a unicode identifier', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'unicode.cc' ), 'line_num' : 9, 'column_num': 8, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 8, 'completions': contains_exactly( CompletionEntryMatcher( 'member_with_å_unicøde', 'int' ), ), 'errors': empty(), } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_UnicodeInLineFilter_test( app ): RunTest( app, { 'description': 'member completion with a unicode identifier', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'unicode.cc' ), 'line_num' : 9, 'column_num': 10, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 8, 'completions': contains_inanyorder( CompletionEntryMatcher( 'member_with_å_unicøde', 'int' ), ), 'errors': empty(), } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_QuotedInclude_test( app ): RunTest( app, { 'description': 'completion of #include "', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 9, 'column_num': 11, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 11, 'completions': contains_exactly( CompletionEntryMatcher( 'a.hpp"' ), CompletionEntryMatcher( 'b.hpp"' ), CompletionEntryMatcher( 'c.hpp"' ), CompletionEntryMatcher( 'dir with spaces/' ), CompletionEntryMatcher( 'quote/' ), CompletionEntryMatcher( 'system/' ), ), 'errors': empty(), } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_QuotedInclude_AfterDirectorySeparator_test( app ): RunTest( app, { 'description': 'completion of #include "quote/', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 9, 'column_num': 27, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 27, 'completions': contains_exactly( CompletionEntryMatcher( 'd.hpp"' ), ), 'errors': empty(), } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_QuotedInclude_AfterDot_test( app ): RunTest( app, { 'description': 'completion of #include "quote/b.', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 9, 'column_num': 28, 'compilation_flags': [ '-x', 'c++' ] }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 27, 'completions': contains_exactly( CompletionEntryMatcher( 'd.hpp"' ), ), 'errors': empty(), } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_QuotedInclude_AfterSpace_test( app ): RunTest( app, { 'description': 'completion of #include "dir with ', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 9, 'column_num': 20, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 11, 'completions': contains_exactly( CompletionEntryMatcher( 'dir with spaces/' ), ), 'errors': empty(), } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_QuotedInclude_Invalid_test( app ): RunTest( app, { 'description': 'completion of an invalid include statement', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 11, 'column_num': 12, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 12, 'completions': empty(), 'errors': empty(), } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_BracketInclude_test( app ): RunTest( app, { 'description': 'completion of #include <', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 10, 'column_num': 11, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 11, 'completions': has_items( CompletionEntryMatcher( 'a.hpp>' ), CompletionEntryMatcher( 'c.hpp>' ) ), 'errors': empty(), } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_BracketInclude_AtDirectorySeparator_test( app ): RunTest( app, { 'description': 'completion of #include <system/', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'test-include', 'main.cpp' ), 'line_num' : 10, 'column_num': 18, 'compilation_flags': [ '-x', 'c++' ], # NOTE: when not forcing semantic, it falls back to the filename # completer and returns the root folder entries. 'force_semantic': True }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 18, 'completions': empty(), 'errors': empty(), } ) }, } ) @WithRetry @IsolatedYcmd() def GetCompletions_cuda_test( app ): RunTest( app, { 'description': 'Completion of CUDA files', 'request': { 'filetype' : 'cuda', 'filepath' : PathToTestFile( 'cuda', 'completion_test.cu' ), 'line_num' : 16, 'column_num': 29, 'force_semantic': True, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 29, 'completions': contains_exactly( CompletionEntryMatcher( 'do_something', 'void', { 'menu_text': 'do_something(float *a)' } ), ), 'errors': empty(), } ) } } ) @IsolatedYcmd( { 'clangd_args': [ '-header-insertion-decorators=1' ] } ) def GetCompletions_WithHeaderInsertionDecorators_test( app ): RunTest( app, { 'description': 'Completion of CUDA files', 'request': { 'filetype' : 'cuda', 'filepath' : PathToTestFile( 'cuda', 'completion_test.cu' ), 'line_num' : 16, 'column_num': 29, 'force_semantic': True, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 29, 'completions': contains_exactly( CompletionEntryMatcher( 'do_something', 'void', { 'menu_text': ' do_something(float *a)' } ), ), 'errors': empty(), } ) } } ) @WithRetry @SharedYcmd def GetCompletions_ServerTriggers_Ignored_test( app ): RunTest( app, { 'description': "We don't trigger completion on things like map< int >|", 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'template.cc' ), 'line_num' : 1, 'column_num': 25 }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 25, 'completions': empty(), 'errors': empty(), } ) } } ) @IsolatedYcmd( { 'extra_conf_globlist': [ PathToTestFile( 'extra_conf', '.ycm_extra_conf.py' ) ] } ) def GetCompletions_SupportExtraConf_test( app ): RunTest( app, { 'description': 'Flags for foo.cpp from extra conf file are used', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'extra_conf', 'foo.cpp' ), 'line_num' : 5, 'column_num': 15 }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 15, 'completions': contains_exactly( CompletionEntryMatcher( 'member_foo' ) ), 'errors': empty(), } ) } } ) RunTest( app, { 'description': 'Same flags are used again for foo.cpp', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'extra_conf', 'foo.cpp' ), 'line_num' : 5, 'column_num': 15 }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 15, 'completions': contains_exactly( CompletionEntryMatcher( 'member_foo' ) ), 'errors': empty(), } ) } } ) RunTest( app, { 'description': 'Flags for bar.cpp from extra conf file are used', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'extra_conf', 'bar.cpp' ), 'line_num' : 5, 'column_num': 15 }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 15, 'completions': contains_exactly( CompletionEntryMatcher( 'member_bar' ) ), 'errors': empty(), } ) } } ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/server_management_test.py���������������������������0000664�0000000�0000000�00000012343�13746324601�0025603�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2011-2020 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 <http://www.gnu.org/licenses/>. from unittest.mock import patch import psutil from hamcrest import ( assert_that, contains_exactly, empty, has_entries, has_entry, has_item ) from ycmd import handlers, utils from ycmd.tests.clangd import ( IsolatedYcmd, PathToTestFile, RunAfterInitialized ) from ycmd.tests.test_utils import ( BuildRequest, CompleterProjectDirectoryMatcher, MockProcessTerminationTimingOut, StopCompleterServer, WaitUntilCompleterServerReady ) def GetDebugInfo( app ): request_data = BuildRequest( filetype = 'cpp' ) return app.post_json( '/debug_info', request_data ).json def GetPid( app ): return GetDebugInfo( app )[ 'completer' ][ 'servers' ][ 0 ][ 'pid' ] def StartClangd( app, filepath = PathToTestFile( 'basic.cpp' ) ): request_data = BuildRequest( filepath = filepath, filetype = 'cpp' ) test = { 'request': request_data } RunAfterInitialized( app, test ) def CheckStopped( app ): assert_that( GetDebugInfo( app ), has_entry( 'completer', has_entries( { 'name': 'C-family', 'servers': contains_exactly( has_entries( { 'name': 'Clangd', 'pid': None, 'is_running': False } ) ), 'items': empty() } ) ) ) @IsolatedYcmd() def ServerManagement_StopServer_Clean_test( app ): StartClangd( app ) StopCompleterServer( app, 'cpp', '' ) CheckStopped( app ) @IsolatedYcmd() @patch( 'os.remove', side_effect = OSError ) @patch( 'ycmd.utils.WaitUntilProcessIsTerminated', MockProcessTerminationTimingOut ) def ServerManagement_StopServer_Unclean_test( rm, app ): StartClangd( app ) StopCompleterServer( app, 'cpp', '' ) CheckStopped( app ) @IsolatedYcmd() def ServerManagement_StopServer_Twice_test( app ): StartClangd( app ) StopCompleterServer( app, 'cpp', '' ) CheckStopped( app ) StopCompleterServer( app, 'cpp', '' ) CheckStopped( app ) @IsolatedYcmd() def ServerManagement_StopServer_Killed_test( app ): StartClangd( app ) process = psutil.Process( GetPid( app ) ) process.terminate() process.wait( timeout = 5 ) StopCompleterServer( app, 'cpp', '' ) CheckStopped( app ) @IsolatedYcmd() def ServerManagement_ServerDiesWhileShuttingDown_test( app ): StartClangd( app ) process = psutil.Process( GetPid( app ) ) completer = handlers._server_state.GetFiletypeCompleter( [ 'cpp' ] ) # We issue a shutdown but make sure it never reaches server by mocking # WriteData in Connection. Then we kill the server and check shutdown still # succeeds. with patch.object( completer.GetConnection(), 'WriteData' ): stop_server_task = utils.StartThread( StopCompleterServer, app, 'cpp', '' ) process.terminate() stop_server_task.join() CheckStopped( app ) @IsolatedYcmd() def ServerManagement_ConnectionRaisesWhileShuttingDown_test( app ): StartClangd( app ) process = psutil.Process( GetPid( app ) ) completer = handlers._server_state.GetFiletypeCompleter( [ 'cpp' ] ) # We issue a shutdown but make sure it never reaches server by mocking # WriteData in Connection. Then we kill the server and check shutdown still # succeeds. with patch.object( completer.GetConnection(), 'GetResponse', side_effect = RuntimeError ): StopCompleterServer( app, 'cpp', '' ) CheckStopped( app ) if process.is_running(): process.terminate() raise AssertionError( 'Termination failed' ) @IsolatedYcmd() def ServerManagement_RestartServer_test( app ): StartClangd( app, PathToTestFile( 'basic.cpp' ) ) assert_that( GetDebugInfo( app ), CompleterProjectDirectoryMatcher( PathToTestFile() ) ) app.post_json( '/run_completer_command', BuildRequest( filepath = PathToTestFile( 'test-include', 'main.cpp' ), filetype = 'cpp', command_arguments = [ 'RestartServer' ], ), ) WaitUntilCompleterServerReady( app, 'cpp' ) assert_that( GetDebugInfo( app ), has_entry( 'completer', has_entries( { 'name': 'C-family', 'servers': contains_exactly( has_entries( { 'name': 'Clangd', 'is_running': True, 'extras': has_item( has_entries( { 'key': 'Project Directory', 'value': PathToTestFile( 'test-include' ), } ) ) } ) ) } ) ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/signature_help_test.py������������������������������0000664�0000000�0000000�00000052527�13746324601�0025122�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import json import requests from unittest.mock import patch from hamcrest import assert_that, contains_exactly, empty, equal_to, has_entries from ycmd import handlers from ycmd.tests.clangd import PathToTestFile, SharedYcmd, IsolatedYcmd from ycmd.tests.test_utils import ( EMPTY_SIGNATURE_HELP, BuildRequest, CombineRequest, ParameterMatcher, SignatureMatcher, SignatureAvailableMatcher, WaitUntilCompleterServerReady ) from ycmd.utils import ReadFile def RunTest( app, test ): """ Method to run a simple completion test and verify the result Note: Compile commands are extracted from a compile_flags.txt file by clangd by iteratively looking at the directory containing the source file and its ancestors. test is a dictionary containing: 'request': kwargs for BuildRequest 'expect': { 'response': server response code (e.g. requests.codes.ok) 'data': matcher for the server response json } """ request = test[ 'request' ] filetype = request.get( 'filetype', 'cpp' ) if 'contents' not in request: contents = ReadFile( request[ 'filepath' ] ) request[ 'contents' ] = contents request[ 'filetype' ] = filetype # Because we aren't testing this command, we *always* ignore errors. This # is mainly because we (may) want to test scenarios where the completer # throws an exception and the easiest way to do that is to throw from # within the Settings function. app.post_json( '/event_notification', CombineRequest( request, { 'event_name': 'FileReadyToParse', 'filetype': filetype } ), expect_errors = True ) WaitUntilCompleterServerReady( app, filetype ) # We also ignore errors here, but then we check the response code ourself. # This is to allow testing of requests returning errors. response = app.post_json( '/signature_help', BuildRequest( **request ), expect_errors = True ) assert_that( response.status_code, equal_to( test[ 'expect' ][ 'response' ] ) ) print( f'Completer response: { json.dumps( response.json, indent = 2 ) }' ) assert_that( response.json, test[ 'expect' ][ 'data' ] ) @SharedYcmd def Signature_Help_Trigger_test( app ): RunTest( app, { 'description': 'trigger after (', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'general_fallback', 'make_drink.cc' ), 'line_num' : 7, 'column_num': 14, 'signature_help_state': 'INACTIVE', }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': contains_exactly( SignatureMatcher( 'make_drink(TypeOfDrink type, ' 'Temperature temp, ' 'int sugargs) -> Drink &', [ ParameterMatcher( 11, 27 ), ParameterMatcher( 29, 45 ), ParameterMatcher( 47, 58 ), ] ), SignatureMatcher( 'make_drink(TypeOfDrink type, ' 'double fizziness, ' 'Flavour Flavour) -> Drink &', [ ParameterMatcher( 11, 27 ), ParameterMatcher( 29, 45 ), ParameterMatcher( 47, 62 ), ] ), ) } ), } ) }, } ) @IsolatedYcmd( { 'disable_signature_help': 1 } ) def Signature_Help_Disabled_test( app ): RunTest( app, { 'description': 'trigger after (', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'general_fallback', 'make_drink.cc' ), 'line_num' : 7, 'column_num': 14, 'signature_help_state': 'INACTIVE', }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': EMPTY_SIGNATURE_HELP, } ) }, } ) @SharedYcmd def Signature_Help_NoTrigger_test( app ): RunTest( app, { 'description': 'do not trigger before (', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'general_fallback', 'make_drink.cc' ), 'line_num' : 7, 'column_num': 13, 'signature_help_state': 'INACTIVE', }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': EMPTY_SIGNATURE_HELP, } ), }, } ) @SharedYcmd def Signature_Help_NoTrigger_After_Trigger_test( app ): RunTest( app, { 'description': 'do not trigger too far after (', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'general_fallback', 'make_drink.cc' ), 'line_num' : 7, 'column_num': 15, 'signature_help_state': 'INACTIVE', }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': EMPTY_SIGNATURE_HELP, } ), }, } ) @SharedYcmd def Signature_Help_Trigger_After_Trigger_test( app ): RunTest( app, { 'description': 'Auto trigger due to state of existing request', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'general_fallback', 'make_drink.cc' ), 'line_num' : 7, 'column_num': 15, 'signature_help_state': 'ACTIVE', }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': contains_exactly( SignatureMatcher( 'make_drink(TypeOfDrink type, ' 'Temperature temp, ' 'int sugargs) -> Drink &', [ ParameterMatcher( 11, 27 ), ParameterMatcher( 29, 45 ), ParameterMatcher( 47, 58 ), ] ), SignatureMatcher( 'make_drink(TypeOfDrink type, ' 'double fizziness, ' 'Flavour Flavour) -> Drink &', [ ParameterMatcher( 11, 27 ), ParameterMatcher( 29, 45 ), ParameterMatcher( 47, 62 ), ] ), ) } ), } ), }, } ) @IsolatedYcmd( { 'disable_signature_help': 1 } ) def Signature_Help_Trigger_After_Trigger_Disabled_test( app ): RunTest( app, { 'description': 'Auto trigger due to state of existing request', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'general_fallback', 'make_drink.cc' ), 'line_num' : 7, 'column_num': 15, 'signature_help_state': 'ACTIVE', }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': EMPTY_SIGNATURE_HELP, } ), }, } ) @SharedYcmd def Signature_Help_Trigger_After_Trigger_PlusText_test( app ): RunTest( app, { 'description': 'Triggering after additional text beyond (', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'general_fallback', 'make_drink.cc' ), 'line_num' : 7, 'column_num': 17, 'signature_help_state': 'ACTIVE', }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': contains_exactly( SignatureMatcher( 'make_drink(TypeOfDrink type, ' 'Temperature temp, ' 'int sugargs) -> Drink &', [ ParameterMatcher( 11, 27 ), ParameterMatcher( 29, 45 ), ParameterMatcher( 47, 58 ), ] ), SignatureMatcher( 'make_drink(TypeOfDrink type, ' 'double fizziness, ' 'Flavour Flavour) -> Drink &', [ ParameterMatcher( 11, 27 ), ParameterMatcher( 29, 45 ), ParameterMatcher( 47, 62 ), ] ), ) } ), } ), }, } ) @SharedYcmd def Signature_Help_Trigger_After_Trigger_PlusCompletion_test( app ): RunTest( app, { 'description': 'Triggering after semantic trigger after (', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'general_fallback', 'make_drink.cc' ), 'line_num' : 7, 'column_num': 28, 'signature_help_state': 'ACTIVE', }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': contains_exactly( SignatureMatcher( 'make_drink(TypeOfDrink type, ' 'Temperature temp, ' 'int sugargs) -> Drink &', [ ParameterMatcher( 11, 27 ), ParameterMatcher( 29, 45 ), ParameterMatcher( 47, 58 ), ] ), SignatureMatcher( 'make_drink(TypeOfDrink type, ' 'double fizziness, ' 'Flavour Flavour) -> Drink &', [ ParameterMatcher( 11, 27 ), ParameterMatcher( 29, 45 ), ParameterMatcher( 47, 62 ), ] ), ) } ), } ), }, } ) @SharedYcmd def Signature_Help_Trigger_After_OtherTrigger_test( app ): RunTest( app, { 'description': 'Triggering after ,', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'general_fallback', 'make_drink.cc' ), 'line_num' : 7, 'column_num': 35, 'signature_help_state': 'INACTIVE', }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 1, 'signatures': contains_exactly( SignatureMatcher( 'make_drink(TypeOfDrink type, ' 'Temperature temp, ' 'int sugargs) -> Drink &', [ ParameterMatcher( 11, 27 ), ParameterMatcher( 29, 45 ), ParameterMatcher( 47, 58 ), ] ), SignatureMatcher( 'make_drink(TypeOfDrink type, ' 'double fizziness, ' 'Flavour Flavour) -> Drink &', [ ParameterMatcher( 11, 27 ), ParameterMatcher( 29, 45 ), ParameterMatcher( 47, 62 ), ] ), ) } ), } ), }, } ) @SharedYcmd def Signature_Help_Trigger_After_Arguments_Narrow_test( app ): RunTest( app, { 'description': 'After resolution of overload', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'general_fallback', 'make_drink.cc' ), 'line_num' : 7, 'column_num': 41, 'signature_help_state': 'ACTIVE', }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 2, 'signatures': contains_exactly( SignatureMatcher( 'make_drink(TypeOfDrink type, ' 'double fizziness, ' 'Flavour Flavour) -> Drink &', [ ParameterMatcher( 11, 27 ), ParameterMatcher( 29, 45 ), ParameterMatcher( 47, 62 ), ] ) ) } ), } ), }, } ) @SharedYcmd def Signature_Help_Trigger_After_Arguments_Narrow2_test( app ): RunTest( app, { 'description': 'After resolution of overload not the first one', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'general_fallback', 'make_drink.cc' ), 'line_num' : 8, 'column_num': 53, 'signature_help_state': 'ACTIVE', }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 2, 'signatures': contains_exactly( SignatureMatcher( 'make_drink(TypeOfDrink type, ' 'Temperature temp, ' 'int sugargs) -> Drink &', [ ParameterMatcher( 11, 27 ), ParameterMatcher( 29, 45 ), ParameterMatcher( 47, 58 ), ] ) ) } ), } ), }, } ) @SharedYcmd def Signature_Help_Trigger_After_OtherTrigger_ReTrigger_test( app ): RunTest( app, { 'description': 'Triggering after , but already ACTIVE', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'general_fallback', 'make_drink.cc' ), 'line_num' : 7, 'column_num': 35, 'signature_help_state': 'ACTIVE', }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 1, 'signatures': contains_exactly( SignatureMatcher( 'make_drink(TypeOfDrink type, ' 'Temperature temp, ' 'int sugargs) -> Drink &', [ ParameterMatcher( 11, 27 ), ParameterMatcher( 29, 45 ), ParameterMatcher( 47, 58 ), ] ), SignatureMatcher( 'make_drink(TypeOfDrink type, ' 'double fizziness, ' 'Flavour Flavour) -> Drink &', [ ParameterMatcher( 11, 27 ), ParameterMatcher( 29, 45 ), ParameterMatcher( 47, 62 ), ] ), ) } ), } ), }, } ) @SharedYcmd def Signature_Help_Trigger_JustBeforeClose_test( app ): RunTest( app, { 'description': 'Last argument, before )', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'general_fallback', 'make_drink.cc' ), 'line_num' : 8, 'column_num': 33, 'signature_help_state': 'ACTIVE', }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': contains_exactly( SignatureMatcher( 'make_drink(TypeOfDrink type, ' 'Temperature temp, ' 'int sugargs) -> Drink &', [ ParameterMatcher( 11, 27 ), ParameterMatcher( 29, 45 ), ParameterMatcher( 47, 58 ), ] ), SignatureMatcher( 'make_drink(TypeOfDrink type, ' 'double fizziness, ' 'Flavour Flavour) -> Drink &', [ ParameterMatcher( 11, 27 ), ParameterMatcher( 29, 45 ), ParameterMatcher( 47, 62 ), ] ), ) } ), } ), }, } ) @SharedYcmd def Signature_Help_Clears_After_EndFunction_test( app ): RunTest( app, { 'description': 'Empty response on )', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'general_fallback', 'make_drink.cc' ), 'line_num' : 7, 'column_num': 70, 'signature_help_state': 'ACTIVE', }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': EMPTY_SIGNATURE_HELP, } ), }, } ) @SharedYcmd def Signature_Help_Clears_After_Function_Call_test( app ): RunTest( app, { 'description': 'Empty response after )', 'request': { 'filetype' : 'cpp', 'filepath' : PathToTestFile( 'general_fallback', 'make_drink.cc' ), 'line_num' : 7, 'column_num': 71, 'signature_help_state': 'ACTIVE', }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': EMPTY_SIGNATURE_HELP, } ), }, } ) @patch( 'ycmd.completers.completer.Completer.ShouldUseSignatureHelpNow', return_value = True ) @patch( 'ycmd.completers.language_server.language_server_completer.' 'LanguageServerCompleter._ServerIsInitialized', return_value = False ) @IsolatedYcmd() def Signature_Help_Server_Not_Initialized_test( should_use_sig, server_init, app ): filepath = PathToTestFile( 'general_fallback', 'make_drink.cc' ) request = { 'filetype' : 'cpp', 'filepath' : filepath, 'line_num' : 7, 'column_num': 71, 'signature_help_state': 'INACTIVE', 'contents': ReadFile( filepath ) } response = app.post_json( '/signature_help', BuildRequest( **request ), expect_errors = True ) assert_that( response.json, has_entries( { 'errors': empty(), 'signature_help': EMPTY_SIGNATURE_HELP, } ) ) def Signature_Help_Available_Server_Not_Initialized_test(): completer = handlers._server_state.GetFiletypeCompleter( [ 'cpp' ] ) @SharedYcmd @patch.object( completer, '_ServerIsInitialized', return_value = False ) def Test( app ): response = app.get( '/signature_help_available', { 'subserver': 'cpp' } ).json assert_that( response, SignatureAvailableMatcher( 'PENDING' ) ) @SharedYcmd def Signature_Help_Supported_test( app ): request = { 'filepath' : PathToTestFile( 'goto.cc' ) } app.post_json( '/event_notification', CombineRequest( request, { 'event_name': 'FileReadyToParse', 'filetype': 'cpp' } ), expect_errors = True ) WaitUntilCompleterServerReady( app, 'cpp' ) response = app.get( '/signature_help_available', { 'subserver': 'cpp' } ).json assert_that( response, SignatureAvailableMatcher( 'YES' ) ) @IsolatedYcmd( { 'disable_signature_help': 1 } ) def Signature_Help_Available_Disabled_By_User_test( app, *args ): request = { 'filepath' : PathToTestFile( 'goto.cc' ) } app.post_json( '/event_notification', CombineRequest( request, { 'event_name': 'FileReadyToParse', 'filetype': 'cpp' } ), expect_errors = True ) WaitUntilCompleterServerReady( app, 'cpp' ) response = app.get( '/signature_help_available', { 'subserver': 'cpp' } ).json assert_that( response, SignatureAvailableMatcher( 'NO' ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/subcommands_test.py���������������������������������0000664�0000000�0000000�00000120513�13746324601�0024413�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# encoding: utf-8 # # 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 <http://www.gnu.org/licenses/>. from __future__ import absolute_import from __future__ import unicode_literals from __future__ import print_function from __future__ import division from hamcrest import ( assert_that, contains_exactly, contains_string, equal_to, has_entries, has_entry, matches_regexp ) from unittest.mock import patch from pprint import pprint import requests import pytest import os.path from ycmd import handlers from ycmd.tests.clangd import ( IsolatedYcmd, SharedYcmd, PathToTestFile, RunAfterInitialized ) from ycmd.tests.test_utils import ( BuildRequest, ChunkMatcher, CombineRequest, LineColMatcher, LocationMatcher, ErrorMatcher, WithRetry, WaitUntilCompleterServerReady ) from ycmd.utils import ReadFile # This test is isolated to trigger objcpp hooks, rather than fetching completer # from cache. @IsolatedYcmd() def Subcommands_DefinedSubcommands_test( app ): file_path = PathToTestFile( 'GoTo_Clang_ZeroBasedLineAndColumn_test.cc' ) RunAfterInitialized( app, { 'request': { 'completer_target': 'filetype_default', 'line_num': 10, 'column_num': 3, 'filetype': 'objcpp', 'filepath': file_path }, 'expect': { 'response': requests.codes.ok, 'data': contains_exactly( *sorted( [ 'ExecuteCommand', 'FixIt', 'Format', 'GetDoc', 'GetDocImprecise', 'GetType', 'GetTypeImprecise', 'GoTo', 'GoToDeclaration', 'GoToDefinition', 'GoToImprecise', 'GoToInclude', 'GoToReferences', 'GoToSymbol', 'RefactorRename', 'RestartServer' ] ) ) }, 'route': '/defined_subcommands', } ) @WithRetry @SharedYcmd @pytest.mark.parametrize( 'cmd', [ 'FixIt', 'Format', 'GetDoc', 'GetDocImprecise', 'GetType', 'GetTypeImprecise', 'GoTo', 'GoToDeclaration', 'GoToDefinition', 'GoToInclude', 'GoToReferences', 'RefactorRename', ] ) def Subcommands_ServerNotInitialized_test( app, cmd ): completer = handlers._server_state.GetFiletypeCompleter( [ 'cpp' ] ) @patch.object( completer, '_ServerIsInitialized', return_value = False ) def Test( app, cmd, *args ): request = { 'line_num': 1, 'column_num': 1, 'event_name': 'FileReadyToParse', 'filetype': 'cpp', 'command_arguments': [ cmd ] } app.post_json( '/event_notification', BuildRequest( **request ), expect_errors = True ) response = app.post_json( '/run_completer_command', BuildRequest( **request ), expect_errors = True ) assert_that( response.status_code, equal_to( requests.codes.server_error ) ) assert_that( response.json, ErrorMatcher( RuntimeError, 'Server is initializing. Please wait.' ) ) Test( app, cmd ) @SharedYcmd def Subcommands_GoTo_ZeroBasedLineAndColumn_test( app ): file_path = PathToTestFile( 'GoTo_Clang_ZeroBasedLineAndColumn_test.cc' ) RunAfterInitialized( app, { 'request': { 'contents': ReadFile( file_path ), 'completer_target': 'filetype_default', 'command_arguments': [ 'GoToDefinition' ], 'line_num': 10, 'column_num': 3, 'filetype': 'cpp', 'filepath': file_path }, 'expect': { 'response': requests.codes.ok, 'data': { 'filepath': os.path.abspath( file_path ), 'line_num': 2, 'column_num': 8 } }, 'route': '/run_completer_command', } ) def RunGoToTest_all( app, folder, command, test ): req = test[ 'req' ] filepath = PathToTestFile( folder, req[ 0 ] ) request = { 'completer_target' : 'filetype_default', 'filepath' : filepath, 'contents' : ReadFile( filepath ), 'filetype' : 'cpp', 'line_num' : req[ 1 ], 'column_num' : req[ 2 ], 'command_arguments': [ command ] + ( [] if len( req ) < 4 else req[ 3 ] ), } response = test[ 'res' ] if isinstance( response, list ): expect = { 'response': requests.codes.ok, 'data': contains_exactly( *[ LocationMatcher( PathToTestFile( folder, os.path.normpath( location[ 0 ] ) ), location[ 1 ], location[ 2 ] ) for location in response ] ) } elif isinstance( response, tuple ): expect = { 'response': requests.codes.ok, 'data': LocationMatcher( PathToTestFile( folder, os.path.normpath( response[ 0 ] ) ), response[ 1 ], response[ 2 ] ) } else: expect = { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( RuntimeError, test[ 'res' ] ) } RunAfterInitialized( app, { 'request': request, 'route' : '/run_completer_command', 'expect' : expect } ) @pytest.mark.parametrize( 'test', [ # Local::x -> definition/declaration of x { 'req': ( 'goto.cc', 23, 21 ), 'res': ( 'goto.cc', 4, 9 ) }, # Local::in_line -> definition/declaration of Local::in_line { 'req': ( 'goto.cc', 24, 26 ), 'res': ( 'goto.cc', 6, 10 ) }, # Local -> definition/declaration of Local { 'req': ( 'goto.cc', 24, 16 ), 'res': ( 'goto.cc', 2, 11 ) }, # Local::out_of_line -> definition of Local::out_of_line { 'req': ( 'goto.cc', 25, 27 ), 'res': ( 'goto.cc', 14, 13 ) }, # GoToDeclaration alternates between definition and declaration { 'req': ( 'goto.cc', 14, 13 ), 'res': ( 'goto.cc', 11, 10 ) }, { 'req': ( 'goto.cc', 11, 10 ), 'res': ( 'goto.cc', 14, 13 ) }, # test -> definition and declaration of test { 'req': ( 'goto.cc', 21, 5 ), 'res': ( 'goto.cc', 19, 5 ) }, { 'req': ( 'goto.cc', 19, 5 ), 'res': ( 'goto.cc', 21, 5 ) }, # Unicøde { 'req': ( 'goto.cc', 34, 9 ), 'res': ( 'goto.cc', 32, 26 ) }, # Another_Unicøde { 'req': ( 'goto.cc', 36, 17 ), 'res': ( 'goto.cc', 32, 54 ) }, { 'req': ( 'goto.cc', 36, 25 ), 'res': ( 'goto.cc', 32, 54 ) }, { 'req': ( 'goto.cc', 38, 3 ), 'res': ( 'goto.cc', 36, 28 ) }, # Expected failures { 'req': ( 'goto.cc', 13, 1 ), 'res': 'Cannot jump to location' }, { 'req': ( 'goto.cc', 16, 6 ), 'res': 'Cannot jump to location' }, ] ) @pytest.mark.parametrize( 'cmd', [ 'GoToImprecise', 'GoToDefinition', 'GoTo' ] ) @SharedYcmd def Subcommands_GoTo_all_test( app, cmd, test ): RunGoToTest_all( app, '', cmd, test ) @pytest.mark.parametrize( 'test', [ # Local::x -> definition/declaration of x { 'req': ( 'goto.cc', 23, 21 ), 'res': ( 'goto.cc', 4, 9 ) }, # Local::in_line -> definition/declaration of Local::in_line { 'req': ( 'goto.cc', 24, 26 ), 'res': ( 'goto.cc', 6, 10 ) }, # Local -> definition/declaration of Local { 'req': ( 'goto.cc', 24, 16 ), 'res': ( 'goto.cc', 2, 11 ) }, # Local::out_of_line -> declaration of Local::out_of_line { 'req': ( 'goto.cc', 25, 27 ), 'res': ( 'goto.cc', 11, 10 ) }, # GoToDeclaration alternates between definition and declaration { 'req': ( 'goto.cc', 14, 13 ), 'res': ( 'goto.cc', 11, 10 ) }, { 'req': ( 'goto.cc', 11, 10 ), 'res': ( 'goto.cc', 14, 13 ) }, # test -> definition and declaration of test { 'req': ( 'goto.cc', 21, 5 ), 'res': ( 'goto.cc', 19, 5 ) }, { 'req': ( 'goto.cc', 19, 5 ), 'res': ( 'goto.cc', 21, 5 ) }, # Unicøde { 'req': ( 'goto.cc', 34, 9 ), 'res': ( 'goto.cc', 32, 26 ) }, # Another_Unicøde { 'req': ( 'goto.cc', 36, 17 ), 'res': ( 'goto.cc', 32, 54 ) }, { 'req': ( 'goto.cc', 36, 25 ), 'res': ( 'goto.cc', 32, 54 ) }, { 'req': ( 'goto.cc', 38, 3 ), 'res': ( 'goto.cc', 36, 28 ) }, # Expected failures { 'req': ( 'goto.cc', 13, 1 ), 'res': 'Cannot jump to location' }, { 'req': ( 'goto.cc', 16, 6 ), 'res': 'Cannot jump to location' }, ] ) @SharedYcmd def Subcommands_GoToDeclaration_all_test( app, test ): RunGoToTest_all( app, '', 'GoToDeclaration', test ) @pytest.mark.parametrize( 'test', [ { 'req': ( 'main.cpp', 1, 6 ), 'res': ( 'a.hpp', 1, 1 ) }, { 'req': ( 'main.cpp', 2, 14 ), 'res': ( 'system/a.hpp', 1, 1 ) }, { 'req': ( 'main.cpp', 3, 1 ), 'res': ( 'quote/b.hpp', 1, 1 ) }, # FIXME: should fail since b.hpp is included with angled brackets but its # folder is added with -iquote. { 'req': ( 'main.cpp', 4, 10 ), 'res': ( 'quote/b.hpp', 1, 1 ) }, { 'req': ( 'main.cpp', 5, 11 ), 'res': ( 'system/c.hpp', 1, 1 ) }, { 'req': ( 'main.cpp', 6, 11 ), 'res': ( 'system/c.hpp', 1, 1 ) }, # Expected failures { 'req': ( 'main.cpp', 7, 1 ), 'res': 'Cannot jump to location' }, { 'req': ( 'main.cpp', 10, 13 ), 'res': 'Cannot jump to location' }, ] ) @pytest.mark.parametrize( 'cmd', [ 'GoToImprecise', 'GoToInclude', 'GoTo' ] ) @SharedYcmd def Subcommands_GoToInclude_test( app, cmd, test ): RunGoToTest_all( app, 'test-include', cmd, test ) @pytest.mark.parametrize( 'test', [ # Function { 'req': ( 'goto.cc', 14, 21 ), 'res': [ ( 'goto.cc', 11, 10 ), ( 'goto.cc', 14, 13 ), ( 'goto.cc', 25, 22 ) ] }, # Namespace { 'req': ( 'goto.cc', 24, 17 ), 'res': [ ( 'goto.cc', 2, 11 ), ( 'goto.cc', 14, 6 ), ( 'goto.cc', 23, 14 ), ( 'goto.cc', 24, 15 ), ( 'goto.cc', 25, 15 ) ] }, # Expected failure { 'req': ( 'goto.cc', 27, 8 ), 'res': 'Cannot jump to location' }, ] ) @SharedYcmd def Subcommands_GoToReferences_test( app, test ): RunGoToTest_all( app, '', 'GoToReferences', test ) @pytest.mark.parametrize( 'test', [ # In same file - 1 result { 'req': ( 'goto.cc', 1, 1, [ 'out_of_line' ] ), 'res': ( 'goto.cc', 14, 13 ) }, # In same file - multiple results { 'req': ( 'goto.cc', 1, 1, [ 'line' ] ), 'res': [ ( 'goto.cc', 6, 10 ), ( 'goto.cc', 14, 13 ) ] }, # None { 'req': ( 'goto.cc', 1, 1, [ '' ] ), 'res': 'Symbol not found' }, # Note we don't actually have any testdata that has a full index, so we can't # test multiple files easily, but that's really a clangd thing, not a ycmd # thing. ] ) @SharedYcmd def Subcommands_GoToSymbol_test( app, test ): RunGoToTest_all( app, '', 'GoToSymbol', test ) def RunGetSemanticTest( app, filepath, filetype, test, command, response = requests.codes.ok ): contents = ReadFile( filepath ) common_args = { 'completer_target' : 'filetype_default', 'command_arguments': command, 'line_num' : 10, 'column_num' : 3, 'filepath' : filepath, 'contents' : contents, 'filetype' : filetype } request = common_args request.update( test[ 0 ] ) test = { 'request': request, 'route': '/run_completer_command', 'expect': { 'response': response, 'data': test[ 1 ] } } RunAfterInitialized( app, test ) @pytest.mark.parametrize( 'test', [ # Basic pod types [ { 'line_num': 24, 'column_num': 3 }, has_entry( 'message', equal_to( 'struct Foo {}' ) ), requests.codes.ok ], # [ { 'line_num': 12, 'column_num': 2 }, 'Foo', [ { 'line_num': 12, 'column_num': 8 }, has_entry( 'message', equal_to( 'struct Foo {}' ) ), requests.codes.ok ], [ { 'line_num': 12, 'column_num': 9 }, has_entry( 'message', equal_to( 'struct Foo {}' ) ), requests.codes.ok ], [ { 'line_num': 12, 'column_num': 10 }, has_entry( 'message', equal_to( 'struct Foo {}' ) ), requests.codes.ok ], # [ { 'line_num': 13, 'column_num': 3 }, 'int', [ { 'line_num': 13, 'column_num': 7 }, has_entry( 'message', equal_to( 'int x; // In Foo' ) ), requests.codes.ok ], # [ { 'line_num': 15, 'column_num': 7 }, 'char' ], # Function # [ { 'line_num': 22, 'column_num': 2 }, 'int main()' ], [ { 'line_num': 22, 'column_num': 6 }, 'int main()', requests.codes.ok ], # Declared and canonical type # On Ns:: [ { 'line_num': 25, 'column_num': 3 }, 'namespace Ns', requests.codes.ok ], # On Type (Type) # [ { 'line_num': 25, 'column_num': 8 }, # 'Ns::Type => Ns::BasicType<char>' ], # On "a" (Ns::Type) # [ { 'line_num': 25, 'column_num': 15 }, # 'Ns::Type => Ns::BasicType<char>' ], # [ { 'line_num': 26, 'column_num': 13 }, # 'Ns::Type => Ns::BasicType<char>' ], # Cursor on decl for refs & pointers [ { 'line_num': 39, 'column_num': 3 }, has_entry( 'message', equal_to( 'struct Foo {}' ) ), requests.codes.ok ], [ { 'line_num': 39, 'column_num': 11 }, has_entry( 'message', equal_to( 'Foo &rFoo = foo; // In main' ) ), requests.codes.ok ], [ { 'line_num': 39, 'column_num': 15 }, has_entry( 'message', equal_to( 'Foo foo; // In main' ) ), requests.codes.ok ], [ { 'line_num': 40, 'column_num': 3 }, has_entry( 'message', equal_to( 'struct Foo {}' ) ), requests.codes.ok ], [ { 'line_num': 40, 'column_num': 11 }, has_entry( 'message', equal_to( 'Foo *pFoo = &foo; // In main' ) ), requests.codes.ok ], [ { 'line_num': 40, 'column_num': 18 }, has_entry( 'message', equal_to( 'Foo foo; // In main' ) ), requests.codes.ok ], # [ { 'line_num': 42, 'column_num': 3 }, 'const Foo &' ], [ { 'line_num': 42, 'column_num': 16 }, has_entry( 'message', equal_to( 'const Foo &crFoo = foo; // In main' ) ), requests.codes.ok ], # [ { 'line_num': 43, 'column_num': 3 }, 'const Foo *' ], [ { 'line_num': 43, 'column_num': 16 }, has_entry( 'message', equal_to( 'const Foo *cpFoo = &foo; // In main' ) ), requests.codes.ok ], # Cursor on usage [ { 'line_num': 45, 'column_num': 13 }, has_entry( 'message', equal_to( 'const Foo &crFoo = foo; // In main' ) ), requests.codes.ok ], # [ { 'line_num': 45, 'column_num': 19 }, 'const int' ], [ { 'line_num': 46, 'column_num': 13 }, has_entry( 'message', equal_to( 'const Foo *cpFoo = &foo; // In main' ) ), requests.codes.ok ], # [ { 'line_num': 46, 'column_num': 20 }, 'const int' ], [ { 'line_num': 47, 'column_num': 12 }, has_entry( 'message', equal_to( 'Foo &rFoo = foo; // In main' ) ), requests.codes.ok ], [ { 'line_num': 47, 'column_num': 17 }, has_entry( 'message', equal_to( 'int y; // In Foo' ) ), requests.codes.ok ], [ { 'line_num': 48, 'column_num': 12 }, has_entry( 'message', equal_to( 'Foo *pFoo = &foo; // In main' ) ), requests.codes.ok ], [ { 'line_num': 48, 'column_num': 18 }, has_entry( 'message', equal_to( 'int x; // In Foo' ) ), requests.codes.ok ], # Auto in declaration # [ { 'line_num': 28, 'column_num': 3 }, 'struct Foo &' ], # [ { 'line_num': 28, 'column_num': 11 }, 'struct Foo &' ], [ { 'line_num': 28, 'column_num': 18 }, has_entry( 'message', equal_to( 'Foo foo; // In main' ) ), requests.codes.ok ], # [ { 'line_num': 29, 'column_num': 3 }, 'Foo *' ], # [ { 'line_num': 29, 'column_num': 11 }, 'Foo *' ], [ { 'line_num': 29, 'column_num': 18 }, has_entry( 'message', equal_to( 'Foo foo; // In main' ) ), requests.codes.ok ], # [ { 'line_num': 31, 'column_num': 3 }, 'const Foo &' ], # [ { 'line_num': 31, 'column_num': 16 }, 'const Foo &' ], # [ { 'line_num': 32, 'column_num': 3 }, 'const Foo *' ], # [ { 'line_num': 32, 'column_num': 16 }, 'const Foo *' ], # Auto in usage # [ { 'line_num': 34, 'column_num': 14 }, 'const Foo' ], # [ { 'line_num': 34, 'column_num': 21 }, 'const int' ], # [ { 'line_num': 35, 'column_num': 14 }, 'const Foo *' ], # [ { 'line_num': 35, 'column_num': 22 }, 'const int' ], [ { 'line_num': 36, 'column_num': 13 }, has_entry( 'message', equal_to( 'auto &arFoo = foo; // In main' ) ), requests.codes.ok ], [ { 'line_num': 36, 'column_num': 19 }, has_entry( 'message', equal_to( 'int y; // In Foo' ) ), requests.codes.ok ], # [ { 'line_num': 37, 'column_num': 13 }, 'Foo *' ], [ { 'line_num': 37, 'column_num': 20 }, has_entry( 'message', equal_to( 'int x; // In Foo' ) ), requests.codes.ok ], # Unicode [ { 'line_num': 51, 'column_num': 13 }, has_entry( 'message', equal_to( 'Unicøde *ø; // In main' ) ), requests.codes.ok ], # Bound methods # On Win32, methods pick up an __attribute__((thiscall)) to annotate their # calling convention. This shows up in the type, which isn't ideal, but # also prohibitively complex to try and strip out. [ { 'line_num': 53, 'column_num': 15 }, has_entry( 'message', matches_regexp( r'int bar\(int i\)(?: __attribute__\(\(thiscall\)\))?; // In Foo' ) ), requests.codes.ok ], [ { 'line_num': 54, 'column_num': 18 }, has_entry( 'message', matches_regexp( r'int bar\(int i\)(?: __attribute__\(\(thiscall\)\))?; // In Foo' ) ), requests.codes.ok ], # Multi-line function declaration [ { 'line_num': 58, 'column_num': 20 }, has_entry( 'message', equal_to( 'unsigned long long long_function_name(unsigned long long first, ' 'unsigned long long second)' ) ), requests.codes.ok ], [ { 'line_num': 61, 'column_num': 20 }, has_entry( 'message', equal_to( 'unsigned long long long_function_name(unsigned long long first, ' 'unsigned long long second); // In namespace ns' ) ), requests.codes.ok ], ] ) @pytest.mark.parametrize( 'subcommand', [ 'GetType', 'GetTypeImprecise' ] ) @SharedYcmd def Subcommands_GetType_test( app, subcommand, test ): RunGetSemanticTest( app, PathToTestFile( 'GetType_Clang_test.cc' ), 'cpp', test, [ subcommand ], test[ 2 ] ) @pytest.mark.parametrize( 'test', [ # from local file [ { 'line_num': 5, 'column_num': 10 }, has_entry( 'detailed_info', equal_to( 'function docstring_int_main_TU_file\n\n→ void\ndocstring\n\n' 'void docstring_int_main_TU_file()' ) ), requests.codes.ok ], # from header [ { 'line_num': 6, 'column_num': 10 }, has_entry( 'detailed_info', equal_to( 'function docstring_from_header_file\n\n→ void\ndocstring\n\n' 'void docstring_from_header_file()' ) ), requests.codes.ok ], # no docstring [ { 'line_num': 7, 'column_num': 7 }, has_entry( 'detailed_info', equal_to( 'variable x\n\nType: int\nValue = 3\n\n' '// In docstring_int_main_TU_file\nint x = 3' ) ), requests.codes.ok ], # no hover [ { 'line_num': 8, 'column_num': 1 }, ErrorMatcher( RuntimeError, 'No documentation available.' ), requests.codes.server_error ] ] ) @pytest.mark.parametrize( 'subcommand', [ 'GetDoc', 'GetDocImprecise' ] ) @SharedYcmd def Subcommands_GetDoc_test( app, subcommand, test ): RunGetSemanticTest( app, PathToTestFile( 'GetDoc_Clang_test.cc' ), 'cpp', test, [ subcommand ], test[ 2 ] ) def RunFixItTest( app, line, column, lang, file_path, check ): contents = ReadFile( file_path ) language_options = { 'cpp11': { 'filetype' : 'cpp', }, 'cuda': { 'filetype' : 'cuda', }, 'objective-c': { 'filetype' : 'objc', }, } args = { 'completer_target' : 'filetype_default', 'contents' : contents, 'filepath' : file_path, 'command_arguments': [ 'FixIt' ], 'line_num' : line, 'column_num' : column, } args.update( language_options[ lang ] ) test = { 'request': args, 'route': '/detailed_diagnostic' } # First get diags. diags = RunAfterInitialized( app, test ) while 'message' in diags and 'diagnostics' in diags[ 'message' ].lower(): receive_diags = { 'request': args, 'route': '/receive_messages' } RunAfterInitialized( app, receive_diags ) diags = RunAfterInitialized( app, test ) results = app.post_json( '/run_completer_command', BuildRequest( **args ) ).json pprint( results ) check( results ) def FixIt_Check_cpp11_Ins( results ): # First fixit # switch(A()) { // expected-error{{explicit conversion to}} assert_that( results, has_entries( { 'fixits': contains_exactly( has_entries( { 'kind': 'quickfix', 'chunks': contains_exactly( has_entries( { 'replacement_text': equal_to( 'static_cast<int>(' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 16, 'column_num': 10 } ), 'end' : has_entries( { 'line_num': 16, 'column_num': 10 } ), } ), } ), has_entries( { 'replacement_text': equal_to( ')' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 16, 'column_num': 13 } ), 'end' : has_entries( { 'line_num': 16, 'column_num': 13 } ), } ), } ) ), 'location': has_entries( { 'line_num': 16, 'column_num': 1 } ) } ) ) } ) ) def FixIt_Check_cpp11_InsMultiLine( results ): # Similar to FixIt_Check_cpp11_1 but inserts split across lines # assert_that( results, has_entries( { 'fixits': contains_exactly( has_entries( { 'kind': 'quickfix', 'chunks': contains_exactly( has_entries( { 'replacement_text': equal_to( 'static_cast<int>(' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 26, 'column_num': 7 } ), 'end' : has_entries( { 'line_num': 26, 'column_num': 7 } ), } ), } ), has_entries( { 'replacement_text': equal_to( ')' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 28, 'column_num': 2 } ), 'end' : has_entries( { 'line_num': 28, 'column_num': 2 } ), } ), } ) ), 'location': has_entries( { 'line_num': 25, 'column_num': 14 } ) } ) ) } ) ) def FixIt_Check_cpp11_Del( results ): # Removal of :: assert_that( results, has_entries( { 'fixits': contains_exactly( has_entries( { 'kind': 'quickfix', 'chunks': contains_exactly( has_entries( { 'replacement_text': equal_to( '' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 35, 'column_num': 7 } ), 'end' : has_entries( { 'line_num': 35, 'column_num': 9 } ), } ), } ) ), 'location': has_entries( { 'line_num': 35, 'column_num': 7 } ) } ) ) } ) ) def FixIt_Check_cpp11_Repl( results ): assert_that( results, has_entries( { 'fixits': contains_exactly( has_entries( { 'kind': 'quickfix', 'chunks': contains_exactly( has_entries( { 'replacement_text': equal_to( 'foo' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 40, 'column_num': 6 } ), 'end' : has_entries( { 'line_num': 40, 'column_num': 9 } ), } ), } ) ), 'location': has_entries( { 'line_num': 40, 'column_num': 6 } ) } ) ) } ) ) def FixIt_Check_cpp11_DelAdd( results ): assert_that( results, has_entries( { 'fixits': contains_exactly( has_entries( { 'kind': 'quickfix', 'chunks': contains_exactly( has_entries( { 'replacement_text': equal_to( '' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 48, 'column_num': 3 } ), 'end' : has_entries( { 'line_num': 48, 'column_num': 4 } ), } ), } ), has_entries( { 'replacement_text': equal_to( '~' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 48, 'column_num': 9 } ), 'end' : has_entries( { 'line_num': 48, 'column_num': 9 } ), } ), } ), ), 'location': has_entries( { 'line_num': 48, 'column_num': 3 } ) } ), has_entries( { 'chunks': contains_exactly( has_entries( { 'replacement_text': equal_to( '= default;' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 48, 'column_num': 15 } ), 'end' : has_entries( { 'line_num': 48, 'column_num': 17 } ), } ), } ), ), 'location': has_entries( { 'line_num': 48, 'column_num': 3 } ) } ), # Unresolved, requires /resolve_fixit request has_entries( { 'text': 'Move function body to declaration', 'resolve': True, 'command': has_entries( { 'command': 'clangd.applyTweak' } ) } ), ) } ) ) def FixIt_Check_objc( results ): assert_that( results, has_entries( { 'fixits': contains_exactly( has_entries( { 'kind': 'quickfix', 'chunks': contains_exactly( has_entries( { 'replacement_text': equal_to( 'id' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 5, 'column_num': 3 } ), 'end' : has_entries( { 'line_num': 5, 'column_num': 3 } ), } ), } ) ), 'location': has_entries( { 'line_num': 5, 'column_num': 3 } ) } ) ) } ) ) def FixIt_Check_objc_NoFixIt( results ): # and finally, a warning with no fixits assert_that( results, equal_to( { 'fixits': [] } ) ) def FixIt_Check_cpp11_MultiFirst( results ): assert_that( results, has_entries( { 'fixits': contains_exactly( # first fix-it at 54,16 has_entries( { 'kind': 'quickfix', 'chunks': contains_exactly( has_entries( { 'replacement_text': equal_to( 'foo' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 54, 'column_num': 16 } ), 'end' : has_entries( { 'line_num': 54, 'column_num': 19 } ), } ), } ) ), 'location': has_entries( { 'line_num': 54, 'column_num': 15 } ) } ), ) } ) ) def FixIt_Check_cpp11_MultiSecond( results ): assert_that( results, has_entries( { 'fixits': contains_exactly( # second fix-it at 54,52 has_entries( { 'kind': 'quickfix', 'chunks': contains_exactly( has_entries( { 'replacement_text': equal_to( '' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 54, 'column_num': 52 } ), 'end' : has_entries( { 'line_num': 54, 'column_num': 53 } ), } ), } ), has_entries( { 'replacement_text': equal_to( '~' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 54, 'column_num': 58 } ), 'end' : has_entries( { 'line_num': 54, 'column_num': 58 } ), } ), } ), ), 'location': has_entries( { 'line_num': 54, 'column_num': 51 } ) } ), has_entries( { 'kind': 'quickfix', 'chunks': contains_exactly( has_entries( { 'replacement_text': equal_to( '= default;' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 54, 'column_num': 64 } ), 'end' : has_entries( { 'line_num': 54, 'column_num': 67 } ), } ), } ) ), 'location': has_entries( { 'line_num': 54, 'column_num': 51 } ) } ), ) } ) ) def FixIt_Check_unicode_Ins( results ): assert_that( results, has_entries( { 'fixits': contains_exactly( has_entries( { 'kind': 'quickfix', 'chunks': contains_exactly( has_entries( { 'replacement_text': equal_to( '=' ), 'range': has_entries( { 'start': has_entries( { 'line_num': 21, 'column_num': 9 } ), 'end' : has_entries( { 'line_num': 21, 'column_num': 11 } ), } ), } ) ), 'location': has_entries( { 'line_num': 21, 'column_num': 16 } ) } ) ) } ) ) def FixIt_Check_cpp11_Note( results ): assert_that( results, has_entries( { 'fixits': contains_exactly( # First note: put parens around it has_entries( { 'kind': 'quickfix', 'text': contains_string( 'parentheses around the assignment' ), 'chunks': contains_exactly( ChunkMatcher( '(', LineColMatcher( 59, 8 ), LineColMatcher( 59, 8 ) ), ChunkMatcher( ')', LineColMatcher( 61, 12 ), LineColMatcher( 61, 12 ) ) ), 'location': LineColMatcher( 60, 1 ), } ), # Second note: change to == has_entries( { 'kind': 'quickfix', 'text': contains_string( '==' ), 'chunks': contains_exactly( ChunkMatcher( '==', LineColMatcher( 60, 8 ), LineColMatcher( 60, 9 ) ) ), 'location': LineColMatcher( 60, 1 ), } ), ) } ) ) def FixIt_Check_cpp11_SpellCheck( results ): assert_that( results, has_entries( { 'fixits': contains_exactly( # Change to SpellingIsNotMyStrongPoint has_entries( { 'kind': 'quickfix', 'text': contains_string( "change 'SpellingIsNotMyStringPiont' to " "'SpellingIsNotMyStrongPoint'" ), 'chunks': contains_exactly( ChunkMatcher( 'SpellingIsNotMyStrongPoint', LineColMatcher( 72, 9 ), LineColMatcher( 72, 35 ) ) ), 'location': LineColMatcher( 72, 9 ), } ) ) } ) ) def FixIt_Check_cuda( results ): assert_that( results, has_entries( { 'fixits': contains_exactly( has_entries( { 'kind': 'quickfix', 'text': contains_string( "change 'int' to 'void'" ), 'chunks': contains_exactly( ChunkMatcher( 'void', LineColMatcher( 3, 12 ), LineColMatcher( 3, 15 ) ) ), 'location': LineColMatcher( 3, 12 ), } ) ) } ) ) def FixIt_Check_SubexprExtract_Resolved( results ): assert_that( results, has_entries( { 'fixits': contains_exactly( has_entries( { 'text': 'Extract subexpression to variable', 'chunks': contains_exactly( ChunkMatcher( 'auto dummy = i + 3;\n ', LineColMatcher( 84, 3 ), LineColMatcher( 84, 3 ) ), ChunkMatcher( 'dummy', LineColMatcher( 84, 14 ), LineColMatcher( 84, 21 ) ), ) } ) ) } ) ) def FixIt_Check_RawStringReplace_Resolved( results ): assert_that( results, has_entries( { 'fixits': contains_exactly( has_entries( { 'text': 'Convert to raw string', 'chunks': contains_exactly( ChunkMatcher( 'R"(\\\\r\\asd\n\\v)"', LineColMatcher( 80, 19 ), LineColMatcher( 80, 36 ) ), ) } ) ) } ) ) def FixIt_Check_MacroExpand_Resolved( results ): assert_that( results, has_entries( { 'fixits': contains_exactly( has_entries( { 'text': "Expand macro 'DECLARE_INT'", 'chunks': contains_exactly( ChunkMatcher( 'int i', LineColMatcher( 83, 3 ), LineColMatcher( 83, 17 ) ), ) } ) ) } ) ) def FixIt_Check_AutoExpand_Resolved( results ): assert_that( results, has_entries( { 'fixits': contains_exactly( has_entries( { 'text': "Expand auto type", 'chunks': contains_exactly( ChunkMatcher( 'const char *', LineColMatcher( 80, 1 ), LineColMatcher( 80, 6 ) ), ) } ) ) } ) ) @pytest.mark.parametrize( 'line,column,language,filepath,check', [ [ 16, 1, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), FixIt_Check_cpp11_Ins ], [ 25, 14, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), FixIt_Check_cpp11_InsMultiLine ], [ 35, 7, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), FixIt_Check_cpp11_Del ], [ 40, 6, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), FixIt_Check_cpp11_Repl ], [ 48, 3, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), FixIt_Check_cpp11_DelAdd ], [ 5, 3, 'objective-c', PathToTestFile( 'objc', 'FixIt_Clang_objc.m' ), FixIt_Check_objc ], [ 7, 1, 'objective-c', PathToTestFile( 'objc', 'FixIt_Clang_objc.m' ), FixIt_Check_objc_NoFixIt ], [ 3, 12, 'cuda', PathToTestFile( 'cuda', 'fixit_test.cu' ), FixIt_Check_cuda ], # multiple errors on a single line; both with fixits. The cursor is on the # first one (so just that one is fixed) [ 54, 15, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), FixIt_Check_cpp11_MultiFirst ], # should put closest fix-it first? [ 54, 51, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), FixIt_Check_cpp11_MultiSecond ], # unicode in line for fixit [ 21, 16, 'cpp11', PathToTestFile( 'unicode.cc' ), FixIt_Check_unicode_Ins ], # FixIt attached to a "child" diagnostic (i.e. a Note) [ 60, 1, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), FixIt_Check_cpp11_Note ], # FixIt due to forced spell checking [ 72, 9, 'cpp11', PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), FixIt_Check_cpp11_SpellCheck ], ] ) @SharedYcmd def Subcommands_FixIt_all_test( app, line, column, language, filepath, check ): RunFixItTest( app, line, column, language, filepath, check ) def RunRangedFixItTest( app, rng, expected, chosen_fixit = 0 ): contents = ReadFile( PathToTestFile( 'FixIt_Clang_cpp11.cpp' ) ) args = { 'completer_target' : 'filetype_default', 'contents' : contents, 'filepath' : PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), 'command_arguments': [ 'FixIt' ], 'range' : rng, 'filetype' : 'cpp' } app.post_json( '/event_notification', CombineRequest( args, { 'event_name': 'FileReadyToParse', } ), expect_errors = True ) WaitUntilCompleterServerReady( app, 'cpp' ) response = app.post_json( '/run_completer_command', BuildRequest( **args ) ).json args[ 'fixit' ] = response[ 'fixits' ][ chosen_fixit ] response = app.post_json( '/resolve_fixit', BuildRequest( **args ) ).json print( 'Resolved fixit response = ' ) print( response ) expected( response ) @WithRetry @pytest.mark.parametrize( 'test', [ [ { 'start': { 'line_num': 80, 'column_num': 1 }, 'end': { 'line_num': 80, 'column_num': 4 }, }, FixIt_Check_AutoExpand_Resolved ], [ { 'start': { 'line_num': 83, 'column_num': 3 }, 'end': { 'line_num': 83, 'column_num': 13 }, }, FixIt_Check_MacroExpand_Resolved ], [ { 'start': { 'line_num': 84, 'column_num': 14 }, 'end': { 'line_num': 84, 'column_num': 20 }, }, FixIt_Check_SubexprExtract_Resolved ], [ { 'start': { 'line_num': 80, 'column_num': 19 }, 'end': { 'line_num': 80, 'column_num': 35 }, }, FixIt_Check_RawStringReplace_Resolved ], ] ) @SharedYcmd def Subcommands_FixIt_Ranged_test( app, test ): RunRangedFixItTest( app, test[ 0 ], test[ 1 ] ) @WithRetry @SharedYcmd def Subcommands_FixIt_AlreadyResolved_test( app ): filename = PathToTestFile( 'FixIt_Clang_cpp11.cpp' ) request = { 'completer_target' : 'filetype_default', 'contents' : ReadFile( filename ), 'filepath' : filename, 'command_arguments': [ 'FixIt' ], 'line_num' : 16, 'column_num' : 1, 'filetype' : 'cpp' } app.post_json( '/event_notification', CombineRequest( request, { 'event_name': 'FileReadyToParse', } ), expect_errors = True ) WaitUntilCompleterServerReady( app, 'cpp' ) expected = app.post_json( '/run_completer_command', BuildRequest( **request ) ).json print( 'expected = ' ) print( expected ) request[ 'fixit' ] = expected[ 'fixits' ][ 0 ] actual = app.post_json( '/resolve_fixit', BuildRequest( **request ) ).json print( 'actual = ' ) print( actual ) assert_that( actual, equal_to( expected ) ) @WithRetry @IsolatedYcmd( { 'clangd_args': [ '-hidden-features' ] } ) def Subcommands_FixIt_ClangdTweaks_test( app ): selection = { 'start': { 'line_num': 80, 'column_num': 19 }, 'end': { 'line_num': 80, 'column_num': 4 } } def NoFixitsProduced( results ): assert_that( results, has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': [], 'location': LocationMatcher( PathToTestFile( 'FixIt_Clang_cpp11.cpp' ), 1, 1 ) } ) ) } ) ) RunRangedFixItTest( app, selection, NoFixitsProduced, 2 ) @SharedYcmd def Subcommands_RefactorRename_test( app ): test = { 'request': { 'filetype': 'cpp', 'completer_target': 'filetype_default', 'contents': ReadFile( PathToTestFile( 'basic.cpp' ) ), 'filepath': PathToTestFile( 'basic.cpp' ), 'command_arguments': [ 'RefactorRename', 'Bar' ], 'line_num': 17, 'column_num': 4, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( 'Bar', LineColMatcher( 1, 8 ), LineColMatcher( 1, 11 ) ), ChunkMatcher( 'Bar', LineColMatcher( 9, 3 ), LineColMatcher( 9, 6 ) ), ChunkMatcher( '\n\n', LineColMatcher( 12, 2 ), LineColMatcher( 15, 1 ) ), ChunkMatcher( 'Bar', LineColMatcher( 15, 8 ), LineColMatcher( 15, 11 ) ), ChunkMatcher( ' ', LineColMatcher( 15, 46 ), LineColMatcher( 16, 1 ) ), ChunkMatcher( 'Bar', LineColMatcher( 17, 3 ), LineColMatcher( 17, 6 ) ), ChunkMatcher( '', LineColMatcher( 17, 14 ), LineColMatcher( 17, 15 ) ), ChunkMatcher( ' ', LineColMatcher( 17, 17 ), LineColMatcher( 17, 17 ) ), ChunkMatcher( ' ', LineColMatcher( 17, 19 ), LineColMatcher( 17, 19 ) ), ) } ) ) } ) }, 'route': '/run_completer_command' } RunAfterInitialized( app, test ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/�������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0022276�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/FixIt_Clang_cpp11.cpp����������������������0000664�0000000�0000000�00000003636�13746324601�0026145�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // A selection of the tests from llvm/tools/clang/test/FixIt/fixit-cxx0x.cpp // // Modified to test fixits across multiple lines and ensure that the number of // diagnostics doesn't hit the compile threshold. // /* This is a test of the various code modification hints that only apply in C++0x. */ struct A { explicit operator int(); // expected-note{{conversion to integral type}} }; // _FixIt_Check_cpp11_Ins (2 inserts on one line) void x() { switch(A()) { // expected-error{{explicit conversion to}} } } // _FixIt_Check_cpp11_InsMultiLine // same as above, except the 2 inserts split over multiple lines, neiher of // which are the *same* line as the diagnostic report (diagnostic on line // "switch", inserts on following line and later line void y() { switch( // diag A // insert: static_cast<int>( ( ) // insert: ) ) { } } // _FixIt_Check_cpp11_Del using ::T = void; // expected-error {{name defined in alias declaration must be an identifier}} namespace dtor_fixit { class foo { // _FixIt_Check_cpp11_Repl ~bar() { } // expected-error {{expected the class name after '~' to name a destructor}} // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:6-[[@LINE-1]]:9}:"foo" }; class bar { ~bar(); }; // _FixIt_Check_cpp11_DelAdd ~bar::bar() {} // expected-error {{'~' in destructor name should be after nested name specifier}} // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:4}:"" // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:9-[[@LINE-2]]:9}:"~" } namespace test_fixit_multiple { class foo { ~bar() { } }; class bar { ~bar(); }; ~bar::bar() { } } void z() { bool x; if ( x = true ) { } } namespace Typo { struct SpellingIsNotMyStrongPoint; } void typo() { Typo::SpellingIsNotMyStringPiont *p; } int foo(int); #define DECLARE_INT(name) int name auto to_raw_str = "\\\\r\\asd\n\\v"; int bar() { DECLARE_INT(i) = 4; return foo( i + 3 ); } ��������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/GetDoc_Clang_test.cc�����������������������0000664�0000000�0000000�00000000172�13746324601�0026115�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "docstring.h" /// docstring void docstring_int_main_TU_file() { docstring_from_header_file(); int x = 3; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/GetType_Clang_test.cc����������������������0000664�0000000�0000000�00000002124�13746324601�0026330�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// This file is used in RunCompleterCommand_GetType_Clang_test namespace Ns { template< typename T > struct BasicType { BasicType( const T * ); }; typedef BasicType< char > Type; } struct Foo { int x; int y; char c; int bar(int i) { return i+1; } }; int main() { Foo foo; Ns::Type a = "hello"; Ns::Type b = a; auto &arFoo = foo; auto *apFoo = &foo; const auto &acrFoo = foo; const auto *acpFoo = &foo; int acry = acrFoo.y; int acpx = acpFoo->x; int ary = arFoo.y; int apx = apFoo->x; Foo &rFoo = foo; Foo *pFoo = &foo; const Foo &crFoo = foo; const Foo *cpFoo = &foo; int cry = crFoo.y; int cpx = cpFoo->x; int ry = rFoo.y; int px = pFoo->x; struct Unicøde; Unicøde *ø; int i = foo.bar(1); int j = apFoo->bar(1); return 0; } unsigned long long long_function_name(unsigned long long first, unsigned long long second); namespace ns { unsigned long long long_function_name(unsigned long long first, unsigned long long second); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/GoTo_Clang_ZeroBasedLineAndColumn_test.cc��0000664�0000000�0000000�00000000246�13746324601�0032171�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// this file is used in RunCompleterCommand_GoTo_Clang_ZeroBasedLineAndColumn_test struct Foo { int x; int y; char c; }; int main() { Foo foo; return 0; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/basic.cpp����������������������������������0000664�0000000�0000000�00000000415�13746324601�0024063�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������struct Foo { int x; int y; char c; }; int main() { Foo foo; // The location after the dot is line 11, col 7 foo. } static Foo test_function_that_has_no_errors() { Foo foo = { 1,2,'c'}; if (foo.c ) { foo.x = 1; foo.y = 2; } return foo; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/compile_flags.txt��������������������������0000664�0000000�0000000�00000000051�13746324601�0025637�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������-xc++ -std=c++11 -iquotequote/ -Isystem/ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/cuda/��������������������������������������0000775�0000000�0000000�00000000000�13746324601�0023212�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/cuda/basic.cu������������������������������0000664�0000000�0000000�00000000173�13746324601�0024625�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "cuda.h" /// This is a test kernel __global__ void kernel() {} int main() { kernel<<<1, 1>>>(); return 0; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/cuda/compile_flags.txt���������������������0000664�0000000�0000000�00000000007�13746324601�0026554�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������-xcuda �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/cuda/completion_test.cu��������������������0000664�0000000�0000000�00000000447�13746324601�0026760�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "cuda.h" namespace Kernels { __device__ void do_something(float* a) {} } template<typename F, class ...Args> __global__ void launch(F& fn, Args args...) { fn(args...); } int main() { // The location after the colon is line 16, col 29 launch<<<1, 1>>>(Kernels:: return 0; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/cuda/cuda.h��������������������������������0000664�0000000�0000000�00000005613�13746324601�0024304�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Modified by ycmd contributors */ /* University of Illinois/NCSA Open Source License Copyright (c) 2007-2016 University of Illinois at Urbana-Champaign. All rights reserved. Developed by: LLVM Team University of Illinois at Urbana-Champaign http://llvm.org Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal with 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: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimers. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimers in the documentation and/or other materials provided with the distribution. * Neither the names of the LLVM Team, University of Illinois at Urbana-Champaign, nor the names of its contributors may be used to endorse or promote products derived from this Software without specific prior written permission. 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 CONTRIBUTORS 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 WITH THE SOFTWARE. */ /* Minimal declarations for CUDA support. Testing purposes only. */ typedef __SIZE_TYPE__ size_t; // Make this file work with nvcc, for testing compatibility. #ifndef __NVCC__ #define __constant__ __attribute__((constant)) #define __device__ __attribute__((device)) #define __global__ __attribute__((global)) #define __host__ __attribute__((host)) #define __shared__ __attribute__((shared)) #define __launch_bounds__(...) __attribute__((launch_bounds(__VA_ARGS__))) struct dim3 { unsigned x, y, z; __host__ __device__ dim3(unsigned x, unsigned y = 1, unsigned z = 1) : x(x), y(y), z(z) {} }; typedef struct cudaStream *cudaStream_t; int cudaConfigureCall(dim3 gridSize, dim3 blockSize, size_t sharedSize = 0, cudaStream_t stream = 0); // Host- and device-side placement new overloads. void *operator new(__SIZE_TYPE__, void *p) { return p; } void *operator new[](__SIZE_TYPE__, void *p) { return p; } __device__ void *operator new(__SIZE_TYPE__, void *p) { return p; } __device__ void *operator new[](__SIZE_TYPE__, void *p) { return p; } #endif // !__NVCC__ ���������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/cuda/fixit_test.cu�������������������������0000664�0000000�0000000�00000000136�13746324601�0025725�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "cuda.h" __global__ int kernel(); int main() { kernel<<<1, 1>>>(); return 0; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/cuda/kernel_call.cu������������������������0000664�0000000�0000000�00000005120�13746324601�0026014�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Modified by ycmd contributors */ /* University of Illinois/NCSA Open Source License Copyright (c) 2007-2016 University of Illinois at Urbana-Champaign. All rights reserved. Developed by: LLVM Team University of Illinois at Urbana-Champaign http://llvm.org Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal with 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: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimers. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimers in the documentation and/or other materials provided with the distribution. * Neither the names of the LLVM Team, University of Illinois at Urbana-Champaign, nor the names of its contributors may be used to endorse or promote products derived from this Software without specific prior written permission. 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 CONTRIBUTORS 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 WITH THE SOFTWARE. */ #include "cuda.h" __global__ void g1(int x) {} template <typename T> void t1(T arg) { g1<<<arg, arg>>>(1); } void h1(int x) {} int h2(int x) { return 1; } int main(void) { g1<<<1, 1>>>(42); g1(42); // expected-error {{call to global function 'g1' not configured}} g1<<<1>>>(42); // expected-error {{too few execution configuration arguments to kernel function call}} g1<<<1, 1, 0, 0, 0>>>(42); // expected-error {{too many execution configuration arguments to kernel function call}} t1(1); h1<<<1, 1>>>(42); // expected-error {{kernel call to non-global function 'h1'}} int (*fp)(int) = h2; fp<<<1, 1>>>(42); // expected-error {{must have void return type}} g1<<<undeclared, 1>>>(42); // expected-error {{use of undeclared identifier 'undeclared'}} } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/docstring.h��������������������������������0000664�0000000�0000000�00000000144�13746324601�0024442�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef DOCS #define DOCS /// docstring void docstring_from_header_file(); #endif /* ifndef DOCS */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/driver_mode_cl/����������������������������0000775�0000000�0000000�00000000000�13746324601�0025253�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/driver_mode_cl/executable/�����������������0000775�0000000�0000000�00000000000�13746324601�0027374�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/driver_mode_cl/executable/compile_flags.txt0000664�0000000�0000000�00000000063�13746324601�0032740�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������-xc++ --driver-mode=cl /c /Idriver_mode_cl_include �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������driver_mode_cl.cpp����������������������������������������������������������������������������������0000664�0000000�0000000�00000000236�13746324601�0032777�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/driver_mode_cl/executable�������������������������������������������������������������������������������������#include "driver_mode_cl_include.h" int main(void) { // Position after the last underscore: // line = 8 // column = 18 driver_mode_cl_ return 0; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������driver_mode_cl_include/�����������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0033775�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/driver_mode_cl/executable�������������������������������������������������������������������������������������driver_mode_cl_include.h����������������������������������������������������������������������������0000664�0000000�0000000�00000000262�13746324601�0040626�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/driver_mode_cl/executable/driver_mode_cl_include��������������������������������������������������������������#ifndef DRIVER_MODE_CL_INCLUDE_H #define DRIVER_MODE_CL_INCLUDE_H void driver_mode_cl_include_func(void); int driver_mode_cl_include_int; #endif /* DRIVER_MODE_CL_INCLUDE_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/driver_mode_cl/flag/�����������������������0000775�0000000�0000000�00000000000�13746324601�0026164�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/driver_mode_cl/flag/compile_flags.txt������0000664�0000000�0000000�00000000064�13746324601�0031531�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������-xc++ --driver-mode=cl /c /Idriver_mode_cl_include/ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/driver_mode_cl/flag/driver_mode_cl.cpp�����0000664�0000000�0000000�00000000236�13746324601�0031646�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "driver_mode_cl_include.h" int main(void) { // Position after the last underscore: // line = 8 // column = 18 driver_mode_cl_ return 0; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/driver_mode_cl/flag/driver_mode_cl_include/0000775�0000000�0000000�00000000000�13746324601�0032644�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������driver_mode_cl_include.h����������������������������������������������������������������������������0000664�0000000�0000000�00000000262�13746324601�0037416�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/driver_mode_cl/flag/driver_mode_cl_include��������������������������������������������������������������������#ifndef DRIVER_MODE_CL_INCLUDE_H #define DRIVER_MODE_CL_INCLUDE_H void driver_mode_cl_include_func(void); int driver_mode_cl_include_int; #endif /* DRIVER_MODE_CL_INCLUDE_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/extra_conf/��������������������������������0000775�0000000�0000000�00000000000�13746324601�0024426�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/extra_conf/.ycm_extra_conf.py��������������0000664�0000000�0000000�00000001043�13746324601�0030054�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import os DIR_OF_THIS_SCRIPT = os.path.abspath( os.path.dirname( __file__ ) ) def Settings( **kwargs ): if kwargs[ 'language' ] == 'cfamily': basename = os.path.basename( kwargs[ 'filename' ] ) if basename == 'foo.cpp': return { 'flags': ['-I', 'include', '-DFOO'] } if basename == 'bar.cpp': return { 'flags': ['g++', '-I', 'include', '-DBAR'], 'include_paths_relative_to_dir': os.path.join( DIR_OF_THIS_SCRIPT, 'subdir' ) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/extra_conf/bar.cpp�������������������������0000664�0000000�0000000�00000000111�13746324601�0025667�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "bar.h" int main() { Structure structure; structure. } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/extra_conf/foo.cpp�������������������������0000664�0000000�0000000�00000000111�13746324601�0025706�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "foo.h" int main() { Structure structure; structure. } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/extra_conf/global_extra_conf.py������������0000664�0000000�0000000�00000000126�13746324601�0030447�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������def Settings( **kwargs ): return { 'ls': {}, 'flags': [ '-I', 'test' ], } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/extra_conf/include/������������������������0000775�0000000�0000000�00000000000�13746324601�0026051�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/extra_conf/include/foo.h�������������������0000664�0000000�0000000�00000000074�13746324601�0027006�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������struct Structure { #ifdef FOO int member_foo; #endif }; ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/extra_conf/subdir/�������������������������0000775�0000000�0000000�00000000000�13746324601�0025716�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/extra_conf/subdir/include/�����������������0000775�0000000�0000000�00000000000�13746324601�0027341�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/extra_conf/subdir/include/bar.h������������0000664�0000000�0000000�00000000074�13746324601�0030257�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������struct Structure { #ifdef BAR int member_bar; #endif }; ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/general_fallback/��������������������������0000775�0000000�0000000�00000000000�13746324601�0025532�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/general_fallback/lang_c.c������������������0000664�0000000�0000000�00000005007�13746324601�0027123�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Identifier completion makes sense for duck-typing. // // The purpose of this test is to both demonstrate the use-case and test the // functionality. Completions of "A.a_parameter" using the identifier // completer feels natural, whereas offering no suggestions feels broken typedef struct { int a_parameter; int another_parameter; } TTest; typedef struct { int another_int; int and_a_final_int; } TTest_Which_Is_Not_TTest; static void do_something( int ); static void do_another_thing( int ); // TESTCASE1: use of . on macro parameter. Macros used this way are compile-time // duck-typing. Note this in this particular instance, libclang actually // offers semantic completions if there is only call to this macro // (e.g. DO_SOMETHING_TO( a_test ) - clever little shrew), so we don't call it /* 1 2 3 4 1234567890123456789012345678901234567890123456789 */ #define DO_SOMETHING_TO( A ) \ do { \ do_something( A.a_parameter ); \ do_another_thing( A.another_parameter ); \ } while (0) // TESTCASE2: use of -> on macro parameter. Macros used this way are // compile-time duck-typing /* 1 2 3 4 1234567890123456789012345678901234567890123456789 */ #define DO_SOMETHING_VIA( P ) \ do { \ do_something( P->a_parameter ); \ do_another_thing( P->another_parameter ); \ } while (0) int main( int argc, char ** argv ) { TTest a_test; // TESTCASE3: use of -> on struct. This is an error, but the user might // subsequently change a_test to be a pointer. Assumption: user knows what they // are doing /* 1 2 3 4 1234567890123456789012345678901234567890123456789 */ if ( a_test->anoth ) { (void) 0; } // TESTCASE4: use of . on struct. Gets semantic suggestions /* 1 2 3 4 1234567890123456789012345678901234567890123456789 */ if ( a_test.a_parameter ) { (void) 0; } // TESTCASE5: use of . on struct for non-matching text. Again, probably an // error, but assume the user knows best, and might change it later. /* 1 2 3 4 1234567890123456789012345678901234567890123456789 */ if ( a_test.do_ ) { (void) 0; } TTest *p_test = &a_test; // TESTCASE6: use of -> on pointer. Semantic completions /* 1 2 3 4 1234567890123456789012345678901234567890123456789 */ if ( p_test-> ) { } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/general_fallback/lang_cpp.cc���������������0000664�0000000�0000000�00000002450�13746324601�0027625�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������namespace { struct TTest { static void PrintDiagnostic(); int a_parameter; int another_parameter; }: void do_something(int); void do_another_thing(int); // TESTCASE1: use of . on template argument. Templates used this way are // compile-time duck typing template<typename T> void DO_SOMETHING_TO( T& t ) { /* 1 2 3 4 1234567890123456789012345678901234567890123456789 */ do_something( t.a_parameter ); } // TESTCASE2: use of -> on template argument. Templates used this way are // compile-time duck typing template<typename T> void DO_SOMETHING_WITH( T* t ) { /* 1 2 3 4 1234567890123456789012345678901234567890123456789 */ do_something( t->a_parameter ); } // TESTCASE3: use of :: on template argument. Templates used this way are // compile-time duck typing template<typename T> void PRINT_A_DIAGNOSTIC( ) { /* 1 2 3 4 1234567890123456789012345678901234567890123456789 */ T::PrintDiagnostic(); } } int main (int , char **) { // bonus test case (regression test) for identifier/forced semantic without // trigger TTest test; /* 1 2 3 4 1234567890123456789012345678901234567890123456789 */ DO_SOMETHING_TO(test); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/general_fallback/make_drink.cc�������������0000664�0000000�0000000�00000001127�13746324601�0030146�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "make_drink.h" using namespace Test; int main( int , char ** ) { make_drink( TypeOfDrink::COFFEE, 10.0, Flavour::ELDERFLOWER ); make_drink( TypeOfDrink::JUICE, Temperature::COLD, 1 ); } void test_right_edge_80() { make_drink( TypeOfDrink::COFFEE, 10, Flavour::ORANGE_AND_PINEAPPLE); } void test_left_edge() { make_drink( TypeOfDrink::JUICE, Temperature ::WARM, 10 ); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/general_fallback/make_drink.h��������������0000664�0000000�0000000�00000001032�13746324601�0030003�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#pragma once namespace Test { struct Drink {}; enum class TypeOfDrink { COFFEE, TEA, JUICE, }; enum class Temperature { HOT, COLD, WARM }; enum class Flavour { ELDERFLOWER, RED, ORANGE_AND_PINEAPPLE }; Drink& make_drink( TypeOfDrink type, Temperature temp, int sugargs ); Drink& make_drink( TypeOfDrink type, double fizziness, Flavour Flavour ); void simple_func( int d, float c, char *S ); void simple_func( int f, char c ); void simple_func( float f, char c ); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/goto.cc������������������������������������0000664�0000000�0000000�00000000764�13746324601�0023564�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// This file is used in RunCompleterCommand_GoTo_all_Clang_test namespace Local { int x; char in_line() { return 'y'; }; char out_of_line(); } char Local::out_of_line() { return 'x'; } int test(); int test() { int ix = Local::x; char cy = Local::in_line(); char cx = Local::out_of_line(); return 0; } void unicode() { /* †est ê */ struct Unicøde { int u; }; struct Another_Unicøde; Unicøde *ç; ç->u; Another_Unicøde *u; u; } ������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/location_extent.cc�������������������������0000664�0000000�0000000�00000000124�13746324601�0026001�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������class Test { Test()// ignore comment }; multiline_\ identifier Test::Test() { } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/max_diagnostics.cc�������������������������0000664�0000000�0000000�00000000071�13746324601�0025757�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������int main() { int test; int test; int test; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/multiple_missing_includes.cc���������������0000664�0000000�0000000�00000000103�13746324601�0030051�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "first_missing_include" #include "second_missing_include" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/objc/��������������������������������������0000775�0000000�0000000�00000000000�13746324601�0023213�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/objc/FixIt_Clang_objc.m��������������������0000664�0000000�0000000�00000000372�13746324601�0026517�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������@protocol X; // _FixIt_Check_objc, _FixIt_Check_objc_NoFixIt void foo() { <X> *P; // expected-warning{{protocol has no object type specified; defaults to qualified 'id'}} char *x = nullptr; // no fix-it for this error (nullptr undefinied) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/objc/compile_flags.txt���������������������0000664�0000000�0000000�00000000000�13746324601�0026546�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/template.cc��������������������������������0000664�0000000�0000000�00000000100�13746324601�0024407�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������template < typename T > void Foo( T& t ) { t.do_a_thing(); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/test-include/������������������������������0000775�0000000�0000000�00000000000�13746324601�0024676�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/test-include/a.hpp�������������������������0000664�0000000�0000000�00000000000�13746324601�0025615�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/test-include/compile_flags.txt�������������0000664�0000000�0000000�00000000105�13746324601�0030237�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������-xc++ -std=c++11 -iquotequote/ -isystemsystem/ -nostdinc -nostdinc++ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/test-include/dir with spaces/��������������0000775�0000000�0000000�00000000000�13746324601�0027647�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/test-include/dir with spaces/d.hpp���������0000664�0000000�0000000�00000000000�13746324601�0030571�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/test-include/main.cpp����������������������0000664�0000000�0000000�00000000421�13746324601�0026323�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "a.hpp" // ./a.hpp #include <a.hpp> // system/a.hpp #include "b.hpp" // quote/b.hpp #include <b.hpp> // error #include "c.hpp" // system/c.hpp #include <c.hpp> // system/c.hpp // not an #include line #include "dir with spaces/d.hpp" #include <system/ #include :" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/test-include/quote/������������������������0000775�0000000�0000000�00000000000�13746324601�0026033�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/test-include/quote/b.hpp�������������������0000664�0000000�0000000�00000000000�13746324601�0026753�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/test-include/system/�����������������������0000775�0000000�0000000�00000000000�13746324601�0026222�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/test-include/system/a.hpp������������������0000664�0000000�0000000�00000000000�13746324601�0027141�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/test-include/system/c.hpp������������������0000664�0000000�0000000�00000000000�13746324601�0027143�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/testdata/unicode.cc���������������������������������0000664�0000000�0000000�00000000515�13746324601�0024234�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ int main( int argc, char ** argv ) { int id_with_å_unicode_chars = 10; struct MyStruct { int member_with_å_unicøde; } myå; myå.w } int another_main() { struct MyStruct { /** * This method has unicøde in it */ int member_with_å_unicøde; } myå; int a == myå.member_with_å_unicøde; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/clangd/utilities_test.py�����������������������������������0000664�0000000�0000000�00000023446�13746324601�0024122�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2011-2012 Google Inc. # 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 <http://www.gnu.org/licenses/>. """This test is for utilities used in clangd.""" from unittest.mock import patch from hamcrest import assert_that, equal_to from ycmd import handlers from ycmd.completers.cpp import clangd_completer from ycmd.completers.language_server.language_server_completer import ( LanguageServerConnectionTimeout ) from ycmd.tests.clangd import IsolatedYcmd, PathToTestFile from ycmd.tests.test_utils import BuildRequest from ycmd.user_options_store import DefaultOptions def ClangdCompleter_GetClangdCommand_NoCustomBinary_test(): user_options = DefaultOptions() # Supported binary in third_party. THIRD_PARTY = '/third_party/clangd' clangd_completer.CLANGD_COMMAND = clangd_completer.NOT_CACHED with patch( 'ycmd.completers.cpp.clangd_completer.GetThirdPartyClangd', return_value = THIRD_PARTY ): assert_that( clangd_completer.GetClangdCommand( user_options )[ 0 ], equal_to( THIRD_PARTY ) ) # With args clangd_completer.CLANGD_COMMAND = clangd_completer.NOT_CACHED CLANGD_ARGS = [ "1", "2", "3" ] user_options[ 'clangd_args' ] = CLANGD_ARGS assert_that( clangd_completer.GetClangdCommand( user_options )[ 1:4 ], equal_to( CLANGD_ARGS ) ) # No supported binary in third_party. clangd_completer.CLANGD_COMMAND = clangd_completer.NOT_CACHED with patch( 'ycmd.completers.cpp.clangd_completer.GetThirdPartyClangd', return_value = None ): assert_that( clangd_completer.GetClangdCommand( user_options ), equal_to( None ) ) clangd_completer.CLANGD_COMMAND = clangd_completer.NOT_CACHED @patch( 'ycmd.completers.cpp.clangd_completer.FindExecutable', lambda exe: exe ) def ClangdCompleter_GetClangdCommand_CustomBinary_test(): CLANGD_PATH = '/test/clangd' user_options = DefaultOptions() user_options[ 'clangd_binary_path' ] = CLANGD_PATH # Supported version. with patch( 'ycmd.completers.cpp.clangd_completer.CheckClangdVersion', return_value = True ): clangd_completer.CLANGD_COMMAND = clangd_completer.NOT_CACHED assert_that( clangd_completer.GetClangdCommand( user_options )[ 0 ], equal_to( CLANGD_PATH ) ) # No Clangd binary in the given path. with patch( 'ycmd.completers.cpp.clangd_completer.FindExecutable', return_value = None ): clangd_completer.CLANGD_COMMAND = clangd_completer.NOT_CACHED assert_that( clangd_completer.GetClangdCommand( user_options ), equal_to( None ) ) # Unsupported version. with patch( 'ycmd.completers.cpp.clangd_completer.CheckClangdVersion', return_value = False ): # Never fall back to the third-party Clangd. clangd_completer.CLANGD_COMMAND = clangd_completer.NOT_CACHED assert_that( clangd_completer.GetClangdCommand( user_options ), equal_to( None ) ) clangd_completer.CLANGD_COMMAND = clangd_completer.NOT_CACHED @patch( 'ycmd.completers.cpp.clangd_completer.GetVersion', side_effect = [ None, ( 5, 0, 0 ), clangd_completer.MIN_SUPPORTED_VERSION, ( 10, 0, 0 ), ( 10, 10, 10 ), ( 100, 100, 100 ) ] ) def ClangdCompleter_CheckClangdVersion_test( *args ): assert_that( clangd_completer.CheckClangdVersion( 'clangd' ), equal_to( True ) ) assert_that( clangd_completer.CheckClangdVersion( 'clangd' ), equal_to( False ) ) assert_that( clangd_completer.CheckClangdVersion( 'clangd' ), equal_to( True ) ) assert_that( clangd_completer.CheckClangdVersion( 'clangd' ), equal_to( True ) ) assert_that( clangd_completer.CheckClangdVersion( 'clangd' ), equal_to( True ) ) assert_that( clangd_completer.CheckClangdVersion( 'clangd' ), equal_to( True ) ) def ClangdCompleter_ShouldEnableClangdCompleter_test(): user_options = DefaultOptions() # Clangd not in third_party (or an old version). with patch( 'ycmd.completers.cpp.clangd_completer.GetThirdPartyClangd', return_value = None ): # Default. clangd_completer.CLANGD_COMMAND = clangd_completer.NOT_CACHED assert_that( clangd_completer.ShouldEnableClangdCompleter( user_options ), equal_to( False ) ) # Found supported binary. with patch( 'ycmd.completers.cpp.clangd_completer.GetClangdCommand', return_value = [ 'clangd' ] ): assert_that( clangd_completer.ShouldEnableClangdCompleter( user_options ), equal_to( True ) ) # No supported binary found. with patch( 'ycmd.completers.cpp.clangd_completer.GetClangdCommand', return_value = None ): assert_that( clangd_completer.ShouldEnableClangdCompleter( user_options ), equal_to( False ) ) # Clangd is disabled. user_options[ 'use_clangd' ] = 0 assert_that( clangd_completer.ShouldEnableClangdCompleter( user_options ), equal_to( False ) ) user_options = DefaultOptions() # Clangd in third_party with a supported version. with patch( 'ycmd.completers.cpp.clangd_completer.GetThirdPartyClangd', return_value = 'third_party_clangd' ): # Default. clangd_completer.CLANGD_COMMAND = clangd_completer.NOT_CACHED assert_that( clangd_completer.ShouldEnableClangdCompleter( user_options ), equal_to( True ) ) # Enabled. user_options[ 'use_clangd' ] = 1 # Found supported binary. with patch( 'ycmd.completers.cpp.clangd_completer.GetClangdCommand', return_value = [ 'clangd' ] ): assert_that( clangd_completer.ShouldEnableClangdCompleter( user_options ), equal_to( True ) ) # No supported binary found. with patch( 'ycmd.completers.cpp.clangd_completer.GetClangdCommand', return_value = None ): assert_that( clangd_completer.ShouldEnableClangdCompleter( user_options ), equal_to( False ) ) # Disabled. user_options[ 'use_clangd' ] = 0 assert_that( clangd_completer.ShouldEnableClangdCompleter( user_options ), equal_to( False ) ) class MockPopen: stdin = None stdout = None pid = 0 def communicate( self ): return ( bytes(), None ) @patch( 'subprocess.Popen', return_value = MockPopen() ) def ClangdCompleter_GetVersion_test( mock_popen ): assert_that( clangd_completer.GetVersion( '' ), equal_to( None ) ) mock_popen.assert_called() def ClangdCompleter_ParseClangdVersion_test(): cases = [ ( 'clangd version 10.0.0 (https://github.com/llvm/llvm-project.git ' '45be5e477e9216363191a8ac9123bea4585cf14f)', ( 10, 0, 0 ) ), ( 'clangd version 8.0.0-3 (tags/RELEASE_800/final)', ( 8, 0, 0 ) ), ( 'LLVM (http://llvm.org/):\nLLVM version 6.0.0\n' 'Optimized build.\nDefault target: x86_64-unknown-linux-gnu\n' 'Host CPU: haswell', ( 6, 0, 0 ) ), ] for version_str, expected in cases: assert_that( clangd_completer.ParseClangdVersion( version_str ), equal_to( expected ) ) @IsolatedYcmd() def ClangdCompleter_ShutdownFail_test( app ): completer = handlers._server_state.GetFiletypeCompleter( [ 'cpp' ] ) with patch.object( completer, 'ShutdownServer', side_effect = Exception ) as shutdown_server: completer._server_handle = MockPopen() with patch.object( completer, 'ServerIsHealthy', return_value = True ): completer.Shutdown() shutdown_server.assert_called() def ClangdCompleter_GetThirdParty_test(): with patch( 'ycmd.completers.cpp.clangd_completer.GetExecutable', return_value = None ): assert_that( clangd_completer.GetThirdPartyClangd(), equal_to( None ) ) with patch( 'ycmd.completers.cpp.clangd_completer.GetExecutable', return_value = '/third_party/clangd' ): with patch( 'ycmd.completers.cpp.clangd_completer.CheckClangdVersion', return_value = True ): assert_that( clangd_completer.GetThirdPartyClangd(), equal_to( '/third_party/clangd' ) ) with patch( 'ycmd.completers.cpp.clangd_completer.CheckClangdVersion', return_value = False ): assert_that( clangd_completer.GetThirdPartyClangd(), equal_to( None ) ) @IsolatedYcmd() def ClangdCompleter_StartServer_Fails_test( app ): with patch( 'ycmd.completers.language_server.language_server_completer.' 'LanguageServerConnection.AwaitServerConnection', side_effect = LanguageServerConnectionTimeout ): with patch( 'ycmd.completers.cpp.clangd_completer.ClangdCompleter.' 'ShutdownServer' ) as shutdown: resp = app.post_json( '/event_notification', BuildRequest( event_name = 'FileReadyToParse', filetype = 'cpp', filepath = PathToTestFile( 'foo.cc' ), contents = "" ) ) assert_that( resp.status_code, equal_to( 200 ) ) shutdown.assert_called() def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/client_test.py���������������������������������������������0000664�0000000�0000000�00000021111�13746324601�0022120�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2016-2020 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 <http://www.gnu.org/licenses/>. from base64 import b64decode, b64encode from hamcrest import assert_that, empty, equal_to, is_in from hmac import compare_digest from tempfile import NamedTemporaryFile import functools import json import os import psutil import requests import subprocess import sys import time from urllib.parse import urljoin, urlparse from ycmd.hmac_utils import CreateHmac, CreateRequestHmac from ycmd.tests import PathToTestFile from ycmd.tests.test_utils import BuildRequest from ycmd.user_options_store import DefaultOptions from ycmd.utils import ( CloseStandardStreams, CreateLogfile, GetUnusedLocalhostPort, ReadFile, RemoveIfExists, SafePopen, ToBytes, ToUnicode ) HEADERS = { 'content-type': 'application/json' } HMAC_HEADER = 'x-ycm-hmac' HMAC_SECRET_LENGTH = 16 DIR_OF_THIS_SCRIPT = os.path.dirname( os.path.abspath( __file__ ) ) PATH_TO_YCMD = os.path.join( DIR_OF_THIS_SCRIPT, '..' ) LOGFILE_FORMAT = 'server_{port}_{std}_' class Client_test: def setup_method( self ): self._location = None self._port = None self._servers = [] self._logfiles = [] self._options_dict = DefaultOptions() self._popen_handle = None self._hmac_secret = os.urandom( HMAC_SECRET_LENGTH ) self._options_dict[ 'hmac_secret' ] = ToUnicode( b64encode( self._hmac_secret ) ) def teardown_method( self ): for server in self._servers: if server.is_running(): server.terminate() CloseStandardStreams( self._popen_handle ) for logfile in self._logfiles: RemoveIfExists( logfile ) def Start( self, idle_suicide_seconds = 60, check_interval_seconds = 60 * 10 ): # The temp options file is deleted by ycmd during startup. with NamedTemporaryFile( mode = 'w+', delete = False ) as options_file: json.dump( self._options_dict, options_file ) self._port = GetUnusedLocalhostPort() self._location = 'http://127.0.0.1:' + str( self._port ) # Define environment variable to enable subprocesses coverage. See: # http://coverage.readthedocs.org/en/coverage-4.0.3/subprocess.html env = os.environ.copy() env[ 'COVERAGE_PROCESS_START' ] = '.coveragerc' ycmd_args = [ sys.executable, PATH_TO_YCMD, f'--port={ self._port }', f'--options_file={ options_file.name }', '--log=debug', f'--idle_suicide_seconds={ idle_suicide_seconds }', f'--check_interval_seconds={ check_interval_seconds }', ] stdout = CreateLogfile( LOGFILE_FORMAT.format( port = self._port, std = 'stdout' ) ) stderr = CreateLogfile( LOGFILE_FORMAT.format( port = self._port, std = 'stderr' ) ) self._logfiles.extend( [ stdout, stderr ] ) ycmd_args.append( f'--stdout={ stdout }' ) ycmd_args.append( f'--stderr={ stderr }' ) self._popen_handle = SafePopen( ycmd_args, stdin_windows = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, env = env ) self._servers.append( psutil.Process( self._popen_handle.pid ) ) self._WaitUntilReady() extra_conf = PathToTestFile( 'client', '.ycm_extra_conf.py' ) self.PostRequest( 'load_extra_conf_file', { 'filepath': extra_conf } ) def _IsReady( self, filetype = None ): params = { 'subserver': filetype } if filetype else None response = self.GetRequest( 'ready', params ) response.raise_for_status() return response.json() def _WaitUntilReady( self, filetype = None, timeout = 60 ): expiration = time.time() + timeout while True: try: if time.time() > expiration: server = ( f'the { filetype } subserver' if filetype else 'ycmd' ) raise RuntimeError( f'Waited for { server } to be ready ' f'for { timeout } seconds, aborting.' ) if self._IsReady( filetype ): return except requests.exceptions.ConnectionError: pass finally: time.sleep( 0.1 ) def StartSubserverForFiletype( self, filetype ): filepath = PathToTestFile( 'client', 'some_file' ) # Calling the BufferVisit event before the FileReadyToParse one is needed # for the TypeScript completer. self.PostRequest( 'event_notification', BuildRequest( filepath = filepath, filetype = filetype, event_name = 'BufferVisit' ) ) self.PostRequest( 'event_notification', BuildRequest( filepath = filepath, filetype = filetype, event_name = 'FileReadyToParse' ) ) self._WaitUntilReady( filetype ) response = self.PostRequest( 'debug_info', BuildRequest( filepath = filepath, filetype = filetype ) ).json() for server in response[ 'completer' ][ 'servers' ]: pid = server[ 'pid' ] if pid: self._servers.append( psutil.Process( pid ) ) self._logfiles.extend( server[ 'logfiles' ] ) def AssertServersAreRunning( self ): for server in self._servers: assert_that( server.is_running(), equal_to( True ) ) def AssertServersShutDown( self, timeout = 5 ): _, alive_procs = psutil.wait_procs( self._servers, timeout = timeout ) assert_that( alive_procs, empty() ) def AssertLogfilesAreRemoved( self ): existing_logfiles = [] for logfile in self._logfiles: if os.path.isfile( logfile ): existing_logfiles.append( logfile ) assert_that( existing_logfiles, empty() ) def GetRequest( self, handler, params = None ): return self._Request( 'GET', handler, params = params ) def PostRequest( self, handler, data = None ): return self._Request( 'POST', handler, data = data ) def _ToUtf8Json( self, data ): return ToBytes( json.dumps( data ) if data else None ) def _Request( self, method, handler, data = None, params = None ): request_uri = self._BuildUri( handler ) data = self._ToUtf8Json( data ) headers = self._ExtraHeaders( method, request_uri, data ) response = requests.request( method, request_uri, headers = headers, data = data, params = params ) return response def _BuildUri( self, handler ): return ToBytes( urljoin( self._location, handler ) ) def _ExtraHeaders( self, method, request_uri, request_body = None ): if not request_body: request_body = bytes( b'' ) headers = dict( HEADERS ) headers[ HMAC_HEADER ] = b64encode( CreateRequestHmac( ToBytes( method ), ToBytes( urlparse( request_uri ).path ), request_body, self._hmac_secret ) ) return headers def AssertResponse( self, response ): assert_that( response.status_code, equal_to( requests.codes.ok ) ) assert_that( HMAC_HEADER, is_in( response.headers ) ) assert_that( self._ContentHmacValid( response ), equal_to( True ) ) def _ContentHmacValid( self, response ): our_hmac = CreateHmac( response.content, self._hmac_secret ) their_hmac = ToBytes( b64decode( response.headers[ HMAC_HEADER ] ) ) return compare_digest( our_hmac, their_hmac ) @staticmethod def CaptureLogfiles( test ): @functools.wraps( test ) def Wrapper( self, *args ): try: test( self, *args ) finally: for logfile in self._logfiles: if os.path.isfile( logfile ): sys.stdout.write( f'Logfile { logfile }:\n\n' ) sys.stdout.write( ReadFile( logfile ) ) sys.stdout.write( '\n' ) return Wrapper def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/completer_test.py������������������������������������������0000664�0000000�0000000�00000006065�13746324601�0022647�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2016-2020 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 <http://www.gnu.org/licenses/>. from ycmd.tests.test_utils import DummyCompleter from ycmd.user_options_store import DefaultOptions from unittest.mock import patch from hamcrest import assert_that, contains_exactly, equal_to def _FilterAndSortCandidates_Match( candidates, query, expected_matches ): completer = DummyCompleter( DefaultOptions() ) matches = completer.FilterAndSortCandidates( candidates, query ) assert_that( expected_matches, equal_to( matches ) ) def FilterAndSortCandidates_OmniCompleter_List_test(): _FilterAndSortCandidates_Match( [ 'password' ], 'p', [ 'password' ] ) _FilterAndSortCandidates_Match( [ 'words' ], 'w', [ 'words' ] ) def FilterAndSortCandidates_OmniCompleter_Dictionary_test(): _FilterAndSortCandidates_Match( { 'words': [ 'password' ] }, 'p', [ 'password' ] ) _FilterAndSortCandidates_Match( { 'words': [ { 'word': 'password' } ] }, 'p', [ { 'word': 'password' } ] ) def FilterAndSortCandidates_ServerCompleter_test(): _FilterAndSortCandidates_Match( [ { 'insertion_text': 'password' } ], 'p', [ { 'insertion_text': 'password' } ] ) def FilterAndSortCandidates_SortOnEmptyQuery_test(): _FilterAndSortCandidates_Match( [ 'foo', 'bar' ], '', [ 'bar', 'foo' ] ) def FilterAndSortCandidates_IgnoreEmptyCandidate_test(): _FilterAndSortCandidates_Match( [ '' ], '', [] ) def FilterAndSortCandidates_Unicode_test(): _FilterAndSortCandidates_Match( [ { 'insertion_text': 'ø' } ], 'ø', [ { 'insertion_text': 'ø' } ] ) @patch( 'ycmd.tests.test_utils.DummyCompleter.GetSubcommandsMap', return_value = { 'Foo': '', 'StopServer': '' } ) def DefinedSubcommands_RemoveStopServerSubcommand_test( subcommands_map ): completer = DummyCompleter( DefaultOptions() ) assert_that( completer.DefinedSubcommands(), contains_exactly( 'Foo' ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/completer_utils_test.py������������������������������������0000664�0000000�0000000�00000023464�13746324601�0024071�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from collections import defaultdict from hamcrest import assert_that, equal_to, none from ycmd.completers import completer_utils as cu from ycmd.utils import re def _ExtractPatternsFromFiletypeTriggerDict( triggerDict ): """Returns a copy of the dictionary with the _sre.SRE_Pattern instances in each set value replaced with the pattern strings. Needed for equality test of two filetype trigger dictionaries.""" copy = triggerDict.copy() for key, values in triggerDict.items(): copy[ key ] = { sre_pattern.pattern for sre_pattern in values } return copy def FiletypeTriggerDictFromSpec_Works_test(): assert_that( defaultdict( set, { 'foo': { cu._PrepareTrigger( 'zoo' ).pattern, cu._PrepareTrigger( 'bar' ).pattern }, 'goo': { cu._PrepareTrigger( 'moo' ).pattern }, 'moo': { cu._PrepareTrigger( 'moo' ).pattern }, 'qux': { cu._PrepareTrigger( 'q' ).pattern } } ), equal_to( _ExtractPatternsFromFiletypeTriggerDict( cu._FiletypeTriggerDictFromSpec( { 'foo': [ 'zoo', 'bar' ], 'goo,moo': [ 'moo' ], 'qux': [ 'q' ] } ) ) ) ) def FiletypeDictUnion_Works_test(): assert_that( defaultdict( set, { 'foo': { 'zoo', 'bar', 'maa' }, 'goo': { 'moo' }, 'bla': { 'boo' }, 'qux': { 'q' } } ), equal_to( cu._FiletypeDictUnion( defaultdict( set, { 'foo': { 'zoo', 'bar' }, 'goo': { 'moo' }, 'qux': { 'q' } } ), defaultdict( set, { 'foo': { 'maa' }, 'bla': { 'boo' }, 'qux': { 'q' } } ) ) ) ) def PrepareTrigger_UnicodeTrigger_Test(): regex = cu._PrepareTrigger( 'æ' ) assert_that( regex.pattern, equal_to( re.escape( u'æ' ) ) ) def MatchesSemanticTrigger_Basic_test(): triggers = [ cu._PrepareTrigger( '.' ) ] assert_that( not cu._MatchesSemanticTrigger( 'foo.bar', 7, 7, triggers ) ) assert_that( not cu._MatchesSemanticTrigger( 'foo.bar', 6, 7, triggers ) ) assert_that( not cu._MatchesSemanticTrigger( 'foo.bar', 5, 7, triggers ) ) assert_that( cu._MatchesSemanticTrigger( 'foo.bar', 4, 7, triggers ) ) assert_that( cu._MatchesSemanticTrigger( 'foo.bar', 3, 7, triggers ) ) assert_that( cu._MatchesSemanticTrigger( 'foo.bar', 2, 7, triggers ) ) assert_that( cu._MatchesSemanticTrigger( 'foo.bar', 1, 7, triggers ) ) assert_that( cu._MatchesSemanticTrigger( 'foo.bar', 0, 7, triggers ) ) assert_that( not cu._MatchesSemanticTrigger( 'foo.bar', 3, 3, triggers ) ) assert_that( not cu._MatchesSemanticTrigger( 'foo.bar', 2, 3, triggers ) ) assert_that( not cu._MatchesSemanticTrigger( 'foo.bar', 1, 3, triggers ) ) assert_that( not cu._MatchesSemanticTrigger( 'foo.bar', 0, 3, triggers ) ) def MatchesSemanticTrigger_JustTrigger_test(): triggers = [ cu._PrepareTrigger( '.' ) ] assert_that( not cu._MatchesSemanticTrigger( '.', 2, 2, triggers ) ) assert_that( cu._MatchesSemanticTrigger( '.', 1, 1, triggers ) ) assert_that( not cu._MatchesSemanticTrigger( '.', 0, 0, triggers ) ) def MatchesSemanticTrigger_TriggerBetweenWords_test(): triggers = [ cu._PrepareTrigger( '.' ) ] assert_that( not cu._MatchesSemanticTrigger( 'foo . bar', 6, 9, triggers ) ) assert_that( cu._MatchesSemanticTrigger( 'foo . bar', 5, 9, triggers ) ) assert_that( cu._MatchesSemanticTrigger( 'foo . bar', 4, 9, triggers ) ) def MatchesSemanticTrigger_BadInput_test(): triggers = [ cu._PrepareTrigger( '.' ) ] assert_that( not cu._MatchesSemanticTrigger( 'foo.bar', 10, 7, triggers ) ) assert_that( not cu._MatchesSemanticTrigger( 'foo.bar', -1, 7, triggers ) ) assert_that( not cu._MatchesSemanticTrigger( 'foo.bar', 4, -1, triggers ) ) assert_that( not cu._MatchesSemanticTrigger( '', -1, 0, triggers ) ) assert_that( not cu._MatchesSemanticTrigger( '', 0, 0, triggers ) ) assert_that( not cu._MatchesSemanticTrigger( '', 1, 0, triggers ) ) assert_that( not cu._MatchesSemanticTrigger( 'foo.bar', 4, 7, [] ) ) def MatchesSemanticTrigger_TriggerIsWrong_test(): triggers = [ cu._PrepareTrigger( ':' ) ] assert_that( not cu._MatchesSemanticTrigger( 'foo.bar', 4, 7, triggers ) ) def MatchesSemanticTrigger_LongerTrigger_test(): triggers = [ cu._PrepareTrigger( '::' ) ] assert_that( not cu._MatchesSemanticTrigger( 'foo::bar', 6, 8, triggers ) ) assert_that( cu._MatchesSemanticTrigger( 'foo::bar', 5, 8, triggers ) ) assert_that( cu._MatchesSemanticTrigger( 'foo::bar', 4, 8, triggers ) ) assert_that( cu._MatchesSemanticTrigger( 'foo::bar', 3, 8, triggers ) ) assert_that( not cu._MatchesSemanticTrigger( 'foo::bar', 4, 4, triggers ) ) assert_that( not cu._MatchesSemanticTrigger( 'foo::bar', 3, 4, triggers ) ) def MatchesSemanticTrigger_OneTriggerMatches_test(): triggers = [ cu._PrepareTrigger( '.' ), cu._PrepareTrigger( ';' ), cu._PrepareTrigger( '::' ) ] assert_that( cu._MatchesSemanticTrigger( 'foo::bar', 5, 8, triggers ) ) def MatchesSemanticTrigger_RegexTrigger_test(): triggers = [ cu._PrepareTrigger( r're!\w+\.' ) ] assert_that( cu._MatchesSemanticTrigger( 'foo.bar', 4, 8, triggers ) ) assert_that( not cu._MatchesSemanticTrigger( 'foo . bar', 5, 8, triggers ) ) def MatchingSemanticTrigger_Basic_test(): triggers = [ cu._PrepareTrigger( '.' ), cu._PrepareTrigger( ';' ), cu._PrepareTrigger( '::' ) ] assert_that( cu._MatchingSemanticTrigger( 'foo->bar', 5, 9, triggers ), none() ) assert_that( cu._MatchingSemanticTrigger( 'foo::bar', 5, 9, triggers ).pattern, equal_to( re.escape( '::' ) ) ) def PreparedTriggers_Basic_test(): triggers = cu.PreparedTriggers() assert_that( triggers.MatchesForFiletype( 'foo.bar', 4, 8, 'c' ) ) assert_that( triggers.MatchingTriggerForFiletype( 'foo.bar', 4, 8, 'c' ).pattern, equal_to( re.escape( '.' ) ) ) assert_that( triggers.MatchesForFiletype( 'foo->bar', 5, 9, 'cpp' ) ) assert_that( triggers.MatchingTriggerForFiletype( 'foo->bar', 5, 9, 'cpp' ).pattern, equal_to( re.escape( '->' ) ) ) def PreparedTriggers_OnlySomeFiletypesSelected_test(): triggers = cu.PreparedTriggers( filetype_set = set( 'c' ) ) assert_that( triggers.MatchesForFiletype( 'foo.bar', 4, 7, 'c' ) ) assert_that( triggers.MatchingTriggerForFiletype( 'foo.bar', 4, 7, 'c' ).pattern, equal_to( re.escape( '.' ) ) ) assert_that( not triggers.MatchesForFiletype( 'foo->bar', 5, 8, 'cpp' ) ) assert_that( triggers.MatchingTriggerForFiletype( 'foo->bar', 5, 8, 'cpp' ), none() ) def PreparedTriggers_UserTriggers_test(): triggers = cu.PreparedTriggers( user_trigger_map = { 'c': [ '->' ] } ) assert_that( triggers.MatchesForFiletype( 'foo->bar', 5, 8, 'c' ) ) assert_that( triggers.MatchingTriggerForFiletype( 'foo->bar', 5, 8, 'c' ).pattern, equal_to( re.escape( '->' ) ) ) def PreparedTriggers_ObjectiveC_test(): triggers = cu.PreparedTriggers() # Bracketed calls assert_that( triggers.MatchesForFiletype( '[foo ', 5, 6, 'objc' ) ) assert_that( not triggers.MatchesForFiletype( '[foo', 4, 5, 'objc' ) ) assert_that( not triggers.MatchesForFiletype( '[3foo ', 6, 6, 'objc' ) ) assert_that( triggers.MatchesForFiletype( '[f3oo ', 6, 6, 'objc' ) ) assert_that( triggers.MatchesForFiletype( '[[foo ', 6, 6, 'objc' ) ) # Bracketless calls assert_that( not triggers.MatchesForFiletype( '3foo ', 5, 5, 'objc' ) ) assert_that( triggers.MatchesForFiletype( 'foo3 ', 5, 5, 'objc' ) ) assert_that( triggers.MatchesForFiletype( 'foo ', 4, 4, 'objc' ) ) # Method composition assert_that( triggers.MatchesForFiletype( '[NSString stringWithFormat:@"Test %@", stuff] ', 46, 46, 'objc' ) ) assert_that( triggers.MatchesForFiletype( ' [NSString stringWithFormat:@"Test"] ', 39, 39, 'objc' ) ) assert_that( triggers.MatchesForFiletype( ' [[NSString stringWithFormat:@"Test"] stringByAppendingString:%@] ', 68, 68, 'objc' ) ) assert_that( not triggers.MatchesForFiletype( '// foo ', 8, 8, 'objc' ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/conftest.py������������������������������������������������0000664�0000000�0000000�00000005564�13746324601�0021446�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2016-2020 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 <http://www.gnu.org/licenses/>. import pytest from ycmd.tests.test_utils import ( ClearCompletionsCache, IsolatedApp, SetUpApp ) shared_app = None @pytest.fixture( scope='module', autouse=True ) def set_up_shared_app(): global shared_app shared_app = SetUpApp() @pytest.fixture def app( request ): which = request.param[ 0 ] assert which == 'isolated' or which == 'shared' if which == 'isolated': with IsolatedApp( request.param[ 1 ] ) as app: yield app else: global shared_app ClearCompletionsCache() yield shared_app """Defines a decorator to be attached to tests of this package. This decorator passes the shared ycmd application as a parameter.""" SharedYcmd = pytest.mark.parametrize( # Name of the fixture/function argument 'app', # Fixture parameters, passed to app() as request.param [ ( 'shared', ) ], # Non-empty ids makes fixture parameters visible in pytest verbose output ids = [ '' ], # Execute the fixture, instead of passing parameters directly to the # function argument indirect = True ) def IsolatedYcmd( custom_options = {} ): """Defines a decorator to be attached to tests of this package. This decorator passes a unique ycmd application as a parameter. It should be used on tests that change the server state in a irreversible way (ex: a semantic subserver is stopped or restarted) or expect a clean state (ex: no semantic subserver started, no .ycm_extra_conf.py loaded, etc). Use the optional parameter |custom_options| to give additional options and/or override the default ones. Example usage: from ycmd.tests.python import IsolatedYcmd @IsolatedYcmd( { 'python_binary_path': '/some/path' } ) def CustomPythonBinaryPath_test( app ): ... """ return pytest.mark.parametrize( # Name of the fixture/function argument 'app', # Fixture parameters, passed to app() as request.param [ ( 'isolated', custom_options ) ], # Non-empty ids makes fixture parameters visible in pytest verbose output ids = [ '' ], # Execute the fixture, instead of passing parameters directly to the # function argument indirect = True ) ��������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/��������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0017642�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/__init__.py���������������������������������������������0000664�0000000�0000000�00000001613�13746324601�0021754�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import os from ycmd.tests.cs.conftest import * # noqa def PathToTestFile( *args ): dir_of_current_script = os.path.dirname( os.path.abspath( __file__ ) ) return os.path.join( dir_of_current_script, 'testdata', *args ) ���������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/conftest.py���������������������������������������������0000664�0000000�0000000�00000013520�13746324601�0022042�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import os import pytest import sys import time from contextlib import contextmanager from ycmd.tests.test_utils import ( BuildRequest, ClearCompletionsCache, IgnoreExtraConfOutsideTestsFolder, IsolatedApp, WaitUntilCompleterServerReady, StopCompleterServer, SetUpApp ) shared_app = None # map of 'app' to filepaths shared_filepaths = {} shared_log_indexes = {} @pytest.fixture( scope='package', autouse=True ) def set_up_shared_app(): global shared_app, shared_filepaths shared_app = SetUpApp() yield for filepath in shared_filepaths.get( shared_app, [] ): StopCompleterServer( shared_app, 'cs', filepath ) @pytest.fixture def app( request ): which = request.param[ 0 ] assert which == 'isolated' or which == 'shared' if which == 'isolated': custom_options = request.param[ 1 ] with IsolatedApp( custom_options ) as app: yield app # Shutdown the isolated app for filepath in shared_filepaths.get( app, [] ): StopCompleterServer( app, 'cs', filepath ) else: global shared_app ClearCompletionsCache() with IgnoreExtraConfOutsideTestsFolder(): yield shared_app """Defines a decorator to be attached to tests of this package. This decorator passes the shared ycmd application as a parameter.""" SharedYcmd = pytest.mark.parametrize( # Name of the fixture/function argument 'app', # Fixture parameters, passed to app() as request.param [ ( 'shared', ) ], # Non-empty ids makes fixture parameters visible in pytest verbose output ids = [ '' ], # Execute the fixture, instead of passing parameters directly to the # function argument indirect = True ) def IsolatedYcmd( custom_options = {} ): """Defines a decorator to be attached to tests of this package. This decorator passes a unique ycmd application as a parameter. It should be used on tests that change the server state in a irreversible way (ex: a semantic subserver is stopped or restarted) or expect a clean state (ex: no semantic subserver started, no .ycm_extra_conf.py loaded, etc). Use the optional parameter |custom_options| to give additional options and/or override the default ones. Example usage: from ycmd.tests.python import IsolatedYcmd @IsolatedYcmd( { 'python_binary_path': '/some/path' } ) def CustomPythonBinaryPath_test( app ): ... """ return pytest.mark.parametrize( # Name of the fixture/function argument 'app', # Fixture parameters, passed to app() as request.param [ ( 'isolated', custom_options ) ], # Non-empty ids makes fixture parameters visible in pytest verbose output ids = [ '' ], # Execute the fixture, instead of passing parameters directly to the # function argument indirect = True ) def GetDebugInfo( app, filepath ): """ TODO: refactor here and in clangd test to common util """ request_data = BuildRequest( filetype = 'cs', filepath = filepath ) return app.post_json( '/debug_info', request_data ).json def ReadFile( filepath, fileposition ): with open( filepath, encoding = 'utf8' ) as f: if fileposition: f.seek( fileposition ) return f.read(), f.tell() def GetDiagnostics( app, filepath ): contents, _ = ReadFile( filepath, 0 ) event_data = BuildRequest( filepath = filepath, event_name = 'FileReadyToParse', filetype = 'cs', contents = contents ) return app.post_json( '/event_notification', event_data ).json @contextmanager def WrapOmniSharpServer( app, filepath ): global shared_filepaths global shared_log_indexes if filepath not in shared_filepaths.setdefault( app, [] ): # StartCompleterServer( app, 'cs', filepath ) GetDiagnostics( app, filepath ) shared_filepaths[ app ].append( filepath ) WaitUntilCsCompleterIsReady( app, filepath ) logfiles = [] response = GetDebugInfo( app, filepath ) for server in response[ 'completer' ][ 'servers' ]: logfiles.extend( server[ 'logfiles' ] ) try: yield finally: for logfile in logfiles: if os.path.isfile( logfile ): log_content, log_end_position = ReadFile( logfile, shared_log_indexes.get( logfile, 0 ) ) shared_log_indexes[ logfile ] = log_end_position sys.stdout.write( f'Logfile { logfile }:\n\n' ) sys.stdout.write( log_content ) sys.stdout.write( '\n' ) def WaitUntilCsCompleterIsReady( app, filepath ): WaitUntilCompleterServerReady( app, 'cs' ) # Omnisharp isn't ready when it says it is, so wait until Omnisharp returns # at least one diagnostic multiple times. success_count = 0 for reraise_error in [ False ] * 39 + [ True ]: try: if len( GetDiagnostics( app, filepath ) ) == 0: raise RuntimeError( "No diagnostic" ) success_count += 1 if success_count > 2: break except Exception: success_count = 0 if reraise_error: raise time.sleep( .5 ) else: raise RuntimeError( "Never was ready" ) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/debug_info_test.py��������������������������������������0000664�0000000�0000000�00000020434�13746324601�0023357�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2016-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_exactly, empty, equal_to, has_entries, has_entry, instance_of ) from unittest.mock import patch from ycmd.completers.cs.hook import GetCompleter from ycmd.tests.cs import PathToTestFile, SharedYcmd from ycmd.tests.test_utils import ( BuildRequest, WaitUntilCompleterServerReady ) from ycmd import user_options_store from ycmd.utils import ReadFile @SharedYcmd def DebugInfo_ServerIsRunning_test( app ): filepath = PathToTestFile( 'testy', 'Program.cs' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'cs', contents = contents, event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', event_data ) WaitUntilCompleterServerReady( app, 'cs' ) request_data = BuildRequest( filepath = filepath, filetype = 'cs' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'C#', 'servers': contains_exactly( has_entries( { 'name': 'OmniSharp', 'is_running': True, 'executable': instance_of( str ), 'pid': instance_of( int ), 'address': instance_of( str ), 'port': instance_of( int ), 'logfiles': contains_exactly( instance_of( str ), instance_of( str ) ), 'extras': contains_exactly( has_entries( { 'key': 'solution', 'value': instance_of( str ) } ) ) } ) ), 'items': empty() } ) ) ) @SharedYcmd def DebugInfo_ServerIsNotRunning_NoSolution_test( app ): request_data = BuildRequest( filetype = 'cs' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'C#', 'servers': contains_exactly( has_entries( { 'name': 'OmniSharp', 'is_running': False, 'executable': instance_of( str ), 'pid': None, 'address': None, 'port': None, 'logfiles': empty() } ) ), 'items': empty() } ) ) ) def SolutionSelectCheck( app, sourcefile, reference_solution, extra_conf_store = None ): # reusable test: verify that the correct solution (reference_solution) is # detected for a given source file (and optionally a given extra_conf) if extra_conf_store: app.post_json( '/load_extra_conf_file', { 'filepath': extra_conf_store } ) result = app.post_json( '/debug_info', BuildRequest( completer_target = 'filetype_default', filepath = sourcefile, filetype = 'cs' ) ).json assert_that( result, has_entry( 'completer', has_entries( { 'name': 'C#', 'servers': contains_exactly( has_entries( { 'extras': contains_exactly( has_entries( { 'key': 'solution', 'value': reference_solution } ) ) } ) ) } ) ) ) @SharedYcmd def DebugInfo_UsesSubfolderHint_test( app ): SolutionSelectCheck( app, PathToTestFile( 'testy-multiple-solutions', 'solution-named-like-folder', 'testy', 'Program.cs' ), PathToTestFile( 'testy-multiple-solutions', 'solution-named-like-folder', 'testy.sln' ) ) @SharedYcmd def DebugInfo_UsesSuperfolderHint_test( app ): SolutionSelectCheck( app, PathToTestFile( 'testy-multiple-solutions', 'solution-named-like-folder', 'not-testy', 'Program.cs' ), PathToTestFile( 'testy-multiple-solutions', 'solution-named-like-folder', 'solution-named-like-folder.sln' ) ) @SharedYcmd def DebugInfo_ExtraConfStoreAbsolute_test( app ): SolutionSelectCheck( app, PathToTestFile( 'testy-multiple-solutions', 'solution-not-named-like-folder', 'extra-conf-abs', 'testy', 'Program.cs' ), PathToTestFile( 'testy-multiple-solutions', 'solution-not-named-like-folder', 'testy2.sln' ), PathToTestFile( 'testy-multiple-solutions', 'solution-not-named-like-folder', 'extra-conf-abs', '.ycm_extra_conf.py' ) ) @SharedYcmd def DebugInfo_ExtraConfStoreRelative_test( app ): SolutionSelectCheck( app, PathToTestFile( 'testy-multiple-solutions', 'solution-not-named-like-folder', 'extra-conf-rel', 'testy', 'Program.cs' ), PathToTestFile( 'testy-multiple-solutions', 'solution-not-named-like-folder', 'extra-conf-rel', 'testy2.sln' ), PathToTestFile( 'testy-multiple-solutions', 'solution-not-named-like-folder', 'extra-conf-rel', '.ycm_extra_conf.py' ) ) @SharedYcmd def DebugInfo_ExtraConfStoreNonexisting_test( app ): SolutionSelectCheck( app, PathToTestFile( 'testy-multiple-solutions', 'solution-not-named-like-folder', 'extra-conf-bad', 'testy', 'Program.cs' ), PathToTestFile( 'testy-multiple-solutions', 'solution-not-named-like-folder', 'extra-conf-bad', 'testy2.sln' ), PathToTestFile( 'testy-multiple-solutions', 'solution-not-named-like-folder', 'extra-conf-bad', 'testy', '.ycm_extra_conf.py' ) ) def GetCompleter_RoslynFound_test(): assert_that( GetCompleter( user_options_store.GetAll() ) ) @patch( 'ycmd.completers.cs.cs_completer.PATH_TO_OMNISHARP_ROSLYN_BINARY', None ) def GetCompleter_RoslynNotFound_test( *args ): assert_that( not GetCompleter( user_options_store.GetAll() ) ) @patch( 'ycmd.completers.cs.cs_completer.FindExecutableWithFallback', wraps = lambda x, fb: x if x == 'roslyn' else fb ) @patch( 'os.path.isfile', return_value = True ) def GetCompleter_RoslynFromUserOption_test( *args ): user_options = user_options_store.GetAll().copy( roslyn_binary_path = 'roslyn' ) assert_that( GetCompleter( user_options )._roslyn_path, equal_to( 'roslyn' ) ) @patch( 'os.path.isfile', return_value = False ) def GetCompleter_CustomPathToServer_NotAFile_test( *args ): user_options = user_options_store.GetAll().copy( roslyn_binary_path = 'does-not-exist' ) assert_that( not GetCompleter( user_options ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/diagnostics_test.py�������������������������������������0000664�0000000�0000000�00000014165�13746324601�0023571�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_exactly, contains_string, equal_to, has_entries, has_entry, has_items ) from ycmd.tests.cs import ( IsolatedYcmd, PathToTestFile, SharedYcmd, WrapOmniSharpServer ) from ycmd.tests.test_utils import ( BuildRequest, LocationMatcher, RangeMatcher, WithRetry ) from ycmd.utils import ReadFile @WithRetry @SharedYcmd def Diagnostics_Basic_test( app ): filepath = PathToTestFile( 'testy', 'Program.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, event_name = 'FileReadyToParse', filetype = 'cs', contents = contents ) app.post_json( '/event_notification', event_data ) diag_data = BuildRequest( filepath = filepath, filetype = 'cs', contents = contents, line_num = 10, column_num = 2 ) results = app.post_json( '/detailed_diagnostic', diag_data ).json assert_that( results, has_entry( 'message', contains_string( "Identifier expected" ) ) ) @SharedYcmd def Diagnostics_ZeroBasedLineAndColumn_test( app ): filepath = PathToTestFile( 'testy', 'Program.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, event_name = 'FileReadyToParse', filetype = 'cs', contents = contents ) results = app.post_json( '/event_notification', event_data ).json assert_that( results, has_items( has_entries( { 'kind': equal_to( 'ERROR' ), 'text': contains_string( "Identifier expected" ), 'location': LocationMatcher( filepath, 10, 12 ), 'location_extent': RangeMatcher( filepath, ( 10, 12 ), ( 10, 12 ) ), } ) ) ) @WithRetry @SharedYcmd def Diagnostics_WithRange_test( app ): filepath = PathToTestFile( 'testy', 'DiagnosticRange.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, event_name = 'FileReadyToParse', filetype = 'cs', contents = contents ) results = app.post_json( '/event_notification', event_data ).json assert_that( results, contains_exactly( has_entries( { 'kind': equal_to( 'WARNING' ), 'text': contains_string( "The variable '\u4e5d' is assigned but its value is never used" ), 'location': LocationMatcher( filepath, 6, 13 ), 'location_extent': RangeMatcher( filepath, ( 6, 13 ), ( 6, 16 ) ) } ) ) ) @IsolatedYcmd() def Diagnostics_MultipleSolution_test( app ): filepaths = [ PathToTestFile( 'testy', 'Program.cs' ), PathToTestFile( 'testy-multiple-solutions', 'solution-named-like-folder', 'testy', 'Program.cs' ) ] for filepath in filepaths: with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, event_name = 'FileReadyToParse', filetype = 'cs', contents = contents ) results = app.post_json( '/event_notification', event_data ).json assert_that( results, has_items( has_entries( { 'kind': equal_to( 'ERROR' ), 'text': contains_string( "Identifier expected" ), 'location': LocationMatcher( filepath, 10, 12 ), 'location_extent': RangeMatcher( filepath, ( 10, 12 ), ( 10, 12 ) ) } ) ) ) @IsolatedYcmd( { 'max_diagnostics_to_display': 1 } ) def Diagnostics_MaximumDiagnosticsNumberExceeded_test( app ): filepath = PathToTestFile( 'testy', 'MaxDiagnostics.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, event_name = 'FileReadyToParse', filetype = 'cs', contents = contents ) results = app.post_json( '/event_notification', event_data ).json assert_that( results, contains_exactly( has_entries( { 'kind': equal_to( 'ERROR' ), 'text': contains_string( "The type 'MaxDiagnostics' already contains " "a definition for 'test'" ), 'location': LocationMatcher( filepath, 4, 16 ), 'location_extent': RangeMatcher( filepath, ( 4, 16 ), ( 4, 20 ) ) } ), has_entries( { 'kind': equal_to( 'ERROR' ), 'text': contains_string( 'Maximum number of diagnostics exceeded.' ), 'location': LocationMatcher( filepath, 1, 1 ), 'location_extent': RangeMatcher( filepath, ( 1, 1 ), ( 1, 1 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 1, 1 ), ( 1, 1 ) ) ) } ) ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/get_completions_test.py���������������������������������0000664�0000000�0000000�00000016603�13746324601�0024454�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2015-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, calling, empty, has_entries, has_items, raises ) from webtest import AppError from ycmd.tests.cs import PathToTestFile, SharedYcmd, WrapOmniSharpServer from ycmd.tests.test_utils import BuildRequest, CompletionEntryMatcher from ycmd.utils import ReadFile @SharedYcmd def GetCompletions_DefaultToIdentifier_test( app ): filepath = PathToTestFile( 'testy', 'Program.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) completion_data = BuildRequest( filepath = filepath, filetype = 'cs', contents = contents, line_num = 10, column_num = 7 ) response_data = app.post_json( '/completions', completion_data ).json print( 'Response: ', response_data ) assert_that( response_data, has_entries( { 'completion_start_column': 4, 'completions': has_items( CompletionEntryMatcher( 'Console', '[ID]' ), ), 'errors': empty(), } ) ) @SharedYcmd def GetCompletions_Basic_test( app ): filepath = PathToTestFile( 'testy', 'Program.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) completion_data = BuildRequest( filepath = filepath, filetype = 'cs', contents = contents, line_num = 10, column_num = 12 ) response_data = app.post_json( '/completions', completion_data ).json print( 'Response: ', response_data ) assert_that( response_data, has_entries( { 'completion_start_column': 12, 'completions': has_items( CompletionEntryMatcher( 'CursorLeft', 'CursorLeft', { 'kind': 'Property' } ), CompletionEntryMatcher( 'CursorSize', 'CursorSize', { 'kind': 'Property' } ), ), 'errors': empty(), } ) ) @SharedYcmd def GetCompletions_Unicode_test( app ): filepath = PathToTestFile( 'testy', 'Unicode.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) completion_data = BuildRequest( filepath = filepath, filetype = 'cs', contents = contents, line_num = 43, column_num = 26 ) response_data = app.post_json( '/completions', completion_data ).json assert_that( response_data, has_entries( { 'completion_start_column': 26, 'completions': has_items( CompletionEntryMatcher( 'DoATest' ), CompletionEntryMatcher( 'an_int' ), CompletionEntryMatcher( 'a_unicøde' ), CompletionEntryMatcher( 'øøø' ), ), 'errors': empty(), } ) ) @SharedYcmd def GetCompletions_MultipleSolution_test( app ): filepaths = [ PathToTestFile( 'testy', 'Program.cs' ), PathToTestFile( 'testy-multiple-solutions', 'solution-named-like-folder', 'testy', 'Program.cs' ) ] for filepath in filepaths: with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) completion_data = BuildRequest( filepath = filepath, filetype = 'cs', contents = contents, line_num = 10, column_num = 12 ) response_data = app.post_json( '/completions', completion_data ).json print( 'Response: ', response_data ) assert_that( response_data, has_entries( { 'completion_start_column': 12, 'completions': has_items( CompletionEntryMatcher( 'CursorLeft', 'CursorLeft', { 'kind': 'Property' } ), CompletionEntryMatcher( 'CursorSize', 'CursorSize', { 'kind': 'Property' } ), ), 'errors': empty(), } ) ) @SharedYcmd def GetCompletions_PathWithSpace_test( app ): filepath = PathToTestFile( 'неприличное слово', 'Program.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) completion_data = BuildRequest( filepath = filepath, filetype = 'cs', contents = contents, line_num = 9, column_num = 12 ) response_data = app.post_json( '/completions', completion_data ).json print( 'Response: ', response_data ) assert_that( response_data, has_entries( { 'completion_start_column': 12, 'completions': has_items( CompletionEntryMatcher( 'CursorLeft', 'CursorLeft', { 'kind': 'Property' } ), CompletionEntryMatcher( 'CursorSize', 'CursorSize', { 'kind': 'Property' } ), ), 'errors': empty(), } ) ) @SharedYcmd def GetCompletions_DoesntStartWithAmbiguousMultipleSolutions_test( app ): filepath = PathToTestFile( 'testy-multiple-solutions', 'solution-not-named-like-folder', 'testy', 'Program.cs' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'cs', contents = contents, event_name = 'FileReadyToParse' ) assert_that( calling( app.post_json ).with_args( '/event_notification', event_data ), raises( AppError, 'Autodetection of solution file failed' ), "The Omnisharp server started, despite us not being able to find a " "suitable solution file to feed it. Did you fiddle with the solution " "finding code in cs_completer.py? Hopefully you've enhanced it: you need " "to update this test then :)" ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �����������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/signature_help_test.py����������������������������������0000664�0000000�0000000�00000017671�13746324601�0024300�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_exactly, empty, has_entries, has_items ) from unittest.mock import patch from ycmd import handlers from ycmd.utils import ReadFile, LOGGER from ycmd.tests.cs import ( PathToTestFile, IsolatedYcmd, SharedYcmd, WrapOmniSharpServer ) from ycmd.tests.test_utils import ( BuildRequest, ParameterMatcher, SignatureMatcher, SignatureAvailableMatcher, CompletionEntryMatcher ) @SharedYcmd def Signature_Help_Available_test( app ): response = app.get( '/signature_help_available', { 'subserver': 'cs' } ).json assert_that( response, SignatureAvailableMatcher( 'YES' ) ) @SharedYcmd def Signature_Help_Available_Server_Not_Ready_test( app ): completer = handlers._server_state.GetFiletypeCompleter( [ 'cs' ] ) with patch.object( completer, 'ServerIsHealthy', return_value = False ): response = app.get( '/signature_help_available', { 'subserver': 'cs' } ).json assert_that( response, SignatureAvailableMatcher( 'PENDING' ) ) @SharedYcmd def SignatureHelp_TriggerComma_test( app ): filepath = PathToTestFile( 'testy', 'ContinuousTest.cs' ) contents = ReadFile( filepath ) request = BuildRequest( line_num = 17, column_num = 16, filetypes = [ 'cs' ], filepath = filepath, contents = contents ) with WrapOmniSharpServer( app, filepath ): response = app.post_json( '/signature_help', request ).json LOGGER.debug( 'response = %s', response ) assert_that( response, has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 1, 'signatures': contains_exactly( SignatureMatcher( 'void ContinuousTest.MultiArg(int i, string s)', [ ParameterMatcher( 29, 34 ), ParameterMatcher( 36, 44 ) ] ) ) } ) } ) ) @SharedYcmd def SignatureHelp_TriggerParen_test( app ): filepath = PathToTestFile( 'testy', 'ContinuousTest.cs' ) contents = ReadFile( filepath ) request = BuildRequest( line_num = 10, column_num = 9, filetypes = [ 'cs' ], filepath = filepath, contents = contents ) with WrapOmniSharpServer( app, filepath ): response = app.post_json( '/signature_help', request ).json LOGGER.debug( 'response = %s', response ) assert_that( response, has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': contains_exactly( SignatureMatcher( 'void ContinuousTest.Main(string[] args)', [ ParameterMatcher( 25, 38 ) ] ) ) } ) } ) ) @IsolatedYcmd( { 'disable_signature_help': True } ) def SignatureHelp_TriggerParen_Disabled_test( app ): filepath = PathToTestFile( 'testy', 'ContinuousTest.cs' ) contents = ReadFile( filepath ) request = BuildRequest( line_num = 10, column_num = 9, filetypes = [ 'cs' ], filepath = filepath, contents = contents ) with WrapOmniSharpServer( app, filepath ): response = app.post_json( '/signature_help', request ).json LOGGER.debug( 'response = %s', response ) assert_that( response, has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': empty() } ) } ) ) @SharedYcmd def SignatureHelp_MultipleSignatures_test( app ): filepath = PathToTestFile( 'testy', 'ContinuousTest.cs' ) contents = ReadFile( filepath ) request = BuildRequest( line_num = 18, column_num = 15, filetypes = [ 'cs' ], filepath = filepath, contents = contents ) with WrapOmniSharpServer( app, filepath ): response = app.post_json( '/signature_help', request ).json LOGGER.debug( 'response = %s', response ) assert_that( response, has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': contains_exactly( SignatureMatcher( 'void ContinuousTest.Overloaded(int i, int a)', [ ParameterMatcher( 31, 36 ), ParameterMatcher( 38, 43 ) ] ), SignatureMatcher( 'void ContinuousTest.Overloaded(string s)', [ ParameterMatcher( 31, 39 ) ] ), ) } ) } ) ) request[ 'column_num' ] = 20 with WrapOmniSharpServer( app, filepath ): response = app.post_json( '/signature_help', request ).json LOGGER.debug( 'response = %s', response ) assert_that( response, has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 1, 'signatures': contains_exactly( SignatureMatcher( 'void ContinuousTest.Overloaded(int i, int a)', [ ParameterMatcher( 31, 36 ), ParameterMatcher( 38, 43 ) ] ), SignatureMatcher( 'void ContinuousTest.Overloaded(string s)', [ ParameterMatcher( 31, 39 ) ] ), ) } ) } ) ) @SharedYcmd def SignatureHelp_NotAFunction_NoError_test( app ): filepath = PathToTestFile( 'testy', 'ContinuousTest.cs' ) contents = ReadFile( filepath ) request = BuildRequest( line_num = 19, column_num = 7, filetypes = [ 'cs' ], filepath = filepath, contents = contents ) with WrapOmniSharpServer( app, filepath ): response = app.post_json( '/signature_help', request ).json LOGGER.debug( 'response = %s', response ) assert_that( response, has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': empty() } ) } ) ) @IsolatedYcmd( { 'disable_signature_help': True } ) def GetCompletions_Basic_NoSigHelp_test( app ): filepath = PathToTestFile( 'testy', 'Program.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) completion_data = BuildRequest( filepath = filepath, filetype = 'cs', contents = contents, line_num = 10, column_num = 12 ) response_data = app.post_json( '/completions', completion_data ).json print( 'Response: ', response_data ) assert_that( response_data, has_entries( { 'completion_start_column': 12, 'completions': has_items( CompletionEntryMatcher( 'CursorLeft', 'CursorLeft', { 'kind': 'Property' } ), CompletionEntryMatcher( 'CursorSize', 'CursorSize', { 'kind': 'Property' } ), ), 'errors': empty(), } ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �����������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/subcommands_test.py�������������������������������������0000664�0000000�0000000�00000076454�13746324601�0023606�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2015-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, empty, has_entries, has_entry, has_items, contains_exactly ) from unittest.mock import patch import os.path import pytest from ycmd import user_options_store from ycmd.tests.cs import ( IsolatedYcmd, PathToTestFile, SharedYcmd, WrapOmniSharpServer ) from ycmd.tests.test_utils import ( BuildRequest, ChunkMatcher, ErrorMatcher, LocationMatcher, MockProcessTerminationTimingOut, RangeMatcher, WaitUntilCompleterServerReady, WithRetry ) from ycmd.utils import ReadFile @SharedYcmd def Subcommands_FixIt_NoFixitsFound_test( app ): fixit_test = PathToTestFile( 'testy', 'FixItTestCase.cs' ) with WrapOmniSharpServer( app, fixit_test ): contents = ReadFile( fixit_test ) request = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'FixIt' ], line_num = 1, column_num = 1, contents = contents, filetype = 'cs', filepath = fixit_test ) response = app.post_json( '/run_completer_command', request ).json assert_that( response, has_entries( { 'fixits': empty() } ) ) @SharedYcmd def Subcommands_FixIt_Multi_test( app ): fixit_test = PathToTestFile( 'testy', 'FixItTestCase.cs' ) with WrapOmniSharpServer( app, fixit_test ): contents = ReadFile( fixit_test ) request = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'FixIt' ], line_num = 5, column_num = 27, contents = contents, filetype = 'cs', filepath = fixit_test ) response = app.post_json( '/run_completer_command', request ).json assert_that( response, has_entries( { 'fixits': contains_exactly( has_entries( { 'text': 'Introduce constant', 'command': has_entries( { 'index': 0 } ), 'resolve': True } ), has_entries( { 'text': 'Convert to binary', 'command': has_entries( { 'index': 1 } ), 'resolve': True } ), has_entries( { 'text': 'Convert to hex', 'command': has_entries( { 'index': 2 } ), 'resolve': True } ), ) } ) ) request.pop( 'command_arguments' ) request.update( { 'fixit': response[ 'fixits' ][ 1 ] } ) response = app.post_json( '/resolve_fixit', request ).json assert_that( response, has_entries( { 'fixits': contains_exactly( has_entries( { 'location': LocationMatcher( fixit_test, 5, 27 ), 'chunks': contains_exactly( has_entries( { 'replacement_text': '0b101', } ) ) } ) ) } ) ) @SharedYcmd def Subcommands_FixIt_Range_test( app ): fixit_test = PathToTestFile( 'testy', 'FixItTestCase.cs' ) with WrapOmniSharpServer( app, fixit_test ): contents = ReadFile( fixit_test ) request = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'FixIt' ], line_num = 5, column_num = 23, contents = contents, filetype = 'cs', filepath = fixit_test ) request.update( { 'range': { 'start': { 'line_num': 5, 'column_num': 23 }, 'end': { 'line_num': 5, 'column_num': 27 } } } ) response = app.post_json( '/run_completer_command', request ).json assert_that( response, has_entries( { 'fixits': contains_exactly( has_entries( { 'text': 'Extract method', 'command': has_entries( { 'index': 0 } ), 'resolve': True } ), has_entries( { 'text': 'Extract local function', 'command': has_entries( { 'index': 1 } ), 'resolve': True } ), ) } ) ) @SharedYcmd def Subcommands_FixIt_Single_test( app ): fixit_test = PathToTestFile( 'testy', 'FixItTestCase.cs' ) with WrapOmniSharpServer( app, fixit_test ): contents = ReadFile( fixit_test ) request = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'FixIt' ], line_num = 4, column_num = 17, contents = contents, filetype = 'cs', filepath = fixit_test ) response = app.post_json( '/run_completer_command', request ).json assert_that( response, has_entries( { 'fixits': contains_exactly( has_entries( { 'location': LocationMatcher( fixit_test, 4, 17 ), 'chunks': contains_exactly( has_entries( { 'replacement_text': 'var', 'range': RangeMatcher( fixit_test, ( 4, 13 ), ( 4, 16 ) ) } ) ) } ) ) } ) ) @SharedYcmd def Subcommands_RefactorRename_MissingNewName_test( app ): continuous_test = PathToTestFile( 'testy', 'ContinuousTest.cs' ) with WrapOmniSharpServer( app, continuous_test ): contents = ReadFile( continuous_test ) request = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'RefactorRename' ], line_num = 5, column_num = 15, contents = contents, filetype = 'cs', filepath = continuous_test ) response = app.post_json( '/run_completer_command', request, expect_errors = True ).json assert_that( response, ErrorMatcher( ValueError, 'Please specify a new name to rename it to.\n' 'Usage: RefactorRename <new name>' ) ) @SharedYcmd def Subcommands_RefactorRename_Unicode_test( app ): unicode_test = PathToTestFile( 'testy', 'Unicode.cs' ) with WrapOmniSharpServer( app, unicode_test ): contents = ReadFile( unicode_test ) request = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'RefactorRename', 'x' ], line_num = 30, column_num = 31, contents = contents, filetype = 'cs', filepath = unicode_test ) response = app.post_json( '/run_completer_command', request ).json assert_that( response, has_entries( { 'fixits': contains_exactly( has_entries( { 'location': LocationMatcher( unicode_test, 30, 31 ), 'chunks': contains_exactly( has_entries( { 'replacement_text': 'x', 'range': RangeMatcher( unicode_test, ( 30, 29 ), ( 30, 35 ) ) } ) ) } ) ) } ) ) @SharedYcmd def Subcommands_RefactorRename_Basic_test( app ): continuous_test = PathToTestFile( 'testy', 'ContinuousTest.cs' ) with WrapOmniSharpServer( app, continuous_test ): contents = ReadFile( continuous_test ) request = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'RefactorRename', 'x' ], line_num = 5, column_num = 15, contents = contents, filetype = 'cs', filepath = continuous_test ) response = app.post_json( '/run_completer_command', request ).json assert_that( response, has_entries( { 'fixits': contains_exactly( has_entries( { 'location': LocationMatcher( continuous_test, 5, 15 ), 'chunks': contains_exactly( has_entries( { 'replacement_text': 'x', 'range': RangeMatcher( continuous_test, ( 5, 15 ), ( 5, 29 ) ) } ) ) } ) ) } ) ) @WithRetry @SharedYcmd def Subcommands_GoTo_Basic_test( app ): filepath = PathToTestFile( 'testy', 'GotoTestCase.cs' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'cs', contents = contents, event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', event_data ) WaitUntilCompleterServerReady( app, 'cs' ) destination = PathToTestFile( 'testy', 'Program.cs' ) goto_data = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'GoTo' ], line_num = 10, column_num = 15, contents = contents, filetype = 'cs', filepath = filepath ) response = app.post_json( '/run_completer_command', goto_data ).json assert_that( response, LocationMatcher( destination, 7, 22 ) ) @SharedYcmd @pytest.mark.parametrize( 'identifier,expected', [ ( 'IGotoTestMultiple', LocationMatcher( PathToTestFile( 'testy', 'GotoTestCase.cs' ), 39, 12 ) ), ( 'DoSomething', has_items( LocationMatcher( PathToTestFile( 'testy', 'GotoTestCase.cs' ), 27, 8 ), LocationMatcher( PathToTestFile( 'testy', 'GotoTestCase.cs' ), 31, 15 ), LocationMatcher( PathToTestFile( 'testy', 'GotoTestCase.cs' ), 36, 8 ), LocationMatcher( PathToTestFile( 'testy', 'GotoTestCase.cs' ), 40, 8 ), LocationMatcher( PathToTestFile( 'testy', 'GotoTestCase.cs' ), 44, 15 ), LocationMatcher( PathToTestFile( 'testy', 'GotoTestCase.cs' ), 49, 15 ) ) ), ( 'asd', ErrorMatcher( RuntimeError, 'No symbols found' ) ) ] ) def Subcommands_GoToSymbol_test( app, identifier, expected ): filepath = PathToTestFile( 'testy', 'GotoTestCase.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) goto_data = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'GoToSymbol', identifier ], line_num = 1, column_num = 1, contents = contents, filetype = 'cs', filepath = filepath ) response = app.post_json( '/run_completer_command', goto_data, expect_errors = True ).json assert_that( response, expected ) @SharedYcmd def Subcommands_GoTo_Unicode_test( app ): filepath = PathToTestFile( 'testy', 'Unicode.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) goto_data = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'GoTo' ], line_num = 45, column_num = 43, contents = contents, filetype = 'cs', filepath = filepath ) response = app.post_json( '/run_completer_command', goto_data ).json assert_that( response, LocationMatcher( filepath, 30, 54 ) ) @SharedYcmd def Subcommands_GoToImplementation_Basic_test( app ): filepath = PathToTestFile( 'testy', 'GotoTestCase.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) goto_data = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'GoToImplementation' ], line_num = 14, column_num = 13, contents = contents, filetype = 'cs', filepath = filepath ) response = app.post_json( '/run_completer_command', goto_data ).json assert_that( response, LocationMatcher( filepath, 31, 15 ) ) @SharedYcmd def Subcommands_GoToImplementation_NoImplementation_test( app ): filepath = PathToTestFile( 'testy', 'GotoTestCase.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) goto_data = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'GoToImplementation' ], line_num = 18, column_num = 13, contents = contents, filetype = 'cs', filepath = filepath ) response = app.post_json( '/run_completer_command', goto_data, expect_errors = True ).json assert_that( response, ErrorMatcher( RuntimeError, 'No implementations found' ) ) @SharedYcmd def Subcommands_CsCompleter_InvalidLocation_test( app ): filepath = PathToTestFile( 'testy', 'GotoTestCase.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) goto_data = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'GoToImplementation' ], line_num = 3, column_num = 1, contents = contents, filetype = 'cs', filepath = filepath ) response = app.post_json( '/run_completer_command', goto_data, expect_errors = True ).json assert_that( response, ErrorMatcher( RuntimeError, "Can't jump to implementation" ) ) @SharedYcmd def Subcommands_GoToImplementationElseDeclaration_NoImplementation_test( app ): filepath = PathToTestFile( 'testy', 'GotoTestCase.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) goto_data = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'GoToImplementationElseDeclaration' ], line_num = 18, column_num = 13, contents = contents, filetype = 'cs', filepath = filepath ) response = app.post_json( '/run_completer_command', goto_data ).json assert_that( response, LocationMatcher( filepath, 36, 8 ) ) @SharedYcmd def Subcommands_GoToImplementationElseDeclaration_SingleImplementation_test( app ): filepath = PathToTestFile( 'testy', 'GotoTestCase.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) goto_data = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'GoToImplementationElseDeclaration' ], line_num = 14, column_num = 13, contents = contents, filetype = 'cs', filepath = filepath ) response = app.post_json( '/run_completer_command', goto_data ).json assert_that( response, LocationMatcher( filepath, 31, 15 ) ) @SharedYcmd def Subcommands_GoToImplementationElseDeclaration_MultipleImplementations_test( app ): filepath = PathToTestFile( 'testy', 'GotoTestCase.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) goto_data = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'GoToImplementationElseDeclaration' ], line_num = 22, column_num = 13, contents = contents, filetype = 'cs', filepath = filepath ) response = app.post_json( '/run_completer_command', goto_data ).json assert_that( response, contains_exactly( LocationMatcher( filepath, 44, 15 ), LocationMatcher( filepath, 49, 15 ) ) ) @SharedYcmd def Subcommands_GoToReferences_InvalidLocation_test( app ): filepath = PathToTestFile( 'testy', 'GotoTestCase.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) goto_data = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'GoToReferences' ], line_num = 3, column_num = 1, contents = contents, filetype = 'cs', filepath = filepath ) response = app.post_json( '/run_completer_command', goto_data, expect_errors = True ).json assert_that( response, ErrorMatcher( RuntimeError, 'No references found' ) ) @SharedYcmd def Subcommands_GoToReferences_MultipleReferences_test( app ): filepath = PathToTestFile( 'testy', 'GotoTestCase.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) goto_data = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'GoToReferences' ], line_num = 18, column_num = 4, contents = contents, filetype = 'cs', filepath = filepath ) response = app.post_json( '/run_completer_command', goto_data ).json assert_that( response, contains_exactly( LocationMatcher( filepath, 17, 54 ), LocationMatcher( filepath, 18, 4 ) ) ) @SharedYcmd def Subcommands_GoToReferences_Basic_test( app ): filepath = PathToTestFile( 'testy', 'GotoTestCase.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) goto_data = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'GoToReferences' ], line_num = 21, column_num = 29, contents = contents, filetype = 'cs', filepath = filepath ) response = app.post_json( '/run_completer_command', goto_data ).json assert_that( response, LocationMatcher( filepath, 21, 15 ) ) @SharedYcmd def Subcommands_GetToImplementation_Unicode_test( app ): filepath = PathToTestFile( 'testy', 'Unicode.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) goto_data = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'GoToImplementation' ], line_num = 48, column_num = 44, contents = contents, filetype = 'cs', filepath = filepath ) response = app.post_json( '/run_completer_command', goto_data ).json assert_that( response, contains_exactly( LocationMatcher( filepath, 49, 66 ), LocationMatcher( filepath, 50, 62 ) ) ) @SharedYcmd def Subcommands_GetType_EmptyMessage_test( app ): filepath = PathToTestFile( 'testy', 'GetTypeTestCase.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) gettype_data = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'GetType' ], line_num = 1, column_num = 1, contents = contents, filetype = 'cs', filepath = filepath ) response = app.post_json( '/run_completer_command', gettype_data, expect_errors = True ).json assert_that( response, ErrorMatcher( RuntimeError, 'No type info available.' ) ) @SharedYcmd def Subcommands_GetType_VariableDeclaration_test( app ): filepath = PathToTestFile( 'testy', 'GetTypeTestCase.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) gettype_data = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'GetType' ], line_num = 5, column_num = 5, contents = contents, filetype = 'cs', filepath = filepath ) response = app.post_json( '/run_completer_command', gettype_data ).json assert_that( response, has_entry( 'message', 'System.String' ) ) @SharedYcmd def Subcommands_GetType_VariableUsage_test( app ): filepath = PathToTestFile( 'testy', 'GetTypeTestCase.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) gettype_data = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'GetType' ], line_num = 6, column_num = 5, contents = contents, filetype = 'cs', filepath = filepath ) response = app.post_json( '/run_completer_command', gettype_data ).json assert_that( response, has_entry( 'message', 'string str' ) ) @SharedYcmd def Subcommands_GetType_DocsIgnored_test( app ): filepath = PathToTestFile( 'testy', 'GetTypeTestCase.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) gettype_data = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'GetType' ], line_num = 10, column_num = 34, contents = contents, filetype = 'cs', filepath = filepath ) response = app.post_json( '/run_completer_command', gettype_data ).json assert_that( response, has_entry( 'message', 'int GetTypeTestCase.an_int_with_docs' ) ) @SharedYcmd def Subcommands_GetDoc_Invalid_test( app ): filepath = PathToTestFile( 'testy', 'GetDocTestCase.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) getdoc_data = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'GetDoc' ], line_num = 1, column_num = 1, contents = contents, filetype = 'cs', filepath = filepath ) response = app.post_json( '/run_completer_command', getdoc_data, expect_errors = True ).json assert_that( response, ErrorMatcher( RuntimeError, 'No documentation available.' ) ) @SharedYcmd def Subcommands_GetDoc_Variable_test( app ): filepath = PathToTestFile( 'testy', 'GetDocTestCase.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) getdoc_data = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'GetDoc' ], line_num = 13, column_num = 28, contents = contents, filetype = 'cs', filepath = filepath ) response = app.post_json( '/run_completer_command', getdoc_data ).json assert_that( response, has_entry( 'detailed_info', 'int GetDocTestCase.an_int\n' 'an integer, or something' ) ) @SharedYcmd def Subcommands_GetDoc_Function_test( app ): filepath = PathToTestFile( 'testy', 'GetDocTestCase.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) getdoc_data = BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'GetDoc' ], line_num = 33, column_num = 27, contents = contents, filetype = 'cs', filepath = filepath ) response = app.post_json( '/run_completer_command', getdoc_data ).json assert_that( response, has_entry( 'detailed_info', 'int GetDocTestCase.DoATest()\n' 'Very important method.\n\nWith multiple lines of ' 'commentary\nAnd Format-\n-ting' ) ) @IsolatedYcmd() def Subcommands_StopServer_NoErrorIfNotStarted_test( app ): filepath = PathToTestFile( 'testy', 'GotoTestCase.cs' ) # Don't wrap the server - we don't want to start it! app.post_json( '/run_completer_command', BuildRequest( filetype = 'cs', filepath = filepath, command_arguments = [ 'StopServer' ] ) ) request_data = BuildRequest( filetype = 'cs', filepath = filepath ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entry( 'servers', contains_exactly( has_entry( 'is_running', False ) ) ) ) ) def StopServer_KeepLogFiles( app ): filepath = PathToTestFile( 'testy', 'GotoTestCase.cs' ) with WrapOmniSharpServer( app, filepath ): event_data = BuildRequest( filetype = 'cs', filepath = filepath ) response = app.post_json( '/debug_info', event_data ).json logfiles = [] for server in response[ 'completer' ][ 'servers' ]: logfiles.extend( server[ 'logfiles' ] ) try: for logfile in logfiles: assert_that( os.path.exists( logfile ), f'Logfile should exist at { logfile }' ) finally: app.post_json( '/run_completer_command', BuildRequest( filetype = 'cs', filepath = filepath, command_arguments = [ 'StopServer' ] ) ) if user_options_store.Value( 'server_keep_logfiles' ): for logfile in logfiles: assert_that( os.path.exists( logfile ), f'Logfile should still exist at { logfile }' ) else: for logfile in logfiles: assert_that( not os.path.exists( logfile ), f'Logfile should no longer exist at { logfile }' ) @IsolatedYcmd( { 'server_keep_logfiles': 1 } ) def Subcommands_StopServer_KeepLogFiles_test( app ): StopServer_KeepLogFiles( app ) @IsolatedYcmd( { 'server_keep_logfiles': 0 } ) def Subcommands_StopServer_DoNotKeepLogFiles_test( app ): StopServer_KeepLogFiles( app ) @IsolatedYcmd() def Subcommands_RestartServer_PidChanges_test( app ): filepath = PathToTestFile( 'testy', 'GotoTestCase.cs' ) with WrapOmniSharpServer( app, filepath ): def GetPid(): request_data = BuildRequest( filetype = 'cs', filepath = filepath ) debug_info = app.post_json( '/debug_info', request_data ).json return debug_info[ "completer" ][ "servers" ][ 0 ][ "pid" ] old_pid = GetPid() app.post_json( '/run_completer_command', BuildRequest( filetype = 'cs', filepath = filepath, command_arguments = [ 'RestartServer' ] ) ) WaitUntilCompleterServerReady( app, 'cs' ) new_pid = GetPid() assert old_pid != new_pid, '%r == %r' % ( old_pid, new_pid ) @IsolatedYcmd() @patch( 'ycmd.utils.WaitUntilProcessIsTerminated', MockProcessTerminationTimingOut ) def Subcommands_StopServer_Timeout_test( app ): filepath = PathToTestFile( 'testy', 'GotoTestCase.cs' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'cs', contents = contents, event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', event_data ) WaitUntilCompleterServerReady( app, 'cs' ) app.post_json( '/run_completer_command', BuildRequest( filetype = 'cs', filepath = filepath, command_arguments = [ 'StopServer' ] ) ) request_data = BuildRequest( filetype = 'cs', filepath = filepath ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entry( 'servers', contains_exactly( has_entry( 'is_running', False ) ) ) ) ) @SharedYcmd def Subcommands_Format_Works_test( app ): filepath = PathToTestFile( 'testy', 'Program.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) request = BuildRequest( command_arguments = [ 'Format' ], line_num = 1, column_num = 1, contents = contents, filetype = 'cs', filepath = filepath ) response = app.post_json( '/run_completer_command', request ).json print( 'completer response = ', response ) assert_that( response, has_entries( { 'fixits': contains_exactly( has_entries( { 'location': LocationMatcher( filepath, 1, 1 ), 'chunks': contains_exactly( ChunkMatcher( '\n }\n ', LocationMatcher( filepath, 11, 1 ), LocationMatcher( filepath, 12, 2 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 10, 1 ), LocationMatcher( filepath, 10, 4 ) ), ChunkMatcher( ' {\n ', LocationMatcher( filepath, 8, 1 ), LocationMatcher( filepath, 9, 4 ) ), ChunkMatcher( '', LocationMatcher( filepath, 7, 26 ), LocationMatcher( filepath, 7, 27 ) ), ChunkMatcher( ' class MainClass\n {\n ', LocationMatcher( filepath, 5, 1 ), LocationMatcher( filepath, 7, 3 ) ), ) } ) ) } ) ) @SharedYcmd def Subcommands_RangeFormat_Works_test( app ): filepath = PathToTestFile( 'testy', 'Program.cs' ) with WrapOmniSharpServer( app, filepath ): contents = ReadFile( filepath ) request = BuildRequest( command_arguments = [ 'Format' ], line_num = 11, column_num = 2, contents = contents, filetype = 'cs', filepath = filepath ) request[ 'range' ] = { 'start': { 'line_num': 8, 'column_num': 1 }, 'end': { 'line_num': 11, 'column_num': 4 } } response = app.post_json( '/run_completer_command', request ).json print( 'completer response = ', response ) assert_that( response, has_entries( { 'fixits': contains_exactly( has_entries( { 'location': LocationMatcher( filepath, 11, 2 ), 'chunks': contains_exactly( ChunkMatcher( '\n ', LocationMatcher( filepath, 11, 1 ), LocationMatcher( filepath, 11, 3 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 10, 1 ), LocationMatcher( filepath, 10, 4 ) ), ChunkMatcher( ' {\n ', LocationMatcher( filepath, 8, 1 ), LocationMatcher( filepath, 9, 4 ) ), ) } ) ) } ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/�����������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0021453�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/.gitignore�������������������������������������0000664�0000000�0000000�00000000005�13746324601�0023436�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������obj/ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/����������������������0000775�0000000�0000000�00000000000�13746324601�0026511�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������solution-named-like-folder/�������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0033563�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions������������������������������������������������������������������������������������������not-testy/������������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0035531�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder���������������������������������������������������������������Program.cs������������������������������������������������������������������������������������������0000664�0000000�0000000�00000000170�13746324601�0037465�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/not-testy�����������������������������������������������������using System; namespace testy { class MainClass { public static void Main (string[] args) { Console. } } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Properties/�����������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0037665�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/not-testy�����������������������������������������������������AssemblyInfo.cs�������������������������������������������������������������������������������������0000664�0000000�0000000�00000001725�13746324601�0042614�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/not-testy/Properties������������������������������������������using System.Reflection; using System.Runtime.CompilerServices; // Information about this assembly is defined by the following attributes. // Change them to the values specific to your project. [assembly: AssemblyTitle ("testy")] [assembly: AssemblyDescription ("")] [assembly: AssemblyConfiguration ("")] [assembly: AssemblyCompany ("")] [assembly: AssemblyProduct ("")] [assembly: AssemblyCopyright ("valloric")] [assembly: AssemblyTrademark ("")] [assembly: AssemblyCulture ("")] // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". // The form "{Major}.{Minor}.*" will automatically update the build and revision, // and "{Major}.{Minor}.{Build}.*" will update just the revision. [assembly: AssemblyVersion ("1.0.*")] // The following attributes are used to specify the signing key for the assembly, // if desired. See the Mono documentation for more information about signing. //[assembly: AssemblyDelaySign(false)] //[assembly: AssemblyKeyFile("")] �������������������������������������������testy.csproj����������������������������������������������������������������������������������������0000664�0000000�0000000�00000003171�13746324601�0040125�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/not-testy�����������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">x86</Platform> <ProductVersion>10.0.0</ProductVersion> <SchemaVersion>2.0</SchemaVersion> <ProjectGuid>{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}</ProjectGuid> <OutputType>Exe</OutputType> <RootNamespace>testy</RootNamespace> <AssemblyName>testy</AssemblyName> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> <OutputPath>bin\Debug</OutputPath> <DefineConstants>DEBUG;</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <Externalconsole>true</Externalconsole> <PlatformTarget>x86</PlatformTarget> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> <DebugType>full</DebugType> <Optimize>true</Optimize> <OutputPath>bin\Release</OutputPath> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <Externalconsole>true</Externalconsole> <PlatformTarget>x86</PlatformTarget> </PropertyGroup> <ItemGroup> <Reference Include="System" /> </ItemGroup> <ItemGroup> <Compile Include="Program.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> </Project>�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������solution-named-like-folder.sln����������������������������������������������������������������������0000664�0000000�0000000�00000001524�13746324601�0041434�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder��������������������������������������������������������������� Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "testy", "testy/testy.csproj", "{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.ActiveCfg = Debug|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.Build.0 = Debug|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.ActiveCfg = Release|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution StartupItem = not-testy/testy.csproj EndGlobalSection EndGlobal ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������testy.sln�������������������������������������������������������������������������������������������0000664�0000000�0000000�00000001520�13746324601�0035447�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder��������������������������������������������������������������� Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "testy", "testy/testy.csproj", "{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.ActiveCfg = Debug|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.Build.0 = Debug|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.ActiveCfg = Release|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution StartupItem = testy/testy.csproj EndGlobalSection EndGlobal ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������testy/����������������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0034733�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder���������������������������������������������������������������Program.cs������������������������������������������������������������������������������������������0000664�0000000�0000000�00000000210�13746324601�0036662�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/testy���������������������������������������������������������using System; namespace testy { class MainClass { public static void Main (string[] args) { int 九 = 9; Console. } } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Properties/�����������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0037067�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/testy���������������������������������������������������������AssemblyInfo.cs�������������������������������������������������������������������������������������0000664�0000000�0000000�00000001725�13746324601�0042016�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/testy/Properties����������������������������������������������using System.Reflection; using System.Runtime.CompilerServices; // Information about this assembly is defined by the following attributes. // Change them to the values specific to your project. [assembly: AssemblyTitle ("testy")] [assembly: AssemblyDescription ("")] [assembly: AssemblyConfiguration ("")] [assembly: AssemblyCompany ("")] [assembly: AssemblyProduct ("")] [assembly: AssemblyCopyright ("valloric")] [assembly: AssemblyTrademark ("")] [assembly: AssemblyCulture ("")] // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". // The form "{Major}.{Minor}.*" will automatically update the build and revision, // and "{Major}.{Minor}.{Build}.*" will update just the revision. [assembly: AssemblyVersion ("1.0.*")] // The following attributes are used to specify the signing key for the assembly, // if desired. See the Mono documentation for more information about signing. //[assembly: AssemblyDelaySign(false)] //[assembly: AssemblyKeyFile("")] �������������������������������������������testy.csproj����������������������������������������������������������������������������������������0000664�0000000�0000000�00000003171�13746324601�0037327�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder/testy���������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">x86</Platform> <ProductVersion>10.0.0</ProductVersion> <SchemaVersion>2.0</SchemaVersion> <ProjectGuid>{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}</ProjectGuid> <OutputType>Exe</OutputType> <RootNamespace>testy</RootNamespace> <AssemblyName>testy</AssemblyName> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> <OutputPath>bin\Debug</OutputPath> <DefineConstants>DEBUG;</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <Externalconsole>true</Externalconsole> <PlatformTarget>x86</PlatformTarget> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> <DebugType>full</DebugType> <Optimize>true</Optimize> <OutputPath>bin\Release</OutputPath> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <Externalconsole>true</Externalconsole> <PlatformTarget>x86</PlatformTarget> </PropertyGroup> <ItemGroup> <Reference Include="System" /> </ItemGroup> <ItemGroup> <Compile Include="Program.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> </Project>�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������testy2.sln������������������������������������������������������������������������������������������0000664�0000000�0000000�00000001520�13746324601�0035531�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-named-like-folder��������������������������������������������������������������� Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "testy", "testy/testy.csproj", "{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.ActiveCfg = Debug|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.Build.0 = Debug|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.ActiveCfg = Release|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution StartupItem = testy/testy.csproj EndGlobalSection EndGlobal ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������solution-not-named-like-folder/���������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0034361�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions������������������������������������������������������������������������������������������extra-conf-abs/�������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0037172�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder�����������������������������������������������������������.ycm_extra_conf.py����������������������������������������������������������������������������������0000664�0000000�0000000�00000000367�13746324601�0042630�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-abs�������������������������������������������� import os def CSharpSolutionFile( path, **kwargs ): #remove '.ycm_extra_conf.py' and 'extra-conf-abs' from filename location=os.path.dirname( __file__ ) location=os.path.dirname( location ) return os.path.join( location, 'testy2.sln' ) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������testy/����������������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0040342�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-abs��������������������������������������������Program.cs������������������������������������������������������������������������������������������0000664�0000000�0000000�00000000170�13746324601�0042276�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-abs/testy��������������������������������������using System; namespace testy { class MainClass { public static void Main (string[] args) { Console. } } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Properties/�����������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0042476�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-abs/testy��������������������������������������AssemblyInfo.cs�������������������������������������������������������������������������������������0000664�0000000�0000000�00000001725�13746324601�0045425�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-abs/testy/Properties���������������������������using System.Reflection; using System.Runtime.CompilerServices; // Information about this assembly is defined by the following attributes. // Change them to the values specific to your project. [assembly: AssemblyTitle ("testy")] [assembly: AssemblyDescription ("")] [assembly: AssemblyConfiguration ("")] [assembly: AssemblyCompany ("")] [assembly: AssemblyProduct ("")] [assembly: AssemblyCopyright ("valloric")] [assembly: AssemblyTrademark ("")] [assembly: AssemblyCulture ("")] // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". // The form "{Major}.{Minor}.*" will automatically update the build and revision, // and "{Major}.{Minor}.{Build}.*" will update just the revision. [assembly: AssemblyVersion ("1.0.*")] // The following attributes are used to specify the signing key for the assembly, // if desired. See the Mono documentation for more information about signing. //[assembly: AssemblyDelaySign(false)] //[assembly: AssemblyKeyFile("")] �������������������������������������������testy.csproj����������������������������������������������������������������������������������������0000664�0000000�0000000�00000003171�13746324601�0042736�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-abs/testy��������������������������������������<?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">x86</Platform> <ProductVersion>10.0.0</ProductVersion> <SchemaVersion>2.0</SchemaVersion> <ProjectGuid>{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}</ProjectGuid> <OutputType>Exe</OutputType> <RootNamespace>testy</RootNamespace> <AssemblyName>testy</AssemblyName> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> <OutputPath>bin\Debug</OutputPath> <DefineConstants>DEBUG;</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <Externalconsole>true</Externalconsole> <PlatformTarget>x86</PlatformTarget> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> <DebugType>full</DebugType> <Optimize>true</Optimize> <OutputPath>bin\Release</OutputPath> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <Externalconsole>true</Externalconsole> <PlatformTarget>x86</PlatformTarget> </PropertyGroup> <ItemGroup> <Reference Include="System" /> </ItemGroup> <ItemGroup> <Compile Include="Program.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> </Project>�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������testy2.sln������������������������������������������������������������������������������������������0000664�0000000�0000000�00000001520�13746324601�0041140�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-abs�������������������������������������������� Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "testy", "testy/testy.csproj", "{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.ActiveCfg = Debug|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.Build.0 = Debug|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.ActiveCfg = Release|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution StartupItem = testy/testy.csproj EndGlobalSection EndGlobal ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������extra-conf-bad/�������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0037153�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder�����������������������������������������������������������testy/����������������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0040323�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-bad��������������������������������������������.ycm_extra_conf.py����������������������������������������������������������������������������������0000664�0000000�0000000�00000000102�13746324601�0043744�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-bad/testy�������������������������������������� def CSharpSolutionFile( path, **kwargs ): return "nosuch.sln" ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Program.cs������������������������������������������������������������������������������������������0000664�0000000�0000000�00000000170�13746324601�0042257�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-bad/testy��������������������������������������using System; namespace testy { class MainClass { public static void Main (string[] args) { Console. } } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Properties/�����������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0042457�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-bad/testy��������������������������������������AssemblyInfo.cs�������������������������������������������������������������������������������������0000664�0000000�0000000�00000001725�13746324601�0045406�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-bad/testy/Properties���������������������������using System.Reflection; using System.Runtime.CompilerServices; // Information about this assembly is defined by the following attributes. // Change them to the values specific to your project. [assembly: AssemblyTitle ("testy")] [assembly: AssemblyDescription ("")] [assembly: AssemblyConfiguration ("")] [assembly: AssemblyCompany ("")] [assembly: AssemblyProduct ("")] [assembly: AssemblyCopyright ("valloric")] [assembly: AssemblyTrademark ("")] [assembly: AssemblyCulture ("")] // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". // The form "{Major}.{Minor}.*" will automatically update the build and revision, // and "{Major}.{Minor}.{Build}.*" will update just the revision. [assembly: AssemblyVersion ("1.0.*")] // The following attributes are used to specify the signing key for the assembly, // if desired. See the Mono documentation for more information about signing. //[assembly: AssemblyDelaySign(false)] //[assembly: AssemblyKeyFile("")] �������������������������������������������testy.csproj����������������������������������������������������������������������������������������0000664�0000000�0000000�00000003171�13746324601�0042717�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-bad/testy��������������������������������������<?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">x86</Platform> <ProductVersion>10.0.0</ProductVersion> <SchemaVersion>2.0</SchemaVersion> <ProjectGuid>{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}</ProjectGuid> <OutputType>Exe</OutputType> <RootNamespace>testy</RootNamespace> <AssemblyName>testy</AssemblyName> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> <OutputPath>bin\Debug</OutputPath> <DefineConstants>DEBUG;</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <Externalconsole>true</Externalconsole> <PlatformTarget>x86</PlatformTarget> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> <DebugType>full</DebugType> <Optimize>true</Optimize> <OutputPath>bin\Release</OutputPath> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <Externalconsole>true</Externalconsole> <PlatformTarget>x86</PlatformTarget> </PropertyGroup> <ItemGroup> <Reference Include="System" /> </ItemGroup> <ItemGroup> <Compile Include="Program.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> </Project>�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������testy2.sln������������������������������������������������������������������������������������������0000664�0000000�0000000�00000001520�13746324601�0041121�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-bad�������������������������������������������� Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "testy", "testy/testy.csproj", "{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.ActiveCfg = Debug|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.Build.0 = Debug|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.ActiveCfg = Release|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution StartupItem = testy/testy.csproj EndGlobalSection EndGlobal ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������extra-conf-rel/�������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0037207�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder�����������������������������������������������������������.ycm_extra_conf.py����������������������������������������������������������������������������������0000664�0000000�0000000�00000000102�13746324601�0042630�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel�������������������������������������������� def CSharpSolutionFile( path, **kwargs ): return "testy2.sln" ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������testy/����������������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0040357�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel��������������������������������������������Program.cs������������������������������������������������������������������������������������������0000664�0000000�0000000�00000000170�13746324601�0042313�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/testy��������������������������������������using System; namespace testy { class MainClass { public static void Main (string[] args) { Console. } } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Properties/�����������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0042513�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/testy��������������������������������������AssemblyInfo.cs�������������������������������������������������������������������������������������0000664�0000000�0000000�00000001725�13746324601�0045442�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/testy/Properties���������������������������using System.Reflection; using System.Runtime.CompilerServices; // Information about this assembly is defined by the following attributes. // Change them to the values specific to your project. [assembly: AssemblyTitle ("testy")] [assembly: AssemblyDescription ("")] [assembly: AssemblyConfiguration ("")] [assembly: AssemblyCompany ("")] [assembly: AssemblyProduct ("")] [assembly: AssemblyCopyright ("valloric")] [assembly: AssemblyTrademark ("")] [assembly: AssemblyCulture ("")] // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". // The form "{Major}.{Minor}.*" will automatically update the build and revision, // and "{Major}.{Minor}.{Build}.*" will update just the revision. [assembly: AssemblyVersion ("1.0.*")] // The following attributes are used to specify the signing key for the assembly, // if desired. See the Mono documentation for more information about signing. //[assembly: AssemblyDelaySign(false)] //[assembly: AssemblyKeyFile("")] �������������������������������������������testy.csproj����������������������������������������������������������������������������������������0000664�0000000�0000000�00000003171�13746324601�0042753�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/testy��������������������������������������<?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">x86</Platform> <ProductVersion>10.0.0</ProductVersion> <SchemaVersion>2.0</SchemaVersion> <ProjectGuid>{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}</ProjectGuid> <OutputType>Exe</OutputType> <RootNamespace>testy</RootNamespace> <AssemblyName>testy</AssemblyName> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> <OutputPath>bin\Debug</OutputPath> <DefineConstants>DEBUG;</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <Externalconsole>true</Externalconsole> <PlatformTarget>x86</PlatformTarget> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> <DebugType>full</DebugType> <Optimize>true</Optimize> <OutputPath>bin\Release</OutputPath> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <Externalconsole>true</Externalconsole> <PlatformTarget>x86</PlatformTarget> </PropertyGroup> <ItemGroup> <Reference Include="System" /> </ItemGroup> <ItemGroup> <Compile Include="Program.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> </Project>�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������testy2.sln������������������������������������������������������������������������������������������0000664�0000000�0000000�00000001512�13746324601�0042326�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel/testy�������������������������������������� Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "testy", "testy.csproj", "{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.ActiveCfg = Debug|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.Build.0 = Debug|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.ActiveCfg = Release|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution StartupItem = testy/testy.csproj EndGlobalSection EndGlobal ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������testy2.sln������������������������������������������������������������������������������������������0000664�0000000�0000000�00000001520�13746324601�0041155�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/extra-conf-rel�������������������������������������������� Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "testy", "testy/testy.csproj", "{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.ActiveCfg = Debug|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.Build.0 = Debug|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.ActiveCfg = Release|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution StartupItem = testy/testy.csproj EndGlobalSection EndGlobal ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������testy/����������������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0035531�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder�����������������������������������������������������������Program.cs������������������������������������������������������������������������������������������0000664�0000000�0000000�00000000170�13746324601�0037465�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/testy�����������������������������������������������������using System; namespace testy { class MainClass { public static void Main (string[] args) { Console. } } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Properties/�����������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0037665�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/testy�����������������������������������������������������AssemblyInfo.cs�������������������������������������������������������������������������������������0000664�0000000�0000000�00000001725�13746324601�0042614�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/testy/Properties������������������������������������������using System.Reflection; using System.Runtime.CompilerServices; // Information about this assembly is defined by the following attributes. // Change them to the values specific to your project. [assembly: AssemblyTitle ("testy")] [assembly: AssemblyDescription ("")] [assembly: AssemblyConfiguration ("")] [assembly: AssemblyCompany ("")] [assembly: AssemblyProduct ("")] [assembly: AssemblyCopyright ("valloric")] [assembly: AssemblyTrademark ("")] [assembly: AssemblyCulture ("")] // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". // The form "{Major}.{Minor}.*" will automatically update the build and revision, // and "{Major}.{Minor}.{Build}.*" will update just the revision. [assembly: AssemblyVersion ("1.0.*")] // The following attributes are used to specify the signing key for the assembly, // if desired. See the Mono documentation for more information about signing. //[assembly: AssemblyDelaySign(false)] //[assembly: AssemblyKeyFile("")] �������������������������������������������testy.csproj����������������������������������������������������������������������������������������0000664�0000000�0000000�00000003171�13746324601�0040125�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder/testy�����������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">x86</Platform> <ProductVersion>10.0.0</ProductVersion> <SchemaVersion>2.0</SchemaVersion> <ProjectGuid>{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}</ProjectGuid> <OutputType>Exe</OutputType> <RootNamespace>testy</RootNamespace> <AssemblyName>testy</AssemblyName> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> <OutputPath>bin\Debug</OutputPath> <DefineConstants>DEBUG;</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <Externalconsole>true</Externalconsole> <PlatformTarget>x86</PlatformTarget> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> <DebugType>full</DebugType> <Optimize>true</Optimize> <OutputPath>bin\Release</OutputPath> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <Externalconsole>true</Externalconsole> <PlatformTarget>x86</PlatformTarget> </PropertyGroup> <ItemGroup> <Reference Include="System" /> </ItemGroup> <ItemGroup> <Compile Include="Program.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> </Project>�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������testy1.sln������������������������������������������������������������������������������������������0000664�0000000�0000000�00000001520�13746324601�0036326�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder����������������������������������������������������������� Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "testy", "testy/testy.csproj", "{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.ActiveCfg = Debug|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.Build.0 = Debug|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.ActiveCfg = Release|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution StartupItem = testy/testy.csproj EndGlobalSection EndGlobal ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������testy2.sln������������������������������������������������������������������������������������������0000664�0000000�0000000�00000001520�13746324601�0036327�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy-multiple-solutions/solution-not-named-like-folder����������������������������������������������������������� Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "testy", "testy/testy.csproj", "{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.ActiveCfg = Debug|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.Build.0 = Debug|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.ActiveCfg = Release|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution StartupItem = testy/testy.csproj EndGlobalSection EndGlobal ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy/�����������������������������������������0000775�0000000�0000000�00000000000�13746324601�0022623�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy/ContinuousTest.cs������������������������0000664�0000000�0000000�00000000570�13746324601�0026162�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������using System; namespace testy { public class ContinuousTest { public static void Main (string[] args) { var test = String Main( } public static void Overloaded (int i, int a){} public static void Overloaded (string s){} public static void MultiArg (int i, string s){} public static void SigTest() { MultiArg(42, Overloaded( 123, if( } } } ����������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy/DiagnosticRange.cs�����������������������0000664�0000000�0000000�00000000176�13746324601�0026217�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������public class Class { public int attribute; public static void Main (string[] args) { int 九 = 9; } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy/FixItTestCase.cs�������������������������0000664�0000000�0000000�00000000233�13746324601�0025627�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������namespace testy { public class FixItTestCase { public FixItTestCase() { int li = 5; const int j = 5; } } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy/GetDocTestCase.cs������������������������0000664�0000000�0000000�00000001561�13746324601�0025756�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * testy is a namespace for testing */ namespace testy { /** * Tests the GetDoc subcommands */ public class GetDocTestCase { /** * Constructor */ public GetDocTestCase() { this.an_int = 1; } /** * Very important method. * * With multiple lines of commentary * And Format- * -ting */ public int DoATest() { return an_int; } /// an integer, or something private int an_int; /// Use this for testing private static void DoTesting() { GetDocTestCase tc; tc.DoATest(); } } } �����������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy/GetTypeTestCase.cs�����������������������0000664�0000000�0000000�00000000364�13746324601�0026172�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������using System; namespace testy { public class GetTypeTestCase { public GetTypeTestCase() { var str = ""; str.EndsWith("A"); } /// These docs are not shown in GetType private int an_int_with_docs; } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy/GotoTestCase.cs��������������������������0000664�0000000�0000000�00000001504�13746324601�0025516�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������using System; using System.Data; namespace testy { class GotoTestCase { static void Main (string[] args) { MainClass.Main(new String[0]); } static void ImplementionTest(IGotoTest test) { test.DoSomething(); } static void InterfaceOnlyTest(IGotoTestWithoutImpl test) { test.DoSomething(); } static void MultipleImplementionTest(IGotoTestMultiple test) { test.DoSomething(); } } interface IGotoTest { void DoSomething(); } class GotoTestImpl : IGotoTest { public void DoSomething() { } } interface IGotoTestWithoutImpl { void DoSomething(); } interface IGotoTestMultiple { void DoSomething(); } class GotoTestMultipleImplOne : IGotoTestMultiple { public void DoSomething() { } } class GotoTestMultipleImplTwo : IGotoTestMultiple { public void DoSomething() { } } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy/ImportTest.cs����������������������������0000664�0000000�0000000�00000000200�13746324601�0025254�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������using System; namespace testy { public class ImportTest { public static void Main (string[] args) { new Date } } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy/MaxDiagnostics.cs������������������������0000664�0000000�0000000�00000000137�13746324601�0026070�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������public class MaxDiagnostics { public int test; public int test; public int test; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy/Program.cs�������������������������������0000664�0000000�0000000�00000000210�13746324601�0024552�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������using System; namespace testy { class MainClass { public static void Main (string[] args) { int 九 = 9; Console. } } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy/Properties/������������������������������0000775�0000000�0000000�00000000000�13746324601�0024757�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy/Properties/AssemblyInfo.cs���������������0000664�0000000�0000000�00000001725�13746324601�0027706�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������using System.Reflection; using System.Runtime.CompilerServices; // Information about this assembly is defined by the following attributes. // Change them to the values specific to your project. [assembly: AssemblyTitle ("testy")] [assembly: AssemblyDescription ("")] [assembly: AssemblyConfiguration ("")] [assembly: AssemblyCompany ("")] [assembly: AssemblyProduct ("")] [assembly: AssemblyCopyright ("valloric")] [assembly: AssemblyTrademark ("")] [assembly: AssemblyCulture ("")] // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". // The form "{Major}.{Minor}.*" will automatically update the build and revision, // and "{Major}.{Minor}.{Build}.*" will update just the revision. [assembly: AssemblyVersion ("1.0.*")] // The following attributes are used to specify the signing key for the assembly, // if desired. See the Mono documentation for more information about signing. //[assembly: AssemblyDelaySign(false)] //[assembly: AssemblyKeyFile("")] �������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy/Unicode.cs�������������������������������0000664�0000000�0000000�00000002533�13746324601�0024543�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * testy is a namespace for testing */ namespace testy { /** * Tests the GetDoc subcommands */ public class Unicøde { /** * Constructor */ public Unicøde () { this.an_int = 1; } /** * Very important methød. * * With multiple lines of commentary * And Format- * -ting */ public int DoATest() { return an_int; } /// an integer, or something private int an_int; private int øøø; private Unicøde a_unicøde; private static void DoSomething( Unicøde unicode ) { } /// Use this for testing private static void DoTesting() { Unicøde tc; tc.DoATest(); Unicøde øø; øø. DoSomething( a_unicøde ); } interface Bøøm { void Båm(); } public class BøømBøøm : Bøøm { public void Båm() { } } public class BåmBåm : Bøøm { public void Båm() { } } } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy/ZeroColumnDiagnostic.cs������������������0000664�0000000�0000000�00000000040�13746324601�0027246�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������class Class{ int Method() } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy/testy.csproj�����������������������������0000664�0000000�0000000�00000003522�13746324601�0025217�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">x86</Platform> <ProductVersion>10.0.0</ProductVersion> <SchemaVersion>2.0</SchemaVersion> <ProjectGuid>{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}</ProjectGuid> <OutputType>Exe</OutputType> <RootNamespace>testy</RootNamespace> <AssemblyName>testy</AssemblyName> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> <OutputPath>bin\Debug</OutputPath> <DefineConstants>DEBUG;</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <Externalconsole>true</Externalconsole> <PlatformTarget>x86</PlatformTarget> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> <DebugType>full</DebugType> <Optimize>true</Optimize> <OutputPath>bin\Release</OutputPath> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <Externalconsole>true</Externalconsole> <PlatformTarget>x86</PlatformTarget> </PropertyGroup> <ItemGroup> <Reference Include="System" /> </ItemGroup> <ItemGroup> <Compile Include="ContinuousTest.cs" /> <Compile Include="FixItTestCase.cs" /> <Compile Include="GetTypeTestCase.cs" /> <Compile Include="GotoTestCase.cs" /> <Compile Include="ImportTest.cs" /> <Compile Include="Program.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> </Project>������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy/testy.sln��������������������������������0000664�0000000�0000000�00000001504�13746324601�0024511�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "testy", "testy.csproj", "{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.ActiveCfg = Debug|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.Build.0 = Debug|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.ActiveCfg = Release|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution StartupItem = testy.csproj EndGlobalSection EndGlobal ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/testy/testy.userprefs��������������������������0000664�0000000�0000000�00000001002�13746324601�0025724�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<Properties> <MonoDevelop.Ide.Workspace ActiveConfiguration="Debug|x86" /> <MonoDevelop.Ide.Workbench ActiveDocument="Program.cs"> <Files> <File FileName="Program.cs" Line="7" Column="25" /> <File FileName="Properties/AssemblyInfo.cs" Line="1" Column="1" /> </Files> </MonoDevelop.Ide.Workbench> <MonoDevelop.Ide.DebuggingService.Breakpoints> <BreakpointStore /> </MonoDevelop.Ide.DebuggingService.Breakpoints> <MonoDevelop.Ide.DebuggingService.PinnedWatches /> </Properties>������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/неприличное слово/�������������0000775�0000000�0000000�00000000000�13746324601�0035572�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/неприличное слово/Program.cs���0000664�0000000�0000000�00000000170�13746324601�0037526�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������using System; namespace testy { class MainClass { public static void Main (string[] args) { Console. } } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/неприличное слово/Properties/��0000775�0000000�0000000�00000000000�13746324601�0037726�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������AssemblyInfo.cs�������������������������������������������������������������������������������������0000664�0000000�0000000�00000001725�13746324601�0042576�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/неприличное слово/Properties����������������������������������������������������������������������using System.Reflection; using System.Runtime.CompilerServices; // Information about this assembly is defined by the following attributes. // Change them to the values specific to your project. [assembly: AssemblyTitle ("testy")] [assembly: AssemblyDescription ("")] [assembly: AssemblyConfiguration ("")] [assembly: AssemblyCompany ("")] [assembly: AssemblyProduct ("")] [assembly: AssemblyCopyright ("valloric")] [assembly: AssemblyTrademark ("")] [assembly: AssemblyCulture ("")] // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". // The form "{Major}.{Minor}.*" will automatically update the build and revision, // and "{Major}.{Minor}.{Build}.*" will update just the revision. [assembly: AssemblyVersion ("1.0.*")] // The following attributes are used to specify the signing key for the assembly, // if desired. See the Mono documentation for more information about signing. //[assembly: AssemblyDelaySign(false)] //[assembly: AssemblyKeyFile("")] �������������������������������������������a project.csproj������������������������������������������������������������������������������������0000664�0000000�0000000�00000003175�13746324601�0040612�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/неприличное слово���������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">x86</Platform> <ProductVersion>10.0.0</ProductVersion> <SchemaVersion>2.0</SchemaVersion> <ProjectGuid>{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}</ProjectGuid> <OutputType>Exe</OutputType> <RootNamespace>testy</RootNamespace> <AssemblyName>testy</AssemblyName> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> <OutputPath>bin\Debug</OutputPath> <DefineConstants>DEBUG;</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <Externalconsole>true</Externalconsole> <PlatformTarget>x86</PlatformTarget> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> <DebugType>full</DebugType> <Optimize>true</Optimize> <OutputPath>bin\Release</OutputPath> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <Externalconsole>true</Externalconsole> <PlatformTarget>x86</PlatformTarget> </PropertyGroup> <ItemGroup> <Reference Include="System" /> </ItemGroup> <ItemGroup> <Compile Include="Program.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> </Project> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/неприличное слово/a project.sln0000664�0000000�0000000�00000001520�13746324601�0040155�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "a project", "a project.csproj", "{0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.ActiveCfg = Debug|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Debug|x86.Build.0 = Debug|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.ActiveCfg = Release|x86 {0C99F719-E00E-4CCD-AB9F-FEFBCD97C51F}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution StartupItem = a project.csproj EndGlobalSection EndGlobal ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������a project.userprefs���������������������������������������������������������������������������������0000664�0000000�0000000�00000001002�13746324601�0041313�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/cs/testdata/неприличное слово���������������������������������������������������������������������������������<Properties> <MonoDevelop.Ide.Workspace ActiveConfiguration="Debug|x86" /> <MonoDevelop.Ide.Workbench ActiveDocument="Program.cs"> <Files> <File FileName="Program.cs" Line="7" Column="25" /> <File FileName="Properties/AssemblyInfo.cs" Line="1" Column="1" /> </Files> </MonoDevelop.Ide.Workbench> <MonoDevelop.Ide.DebuggingService.Breakpoints> <BreakpointStore /> </MonoDevelop.Ide.DebuggingService.Breakpoints> <MonoDevelop.Ide.DebuggingService.PinnedWatches /> </Properties>������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/diagnostics_test.py����������������������������������������0000664�0000000�0000000�00000004371�13746324601�0023162�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from hamcrest import assert_that, equal_to from unittest.mock import patch import requests from ycmd.responses import NoDiagnosticSupport, BuildDisplayMessageResponse from ycmd.tests import SharedYcmd from ycmd.tests.test_utils import ( BuildRequest, DummyCompleter, ErrorMatcher, MessageMatcher, PatchCompleter ) @SharedYcmd def Diagnostics_DoesntWork_test( app ): with PatchCompleter( DummyCompleter, filetype = 'dummy_filetype' ): diag_data = BuildRequest( contents = "foo = 5", line_num = 2, filetype = 'dummy_filetype' ) response = app.post_json( '/detailed_diagnostic', diag_data, expect_errors = True ) assert_that( response.status_code, equal_to( requests.codes.internal_server_error ) ) assert_that( response.json, ErrorMatcher( NoDiagnosticSupport ) ) @SharedYcmd @patch( 'ycmd.tests.test_utils.DummyCompleter.GetDetailedDiagnostic', return_value = BuildDisplayMessageResponse( 'detailed diagnostic' ) ) def Diagnostics_DoesWork_test( get_detailed_diag, app ): with PatchCompleter( DummyCompleter, filetype = 'dummy_filetype' ): diag_data = BuildRequest( contents = 'foo = 5', filetype = 'dummy_filetype' ) response = app.post_json( '/detailed_diagnostic', diag_data ) assert_that( response.json, MessageMatcher( 'detailed diagnostic' ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/extra_conf_store_test.py�����������������������������������0000664�0000000�0000000�00000026366�13746324601�0024227�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2016-2020 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 <http://www.gnu.org/licenses/>. import inspect from unittest.mock import patch from hamcrest import ( assert_that, calling, equal_to, has_length, has_property, none, raises, same_instance ) from ycmd import extra_conf_store from ycmd.responses import UnknownExtraConf from ycmd.tests import IsolatedYcmd, PathToTestFile from ycmd.tests.test_utils import TemporarySymlink, UnixOnly, WindowsOnly GLOBAL_EXTRA_CONF = PathToTestFile( 'extra_conf', 'global_extra_conf.py' ) ERRONEOUS_EXTRA_CONF = PathToTestFile( 'extra_conf', 'erroneous_extra_conf.py' ) NO_EXTRA_CONF = PathToTestFile( 'extra_conf', 'no_extra_conf.py' ) PROJECT_EXTRA_CONF = PathToTestFile( 'extra_conf', 'project', '.ycm_extra_conf.py' ) @IsolatedYcmd() def ExtraConfStore_ModuleForSourceFile_UnknownExtraConf_test( app ): filename = PathToTestFile( 'extra_conf', 'project', 'some_file' ) assert_that( calling( extra_conf_store.ModuleForSourceFile ).with_args( filename ), raises( UnknownExtraConf, 'Found .*\\.ycm_extra_conf\\.py\\. Load?' ) ) @IsolatedYcmd( { 'confirm_extra_conf': 0 } ) def ExtraConfStore_ModuleForSourceFile_NoConfirmation_test( app ): filename = PathToTestFile( 'extra_conf', 'project', 'some_file' ) module = extra_conf_store.ModuleForSourceFile( filename ) assert_that( inspect.ismodule( module ) ) assert_that( inspect.getfile( module ), equal_to( PROJECT_EXTRA_CONF ) ) assert_that( module, has_property( 'is_global_ycm_extra_conf' ) ) assert_that( module.is_global_ycm_extra_conf, equal_to( False ) ) assert_that( extra_conf_store.IsGlobalExtraConfModule( module ), equal_to( False ) ) @IsolatedYcmd( { 'extra_conf_globlist': [ PROJECT_EXTRA_CONF ] } ) def ExtraConfStore_ModuleForSourceFile_Whitelisted_test( app ): filename = PathToTestFile( 'extra_conf', 'project', 'some_file' ) module = extra_conf_store.ModuleForSourceFile( filename ) assert_that( inspect.ismodule( module ) ) assert_that( inspect.getfile( module ), equal_to( PROJECT_EXTRA_CONF ) ) assert_that( module, has_property( 'is_global_ycm_extra_conf' ) ) assert_that( module.is_global_ycm_extra_conf, equal_to( False ) ) assert_that( extra_conf_store.IsGlobalExtraConfModule( module ), equal_to( False ) ) @IsolatedYcmd( { 'extra_conf_globlist': [ '!' + PROJECT_EXTRA_CONF ] } ) def ExtraConfStore_ModuleForSourceFile_Blacklisted_test( app ): filename = PathToTestFile( 'extra_conf', 'project', 'some_file' ) assert_that( extra_conf_store.ModuleForSourceFile( filename ), none() ) @patch.dict( 'os.environ', { 'YCMD_TEST': PROJECT_EXTRA_CONF } ) @IsolatedYcmd( { 'extra_conf_globlist': [ '$YCMD_TEST' ] } ) def ExtraConfStore_ModuleForSourceFile_UnixVarEnv_test( app ): filename = PathToTestFile( 'extra_conf', 'project', 'some_file' ) module = extra_conf_store.ModuleForSourceFile( filename ) assert_that( inspect.ismodule( module ) ) assert_that( inspect.getfile( module ), equal_to( PROJECT_EXTRA_CONF ) ) assert_that( module, has_property( 'is_global_ycm_extra_conf' ) ) assert_that( module.is_global_ycm_extra_conf, equal_to( False ) ) assert_that( extra_conf_store.IsGlobalExtraConfModule( module ), equal_to( False ) ) @WindowsOnly @patch.dict( 'os.environ', { 'YCMD_TEST': PROJECT_EXTRA_CONF } ) @IsolatedYcmd( { 'extra_conf_globlist': [ '%YCMD_TEST%' ] } ) def ExtraConfStore_ModuleForSourceFile_WinVarEnv_test( app ): filename = PathToTestFile( 'extra_conf', 'project', 'some_file' ) module = extra_conf_store.ModuleForSourceFile( filename ) assert_that( inspect.ismodule( module ) ) assert_that( inspect.getfile( module ), equal_to( PROJECT_EXTRA_CONF ) ) assert_that( module, has_property( 'is_global_ycm_extra_conf' ) ) assert_that( module.is_global_ycm_extra_conf, equal_to( False ) ) assert_that( extra_conf_store.IsGlobalExtraConfModule( module ), equal_to( False ) ) @UnixOnly @IsolatedYcmd( { 'extra_conf_globlist': [ PathToTestFile( 'extra_conf', 'symlink', '*' ) ] } ) def ExtraConfStore_ModuleForSourceFile_SupportSymlink_test( app ): with TemporarySymlink( PathToTestFile( 'extra_conf', 'project' ), PathToTestFile( 'extra_conf', 'symlink' ) ): filename = PathToTestFile( 'extra_conf', 'project', 'some_file' ) module = extra_conf_store.ModuleForSourceFile( filename ) assert_that( inspect.ismodule( module ) ) assert_that( inspect.getfile( module ), equal_to( PROJECT_EXTRA_CONF ) ) assert_that( module, has_property( 'is_global_ycm_extra_conf' ) ) assert_that( module.is_global_ycm_extra_conf, equal_to( False ) ) assert_that( extra_conf_store.IsGlobalExtraConfModule( module ), equal_to( False ) ) @IsolatedYcmd( { 'global_ycm_extra_conf': GLOBAL_EXTRA_CONF } ) def ExtraConfStore_ModuleForSourceFile_GlobalExtraConf_test( app ): filename = PathToTestFile( 'extra_conf', 'some_file' ) module = extra_conf_store.ModuleForSourceFile( filename ) assert_that( inspect.ismodule( module ) ) assert_that( inspect.getfile( module ), equal_to( GLOBAL_EXTRA_CONF ) ) assert_that( module, has_property( 'is_global_ycm_extra_conf' ) ) assert_that( module.is_global_ycm_extra_conf, equal_to( True ) ) assert_that( extra_conf_store.IsGlobalExtraConfModule( module ), equal_to( True ) ) @patch.dict( 'os.environ', { 'YCMD_TEST': GLOBAL_EXTRA_CONF } ) @IsolatedYcmd( { 'global_ycm_extra_conf': '$YCMD_TEST' } ) def ExtraConfStore_ModuleForSourceFile_GlobalExtraConf_UnixEnvVar_test( app ): filename = PathToTestFile( 'extra_conf', 'some_file' ) module = extra_conf_store.ModuleForSourceFile( filename ) assert_that( inspect.ismodule( module ) ) assert_that( inspect.getfile( module ), equal_to( GLOBAL_EXTRA_CONF ) ) assert_that( module, has_property( 'is_global_ycm_extra_conf' ) ) assert_that( module.is_global_ycm_extra_conf, equal_to( True ) ) assert_that( extra_conf_store.IsGlobalExtraConfModule( module ), equal_to( True ) ) @WindowsOnly @patch.dict( 'os.environ', { 'YCMD_TEST': GLOBAL_EXTRA_CONF } ) @IsolatedYcmd( { 'global_ycm_extra_conf': '%YCMD_TEST%' } ) def ExtraConfStore_ModuleForSourceFile_GlobalExtraConf_WinEnvVar_test( app ): filename = PathToTestFile( 'extra_conf', 'some_file' ) module = extra_conf_store.ModuleForSourceFile( filename ) assert_that( inspect.ismodule( module ) ) assert_that( inspect.getfile( module ), equal_to( GLOBAL_EXTRA_CONF ) ) assert_that( module, has_property( 'is_global_ycm_extra_conf' ) ) assert_that( module.is_global_ycm_extra_conf, equal_to( True ) ) assert_that( extra_conf_store.IsGlobalExtraConfModule( module ), equal_to( True ) ) @IsolatedYcmd( { 'global_ycm_extra_conf': NO_EXTRA_CONF } ) @patch( 'ycmd.extra_conf_store.LOGGER', autospec = True ) def ExtraConfStore_CallGlobalExtraConfMethod_NoGlobalExtraConf_test( logger, app ): extra_conf_store._CallGlobalExtraConfMethod( 'SomeMethod' ) assert_that( logger.method_calls, has_length( 1 ) ) logger.debug.assert_called_with( 'No global extra conf, not calling method %s', 'SomeMethod' ) @IsolatedYcmd( { 'global_ycm_extra_conf': GLOBAL_EXTRA_CONF } ) @patch( 'ycmd.extra_conf_store.LOGGER', autospec = True ) def CallGlobalExtraConfMethod_NoMethodInGlobalExtraConf_test( logger, app ): extra_conf_store._CallGlobalExtraConfMethod( 'MissingMethod' ) assert_that( logger.method_calls, has_length( 1 ) ) logger.debug.assert_called_with( 'Global extra conf not loaded or no function %s', 'MissingMethod' ) @IsolatedYcmd( { 'global_ycm_extra_conf': GLOBAL_EXTRA_CONF } ) @patch( 'ycmd.extra_conf_store.LOGGER', autospec = True ) def CallGlobalExtraConfMethod_NoExceptionFromMethod_test( logger, app ): extra_conf_store._CallGlobalExtraConfMethod( 'NoException' ) assert_that( logger.method_calls, has_length( 1 ) ) logger.info.assert_called_with( 'Calling global extra conf method %s on conf file %s', 'NoException', GLOBAL_EXTRA_CONF ) @IsolatedYcmd( { 'global_ycm_extra_conf': GLOBAL_EXTRA_CONF } ) @patch( 'ycmd.extra_conf_store.LOGGER', autospec = True ) def CallGlobalExtraConfMethod_CatchExceptionFromMethod_test( logger, app ): extra_conf_store._CallGlobalExtraConfMethod( 'RaiseException' ) assert_that( logger.method_calls, has_length( 2 ) ) logger.info.assert_called_with( 'Calling global extra conf method %s on conf file %s', 'RaiseException', GLOBAL_EXTRA_CONF ) logger.exception.assert_called_with( 'Error occurred while calling global extra conf method %s on conf file %s', 'RaiseException', GLOBAL_EXTRA_CONF ) @IsolatedYcmd( { 'global_ycm_extra_conf': ERRONEOUS_EXTRA_CONF } ) @patch( 'ycmd.extra_conf_store.LOGGER', autospec = True ) def CallGlobalExtraConfMethod_CatchExceptionFromExtraConf_test( logger, app ): extra_conf_store._CallGlobalExtraConfMethod( 'NoException' ) assert_that( logger.method_calls, has_length( 1 ) ) logger.exception.assert_called_with( 'Error occurred while loading global extra conf %s', ERRONEOUS_EXTRA_CONF ) @IsolatedYcmd() def Load_DoNotReloadExtraConf_NoForce_test( app ): with patch( 'ycmd.extra_conf_store._ShouldLoad', return_value = True ): module = extra_conf_store.Load( PROJECT_EXTRA_CONF ) assert_that( inspect.ismodule( module ) ) assert_that( inspect.getfile( module ), equal_to( PROJECT_EXTRA_CONF ) ) assert_that( module, has_property( 'is_global_ycm_extra_conf' ) ) assert_that( module.is_global_ycm_extra_conf, equal_to( False ) ) assert_that( extra_conf_store.IsGlobalExtraConfModule( module ), equal_to( False ) ) assert_that( extra_conf_store.Load( PROJECT_EXTRA_CONF ), same_instance( module ) ) @IsolatedYcmd() def Load_DoNotReloadExtraConf_ForceEqualsTrue_test( app ): with patch( 'ycmd.extra_conf_store._ShouldLoad', return_value = True ): module = extra_conf_store.Load( PROJECT_EXTRA_CONF ) assert_that( inspect.ismodule( module ) ) assert_that( inspect.getfile( module ), equal_to( PROJECT_EXTRA_CONF ) ) assert_that( module, has_property( 'is_global_ycm_extra_conf' ) ) assert_that( module.is_global_ycm_extra_conf, equal_to( False ) ) assert_that( extra_conf_store.IsGlobalExtraConfModule( module ), equal_to( False ) ) assert_that( extra_conf_store.Load( PROJECT_EXTRA_CONF, force = True ), same_instance( module ) ) def ExtraConfStore_IsGlobalExtraConfStore_NotAExtraConf_test(): assert_that( calling( extra_conf_store.IsGlobalExtraConfModule ).with_args( extra_conf_store ), raises( AttributeError ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/filename_completer_test.py���������������������������������0000664�0000000�0000000�00000035054�13746324601�0024507�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2014-2020 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 <http://www.gnu.org/licenses/>. import os import pytest from hamcrest import assert_that, contains_inanyorder, empty, is_not from unittest.mock import patch from ycmd.tests import IsolatedYcmd from ycmd.tests.test_utils import ( BuildRequest, CurrentWorkingDirectory, CompletionEntryMatcher, WindowsOnly ) from ycmd.utils import GetCurrentDirectory, ToBytes TEST_DIR = os.path.dirname( os.path.abspath( __file__ ) ) DATA_DIR = os.path.join( TEST_DIR, 'testdata', 'filename_completer', 'inner_dir' ) ROOT_FOLDER_COMPLETIONS = tuple( ( path, '[Dir]' if os.path.isdir( os.path.sep + path ) else '[File]' ) for path in os.listdir( os.path.sep ) ) DRIVE = os.path.splitdrive( TEST_DIR )[ 0 ] PATH_TO_TEST_FILE = os.path.join( DATA_DIR, 'test.cpp' ) def FilenameCompleter_Completion( app, contents, environ, filetype, completions ): completion_data = BuildRequest( contents = contents, filepath = PATH_TO_TEST_FILE, filetype = filetype, column_num = len( ToBytes( contents ) ) + 1 ) if completions: completion_matchers = [ CompletionEntryMatcher( *completion ) for completion in completions ] expected_results = contains_inanyorder( *completion_matchers ) else: expected_results = empty() with patch.dict( 'os.environ', environ ): results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, expected_results ) # A series of tests represented by tuples whose elements are: # - the line to complete; # - the environment variables; # - the expected completions. @pytest.mark.parametrize( 'contents,env,expected', [ ( '/', {}, ROOT_FOLDER_COMPLETIONS ), ( '//', {}, () ), ( 'const char* c = "/', {}, ROOT_FOLDER_COMPLETIONS ), ( 'const char* c = "./', {}, ( ( 'dir with spaces (x64)', '[Dir]' ), ( 'foo漢字.txt', '[File]' ), ( 'test.cpp', '[File]' ), ( 'test.hpp', '[File]' ) ) ), ( 'const char* c = "./漢', {}, ( ( 'foo漢字.txt', '[File]' ), ) ), ( 'const char* c = "./dir with spaces (x64)/', {}, ( ( 'Qt', '[Dir]' ), ( 'QtGui', '[Dir]' ) ) ), ( 'const char* c = "./dir with spaces (x64)//', {}, ( ( 'Qt', '[Dir]' ), ( 'QtGui', '[Dir]' ) ) ), ( 'const char* c = "../', {}, ( ( 'inner_dir', '[Dir]' ), ( '∂†∫', '[Dir]' ) ) ), ( 'const char* c = "../inner_dir/', {}, ( ( 'dir with spaces (x64)', '[Dir]' ), ( 'foo漢字.txt', '[File]' ), ( 'test.cpp', '[File]' ), ( 'test.hpp', '[File]' ) ) ), ( 'const char* c = "../inner_dir/dir with spaces (x64)/', {}, ( ( 'Qt', '[Dir]' ), ( 'QtGui', '[Dir]' ) ) ), ( 'const char* c = "~/', { 'HOME': DATA_DIR , 'USERPROFILE': DATA_DIR }, ( ( 'dir with spaces (x64)', '[Dir]' ), ( 'foo漢字.txt', '[File]' ), ( 'test.cpp', '[File]' ), ( 'test.hpp', '[File]' ) ) ), ( 'const char* c = "~/dir with spaces (x64)/', { 'HOME': DATA_DIR, 'USERPROFILE': DATA_DIR }, ( ( 'Qt', '[Dir]' ), ( 'QtGui', '[Dir]' ) ) ), ( 'const char* c = "~/dir with spaces (x64)/Qt/', { 'HOME': DATA_DIR, 'USERPROFILE': DATA_DIR }, ( ( 'QtGui', '[File]' ), ) ), ( 'const char* c = "dir with spaces (x64)/', {}, ( ( 'Qt', '[Dir]' ), ( 'QtGui', '[Dir]' ) ) ), ( 'const char* c = "dir with spaces (x64)/Qt/', {}, ( ( 'QtGui', '[File]' ), ) ), ( 'const char* c = "dir with spaces (x64)/Qt/QtGui dir with spaces (x64)/', {}, ( ( 'Qt', '[Dir]' ), ( 'QtGui', '[Dir]' ) ) ), ( 'const char* c = "dir with spaces (x64)/Qt/QtGui/', {}, () ), ( 'const char* c = "dir with spaces (x64)/Qt/QtGui /', {}, () ), ( 'set x = $YCM_TEST_DATA_DIR/dir with spaces (x64)/QtGui/', { 'YCM_TEST_DATA_DIR': DATA_DIR }, ( ( 'QDialog', '[File]' ), ( 'QWidget', '[File]' ) ) ), ( 'set x = $YCM_TEST_DIR/testdata/filename_completer/inner_dir/test', { 'YCM_TEST_DIR': TEST_DIR }, ( ( 'test.cpp', '[File]' ), ( 'test.hpp', '[File]' ) ) ), ( 'set x = $YCMTESTDIR/testdata/filename_completer/', { 'YCMTESTDIR': TEST_DIR }, ( ( 'inner_dir', '[Dir]' ), ( '∂†∫', '[Dir]' ) ) ), ( 'set x = $ycm_test_dir/testdata/filename_completer/inn', { 'ycm_test_dir': TEST_DIR }, ( ( 'inner_dir', '[Dir]' ), ) ), ( 'set x = ' + TEST_DIR + '/testdata/filename_completer/$YCM_TEST_filename_completer/', { 'YCM_TEST_filename_completer': 'inner_dir' }, ( ( 'dir with spaces (x64)', '[Dir]' ), ( 'foo漢字.txt', '[File]' ), ( 'test.cpp', '[File]' ), ( 'test.hpp', '[File]' ) ) ), ( 'set x = ' + TEST_DIR + '/testdata/filename_completer/$YCM_TEST_filename_c0mpleter/test', { 'YCM_TEST_filename_c0mpleter': 'inner_dir' }, ( ( 'test.cpp', '[File]' ), ( 'test.hpp', '[File]' ) ) ), ( 'set x = ' + TEST_DIR + '/${YCM_TEST_td}ata/filename_completer/', { 'YCM_TEST_td': 'testd' }, ( ( 'inner_dir', '[Dir]' ), ( '∂†∫', '[Dir]' ) ) ), ( 'set x = ' + TEST_DIR + '/tes${YCM_TEST_td}/filename_completer/', { 'YCM_TEST_td': 'tdata' }, ( ( 'inner_dir', '[Dir]' ), ( '∂†∫', '[Dir]' ) ) ), ( 'set x = ' + TEST_DIR + '/testdata/filename_completer${YCM_TEST_td}/', {}, () ), ( 'set x = ' + TEST_DIR + '/testdata/filename_completer${YCM_empty_var}/', { 'YCM_empty_var': '' }, ( ( 'inner_dir', '[Dir]' ), ( '∂†∫', '[Dir]' ) ) ), ( 'set x = ' + TEST_DIR + '/$YCM_TEST_td}/', { 'YCM_TEST_td': 'testdata/filename_completer' }, () ), ( 'set x = ' + TEST_DIR + '/${YCM_TEST_td/', { 'YCM_TEST_td': 'testdata/filename_completer' }, () ), ( 'set x = ' + TEST_DIR + '/$ YCM_TEST_td/', { 'YCM_TEST_td': 'testdata/filename_completer' }, () ), ( 'test ' + DATA_DIR + '/../∂', {}, ( ( '∂†∫', '[Dir]' ), ) ), ] ) @IsolatedYcmd( { 'max_num_candidates': 0 } ) def FilenameCompleter_Completion_test( app, contents, env, expected ): FilenameCompleter_Completion( app, contents, env, 'foo', expected ) @pytest.mark.parametrize( 'contents,env,expected', [ ( '\\', {}, ROOT_FOLDER_COMPLETIONS ), ( '/\\', {}, () ), ( '\\\\', {}, () ), ( '\\/', {}, () ), ( 'const char* c = "\\', {}, ROOT_FOLDER_COMPLETIONS ), ( 'const char* c = "' + DRIVE + '/', {}, ROOT_FOLDER_COMPLETIONS ), ( 'const char* c = "' + DRIVE + '\\', {}, ROOT_FOLDER_COMPLETIONS ), ( 'const char* c = ".\\', {}, ( ( 'dir with spaces (x64)', '[Dir]' ), ( 'foo漢字.txt', '[File]' ), ( 'test.cpp', '[File]' ), ( 'test.hpp', '[File]' ) ) ), ( 'const char* c = ".\\dir with spaces (x64)\\', {}, ( ( 'Qt', '[Dir]' ), ( 'QtGui', '[Dir]' ) ) ), ( 'const char* c = ".\\dir with spaces (x64)\\\\', {}, ( ( 'Qt', '[Dir]' ), ( 'QtGui', '[Dir]' ) ) ), ( 'const char* c = ".\\dir with spaces (x64)/\\', {}, ( ( 'Qt', '[Dir]' ), ( 'QtGui', '[Dir]' ) ) ), ( 'const char* c = ".\\dir with spaces (x64)\\/', {}, ( ( 'Qt', '[Dir]' ), ( 'QtGui', '[Dir]' ) ) ), ( 'const char* c = ".\\dir with spaces (x64)/Qt\\', {}, ( ( 'QtGui', '[File]' ), ) ), ( 'const char* c = "dir with spaces (x64)\\Qt\\', {}, ( ( 'QtGui', '[File]' ), ) ), ( 'dir with spaces (x64)\\Qt/QtGui dir with spaces (x64)\\', {}, ( ( 'Qt', '[Dir]' ), ( 'QtGui', '[Dir]' ) ) ), ( 'set x = %YCM_TEST_DIR%\\testdata/filename_completer\\inner_dir/test', { 'YCM_TEST_DIR': TEST_DIR }, ( ( 'test.cpp', '[File]' ), ( 'test.hpp', '[File]' ) ) ), ( 'set x = YCM_TEST_DIR%\\testdata/filename_completer\\inner_dir/test', { 'YCM_TEST_DIR': TEST_DIR }, () ), ( 'set x = %YCM_TEST_DIR\\testdata/filename_completer\\inner_dir/test', { 'YCM_TEST_DIR': TEST_DIR }, () ), ] ) @WindowsOnly @IsolatedYcmd( { 'max_num_candidates': 0 } ) def FilenameCompleter_Completion_Windows_test( app, contents, env, expected ): FilenameCompleter_Completion( app, contents, env, 'foo', expected ) @IsolatedYcmd( { 'filepath_completion_use_working_dir': 0 } ) def WorkingDir_UseFilePath_test( app ): assert_that( GetCurrentDirectory() != DATA_DIR, 'Please run this test from a ' 'different directory' ) completion_data = BuildRequest( contents = 'ls ./dir with spaces (x64)/', filepath = PATH_TO_TEST_FILE, column_num = 28 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, contains_inanyorder( CompletionEntryMatcher( 'Qt', '[Dir]' ), CompletionEntryMatcher( 'QtGui', '[Dir]' ) ) ) @IsolatedYcmd( { 'filepath_completion_use_working_dir': 1 } ) def WorkingDir_UseServerWorkingDirectory_test( app ): test_dir = os.path.join( DATA_DIR, 'dir with spaces (x64)' ) with CurrentWorkingDirectory( test_dir ) as old_current_dir: assert_that( old_current_dir != test_dir, 'Please run this test from a different directory' ) completion_data = BuildRequest( contents = 'ls ./', filepath = PATH_TO_TEST_FILE, column_num = 6 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, contains_inanyorder( CompletionEntryMatcher( 'Qt', '[Dir]' ), CompletionEntryMatcher( 'QtGui', '[Dir]' ) ) ) @IsolatedYcmd( { 'filepath_completion_use_working_dir': 1 } ) def WorkingDir_UseServerWorkingDirectory_Unicode_test( app ): test_dir = os.path.join( TEST_DIR, 'testdata', 'filename_completer', '∂†∫' ) with CurrentWorkingDirectory( test_dir ) as old_current_dir: assert_that( old_current_dir != test_dir, 'Please run this test from a different directory' ) # We don't supply working_dir in the request, so the current working # directory is used. completion_data = BuildRequest( contents = 'ls ./', filepath = PATH_TO_TEST_FILE, column_num = 6 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, contains_inanyorder( CompletionEntryMatcher( '†es†.txt', '[File]' ) ) ) @IsolatedYcmd( { 'filepath_completion_use_working_dir': 1 } ) def WorkingDir_UseClientWorkingDirectory_test( app ): test_dir = os.path.join( DATA_DIR, 'dir with spaces (x64)' ) assert_that( GetCurrentDirectory() != test_dir, 'Please run this test from a different directory' ) # We supply working_dir in the request, so we expect results to be # relative to the supplied path. completion_data = BuildRequest( contents = 'ls ./', filepath = PATH_TO_TEST_FILE, column_num = 6, working_dir = test_dir ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, contains_inanyorder( CompletionEntryMatcher( 'Qt', '[Dir]' ), CompletionEntryMatcher( 'QtGui', '[Dir]' ) ) ) @IsolatedYcmd( { 'filepath_blacklist': {} } ) def FilenameCompleter_NoFiletypeBlacklisted_test( app ): completion_data = BuildRequest( filetypes = [ 'foo', 'bar' ], contents = './', column_num = 3 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, is_not( empty() ) ) @IsolatedYcmd( { 'filepath_blacklist': { 'foo': 1 } } ) def FilenameCompleter_FirstFiletypeBlacklisted_test( app ): completion_data = BuildRequest( filetypes = [ 'foo', 'bar' ], contents = './', column_num = 3 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, empty() ) @IsolatedYcmd( { 'filepath_blacklist': { 'bar': 1 } } ) def FilenameCompleter_SecondFiletypeBlacklisted_test( app ): completion_data = BuildRequest( filetypes = [ 'foo', 'bar' ], contents = './', column_num = 3 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, empty() ) @IsolatedYcmd( { 'filepath_blacklist': { '*': 1 } } ) def FilenameCompleter_AllFiletypesBlacklisted_test( app ): completion_data = BuildRequest( filetypes = [ 'foo', 'bar' ], contents = './', column_num = 3 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, empty() ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/get_completions_test.py������������������������������������0000664�0000000�0000000�00000074125�13746324601�0024052�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2013-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_exactly, contains_inanyorder, empty, equal_to, has_entries, has_items ) from unittest.mock import patch from ycmd.tests import IsolatedYcmd, SharedYcmd, PathToTestFile from ycmd.tests.test_utils import ( BuildRequest, CompletionEntryMatcher, DummyCompleter, PatchCompleter ) @SharedYcmd def GetCompletions_RequestValidation_NoLineNumException_test( app ): response = app.post_json( '/semantic_completion_available', { 'column_num': 0, 'filepath': '/foo', 'file_data': { '/foo': { 'filetypes': [ 'text' ], 'contents': 'zoo' } } }, status = '5*', expect_errors = True ) response.mustcontain( 'missing', 'line_num' ) @SharedYcmd def GetCompletions_IdentifierCompleter_Works_test( app ): event_data = BuildRequest( contents = 'foo foogoo ba', event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', event_data ) # query is 'oo' completion_data = BuildRequest( contents = 'oo foo foogoo ba', column_num = 3 ) response_data = app.post_json( '/completions', completion_data ).json assert_that( 1, equal_to( response_data[ 'completion_start_column' ] ) ) assert_that( response_data[ 'completions' ], has_items( CompletionEntryMatcher( 'foo', '[ID]' ), CompletionEntryMatcher( 'foogoo', '[ID]' ) ) ) @IsolatedYcmd( { 'min_num_identifier_candidate_chars': 4 } ) def GetCompletions_IdentifierCompleter_FilterShortCandidates_test( app ): event_data = BuildRequest( contents = 'foo foogoo gooo', event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', event_data ) completion_data = BuildRequest( contents = 'oo', column_num = 3 ) response = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( response, contains_inanyorder( CompletionEntryMatcher( 'foogoo' ), CompletionEntryMatcher( 'gooo' ) ) ) @SharedYcmd def GetCompletions_IdentifierCompleter_StartColumn_AfterWord_test( app ): completion_data = BuildRequest( contents = 'oo foo foogoo ba', column_num = 11 ) response_data = app.post_json( '/completions', completion_data ).json assert_that( 8, equal_to( response_data[ 'completion_start_column' ] ) ) @SharedYcmd def GetCompletions_IdentifierCompleter_WorksForSpecialIdentifierChars_test( app ): contents = """ textarea { font-family: sans-serif; font-size: 12px; }""" event_data = BuildRequest( contents = contents, filetype = 'css', event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', event_data ) # query is 'fo' completion_data = BuildRequest( contents = 'fo ' + contents, filetype = 'css', column_num = 3 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'font-size', '[ID]' ), CompletionEntryMatcher( 'font-family', '[ID]' ) ) ) @SharedYcmd def GetCompletions_IdentifierCompleter_Unicode_InLine_test( app ): contents = """ This is some text cøntaining unicøde """ event_data = BuildRequest( contents = contents, filetype = 'css', event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', event_data ) # query is 'tx' completion_data = BuildRequest( contents = 'tx ' + contents, filetype = 'css', column_num = 3 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'text', '[ID]' ) ) ) @SharedYcmd def GetCompletions_IdentifierCompleter_UnicodeQuery_InLine_test( app ): contents = """ This is some text cøntaining unicøde """ event_data = BuildRequest( contents = contents, filetype = 'css', event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', event_data ) # query is 'cø' completion_data = BuildRequest( contents = 'cø ' + contents, filetype = 'css', column_num = 4 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'cøntaining', '[ID]' ), CompletionEntryMatcher( 'unicøde', '[ID]' ) ) ) @IsolatedYcmd() def GetCompletions_IdentifierCompleter_Unicode_MultipleCodePoints_test( app ): # The first ō is on one code point while the second is on two ("o" + combining # macron character). event_data = BuildRequest( contents = 'fōo\nfōo', event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', event_data ) # query is 'fo' completion_data = BuildRequest( contents = 'fōo\nfōo\nfo', line_num = 3, column_num = 3 ) response_data = app.post_json( '/completions', completion_data ).json assert_that( 1, equal_to( response_data[ 'completion_start_column' ] ) ) assert_that( response_data[ 'completions' ], has_items( CompletionEntryMatcher( 'fōo', '[ID]' ), CompletionEntryMatcher( 'fōo', '[ID]' ) ) ) @SharedYcmd @patch( 'ycmd.tests.test_utils.DummyCompleter.CandidatesList', return_value = [ 'foo', 'bar', 'qux' ] ) def GetCompletions_ForceSemantic_Works_test( candidates, app ): with PatchCompleter( DummyCompleter, 'dummy_filetype' ): completion_data = BuildRequest( filetype = 'dummy_filetype', force_semantic = True ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'foo' ), CompletionEntryMatcher( 'bar' ), CompletionEntryMatcher( 'qux' ) ) ) @SharedYcmd def GetCompletions_ForceSemantic_NoSemanticCompleter_test( app, *args ): event_data = BuildRequest( event_name = 'FileReadyToParse', filetype = 'dummy_filetype', contents = 'complete_this_word\ncom' ) app.post_json( '/event_notification', event_data ) completion_data = BuildRequest( filetype = 'dummy_filetype', force_semantic = True, contents = 'complete_this_word\ncom', line_number = 2, column_num = 4 ) results = app.post_json( '/completions', completion_data ).json assert_that( results, has_entries( { 'completions': empty(), 'errors': empty(), } ) ) # For proof, show that non-forced completion would return identifiers completion_data = BuildRequest( filetype = 'dummy_filetype', contents = 'complete_this_word\ncom', line_number = 2, column_num = 4 ) results = app.post_json( '/completions', completion_data ).json assert_that( results, has_entries( { 'completions': contains_exactly( CompletionEntryMatcher( 'com' ), CompletionEntryMatcher( 'complete_this_word' ) ), 'errors': empty(), } ) ) @SharedYcmd def GetCompletions_IdentifierCompleter_SyntaxKeywordsAdded_test( app ): event_data = BuildRequest( event_name = 'FileReadyToParse', syntax_keywords = [ 'foo', 'bar', 'zoo' ] ) app.post_json( '/event_notification', event_data ) completion_data = BuildRequest( contents = 'oo ', column_num = 3 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'foo' ), CompletionEntryMatcher( 'zoo' ) ) ) @SharedYcmd def GetCompletions_IdentifierCompleter_TagsAdded_test( app ): event_data = BuildRequest( event_name = 'FileReadyToParse', tag_files = [ PathToTestFile( 'basic.tags' ) ] ) app.post_json( '/event_notification', event_data ) completion_data = BuildRequest( contents = 'oo', column_num = 3, filetype = 'cpp' ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'foosy' ), CompletionEntryMatcher( 'fooaaa' ) ) ) @SharedYcmd def GetCompletions_IdentifierCompleter_JustFinishedIdentifier_test( app ): event_data = BuildRequest( event_name = 'CurrentIdentifierFinished', column_num = 4, contents = 'foo' ) app.post_json( '/event_notification', event_data ) completion_data = BuildRequest( contents = 'oo', column_num = 3 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'foo' ) ) ) @IsolatedYcmd() def GetCompletions_IdentifierCompleter_IgnoreFinishedIdentifierInString_test( app ): event_data = BuildRequest( event_name = 'CurrentIdentifierFinished', column_num = 6, contents = '"foo"' ) app.post_json( '/event_notification', event_data ) completion_data = BuildRequest( contents = 'oo', column_num = 3 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, empty() ) @SharedYcmd def GetCompletions_IdentifierCompleter_IdentifierUnderCursor_test( app ): event_data = BuildRequest( event_name = 'InsertLeave', column_num = 2, contents = 'foo' ) app.post_json( '/event_notification', event_data ) completion_data = BuildRequest( contents = 'oo', column_num = 3 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'foo' ) ) ) @IsolatedYcmd() def GetCompletions_IdentifierCompleter_IgnoreCursorIdentifierInString_test( app ): event_data = BuildRequest( event_name = 'InsertLeave', column_num = 3, contents = '"foo"' ) app.post_json( '/event_notification', event_data ) completion_data = BuildRequest( contents = 'oo', column_num = 3 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, empty() ) @SharedYcmd def GetCompletions_FilenameCompleter_Works_test( app ): filepath = PathToTestFile( 'filename_completer', 'test.foo' ) completion_data = BuildRequest( filepath = filepath, contents = './', column_num = 3 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'inner_dir', '[Dir]' ) ) ) @SharedYcmd def GetCompletions_FilenameCompleter_FallBackToIdentifierCompleter_test( app ): filepath = PathToTestFile( 'filename_completer', 'test.foo' ) event_data = BuildRequest( filepath = filepath, contents = './nonexisting_dir', filetype = 'foo', event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', event_data ) completion_data = BuildRequest( filepath = filepath, contents = './nonexisting_dir nd', filetype = 'foo', column_num = 21 ) assert_that( app.post_json( '/completions', completion_data ).json[ 'completions' ], has_items( CompletionEntryMatcher( 'nonexisting_dir', '[ID]' ) ) ) @SharedYcmd def GetCompletions_UltiSnipsCompleter_Works_test( app ): event_data = BuildRequest( event_name = 'BufferVisit', ultisnips_snippets = [ { 'trigger': 'foo', 'description': 'bar' }, { 'trigger': 'zoo', 'description': 'goo' }, ] ) app.post_json( '/event_notification', event_data ) completion_data = BuildRequest( contents = 'oo ', column_num = 3 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'foo', extra_menu_info='<snip> bar' ), CompletionEntryMatcher( 'zoo', extra_menu_info='<snip> goo' ) ) ) @IsolatedYcmd( { 'use_ultisnips_completer': 0 } ) def GetCompletions_UltiSnipsCompleter_UnusedWhenOffWithOption_test( app ): event_data = BuildRequest( event_name = 'BufferVisit', ultisnips_snippets = [ { 'trigger': 'foo', 'description': 'bar' }, { 'trigger': 'zoo', 'description': 'goo' }, ] ) app.post_json( '/event_notification', event_data ) completion_data = BuildRequest( contents = 'oo ', column_num = 3 ) assert_that( app.post_json( '/completions', completion_data ).json, has_entries( { 'completions': empty() } ) ) @IsolatedYcmd( { 'semantic_triggers': { 'dummy_filetype': [ '_' ] } } ) @patch( 'ycmd.tests.test_utils.DummyCompleter.CandidatesList', return_value = [ 'some_candidate' ] ) def GetCompletions_SemanticCompleter_WorksWhenTriggerIsIdentifier_test( candidates, app ): with PatchCompleter( DummyCompleter, 'dummy_filetype' ): completion_data = BuildRequest( filetype = 'dummy_filetype', contents = 'some_can', column_num = 9 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'some_candidate' ) ) ) @SharedYcmd @patch( 'ycmd.tests.test_utils.DummyCompleter.ShouldUseNowInner', return_value = True ) @patch( 'ycmd.tests.test_utils.DummyCompleter.CandidatesList', return_value = [ 'attribute' ] ) def GetCompletions_CacheIsValid_test( candidates_list, should_use, app ): with PatchCompleter( DummyCompleter, 'dummy_filetype' ): completion_data = BuildRequest( filetype = 'dummy_filetype', contents = 'object.attr', line_num = 1, column_num = 12 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'attribute' ) ) ) completion_data = BuildRequest( filetype = 'dummy_filetype', contents = 'object.attri', line_num = 1, column_num = 13 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'attribute' ) ) ) # We ask for candidates only once because of cache. assert_that( candidates_list.call_count, equal_to( 1 ) ) @SharedYcmd @patch( 'ycmd.tests.test_utils.DummyCompleter.ShouldUseNowInner', return_value = True ) @patch( 'ycmd.tests.test_utils.DummyCompleter.CandidatesList', side_effect = [ [ 'attributeA' ], [ 'attributeB' ] ] ) def GetCompletions_CacheIsNotValid_DifferentLineNumber_test( candidates_list, should_use, app ): with PatchCompleter( DummyCompleter, 'dummy_filetype' ): completion_data = BuildRequest( filetype = 'dummy_filetype', contents = 'objectA.attr\n' 'objectB.attr', line_num = 1, column_num = 12 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'attributeA' ) ) ) completion_data = BuildRequest( filetype = 'dummy_filetype', contents = 'objectA.\n' 'objectB.', line_num = 2, column_num = 12 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'attributeB' ) ) ) # We ask for candidates twice because of cache invalidation: # line numbers are different between requests. assert_that( candidates_list.call_count, equal_to( 2 ) ) @SharedYcmd @patch( 'ycmd.tests.test_utils.DummyCompleter.ShouldUseNowInner', return_value = True ) @patch( 'ycmd.tests.test_utils.DummyCompleter.CandidatesList', side_effect = [ [ 'attributeA' ], [ 'attributeB' ] ] ) def GetCompletions_CacheIsNotValid_DifferentStartColumn_test( candidates_list, should_use, app ): with PatchCompleter( DummyCompleter, 'dummy_filetype' ): # Start column is 9 completion_data = BuildRequest( filetype = 'dummy_filetype', contents = 'objectA.attr', line_num = 1, column_num = 12 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'attributeA' ) ) ) # Start column is 8 completion_data = BuildRequest( filetype = 'dummy_filetype', contents = 'object.attri', line_num = 1, column_num = 12 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'attributeB' ) ) ) # We ask for candidates twice because of cache invalidation: # start columns are different between requests. assert_that( candidates_list.call_count, equal_to( 2 ) ) @SharedYcmd @patch( 'ycmd.tests.test_utils.DummyCompleter.ShouldUseNowInner', return_value = True ) @patch( 'ycmd.tests.test_utils.DummyCompleter.CandidatesList', side_effect = [ [ 'attributeA' ], [ 'attributeB' ] ] ) def GetCompletions_CacheIsNotValid_DifferentForceSemantic_test( candidates_list, should_use, app ): with PatchCompleter( DummyCompleter, 'dummy_filetype' ): completion_data = BuildRequest( filetype = 'dummy_filetype', contents = 'objectA.attr', line_num = 1, column_num = 12, force_semantic = True ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'attributeA' ) ) ) completion_data = BuildRequest( filetype = 'dummy_filetype', contents = 'objectA.attr', line_num = 1, column_num = 12 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'attributeB' ) ) ) # We ask for candidates twice because of cache invalidation: # semantic completion is forced for one of the request, not the other. assert_that( candidates_list.call_count, equal_to( 2 ) ) @SharedYcmd @patch( 'ycmd.tests.test_utils.DummyCompleter.ShouldUseNowInner', return_value = True ) @patch( 'ycmd.tests.test_utils.DummyCompleter.CandidatesList', side_effect = [ [ 'attributeA' ], [ 'attributeB' ] ] ) def GetCompletions_CacheIsNotValid_DifferentContents_test( candidates_list, should_use, app ): with PatchCompleter( DummyCompleter, 'dummy_filetype' ): completion_data = BuildRequest( filetype = 'dummy_filetype', contents = 'objectA = foo\n' 'objectA.attr', line_num = 2, column_num = 12 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'attributeA' ) ) ) completion_data = BuildRequest( filetype = 'dummy_filetype', contents = 'objectA = bar\n' 'objectA.attr', line_num = 2, column_num = 12 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'attributeB' ) ) ) # We ask for candidates twice because of cache invalidation: # both requests have the same cursor position and current line but file # contents are different. assert_that( candidates_list.call_count, equal_to( 2 ) ) @SharedYcmd @patch( 'ycmd.tests.test_utils.DummyCompleter.ShouldUseNowInner', return_value = True ) @patch( 'ycmd.tests.test_utils.DummyCompleter.CandidatesList', side_effect = [ [ 'attributeA' ], [ 'attributeB' ] ] ) def GetCompletions_CacheIsNotValid_DifferentNumberOfLines_test( candidates_list, should_use, app ): with PatchCompleter( DummyCompleter, 'dummy_filetype' ): completion_data = BuildRequest( filetype = 'dummy_filetype', contents = 'objectA.attr\n' 'objectB.attr', line_num = 1, column_num = 12 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'attributeA' ) ) ) completion_data = BuildRequest( filetype = 'dummy_filetype', contents = 'objectA.attr', line_num = 1, column_num = 12 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'attributeB' ) ) ) # We ask for candidates twice because of cache invalidation: # both requests have the same cursor position and current line but the # number of lines in the current file is different. assert_that( candidates_list.call_count, equal_to( 2 ) ) @SharedYcmd @patch( 'ycmd.tests.test_utils.DummyCompleter.ShouldUseNowInner', return_value = True ) @patch( 'ycmd.tests.test_utils.DummyCompleter.CandidatesList', side_effect = [ [ 'attributeA' ], [ 'attributeB' ] ] ) def GetCompletions_CacheIsNotValid_DifferentFileData_test( candidates_list, should_use, app ): with PatchCompleter( DummyCompleter, 'dummy_filetype' ): completion_data = BuildRequest( filetype = 'dummy_filetype', contents = 'objectA.attr', line_num = 1, column_num = 12, file_data = { '/bar': { 'contents': 'objectA = foo', 'filetypes': [ 'dummy_filetype' ] } } ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'attributeA' ) ) ) completion_data = BuildRequest( filetype = 'dummy_filetype', contents = 'objectA.attr', line_num = 1, column_num = 12, file_data = { '/bar': { 'contents': 'objectA = bar', 'filetypes': [ 'dummy_filetype' ] } } ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'attributeB' ) ) ) # We ask for candidates twice because of cache invalidation: # both requests have the same cursor position and contents for the current # file but different contents for another file. assert_that( candidates_list.call_count, equal_to( 2 ) ) @SharedYcmd @patch( 'ycmd.tests.test_utils.DummyCompleter.ShouldUseNowInner', return_value = True ) @patch( 'ycmd.tests.test_utils.DummyCompleter.CandidatesList', side_effect = [ [ 'attributeA' ], [ 'attributeB' ] ] ) def GetCompletions_CacheIsNotValid_DifferentExtraConfData_test( candidates_list, should_use, app ): with PatchCompleter( DummyCompleter, 'dummy_filetype' ): completion_data = BuildRequest( filetype = 'dummy_filetype', contents = 'objectA.attr', line_num = 1, column_num = 12 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'attributeA' ) ) ) completion_data = BuildRequest( filetype = 'dummy_filetype', contents = 'objectA.attr', line_num = 1, column_num = 12, extra_conf_data = { 'key': 'value' } ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'attributeB' ) ) ) # We ask for candidates twice because of cache invalidation: # both requests are identical except the extra conf data. assert_that( candidates_list.call_count, equal_to( 2 ) ) @SharedYcmd @patch( 'ycmd.tests.test_utils.DummyCompleter.ShouldUseNowInner', return_value = True ) @patch( 'ycmd.tests.test_utils.DummyCompleter.CandidatesList', return_value = [ 'aba', 'cbc' ] ) def GetCompletions_FilterThenReturnFromCache_test( candidates_list, should_use, app ): with PatchCompleter( DummyCompleter, 'dummy_filetype' ): # First, fill the cache with an empty query completion_data = BuildRequest( filetype = 'dummy_filetype', contents = 'objectA.', line_num = 1, column_num = 9 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'aba' ), CompletionEntryMatcher( 'cbc' ) ) ) # Now, filter them. This causes them to be converted to bytes and back completion_data = BuildRequest( filetype = 'dummy_filetype', contents = 'objectA.c', line_num = 1, column_num = 10 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'cbc' ) ) ) # Finally, request the original (unfiltered) set again. Ensure that we get # proper results (not some bytes objects) completion_data = BuildRequest( filetype = 'dummy_filetype', contents = 'objectA.', line_num = 1, column_num = 9 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'aba' ), CompletionEntryMatcher( 'cbc' ) ) ) assert_that( candidates_list.call_count, equal_to( 1 ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/go/��������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0017642�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/go/__init__.py���������������������������������������������0000664�0000000�0000000�00000001327�13746324601�0021756�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from ycmd.tests.go.conftest import * # noqa ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/go/conftest.py���������������������������������������������0000664�0000000�0000000�00000007470�13746324601�0022051�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import os import pytest from ycmd.tests.test_utils import ( BuildRequest, ClearCompletionsCache, IgnoreExtraConfOutsideTestsFolder, IsolatedApp, SetUpApp, StopCompleterServer, WaitUntilCompleterServerReady ) shared_app = None @pytest.fixture( scope='module', autouse=True ) def set_up_shared_app(): global shared_app shared_app = SetUpApp() with IgnoreExtraConfOutsideTestsFolder(): StartGoCompleterServerInDirectory( shared_app, PathToTestFile() ) yield StopCompleterServer( shared_app, 'go' ) def StartGoCompleterServerInDirectory( app, directory ): app.post_json( '/event_notification', BuildRequest( filepath = os.path.join( directory, 'goto.go' ), event_name = 'FileReadyToParse', filetype = 'go' ) ) WaitUntilCompleterServerReady( app, 'go' ) @pytest.fixture def app( request ): which = request.param[ 0 ] assert which == 'isolated' or which == 'shared' if which == 'isolated': with IsolatedApp( {} ) as app: yield app StopCompleterServer( app, 'go' ) else: global shared_app ClearCompletionsCache() with IgnoreExtraConfOutsideTestsFolder(): yield shared_app """Defines a decorator to be attached to tests of this package. This decorator passes the shared ycmd application as a parameter.""" SharedYcmd = pytest.mark.parametrize( # Name of the fixture/function argument 'app', # Fixture parameters, passed to app() as request.param [ ( 'shared', ) ], # Non-empty ids makes fixture parameters visible in pytest verbose output ids = [ '' ], # Execute the fixture, instead of passing parameters directly to the # function argument indirect = True ) """Defines a decorator to be attached to tests of this package. This decorator passes a unique ycmd application as a parameter. It should be used on tests that change the server state in a irreversible way (ex: a semantic subserver is stopped or restarted) or expect a clean state (ex: no semantic subserver started, no .ycm_extra_conf.py loaded, etc). Use the optional parameter |custom_options| to give additional options and/or override the default ones. Example usage: from ycmd.tests.python import IsolatedYcmd @IsolatedYcmd( { 'python_binary_path': '/some/path' } ) def CustomPythonBinaryPath_test( app ): ... """ IsolatedYcmd = pytest.mark.parametrize( # Name of the fixture/function argument 'app', # Fixture parameters, passed to app() as request.param [ ( 'isolated', ) ], # Non-empty ids makes fixture parameters visible in pytest verbose output ids = [ '' ], # Execute the fixture, instead of passing parameters directly to the # function argument indirect = True ) def PathToTestFile( *args ): dir_of_current_script = os.path.dirname( os.path.abspath( __file__ ) ) # GOPLS doesn't work if any parent directory is named "testdata" return os.path.join( dir_of_current_script, 'go_module', *args ) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/go/debug_info_test.py��������������������������������������0000664�0000000�0000000�00000007171�13746324601�0023362�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2016-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_exactly, has_entries, has_entry, instance_of, matches_regexp ) from ycmd.tests.go import ( IsolatedYcmd, PathToTestFile, SharedYcmd, StartGoCompleterServerInDirectory ) from ycmd.tests.test_utils import BuildRequest @SharedYcmd def DebugInfo_test( app ): request_data = BuildRequest( filetype = 'go' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'Go', 'servers': contains_exactly( has_entries( { 'name': 'gopls', 'is_running': instance_of( bool ), 'executable': contains_exactly( instance_of( str ), instance_of( str ), instance_of( str ), instance_of( str ) ), 'address': None, 'port': None, 'pid': instance_of( int ), 'logfiles': contains_exactly( instance_of( str ) ), 'extras': contains_exactly( has_entries( { 'key': 'Server State', 'value': instance_of( str ), } ), has_entries( { 'key': 'Project Directory', 'value': PathToTestFile(), } ), has_entries( { 'key': 'Settings', 'value': matches_regexp( '{\n "hoverKind": "Structured"\n}' ) } ), ) } ) ), } ) ) ) @IsolatedYcmd def DebugInfo_ProjectDirectory_test( app ): project_dir = PathToTestFile( 'td' ) StartGoCompleterServerInDirectory( app, project_dir ) assert_that( app.post_json( '/debug_info', BuildRequest( filetype = 'go' ) ).json, has_entry( 'completer', has_entries( { 'name': 'Go', 'servers': contains_exactly( has_entries( { 'name': 'gopls', 'is_running': instance_of( bool ), 'executable': contains_exactly( instance_of( str ), instance_of( str ), instance_of( str ), instance_of( str ) ), 'address': None, 'port': None, 'pid': instance_of( int ), 'logfiles': contains_exactly( instance_of( str ) ), 'extras': contains_exactly( has_entries( { 'key': 'Server State', 'value': instance_of( str ), } ), has_entries( { 'key': 'Project Directory', 'value': PathToTestFile(), } ), has_entries( { 'key': 'Settings', 'value': matches_regexp( '{\n "hoverKind": "Structured"\n}' ) } ), ) } ) ), } ) ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/go/diagnostics_test.py�������������������������������������0000664�0000000�0000000�00000010450�13746324601�0023562�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_exactly, contains_inanyorder, has_entries, has_entry ) from pprint import pformat import json from ycmd.tests.go import PathToTestFile, SharedYcmd from ycmd.tests.test_utils import ( BuildRequest, LocationMatcher, PollForMessages, PollForMessagesTimeoutException, RangeMatcher, WaitForDiagnosticsToBeReady, WithRetry ) from ycmd.utils import ReadFile MAIN_FILEPATH = PathToTestFile( 'goto.go' ) DIAG_MATCHERS_PER_FILE = { MAIN_FILEPATH: contains_inanyorder( has_entries( { 'kind': 'ERROR', 'text': 'undeclared name: diagnostics_test', 'location': LocationMatcher( MAIN_FILEPATH, 12, 5 ), 'location_extent': RangeMatcher( MAIN_FILEPATH, ( 12, 5 ), ( 12, 21 ) ), 'ranges': contains_exactly( RangeMatcher( MAIN_FILEPATH, ( 12, 5 ), ( 12, 21 ) ) ), 'fixit_available': False } ) ) } @WithRetry @SharedYcmd def Diagnostics_DetailedDiags_test( app ): filepath = PathToTestFile( 'goto.go' ) contents = ReadFile( filepath ) WaitForDiagnosticsToBeReady( app, filepath, contents, 'go' ) request_data = BuildRequest( contents = contents, filepath = filepath, filetype = 'go', line_num = 12, column_num = 5 ) results = app.post_json( '/detailed_diagnostic', request_data ).json assert_that( results, has_entry( 'message', 'undeclared name: diagnostics_test' ) ) @WithRetry @SharedYcmd def Diagnostics_FileReadyToParse_test( app ): filepath = PathToTestFile( 'goto.go' ) contents = ReadFile( filepath ) # It can take a while for the diagnostics to be ready. results = WaitForDiagnosticsToBeReady( app, filepath, contents, 'go' ) print( f'completer response: { pformat( results ) }' ) assert_that( results, DIAG_MATCHERS_PER_FILE[ filepath ] ) @WithRetry @SharedYcmd def Diagnostics_Poll_test( app ): filepath = PathToTestFile( 'goto.go' ) contents = ReadFile( filepath ) # Poll until we receive _all_ the diags asynchronously. to_see = sorted( DIAG_MATCHERS_PER_FILE.keys() ) seen = {} try: for message in PollForMessages( app, { 'filepath': filepath, 'contents': contents, 'filetype': 'go' } ): if 'diagnostics' in message: if message[ 'filepath' ] not in DIAG_MATCHERS_PER_FILE: continue seen[ message[ 'filepath' ] ] = True assert_that( message, has_entries( { 'diagnostics': DIAG_MATCHERS_PER_FILE[ message[ 'filepath' ] ], 'filepath': message[ 'filepath' ] } ) ) if sorted( seen.keys() ) == to_see: break # Eventually PollForMessages will throw a timeout exception and we'll fail # if we don't see all of the expected diags. except PollForMessagesTimeoutException as e: raise AssertionError( str( e ) + 'Timed out waiting for full set of diagnostics. ' f'Expected to see diags for { json.dumps( to_see, indent = 2 ) }, ' f'but only saw { json.dumps( sorted( seen.keys() ), indent = 2 ) }.' ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/go/get_completions_test.py���������������������������������0000664�0000000�0000000�00000006045�13746324601�0024453�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2015-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( all_of, assert_that, has_items, has_key, is_not ) from ycmd.tests.go import PathToTestFile, SharedYcmd from ycmd.tests.test_utils import ( BuildRequest, CompletionEntryMatcher, WithRetry ) from ycmd.utils import ReadFile @WithRetry( reruns = 100 ) @SharedYcmd def GetCompletions_Basic_test( app ): filepath = PathToTestFile( 'td', 'test.go' ) completion_data = BuildRequest( filepath = filepath, filetype = 'go', contents = ReadFile( filepath ), force_semantic = True, line_num = 10, column_num = 9 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, all_of( has_items( CompletionEntryMatcher( 'Llongfile', 'int', { 'detailed_info': 'Llongfile\n\n' 'These flags define which text to' ' prefix to each log entry generated' ' by the Logger.', 'menu_text': 'Llongfile', 'kind': 'Constant', } ), CompletionEntryMatcher( 'Logger', 'struct{...}', { 'detailed_info': 'Logger\n\n' 'A Logger represents an active logging' ' object that generates lines of output' ' to an io.Writer.', 'menu_text': 'Logger', 'kind': 'Struct', } ) ) ) ) # This completer does not require or support resolve assert_that( results[ 0 ], is_not( has_key( 'resolve' ) ) ) assert_that( results[ 0 ], is_not( has_key( 'item' ) ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/go/go_completer_test.py������������������������������������0000664�0000000�0000000�00000003105�13746324601�0023731�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from unittest.mock import patch from hamcrest import assert_that, equal_to from ycmd import user_options_store from ycmd.completers.go.hook import GetCompleter def GetCompleter_GoplsFound_test(): assert_that( GetCompleter( user_options_store.GetAll() ) ) @patch( 'ycmd.completers.go.go_completer.PATH_TO_GOPLS', None ) def GetCompleter_GoplsNotFound_test( *args ): assert_that( not GetCompleter( user_options_store.GetAll() ) ) @patch( 'ycmd.utils.FindExecutableWithFallback', wraps = lambda x, fb: x if x == 'gopls' else None ) @patch( 'os.path.isfile', return_value = True ) def GetCompleter_GoplsFromUserOption_test( *args ): user_options = user_options_store.GetAll().copy( gopls_binary_path = 'gopls' ) assert_that( GetCompleter( user_options )._gopls_path, equal_to( 'gopls' ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/go/go_module/����������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0021614�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/go/go_module/fixit.go��������������������������������������0000664�0000000�0000000�00000000036�13746324601�0023265�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package main import ( "fmt" ) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/go/go_module/go.mod����������������������������������������0000664�0000000�0000000�00000000051�13746324601�0022716�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������module example.com/owner/module go 1.12 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/go/go_module/goto.go���������������������������������������0000664�0000000�0000000�00000000151�13746324601�0023110�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package main func dummy() { } func main() { dummy() //GoTo } func foo() { diagnostics_test } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/go/go_module/td/�������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0022223�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/go/go_module/td/signature_help.go��������������������������0000664�0000000�0000000�00000000205�13746324601�0025560�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package td import "fmt" func add(x int, y int) int { return x + y } func main() { fmt.Println(add(42, 13)) does_not_exist( } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/go/go_module/td/test.go������������������������������������0000664�0000000�0000000�00000000201�13746324601�0023522�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Package td is dummy data for gopls completion test. package td import ( "log" ) // Now with doc! func Hello() { log.Log } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/go/go_module/thing.go��������������������������������������0000664�0000000�0000000�00000000142�13746324601�0023251�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package main type thinger interface { DoThing() } type thing string func (thing) DoThing() {} ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/go/go_module/unicode/��������������������������������������0000775�0000000�0000000�00000000000�13746324601�0023242�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/go/go_module/unicode/unicode.go����������������������������0000664�0000000�0000000�00000000336�13746324601�0025221�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package unicode import ( "fmt" ) type Test struct { Unicøde int } func main() { const test = `unicøde`; fmt.prf const test2 = `†es†`; fmt.erro å_test := Test{ Unicøde: 10, } å_test. } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/go/server_management_test.py�������������������������������0000664�0000000�0000000�00000010560�13746324601�0024757�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from hamcrest import assert_that, contains_exactly, equal_to, has_entry from unittest.mock import patch from ycmd.completers.language_server.language_server_completer import ( LanguageServerConnectionTimeout ) from ycmd.tests.go import ( PathToTestFile, IsolatedYcmd, StartGoCompleterServerInDirectory ) from ycmd.tests.test_utils import ( BuildRequest, MockProcessTerminationTimingOut, WaitUntilCompleterServerReady ) def AssertGoCompleterServerIsRunning( app, is_running ): request_data = BuildRequest( filetype = 'go' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entry( 'servers', contains_exactly( has_entry( 'is_running', is_running ) ) ) ) ) @IsolatedYcmd def ServerManagement_RestartServer_test( app ): filepath = PathToTestFile( 'goto.go' ) StartGoCompleterServerInDirectory( app, filepath ) AssertGoCompleterServerIsRunning( app, True ) app.post_json( '/run_completer_command', BuildRequest( filepath = filepath, filetype = 'go', command_arguments = [ 'RestartServer' ], ), ) WaitUntilCompleterServerReady( app, 'go' ) AssertGoCompleterServerIsRunning( app, True ) @IsolatedYcmd @patch( 'shutil.rmtree', side_effect = OSError ) @patch( 'ycmd.utils.WaitUntilProcessIsTerminated', MockProcessTerminationTimingOut ) def ServerManagement_CloseServer_Unclean_test( rm_tree, app ): StartGoCompleterServerInDirectory( app, PathToTestFile() ) app.post_json( '/run_completer_command', BuildRequest( filetype = 'go', command_arguments = [ 'StopServer' ] ) ) request_data = BuildRequest( filetype = 'go' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entry( 'servers', contains_exactly( has_entry( 'is_running', False ) ) ) ) ) @IsolatedYcmd def ServerManagement_StopServerTwice_test( app ): StartGoCompleterServerInDirectory( app, PathToTestFile() ) app.post_json( '/run_completer_command', BuildRequest( filetype = 'go', command_arguments = [ 'StopServer' ], ), ) AssertGoCompleterServerIsRunning( app, False ) # Stopping a stopped server is a no-op app.post_json( '/run_completer_command', BuildRequest( filetype = 'go', command_arguments = [ 'StopServer' ], ), ) AssertGoCompleterServerIsRunning( app, False ) @IsolatedYcmd def ServerManagement_StartServer_Fails_test( app ): with patch( 'ycmd.completers.language_server.language_server_completer.' 'LanguageServerConnection.AwaitServerConnection', side_effect = LanguageServerConnectionTimeout ): resp = app.post_json( '/event_notification', BuildRequest( event_name = 'FileReadyToParse', filetype = 'go', filepath = PathToTestFile( 'goto.go' ), contents = "" ) ) assert_that( resp.status_code, equal_to( 200 ) ) request_data = BuildRequest( filetype = 'go' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entry( 'servers', contains_exactly( has_entry( 'is_running', False ) ) ) ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/go/signature_help_test.py����������������������������������0000664�0000000�0000000�00000012052�13746324601�0024264�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from hamcrest import assert_that, contains_exactly, empty, equal_to, has_entries import requests from ycmd.utils import ReadFile from ycmd.tests.go import PathToTestFile, SharedYcmd from ycmd.tests.test_utils import ( CombineRequest, ParameterMatcher, SignatureMatcher, SignatureAvailableMatcher, WaitUntilCompleterServerReady ) def ProjectPath( *args ): return PathToTestFile( 'extra_confs', 'simple_extra_conf_project', 'src', *args ) def RunTest( app, test ): """ Method to run a simple signature help test and verify the result test is a dictionary containing: 'request': kwargs for BuildRequest 'expect': { 'response': server response code (e.g. httplib.OK) 'data': matcher for the server response json } """ contents = ReadFile( test[ 'request' ][ 'filepath' ] ) app.post_json( '/event_notification', CombineRequest( test[ 'request' ], { 'event_name': 'FileReadyToParse', 'contents': contents, } ), expect_errors = True ) # We ignore errors here and we check the response code ourself. # This is to allow testing of requests returning errors. response = app.post_json( '/signature_help', CombineRequest( test[ 'request' ], { 'contents': contents } ), expect_errors = True ) assert_that( response.status_code, equal_to( test[ 'expect' ][ 'response' ] ) ) assert_that( response.json, test[ 'expect' ][ 'data' ] ) @SharedYcmd def SignatureHelp_NoParams_test( app ): RunTest( app, { 'description': 'Trigger after (', 'request': { 'filetype' : 'go', 'filepath' : PathToTestFile( 'goto.go' ), 'line_num' : 8, 'column_num': 11, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': contains_exactly( SignatureMatcher( 'dummy()', [] ) ), } ), } ) } } ) @SharedYcmd def SignatureHelp_NullResponse_test( app ): RunTest( app, { 'description': 'No error on null response', 'request': { 'filetype' : 'go', 'filepath' : PathToTestFile( 'td', 'signature_help.go' ), 'line_num' : 11, 'column_num': 17, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': empty(), } ), } ) } } ) @SharedYcmd def SignatureHelp_MethodTrigger_test( app ): RunTest( app, { 'description': 'Trigger after (', 'request': { 'filetype' : 'go', 'filepath' : PathToTestFile( 'td', 'signature_help.go' ), 'line_num' : 10, 'column_num': 18, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': contains_exactly( SignatureMatcher( 'add(x int, y int) int', [ ParameterMatcher( 4, 9 ), ParameterMatcher( 11, 16 ) ] ) ), } ), } ) } } ) @SharedYcmd def Signature_Help_Available_test( app ): request = { 'filepath' : PathToTestFile( 'td', 'signature_help.go' ) } app.post_json( '/event_notification', CombineRequest( request, { 'event_name': 'FileReadyToParse', 'filetype': 'go' } ), expect_errors = True ) WaitUntilCompleterServerReady( app, 'go' ) response = app.get( '/signature_help_available', { 'subserver': 'go' } ).json assert_that( response, SignatureAvailableMatcher( 'YES' ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/go/subcommands_test.py�������������������������������������0000664�0000000�0000000�00000034711�13746324601�0023574�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2015-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_exactly, contains_inanyorder, empty, equal_to, has_entries, has_entry, matches_regexp ) from unittest.mock import patch from pprint import pformat import os import pytest import requests from ycmd import handlers from ycmd.completers.language_server.language_server_completer import ( ResponseFailedException ) from ycmd.tests.go import ( PathToTestFile, SharedYcmd, StartGoCompleterServerInDirectory ) from ycmd.tests.test_utils import ( BuildRequest, ChunkMatcher, ErrorMatcher, ExpectedFailure, LocationMatcher ) from ycmd.utils import ReadFile RESPONSE_TIMEOUT = 5 def RunTest( app, test, contents = None ): if not contents: contents = ReadFile( test[ 'request' ][ 'filepath' ] ) def CombineRequest( request, data ): kw = request request.update( data ) return BuildRequest( **kw ) # Because we aren't testing this command, we *always* ignore errors. This # is mainly because we (may) want to test scenarios where the completer # throws an exception and the easiest way to do that is to throw from # within the FlagsForFile function. app.post_json( '/event_notification', CombineRequest( test[ 'request' ], { 'event_name': 'FileReadyToParse', 'contents': contents, 'filetype': 'go', } ), expect_errors = True ) # We also ignore errors here, but then we check the response code # ourself. This is to allow testing of requests returning errors. response = app.post_json( '/run_completer_command', CombineRequest( test[ 'request' ], { 'completer_target': 'filetype_default', 'contents': contents, 'filetype': 'go', 'command_arguments': ( [ test[ 'request' ][ 'command' ] ] + test[ 'request' ].get( 'arguments', [] ) ) } ), expect_errors = True ) print( f'completer response: { pformat( response.json ) }' ) assert_that( response.status_code, equal_to( test[ 'expect' ][ 'response' ] ) ) assert_that( response.json, test[ 'expect' ][ 'data' ] ) def RunFixItTest( app, description, filepath, line, col, fixits_for_line ): RunTest( app, { 'description': description, 'request': { 'command': 'FixIt', 'line_num': line, 'column_num': col, 'filepath': filepath, }, 'expect': { 'response': requests.codes.ok, 'data': fixits_for_line, } } ) @SharedYcmd def Subcommands_DefinedSubcommands_test( app ): subcommands_data = BuildRequest( completer_target = 'go' ) assert_that( app.post_json( '/defined_subcommands', subcommands_data ).json, contains_inanyorder( 'Format', 'GetDoc', 'GetType', 'RefactorRename', 'GoTo', 'GoToDeclaration', 'GoToDefinition', 'GoToReferences', 'GoToImplementation', 'GoToType', 'GoToSymbol', 'FixIt', 'RestartServer', 'ExecuteCommand' ) ) @SharedYcmd def Subcommands_ServerNotInitialized_test( app ): filepath = PathToTestFile( 'goto.go' ) completer = handlers._server_state.GetFiletypeCompleter( [ 'go' ] ) @patch.object( completer, '_ServerIsInitialized', return_value = False ) def Test( app, cmd, arguments, *args ): RunTest( app, { 'description': 'Subcommand ' + cmd + ' handles server not ready', 'request': { 'command': cmd, 'line_num': 1, 'column_num': 1, 'filepath': filepath, 'arguments': arguments, }, 'expect': { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( RuntimeError, 'Server is initializing. Please wait.' ), } } ) Test( app, 'Format', [] ) Test( app, 'GetDoc', [] ) Test( app, 'GetType', [] ) Test( app, 'GoTo', [] ) Test( app, 'GoToDeclaration', [] ) Test( app, 'GoToDefinition', [] ) Test( app, 'GoToType', [] ) Test( app, 'FixIt', [] ) @SharedYcmd def Subcommands_Format_WholeFile_test( app ): # RLS can't execute textDocument/formatting if any file # under the project root has errors, so we need to use # a different project just for formatting. # For further details check https://github.com/go-lang/rls/issues/1397 project_dir = PathToTestFile() StartGoCompleterServerInDirectory( app, project_dir ) filepath = os.path.join( project_dir, 'goto.go' ) RunTest( app, { 'description': 'Formatting is applied on the whole file', 'request': { 'command': 'Format', 'filepath': filepath, 'options': { 'tab_size': 2, 'insert_spaces': True } }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( '', LocationMatcher( filepath, 8, 1 ), LocationMatcher( filepath, 8, 5 ) ), ChunkMatcher( '\t', LocationMatcher( filepath, 8, 5 ), LocationMatcher( filepath, 8, 5 ) ), ChunkMatcher( '', LocationMatcher( filepath, 12, 1 ), LocationMatcher( filepath, 12, 5 ) ), ChunkMatcher( '\t', LocationMatcher( filepath, 12, 5 ), LocationMatcher( filepath, 12, 5 ) ), ) } ) ) } ) } } ) @ExpectedFailure( 'rangeFormat is not yet implemented', matches_regexp( '\nExpected: <200>\n but: was <500>\n' ) ) @SharedYcmd def Subcommands_Format_Range_test( app ): project_dir = PathToTestFile() StartGoCompleterServerInDirectory( app, project_dir ) filepath = os.path.join( project_dir, 'goto.go' ) RunTest( app, { 'description': 'Formatting is applied on some part of the file', 'request': { 'command': 'Format', 'filepath': filepath, 'range': { 'start': { 'line_num': 7, 'column_num': 1, }, 'end': { 'line_num': 9, 'column_num': 2 } }, 'options': { 'tab_size': 4, 'insert_spaces': False } }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( 'fn unformatted_function(param: bool) -> bool {\n' '\treturn param;\n' '}\n' '\n' 'fn \n' 'main()\n' ' {\n' ' unformatted_function( false );\n' '}\n', LocationMatcher( filepath, 1, 1 ), LocationMatcher( filepath, 9, 1 ) ), ) } ) ) } ) } } ) @SharedYcmd def Subcommands_GetDoc_UnknownType_test( app ): RunTest( app, { 'description': 'GetDoc on a unknown type raises an error', 'request': { 'command': 'GetDoc', 'line_num': 2, 'column_num': 4, 'filepath': PathToTestFile( 'td', 'test.go' ), }, 'expect': { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( RuntimeError, 'No documentation available.' ) } } ) @SharedYcmd def Subcommands_GetDoc_Function_test( app ): RunTest( app, { 'description': 'GetDoc on a function returns its type', 'request': { 'command': 'GetDoc', 'line_num': 9, 'column_num': 6, 'filepath': PathToTestFile( 'td', 'test.go' ), }, 'expect': { 'response': requests.codes.ok, 'data': has_entry( 'detailed_info', 'func Hello()\nNow with doc!' ), } } ) @SharedYcmd def Subcommands_GetType_UnknownType_test( app ): RunTest( app, { 'description': 'GetType on a unknown type raises an error', 'request': { 'command': 'GetType', 'line_num': 2, 'column_num': 4, 'filepath': PathToTestFile( 'td', 'test.go' ), }, 'expect': { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( RuntimeError, 'Unknown type.' ) } } ) @SharedYcmd def Subcommands_GetType_Function_test( app ): RunTest( app, { 'description': 'GetType on a function returns its type', 'request': { 'command': 'GetType', 'line_num': 9, 'column_num': 6, 'filepath': PathToTestFile( 'td', 'test.go' ), }, 'expect': { 'response': requests.codes.ok, 'data': has_entry( 'message', 'func Hello()' ), } } ) def RunGoToTest( app, command, test ): folder = PathToTestFile() filepath = PathToTestFile( test[ 'req' ][ 0 ] ) request = { 'command': command, 'line_num': test[ 'req' ][ 1 ], 'column_num': test[ 'req' ][ 2 ], 'filepath': filepath, } response = test[ 'res' ] if isinstance( response, list ): expect = { 'response': requests.codes.ok, 'data': contains_exactly( *[ LocationMatcher( os.path.join( folder, location[ 0 ] ), location[ 1 ], location[ 2 ] ) for location in response ] ) } elif isinstance( response, tuple ): expect = { 'response': requests.codes.ok, 'data': LocationMatcher( os.path.join( folder, response[ 0 ] ), response[ 1 ], response[ 2 ] ) } else: expect = { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( ResponseFailedException ) } RunTest( app, { 'request': request, 'expect' : expect } ) @pytest.mark.parametrize( 'command', [ 'GoToDeclaration', 'GoToDefinition', 'GoTo' ] ) @pytest.mark.parametrize( 'test', [ # Struct { 'req': ( os.path.join( 'unicode', 'unicode.go' ), 13, 5 ), 'res': ( os.path.join( 'unicode', 'unicode.go' ), 10, 5 ) }, # Function { 'req': ( 'goto.go', 8, 5 ), 'res': ( 'goto.go', 3, 6 ) }, # Keyword { 'req': ( 'goto.go', 3, 2 ), 'res': 'Cannot jump to location' }, ] ) @SharedYcmd def Subcommands_GoTo_test( app, command, test ): RunGoToTest( app, command, test ) @pytest.mark.parametrize( 'test', [ # Works { 'req': ( os.path.join( 'unicode', 'unicode.go' ), 13, 5 ), 'res': ( os.path.join( 'unicode', 'unicode.go' ), 3, 6 ) }, # Fails { 'req': ( os.path.join( 'unicode', 'unicode.go' ), 11, 7 ), 'res': 'Cannot jump to location' } ] ) @SharedYcmd def Subcommands_GoToType_test( app, test ): RunGoToTest( app, 'GoToType', test ) @pytest.mark.parametrize( 'test', [ # Works { 'req': ( 'thing.go', 3, 8 ), 'res': ( 'thing.go', 7, 6 ) }, # Fails { 'req': ( 'thing.go', 12, 7 ), 'res': 'Cannot jump to location' } ] ) @SharedYcmd def Subcommands_GoToImplementation_test( app, test ): RunGoToTest( app, 'GoToImplementation', test ) @SharedYcmd def Subcommands_FixIt_NullResponse_test( app ): filepath = PathToTestFile( 'td', 'test.go' ) RunFixItTest( app, 'Gopls returned NULL for response[ \'result\' ]', filepath, 1, 1, has_entry( 'fixits', empty() ) ) @SharedYcmd def Subcommands_FixIt_Simple_test( app ): filepath = PathToTestFile( 'fixit.go' ) fixit = has_entries( { 'fixits': contains_exactly( has_entries( { 'text': "Organize Imports", 'chunks': contains_exactly( ChunkMatcher( '', LocationMatcher( filepath, 2, 1 ), LocationMatcher( filepath, 3, 1 ) ), ), 'kind': 'source.organizeImports', } ), ) } ) RunFixItTest( app, 'Only one fixit returned', filepath, 1, 1, fixit ) @SharedYcmd def Subcommands_RefactorRename_test( app ): filepath = PathToTestFile( 'unicode', 'unicode.go' ) RunTest( app, { 'description': 'RefactorRename on a function renames all its occurences', 'request': { 'command': 'RefactorRename', 'arguments': [ 'xxx' ], 'line_num': 10, 'column_num': 17, 'filepath': filepath }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'text': '', 'chunks': contains_exactly( ChunkMatcher( 'xxx', LocationMatcher( filepath, 3, 6 ), LocationMatcher( filepath, 3, 10 ) ), ChunkMatcher( 'xxx', LocationMatcher( filepath, 10, 16 ), LocationMatcher( filepath, 10, 20 ) ), ) } ) ) } ) } } ) @SharedYcmd def Subcommands_GoToReferences_test( app ): filepath = os.path.join( 'unicode', 'unicode.go' ) test = { 'req': ( filepath, 10, 5 ), 'res': [ ( filepath, 10, 5 ), ( filepath, 13, 5 ) ] } RunGoToTest( app, 'GoToReferences', test ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/hmac_utils_test.py�����������������������������������������0000664�0000000�0000000�00000005034�13746324601�0023000�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from binascii import hexlify from hamcrest import raises, assert_that, calling, equal_to from ycmd import hmac_utils as hu def CreateHmac_ArgsNotBytes_test(): assert_that( calling( hu.CreateHmac ).with_args( u'foo', bytes( b'foo' ) ), raises( TypeError, '.*content*' ) ) assert_that( calling( hu.CreateHmac ).with_args( bytes( b'foo' ), u'foo' ), raises( TypeError, '.*hmac_secret*' ) ) def CreateHmac_WithBytes_test(): # Test vectors from Wikipedia (HMAC_SHA256): https://goo.gl/cvX0Tn assert_that( hexlify( hu.CreateHmac( bytes( b'The quick brown fox jumps over the lazy dog' ), bytes( b'key' ) ) ), equal_to( bytes( b'f7bc83f430538424b13298e6aa6fb143' b'ef4d59a14946175997479dbc2d1a3cd8' ) ) ) def CreateRequestHmac_ArgsNotBytes_test(): assert_that( calling( hu.CreateRequestHmac ).with_args( u'foo', bytes( b'foo' ), bytes( b'foo' ), bytes( b'foo' ) ), raises( TypeError, '.*method*' ) ) assert_that( calling( hu.CreateRequestHmac ).with_args( bytes( b'foo' ), u'foo', bytes( b'foo' ), bytes( b'foo' ) ), raises( TypeError, '.*path*' ) ) assert_that( calling( hu.CreateRequestHmac ).with_args( bytes( b'foo' ), bytes( b'foo' ), u'foo', bytes( b'foo' ) ), raises( TypeError, '.*body*' ) ) assert_that( calling( hu.CreateRequestHmac ).with_args( bytes( b'foo' ), bytes( b'foo' ), bytes( b'foo' ), u'foo' ), raises( TypeError, '.*hmac_secret*' ) ) def CreateRequestHmac_WithBytes_test(): assert_that( hexlify( hu.CreateRequestHmac( bytes( b'GET' ), bytes( b'/foo' ), bytes( b'body' ), bytes( b'key' ) ) ), equal_to( bytes( b'bfbb6bc7a2b3eca2a78f4e7ec8a7dfa7' b'e58bb8974166eaf20e0224d999894b34' ) ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/identifier_completer_test.py�������������������������������0000664�0000000�0000000�00000024561�13746324601�0025052�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import os from hamcrest import assert_that, empty, equal_to, contains_exactly from ycmd.user_options_store import DefaultOptions from ycmd.completers.all import identifier_completer as ic from ycmd.completers.all.identifier_completer import IdentifierCompleter from ycmd.request_wrap import RequestWrap from ycmd.tests import PathToTestFile from ycmd.tests.test_utils import BuildRequest def BuildRequestWrap( contents, column_num, line_num = 1 ): return RequestWrap( BuildRequest( column_num = column_num, line_num = line_num, contents = contents ) ) def GetCursorIdentifier_StartOfLine_test(): assert_that( 'foo', equal_to( ic._GetCursorIdentifier( False, BuildRequestWrap( 'foo', 1 ) ) ) ) assert_that( 'fooBar', equal_to( ic._GetCursorIdentifier( False, BuildRequestWrap( 'fooBar', 1 ) ) ) ) def GetCursorIdentifier_EndOfLine_test(): assert_that( 'foo', equal_to( ic._GetCursorIdentifier( False, BuildRequestWrap( 'foo', 3 ) ) ) ) def GetCursorIdentifier_PastEndOfLine_test(): assert_that( '', equal_to( ic._GetCursorIdentifier( False, BuildRequestWrap( 'foo', 11 ) ) ) ) def GetCursorIdentifier_NegativeColumn_test(): assert_that( 'foo', equal_to( ic._GetCursorIdentifier( False, BuildRequestWrap( 'foo', -10 ) ) ) ) def GetCursorIdentifier_StartOfLine_StopsAtNonIdentifierChar_test(): assert_that( 'foo', equal_to( ic._GetCursorIdentifier( False, BuildRequestWrap( 'foo(goo)', 1 ) ) ) ) def GetCursorIdentifier_AtNonIdentifier_test(): assert_that( 'goo', equal_to( ic._GetCursorIdentifier( False, BuildRequestWrap( 'foo(goo)', 4 ) ) ) ) def GetCursorIdentifier_WalksForwardForIdentifier_test(): assert_that( 'foo', equal_to( ic._GetCursorIdentifier( False, BuildRequestWrap( ' foo', 1 ) ) ) ) def GetCursorIdentifier_FindsNothingForward_test(): assert_that( '', equal_to( ic._GetCursorIdentifier( False, BuildRequestWrap( 'foo ()***()', 5 ) ) ) ) def GetCursorIdentifier_SingleCharIdentifier_test(): assert_that( 'f', equal_to( ic._GetCursorIdentifier( False, BuildRequestWrap( ' f ', 1 ) ) ) ) def GetCursorIdentifier_StartsInMiddleOfIdentifier_test(): assert_that( 'foobar', equal_to( ic._GetCursorIdentifier( False, BuildRequestWrap( 'foobar', 4 ) ) ) ) def GetCursorIdentifier_LineEmpty_test(): assert_that( '', equal_to( ic._GetCursorIdentifier( False, BuildRequestWrap( '', 12 ) ) ) ) def GetCursorIdentifier_IgnoreIdentifierFromCommentsAndStrings_test(): assert_that( '', equal_to( ic._GetCursorIdentifier( False, BuildRequestWrap( '"foobar"', 4 ) ) ) ) assert_that( '', equal_to( ic._GetCursorIdentifier( False, BuildRequestWrap( '/*\n' ' * foobar\n' ' */', 5, 2 ) ) ) ) def GetCursorIdentifier_CollectIdentifierFromCommentsAndStrings_test(): assert_that( 'foobar', equal_to( ic._GetCursorIdentifier( True, BuildRequestWrap( '"foobar"', 4 ) ) ) ) assert_that( 'foobar', equal_to( ic._GetCursorIdentifier( True, BuildRequestWrap( '/*\n' ' * foobar\n' ' */', 5, 2 ) ) ) ) def PreviousIdentifier_Simple_test(): assert_that( 'foo', equal_to( ic._PreviousIdentifier( 2, False, BuildRequestWrap( 'foo', 4 ) ) ) ) def PreviousIdentifier_WholeIdentShouldBeBeforeColumn_test(): assert_that( '', equal_to( ic._PreviousIdentifier( 2, False, BuildRequestWrap( 'foobar', column_num = 4 ) ) ) ) def PreviousIdentifier_DoNotWrap_test(): assert_that( '', equal_to( ic._PreviousIdentifier( 2, False, BuildRequestWrap( 'foobar\n bar', column_num = 4 ) ) ) ) def PreviousIdentifier_IgnoreForwardIdents_test(): assert_that( 'foo', equal_to( ic._PreviousIdentifier( 2, False, BuildRequestWrap( 'foo bar zoo', 4 ) ) ) ) def PreviousIdentifier_IgnoreTooSmallIdent_test(): assert_that( '', equal_to( ic._PreviousIdentifier( 4, False, BuildRequestWrap( 'foo', 4 ) ) ) ) def PreviousIdentifier_IgnoreTooSmallIdent_DontContinueLooking_test(): assert_that( '', equal_to( ic._PreviousIdentifier( 4, False, BuildRequestWrap( 'abcde foo', 10 ) ) ) ) def PreviousIdentifier_WhitespaceAfterIdent_test(): assert_that( 'foo', equal_to( ic._PreviousIdentifier( 2, False, BuildRequestWrap( 'foo ', 6 ) ) ) ) def PreviousIdentifier_JunkAfterIdent_test(): assert_that( 'foo', equal_to( ic._PreviousIdentifier( 2, False, BuildRequestWrap( 'foo ;;()** ', 13 ) ) ) ) def PreviousIdentifier_IdentInMiddleOfJunk_test(): assert_that( 'aa', equal_to( ic._PreviousIdentifier( 2, False, BuildRequestWrap( 'foo ;;(aa)** ', 13 ) ) ) ) def PreviousIdentifier_IdentOnPreviousLine_test(): assert_that( 'foo', equal_to( ic._PreviousIdentifier( 2, False, BuildRequestWrap( 'foo\n ', column_num = 3, line_num = 2 ) ) ) ) assert_that( 'foo', equal_to( ic._PreviousIdentifier( 2, False, BuildRequestWrap( 'foo\n', column_num = 1, line_num = 2 ) ) ) ) def PreviousIdentifier_IdentOnPreviousLine_JunkAfterIdent_test(): assert_that( 'foo', equal_to( ic._PreviousIdentifier( 2, False, BuildRequestWrap( 'foo **;()\n ', column_num = 3, line_num = 2 ) ) ) ) def PreviousIdentifier_NoGoodIdentFound_test(): assert_that( '', equal_to( ic._PreviousIdentifier( 5, False, BuildRequestWrap( 'foo\n ', column_num = 2, line_num = 2 ) ) ) ) def PreviousIdentifier_IgnoreIdentifierFromCommentsAndStrings_test(): assert_that( '', equal_to( ic._PreviousIdentifier( 2, False, BuildRequestWrap( '"foo"\n', column_num = 1, line_num = 2 ) ) ) ) assert_that( '', equal_to( ic._PreviousIdentifier( 2, False, BuildRequestWrap( '/*\n' ' * foo\n' ' */', column_num = 2, line_num = 3 ) ) ) ) def PreviousIdentifier_CollectIdentifierFromCommentsAndStrings_test(): assert_that( 'foo', equal_to( ic._PreviousIdentifier( 2, True, BuildRequestWrap( '"foo"\n', column_num = 1, line_num = 2 ) ) ) ) assert_that( 'foo', equal_to( ic._PreviousIdentifier( 2, True, BuildRequestWrap( '/*\n' ' * foo\n' ' */', column_num = 2, line_num = 3 ) ) ) ) def FilterUnchangedTagFiles_NoFiles_test(): ident_completer = IdentifierCompleter( DefaultOptions() ) assert_that( list( ident_completer._FilterUnchangedTagFiles( [] ) ), empty() ) def FilterUnchangedTagFiles_SkipBadFiles_test(): ident_completer = IdentifierCompleter( DefaultOptions() ) assert_that( list( ident_completer._FilterUnchangedTagFiles( [ '/some/tags' ] ) ), empty() ) def FilterUnchangedTagFiles_KeepGoodFiles_test(): ident_completer = IdentifierCompleter( DefaultOptions() ) tag_file = PathToTestFile( 'basic.tags' ) assert_that( ident_completer._FilterUnchangedTagFiles( [ tag_file ] ), contains_exactly( tag_file ) ) def FilterUnchangedTagFiles_SkipUnchangesFiles_test(): ident_completer = IdentifierCompleter( DefaultOptions() ) # simulate an already open tags file that didn't change in the meantime. tag_file = PathToTestFile( 'basic.tags' ) ident_completer._tags_file_last_mtime[ tag_file ] = os.path.getmtime( tag_file ) assert_that( list( ident_completer._FilterUnchangedTagFiles( [ tag_file ] ) ), empty() ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �����������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/identifier_utils_test.py�����������������������������������0000664�0000000�0000000�00000044422�13746324601�0024216�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2013-2020 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 <http://www.gnu.org/licenses/>. import pytest from ycmd import identifier_utils as iu from hamcrest import assert_that, equal_to, has_item def RemoveIdentifierFreeText_CppComments_test(): assert_that( "foo \nbar \nqux", equal_to( iu.RemoveIdentifierFreeText( "foo \nbar //foo \nqux" ) ) ) def RemoveIdentifierFreeText_PythonComments_test(): assert_that( "foo \nbar \nqux", equal_to( iu.RemoveIdentifierFreeText( "foo \nbar #foo \nqux" ) ) ) def RemoveIdentifierFreeText_CstyleComments_test(): assert_that( "\n bar", equal_to( iu.RemoveIdentifierFreeText( "/* foo\n */ bar" ) ) ) assert_that( "foo \nbar \nqux", equal_to( iu.RemoveIdentifierFreeText( "foo \nbar /* foo */\nqux" ) ) ) assert_that( "foo \nbar \n\nqux", equal_to( iu.RemoveIdentifierFreeText( "foo \nbar /* foo \n foo2 */\nqux" ) ) ) def RemoveIdentifierFreeText_SimpleSingleQuoteString_test(): assert_that( "foo \nbar \nqux", equal_to( iu.RemoveIdentifierFreeText( "foo \nbar 'foo'\nqux" ) ) ) def RemoveIdentifierFreeText_SimpleDoubleQuoteString_test(): assert_that( "foo \nbar \nqux", equal_to( iu.RemoveIdentifierFreeText( 'foo \nbar "foo"\nqux' ) ) ) def RemoveIdentifierFreeText_EscapedQuotes_test(): assert_that( "foo \nbar \nqux", equal_to( iu.RemoveIdentifierFreeText( "foo \nbar 'fo\\'oz\\nfoo'\nqux" ) ) ) assert_that( "foo \nbar \nqux", equal_to( iu.RemoveIdentifierFreeText( 'foo \nbar "fo\\"oz\\nfoo"\nqux' ) ) ) def RemoveIdentifierFreeText_SlashesInStrings_test(): assert_that( "foo \nbar baz\nqux ", equal_to( iu.RemoveIdentifierFreeText( 'foo \nbar "fo\\\\"baz\nqux "qwe"' ) ) ) assert_that( "foo \nbar \nqux ", equal_to( iu.RemoveIdentifierFreeText( "foo '\\\\'\nbar '\\\\'\nqux '\\\\'" ) ) ) def RemoveIdentifierFreeText_EscapedQuotesStartStrings_test(): assert_that( "\\\"foo\\\" zoo", equal_to( iu.RemoveIdentifierFreeText( "\\\"foo\\\"'\"''bar' zoo'test'" ) ) ) assert_that( "\\'foo\\' zoo", equal_to( iu.RemoveIdentifierFreeText( "\\'foo\\'\"'\"\"bar\" zoo\"test\"" ) ) ) def RemoveIdentifierFreeText_NoMultilineString_test(): assert_that( "'\nlet x = \nlet y = ", equal_to( iu.RemoveIdentifierFreeText( "'\nlet x = 'foo'\nlet y = 'bar'" ) ) ) assert_that( "\"\nlet x = \nlet y = ", equal_to( iu.RemoveIdentifierFreeText( "\"\nlet x = \"foo\"\nlet y = \"bar\"" ) ) ) def RemoveIdentifierFreeText_PythonMultilineString_test(): assert_that( "\n\n\nzoo", equal_to( iu.RemoveIdentifierFreeText( "\"\"\"\nfoobar\n\"\"\"\nzoo" ) ) ) assert_that( "\n\n\nzoo", equal_to( iu.RemoveIdentifierFreeText( "'''\nfoobar\n'''\nzoo" ) ) ) def RemoveIdentifierFreeText_GoBackQuoteString_test(): assert_that( "foo \nbar `foo`\nqux", equal_to( iu.RemoveIdentifierFreeText( "foo \nbar `foo`\nqux" ) ) ) assert_that( "foo \nbar \nqux", equal_to( iu.RemoveIdentifierFreeText( "foo \nbar `foo`\nqux", filetype = 'go' ) ) ) def ExtractIdentifiersFromText_test(): assert_that( [ "foo", "_bar", "BazGoo", "FOO", "_", "x", "one", "two", "moo", "qqq" ], equal_to( iu.ExtractIdentifiersFromText( "foo $_bar \n&BazGoo\n FOO= !!! '-' " "- _ (x) one-two !moo [qqq]" ) ) ) def ExtractIdentifiersFromText_Css_test(): assert_that( [ "foo", "-zoo", "font-size", "px", "a99" ], equal_to( iu.ExtractIdentifiersFromText( "foo -zoo {font-size: 12px;} a99", "css" ) ) ) def ExtractIdentifiersFromText_Html_test(): assert_that( [ "foo", "goo-foo", "zoo", "bar", "aa", "z", "b@g", "fo", "ba" ], equal_to( iu.ExtractIdentifiersFromText( '<foo> <goo-foo zoo=bar aa="" z=\'\'/> b@g fo.ba', "html" ) ) ) def ExtractIdentifiersFromText_Html_TemplateChars_test(): assert_that( iu.ExtractIdentifiersFromText( '<foo>{{goo}}</foo>', 'html' ), has_item( 'goo' ) ) def ExtractIdentifiersFromText_JavaScript_test(): assert_that( [ "var", "foo", "require", "bar" ], equal_to( iu.ExtractIdentifiersFromText( "var foo = require('bar');", 'javascript' ) ) ) def IsIdentifier_Default_test(): assert_that( iu.IsIdentifier( 'foo' ) ) assert_that( iu.IsIdentifier( 'foo129' ) ) assert_that( iu.IsIdentifier( 'f12' ) ) assert_that( iu.IsIdentifier( 'f12' ) ) assert_that( iu.IsIdentifier( '_foo' ) ) assert_that( iu.IsIdentifier( '_foo129' ) ) assert_that( iu.IsIdentifier( '_f12' ) ) assert_that( iu.IsIdentifier( '_f12' ) ) assert_that( iu.IsIdentifier( 'uniçode' ) ) assert_that( iu.IsIdentifier( 'uç' ) ) assert_that( iu.IsIdentifier( 'ç' ) ) assert_that( iu.IsIdentifier( 'çode' ) ) assert_that( not iu.IsIdentifier( '1foo129' ) ) assert_that( not iu.IsIdentifier( '-foo' ) ) assert_that( not iu.IsIdentifier( 'foo-' ) ) assert_that( not iu.IsIdentifier( 'font-face' ) ) assert_that( not iu.IsIdentifier( None ) ) assert_that( not iu.IsIdentifier( '' ) ) def IsIdentifier_JavaScript_test(): assert_that( iu.IsIdentifier( '_føo1', 'javascript' ) ) assert_that( iu.IsIdentifier( 'fø_o1', 'javascript' ) ) assert_that( iu.IsIdentifier( '$føo1', 'javascript' ) ) assert_that( iu.IsIdentifier( 'fø$o1', 'javascript' ) ) assert_that( not iu.IsIdentifier( '1føo', 'javascript' ) ) def IsIdentifier_TypeScript_test(): assert_that( iu.IsIdentifier( '_føo1', 'typescript' ) ) assert_that( iu.IsIdentifier( 'fø_o1', 'typescript' ) ) assert_that( iu.IsIdentifier( '$føo1', 'typescript' ) ) assert_that( iu.IsIdentifier( 'fø$o1', 'typescript' ) ) assert_that( not iu.IsIdentifier( '1føo', 'typescript' ) ) def IsIdentifier_Css_test(): assert_that( iu.IsIdentifier( 'foo' , 'css' ) ) assert_that( iu.IsIdentifier( 'a' , 'css' ) ) assert_that( iu.IsIdentifier( 'a1' , 'css' ) ) assert_that( iu.IsIdentifier( 'a-' , 'css' ) ) assert_that( iu.IsIdentifier( 'a-b' , 'css' ) ) assert_that( iu.IsIdentifier( '_b' , 'css' ) ) assert_that( iu.IsIdentifier( '-ms-foo' , 'css' ) ) assert_that( iu.IsIdentifier( '-_o' , 'css' ) ) assert_that( iu.IsIdentifier( 'font-face', 'css' ) ) assert_that( iu.IsIdentifier( 'αβγ' , 'css' ) ) assert_that( not iu.IsIdentifier( '-3b', 'css' ) ) assert_that( not iu.IsIdentifier( '-3' , 'css' ) ) assert_that( not iu.IsIdentifier( '--' , 'css' ) ) assert_that( not iu.IsIdentifier( '3' , 'css' ) ) assert_that( not iu.IsIdentifier( '' , 'css' ) ) assert_that( not iu.IsIdentifier( '€' , 'css' ) ) def IsIdentifier_R_test(): assert_that( iu.IsIdentifier( 'a' , 'r' ) ) assert_that( iu.IsIdentifier( 'a.b' , 'r' ) ) assert_that( iu.IsIdentifier( 'a.b.c', 'r' ) ) assert_that( iu.IsIdentifier( 'a_b' , 'r' ) ) assert_that( iu.IsIdentifier( 'a1' , 'r' ) ) assert_that( iu.IsIdentifier( 'a_1' , 'r' ) ) assert_that( iu.IsIdentifier( '.a' , 'r' ) ) assert_that( iu.IsIdentifier( '.a_b' , 'r' ) ) assert_that( iu.IsIdentifier( '.a1' , 'r' ) ) assert_that( iu.IsIdentifier( '...' , 'r' ) ) assert_that( iu.IsIdentifier( '..1' , 'r' ) ) assert_that( not iu.IsIdentifier( '.1a', 'r' ) ) assert_that( not iu.IsIdentifier( '.1' , 'r' ) ) assert_that( not iu.IsIdentifier( '1a' , 'r' ) ) assert_that( not iu.IsIdentifier( '123', 'r' ) ) assert_that( not iu.IsIdentifier( '_1a', 'r' ) ) assert_that( not iu.IsIdentifier( '_a' , 'r' ) ) assert_that( not iu.IsIdentifier( '' , 'r' ) ) def IsIdentifier_Clojure_test(): assert_that( iu.IsIdentifier( 'foo' , 'clojure' ) ) assert_that( iu.IsIdentifier( 'f9' , 'clojure' ) ) assert_that( iu.IsIdentifier( 'a.b.c', 'clojure' ) ) assert_that( iu.IsIdentifier( 'a.c' , 'clojure' ) ) assert_that( iu.IsIdentifier( 'a/c' , 'clojure' ) ) assert_that( iu.IsIdentifier( '*' , 'clojure' ) ) assert_that( iu.IsIdentifier( 'a*b' , 'clojure' ) ) assert_that( iu.IsIdentifier( '?' , 'clojure' ) ) assert_that( iu.IsIdentifier( 'a?b' , 'clojure' ) ) assert_that( iu.IsIdentifier( ':' , 'clojure' ) ) assert_that( iu.IsIdentifier( 'a:b' , 'clojure' ) ) assert_that( iu.IsIdentifier( '+' , 'clojure' ) ) assert_that( iu.IsIdentifier( 'a+b' , 'clojure' ) ) assert_that( iu.IsIdentifier( '-' , 'clojure' ) ) assert_that( iu.IsIdentifier( 'a-b' , 'clojure' ) ) assert_that( iu.IsIdentifier( '!' , 'clojure' ) ) assert_that( iu.IsIdentifier( 'a!b' , 'clojure' ) ) assert_that( not iu.IsIdentifier( '9f' , 'clojure' ) ) assert_that( not iu.IsIdentifier( '9' , 'clojure' ) ) assert_that( not iu.IsIdentifier( 'a/b/c', 'clojure' ) ) assert_that( not iu.IsIdentifier( '(a)' , 'clojure' ) ) assert_that( not iu.IsIdentifier( '' , 'clojure' ) ) def IsIdentifier_Elisp_test(): # elisp is using the clojure regexes, so we're testing this more lightly assert_that( iu.IsIdentifier( 'foo' , 'elisp' ) ) assert_that( iu.IsIdentifier( 'f9' , 'elisp' ) ) assert_that( iu.IsIdentifier( 'a.b.c', 'elisp' ) ) assert_that( iu.IsIdentifier( 'a/c' , 'elisp' ) ) assert_that( not iu.IsIdentifier( '9f' , 'elisp' ) ) assert_that( not iu.IsIdentifier( '9' , 'elisp' ) ) assert_that( not iu.IsIdentifier( 'a/b/c', 'elisp' ) ) assert_that( not iu.IsIdentifier( '(a)' , 'elisp' ) ) assert_that( not iu.IsIdentifier( '' , 'elisp' ) ) def IsIdentifier_Haskell_test(): assert_that( iu.IsIdentifier( 'foo' , 'haskell' ) ) assert_that( iu.IsIdentifier( "foo'", 'haskell' ) ) assert_that( iu.IsIdentifier( "x'" , 'haskell' ) ) assert_that( iu.IsIdentifier( "_x'" , 'haskell' ) ) assert_that( iu.IsIdentifier( "_x" , 'haskell' ) ) assert_that( iu.IsIdentifier( "x9" , 'haskell' ) ) assert_that( not iu.IsIdentifier( "'x", 'haskell' ) ) assert_that( not iu.IsIdentifier( "9x", 'haskell' ) ) assert_that( not iu.IsIdentifier( "9" , 'haskell' ) ) assert_that( not iu.IsIdentifier( '' , 'haskell' ) ) def IsIdentifier_Tex_test(): assert_that( iu.IsIdentifier( 'foo' , 'tex' ) ) assert_that( iu.IsIdentifier( 'fig:foo' , 'tex' ) ) assert_that( iu.IsIdentifier( 'fig:foo-bar', 'tex' ) ) assert_that( iu.IsIdentifier( 'sec:summary', 'tex' ) ) assert_that( iu.IsIdentifier( 'eq:bar_foo' , 'tex' ) ) assert_that( iu.IsIdentifier( 'fōo' , 'tex' ) ) assert_that( iu.IsIdentifier( 'some8' , 'tex' ) ) assert_that( not iu.IsIdentifier( '\\section', 'tex' ) ) assert_that( not iu.IsIdentifier( 'foo:' , 'tex' ) ) assert_that( not iu.IsIdentifier( '-bar' , 'tex' ) ) assert_that( not iu.IsIdentifier( '' , 'tex' ) ) def IsIdentifier_Perl6_test(): assert_that( iu.IsIdentifier( 'foo' , 'perl6' ) ) assert_that( iu.IsIdentifier( "f-o" , 'perl6' ) ) assert_that( iu.IsIdentifier( "x'y" , 'perl6' ) ) assert_that( iu.IsIdentifier( "_x-y" , 'perl6' ) ) assert_that( iu.IsIdentifier( "x-y'a", 'perl6' ) ) assert_that( iu.IsIdentifier( "x-_" , 'perl6' ) ) assert_that( iu.IsIdentifier( "x-_7" , 'perl6' ) ) assert_that( iu.IsIdentifier( "_x" , 'perl6' ) ) assert_that( iu.IsIdentifier( "x9" , 'perl6' ) ) assert_that( not iu.IsIdentifier( "'x" , 'perl6' ) ) assert_that( not iu.IsIdentifier( "x'" , 'perl6' ) ) assert_that( not iu.IsIdentifier( "-x" , 'perl6' ) ) assert_that( not iu.IsIdentifier( "x-" , 'perl6' ) ) assert_that( not iu.IsIdentifier( "x-1" , 'perl6' ) ) assert_that( not iu.IsIdentifier( "x--" , 'perl6' ) ) assert_that( not iu.IsIdentifier( "x--a", 'perl6' ) ) assert_that( not iu.IsIdentifier( "x-'" , 'perl6' ) ) assert_that( not iu.IsIdentifier( "x-'a", 'perl6' ) ) assert_that( not iu.IsIdentifier( "x-a-", 'perl6' ) ) assert_that( not iu.IsIdentifier( "x+" , 'perl6' ) ) assert_that( not iu.IsIdentifier( "9x" , 'perl6' ) ) assert_that( not iu.IsIdentifier( "9" , 'perl6' ) ) assert_that( not iu.IsIdentifier( '' , 'perl6' ) ) def IsIdentifier_Scheme_test(): assert_that( iu.IsIdentifier( 'λ' , 'scheme' ) ) assert_that( iu.IsIdentifier( '_' , 'scheme' ) ) assert_that( iu.IsIdentifier( '+' , 'scheme' ) ) assert_that( iu.IsIdentifier( '-' , 'scheme' ) ) assert_that( iu.IsIdentifier( '...' , 'scheme' ) ) assert_that( iu.IsIdentifier( r'\x01;' , 'scheme' ) ) assert_that( iu.IsIdentifier( r'h\x65;lle', 'scheme' ) ) assert_that( iu.IsIdentifier( 'foo' , 'scheme' ) ) assert_that( iu.IsIdentifier( 'foo+-*/1-1', 'scheme' ) ) assert_that( iu.IsIdentifier( 'call/cc' , 'scheme' ) ) assert_that( not iu.IsIdentifier( '.' , 'scheme' ) ) assert_that( not iu.IsIdentifier( '..' , 'scheme' ) ) assert_that( not iu.IsIdentifier( '--' , 'scheme' ) ) assert_that( not iu.IsIdentifier( '++' , 'scheme' ) ) assert_that( not iu.IsIdentifier( '+1' , 'scheme' ) ) assert_that( not iu.IsIdentifier( '-1' , 'scheme' ) ) assert_that( not iu.IsIdentifier( '-abc' , 'scheme' ) ) assert_that( not iu.IsIdentifier( '-<abc' , 'scheme' ) ) assert_that( not iu.IsIdentifier( '@' , 'scheme' ) ) assert_that( not iu.IsIdentifier( '@a' , 'scheme' ) ) assert_that( not iu.IsIdentifier( '-@a' , 'scheme' ) ) assert_that( not iu.IsIdentifier( '-12a' , 'scheme' ) ) assert_that( not iu.IsIdentifier( '12a' , 'scheme' ) ) assert_that( not iu.IsIdentifier( '\\' , 'scheme' ) ) assert_that( not iu.IsIdentifier( r'\x' , 'scheme' ) ) assert_that( not iu.IsIdentifier( r'\x123' , 'scheme' ) ) assert_that( not iu.IsIdentifier( r'aa\x123;cc\x', 'scheme' ) ) def StartOfLongestIdentifierEndingAtIndex_Simple_test(): assert_that( 0, equal_to( iu.StartOfLongestIdentifierEndingAtIndex( 'foo', 3 ) ) ) assert_that( 0, equal_to( iu.StartOfLongestIdentifierEndingAtIndex( 'f12', 3 ) ) ) def StartOfLongestIdentifierEndingAtIndex_BadInput_test(): assert_that( 0, equal_to( iu.StartOfLongestIdentifierEndingAtIndex( '', 0 ) ) ) assert_that( 1, equal_to( iu.StartOfLongestIdentifierEndingAtIndex( '', 1 ) ) ) assert_that( 5, equal_to( iu.StartOfLongestIdentifierEndingAtIndex( None, 5 ) ) ) assert_that( -1, equal_to( iu.StartOfLongestIdentifierEndingAtIndex( 'foo', -1 ) ) ) assert_that( 10, equal_to( iu.StartOfLongestIdentifierEndingAtIndex( 'foo', 10 ) ) ) def StartOfLongestIdentifierEndingAtIndex_Punctuation_test(): assert_that( 1, equal_to( iu.StartOfLongestIdentifierEndingAtIndex( '(foo', 4 ) ) ) assert_that( 6, equal_to( iu.StartOfLongestIdentifierEndingAtIndex( ' foo', 9 ) ) ) assert_that( 4, equal_to( iu.StartOfLongestIdentifierEndingAtIndex( 'gar;foo', 7 ) ) ) assert_that( 2, equal_to( iu.StartOfLongestIdentifierEndingAtIndex( '...', 2 ) ) ) def StartOfLongestIdentifierEndingAtIndex_PunctuationWithUnicode_test(): assert_that( 1, equal_to( iu.StartOfLongestIdentifierEndingAtIndex( u'(fäö', 4 ) ) ) assert_that( 2, equal_to( iu.StartOfLongestIdentifierEndingAtIndex( u' fäö', 5 ) ) ) # Not a test, but a test helper function def LoopExpectLongestIdentifier( ident, expected, end_index ): assert_that( expected, equal_to( iu.StartOfLongestIdentifierEndingAtIndex( ident, end_index ) ) ) @pytest.mark.parametrize( 'ident', [ 'foobar', 'f12341234', 'f123f1234', 'fäöttccoö', ], ids = [ 'Simple', '1stNonNumber', 'Subidentifier', 'Unicode' ] ) def StartOfLongestIdentifierEndingAtIndex_Entire_test( ident ): for i in range( len( ident ) ): LoopExpectLongestIdentifier( ident, 0, i ) def StartOfLongestIdentifierEndingAtIndex_Entire_AllBad_test(): ident = '....' for i in range( len( ident ) ): LoopExpectLongestIdentifier( ident, i, i ) # Not a test, but a test helper function def LoopExpectIdentfierAtIndex( ident, index, expected ): assert_that( expected, equal_to( iu.IdentifierAtIndex( ident, index ) ) ) @pytest.mark.parametrize( 'ident', [ 'foobar', 'fäöttccoö', ], ids = [ 'Simple', 'Unicode' ] ) def IdentifierAtIndex_Entire_test( ident ): for i in range( len( ident ) ): LoopExpectIdentfierAtIndex( ident, i, ident ) def IdentifierAtIndex_BadInput_test(): assert_that( '', equal_to( iu.IdentifierAtIndex( '', 0 ) ) ) assert_that( '', equal_to( iu.IdentifierAtIndex( '', 5 ) ) ) assert_that( '', equal_to( iu.IdentifierAtIndex( 'foo', 5 ) ) ) assert_that( 'foo', equal_to( iu.IdentifierAtIndex( 'foo', -5 ) ) ) def IdentifierAtIndex_IndexPastIdent_test(): assert_that( '', equal_to( iu.IdentifierAtIndex( 'foo ', 5 ) ) ) def IdentifierAtIndex_StopsAtNonIdentifier_test(): assert_that( 'foo', equal_to( iu.IdentifierAtIndex( 'foo(goo)', 0 ) ) ) assert_that( 'goo', equal_to( iu.IdentifierAtIndex( 'foo(goo)', 5 ) ) ) def IdentifierAtIndex_LooksAhead_Success_test(): assert_that( 'goo', equal_to( iu.IdentifierAtIndex( 'foo(goo)', 3 ) ) ) assert_that( 'goo', equal_to( iu.IdentifierAtIndex( ' goo', 0 ) ) ) def IdentifierAtIndex_LooksAhead_Failure_test(): assert_that( '', equal_to( iu.IdentifierAtIndex( 'foo ()***()', 5 ) ) ) def IdentifierAtIndex_SingleCharIdent_test(): assert_that( 'f', equal_to( iu.IdentifierAtIndex( ' f ', 1 ) ) ) def IdentifierAtIndex_Css_test(): assert_that( 'font-face', equal_to( iu.IdentifierAtIndex( 'font-face', 0, 'css' ) ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0020156�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/__init__.py�������������������������������������������0000664�0000000�0000000�00000001336�13746324601�0022272�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2017-2020 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 <http://www.gnu.org/licenses/>. from ycmd.tests.java.conftest import * # noqa ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/conftest.py�������������������������������������������0000664�0000000�0000000�00000011725�13746324601�0022363�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import contextlib import os import pytest from ycmd.tests.test_utils import ( BuildRequest, ClearCompletionsCache, IgnoreExtraConfOutsideTestsFolder, IsolatedApp, SetUpApp, StopCompleterServer, WaitUntilCompleterServerReady ) shared_app = None SERVER_STARTUP_TIMEOUT = 120 # seconds DEFAULT_PROJECT_DIR = 'simple_eclipse_project' @pytest.fixture( scope='module', autouse=True ) def set_up_shared_app(): """Initializes the ycmd server as a WebTest application that will be shared by all tests using the SharedYcmd decorator in this package. Additional configuration that is common to these tests, like starting a semantic subserver, should be done here.""" global shared_app shared_app = SetUpApp() with IgnoreExtraConfOutsideTestsFolder(): StartJavaCompleterServerInDirectory( shared_app, PathToTestFile( DEFAULT_PROJECT_DIR ) ) yield StopCompleterServer( shared_app, 'java' ) def StartJavaCompleterServerInDirectory( app, directory ): StartJavaCompleterServerWithFile( app, os.path.join( directory, 'test.java' ) ) def StartJavaCompleterServerWithFile( app, file_path ): app.post_json( '/event_notification', BuildRequest( event_name = 'FileReadyToParse', filepath = file_path, filetype = 'java' ) ) WaitUntilCompleterServerReady( app, 'java', SERVER_STARTUP_TIMEOUT ) @pytest.fixture def isolated_app(): """Defines a pytest fixture to be used in cases where it is easier to specify user options of the isolated ycmdat some point inside the function. Example usage: def some_test( isolated_app ): with TemporaryTestDir() as tmp_dir: with isolated_app( user_options ) as app: """ @contextlib.contextmanager def manager( custom_options = {} ): with IsolatedApp( custom_options ) as app: try: yield app finally: StopCompleterServer( app, 'java' ) return manager @pytest.fixture def app( request ): which = request.param[ 0 ] assert which == 'isolated' or which == 'shared' if which == 'isolated': with IsolatedApp( request.param[ 1 ] ) as app: yield app StopCompleterServer( app, 'java' ) else: global shared_app ClearCompletionsCache() with IgnoreExtraConfOutsideTestsFolder(): yield shared_app """Defines a decorator to be attached to tests of this package. This decorator passes the shared ycmd application as a parameter.""" SharedYcmd = pytest.mark.parametrize( # Name of the fixture/function argument 'app', # Fixture parameters, passed to app() as request.param [ ( 'shared', ) ], # Non-empty ids makes fixture parameters visible in pytest verbose output ids = [ '' ], # Execute the fixture, instead of passing parameters directly to the # function argument indirect = True ) def IsolatedYcmd( custom_options = {} ): """Defines a decorator to be attached to tests of this package. This decorator passes a unique ycmd application as a parameter. It should be used on tests that change the server state in a irreversible way (ex: a semantic subserver is stopped or restarted) or expect a clean state (ex: no semantic subserver started, no .ycm_extra_conf.py loaded, etc). Use the optional parameter |custom_options| to give additional options and/or override the default ones. Example usage: from ycmd.tests.python import IsolatedYcmd @IsolatedYcmd( { 'python_binary_path': '/some/path' } ) def CustomPythonBinaryPath_test( app ): ... """ return pytest.mark.parametrize( # Name of the fixture/function argument 'app', # Fixture parameters, passed to app() as request.param [ ( 'isolated', custom_options ) ], # Non-empty ids makes fixture parameters visible in pytest verbose output ids = [ '' ], # Execute the fixture, instead of passing parameters directly to the # function argument indirect = True ) def PathToTestFile( *args ): dir_of_current_script = os.path.dirname( os.path.abspath( __file__ ) ) return os.path.join( dir_of_current_script, 'testdata', *args ) �������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/debug_info_test.py������������������������������������0000664�0000000�0000000�00000015425�13746324601�0023677�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_exactly, equal_to, has_entry, has_entries, instance_of ) from unittest.mock import patch from ycmd.tests.java import ( DEFAULT_PROJECT_DIR, IsolatedYcmd, PathToTestFile, SharedYcmd, StartJavaCompleterServerInDirectory ) from ycmd.tests.test_utils import BuildRequest, WaitUntilCompleterServerReady from ycmd.completers.language_server import language_server_completer as lsc import json import threading @IsolatedYcmd() def DebugInfo_HandleNotificationInPollThread_Throw_test( app ): filepath = PathToTestFile( DEFAULT_PROJECT_DIR, 'src', 'com', 'youcompleteme', 'Test.java' ) StartJavaCompleterServerInDirectory( app, filepath ) # This mock will be called in the message pump thread, so synchronize the # result (thrown) using an Event thrown = threading.Event() def ThrowOnLogMessage( msg ): thrown.set() raise RuntimeError( "ThrowOnLogMessage" ) with patch.object( lsc.LanguageServerCompleter, 'HandleNotificationInPollThread', side_effect = ThrowOnLogMessage ): app.post_json( '/run_completer_command', BuildRequest( filepath = filepath, filetype = 'java', command_arguments = [ 'RestartServer' ], ), ) # Ensure that we still process and handle messages even though a # message-pump-thread-handler raised an error. WaitUntilCompleterServerReady( app, 'java' ) # Prove that the exception was thrown. assert_that( thrown.is_set(), equal_to( True ) ) @SharedYcmd def DebugInfo_test( app ): request_data = BuildRequest( filetype = 'java' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'Java', 'servers': contains_exactly( has_entries( { 'name': 'jdt.ls', 'is_running': instance_of( bool ), 'executable': instance_of( list ), 'pid': instance_of( int ), 'logfiles': contains_exactly( instance_of( str ), instance_of( str ) ), 'extras': contains_exactly( has_entries( { 'key': 'Server State', 'value': 'Initialized' } ), has_entries( { 'key': 'Project Directory', 'value': PathToTestFile( 'simple_eclipse_project' ) } ), has_entries( { 'key': 'Settings', 'value': json.dumps( { 'bundles': [] }, indent = 2, sort_keys = True ) } ), has_entries( { 'key': 'Startup Status', 'value': 'Ready' } ), has_entries( { 'key': 'Java Path', 'value': instance_of( str ) } ), has_entries( { 'key': 'Launcher Config.', 'value': instance_of( str ) } ), has_entries( { 'key': 'Workspace Path', 'value': instance_of( str ) } ), has_entries( { 'key': 'Extension Path', 'value': contains_exactly( instance_of( str ) ) } ), ) } ) ) } ) ) ) @IsolatedYcmd( { 'extra_conf_globlist': PathToTestFile( 'extra_confs', '*' ) } ) def DebugInfo_ExtraConf_SettingsValid_test( app ): StartJavaCompleterServerInDirectory( app, PathToTestFile( 'extra_confs', 'simple_extra_conf_project' ) ) filepath = PathToTestFile( 'extra_confs', 'simple_extra_conf_project', 'src', 'ExtraConf.java' ) request_data = BuildRequest( filepath = filepath, filetype = 'java' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'Java', 'servers': contains_exactly( has_entries( { 'name': 'jdt.ls', 'is_running': instance_of( bool ), 'executable': instance_of( list ), 'pid': instance_of( int ), 'logfiles': contains_exactly( instance_of( str ), instance_of( str ) ), 'extras': contains_exactly( has_entries( { 'key': 'Server State', 'value': 'Initialized' } ), has_entries( { 'key': 'Project Directory', 'value': PathToTestFile( 'extra_confs', 'simple_extra_conf_project' ) } ), has_entries( { 'key': 'Settings', 'value': json.dumps( { 'java.rename.enabled': False, 'bundles': [] }, indent = 2, sort_keys = True ) } ), has_entries( { 'key': 'Startup Status', 'value': 'Ready' } ), has_entries( { 'key': 'Java Path', 'value': instance_of( str ) } ), has_entries( { 'key': 'Launcher Config.', 'value': instance_of( str ) } ), has_entries( { 'key': 'Workspace Path', 'value': instance_of( str ) } ), has_entries( { 'key': 'Extension Path', 'value': contains_exactly( instance_of( str ) ) } ), ) } ) ) } ) ) ) # Make sure a didSave notification doesn't cause anything to error. event_data = BuildRequest( event_name = 'FileSave', contents = 'asd', filepath = filepath, filetype = 'java' ) app.post_json( '/event_notification', event_data ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'Java', 'servers': contains_exactly( has_entries( { 'name': 'jdt.ls', 'is_running': instance_of( bool ) } ) ) } ) ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/diagnostics_test.py�����������������������������������0000664�0000000�0000000�00000067700�13746324601�0024110�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2017-2020 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 <http://www.gnu.org/licenses/>. import contextlib import json import time from hamcrest import ( assert_that, contains_exactly, contains_inanyorder, empty, equal_to, has_entries, has_entry, has_item, matches_regexp ) from ycmd.tests.java import ( DEFAULT_PROJECT_DIR, IsolatedYcmd, PathToTestFile, SharedYcmd, StartJavaCompleterServerInDirectory ) from ycmd.tests.test_utils import ( BuildRequest, LocationMatcher, PollForMessages, PollForMessagesTimeoutException, RangeMatcher, WaitForDiagnosticsToBeReady, WithRetry ) from ycmd.utils import ReadFile, StartThread from ycmd.completers import completer from pprint import pformat from unittest.mock import patch from ycmd.completers.language_server import language_server_protocol as lsp from ycmd import handlers def ProjectPath( *args ): return PathToTestFile( DEFAULT_PROJECT_DIR, 'src', 'com', 'test', *args ) TestFactory = ProjectPath( 'TestFactory.java' ) TestLauncher = ProjectPath( 'TestLauncher.java' ) TestWidgetImpl = ProjectPath( 'TestWidgetImpl.java' ) youcompleteme_Test = PathToTestFile( DEFAULT_PROJECT_DIR, 'src', 'com', 'youcompleteme', 'Test.java' ) DIAG_MATCHERS_PER_FILE = { TestFactory: contains_inanyorder( has_entries( { 'kind': 'WARNING', 'text': 'The value of the field TestFactory.Bar.testString is not used ' '[570425421]', 'location': LocationMatcher( TestFactory, 15, 19 ), 'location_extent': RangeMatcher( TestFactory, ( 15, 19 ), ( 15, 29 ) ), 'ranges': contains_exactly( RangeMatcher( TestFactory, ( 15, 19 ), ( 15, 29 ) ) ), 'fixit_available': False } ), has_entries( { 'kind': 'ERROR', 'text': 'Wibble cannot be resolved to a type [16777218]', 'location': LocationMatcher( TestFactory, 18, 24 ), 'location_extent': RangeMatcher( TestFactory, ( 18, 24 ), ( 18, 30 ) ), 'ranges': contains_exactly( RangeMatcher( TestFactory, ( 18, 24 ), ( 18, 30 ) ) ), 'fixit_available': False } ), has_entries( { 'kind': 'ERROR', 'text': 'Wibble cannot be resolved to a variable [33554515]', 'location': LocationMatcher( TestFactory, 19, 15 ), 'location_extent': RangeMatcher( TestFactory, ( 19, 15 ), ( 19, 21 ) ), 'ranges': contains_exactly( RangeMatcher( TestFactory, ( 19, 15 ), ( 19, 21 ) ) ), 'fixit_available': False } ), has_entries( { 'kind': 'ERROR', 'text': 'Type mismatch: cannot convert from int to boolean [16777233]', 'location': LocationMatcher( TestFactory, 27, 10 ), 'location_extent': RangeMatcher( TestFactory, ( 27, 10 ), ( 27, 16 ) ), 'ranges': contains_exactly( RangeMatcher( TestFactory, ( 27, 10 ), ( 27, 16 ) ) ), 'fixit_available': False } ), has_entries( { 'kind': 'ERROR', 'text': 'Type mismatch: cannot convert from int to boolean [16777233]', 'location': LocationMatcher( TestFactory, 30, 10 ), 'location_extent': RangeMatcher( TestFactory, ( 30, 10 ), ( 30, 16 ) ), 'ranges': contains_exactly( RangeMatcher( TestFactory, ( 30, 10 ), ( 30, 16 ) ) ), 'fixit_available': False } ), has_entries( { 'kind': 'ERROR', 'text': 'The method doSomethingVaguelyUseful() in the type ' 'AbstractTestWidget is not applicable for the arguments ' '(TestFactory.Bar) [67108979]', 'location': LocationMatcher( TestFactory, 30, 23 ), 'location_extent': RangeMatcher( TestFactory, ( 30, 23 ), ( 30, 47 ) ), 'ranges': contains_exactly( RangeMatcher( TestFactory, ( 30, 23 ), ( 30, 47 ) ) ), 'fixit_available': False } ), ), TestWidgetImpl: contains_inanyorder( has_entries( { 'kind': 'WARNING', 'text': 'The value of the local variable a is not used [536870973]', 'location': LocationMatcher( TestWidgetImpl, 15, 9 ), 'location_extent': RangeMatcher( TestWidgetImpl, ( 15, 9 ), ( 15, 10 ) ), 'ranges': contains_exactly( RangeMatcher( TestWidgetImpl, ( 15, 9 ), ( 15, 10 ) ) ), 'fixit_available': False } ), has_entries( { 'kind': 'ERROR', 'text': 'ISR cannot be resolved to a variable [33554515]', 'location': LocationMatcher( TestWidgetImpl, 34, 12 ), 'location_extent': RangeMatcher( TestWidgetImpl, ( 34, 12 ), ( 34, 15 ) ), 'ranges': contains_exactly( RangeMatcher( TestWidgetImpl, ( 34, 12 ), ( 34, 15 ) ) ), 'fixit_available': False } ), has_entries( { 'kind': 'ERROR', 'text': 'Syntax error, insert ";" to complete BlockStatements ' '[1610612976]', 'location': LocationMatcher( TestWidgetImpl, 34, 12 ), 'location_extent': RangeMatcher( TestWidgetImpl, ( 34, 12 ), ( 34, 15 ) ), 'ranges': contains_exactly( RangeMatcher( TestWidgetImpl, ( 34, 12 ), ( 34, 15 ) ) ), 'fixit_available': False } ), ), TestLauncher: contains_inanyorder( has_entries( { 'kind': 'ERROR', 'text': 'The type new TestLauncher.Launchable(){} must implement the ' 'inherited abstract method TestLauncher.Launchable.launch(' 'TestFactory) [67109264]', 'location': LocationMatcher( TestLauncher, 28, 16 ), 'location_extent': RangeMatcher( TestLauncher, ( 28, 16 ), ( 28, 28 ) ), 'ranges': contains_exactly( RangeMatcher( TestLauncher, ( 28, 16 ), ( 28, 28 ) ) ), 'fixit_available': False } ), has_entries( { 'kind': 'ERROR', 'text': 'The method launch() of type new TestLauncher.Launchable(){} ' 'must override or implement a supertype method [67109498]', 'location': LocationMatcher( TestLauncher, 30, 19 ), 'location_extent': RangeMatcher( TestLauncher, ( 30, 19 ), ( 30, 27 ) ), 'ranges': contains_exactly( RangeMatcher( TestLauncher, ( 30, 19 ), ( 30, 27 ) ) ), 'fixit_available': False } ), has_entries( { 'kind': 'ERROR', 'text': 'Cannot make a static reference to the non-static field factory ' '[33554506]', 'location': LocationMatcher( TestLauncher, 31, 32 ), 'location_extent': RangeMatcher( TestLauncher, ( 31, 32 ), ( 31, 39 ) ), 'ranges': contains_exactly( RangeMatcher( TestLauncher, ( 31, 32 ), ( 31, 39 ) ) ), 'fixit_available': False } ), ), youcompleteme_Test: contains_exactly( has_entries( { 'kind': 'ERROR', 'text': 'The method doUnicødeTes() in the type Test is not applicable ' 'for the arguments (String) [67108979]', 'location': LocationMatcher( youcompleteme_Test, 13, 10 ), 'location_extent': RangeMatcher( youcompleteme_Test, ( 13, 10 ), ( 13, 23 ) ), 'ranges': contains_exactly( RangeMatcher( youcompleteme_Test, ( 13, 10 ), ( 13, 23 ) ) ), 'fixit_available': False } ), ), PathToTestFile( DEFAULT_PROJECT_DIR, 'test.java' ): contains_exactly( has_entries( { 'text': matches_regexp( 'test.java is not on the classpath .*' ) } ) ), } def _WaitForDiagnosticsForFile( app, filepath, contents, diags_filepath, diags_are_ready = lambda d: True, **kwargs ): diags = None try: for message in PollForMessages( app, { 'filepath': filepath, 'contents': contents, 'filetype': 'java' }, **kwargs ): if ( 'diagnostics' in message and message[ 'filepath' ] == diags_filepath ): print( f'Message { pformat( message ) }' ) diags = message[ 'diagnostics' ] if diags_are_ready( diags ): return diags # Eventually PollForMessages will throw a timeout exception and we'll fail # if we don't see the diagnostics go empty except PollForMessagesTimeoutException as e: raise AssertionError( f'{ e }. Timed out waiting for diagnostics for file { diags_filepath }.' ) return diags @WithRetry @SharedYcmd def Diagnostics_DetailedDiags_test( app ): filepath = TestFactory contents = ReadFile( filepath ) WaitForDiagnosticsToBeReady( app, filepath, contents, 'java' ) request_data = BuildRequest( contents = contents, filepath = filepath, filetype = 'java', line_num = 15, column_num = 19 ) results = app.post_json( '/detailed_diagnostic', request_data ).json assert_that( results, has_entry( 'message', 'The value of the field TestFactory.Bar.testString is not used' ) ) @WithRetry @SharedYcmd def FileReadyToParse_Diagnostics_Simple_test( app ): filepath = ProjectPath( 'TestFactory.java' ) contents = ReadFile( filepath ) # It can take a while for the diagnostics to be ready results = WaitForDiagnosticsToBeReady( app, filepath, contents, 'java' ) print( f'completer response: { pformat( results ) }' ) assert_that( results, DIAG_MATCHERS_PER_FILE[ filepath ] ) @IsolatedYcmd() def FileReadyToParse_Diagnostics_FileNotOnDisk_test( app ): StartJavaCompleterServerInDirectory( app, PathToTestFile( DEFAULT_PROJECT_DIR ) ) contents = ''' package com.test; class Test { public String test } ''' filepath = ProjectPath( 'Test.java' ) event_data = BuildRequest( event_name = 'FileReadyToParse', contents = contents, filepath = filepath, filetype = 'java' ) results = app.post_json( '/event_notification', event_data ).json # This is a new file, so the diagnostics can't possibly be available when the # initial parse request is sent. We receive these asynchronously. assert_that( results, empty() ) diag_matcher = contains_exactly( has_entries( { 'kind': 'ERROR', 'text': 'Syntax error, insert ";" to complete ClassBodyDeclarations ' '[1610612976]', 'location': LocationMatcher( filepath, 4, 21 ), 'location_extent': RangeMatcher( filepath, ( 4, 21 ), ( 4, 25 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 4, 21 ), ( 4, 25 ) ) ), 'fixit_available': False } ) ) # Poll until we receive the diags for message in PollForMessages( app, { 'filepath': filepath, 'contents': contents, 'filetype': 'java' } ): if 'diagnostics' in message and message[ 'filepath' ] == filepath: print( f'Message { pformat( message ) }' ) assert_that( message, has_entries( { 'diagnostics': diag_matcher, 'filepath': filepath } ) ) break # Now confirm that we _also_ get these from the FileReadyToParse request for tries in range( 0, 60 ): results = app.post_json( '/event_notification', event_data ).json if results: break time.sleep( 0.5 ) print( f'completer response: { pformat( results ) }' ) assert_that( results, diag_matcher ) @WithRetry @IsolatedYcmd() def Poll_Diagnostics_ProjectWide_Eclipse_test( app ): StartJavaCompleterServerInDirectory( app, PathToTestFile( DEFAULT_PROJECT_DIR ) ) filepath = TestLauncher contents = ReadFile( filepath ) # Poll until we receive _all_ the diags asynchronously to_see = sorted( DIAG_MATCHERS_PER_FILE.keys() ) seen = {} try: for message in PollForMessages( app, { 'filepath': filepath, 'contents': contents, 'filetype': 'java' } ): print( f'Message { pformat( message ) }' ) if 'diagnostics' in message: seen[ message[ 'filepath' ] ] = True if message[ 'filepath' ] not in DIAG_MATCHERS_PER_FILE: raise AssertionError( 'Received diagnostics for unexpected file ' f'{ message[ "filepath" ] }. Only expected { to_see }' ) assert_that( message, has_entries( { 'diagnostics': DIAG_MATCHERS_PER_FILE[ message[ 'filepath' ] ], 'filepath': message[ 'filepath' ] } ) ) if sorted( seen.keys() ) == to_see: break else: print( 'Seen diagnostics for {0}, still waiting for {1}'.format( json.dumps( sorted( seen.keys() ), indent = 2 ), json.dumps( [ x for x in to_see if x not in seen ], indent = 2 ) ) ) # Eventually PollForMessages will throw # a timeout exception and we'll fail # if we don't see all of the expected diags except PollForMessagesTimeoutException as e: raise AssertionError( str( e ) + 'Timed out waiting for full set of diagnostics. ' f'Expected to see diags for { json.dumps( to_see, indent = 2 ) }, ' f'but only saw { json.dumps( sorted( seen.keys() ), indent = 2 ) }.' ) @contextlib.contextmanager def PollingThread( app, messages_for_filepath, filepath, contents ): done = False def PollForMessagesInAnotherThread(): try: for message in PollForMessages( app, { 'filepath': filepath, 'contents': contents, 'filetype': 'java' } ): if done: return if 'filepath' in message and message[ 'filepath' ] == filepath: messages_for_filepath.append( message ) except PollForMessagesTimeoutException: pass try: poller = StartThread( PollForMessagesInAnotherThread ) yield finally: done = True poller.join( 120 ) assert not poller.is_alive() @WithRetry @IsolatedYcmd() def Poll_Diagnostics_ChangeFileContents_test( app ): StartJavaCompleterServerInDirectory( app, PathToTestFile( DEFAULT_PROJECT_DIR ) ) filepath = youcompleteme_Test old_contents = """package com.youcompleteme; public class Test { public String test; }""" messages_for_filepath = [] with PollingThread( app, messages_for_filepath, filepath, old_contents ): new_contents = """package com.youcompleteme; public class Test { public String test; public String test; }""" event_data = BuildRequest( event_name = 'FileReadyToParse', contents = new_contents, filepath = filepath, filetype = 'java' ) app.post_json( '/event_notification', event_data ).json expiration = time.time() + 10 while True: try: assert_that( messages_for_filepath, has_item( has_entries( { 'filepath': filepath, 'diagnostics': contains_exactly( has_entries( { 'kind': 'ERROR', 'text': 'Duplicate field Test.test [33554772]', 'location': LocationMatcher( youcompleteme_Test, 4, 17 ), 'location_extent': RangeMatcher( youcompleteme_Test, ( 4, 17 ), ( 4, 21 ) ), 'ranges': contains_exactly( RangeMatcher( youcompleteme_Test, ( 4, 17 ), ( 4, 21 ) ) ), 'fixit_available': False } ), has_entries( { 'kind': 'ERROR', 'text': 'Duplicate field Test.test [33554772]', 'location': LocationMatcher( youcompleteme_Test, 5, 17 ), 'location_extent': RangeMatcher( youcompleteme_Test, ( 5, 17 ), ( 5, 21 ) ), 'ranges': contains_exactly( RangeMatcher( youcompleteme_Test, ( 5, 17 ), ( 5, 21 ) ) ), 'fixit_available': False } ) ) } ) ) ) break except AssertionError: if time.time() > expiration: raise time.sleep( 0.25 ) @IsolatedYcmd() def FileReadyToParse_ServerNotReady_test( app ): filepath = TestFactory contents = ReadFile( filepath ) StartJavaCompleterServerInDirectory( app, ProjectPath() ) completer = handlers._server_state.GetFiletypeCompleter( [ 'java' ] ) # It can take a while for the diagnostics to be ready for tries in range( 0, 60 ): event_data = BuildRequest( event_name = 'FileReadyToParse', contents = contents, filepath = filepath, filetype = 'java' ) results = app.post_json( '/event_notification', event_data ).json if results: break time.sleep( 0.5 ) # To make the test fair, we make sure there are some results prior to the # 'server not running' call assert results # Call the FileReadyToParse handler but pretend that the server isn't running with patch.object( completer, 'ServerIsHealthy', return_value = False ): event_data = BuildRequest( event_name = 'FileReadyToParse', contents = contents, filepath = filepath, filetype = 'java' ) results = app.post_json( '/event_notification', event_data ).json assert_that( results, empty() ) @IsolatedYcmd() def FileReadyToParse_ChangeFileContents_test( app ): filepath = TestFactory contents = ReadFile( filepath ) StartJavaCompleterServerInDirectory( app, ProjectPath() ) # It can take a while for the diagnostics to be ready for tries in range( 0, 60 ): event_data = BuildRequest( event_name = 'FileReadyToParse', contents = contents, filepath = filepath, filetype = 'java' ) results = app.post_json( '/event_notification', event_data ).json if results: break time.sleep( 0.5 ) # To make the test fair, we make sure there are some results prior to the # 'server not running' call assert results # Call the FileReadyToParse handler but pretend that the server isn't running contents = 'package com.test; class TestFactory {}' # It can take a while for the diagnostics to be ready event_data = BuildRequest( event_name = 'FileReadyToParse', contents = contents, filepath = filepath, filetype = 'java' ) app.post_json( '/event_notification', event_data ) diags = None try: for message in PollForMessages( app, { 'filepath': filepath, 'contents': contents, 'filetype': 'java' } ): print( f'Message { pformat( message ) }' ) if 'diagnostics' in message and message[ 'filepath' ] == filepath: diags = message[ 'diagnostics' ] if not diags: break # Eventually PollForMessages will throw a timeout exception and we'll fail # if we don't see the diagnostics go empty except PollForMessagesTimeoutException as e: raise AssertionError( f'{ e }. Timed out waiting for diagnostics to clear for updated file. ' f'Expected to see none, but diags were: { diags }' ) assert_that( diags, empty() ) # Close the file (ensuring no exception) event_data = BuildRequest( event_name = 'BufferUnload', contents = contents, filepath = filepath, filetype = 'java' ) result = app.post_json( '/event_notification', event_data ).json assert_that( result, equal_to( {} ) ) # Close the file again, someone erroneously (ensuring no exception) event_data = BuildRequest( event_name = 'BufferUnload', contents = contents, filepath = filepath, filetype = 'java' ) result = app.post_json( '/event_notification', event_data ).json assert_that( result, equal_to( {} ) ) @IsolatedYcmd() def FileReadyToParse_ChangeFileContentsFileData_test( app ): filepath = TestFactory contents = ReadFile( filepath ) unsaved_buffer_path = TestLauncher file_data = { unsaved_buffer_path: { 'contents': 'package com.test; public class TestLauncher {}', 'filetypes': [ 'java' ], } } StartJavaCompleterServerInDirectory( app, ProjectPath() ) # It can take a while for the diagnostics to be ready results = WaitForDiagnosticsToBeReady( app, filepath, contents, 'java' ) assert results # Check that we have diagnostics for the saved file diags = _WaitForDiagnosticsForFile( app, filepath, contents, unsaved_buffer_path, lambda d: d ) assert_that( diags, DIAG_MATCHERS_PER_FILE[ unsaved_buffer_path ] ) # Now update the unsaved file with new contents event_data = BuildRequest( event_name = 'FileReadyToParse', contents = contents, filepath = filepath, filetype = 'java', file_data = file_data ) app.post_json( '/event_notification', event_data ) # Check that we have no diagnostics for the dirty file diags = _WaitForDiagnosticsForFile( app, filepath, contents, unsaved_buffer_path, lambda d: not d ) assert_that( diags, empty() ) # Now send the request again, but don't include the unsaved file. It should be # read from disk, causing the diagnostics for that file to appear. event_data = BuildRequest( event_name = 'FileReadyToParse', contents = contents, filepath = filepath, filetype = 'java' ) app.post_json( '/event_notification', event_data ) # Check that we now have diagnostics for the previously-dirty file diags = _WaitForDiagnosticsForFile( app, filepath, contents, unsaved_buffer_path, lambda d: d ) assert_that( diags, DIAG_MATCHERS_PER_FILE[ unsaved_buffer_path ] ) @WithRetry @SharedYcmd def OnBufferUnload_ServerNotRunning_test( app ): filepath = TestFactory contents = ReadFile( filepath ) completer = handlers._server_state.GetFiletypeCompleter( [ 'java' ] ) with patch.object( completer, 'ServerIsHealthy', return_value = False ): event_data = BuildRequest( event_name = 'BufferUnload', contents = contents, filepath = filepath, filetype = 'java' ) result = app.post_json( '/event_notification', event_data ).json assert_that( result, equal_to( {} ) ) @IsolatedYcmd() def PollForMessages_InvalidUri_test( app, *args ): StartJavaCompleterServerInDirectory( app, PathToTestFile( 'simple_eclipse_project' ) ) filepath = TestFactory contents = ReadFile( filepath ) with patch( 'ycmd.completers.language_server.language_server_protocol.UriToFilePath', side_effect = lsp.InvalidUriException ): for tries in range( 0, 5 ): response = app.post_json( '/receive_messages', BuildRequest( filetype = 'java', filepath = filepath, contents = contents ) ).json if response is True: break elif response is False: raise AssertionError( 'Message poll was aborted unexpectedly' ) elif 'diagnostics' in response: raise AssertionError( 'Did not expect diagnostics when file paths ' 'are invalid' ) time.sleep( 0.5 ) assert_that( response, equal_to( True ) ) @IsolatedYcmd() @patch.object( completer, 'MESSAGE_POLL_TIMEOUT', 2 ) def PollForMessages_ServerNotRunning_test( app ): StartJavaCompleterServerInDirectory( app, PathToTestFile( 'simple_eclipse_project' ) ) filepath = TestFactory contents = ReadFile( filepath ) app.post_json( '/run_completer_command', BuildRequest( filetype = 'java', command_arguments = [ 'StopServer' ], ), ) response = app.post_json( '/receive_messages', BuildRequest( filetype = 'java', filepath = filepath, contents = contents ) ).json assert_that( response, equal_to( False ) ) @IsolatedYcmd() def PollForMessages_AbortedWhenServerDies_test( app ): StartJavaCompleterServerInDirectory( app, PathToTestFile( 'simple_eclipse_project' ) ) filepath = TestFactory contents = ReadFile( filepath ) state = { 'aborted': False } def AwaitMessages(): max_tries = 20 for tries in range( 0, max_tries ): response = app.post_json( '/receive_messages', BuildRequest( filetype = 'java', filepath = filepath, contents = contents ) ).json if response is False: state[ 'aborted' ] = True return raise AssertionError( f'The poll request was not aborted in { max_tries } tries' ) message_poll_task = StartThread( AwaitMessages ) app.post_json( '/run_completer_command', BuildRequest( filetype = 'java', command_arguments = [ 'StopServer' ], ), ) message_poll_task.join() assert_that( state[ 'aborted' ] ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ����������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/get_completions_test.py�������������������������������0000664�0000000�0000000�00000066721�13746324601�0024776�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2017-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_exactly, contains_inanyorder, empty, equal_to, matches_regexp, has_entries, has_item, has_items, has_key, instance_of, is_not ) from pprint import pformat import requests import os from ycmd import handlers from ycmd.tests.java import ( DEFAULT_PROJECT_DIR, IsolatedYcmd, PathToTestFile, SharedYcmd ) from ycmd.tests.test_utils import ( ClearCompletionsCache, CombineRequest, ChunkMatcher, CompletionEntryMatcher, ErrorMatcher, LocationMatcher, WithRetry, UnixOnly ) from ycmd.utils import ReadFile from unittest.mock import patch from ycmd.completers.completer import CompletionsChanged def ProjectPath( *args ): return PathToTestFile( DEFAULT_PROJECT_DIR, 'src', 'com', 'test', *args ) def RunTest( app, test ): """ Method to run a simple completion test and verify the result test is a dictionary containing: 'request': kwargs for BuildRequest 'expect': { 'response': server response code (e.g. httplib.OK) 'data': matcher for the server response json } """ ClearCompletionsCache() contents = ReadFile( test[ 'request' ][ 'filepath' ] ) app.post_json( '/event_notification', CombineRequest( test[ 'request' ], { 'event_name': 'FileReadyToParse', 'contents': contents, } ), expect_errors = True ) # We ignore errors here and we check the response code ourself. # This is to allow testing of requests returning errors. request = CombineRequest( test[ 'request' ], { 'contents': contents } ) response = app.post_json( '/completions', request, expect_errors = True ) print( f'completer response: { pformat( response.json ) }' ) assert_that( response.status_code, equal_to( test[ 'expect' ][ 'response' ] ) ) assert_that( response.json, test[ 'expect' ][ 'data' ] ) return request, response.json PUBLIC_OBJECT_METHODS = [ CompletionEntryMatcher( 'equals', 'Object.equals(Object arg0) : boolean', { 'kind': 'Method' } ), CompletionEntryMatcher( 'getClass', 'Object.getClass() : Class<?>', { 'kind': 'Method' } ), CompletionEntryMatcher( 'hashCode', 'Object.hashCode() : int', { 'kind': 'Method' } ), CompletionEntryMatcher( 'notify', 'Object.notify() : void', { 'kind': 'Method' } ), CompletionEntryMatcher( 'notifyAll', 'Object.notifyAll() : void', { 'kind': 'Method' } ), CompletionEntryMatcher( 'toString', 'Object.toString() : String', { 'kind': 'Method' } ), CompletionEntryMatcher( 'wait', 'Object.wait(long arg0, int arg1) : void', { 'menu_text': matches_regexp( 'wait\\(long .*, int .*\\) : void' ), 'kind': 'Method', } ), CompletionEntryMatcher( 'wait', 'Object.wait(long arg0) : void', { 'menu_text': matches_regexp( 'wait\\(long .*\\) : void' ), 'kind': 'Method', } ), CompletionEntryMatcher( 'wait', 'Object.wait() : void', { 'menu_text': 'wait() : void', 'kind': 'Method', } ), ] # The zealots that designed java made everything inherit from Object (except, # possibly Object, or Class, or whichever one they used to break the Smalltalk # infinite recursion problem). Anyway, that means that we get a lot of noise # suggestions from the Object Class interface. This allows us to write: # # contains_inanyorder( *WithObjectMethods( CompletionEntryMatcher( ... ) ) ) # # and focus on what we care about. def WithObjectMethods( *args ): return list( PUBLIC_OBJECT_METHODS ) + list( args ) @WithRetry @SharedYcmd def GetCompletions_NoQuery_test( app ): RunTest( app, { 'description': 'semantic completion works for builtin types (no query)', 'request': { 'filetype' : 'java', 'filepath' : ProjectPath( 'TestFactory.java' ), 'line_num' : 27, 'column_num': 12, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': has_items( CompletionEntryMatcher( 'test', 'TestFactory.Bar.test : int', { 'kind': 'Field' } ), CompletionEntryMatcher( 'testString', 'TestFactory.Bar.testString : String', { 'kind': 'Field' } ) ), 'errors': empty(), } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_WithQuery_test( app ): RunTest( app, { 'description': 'semantic completion works for builtin types (with query)', 'request': { 'filetype' : 'java', 'filepath' : ProjectPath( 'TestFactory.java' ), 'line_num' : 27, 'column_num': 15, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_inanyorder( CompletionEntryMatcher( 'test', 'TestFactory.Bar.test : int', { 'kind': 'Field' } ), CompletionEntryMatcher( 'testString', 'TestFactory.Bar.testString : String', { 'kind': 'Field' } ) ), 'errors': empty(), } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_DetailFromCache_test( app ): for i in range( 0, 2 ): RunTest( app, { 'description': 'completion works when the elements come from the cache', 'request': { 'filetype' : 'java', 'filepath' : ProjectPath( 'TestLauncher.java' ), 'line_num' : 32, 'column_num': 15, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 11, 'completions': has_item( CompletionEntryMatcher( 'doSomethingVaguelyUseful', 'AbstractTestWidget.doSomethingVaguelyUseful() : void', { 'kind': 'Method', 'menu_text': 'doSomethingVaguelyUseful() : void', } ) ), 'errors': empty(), } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_Package_test( app ): RunTest( app, { 'description': 'completion works for package statements', 'request': { 'filetype' : 'java', 'filepath' : ProjectPath( 'wobble', 'Wibble.java' ), 'line_num' : 1, 'column_num': 18, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 9, 'completions': contains_exactly( CompletionEntryMatcher( 'com.test.wobble', None, { 'kind': 'Module' } ), ), 'errors': empty(), } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_Import_Class_test( app ): RunTest( app, { 'description': 'completion works for import statements with a single class', 'request': { 'filetype' : 'java', 'filepath' : ProjectPath( 'TestLauncher.java' ), 'line_num' : 3, 'column_num': 34, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 34, 'completions': contains_exactly( CompletionEntryMatcher( 'Tset', 'com.youcompleteme.testing.Tset', { 'menu_text': 'Tset - com.youcompleteme.testing', 'kind': 'Class', } ) ), 'errors': empty(), } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_Import_Classes_test( app ): filepath = ProjectPath( 'TestLauncher.java' ) RunTest( app, { 'description': 'completion works for imports with multiple classes', 'request': { 'filetype' : 'java', 'filepath' : filepath, 'line_num' : 4, 'column_num': 52, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 52, 'completions': contains_exactly( CompletionEntryMatcher( 'A;', None, { 'menu_text': 'A - com.test.wobble', 'kind': 'Class', } ), CompletionEntryMatcher( 'A_Very_Long_Class_Here;', None, { 'menu_text': 'A_Very_Long_Class_Here - com.test.wobble', 'kind': 'Class', } ), CompletionEntryMatcher( 'Waggle;', None, { 'menu_text': 'Waggle - com.test.wobble', 'kind': 'Interface', } ), CompletionEntryMatcher( 'Wibble;', None, { 'menu_text': 'Wibble - com.test.wobble', 'kind': 'Enum', } ), ), 'errors': empty(), } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_Import_ModuleAndClass_test( app ): filepath = ProjectPath( 'TestLauncher.java' ) RunTest( app, { 'description': 'completion works for imports of classes and modules', 'request': { 'filetype' : 'java', 'filepath' : filepath, 'line_num' : 3, 'column_num': 26, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 26, 'completions': contains_exactly( CompletionEntryMatcher( 'testing.*;', None, { 'menu_text': 'com.youcompleteme.testing', 'kind': 'Module', } ), CompletionEntryMatcher( 'Test;', None, { 'menu_text': 'Test - com.youcompleteme', 'kind': 'Class', } ), ), 'errors': empty(), } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_WithFixIt_test( app ): filepath = ProjectPath( 'TestFactory.java' ) RunTest( app, { 'description': 'semantic completion with when additional textEdit', 'request': { 'filetype' : 'java', 'filepath' : filepath, 'line_num' : 19, 'column_num': 25, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 22, 'completions': contains_inanyorder( CompletionEntryMatcher( 'CUTHBERT', 'com.test.wobble.Wibble.CUTHBERT : Wibble', { 'kind': 'EnumMember', 'extra_data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( 'Wibble', LocationMatcher( filepath, 19, 15 ), LocationMatcher( filepath, 19, 21 ) ), # OK, so it inserts the import ChunkMatcher( '\n\nimport com.test.wobble.Wibble;\n\n', LocationMatcher( filepath, 1, 18 ), LocationMatcher( filepath, 3, 1 ) ), ), } ) ), } ), } ), ), 'errors': empty(), } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_RejectMultiLineInsertion_test( app ): filepath = ProjectPath( 'TestLauncher.java' ) RunTest( app, { 'description': 'completion item discarded when not valid', 'request': { 'filetype' : 'java', 'filepath' : filepath, 'line_num' : 28, 'column_num' : 16, 'force_semantic': True }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 16, 'completions': contains_exactly( CompletionEntryMatcher( 'TestLauncher', 'com.test.TestLauncher.TestLauncher(int test)', { 'kind': 'Constructor' } ) # Note: There would be a suggestion here for the _real_ thing we want, # which is a TestLauncher.Launchable, but this would generate the code # for an anonymous inner class via a completion TextEdit (not # AdditionalTextEdit) which we don't support. ), 'errors': empty(), } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_UnicodeIdentifier_test( app ): filepath = PathToTestFile( DEFAULT_PROJECT_DIR, 'src', 'com', 'youcompleteme', 'Test.java' ) RunTest( app, { 'description': 'Completion works for unicode identifier', 'request': { 'filetype' : 'java', 'filepath' : filepath, 'line_num' : 16, 'column_num' : 35, 'force_semantic': True }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 35, 'completions': has_items( CompletionEntryMatcher( 'a_test', 'Test.TéstClass.a_test : int', { 'kind': 'Field', 'detailed_info': 'a_test : int\n\n', } ), CompletionEntryMatcher( 'åtest', 'Test.TéstClass.åtest : boolean', { 'kind': 'Field', 'detailed_info': 'åtest : boolean\n\n', } ), CompletionEntryMatcher( 'testywesty', 'Test.TéstClass.testywesty : String', { 'kind': 'Field', } ), ), 'errors': empty(), } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_ResolveFailed_test( app ): filepath = PathToTestFile( DEFAULT_PROJECT_DIR, 'src', 'com', 'youcompleteme', 'Test.java' ) from ycmd.completers.language_server import language_server_protocol as lsapi def BrokenResolveCompletion( request_id, completion ): return lsapi.BuildRequest( request_id, 'completionItem/FAIL', completion ) with patch( 'ycmd.completers.language_server.language_server_protocol.' 'ResolveCompletion', side_effect = BrokenResolveCompletion ): RunTest( app, { 'description': 'Completion works for unicode identifier', 'request': { 'filetype' : 'java', 'filepath' : filepath, 'line_num' : 16, 'column_num' : 35, 'force_semantic': True }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 35, 'completions': has_items( CompletionEntryMatcher( 'a_test', 'Test.TéstClass.a_test : int', { 'kind': 'Field', 'detailed_info': 'a_test : int\n\n', } ), CompletionEntryMatcher( 'åtest', 'Test.TéstClass.åtest : boolean', { 'kind': 'Field', 'detailed_info': 'åtest : boolean\n\n', } ), CompletionEntryMatcher( 'testywesty', 'Test.TéstClass.testywesty : String', { 'kind': 'Field', } ), ), 'errors': empty(), } ) }, } ) @WithRetry @IsolatedYcmd() def GetCompletions_ServerNotInitialized_test( app ): filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'AbstractTestWidget.java' ) completer = handlers._server_state.GetFiletypeCompleter( [ 'java' ] ) def MockHandleInitializeInPollThread( self, response ): pass with patch.object( completer, '_HandleInitializeInPollThread', MockHandleInitializeInPollThread ): RunTest( app, { 'description': 'Completion works for unicode identifier', 'request': { 'filetype' : 'java', 'filepath' : filepath, 'line_num' : 16, 'column_num' : 35, 'force_semantic': True }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'completions': empty(), 'completion_start_column': 6 } ), } } ) @WithRetry @SharedYcmd def GetCompletions_MoreThan10_NoResolve_ThenResolve_test( app ): ClearCompletionsCache() request, response = RunTest( app, { 'description': "More than 10 candiates after filtering, don't resolve", 'request': { 'filetype' : 'java', 'filepath' : ProjectPath( 'TestWithDocumentation.java' ), 'line_num' : 6, 'column_num': 7, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': has_item( CompletionEntryMatcher( 'useAString', 'MethodsWithDocumentation.useAString(String s) : void', { 'kind': 'Method', # This is the un-resolved info (no documentation) 'detailed_info': 'useAString(String s) : void\n\n', 'extra_data': has_entries( { 'resolve': instance_of( int ) } ) } ), ), 'completion_start_column': 7, 'errors': empty(), } ) }, } ) # We know the item we want is there, pull out the resolve ID resolve = None for item in response[ 'completions' ]: if item[ 'insertion_text' ] == 'useAString': resolve = item[ 'extra_data' ][ 'resolve' ] break assert resolve is not None request[ 'resolve' ] = resolve # Do this twice to prove that the request is idempotent for i in range( 2 ): response = app.post_json( '/resolve_completion', request ).json print( f"Resolve response: { pformat( response ) }" ) nl = os.linesep assert_that( response, has_entries( { 'completion': CompletionEntryMatcher( 'useAString', 'MethodsWithDocumentation.useAString(String s) : void', { 'kind': 'Method', # This is the resolved info (no documentation) 'detailed_info': 'useAString(String s) : void\n' '\n' f'Multiple lines of description here.{ nl }' f'{ nl }' f' * **Parameters:**{ nl }' f' { nl }' f' * **s** a string' } ), 'errors': empty(), } ) ) # The item is resoled assert_that( response[ 'completion' ], is_not( has_key( 'resolve' ) ) ) assert_that( response[ 'completion' ], is_not( has_key( 'item' ) ) ) @WithRetry @SharedYcmd def GetCompletions_FewerThan10_Resolved_test( app ): ClearCompletionsCache() nl = os.linesep request, response = RunTest( app, { 'description': "More than 10 candiates after filtering, don't resolve", 'request': { 'filetype' : 'java', 'filepath' : ProjectPath( 'TestWithDocumentation.java' ), 'line_num' : 6, 'column_num': 10, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': has_item( CompletionEntryMatcher( 'useAString', 'MethodsWithDocumentation.useAString(String s) : void', { 'kind': 'Method', # This is the resolved info (no documentation) 'detailed_info': 'useAString(String s) : void\n' '\n' f'Multiple lines of description here.{ nl }' f'{ nl }' f' * **Parameters:**{ nl }' f' { nl }' f' * **s** a string' } ), ), 'completion_start_column': 7, 'errors': empty(), } ) }, } ) # All items are resolved assert_that( response[ 'completions' ][ 0 ], is_not( has_key( 'resolve' ) ) ) assert_that( response[ 'completions' ][ 0 ], is_not( has_key( 'item' ) ) ) assert_that( response[ 'completions' ][ -1 ], is_not( has_key( 'resolve' ) ) ) assert_that( response[ 'completions' ][ -1 ], is_not( has_key( 'item' ) ) ) @WithRetry @SharedYcmd def GetCompletions_MoreThan10_NoResolve_ThenResolveCacheBad_test( app ): ClearCompletionsCache() request, response = RunTest( app, { 'description': "More than 10 candiates after filtering, don't resolve", 'request': { 'filetype' : 'java', 'filepath' : ProjectPath( 'TestWithDocumentation.java' ), 'line_num' : 6, 'column_num': 7, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': has_item( CompletionEntryMatcher( 'useAString', 'MethodsWithDocumentation.useAString(String s) : void', { 'kind': 'Method', # This is the un-resolved info (no documentation) 'detailed_info': 'useAString(String s) : void\n\n', 'extra_data': has_entries( { 'resolve': instance_of( int ) } ) } ), ), 'completion_start_column': 7, 'errors': empty(), } ) }, } ) # We know the item we want is there, pull out the resolve ID resolve = None for item in response[ 'completions' ]: if item[ 'insertion_text' ] == 'useAString': resolve = item[ 'extra_data' ][ 'resolve' ] break assert resolve is not None request[ 'resolve' ] = resolve # Use a different position - should mean the cache is not valid for request request[ 'column_num' ] = 20 response = app.post_json( '/resolve_completion', request ).json print( f"Resolve response: { pformat( response ) }" ) assert_that( response, has_entries( { 'completion': None, 'errors': contains_exactly( ErrorMatcher( CompletionsChanged ) ) } ) ) @WithRetry @UnixOnly @SharedYcmd def GetCompletions_MoreThan10ForceSemantic_test( app ): ClearCompletionsCache() RunTest( app, { 'description': 'When forcing we pass the query, which reduces candidates', 'request': { 'filetype' : 'java', 'filepath' : ProjectPath( 'TestLauncher.java' ), 'line_num' : 4, 'column_num': 15, 'force_semantic': True }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_exactly( CompletionEntryMatcher( 'com.youcompleteme.*;', None, { 'kind': 'Module', 'detailed_info': 'com.youcompleteme\n\n', } ), CompletionEntryMatcher( 'com.youcompleteme.testing.*;', None, { 'kind': 'Module', 'detailed_info': 'com.youcompleteme.testing\n\n', } ), ), 'completion_start_column': 8, 'errors': empty(), } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_ForceAtTopLevel_NoImport_test( app ): RunTest( app, { 'description': 'When forcing semantic completion, pass the query to server', 'request': { 'filetype' : 'java', 'filepath' : ProjectPath( 'TestWidgetImpl.java' ), 'line_num' : 30, 'column_num': 20, 'force_semantic': True, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_exactly( CompletionEntryMatcher( 'TestFactory', None, { 'kind': 'Class', 'menu_text': 'TestFactory - com.test', } ), ), 'completion_start_column': 12, 'errors': empty(), } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_NoForceAtTopLevel_NoImport_test( app ): RunTest( app, { 'description': 'When not forcing semantic completion, use no context', 'request': { 'filetype' : 'java', 'filepath' : ProjectPath( 'TestWidgetImpl.java' ), 'line_num' : 30, 'column_num': 20, 'force_semantic': False, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_exactly( CompletionEntryMatcher( 'TestFactory', '[ID]', {} ), ), 'completion_start_column': 12, 'errors': empty(), } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_ForceAtTopLevel_WithImport_test( app ): filepath = ProjectPath( 'TestWidgetImpl.java' ) RunTest( app, { 'description': 'Top level completions have import FixIts', 'request': { 'filetype' : 'java', 'filepath' : filepath, 'line_num' : 34, 'column_num': 16, 'force_semantic': True, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': has_item( CompletionEntryMatcher( 'InputStreamReader', None, { 'kind': 'Class', 'menu_text': 'InputStreamReader - java.io', 'extra_data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( '\n\nimport java.io.InputStreamReader;\n\n', LocationMatcher( filepath, 1, 18 ), LocationMatcher( filepath, 3, 1 ) ), ), } ) ), } ), } ), ), 'completion_start_column': 12, 'errors': empty(), } ) }, } ) @WithRetry @SharedYcmd def GetCompletions_UseServerTriggers_test( app ): filepath = ProjectPath( 'TestWidgetImpl.java' ) RunTest( app, { 'description': 'We use the semantic triggers from the server (@ here)', 'request': { 'filetype' : 'java', 'filepath' : filepath, 'line_num' : 24, 'column_num': 7, 'force_semantic': False, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completion_start_column': 4, 'completions': has_item( CompletionEntryMatcher( 'Override', None, { 'kind': 'Interface', 'menu_text': 'Override - java.lang', } ) ) } ) } } ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �����������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/java_completer_test.py��������������������������������0000664�0000000�0000000�00000022422�13746324601�0024564�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import os import requests from hamcrest import assert_that, equal_to, calling, has_entries, is_not, raises from unittest.mock import patch from ycmd import handlers, user_options_store from ycmd.tests.test_utils import BuildRequest, ErrorMatcher from ycmd.tests.java import SharedYcmd from ycmd.completers.java import java_completer, hook from ycmd.completers.java.java_completer import NO_DOCUMENTATION_MESSAGE from ycmd.tests import IsolatedYcmd as IsolatedYcmdWithoutJava DEFAULT_OPTIONS = user_options_store.DefaultOptions() @patch( 'ycmd.completers.java.java_completer.utils.FindExecutable', return_value = '' ) def ShouldEnableJavaCompleter_NoJava_test( *args ): assert_that( java_completer.ShouldEnableJavaCompleter( DEFAULT_OPTIONS ), equal_to( False ) ) @IsolatedYcmdWithoutJava( { 'java_binary_path': '/this/path/does/not/exist' } ) def ShouldEnableJavaCompleter_JavaNotFound_test( app ): request_data = BuildRequest( filetype = 'java' ) response = app.post_json( '/defined_subcommands', request_data, expect_errors = True ) assert_that( response.status_code, equal_to( requests.codes.internal_server_error ) ) assert_that( response.json, ErrorMatcher( ValueError, 'No semantic completer exists for filetypes: ' "['java']" ) ) def ShouldEnableJavaCompleter_NotInstalled_test(): orig_language_server_home = java_completer.LANGUAGE_SERVER_HOME try: java_completer.LANGUAGE_SERVER_HOME = '' assert_that( java_completer.ShouldEnableJavaCompleter( DEFAULT_OPTIONS ), equal_to( False ) ) finally: java_completer.LANGUAGE_SERVER_HOME = orig_language_server_home @patch( 'glob.glob', return_value = [] ) def ShouldEnableJavaCompleter_NoLauncherJar_test( glob ): assert_that( java_completer.ShouldEnableJavaCompleter( DEFAULT_OPTIONS ), equal_to( False ) ) glob.assert_called() def WorkspaceDirForProject_HashProjectDir_test(): assert_that( java_completer._WorkspaceDirForProject( os.getcwd(), os.getcwd(), False ), equal_to( java_completer._WorkspaceDirForProject( os.getcwd(), os.getcwd(), False ) ) ) def WorkspaceDirForProject_UniqueDir_test(): assert_that( java_completer._WorkspaceDirForProject( os.getcwd(), os.getcwd(), True ), is_not( equal_to( java_completer._WorkspaceDirForProject( os.getcwd(), os.getcwd(), True ) ) ) ) @SharedYcmd def JavaCompleter_GetType_test( app ): completer = handlers._server_state.GetFiletypeCompleter( [ 'java' ] ) # The LSP defines the hover response as either: # - a string # - a list of strings # - an object with keys language, value # - a list of objects with keys language, value # = an object with keys kind, value with patch.object( completer, 'GetHoverResponse', return_value = '' ): assert_that( calling( completer.GetType ).with_args( BuildRequest() ), raises( RuntimeError, 'Unknown type' ) ) with patch.object( completer, 'GetHoverResponse', return_value = 'string' ): assert_that( calling( completer.GetType ).with_args( BuildRequest() ), raises( RuntimeError, 'Unknown type' ) ) with patch.object( completer, 'GetHoverResponse', return_value = 'value' ): assert_that( calling( completer.GetType ).with_args( BuildRequest() ), raises( RuntimeError, 'Unknown type' ) ) with patch.object( completer, 'GetHoverResponse', return_value = [] ): assert_that( calling( completer.GetType ).with_args( BuildRequest() ), raises( RuntimeError, 'Unknown type' ) ) with patch.object( completer, 'GetHoverResponse', return_value = [ 'a', 'b' ] ): assert_that( calling( completer.GetType ).with_args( BuildRequest() ), raises( RuntimeError, 'Unknown type' ) ) with patch.object( completer, 'GetHoverResponse', return_value = { 'language': 'java', 'value': 'test' } ): assert_that( completer.GetType( BuildRequest() ), has_entries( { 'message': 'test' } ) ) with patch.object( completer, 'GetHoverResponse', return_value = [ { 'language': 'java', 'value': 'test' } ] ): assert_that( completer.GetType( BuildRequest() ), has_entries( { 'message': 'test' } ) ) with patch.object( completer, 'GetHoverResponse', return_value = [ { 'language': 'java', 'value': 'test' }, { 'language': 'java', 'value': 'not test' } ] ): assert_that( completer.GetType( BuildRequest() ), has_entries( { 'message': 'test' } ) ) with patch.object( completer, 'GetHoverResponse', return_value = [ { 'language': 'java', 'value': 'test' }, 'line 1', 'line 2' ] ): assert_that( completer.GetType( BuildRequest() ), has_entries( { 'message': 'test' } ) ) with patch.object( completer, 'GetHoverResponse', return_value = { 'kind': 'plaintext', 'value': 'test' } ): assert_that( calling( completer.GetType ).with_args( BuildRequest() ), raises( RuntimeError, 'Unknown type' ) ) @SharedYcmd def JavaCompleter_GetDoc_test( app ): completer = handlers._server_state.GetFiletypeCompleter( [ 'java' ] ) # The LSP defines the hover response as either: # - a string # - a list of strings # - an object with keys language, value # - a list of objects with keys language, value # = an object with keys kind, value with patch.object( completer, 'GetHoverResponse', return_value = '' ): assert_that( calling( completer.GetDoc ).with_args( BuildRequest() ), raises( RuntimeError, NO_DOCUMENTATION_MESSAGE ) ) with patch.object( completer, 'GetHoverResponse', return_value = 'string' ): assert_that( calling( completer.GetDoc ).with_args( BuildRequest() ), raises( RuntimeError, NO_DOCUMENTATION_MESSAGE ) ) with patch.object( completer, 'GetHoverResponse', return_value = [] ): assert_that( calling( completer.GetDoc ).with_args( BuildRequest() ), raises( RuntimeError, NO_DOCUMENTATION_MESSAGE ) ) with patch.object( completer, 'GetHoverResponse', return_value = [ 'a', 'b' ] ): assert_that( completer.GetDoc( BuildRequest() ), has_entries( { 'detailed_info': 'a\nb' } ) ) with patch.object( completer, 'GetHoverResponse', return_value = { 'language': 'java', 'value': 'test' } ): assert_that( calling( completer.GetDoc ).with_args( BuildRequest() ), raises( RuntimeError, NO_DOCUMENTATION_MESSAGE ) ) with patch.object( completer, 'GetHoverResponse', return_value = [ { 'language': 'java', 'value': 'test' } ] ): assert_that( calling( completer.GetDoc ).with_args( BuildRequest() ), raises( RuntimeError, NO_DOCUMENTATION_MESSAGE ) ) with patch.object( completer, 'GetHoverResponse', return_value = [ { 'language': 'java', 'value': 'test' }, { 'language': 'java', 'value': 'not test' } ] ): assert_that( calling( completer.GetDoc ).with_args( BuildRequest() ), raises( RuntimeError, NO_DOCUMENTATION_MESSAGE ) ) with patch.object( completer, 'GetHoverResponse', return_value = [ { 'language': 'java', 'value': 'test' }, 'line 1', 'line 2' ] ): assert_that( completer.GetDoc( BuildRequest() ), has_entries( { 'detailed_info': 'line 1\nline 2' } ) ) with patch.object( completer, 'GetHoverResponse', return_value = { 'kind': 'plaintext', 'value': 'test' } ): assert_that( calling( completer.GetDoc ).with_args( BuildRequest() ), raises( RuntimeError, NO_DOCUMENTATION_MESSAGE ) ) @patch( 'ycmd.completers.java.hook.ShouldEnableJavaCompleter', return_value = False ) def JavaHook_JavaNotEnabled_test( *args ): assert_that( hook.GetCompleter( {} ), equal_to( None ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/server_management_test.py�����������������������������0000664�0000000�0000000�00000047262�13746324601�0025304�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2017-2020 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 <http://www.gnu.org/licenses/>. import functools import os import psutil import requests import time from unittest.mock import patch from hamcrest import ( assert_that, contains_exactly, equal_to, has_entries, has_entry, has_item, starts_with ) from ycmd.completers.language_server.language_server_completer import ( LanguageServerConnectionTimeout ) from ycmd.tests.java import ( PathToTestFile, IsolatedYcmd, SharedYcmd, StartJavaCompleterServerInDirectory, StartJavaCompleterServerWithFile ) from ycmd.tests.test_utils import ( BuildRequest, CompleterProjectDirectoryMatcher, ErrorMatcher, MockProcessTerminationTimingOut, TemporaryTestDir, WaitUntilCompleterServerReady ) from ycmd import utils, handlers def TidyJDTProjectFiles( dir_name ): """Defines a test decorator which deletes the .project etc. files that are created by the jdt.ls server when it detects a project. This ensures the tests actually check that jdt.ls detects the project.""" def decorator( test ): @functools.wraps( test ) def Wrapper( *args, **kwargs ): utils.RemoveIfExists( os.path.join( dir_name, '.project' ) ) utils.RemoveIfExists( os.path.join( dir_name, '.classpath' ) ) utils.RemoveDirIfExists( os.path.join( dir_name, '.settings' ) ) try: test( *args, **kwargs ) finally: utils.RemoveIfExists( os.path.join( dir_name, '.project' ) ) utils.RemoveIfExists( os.path.join( dir_name, '.classpath' ) ) utils.RemoveDirIfExists( os.path.join( dir_name, '.settings' ) ) return Wrapper return decorator @TidyJDTProjectFiles( PathToTestFile( 'simple_maven_project' ) ) @IsolatedYcmd() def ServerManagement_RestartServer_test( app ): StartJavaCompleterServerInDirectory( app, PathToTestFile( 'simple_eclipse_project' ) ) eclipse_project = PathToTestFile( 'simple_eclipse_project' ) maven_project = PathToTestFile( 'simple_maven_project' ) # Run the debug info to check that we have the correct project dir request_data = BuildRequest( filetype = 'java' ) assert_that( app.post_json( '/debug_info', request_data ).json, CompleterProjectDirectoryMatcher( eclipse_project ) ) # Restart the server with a different client working directory filepath = PathToTestFile( 'simple_maven_project', 'src', 'main', 'java', 'com', 'test', 'TestFactory.java' ) app.post_json( '/run_completer_command', BuildRequest( filepath = filepath, filetype = 'java', working_dir = maven_project, command_arguments = [ 'RestartServer' ], ), ) WaitUntilCompleterServerReady( app, 'java' ) app.post_json( '/event_notification', BuildRequest( filepath = filepath, filetype = 'java', working_dir = maven_project, event_name = 'FileReadyToParse', ) ) # Run the debug info to check that we have the correct project dir request_data = BuildRequest( filetype = 'java' ) assert_that( app.post_json( '/debug_info', request_data ).json, CompleterProjectDirectoryMatcher( maven_project ) ) def ServerManagement_WipeWorkspace_NoConfig_test( isolated_app ): with TemporaryTestDir() as tmp_dir: with isolated_app( { 'java_jdtls_use_clean_workspace': 0, 'java_jdtls_workspace_root_path': tmp_dir } ) as app: StartJavaCompleterServerInDirectory( app, PathToTestFile( 'simple_eclipse_project', 'src' ) ) project = PathToTestFile( 'simple_eclipse_project' ) filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'youcompleteme', 'Test.java' ) app.post_json( '/run_completer_command', BuildRequest( filepath = filepath, filetype = 'java', command_arguments = [ 'WipeWorkspace' ], ), ) WaitUntilCompleterServerReady( app, 'java' ) assert_that( app.post_json( '/debug_info', BuildRequest( filetype = 'java', filepath = filepath ) ).json, CompleterProjectDirectoryMatcher( project ) ) assert_that( app.post_json( '/debug_info', BuildRequest( filetype = 'java', filepath = filepath ) ).json, has_entry( 'completer', has_entry( 'servers', contains_exactly( has_entry( 'extras', has_item( has_entries( { 'key': 'Workspace Path', 'value': starts_with( tmp_dir ), } ) ) ) ) ) ) ) def ServerManagement_WipeWorkspace_WithConfig_test( isolated_app ): with TemporaryTestDir() as tmp_dir: with isolated_app( { 'java_jdtls_use_clean_workspace': 0, 'java_jdtls_workspace_root_path': tmp_dir } ) as app: StartJavaCompleterServerInDirectory( app, PathToTestFile( 'simple_eclipse_project', 'src' ) ) project = PathToTestFile( 'simple_eclipse_project' ) filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'youcompleteme', 'Test.java' ) app.post_json( '/run_completer_command', BuildRequest( filepath = filepath, filetype = 'java', command_arguments = [ 'WipeWorkspace', '--with-config' ], ), ) WaitUntilCompleterServerReady( app, 'java' ) assert_that( app.post_json( '/debug_info', BuildRequest( filetype = 'java', filepath = filepath ) ).json, CompleterProjectDirectoryMatcher( project ) ) assert_that( app.post_json( '/debug_info', BuildRequest( filetype = 'java', filepath = filepath ) ).json, has_entry( 'completer', has_entry( 'servers', contains_exactly( has_entry( 'extras', has_item( has_entries( { 'key': 'Workspace Path', 'value': starts_with( tmp_dir ), } ) ) ) ) ) ) ) @IsolatedYcmd( { 'extra_conf_globlist': PathToTestFile( 'multiple_projects', '*' ) } ) def ServerManagement_ProjectDetection_MultipleProjects_test( app ): # The ycm_extra_conf.py file should set the project path to # multiple_projects/src project = PathToTestFile( 'multiple_projects', 'src' ) StartJavaCompleterServerWithFile( app, os.path.join( project, 'core', 'java', 'com', 'puremourning', 'widget', 'core', 'Utils.java' ) ) # Run the debug info to check that we have the correct project dir request_data = BuildRequest( filetype = 'java' ) assert_that( app.post_json( '/debug_info', request_data ).json, CompleterProjectDirectoryMatcher( project ) ) @IsolatedYcmd() def ServerManagement_ProjectDetection_EclipseParent_test( app ): StartJavaCompleterServerInDirectory( app, PathToTestFile( 'simple_eclipse_project', 'src' ) ) project = PathToTestFile( 'simple_eclipse_project' ) # Run the debug info to check that we have the correct project dir request_data = BuildRequest( filetype = 'java' ) assert_that( app.post_json( '/debug_info', request_data ).json, CompleterProjectDirectoryMatcher( project ) ) @TidyJDTProjectFiles( PathToTestFile( 'simple_maven_project' ) ) @IsolatedYcmd() def ServerManagement_ProjectDetection_MavenParent_test( app ): StartJavaCompleterServerInDirectory( app, PathToTestFile( 'simple_maven_project', 'src', 'main', 'java', 'com', 'test' ) ) project = PathToTestFile( 'simple_maven_project' ) # Run the debug info to check that we have the correct project dir request_data = BuildRequest( filetype = 'java' ) assert_that( app.post_json( '/debug_info', request_data ).json, CompleterProjectDirectoryMatcher( project ) ) @TidyJDTProjectFiles( PathToTestFile( 'simple_maven_project', 'simple_submodule' ) ) @TidyJDTProjectFiles( PathToTestFile( 'simple_maven_project' ) ) @IsolatedYcmd() def ServerManagement_ProjectDetection_MavenParent_Submodule_test( app ): StartJavaCompleterServerInDirectory( app, PathToTestFile( 'simple_maven_project', 'simple_submodule', 'src', 'main', 'java', 'com', 'test' ) ) project = PathToTestFile( 'simple_maven_project' ) # Run the debug info to check that we have the correct project dir request_data = BuildRequest( filetype = 'java' ) assert_that( app.post_json( '/debug_info', request_data ).json, CompleterProjectDirectoryMatcher( project ) ) @SharedYcmd def ServerManagement_OpenProject_RelativePathNoWD_test( app ): response = app.post_json( '/run_completer_command', BuildRequest( filetype = 'java', command_arguments = [ 'OpenProject', os.path.join( '..', 'simple_maven_project' ), ], ), expect_errors = True, ) assert_that( response.status_code, equal_to( requests.codes.internal_server_error ) ) assert_that( response.json, ErrorMatcher( ValueError, 'Project directory must be absolute' ) ) @SharedYcmd def ServerManagement_OpenProject_RelativePathNoPath_test( app ): response = app.post_json( '/run_completer_command', BuildRequest( filetype = 'java', command_arguments = [ 'OpenProject', ], ), expect_errors = True, ) assert_that( response.status_code, equal_to( requests.codes.internal_server_error ) ) assert_that( response.json, ErrorMatcher( ValueError, 'Usage: OpenProject <project directory>' ) ) def ServerManagement_ProjectDetection_NoParent_test( isolated_app ): with TemporaryTestDir() as tmp_dir: with isolated_app() as app: StartJavaCompleterServerInDirectory( app, tmp_dir ) # Run the debug info to check that we have the correct project dir (cwd) request_data = BuildRequest( filetype = 'java', filepath = os.path.join( tmp_dir, 'foo.java' ) ) assert_that( app.post_json( '/debug_info', request_data ).json, CompleterProjectDirectoryMatcher( tmp_dir ) ) @IsolatedYcmd() @patch( 'shutil.rmtree', side_effect = OSError ) @patch( 'ycmd.utils.WaitUntilProcessIsTerminated', MockProcessTerminationTimingOut ) def ServerManagement_CloseServer_Unclean_test( rm, app ): StartJavaCompleterServerInDirectory( app, PathToTestFile( 'simple_eclipse_project' ) ) app.post_json( '/run_completer_command', BuildRequest( filetype = 'java', command_arguments = [ 'StopServer' ] ) ) request_data = BuildRequest( filetype = 'java' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entry( 'servers', contains_exactly( has_entry( 'is_running', False ) ) ) ) ) @IsolatedYcmd() def ServerManagement_StopServerTwice_test( app ): StartJavaCompleterServerInDirectory( app, PathToTestFile( 'simple_eclipse_project' ) ) app.post_json( '/run_completer_command', BuildRequest( filetype = 'java', command_arguments = [ 'StopServer' ], ), ) request_data = BuildRequest( filetype = 'java' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entry( 'servers', contains_exactly( has_entry( 'is_running', False ) ) ) ) ) # Stopping a stopped server is a no-op app.post_json( '/run_completer_command', BuildRequest( filetype = 'java', command_arguments = [ 'StopServer' ], ), ) request_data = BuildRequest( filetype = 'java' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entry( 'servers', contains_exactly( has_entry( 'is_running', False ) ) ) ) ) @IsolatedYcmd() def ServerManagement_ServerDies_test( app ): StartJavaCompleterServerInDirectory( app, PathToTestFile( 'simple_eclipse_project' ) ) request_data = BuildRequest( filetype = 'java' ) debug_info = app.post_json( '/debug_info', request_data ).json print( f'Debug info: { debug_info }' ) pid = debug_info[ 'completer' ][ 'servers' ][ 0 ][ 'pid' ] print( f'pid: { pid }' ) process = psutil.Process( pid ) process.terminate() for tries in range( 0, 10 ): request_data = BuildRequest( filetype = 'java' ) debug_info = app.post_json( '/debug_info', request_data ).json if not debug_info[ 'completer' ][ 'servers' ][ 0 ][ 'is_running' ]: break time.sleep( 0.5 ) assert_that( debug_info, has_entry( 'completer', has_entry( 'servers', contains_exactly( has_entry( 'is_running', False ) ) ) ) ) @IsolatedYcmd() def ServerManagement_ServerDiesWhileShuttingDown_test( app ): StartJavaCompleterServerInDirectory( app, PathToTestFile( 'simple_eclipse_project' ) ) request_data = BuildRequest( filetype = 'java' ) debug_info = app.post_json( '/debug_info', request_data ).json print( f'Debug info: { debug_info }' ) pid = debug_info[ 'completer' ][ 'servers' ][ 0 ][ 'pid' ] print( f'pid: { pid }' ) process = psutil.Process( pid ) def StopServerInAnotherThread(): app.post_json( '/run_completer_command', BuildRequest( filetype = 'java', command_arguments = [ 'StopServer' ], ), ) completer = handlers._server_state.GetFiletypeCompleter( [ 'java' ] ) # In this test we mock out the sending method so that we don't actually send # the shutdown request. We then assisted-suicide the downstream server, which # causes the shutdown request to be aborted. This is interpreted by the # shutdown code as a successful shutdown. We need to do the shutdown and # terminate in parallel as the post_json is a blocking call. with patch.object( completer.GetConnection(), 'WriteData' ): stop_server_task = utils.StartThread( StopServerInAnotherThread ) process.terminate() stop_server_task.join() request_data = BuildRequest( filetype = 'java' ) debug_info = app.post_json( '/debug_info', request_data ).json assert_that( debug_info, has_entry( 'completer', has_entry( 'servers', contains_exactly( has_entry( 'is_running', False ) ) ) ) ) @IsolatedYcmd() def ServerManagement_ConnectionRaisesWhileShuttingDown_test( app ): StartJavaCompleterServerInDirectory( app, PathToTestFile( 'simple_eclipse_project' ) ) request_data = BuildRequest( filetype = 'java' ) debug_info = app.post_json( '/debug_info', request_data ).json print( f'Debug info: { debug_info }' ) pid = debug_info[ 'completer' ][ 'servers' ][ 0 ][ 'pid' ] print( f'pid: { pid }' ) process = psutil.Process( pid ) completer = handlers._server_state.GetFiletypeCompleter( [ 'java' ] ) # In this test we mock out the GetResponse method, which is used to send the # shutdown request. This means we only send the exit notification. It's # possible that the server won't like this, but it seems reasonable for it to # actually exit at that point. with patch.object( completer.GetConnection(), 'GetResponse', side_effect = RuntimeError ): app.post_json( '/run_completer_command', BuildRequest( filetype = 'java', command_arguments = [ 'StopServer' ], ), ) request_data = BuildRequest( filetype = 'java' ) debug_info = app.post_json( '/debug_info', request_data ).json assert_that( debug_info, has_entry( 'completer', has_entry( 'servers', contains_exactly( has_entry( 'is_running', False ) ) ) ) ) if process.is_running(): process.terminate() raise AssertionError( 'jst.ls process is still running after exit handler' ) @IsolatedYcmd() def ServerManagement_StartServer_Fails_test( app ): filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'youcompleteme', 'Test.java' ) with patch( 'ycmd.completers.language_server.language_server_completer.' 'LanguageServerConnection.AwaitServerConnection', side_effect = LanguageServerConnectionTimeout ): resp = app.post_json( '/event_notification', BuildRequest( event_name = 'FileReadyToParse', filetype = 'java', filepath = filepath, contents = "" ) ) assert_that( resp.status_code, equal_to( 200 ) ) request_data = BuildRequest( filetype = 'java' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entry( 'servers', contains_exactly( has_entry( 'is_running', False ) ) ) ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/signature_help_test.py��������������������������������0000664�0000000�0000000�00000013156�13746324601�0024606�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_exactly, empty, equal_to, has_entries ) import requests from ycmd.utils import ReadFile from ycmd.tests.java import PathToTestFile, SharedYcmd from ycmd.tests.test_utils import ( CombineRequest, ParameterMatcher, SignatureMatcher, SignatureAvailableMatcher, WaitUntilCompleterServerReady, WithRetry ) def ProjectPath( *args ): return PathToTestFile( 'extra_confs', 'simple_extra_conf_project', 'src', *args ) def RunTest( app, test ): """ Method to run a simple signature help test and verify the result test is a dictionary containing: 'request': kwargs for BuildRequest 'expect': { 'response': server response code (e.g. httplib.OK) 'data': matcher for the server response json } """ contents = ReadFile( test[ 'request' ][ 'filepath' ] ) app.post_json( '/event_notification', CombineRequest( test[ 'request' ], { 'event_name': 'FileReadyToParse', 'contents': contents, } ), expect_errors = True ) # We ignore errors here and we check the response code ourself. # This is to allow testing of requests returning errors. response = app.post_json( '/signature_help', CombineRequest( test[ 'request' ], { 'contents': contents } ), expect_errors = True ) assert_that( response.status_code, equal_to( test[ 'expect' ][ 'response' ] ) ) assert_that( response.json, test[ 'expect' ][ 'data' ] ) @WithRetry @SharedYcmd def SignatureHelp_MethodTrigger_test( app ): RunTest( app, { 'description': 'Trigger after (', 'request': { 'filetype' : 'java', 'filepath' : ProjectPath( 'SignatureHelp.java' ), 'line_num' : 9, 'column_num': 17, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': contains_exactly( SignatureMatcher( 'unique(double d) : void', [ ParameterMatcher( 7, 15 ) ] ) ), } ), } ) } } ) @WithRetry @SharedYcmd def SignatureHelp_ArgTrigger_test( app ): RunTest( app, { 'description': 'Trigger after ,', 'request': { 'filetype' : 'java', 'filepath' : ProjectPath( 'SignatureHelp.java' ), 'line_num' : 5, 'column_num': 23, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 1, 'activeParameter': 1, 'signatures': contains_exactly( SignatureMatcher( 'test(int i, String s) : void', [ ParameterMatcher( 5, 10 ), ParameterMatcher( 12, 20 ) ] ), SignatureMatcher( 'test(String s, String s1) : void', [ ParameterMatcher( 5, 13 ), ParameterMatcher( 15, 24 ) ] ) ), } ), } ) } } ) @WithRetry @SharedYcmd def SignatureHelp_Constructor_test( app ): RunTest( app, { 'description': 'Constructor', 'request': { 'filetype' : 'java', 'filepath' : ProjectPath( 'SignatureHelp.java' ), 'line_num' : 17, 'column_num': 41, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': contains_exactly( SignatureMatcher( 'SignatureHelp(String signature)', [ ParameterMatcher( 14, 30 ) ] ) ), } ), } ) } } ) @SharedYcmd def Signature_Help_Available_test( app ): request = { 'filepath' : ProjectPath( 'SignatureHelp.java' ) } app.post_json( '/event_notification', CombineRequest( request, { 'event_name': 'FileReadyToParse', 'filetype': 'java' } ), expect_errors = True ) WaitUntilCompleterServerReady( app, 'java' ) response = app.get( '/signature_help_available', { 'subserver': 'java' } ).json assert_that( response, SignatureAvailableMatcher( 'YES' ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/subcommands_test.py�����������������������������������0000664�0000000�0000000�00000241314�13746324601�0024107�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2017-2020 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 <http://www.gnu.org/licenses/>. import time from hamcrest import ( assert_that, contains_exactly, contains_inanyorder, empty, equal_to, has_entries, has_entry, instance_of, is_not, matches_regexp ) from pprint import pformat import requests import pytest import json from ycmd.utils import ReadFile from ycmd.completers.java.java_completer import NO_DOCUMENTATION_MESSAGE from ycmd.tests.java import ( PathToTestFile, SharedYcmd, StartJavaCompleterServerWithFile, IsolatedYcmd ) from ycmd.tests.test_utils import ( BuildRequest, ChunkMatcher, CombineRequest, ErrorMatcher, ExpectedFailure, LocationMatcher, WithRetry ) from unittest.mock import patch from ycmd import handlers from ycmd.completers.language_server import language_server_protocol as lsp from ycmd.completers.language_server.language_server_completer import ( ResponseTimeoutException, ResponseFailedException ) from ycmd.responses import UnknownExtraConf TESTLAUNCHER_JAVA = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestLauncher.java' ) TEST_JAVA = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'youcompleteme', 'Test.java' ) TSET_JAVA = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'youcompleteme', 'testing', 'Tset.java' ) @WithRetry @SharedYcmd def Subcommands_DefinedSubcommands_test( app ): subcommands_data = BuildRequest( completer_target = 'java' ) assert_that( app.post_json( '/defined_subcommands', subcommands_data ).json, contains_inanyorder( 'FixIt', 'ExecuteCommand', 'Format', 'GoToDeclaration', 'GoToDefinition', 'GoTo', 'GetDoc', 'GetType', 'GoToImplementation', 'GoToReferences', 'GoToType', 'GoToSymbol', 'OpenProject', 'OrganizeImports', 'RefactorRename', 'RestartServer', 'WipeWorkspace' ) ) @pytest.mark.parametrize( 'cmd,arguments', [ ( 'GoTo', [] ), ( 'GoToDeclaration', [] ), ( 'GoToDefinition', [] ), ( 'GoToReferences', [] ), ( 'GoToSymbol', [ 'test' ] ), ( 'GetType', [] ), ( 'GetDoc', [] ), ( 'FixIt', [] ), ( 'Format', [] ), ( 'OrganizeImports', [] ), ( 'RefactorRename', [ 'test' ] ), ] ) @SharedYcmd def Subcommands_ServerNotInitialized_test( app, cmd, arguments ): filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'AbstractTestWidget.java' ) completer = handlers._server_state.GetFiletypeCompleter( [ 'java' ] ) @patch.object( completer, '_ServerIsInitialized', return_value = False ) def Test( app, cmd, arguments, *args ): RunTest( app, { 'description': 'Subcommand ' + cmd + ' handles server not ready', 'request': { 'command': cmd, 'line_num': 1, 'column_num': 1, 'filepath': filepath, 'arguments': arguments, }, 'expect': { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( RuntimeError, 'Server is initializing. Please wait.' ), } } ) Test( app, cmd, arguments ) def RunTest( app, test, contents = None ): if not contents: contents = ReadFile( test[ 'request' ][ 'filepath' ] ) # Because we aren't testing this command, we *always* ignore errors. This # is mainly because we (may) want to test scenarios where the completer # throws an exception. app.post_json( '/event_notification', CombineRequest( test[ 'request' ], { 'event_name': 'FileReadyToParse', 'contents': contents, 'filetype': 'java', } ), expect_errors = True ) # We also ignore errors here, but then we check the response code # ourself. This is to allow testing of requests returning errors. expiry = time.time() + 10 while True: try: response = app.post_json( '/run_completer_command', CombineRequest( test[ 'request' ], { 'completer_target': 'filetype_default', 'contents': contents, 'filetype': 'java', 'command_arguments': ( [ test[ 'request' ][ 'command' ] ] + test[ 'request' ].get( 'arguments', [] ) ) } ), expect_errors = True ) print( f'completer response: { pformat( response.json ) }' ) assert_that( response.status_code, equal_to( test[ 'expect' ][ 'response' ] ) ) assert_that( response.json, test[ 'expect' ][ 'data' ] ) break except AssertionError: if time.time() > expiry: raise time.sleep( 0.25 ) @WithRetry @SharedYcmd def Subcommands_GetDoc_NoDoc_test( app ): filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'AbstractTestWidget.java' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'java', line_num = 18, column_num = 1, contents = contents, command_arguments = [ 'GetDoc' ], completer_target = 'filetype_default' ) response = app.post_json( '/run_completer_command', event_data, expect_errors = True ) assert_that( response.status_code, equal_to( requests.codes.internal_server_error ) ) assert_that( response.json, ErrorMatcher( RuntimeError, NO_DOCUMENTATION_MESSAGE ) ) @WithRetry @SharedYcmd def Subcommands_GetDoc_Method_test( app ): filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'AbstractTestWidget.java' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'java', line_num = 17, column_num = 17, contents = contents, command_arguments = [ 'GetDoc' ], completer_target = 'filetype_default' ) response = app.post_json( '/run_completer_command', event_data ).json assert_that( response, has_entry( 'detailed_info', 'Return runtime debugging info. Useful for finding the ' 'actual code which is useful.' ) ) @WithRetry @SharedYcmd def Subcommands_GetDoc_Class_test( app ): filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestWidgetImpl.java' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'java', line_num = 11, column_num = 7, contents = contents, command_arguments = [ 'GetDoc' ], completer_target = 'filetype_default' ) response = app.post_json( '/run_completer_command', event_data ).json assert_that( response, has_entry( 'detailed_info', 'This is the actual code that matters. This concrete ' 'implementation is the equivalent of the main function ' 'in other languages' ) ) @WithRetry @SharedYcmd def Subcommands_GetType_NoKnownType_test( app ): filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestWidgetImpl.java' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'java', line_num = 28, column_num = 1, contents = contents, command_arguments = [ 'GetType' ], completer_target = 'filetype_default' ) response = app.post_json( '/run_completer_command', event_data, expect_errors = True ) assert_that( response.status_code, equal_to( requests.codes.internal_server_error ) ) assert_that( response.json, ErrorMatcher( RuntimeError, 'Unknown type' ) ) @WithRetry @SharedYcmd def Subcommands_GetType_Class_test( app ): filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestWidgetImpl.java' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'java', line_num = 11, column_num = 7, contents = contents, command_arguments = [ 'GetType' ], completer_target = 'filetype_default' ) response = app.post_json( '/run_completer_command', event_data ).json assert_that( response, has_entry( 'message', 'com.test.TestWidgetImpl' ) ) @WithRetry @SharedYcmd def Subcommands_GetType_Constructor_test( app ): filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestWidgetImpl.java' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'java', line_num = 14, column_num = 3, contents = contents, command_arguments = [ 'GetType' ], completer_target = 'filetype_default' ) response = app.post_json( '/run_completer_command', event_data ).json assert_that( response, has_entry( 'message', 'com.test.TestWidgetImpl.TestWidgetImpl(String info)' ) ) @WithRetry @SharedYcmd def Subcommands_GetType_ClassMemberVariable_test( app ): filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestWidgetImpl.java' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'java', line_num = 12, column_num = 18, contents = contents, command_arguments = [ 'GetType' ], completer_target = 'filetype_default' ) response = app.post_json( '/run_completer_command', event_data ).json assert_that( response, has_entry( 'message', 'String info' ) ) @WithRetry @SharedYcmd def Subcommands_GetType_MethodArgument_test( app ): filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestWidgetImpl.java' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'java', line_num = 16, column_num = 17, contents = contents, command_arguments = [ 'GetType' ], completer_target = 'filetype_default' ) response = app.post_json( '/run_completer_command', event_data ).json assert_that( response, has_entry( 'message', 'String info - com.test.TestWidgetImpl.TestWidgetImpl(String)' ) ) @WithRetry @SharedYcmd def Subcommands_GetType_MethodVariable_test( app ): filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestWidgetImpl.java' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'java', line_num = 15, column_num = 9, contents = contents, command_arguments = [ 'GetType' ], completer_target = 'filetype_default' ) response = app.post_json( '/run_completer_command', event_data ).json assert_that( response, has_entry( 'message', 'int a - com.test.TestWidgetImpl.TestWidgetImpl(String)' ) ) @WithRetry @SharedYcmd def Subcommands_GetType_Method_test( app ): filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestWidgetImpl.java' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'java', line_num = 20, column_num = 15, contents = contents, command_arguments = [ 'GetType' ], completer_target = 'filetype_default' ) response = app.post_json( '/run_completer_command', event_data ).json assert_that( response, has_entry( 'message', 'void com.test.TestWidgetImpl.doSomethingVaguelyUseful()' ) ) @WithRetry @SharedYcmd def Subcommands_GetType_Unicode_test( app ): contents = ReadFile( TEST_JAVA ) app.post_json( '/event_notification', BuildRequest( filepath = TEST_JAVA, filetype = 'java', contents = contents, event_name = 'FileReadyToParse' ) ) event_data = BuildRequest( filepath = TEST_JAVA, filetype = 'java', line_num = 7, column_num = 17, contents = contents, command_arguments = [ 'GetType' ], completer_target = 'filetype_default' ) response = app.post_json( '/run_completer_command', event_data ).json assert_that( response, has_entry( 'message', 'String whåtawîdgé - com.youcompleteme.Test.doUnicødeTes()' ) ) @WithRetry @SharedYcmd def Subcommands_GetType_LiteralValue_test( app ): filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestWidgetImpl.java' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'java', line_num = 15, column_num = 13, contents = contents, command_arguments = [ 'GetType' ], completer_target = 'filetype_default' ) response = app.post_json( '/run_completer_command', event_data, expect_errors = True ) assert_that( response.status_code, equal_to( requests.codes.internal_server_error ) ) assert_that( response.json, ErrorMatcher( RuntimeError, 'Unknown type' ) ) @WithRetry @SharedYcmd def Subcommands_GoTo_NoLocation_test( app ): filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'AbstractTestWidget.java' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'java', line_num = 18, column_num = 1, contents = contents, command_arguments = [ 'GoTo' ], completer_target = 'filetype_default' ) response = app.post_json( '/run_completer_command', event_data, expect_errors = True ) assert_that( response.status_code, equal_to( requests.codes.internal_server_error ) ) assert_that( response.json, ErrorMatcher( RuntimeError, 'Cannot jump to location' ) ) @WithRetry @SharedYcmd def Subcommands_GoToReferences_NoReferences_test( app ): filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'AbstractTestWidget.java' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'java', line_num = 2, column_num = 1, contents = contents, command_arguments = [ 'GoToReferences' ], completer_target = 'filetype_default' ) response = app.post_json( '/run_completer_command', event_data, expect_errors = True ) assert_that( response.status_code, equal_to( requests.codes.internal_server_error ) ) assert_that( response.json, ErrorMatcher( RuntimeError, 'Cannot jump to location' ) ) @WithRetry @IsolatedYcmd( { 'extra_conf_globlist': PathToTestFile( 'multiple_projects', '*' ) } ) def Subcommands_GoToReferences_MultipleProjects_test( app ): filepath = PathToTestFile( 'multiple_projects', 'src', 'core', 'java', 'com', 'puremourning', 'widget', 'core', 'Utils.java' ) StartJavaCompleterServerWithFile( app, filepath ) RunTest( app, { 'description': 'GoToReferences works across multiple projects', 'request': { 'command': 'GoToReferences', 'filepath': filepath, 'line_num': 5, 'column_num': 22, }, 'expect': { 'response': requests.codes.ok, 'data': contains_inanyorder( LocationMatcher( filepath, 8, 35 ), LocationMatcher( PathToTestFile( 'multiple_projects', 'src', 'input', 'java', 'com', 'puremourning', 'widget', 'input', 'InputApp.java' ), 8, 16 ) ) } } ) @WithRetry @SharedYcmd def Subcommands_GoToReferences_test( app ): filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'AbstractTestWidget.java' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'java', line_num = 10, column_num = 15, contents = contents, command_arguments = [ 'GoToReferences' ], completer_target = 'filetype_default' ) response = app.post_json( '/run_completer_command', event_data ).json assert_that( response, contains_exactly( has_entries( { 'filepath': PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestFactory.java' ), 'column_num': 9, 'description': " w.doSomethingVaguelyUseful();", 'line_num': 28 } ), has_entries( { 'filepath': PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestLauncher.java' ), 'column_num': 11, 'description': " w.doSomethingVaguelyUseful();", 'line_num': 32 } ) ) ) @WithRetry @SharedYcmd def Subcommands_GoToSymbol_SingleSameFile_test( app ): contents = ReadFile( TEST_JAVA ) event_data = BuildRequest( filepath = TEST_JAVA, filetype = 'java', line_num = 1, column_num = 1, contents = contents, command_arguments = [ 'GoToSymbol', 'TéstClass' ], completer_target = 'filetype_default' ) response = app.post_json( '/run_completer_command', event_data ).json assert_that( response, has_entries( { 'filepath': TEST_JAVA, 'description': "TéstClass", 'line_num': 20, 'column_num': 16, } ) ) @WithRetry @SharedYcmd def Subcommands_GoToSymbol_Multiple_test( app ): contents = ReadFile( TEST_JAVA ) event_data = BuildRequest( filepath = TEST_JAVA, filetype = 'java', line_num = 1, column_num = 1, contents = contents, command_arguments = [ 'GoToSymbol', 'test' ], completer_target = 'filetype_default' ) response = app.post_json( '/run_completer_command', event_data ).json assert_that( response, contains_inanyorder( has_entries( { 'filepath': PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestFactory.java' ) , 'description': "TestFactory", 'line_num': 12, 'column_num': 14, } ), has_entries( { 'filepath': PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestWidgetImpl.java' ) , 'description': "TestWidgetImpl", 'line_num': 11, 'column_num': 7, } ), has_entries( { 'filepath': PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestLauncher.java' ) , 'description': "TestLauncher", 'line_num': 6, 'column_num': 7, } ), has_entries( { 'filepath': PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'youcompleteme', 'Test.java' ) , 'description': "Test", 'line_num': 3, 'column_num': 14, } ), has_entries( { 'filepath': PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestWithDocumentation.java' ) , 'description': "TestWithDocumentation", 'line_num': 3, 'column_num': 14, } ) ) ) @WithRetry @SharedYcmd def Subcommands_GoToSymbol_None_test( app ): contents = ReadFile( TEST_JAVA ) event_data = BuildRequest( filepath = TEST_JAVA, filetype = 'java', line_num = 1, column_num = 1, contents = contents, command_arguments = [ 'GoToSymbol', 'abcd' ], completer_target = 'filetype_default' ) response = app.post_json( '/run_completer_command', event_data, expect_errors = True ) assert_that( response.status_code, equal_to( requests.codes.internal_server_error ) ) assert_that( response.json, ErrorMatcher( RuntimeError, 'Symbol not found' ) ) @WithRetry @SharedYcmd def Subcommands_RefactorRename_Simple_test( app ): filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestLauncher.java' ) RunTest( app, { 'description': 'RefactorRename works within a single scope/file', 'request': { 'command': 'RefactorRename', 'arguments': [ 'renamed_l' ], 'filepath': filepath, 'line_num': 28, 'column_num': 5, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( 'renamed_l = new TestLauncher( 10 );' '\n renamed_l', LocationMatcher( filepath, 27, 18 ), LocationMatcher( filepath, 28, 6 ) ), ), 'location': LocationMatcher( filepath, 28, 5 ) } ) ) } ) } } ) @ExpectedFailure( 'Renaming does not work on overridden methods ' 'since jdt.ls 0.21.0', matches_regexp( 'No item matched:.*TestWidgetImpl.java' ) ) @WithRetry @SharedYcmd def Subcommands_RefactorRename_MultipleFiles_test( app ): AbstractTestWidget = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'AbstractTestWidget.java' ) TestFactory = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestFactory.java' ) TestLauncher = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestLauncher.java' ) TestWidgetImpl = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestWidgetImpl.java' ) RunTest( app, { 'description': 'RefactorRename works across files', 'request': { 'command': 'RefactorRename', 'arguments': [ 'a_quite_long_string' ], 'filepath': TestLauncher, 'line_num': 32, 'column_num': 13, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( 'a_quite_long_string', LocationMatcher( AbstractTestWidget, 10, 15 ), LocationMatcher( AbstractTestWidget, 10, 39 ) ), ChunkMatcher( 'a_quite_long_string', LocationMatcher( TestFactory, 28, 9 ), LocationMatcher( TestFactory, 28, 33 ) ), ChunkMatcher( 'a_quite_long_string', LocationMatcher( TestLauncher, 32, 11 ), LocationMatcher( TestLauncher, 32, 35 ) ), ChunkMatcher( 'a_quite_long_string', LocationMatcher( TestWidgetImpl, 20, 15 ), LocationMatcher( TestWidgetImpl, 20, 39 ) ), ), 'location': LocationMatcher( TestLauncher, 32, 13 ) } ) ) } ) } } ) @WithRetry @SharedYcmd def Subcommands_RefactorRename_Missing_New_Name_test( app ): filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestLauncher.java' ) RunTest( app, { 'description': 'RefactorRename raises an error without new name', 'request': { 'command': 'RefactorRename', 'line_num': 15, 'column_num': 5, 'filepath': filepath, }, 'expect': { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( ValueError, 'Please specify a new name to rename it to.\n' 'Usage: RefactorRename <new name>' ), } } ) @WithRetry @SharedYcmd def Subcommands_RefactorRename_Unicode_test( app ): RunTest( app, { 'description': 'Rename works for unicode identifier', 'request': { 'command': 'RefactorRename', 'arguments': [ 'shorter' ], 'line_num': 7, 'column_num': 21, 'filepath': TEST_JAVA, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( 'shorter = "Test";\n return shorter', LocationMatcher( TEST_JAVA, 7, 12 ), LocationMatcher( TEST_JAVA, 8, 25 ) ), ), } ) ), } ), }, } ) def RunFixItTest( app, description, filepath, line, col, fixits_for_line ): RunTest( app, { 'description': description, 'request': { 'command': 'FixIt', 'line_num': line, 'column_num': col, 'filepath': filepath, }, 'expect': { 'response': requests.codes.ok, 'data': fixits_for_line, } } ) @WithRetry @pytest.mark.parametrize( 'description,column', [ ( 'FixIt works at the firtst char of the line', 1 ), ( 'FixIt works at the begin of the range of the diag.', 15 ), ( 'FixIt works at the end of the range of the diag.', 20 ), ( 'FixIt works at the end of the line', 34 ), ] ) @SharedYcmd def Subcommands_FixIt_SingleDiag_MultipleOption_Insertion_test( app, description, column ): import os wibble_path = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'Wibble.java' ) wibble_text = 'package com.test;{0}{0}public {1} Wibble {{{0}{0}}}{0}' filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestFactory.java' ) # Note: The code actions for creating variables are really not very useful. # The import is, however, and the FixIt almost exactly matches the one # supplied when completing 'CUTHBERT' and auto-inserting. fixits_for_line = has_entries( { 'fixits': contains_inanyorder( has_entries( { 'text': "Import 'Wibble' (com.test.wobble)", 'kind': 'quickfix', 'chunks': contains_exactly( ChunkMatcher( 'package com.test;\n\n' 'import com.test.wobble.Wibble;\n\n', LocationMatcher( filepath, 1, 1 ), LocationMatcher( filepath, 3, 1 ) ), ), } ), has_entries( { 'text': "Create constant 'Wibble'", 'kind': 'quickfix', 'chunks': contains_exactly( ChunkMatcher( '\n\nprivate static final String Wibble = null;', LocationMatcher( filepath, 16, 4 ), LocationMatcher( filepath, 16, 4 ) ), ), } ), has_entries( { 'text': "Create class 'Wibble'", 'kind': 'quickfix', 'chunks': contains_exactly( ChunkMatcher( wibble_text.format( os.linesep, 'class' ), LocationMatcher( wibble_path, 1, 1 ), LocationMatcher( wibble_path, 1, 1 ) ), ), } ), has_entries( { 'text': "Create interface 'Wibble'", 'kind': 'quickfix', 'chunks': contains_exactly( ChunkMatcher( wibble_text.format( os.linesep, 'interface' ), LocationMatcher( wibble_path, 1, 1 ), LocationMatcher( wibble_path, 1, 1 ) ), ), } ), has_entries( { 'text': "Create enum 'Wibble'", 'kind': 'quickfix', 'chunks': contains_exactly( ChunkMatcher( wibble_text.format( os.linesep, 'enum' ), LocationMatcher( wibble_path, 1, 1 ), LocationMatcher( wibble_path, 1, 1 ) ), ), } ), has_entries( { 'text': "Create local variable 'Wibble'", 'kind': 'quickfix', 'chunks': contains_exactly( ChunkMatcher( 'Object Wibble;\n\t', LocationMatcher( filepath, 19, 5 ), LocationMatcher( filepath, 19, 5 ) ), ), } ), has_entries( { 'text': "Create field 'Wibble'", 'kind': 'quickfix', 'chunks': contains_exactly( ChunkMatcher( '\n\nprivate Object Wibble;', LocationMatcher( filepath, 16, 4 ), LocationMatcher( filepath, 16, 4 ) ), ), } ), has_entries( { 'text': "Create parameter 'Wibble'", 'kind': 'quickfix', 'chunks': contains_exactly( ChunkMatcher( ', Object Wibble', LocationMatcher( filepath, 18, 32 ), LocationMatcher( filepath, 18, 32 ) ), ), } ), has_entries( { 'text': 'Generate toString()...', 'kind': 'source.generate.toString', 'chunks': contains_exactly( ChunkMatcher( '\n\n@Override\npublic String toString() {' '\n\treturn "TestFactory []";\n}', LocationMatcher( filepath, 32, 4 ), LocationMatcher( filepath, 32, 4 ) ), ), } ), has_entries( { 'text': 'Organize imports', 'kind': 'source.organizeImports', 'chunks': contains_exactly( ChunkMatcher( '\n\nimport com.test.wobble.Wibble;\n\n', LocationMatcher( filepath, 1, 18 ), LocationMatcher( filepath, 3, 1 ) ), ), } ), has_entries( { 'text': 'Change modifiers to final where possible', 'kind': 'source.generate.finalModifiers', 'chunks': contains_exactly( ChunkMatcher( 'final Wibble w ) {\n if ( w == Wibble.CUTHBERT ) {' '\n }\n }\n\n public AbstractTestWidget getWidget' '( final String info ) {\n final AbstractTestWidget' ' w = new TestWidgetImpl( info );\n final ', LocationMatcher( filepath, 18, 24 ), LocationMatcher( filepath, 25, 5 ) ), ), } ), ) } ) RunFixItTest( app, description, filepath, 19, column, fixits_for_line ) @WithRetry @SharedYcmd def Subcommands_FixIt_SingleDiag_SingleOption_Modify_test( app ): filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestFactory.java' ) # TODO: As there is only one option, we automatically apply it. # In Java case this might not be the right thing. It's a code assist, not a # FixIt really. Perhaps we should change the client to always ask for # confirmation? fixits = has_entries( { 'fixits': contains_inanyorder( has_entries( { 'text': "Change type of 'test' to 'boolean'", 'kind': 'quickfix', 'chunks': contains_exactly( ChunkMatcher( 'boolean', LocationMatcher( filepath, 14, 12 ), LocationMatcher( filepath, 14, 15 ) ), ), } ), has_entries( { 'text': 'Generate toString()...', 'kind': 'source.generate.toString', 'chunks': contains_exactly( ChunkMatcher( '\n\n@Override\npublic String toString() {' '\n\treturn "TestFactory []";\n}', LocationMatcher( filepath, 32, 4 ), LocationMatcher( filepath, 32, 4 ) ), ), } ), has_entries( { 'text': 'Organize imports', 'kind': 'source.organizeImports', 'chunks': contains_exactly( ChunkMatcher( '\n\nimport com.test.wobble.Wibble;\n\n', LocationMatcher( filepath, 1, 18 ), LocationMatcher( filepath, 3, 1 ) ), ), } ), has_entries( { 'text': 'Change modifiers to final where possible', 'kind': 'source.generate.finalModifiers', 'chunks': contains_exactly( ChunkMatcher( 'final Wibble w ) {\n if ( w == Wibble.CUTHBERT ) {' '\n }\n }\n\n public AbstractTestWidget getWidget' '( final String info ) {\n final AbstractTestWidget' ' w = new TestWidgetImpl( info );\n final ', LocationMatcher( filepath, 18, 24 ), LocationMatcher( filepath, 25, 5 ) ), ), } ), ) } ) RunFixItTest( app, 'FixIts can change lines as well as add them', filepath, 27, 12, fixits ) @WithRetry @SharedYcmd def Subcommands_FixIt_SingleDiag_MultiOption_Delete_test( app ): filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestFactory.java' ) fixits = has_entries( { 'fixits': contains_inanyorder( has_entries( { 'text': "Remove 'testString', keep assignments with side effects", 'kind': 'quickfix', 'chunks': contains_exactly( ChunkMatcher( '', LocationMatcher( filepath, 14, 21 ), LocationMatcher( filepath, 15, 30 ) ), ), } ), # The edit reported for this is huge and uninteresting really. Manual # testing can show that it works. This test is really about the previous # FixIt (and nonetheless, the previous tests ensure that we correctly # populate the chunks list; the contents all come from jdt.ls) has_entries( { 'text': "Create getter and setter for 'testString'", 'chunks': instance_of( list ) } ), has_entries( { 'text': "Organize imports", 'chunks': instance_of( list ) } ), has_entries( { 'text': "Generate Getters and Setters", 'chunks': instance_of( list ) } ), has_entries( { 'text': 'Change modifiers to final where possible', 'chunks': instance_of( list ) } ), ) } ) RunFixItTest( app, 'FixIts can change lines as well as add them', filepath, 15, 29, fixits ) @WithRetry @pytest.mark.parametrize( 'description,column,expect_fixits', [ ( 'diags are merged in FixIt options - start of line', 1, 'MERGE' ), ( 'diags are not merged in FixIt options - start of diag 1', 10, 'FIRST' ), ( 'diags are not merged in FixIt options - end of diag 1', 15, 'FIRST' ), ( 'diags are not merged in FixIt options - start of diag 2', 23, 'SECOND' ), ( 'diags are not merged in FixIt options - end of diag 2', 46, 'SECOND' ), ( 'diags are merged in FixIt options - end of line', 55, 'MERGE' ), ] ) @SharedYcmd def Subcommands_FixIt_MultipleDiags_test( app, description, column, expect_fixits ): filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestFactory.java' ) FIRST = [ has_entries( { 'text': "Change type of 'test' to 'boolean'", 'kind': 'quickfix', 'chunks': contains_exactly( ChunkMatcher( 'boolean', LocationMatcher( filepath, 14, 12 ), LocationMatcher( filepath, 14, 15 ) ), ), } ), ] SECOND = [ has_entries( { 'text': "Remove argument to match 'doSomethingVaguelyUseful()'", 'kind': 'quickfix', 'chunks': contains_exactly( ChunkMatcher( '', LocationMatcher( filepath, 30, 48 ), LocationMatcher( filepath, 30, 50 ) ), ), } ), has_entries( { 'text': "Change method 'doSomethingVaguelyUseful()': Add parameter " "'Bar'", 'chunks': instance_of( list ), } ), has_entries( { 'text': "Create method 'doSomethingVaguelyUseful(Bar)' in type " "'AbstractTestWidget'", 'chunks': instance_of( list ), } ), ] ACTIONS = [ has_entries( { 'text': "Generate toString()...", 'chunks': instance_of( list ), } ), has_entries( { 'text': "Organize imports", 'chunks': instance_of( list ), } ), has_entries( { 'text': 'Change modifiers to final where possible', 'chunks': instance_of( list ), } ), ] FIXITS = { 'FIRST': FIRST + ACTIONS, 'SECOND': SECOND + ACTIONS, 'MERGE': FIRST + SECOND + ACTIONS, } fixits = has_entries( { 'fixits': contains_inanyorder( *FIXITS[ expect_fixits ] ) } ) RunFixItTest( app, description, filepath, 30, column, fixits ) @SharedYcmd def Subcommands_FixIt_Range_test( app ): filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestLauncher.java' ) RunTest( app, { 'description': 'Formatting is applied on some part of the file ' 'with tabs composed of 4 spaces', 'request': { 'command': 'FixIt', 'filepath': filepath, 'range': { 'start': { 'line_num': 34, 'column_num': 28, }, 'end': { 'line_num': 34, 'column_num': 73 } }, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_inanyorder( has_entries( { 'text': 'Extract to field', 'kind': 'refactor.extract.field', 'chunks': contains_exactly( ChunkMatcher( matches_regexp( 'private String \\w+;\n' '\n' '\t@Override\n' ' public void launch\\(\\) {\n' ' AbstractTestWidget w = ' 'factory.getWidget\\( "Test" \\);\n' ' ' 'w.doSomethingVaguelyUseful\\(\\);\n' '\n' ' \\w+ = "Did something ' 'useful: " \\+ w.getWidgetInfo\\(\\);\n' '\t\tSystem.out.println\\( \\w+' ), LocationMatcher( filepath, 29, 7 ), LocationMatcher( filepath, 34, 73 ) ), ), } ), has_entries( { 'text': 'Extract to method', 'kind': 'refactor.extract.function', 'chunks': contains_exactly( # This one is a wall of text that rewrites 35 lines ChunkMatcher( instance_of( str ), LocationMatcher( filepath, 1, 1 ), LocationMatcher( filepath, 35, 8 ) ), ), } ), has_entries( { 'text': 'Extract to local variable (replace all occurrences)', 'kind': 'refactor.extract.variable', 'chunks': contains_exactly( ChunkMatcher( matches_regexp( 'String \\w+ = "Did something ' 'useful: " \\+ w.getWidgetInfo\\(\\);\n' '\t\tSystem.out.println\\( \\w+' ), LocationMatcher( filepath, 34, 9 ), LocationMatcher( filepath, 34, 73 ) ), ), } ), has_entries( { 'text': 'Extract to local variable', 'kind': 'refactor.extract.variable', 'chunks': contains_exactly( ChunkMatcher( matches_regexp( 'String \\w+ = "Did something ' 'useful: " \\+ w.getWidgetInfo\\(\\);\n' '\t\tSystem.out.println\\( \\w+' ), LocationMatcher( filepath, 34, 9 ), LocationMatcher( filepath, 34, 73 ) ), ), } ), has_entries( { 'text': 'Introduce Parameter...', 'kind': 'refactor.introduce.parameter', 'chunks': contains_exactly( ChunkMatcher( 'String string) {\n' ' AbstractTestWidget w = factory.getWidget( "Test" );\n' ' w.doSomethingVaguelyUseful();\n' '\n' ' System.out.println( string', LocationMatcher( filepath, 30, 26 ), LocationMatcher( filepath, 34, 73 ) ), ), } ), has_entries( { 'text': 'Organize imports', 'chunks': instance_of( list ), } ), has_entries( { 'text': 'Change modifiers to final where possible', 'chunks': instance_of( list ), } ), ) } ) } } ) @WithRetry @SharedYcmd def Subcommands_FixIt_NoDiagnostics_test( app ): filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestFactory.java' ) RunFixItTest( app, "no FixIts means you gotta code it yo' self", filepath, 1, 1, has_entries( { 'fixits': contains_inanyorder( has_entries( { 'text': 'Change modifiers to final where possible', 'chunks': instance_of( list ) } ), has_entries( { 'text': 'Organize imports', 'chunks': instance_of( list ) } ), has_entries( { 'text': 'Generate toString()...', 'chunks': instance_of( list ) } ) ) } ) ) @WithRetry @SharedYcmd def Subcommands_FixIt_Unicode_test( app ): fixits = has_entries( { 'fixits': contains_inanyorder( has_entries( { 'text': "Remove argument to match 'doUnicødeTes()'", 'kind': 'quickfix', 'chunks': contains_exactly( ChunkMatcher( '', LocationMatcher( TEST_JAVA, 13, 24 ), LocationMatcher( TEST_JAVA, 13, 29 ) ), ), } ), has_entries( { 'text': "Change method 'doUnicødeTes()': Add parameter 'String'", 'kind': 'quickfix', 'chunks': contains_exactly( ChunkMatcher( 'String test2', LocationMatcher( TEST_JAVA, 6, 31 ), LocationMatcher( TEST_JAVA, 6, 31 ) ), ), } ), has_entries( { 'text': "Create method 'doUnicødeTes(String)'", 'kind': 'quickfix', 'chunks': contains_exactly( ChunkMatcher( 'private void doUnicødeTes(String test2) {\n}\n\n\n', LocationMatcher( TEST_JAVA, 20, 3 ), LocationMatcher( TEST_JAVA, 20, 3 ) ), ), } ), has_entries( { 'text': 'Change modifiers to final where possible', 'chunks': instance_of( list ), } ), has_entries( { 'text': "Generate Getters and Setters", 'chunks': instance_of( list ), } ), ) } ) RunFixItTest( app, 'FixIts and diagnostics work with unicode strings', TEST_JAVA, 13, 1, fixits ) @WithRetry @IsolatedYcmd() def Subcommands_FixIt_InvalidURI_test( app ): filepath = PathToTestFile( 'simple_eclipse_project', 'src', 'com', 'test', 'TestFactory.java' ) fixits = has_entries( { 'fixits': contains_inanyorder( has_entries( { 'kind': 'quickfix', 'text': "Change type of 'test' to 'boolean'", 'chunks': contains_exactly( ChunkMatcher( 'boolean', LocationMatcher( '', 14, 12 ), LocationMatcher( '', 14, 15 ) ), ), } ), has_entries( { 'text': 'Organize imports', 'kind': 'source.organizeImports', 'chunks': contains_exactly( ChunkMatcher( '\n\nimport com.test.wobble.Wibble;\n\n', LocationMatcher( '', 1, 1 ), LocationMatcher( '', 3, 1 ) ), ), } ), has_entries( { 'text': 'Change modifiers to final where possible', 'kind': 'source.generate.finalModifiers', 'chunks': contains_exactly( ChunkMatcher( "final Wibble w ) {\n if ( w == Wibble.CUTHBERT ) {" "\n }\n }\n\n public AbstractTestWidget getWidget" "( final String info ) {\n final AbstractTestWidget" " w = new TestWidgetImpl( info );\n final ", LocationMatcher( '', 18, 24 ), LocationMatcher( '', 25, 5 ) ), ), } ), has_entries( { 'text': 'Generate toString()...', 'kind': 'source.generate.toString', 'chunks': contains_exactly( ChunkMatcher( '\n\n@Override\npublic String toString() {' '\n\treturn "TestFactory []";\n}', LocationMatcher( '', 32, 4 ), LocationMatcher( '', 32, 4 ) ), ), } ), ) } ) contents = ReadFile( filepath ) # Wait for jdt.ls to have parsed the file and returned some diagnostics for tries in range( 0, 60 ): results = app.post_json( '/event_notification', BuildRequest( filepath = filepath, filetype = 'java', contents = contents, event_name = 'FileReadyToParse' ) ) if results.json: break time.sleep( .25 ) with patch( 'ycmd.completers.language_server.language_server_protocol.UriToFilePath', side_effect = lsp.InvalidUriException ): RunTest( app, { 'description': 'Invalid URIs do not make us crash', 'request': { 'command': 'FixIt', 'line_num': 27, 'column_num': 12, 'filepath': filepath, }, 'expect': { 'response': requests.codes.ok, 'data': fixits, } } ) @WithRetry @SharedYcmd def Subcommands_Format_WholeFile_Spaces_test( app ): RunTest( app, { 'description': 'Formatting is applied on the whole file ' 'with tabs composed of 4 spaces', 'request': { 'command': 'Format', 'filepath': TEST_JAVA, 'options': { 'tab_size': 4, 'insert_spaces': True } }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( '\n ', LocationMatcher( TEST_JAVA, 3, 20 ), LocationMatcher( TEST_JAVA, 4, 3 ) ), ChunkMatcher( '\n\n ', LocationMatcher( TEST_JAVA, 4, 22 ), LocationMatcher( TEST_JAVA, 6, 3 ) ), ChunkMatcher( '\n ', LocationMatcher( TEST_JAVA, 6, 34 ), LocationMatcher( TEST_JAVA, 7, 5 ) ), ChunkMatcher( '\n ', LocationMatcher( TEST_JAVA, 7, 35 ), LocationMatcher( TEST_JAVA, 8, 5 ) ), ChunkMatcher( '', LocationMatcher( TEST_JAVA, 8, 25 ), LocationMatcher( TEST_JAVA, 8, 26 ) ), ChunkMatcher( '\n ', LocationMatcher( TEST_JAVA, 8, 27 ), LocationMatcher( TEST_JAVA, 9, 3 ) ), ChunkMatcher( '\n\n ', LocationMatcher( TEST_JAVA, 9, 4 ), LocationMatcher( TEST_JAVA, 11, 3 ) ), ChunkMatcher( '\n ', LocationMatcher( TEST_JAVA, 11, 29 ), LocationMatcher( TEST_JAVA, 12, 5 ) ), ChunkMatcher( '\n ', LocationMatcher( TEST_JAVA, 12, 26 ), LocationMatcher( TEST_JAVA, 13, 5 ) ), ChunkMatcher( '', LocationMatcher( TEST_JAVA, 13, 24 ), LocationMatcher( TEST_JAVA, 13, 25 ) ), ChunkMatcher( '', LocationMatcher( TEST_JAVA, 13, 29 ), LocationMatcher( TEST_JAVA, 13, 30 ) ), ChunkMatcher( '\n\n ', LocationMatcher( TEST_JAVA, 13, 32 ), LocationMatcher( TEST_JAVA, 15, 5 ) ), ChunkMatcher( '\n ', LocationMatcher( TEST_JAVA, 15, 58 ), LocationMatcher( TEST_JAVA, 16, 5 ) ), ChunkMatcher( '\n ', LocationMatcher( TEST_JAVA, 16, 42 ), LocationMatcher( TEST_JAVA, 17, 3 ) ), ChunkMatcher( '\n\n ', LocationMatcher( TEST_JAVA, 17, 4 ), LocationMatcher( TEST_JAVA, 20, 3 ) ), ChunkMatcher( '\n ', LocationMatcher( TEST_JAVA, 20, 28 ), LocationMatcher( TEST_JAVA, 21, 5 ) ), ChunkMatcher( '\n ', LocationMatcher( TEST_JAVA, 21, 28 ), LocationMatcher( TEST_JAVA, 22, 5 ) ), ChunkMatcher( '\n ', LocationMatcher( TEST_JAVA, 22, 30 ), LocationMatcher( TEST_JAVA, 23, 5 ) ), ChunkMatcher( '\n ', LocationMatcher( TEST_JAVA, 23, 23 ), LocationMatcher( TEST_JAVA, 24, 5 ) ), ChunkMatcher( '\n ', LocationMatcher( TEST_JAVA, 24, 27 ), LocationMatcher( TEST_JAVA, 25, 3 ) ), ) } ) ) } ) } } ) @WithRetry @SharedYcmd def Subcommands_Format_WholeFile_Tabs_test( app ): RunTest( app, { 'description': 'Formatting is applied on the whole file ' 'with tabs composed of 2 spaces', 'request': { 'command': 'Format', 'filepath': TEST_JAVA, 'options': { 'tab_size': 4, 'insert_spaces': False } }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( '\n\t', LocationMatcher( TEST_JAVA, 3, 20 ), LocationMatcher( TEST_JAVA, 4, 3 ) ), ChunkMatcher( '\n\n\t', LocationMatcher( TEST_JAVA, 4, 22 ), LocationMatcher( TEST_JAVA, 6, 3 ) ), ChunkMatcher( '\n\t\t', LocationMatcher( TEST_JAVA, 6, 34 ), LocationMatcher( TEST_JAVA, 7, 5 ) ), ChunkMatcher( '\n\t\t', LocationMatcher( TEST_JAVA, 7, 35 ), LocationMatcher( TEST_JAVA, 8, 5 ) ), ChunkMatcher( '', LocationMatcher( TEST_JAVA, 8, 25 ), LocationMatcher( TEST_JAVA, 8, 26 ) ), ChunkMatcher( '\n\t', LocationMatcher( TEST_JAVA, 8, 27 ), LocationMatcher( TEST_JAVA, 9, 3 ) ), ChunkMatcher( '\n\n\t', LocationMatcher( TEST_JAVA, 9, 4 ), LocationMatcher( TEST_JAVA, 11, 3 ) ), ChunkMatcher( '\n\t\t', LocationMatcher( TEST_JAVA, 11, 29 ), LocationMatcher( TEST_JAVA, 12, 5 ) ), ChunkMatcher( '\n\t\t', LocationMatcher( TEST_JAVA, 12, 26 ), LocationMatcher( TEST_JAVA, 13, 5 ) ), ChunkMatcher( '', LocationMatcher( TEST_JAVA, 13, 24 ), LocationMatcher( TEST_JAVA, 13, 25 ) ), ChunkMatcher( '', LocationMatcher( TEST_JAVA, 13, 29 ), LocationMatcher( TEST_JAVA, 13, 30 ) ), ChunkMatcher( '\n\n\t\t', LocationMatcher( TEST_JAVA, 13, 32 ), LocationMatcher( TEST_JAVA, 15, 5 ) ), ChunkMatcher( '\n\t\t', LocationMatcher( TEST_JAVA, 15, 58 ), LocationMatcher( TEST_JAVA, 16, 5 ) ), ChunkMatcher( '\n\t', LocationMatcher( TEST_JAVA, 16, 42 ), LocationMatcher( TEST_JAVA, 17, 3 ) ), ChunkMatcher( '\n\n\t', LocationMatcher( TEST_JAVA, 17, 4 ), LocationMatcher( TEST_JAVA, 20, 3 ) ), ChunkMatcher( '\n\t\t', LocationMatcher( TEST_JAVA, 20, 28 ), LocationMatcher( TEST_JAVA, 21, 5 ) ), ChunkMatcher( '\n\t\t', LocationMatcher( TEST_JAVA, 21, 28 ), LocationMatcher( TEST_JAVA, 22, 5 ) ), ChunkMatcher( '\n\t\t', LocationMatcher( TEST_JAVA, 22, 30 ), LocationMatcher( TEST_JAVA, 23, 5 ) ), ChunkMatcher( '\n\t\t', LocationMatcher( TEST_JAVA, 23, 23 ), LocationMatcher( TEST_JAVA, 24, 5 ) ), ChunkMatcher( '\n\t', LocationMatcher( TEST_JAVA, 24, 27 ), LocationMatcher( TEST_JAVA, 25, 3 ) ), ) } ) ) } ) } } ) @WithRetry @SharedYcmd def Subcommands_Format_Range_Spaces_test( app ): RunTest( app, { 'description': 'Formatting is applied on some part of the file ' 'with tabs composed of 4 spaces', 'request': { 'command': 'Format', 'filepath': TEST_JAVA, 'range': { 'start': { 'line_num': 20, 'column_num': 1, }, 'end': { 'line_num': 25, 'column_num': 4 } }, 'options': { 'tab_size': 4, 'insert_spaces': True } }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( ' ', LocationMatcher( TEST_JAVA, 20, 1 ), LocationMatcher( TEST_JAVA, 20, 3 ) ), ChunkMatcher( '\n ', LocationMatcher( TEST_JAVA, 20, 28 ), LocationMatcher( TEST_JAVA, 21, 5 ) ), ChunkMatcher( '\n ', LocationMatcher( TEST_JAVA, 21, 28 ), LocationMatcher( TEST_JAVA, 22, 5 ) ), ChunkMatcher( '\n ', LocationMatcher( TEST_JAVA, 22, 30 ), LocationMatcher( TEST_JAVA, 23, 5 ) ), ChunkMatcher( '\n ', LocationMatcher( TEST_JAVA, 23, 23 ), LocationMatcher( TEST_JAVA, 24, 5 ) ), ) } ) ) } ) } } ) @WithRetry @SharedYcmd def Subcommands_Format_Range_Tabs_test( app ): RunTest( app, { 'description': 'Formatting is applied on some part of the file ' 'with tabs instead of spaces', 'request': { 'command': 'Format', 'filepath': TEST_JAVA, 'range': { 'start': { 'line_num': 20, 'column_num': 1, }, 'end': { 'line_num': 25, 'column_num': 4 } }, 'options': { 'tab_size': 4, 'insert_spaces': False } }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( '\t', LocationMatcher( TEST_JAVA, 20, 1 ), LocationMatcher( TEST_JAVA, 20, 3 ) ), ChunkMatcher( '\n\t\t', LocationMatcher( TEST_JAVA, 20, 28 ), LocationMatcher( TEST_JAVA, 21, 5 ) ), ChunkMatcher( '\n\t\t', LocationMatcher( TEST_JAVA, 21, 28 ), LocationMatcher( TEST_JAVA, 22, 5 ) ), ChunkMatcher( '\n\t\t', LocationMatcher( TEST_JAVA, 22, 30 ), LocationMatcher( TEST_JAVA, 23, 5 ) ), ChunkMatcher( '\n\t\t', LocationMatcher( TEST_JAVA, 23, 23 ), LocationMatcher( TEST_JAVA, 24, 5 ) ), ChunkMatcher( '\n\t', LocationMatcher( TEST_JAVA, 24, 27 ), LocationMatcher( TEST_JAVA, 25, 3 ) ), ) } ) ) } ) } } ) @WithRetry @SharedYcmd def RunGoToTest( app, description, filepath, line, col, cmd, goto_response ): RunTest( app, { 'description': description, 'request': { 'command': cmd, 'line_num': line, 'column_num': col, 'filepath': filepath }, 'expect': { 'response': requests.codes.ok, 'data': goto_response, } } ) @pytest.mark.parametrize( 'test', [ # Member function local variable { 'request': { 'line': 28, 'col': 5, 'filepath': TESTLAUNCHER_JAVA }, 'response': { 'line_num': 27, 'column_num': 18, 'filepath': TESTLAUNCHER_JAVA }, 'description': 'GoTo works for member local variable' }, # Member variable { 'request': { 'line': 22, 'col': 7, 'filepath': TESTLAUNCHER_JAVA }, 'response': { 'line_num': 8, 'column_num': 16, 'filepath': TESTLAUNCHER_JAVA }, 'description': 'GoTo works for member variable' }, # Method { 'request': { 'line': 28, 'col': 7, 'filepath': TESTLAUNCHER_JAVA }, 'response': { 'line_num': 21, 'column_num': 16, 'filepath': TESTLAUNCHER_JAVA }, 'description': 'GoTo works for method' }, # Constructor { 'request': { 'line': 38, 'col': 26, 'filepath': TESTLAUNCHER_JAVA }, 'response': { 'line_num': 10, 'column_num': 10, 'filepath': TESTLAUNCHER_JAVA }, 'description': 'GoTo works for jumping to constructor' }, # Jump to self - main() { 'request': { 'line': 26, 'col': 22, 'filepath': TESTLAUNCHER_JAVA }, 'response': { 'line_num': 26, 'column_num': 22, 'filepath': TESTLAUNCHER_JAVA }, 'description': 'GoTo works for jumping to the same position' }, # Static method { 'request': { 'line': 37, 'col': 11, 'filepath': TESTLAUNCHER_JAVA }, 'response': { 'line_num': 13, 'column_num': 21, 'filepath': TESTLAUNCHER_JAVA }, 'description': 'GoTo works for static method' }, # Static variable { 'request': { 'line': 14, 'col': 11, 'filepath': TESTLAUNCHER_JAVA }, 'response': { 'line_num': 12, 'column_num': 21, 'filepath': TESTLAUNCHER_JAVA }, 'description': 'GoTo works for static variable' }, # Argument variable { 'request': { 'line': 23, 'col': 5, 'filepath': TESTLAUNCHER_JAVA }, 'response': { 'line_num': 21, 'column_num': 32, 'filepath': TESTLAUNCHER_JAVA }, 'description': 'GoTo works for argument variable' }, # Class { 'request': { 'line': 27, 'col': 10, 'filepath': TESTLAUNCHER_JAVA }, 'response': { 'line_num': 6, 'column_num': 7, 'filepath': TESTLAUNCHER_JAVA }, 'description': 'GoTo works for jumping to class declaration' }, # Unicode { 'request': { 'line': 8, 'col': 12, 'filepath': TEST_JAVA }, 'response': { 'line_num': 7, 'column_num': 12, 'filepath': TEST_JAVA }, 'description': 'GoTo works for unicode identifiers' } ] ) @pytest.mark.parametrize( 'command', [ 'GoTo', 'GoToDefinition', 'GoToDeclaration' ] ) @SharedYcmd def Subcommands_GoTo_test( app, command, test ): RunGoToTest( app, test[ 'description' ], test[ 'request' ][ 'filepath' ], test[ 'request' ][ 'line' ], test[ 'request' ][ 'col' ], command, has_entries( test[ 'response' ] ) ) @pytest.mark.parametrize( 'test', [ # Member function local variable { 'request': { 'line': 28, 'col': 5, 'filepath': TESTLAUNCHER_JAVA }, 'response': { 'line_num': 6, 'column_num': 7, 'filepath': TESTLAUNCHER_JAVA }, 'description': 'GoToType works for member local variable' }, # Member variable { 'request': { 'line': 22, 'col': 7, 'filepath': TESTLAUNCHER_JAVA }, 'response': { 'line_num': 6, 'column_num': 14, 'filepath': TSET_JAVA }, 'description': 'GoToType works for member variable' }, ] ) @SharedYcmd def Subcommands_GoToType_test( app, test ): RunGoToTest( app, test[ 'description' ], test[ 'request' ][ 'filepath' ], test[ 'request' ][ 'line' ], test[ 'request' ][ 'col' ], 'GoToType', has_entries( test[ 'response' ] ) ) @pytest.mark.parametrize( 'test', [ # Interface { 'request': { 'line': 17, 'col': 25, 'filepath': TESTLAUNCHER_JAVA }, 'response': { 'line_num': 28, 'column_num': 16, 'filepath': TESTLAUNCHER_JAVA }, 'description': 'GoToImplementation on interface ' 'jumps to its implementation' }, # Interface reference { 'request': { 'line': 21, 'col': 30, 'filepath': TESTLAUNCHER_JAVA }, 'response': { 'line_num': 28, 'column_num': 16, 'filepath': TESTLAUNCHER_JAVA }, 'description': 'GoToImplementation on interface reference ' 'jumpts to its implementation' }, ] ) @SharedYcmd def Subcommands_GoToImplementation_test( app, test ): RunGoToTest( app, test[ 'description' ], test[ 'request' ][ 'filepath' ], test[ 'request' ][ 'line' ], test[ 'request' ][ 'col' ], 'GoToImplementation', has_entries( test[ 'response' ] ) ) @WithRetry @SharedYcmd def Subcommands_OrganizeImports_test( app ): RunTest( app, { 'description': 'Imports are resolved and sorted, ' 'and unused ones are removed', 'request': { 'command': 'OrganizeImports', 'filepath': TESTLAUNCHER_JAVA }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( 'import com.youcompleteme.Test;\n' 'import com.youcompleteme.testing.Tset;', LocationMatcher( TESTLAUNCHER_JAVA, 3, 1 ), LocationMatcher( TESTLAUNCHER_JAVA, 4, 54 ) ), ) } ) ) } ) } } ) @WithRetry @SharedYcmd @patch( 'ycmd.completers.language_server.language_server_completer.' 'REQUEST_TIMEOUT_COMMAND', 5 ) def Subcommands_RequestTimeout_test( app ): with patch.object( handlers._server_state.GetFiletypeCompleter( [ 'java' ] ).GetConnection(), 'WriteData' ): RunTest( app, { 'description': 'Request timeout throws an error', 'request': { 'command': 'FixIt', 'line_num': 1, 'column_num': 1, 'filepath': TEST_JAVA, }, 'expect': { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( ResponseTimeoutException, 'Response Timeout' ) } } ) @WithRetry @SharedYcmd def Subcommands_RequestFailed_test( app ): connection = handlers._server_state.GetFiletypeCompleter( [ 'java' ] ).GetConnection() def WriteJunkToServer( data ): junk = data.replace( bytes( b'textDocument/codeAction' ), bytes( b'textDocument/codeFAILED' ) ) with connection._stdin_lock: connection._server_stdin.write( junk ) connection._server_stdin.flush() with patch.object( connection, 'WriteData', side_effect = WriteJunkToServer ): RunTest( app, { 'description': 'Response errors propagate to the client', 'request': { 'command': 'FixIt', 'line_num': 1, 'column_num': 1, 'filepath': TEST_JAVA, }, 'expect': { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( ResponseFailedException ) } } ) @WithRetry @SharedYcmd def Subcommands_IndexOutOfRange_test( app ): RunTest( app, { 'description': 'Request with invalid position does not crash', 'request': { 'command': 'FixIt', 'line_num': 99, 'column_num': 99, 'filepath': TEST_JAVA, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'text': 'Generate Getters and Setters', 'chunks': instance_of( list ) } ), has_entries( { 'text': 'Change modifiers to final where possible', 'chunks': instance_of( list ) } ), ) } ), } } ) @WithRetry @SharedYcmd def Subcommands_InvalidRange_test( app ): RunTest( app, { 'description': 'Request with invalid visual range is rejected', 'request': { 'command': 'FixIt', 'line_num': 99, 'column_num': 99, 'filepath': TEST_JAVA, 'range': { 'start': { 'line_num': 99, 'column_num': 99 }, 'end': { 'line_num': 100, 'column_num': 100 } } }, 'expect': { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( RuntimeError, 'Invalid range' ), } } ) @WithRetry @SharedYcmd def Subcommands_DifferentFileTypesUpdate_test( app ): RunTest( app, { 'description': 'Request error handles the error', 'request': { 'command': 'FixIt', 'line_num': 99, 'column_num': 99, 'filepath': TEST_JAVA, 'file_data': { '!/bin/sh': { 'filetypes': [], 'contents': 'this should be ignored by the completer', }, '/path/to/non/project/file': { 'filetypes': [ 'c' ], 'contents': 'this should be ignored by the completer', }, TESTLAUNCHER_JAVA: { 'filetypes': [ 'some', 'java', 'junk', 'also' ], 'contents': ReadFile( TESTLAUNCHER_JAVA ), }, '!/usr/bin/sh': { 'filetypes': [ 'java' ], 'contents': '\n', }, } }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'text': 'Generate Getters and Setters', 'chunks': instance_of( list ) } ), has_entries( { 'text': 'Change modifiers to final where possible', 'chunks': instance_of( list ) } ), ) } ), } } ) @WithRetry @IsolatedYcmd( { 'extra_conf_globlist': PathToTestFile( 'extra_confs', '*' ) } ) def Subcommands_ExtraConf_SettingsValid_test( app ): filepath = PathToTestFile( 'extra_confs', 'simple_extra_conf_project', 'src', 'ExtraConf.java' ) RunTest( app, { 'description': 'RefactorRename is disabled in extra conf.', 'request': { 'command': 'RefactorRename', 'arguments': [ 'renamed_l' ], 'filepath': filepath, 'line_num': 1, 'column_num': 7, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': empty(), 'location': LocationMatcher( filepath, 1, 7 ) } ) ) } ) } } ) @WithRetry @IsolatedYcmd( { 'extra_conf_globlist': PathToTestFile( 'extra_confs', '*' ) } ) def Subcommands_AdditionalFormatterOptions_test( app ): filepath = PathToTestFile( 'extra_confs', 'simple_extra_conf_project', 'src', 'ExtraConf.java' ) RunTest( app, { 'description': 'Format respects settings from extra conf.', 'request': { 'command': 'Format', 'filepath': filepath, 'options': { 'tab_size': 4, 'insert_spaces': True } }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( '\n ', LocationMatcher( filepath, 1, 18 ), LocationMatcher( filepath, 2, 3 ) ), ChunkMatcher( '\n ', LocationMatcher( filepath, 2, 20 ), LocationMatcher( filepath, 2, 21 ) ), ChunkMatcher( '', LocationMatcher( filepath, 2, 29 ), LocationMatcher( filepath, 2, 30 ) ), ChunkMatcher( '\n ', LocationMatcher( filepath, 2, 33 ), LocationMatcher( filepath, 2, 33 ) ), ChunkMatcher( '\n\n ', LocationMatcher( filepath, 2, 34 ), LocationMatcher( filepath, 4, 3 ) ), ChunkMatcher( '\n ', LocationMatcher( filepath, 4, 27 ), LocationMatcher( filepath, 4, 28 ) ), ChunkMatcher( '', LocationMatcher( filepath, 4, 41 ), LocationMatcher( filepath, 4, 42 ) ), ChunkMatcher( '\n ', LocationMatcher( filepath, 4, 45 ), LocationMatcher( filepath, 5, 5 ) ), ChunkMatcher( '\n ', LocationMatcher( filepath, 5, 33 ), LocationMatcher( filepath, 5, 34 ) ), ChunkMatcher( '', LocationMatcher( filepath, 5, 36 ), LocationMatcher( filepath, 5, 37 ) ), ChunkMatcher( '\n ', LocationMatcher( filepath, 5, 39 ), LocationMatcher( filepath, 6, 5 ) ), ChunkMatcher( '\n ', LocationMatcher( filepath, 6, 33 ), LocationMatcher( filepath, 6, 34 ) ), ChunkMatcher( '', LocationMatcher( filepath, 6, 35 ), LocationMatcher( filepath, 6, 36 ) ), ChunkMatcher( '\n ', LocationMatcher( filepath, 6, 38 ), LocationMatcher( filepath, 7, 5 ) ), ChunkMatcher( '\n ', LocationMatcher( filepath, 7, 11 ), LocationMatcher( filepath, 8, 5 ) ), ChunkMatcher( '\n ', LocationMatcher( filepath, 8, 11 ), LocationMatcher( filepath, 9, 3 ) ), ), 'location': LocationMatcher( filepath, 1, 1 ) } ) ) } ) } } ) @WithRetry @IsolatedYcmd() def Subcommands_ExtraConf_SettingsValid_UnknownExtraConf_test( app ): filepath = PathToTestFile( 'extra_confs', 'simple_extra_conf_project', 'src', 'ExtraConf.java' ) contents = ReadFile( filepath ) response = app.post_json( '/event_notification', BuildRequest( **{ 'event_name': 'FileReadyToParse', 'contents': contents, 'filepath': filepath, 'line_num': 1, 'column_num': 7, 'filetype': 'java', } ), expect_errors = True ) print( 'FileReadyToParse result: ' f'{ json.dumps( response.json, indent = 2 ) }' ) assert_that( response.status_code, equal_to( requests.codes.internal_server_error ) ) assert_that( response.json, ErrorMatcher( UnknownExtraConf ) ) app.post_json( '/ignore_extra_conf_file', { 'filepath': PathToTestFile( 'extra_confs', '.ycm_extra_conf.py' ) } ) RunTest( app, { 'description': 'RefactorRename is disabled in extra conf but ignored.', 'request': { 'command': 'RefactorRename', 'arguments': [ 'renamed_l' ], 'filepath': filepath, 'contents': contents, 'line_num': 1, 'column_num': 7, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { # Just prove that we actually got a reasonable result 'chunks': is_not( empty() ), } ) ) } ) } } ) @SharedYcmd def Subcommands_ExecuteCommand_NoArguments_test( app ): RunTest( app, { 'description': 'Running a command without args fails', 'request': { 'command': 'ExecuteCommand', 'line_num': 1, 'column_num': 1, 'filepath': TEST_JAVA, }, 'expect': { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( ValueError, 'Must specify a command to execute' ), } } ) @SharedYcmd def Subcommands_ExecuteCommand_test( app ): RunTest( app, { 'description': 'Running a command does what it says it does', 'request': { 'command': 'ExecuteCommand', 'arguments': [ 'java.edit.organizeImports' ], 'line_num': 1, 'column_num': 1, 'filepath': TEST_JAVA, }, 'expect': { # We don't specify the path for import organize, and jdt.ls returns shrug 'response': requests.codes.ok, 'data': '' } } ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/���������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0021767�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/.gitignore�����������������������������������0000664�0000000�0000000�00000000010�13746324601�0023746�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������*.class ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/extra_confs/���������������������������������0000775�0000000�0000000�00000000000�13746324601�0024302�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/extra_confs/.ycm_extra_conf.py���������������0000664�0000000�0000000�00000000326�13746324601�0027733�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������def Settings( **kwargs ): assert kwargs[ 'language' ] == 'java' return { 'ls': { 'java.rename.enabled' : False }, 'formatting_options': { 'org.eclipse.jdt.core.formatter.lineSplit': 30, } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/extra_confs/simple_extra_conf_project/�������0000775�0000000�0000000�00000000000�13746324601�0031531�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������.classpath������������������������������������������������������������������������������������������0000664�0000000�0000000�00000000406�13746324601�0033435�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/extra_confs/simple_extra_conf_project���������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" output="target/classes" path="src" /> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry kind="output" path="target/classes"/> </classpath> ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������.gitignore������������������������������������������������������������������������������������������0000664�0000000�0000000�00000000010�13746324601�0033431�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/extra_confs/simple_extra_conf_project���������������������������������������������������������������������������target/ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������.project��������������������������������������������������������������������������������������������0000664�0000000�0000000�00000001264�13746324601�0033124�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/extra_confs/simple_extra_conf_project���������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <!-- This is the minimal project configuration (combined with .cloasspath) that allow JDT LS to fully work with a trivial eclipse project. Specification: https://help.eclipse.org/oxygen/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fmisc%2Fproject_description_file.html --> <projectDescription> <name>ExtraConf</name> <comment></comment> <projects> </projects> <buildSpec> <buildCommand> <name>org.eclipse.jdt.core.javabuilder</name> <arguments> </arguments> </buildCommand> </buildSpec> <natures> <nature>org.eclipse.jdt.core.javanature</nature> </natures> </projectDescription> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/extra_confs/simple_extra_conf_project/src/���0000775�0000000�0000000�00000000000�13746324601�0032320�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ExtraConf.java��������������������������������������������������������������������������������������0000664�0000000�0000000�00000000313�13746324601�0034772�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/extra_confs/simple_extra_conf_project/src�����������������������������������������������������������������������class ExtraConf { public ExtraConf( int test ) {} public static void main( String[] args ) { ExtraConf l = new ExtraConf( 10 ); ExtraConf t = new ExtraConf( 4 ); l = t; t = l; } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������SignatureHelp.java����������������������������������������������������������������������������������0000664�0000000�0000000�00000000716�13746324601�0035662�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/extra_confs/simple_extra_conf_project/src����������������������������������������������������������������������� public class SignatureHelp { public SignatureHelp( String signature ) { this.test( "test", "t" ); this.test( 1, "2" ); char [] data = new char[10]; String.copyValueOf( data, 1, 2 ); this.unique( 10.0 ); String s = new String( data ); } public void test( String s, String s1 ) { String.copyValueOf( } public void test( int i, String s ) { SignatureHelp s = new SignatureHelp( } public void unique( double d ) {} } ��������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/multiple_projects/���������������������������0000775�0000000�0000000�00000000000�13746324601�0025533�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/multiple_projects/.ycm_extra_conf.py���������0000664�0000000�0000000�00000000112�13746324601�0031155�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������def Settings( **kwargs ): return { 'project_directory': './src' } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/multiple_projects/src/�����������������������0000775�0000000�0000000�00000000000�13746324601�0026322�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/multiple_projects/src/core/������������������0000775�0000000�0000000�00000000000�13746324601�0027252�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/multiple_projects/src/core/.classpath��������0000664�0000000�0000000�00000000425�13746324601�0031236�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" output="target/classes" path="java" /> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry kind="output" path="target/classes"/> </classpath> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/multiple_projects/src/core/.project����������0000664�0000000�0000000�00000001306�13746324601�0030721�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <!-- This is the minimal project configuration (combined with .cloasspath) that allow JDT LS to fully work with a trivial eclipse project. Specification: https://help.eclipse.org/oxygen/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fmisc%2Fproject_description_file.html --> <projectDescription> <name>com.puremourning.widget.core</name> <comment></comment> <projects> </projects> <buildSpec> <buildCommand> <name>org.eclipse.jdt.core.javabuilder</name> <arguments> </arguments> </buildCommand> </buildSpec> <natures> <nature>org.eclipse.jdt.core.javanature</nature> </natures> </projectDescription> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/multiple_projects/src/core/java/�������������0000775�0000000�0000000�00000000000�13746324601�0030173�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/multiple_projects/src/core/java/com/���������0000775�0000000�0000000�00000000000�13746324601�0030751�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������puremourning/���������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0033424�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/multiple_projects/src/core/java/com�����������������������������������������������������������������������������widget/���������������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0034707�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/multiple_projects/src/core/java/com/puremourning����������������������������������������������������������������core/�����������������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0035637�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/multiple_projects/src/core/java/com/puremourning/widget���������������������������������������������������������Utils.java������������������������������������������������������������������������������������������0000664�0000000�0000000�00000000262�13746324601�0037602�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/multiple_projects/src/core/java/com/puremourning/widget/core����������������������������������������������������package com.puremourning.widget.core; public class Utils { public static int Test = 100; public static void DoSomething() { System.out.println( "test " + Test ); } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/multiple_projects/src/input/�����������������0000775�0000000�0000000�00000000000�13746324601�0027461�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/multiple_projects/src/input/.classpath�������0000664�0000000�0000000�00000000561�13746324601�0031446�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" output="target/classes" path="java" /> <classpathentry exported="true" kind="src" path="/com.puremourning.widget.core/" /> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry kind="output" path="target/classes"/> </classpath> �����������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/multiple_projects/src/input/.project���������0000664�0000000�0000000�00000001307�13746324601�0031131�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <!-- This is the minimal project configuration (combined with .cloasspath) that allow JDT LS to fully work with a trivial eclipse project. Specification: https://help.eclipse.org/oxygen/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fmisc%2Fproject_description_file.html --> <projectDescription> <name>com.puremourning.widget.input</name> <comment></comment> <projects> </projects> <buildSpec> <buildCommand> <name>org.eclipse.jdt.core.javabuilder</name> <arguments> </arguments> </buildCommand> </buildSpec> <natures> <nature>org.eclipse.jdt.core.javanature</nature> </natures> </projectDescription> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/multiple_projects/src/input/java/������������0000775�0000000�0000000�00000000000�13746324601�0030402�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/multiple_projects/src/input/java/com/��������0000775�0000000�0000000�00000000000�13746324601�0031160�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������puremourning/���������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0033633�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/multiple_projects/src/input/java/com����������������������������������������������������������������������������widget/���������������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0035116�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/multiple_projects/src/input/java/com/puremourning���������������������������������������������������������������input/����������������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0036255�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/multiple_projects/src/input/java/com/puremourning/widget��������������������������������������������������������InputApp.java���������������������������������������������������������������������������������������0000664�0000000�0000000�00000000334�13746324601�0040660�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/multiple_projects/src/input/java/com/puremourning/widget/input��������������������������������������������������package com.puremourning.widget.input; import com.puremourning.widget.core.Utils; public class InputApp { public static void main( String[] args ) { Utils.DoSomething(); if ( Utils.Test == 1 ) { } } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_eclipse_project/����������������������0000775�0000000�0000000�00000000000�13746324601�0026512�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_eclipse_project/.classpath������������0000664�0000000�0000000�00000000406�13746324601�0030475�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" output="target/classes" path="src" /> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry kind="output" path="target/classes"/> </classpath> ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_eclipse_project/.gitignore������������0000664�0000000�0000000�00000000010�13746324601�0030471�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������target/ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_eclipse_project/.project��������������0000664�0000000�0000000�00000001257�13746324601�0030166�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <!-- This is the minimal project configuration (combined with .cloasspath) that allow JDT LS to fully work with a trivial eclipse project. Specification: https://help.eclipse.org/oxygen/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fmisc%2Fproject_description_file.html --> <projectDescription> <name>Test</name> <comment></comment> <projects> </projects> <buildSpec> <buildCommand> <name>org.eclipse.jdt.core.javabuilder</name> <arguments> </arguments> </buildCommand> </buildSpec> <natures> <nature>org.eclipse.jdt.core.javanature</nature> </natures> </projectDescription> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_eclipse_project/src/������������������0000775�0000000�0000000�00000000000�13746324601�0027301�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_eclipse_project/src/com/��������������0000775�0000000�0000000�00000000000�13746324601�0030057�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_eclipse_project/src/com/test/���������0000775�0000000�0000000�00000000000�13746324601�0031036�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������AbstractTestWidget.java�����������������������������������������������������������������������������0000664�0000000�0000000�00000000613�13746324601�0035371�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_eclipse_project/src/com/test�����������������������������������������������������������������������������package com.test; public interface AbstractTestWidget { /** * Do the actually useful stuff. * * Eventually, you have to find the code which is useful, as opposed to just * boilerplate. */ public void doSomethingVaguelyUseful(); /** * Return runtime debugging info. * * Useful for finding the actual code which is useful. */ public String getWidgetInfo(); }; ���������������������������������������������������������������������������������������������������������������������MethodsWithDocumentation.java�����������������������������������������������������������������������0000664�0000000�0000000�00000001071�13746324601�0036612�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_eclipse_project/src/com/test�����������������������������������������������������������������������������package com.test; public class MethodsWithDocumentation { /** * This is the contructor */ public MethodsWithDocumentation() { } /** * Single line description. * * @return a string */ public String getAString() { return ""; } /** * Multiple lines of * description * here. * * @param s a string */ public void useAString( String s ) { } public static void main( String[] args ) { MethodsWithDocumentation m = new MethodsWithDocumentation(); m.useAString( m.getAString() ); m.hashCode(); } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������TestFactory.java������������������������������������������������������������������������������������0000664�0000000�0000000�00000001332�13746324601�0034070�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_eclipse_project/src/com/test�����������������������������������������������������������������������������package com.test; /** * @title TestFactory * * TestFactory is a pointless thing that OO programmers think is necessary * because they read about it in a book. * * All it does is instantiate the (one and only) concrete AbstractTestWidget * implementation */ public class TestFactory { private static class Bar { public int test; public String testString; } private void Wimble( Wibble w ) { if ( w == Wibble.CUTHBERT ) { } } public AbstractTestWidget getWidget( String info ) { AbstractTestWidget w = new TestWidgetImpl( info ); Bar b = new Bar(); if ( b.test ) { w.doSomethingVaguelyUseful(); } if ( b.test ) { w.doSomethingVaguelyUseful( b ); } return w; } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������TestLauncher.java�����������������������������������������������������������������������������������0000664�0000000�0000000�00000001735�13746324601�0034231�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_eclipse_project/src/com/test�����������������������������������������������������������������������������package com.test; import com.youcompleteme.testing.Tset; import com.youcompleteme.*; import com.test.wobble.*; class TestLauncher { private TestFactory factory = new TestFactory(); private Tset tset = new Tset(); public TestLauncher( int test ) {} public static int static_int = 5; public static int static_method() { return static_int; } private interface Launchable { public void launch( TestFactory f ); } private void Run( Launchable l ) { tset.getTset().add( new Test() ); l.launch( factory ); } public static void main( String[] args ) { TestLauncher l = new TestLauncher( 10 ); l.Run( new Launchable() { @Override public void launch() { AbstractTestWidget w = factory.getWidget( "Test" ); w.doSomethingVaguelyUseful(); System.out.println( "Did something useful: " + w.getWidgetInfo() ); } }); static_method(); TestLauncher t = new TestLauncher( 4 ); t.Run( null ); } } �����������������������������������TestWidgetImpl.java���������������������������������������������������������������������������������0000664�0000000�0000000�00000001174�13746324601�0034532�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_eclipse_project/src/com/test�����������������������������������������������������������������������������package com.test; /** * This is the actual code that matters. * * This concrete implementation is the equivalent of the main function in other * languages */ class TestWidgetImpl implements AbstractTestWidget { private String info; TestWidgetImpl( String info ) { int a = 5; // just for testing this.info = info; } @Override public void doSomethingVaguelyUseful() { System.out.println( "42" ); } @Override public String getWidgetInfo() { return this.info; } public Class<?> TestTopLevel() { return TestFactory.class; } public Class<?> testTopLevelImport() { return ISR } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������TestWithDocumentation.java��������������������������������������������������������������������������0000664�0000000�0000000�00000000342�13746324601�0036126�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_eclipse_project/src/com/test�����������������������������������������������������������������������������package com.test; public class TestWithDocumentation { public static void main( String[] args ) { MethodsWithDocumentation m = new MethodsWithDocumentation(); m.useAString( m.getAString() ); m.hashCode(); } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_eclipse_project/src/com/test/wobble/��0000775�0000000�0000000�00000000000�13746324601�0032310�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������A.java����������������������������������������������������������������������������������������������0000664�0000000�0000000�00000000056�13746324601�0033255�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_eclipse_project/src/com/test/wobble����������������������������������������������������������������������package com.test.wobble; public class A { } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������A_Very_Long_Class_Here.java�������������������������������������������������������������������������0000664�0000000�0000000�00000000103�13746324601�0037322�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_eclipse_project/src/com/test/wobble����������������������������������������������������������������������package com.test.wobble; public class A_Very_Long_Class_Here { } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Waggle.java�����������������������������������������������������������������������������������������0000664�0000000�0000000�00000000067�13746324601�0034305�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_eclipse_project/src/com/test/wobble����������������������������������������������������������������������package com.test.wobble; public interface Waggle { } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Wibble.java�����������������������������������������������������������������������������������������0000664�0000000�0000000�00000000117�13746324601�0034277�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_eclipse_project/src/com/test/wobble����������������������������������������������������������������������package com.test.wobble; public enum Wibble { CUTHBERT, DIBBLE, TRUMP } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_eclipse_project/src/com/youcompleteme/0000775�0000000�0000000�00000000000�13746324601�0032746�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Test.java�������������������������������������������������������������������������������������������0000664�0000000�0000000�00000000774�13746324601�0034461�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_eclipse_project/src/com/youcompleteme��������������������������������������������������������������������package com.youcompleteme; public class Test { public String test; public String doUnicødeTes() { String whåtawîdgé = "Test"; return whåtawîdgé ; } private int DoWhatever() { this.doUnicødeTes(); this.doUnicødeTes( test ); TéstClass tésting_with_unicøde = new TéstClass(); return tésting_with_unicøde.a_test; } public class TéstClass { /** Test in the west */ public String testywesty; public int a_test; public boolean åtest; } } ����testing/��������������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0034344�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_eclipse_project/src/com/youcompleteme��������������������������������������������������������������������Tset.java�������������������������������������������������������������������������������������������0000664�0000000�0000000�00000000264�13746324601�0036130�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_eclipse_project/src/com/youcompleteme/testing������������������������������������������������������������package com.youcompleteme.testing; import java.util.Set; import com.youcompleteme.*; public class Tset { Set<Test> tset; public Set<Test> getTset() { return tset; } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_gradle_project/�����������������������0000775�0000000�0000000�00000000000�13746324601�0026324�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_gradle_project/.gitignore�������������0000664�0000000�0000000�00000000050�13746324601�0030307�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������.gradle/ .classpath .settings/ .project ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_gradle_project/build.gradle�����������0000664�0000000�0000000�00000000025�13746324601�0030600�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������apply plugin: 'java' �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_gradle_project/gradle/����������������0000775�0000000�0000000�00000000000�13746324601�0027562�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_gradle_project/gradle/wrapper/��������0000775�0000000�0000000�00000000000�13746324601�0031242�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������gradle-wrapper.jar����������������������������������������������������������������������������������0000664�0000000�0000000�00000152664�13746324601�0034613�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_gradle_project/gradle/wrapper����������������������������������������������������������������������������PK�����A������������ ���META-INF/�PK�����A�8J?���T������META-INF/MANIFEST.MFMLK-. K-*ϳR03-IM+I, dZ)%bµrr�PK�����A���������������org/�PK�����A������������ ���org/gradle/�PK�����A���������������org/gradle/wrapper/�PK�����A�zZ�� ��-���org/gradle/wrapper/BootstrapMainStarter.classVYwV˖#DgH !bSJiIh @c5žv2$ڗSz?B;Wopsٿ;#g�^O2Bx[aKXpA .X%˒ e\U &Ẅ)XpCƻHXp+|y/wPe#+!ǼC(H8=' f Ni[.יuM]׉JYU_U-kDJb)*$ Y⁥nn2+q4M%U3Ҷj̚/񣀣ѵ]UHۖff]f&4:V< ./EQY@˽Z}4le'uTJj1jN\_IձrvHz|;$󗘽a X,py$9ؘM/yfLq[YikQQ)Ke#,p)OżTGd`3¢r,[Yh.-(8Q3 ^:J=G]ܓ+(`* ,q_l0zFqjq8~XlC`5eOGX@.]O|Ϩ^Yb >Ǩr-? BJC|Mݸ(+o|z^)Vd;04z.bvD`".g3rJD3<rN2hen!2>V+?~0?{g7y}^@O4V%\IZ$S$9T.٬qf ]}UA!  + ԏ{e n]*d0}Nε0mJ-�kмYqZNeex45yz7n#4b$wѦ9z'e42l$ X)L#jSCŁv)I@EBM@ mg0}y[vvDc:t`րC܏c*�2&H+?'9TOˈ@+WH?TA XR*;C]$ݝ']AL &w#,G\7 xϣ;NZr?a\VmˈãFX&1 cqBJCl"[F$k'UBmk$u : Hd VcO1 bᥩgsSxgL 'x* #_/2a/O0REϝ [&H| qs6!0~tMu4YO?G'N";8IPK�����A�hdf������#���org/gradle/wrapper/Download$1.class}M 0h5Z+v/ ׆p!.<AlCl II q<=| CbB|7}%aVJa34(-&u+' -yD]K�Br0FKOH;(~T2o?t=|" BF u-}�PK�����A�o:s��@��4���org/gradle/wrapper/Download$ProxyAuthenticator.classS]oA=eiqUJmk, Ƅ`LLiRmb4 aL|hG�UB }{s=wfo?�F9ric l&l[6pFy 0ğ3D=XM6RPoA@.<7q0~%՞nP bp^Pw8T-x3^_NG(쇁ͳ* -w(/UseȰ/ f*2a=?wbr]S 7N Eq,Ŷ֝bnj+{)Y(;C w ?չ3[!C܋|}*!'3-kmJqk -kzW按5k/Ez4K 1"_ȉ Mk|:#\\!Cqr~G;w$1S>Mˎµqd#c-l̈0b\7pK^\dNPK�����A���&��!���org/gradle/wrapper/Download.classY `u=OFVH`b- FZ$`l#ia5#r[7C4Mܴq>Nb'NZ_ N-n7m~6iM?i~Zm}}{Ͻ).HກaTgdYټ,W/_/ienxbWyu[ߓ˿/???13y E}Y_Kk[7AknU3rN_3_RͿd|]6)/mG7to؇t|_&*HT8""$4]蚨S.juajx& Q/t]M5ѤhD.uFj"n&]lͺb.nV6MlĭM�^J@xs/-fYSzc b9& ]'[?a2v@mCcOT ܙlrJ5?o{Cjq5gSs;e931K:3\6<a{4{gCɴo;rJt/:)JsнI'k]E1!wԼa(#I;iMY)+5ayI9O$Mi5fq1nrLƳQ^*D^ͭ}t%SQ 1PIi?_K\" 2+Ee2^l!gҶ;caIGOf%BQ'37fKxVZ+`H!QR`V\rQ2{JJ݌/R<?idۚrUAtQK[\fh$ZJ'ʽsV,dLt4329LZi{gJ 3cm֥6ytҺsk7mE\rCoy.8>yLrހ奒#;J8TR Yxʤ,/X~fe6Ge0};^t=%l;Lw0܄$dέ oRq.s MNzSijs3ޔ-0.`Kc"͛Ma&|dpʹݬw< 5S9U2϶vMtStiCi )v~ QѧBN4C.IeGwc#x xw*kb)L_gW}51`C)HŞ\mR/3 M511$T E D ^.هM1"kb'Ę&b8)M1!LqJ,)ބMik3wlβ@P*mCO  葓'G*ADg{Cms~t) D}7ZHQ2E+GQdFtEGS6ӃdK'<LQ`\/H>+G|_&Rʩ'Cl~ ϙbRrJ$LaiE>41cYLi,&69q>RmE\9tʋ*M8HbN: 0W/+6UBQ9{\k2$Z/Ltis?ZDori1^H9dϳIWM:z1Ϯd:~4wC"ʹ&щaهrAޕrFnOg 8.SCn84\4[6mLq{{{:ں=Wk _dHJNb^:ielK%(-&ZB XU[2Jo#mi+⽱ZF0K#Ojayɍ R;HziUٝ&}A RYuemi\,gNILIJ IcgX8. EI))JLL6,M<US[ILfƓ(�YʭK.JqT~M)_rUӤǾ{XC"o/e3e/+{z|P1B 7K0gF:zT꼶xԋ^'5|pPV5e^?\o /߳_uNdlse9UͿ!`3hj+.`!**RxsKQZzGy7% d2.;([,}U(T9tnM:={+=?!|kIKwfaUgwkj>\z]oˎPR$0 !l9g? s;%caD`?,_ |+k".]hAik/mQUl){k{}w%HUzRXh^\mn[DU[UTu^E-`[U- VJb-nC؄J]l.YJ<IK)!%6_C _sIzAt Jx o!uΘՊ#,Ow,|;ݝREs,ţ4qUheog?1('OxuzИŚuോhh GXGK"p$e!bcOpM+^{t9iQfO]$ѳ]S,as7/"ޒ-ױ5޶/5\ԵԪomKZ:WgIT[qJM{j97[>:#zusM띊o@꼁,֞\4kx&{_Uߜh樇}:{1!10hGcD02'qO8k~ԟ#k 8/ q|saR75+x~;U/.B!5(gdCxj)$G^�'pb/*Q>T GPx?>@,M/<Q=ޖNɿ]J!p$dCTE&%y3E=*/i,z{/mZfGDz4<MaM1E,_x"J#rO\(%ĕ'y,w8>\bp#|T8W7a(ٞ@ p#=H p=|<QW'X#Q'pS/SI1TxD`V ynoq爹Ljlj4i8^MQYt9578 bdO'}R2X3;8~3p8vnf|o{'ӝ9_!>,aE] O#*g�_,*S3s2BW/Qy=%ΆBCJrO0lO(<٨+X<O0cP=-X1F]ԕ2>CB1| u#9G IR%̈Ad% rݸ^R?YU"Ta^H?PK�����A�N/ݡ������1���org/gradle/wrapper/DownloadProgressListener.classu @E+jDEкe$B~҇)̘֢"n~<0y&\8VfsE1a< 2y`+Χ0TxLnQD1Aj,IX(D8gQkY?0&/׌##Ѕ>v~�PK�����A�~0^�� ��3���org/gradle/wrapper/ExclusiveFileAccessManager.classV[s~>Y5685D2؂@*$FT sqB҇xVKOIKڤmzJچrӛ"әf&τ\3nMgzQ|;}{WG8:¹< 5Lj8#) _ӑ9yA_R'u5\T% /R=(4U qYV%:`+:ᴣ:Q7tPO@u}WqMu )S<?yiP<viX몕u,w6;;;,ЕZ`ԥ@G`WWk 9N rarv@"](dyLSʌ'G*^r,Va<i%[o[ժǮz;*lGJV\kVtep-yn yqLKԧgRU_`'J;"AvKsJ:`*Q̪G]l^Z>oD)H=喽J$m׹Uw45.^A>PoeIKeٷ1#Y"Y lrTg]+̭MTI#=+I2qvŗt\fdjlݖt=^DE'/;JK[֑a4^/5Rp~00Fkho <NY^!OҼWoi557Y6].p0՝zYHj$%=T2/iG I'E%)4Pĸ9~!9ӻlX~glDiNk0K17k^&gj_ nhwpR- Va-a~n7!"^ 3ܓά~ENY^nIgkI-UzZ&H`/c!MVY׈/]ˮvڵM35ϩ l]/u6;7`v]-%#kD͑>H6 9=#\rb Z0͠)nC id 0] :zUW q ! wӔiS;?Dl`LJh{?T>9s܃&<1#5|_~nDRb\_H ,">"wq@GΡ;0N 6O躅Cj:} nn{t/r K2- v:\G/Ug}[qx[{\"h\2l  >{*yR 4v.ebT.ٻ+`9=34HTM| )׹L"hgOE7s-6ep_̴kH="sEVb˔z5UVMmm|G0Z(=ç8}'�Ei Q}(8!v]wq&2{ 4zmOgoG#|gmH"aA<Gɢ2<cT75ǨyI! GI;.`OOCF$ħOPS,XT{'.y`F_V/"o.ټbpZED)gPK�����A�z\��Q��-���org/gradle/wrapper/GradleUserHomeLookup.classS[OAF]R(j[[ZU˪T Od .dYlW$jj>G5=R+ȃɹws??~XªQx)I)`^F\F Ṃ�zQFRhMK K [A*_ɮoANϖvӟtp854˰ZsM0ݍ+e錞K{zahӱa{jr⿅ >4fڦ?(06 %L7k}8e*)v0 DqZ5*>F]m4xqNuj}g'-mZ0Zjw䜦[b!ڋ3)UD0A\>yIA$Rf MxfFӴ*e]ӫxwԯ x wuH𘗽P`{!!}%nx/ q}Jhͮ0,މ=�q@{,Qzii“G7 !8CH3 `_[(`+8$U)<$4OZd4}/z@:CYׅ"D "Vv I (&%꿮)[|SW/9s ,n%BrUv/PK�����A�j ��4��*���org/gradle/wrapper/GradleWrapperMain.classX|?}ll0" A�/l؄Ա dlcbHBE$Nfu$It&3I'I[ٍδN+{;˲t?޷^Ï 6؆UlKRLi%\+xUrj=~kNEĪToRf5-*㭒vC}_ ǻ$*= ޫbާb(xP><~|@E>" 5)a?xDc ΪxV|Tq >S*IgWEy<d_W|Mr\~|Sŷm㻒{**~ ~g*s흑C:ԷgW 9q=9Rh@EL[zC TD5vQA͂n%c6 CsҪ"ћI6R!4z|HOXZ`]LGSHHF*wv=z,I| ,?Xl-]O_DZrޥ*<O<bFZBiZ1▎면zFpHiʢf8IK N-#''*A4y(c<DrF舾 ].rXxx.tx;,RًIWBNьeX]'ƴ9HVώ q4^QP˅62LIX.3A9gN%-dJW̐J\jeq2f&51nI#FԈ[94}&/&E_5':`⢂m5\4V f Gq3х I/4]UNF  1ǘ~_)9&Pr,3MG j~?i3¤)zzGWM iܩ] _Ru˾<;ɸ8''4<K/gۦ)X{:m$QS\y _͞tX!<Z~z\e-JD(Մˆ ?UemZ;F<D\ilsDRM,ETkF,RbMԊ%tyjɗLҊ%ܡT#H;B35L,֡'1!8=:bB!#)3;Pt]ˬ4;|ԈZX!VJ!MaSN)jq1CklT!5b&։:5lBq#;3FB9(^ ٣0rft=4W^4QMX/YlÚ+b[s6IQ^yb_$ˠoXS0ts<8>z2/LHZR-WGb kmQ-6F^y%1Wk<ǂ<2*5[qU<((#p.<.m {9Dev4vG *Z_x;jsH0de[׽΅ .y:öOνyu/r. ]T9x8S;'e, Wá2JRilSƶh8iūt--إb֩p!Ů8?r/>ЊΜߒsS$7&drM-2yayyIR ³Ϥ @t̰Ʊ;Wh[":xG0zxr]CvYcN/s >gh[Ry.ϴS|l=1Ѹ)/@nE]6}y1xuss(c;6b7f CX 5Cy{y +I!!kqAR7p82D1B,WN㓘i.) OeJx~l`P(BWHpB)K$ 1@k$[Bhj(Yy7O"(7K[OV56Napl ˹VLbBSX5O`.rZźw!u-ݼ] E# ;тt5@lAI\۰w/Vƕ8vww#|6џe@_2 9�nDX{a"$}~3ɥRP( ^q8i7_on5uh[낚@3b@'ElZMTQ(U]yUNvMο3՝]_@XxY_s% l(dדǮ6ǎtt{W56f)Y\eCEd [&pYh e^,Gmx~$3κʴO3&+( lA`Sg?V[=Y0xsi9]_]f0K;DYtn|f]:ۙ;"MigS;]=1vK<4i zY}g}%je]cFz* lLL5L&z9L_1w1uw3mČ^ݪ _I ΂ hqk;@Y{A͢_z39E ,e! $w wܷ+0H0}uSJ`IPK�����A�Xs������"���org/gradle/wrapper/IDownload.classE 0  ^b AP^26J;t>;ɗ|�{z~+%5O&WΔ(a_4[gR#!XbQVg={}1AYCX'R5c/J$S@pP\mKulPK�����A���6��"���org/gradle/wrapper/Install$1.classW{W~O2fZp[hF, P mC@Bcu;l&3,jVjTw%mW7ly9||;9y߯` [ C22LɐO"3Nj(&DAd%pJ"eMb$> /H|T8#&g8"yGEc|':I|JO'pAğI\`BO%<>#KIBg5<+ ]g v}*=h٦Š)㌑ܬw+tN*Ö򬩊oθg9lFD-:'b3DO7zl3 =e'yY0cFc0m8" U٪]-zc{.R\v^ozSQSf}er̔59)1ód]Fe PGl\6)kl㾣tBu{[gJՏVm"ogG|p=Q1Ӧ/wLPv:k 2l4[n)g1mx+=,R}ZLnd`;p.o$fYWn:e%Ҳ1~ţ}癎bHcn˛aJZַ {n>d.[t<n_v;q`A|C&TXJ.xAǷmVq{Zwd=u|}~ C ?2.)Ķ cyQ g~_ᲆWtq:aN5Ẏߊ߁^=lڦ/NH*&kx]:~?h?U*.w&xKl~R,'Zk~bdd< w8=+jF6E_^$-`y+$1cҷķï/ݦ,AÄ"{zZkspyǭrs%q*v74cf]x+pkm4ruggI,6z2 Jvm_:ڶ'W-.(C,U 1ǶޮCgl >FFǻ[R4+AG]-ao߰[+J۪M<:ҽϬRrf("S}V`rn Mt(7X7vGoZ3snK)vWfWru<+"l}5VĻ8o<2f0 fGZt@1y*s,:2}dRYD3,bT|UhPp^z0 Hw<xW/_Gx :XvW*wPʫPbE\EE@0|z]-_hd9>"N/g|h*Z >wTST*UqgrOEa}h:9=<KŮᝊ=.7ARq%DS ϡg^yU*x7ihQwo9 L3wyqcs\;]!}k9#MN"rd$yT ɢF]aߤ߱a&^J2xX ᠪuU/z 2y�1N0'Y4dySTZ=GVaR$CWA(apL2*zc5^(%QJƻB_7ՠ̻8ǃ U4/PK�����A� @$��)�� ���org/gradle/wrapper/Install.classY |?'ͰB#v4( ! a1 $w%ΝKVVZuwUT4*uZgk}}o{o)fnnnAofs}yoO=KD q@E4y҄Kx'~.D!(c)d"MarQni҄)<CTxVgxk <7ħ<)4hq \ i*󹊫^ /C4H /rt;#DE\<3!> VU!Z^^Z(5Өy .MA^+!:I>[By<7ISͼE nI[C/Tx{Zo3_q4 !j6Cۂ 3. NyFEuh͗Jc) NnƖ)w)n^Y'^y]!>\+Im .'F)\߰vS-74ڴq]04]ҫzŶx quxf=.W1Nbє:;-=5{,۰A F=hB0X.ݺU cmQ<k9X4m.c5jۍdr٪(4s4㦽颒O59إu4ɌͩXam1(HͺewowLOh҇-Cz3i3 v)KD|Et3Q-6Wg30)h{lK&+4%7M10{JKh鲖TiRTTTjt7fZr\+ \oZuV[n-[%܂TxnH*^2z_#<ω!$fӓF{2"XovI[Vv 3<)t =QmzζU>x_u<0u%`6FU*Иiصf2;m!�ozcSz#nݮӂ:V7SGB{ڨgRreOG_]Ѿ3)YZ2VjTb`CNQyDj7<FmD`ԅTӼᐭ_ymq hZθn,c F0ɴa7TXqadRPMmLf0~FdGܜ#zuŠLG@ 8˨뭄dU)3RžFNceoEΊ8XZn?{JMk1~%b1=(S"axLn1ؗt+L$uY1OR$S~ylbhO!_fB9\#n[}_8vj$GdruY焔)}Qx*fd1dP.J|Ec!:=ݗJ<sT.`-&_T,[`S5t*] Bi Z(w!V]"Hy򄲋!ZO븹eUy2/^*?'WszsY&\VeV8EW%Tz^P{i@/TzQ;/=O”EENt»UuG/ǗUoP+|c+5o_Cl -kj+Q(|77bDoVUv;ZkR-*Js ]`xT6cF&[3]|](hHHj񄭡u3>L&$Ui WZfbi 3Pc _v U~m˴By'">*?Qi/ݣ߃U)v'kKR/sbz֥26Èk [1S[1m46߳ܞu[ ,4XOxHL#*=5)Z53 X#%nAt6FvZif[-D1-PShPZԤ!;ctmW8 dZ@3#GU>ȏ)Oq?YkYzN*?%oLj3<X ޹]KU~gU>)} `,kiZaaC=\i Fϫ1U;H4PSUރbA!д՚ʯ˴ٟ^1%Rш(z}Г3MNp:"p0أm@I$TU O-eoT~E Y;¿P=A/!KT|̿u#i)9{H[?T7|ʿۑ['ꑐGOCuܗ3R46s ^js$%ouƞRy֎ӎL vN+envgNX9z,2 SqIp7W(8S.2p`~v`^z `r۪Fh趔?paE ɤɹ%̷k/K K=qr@xt9nJűuCrYC)3q> t8i9Čm{<)L%e0.cƥHQTɑ=I_ۖLDS!4rCe�?EdCdn[rd]I}yr/#v=J\ "8wDTqrQW%7�N-ǒن4S6&ݴ:ǚKV9o'VN(fPQVy8p/ZMJ_IoP±ɹ :9FIeFtt d�-7TZ2X䀦D' as<y( uFn,(\m3.=6a Q# Ǎ^;9zĬ3\N*6<1756q u,L%p4!FAf7R"ʥ0]NWӕʡ@x/y⼌dC_Be|] ~< P^ Z 'qeH)8DC4z)v.Vj4N h64#e.M }yyr7ˇޯ#)x, kLl.{eR?蓼\yL \80HSZpnEy�QrB2P4 G5.+F  TBn}}X[Fl¬4�} Xy7Í aڂi)vЌf2t&ПfVsi2 }G?5rN�i5oY0<{-DGÁ+)5y~뿇tꖰ"]o~*6jJh+ .vOP;tj9"t.:b$R3�c(x1V #y)(Jgz-=�#!bAzRL]ìoc40}f] s%}7ez4 1"Pk.]s+>6H[ JPi0G0ZT7=\9z$UP@,�Yhhw>=T 9i v"9XA zu꧁NµE+~X`^Gʖ@/>FKOKipur^E)0tįopb >xG$<鬂@5)'ț?yt$[ԛxJr`t 3x4W>[[hE5pVAjE8$C[vT fCfu |s+m!t6 'HP9=KG~p+eM1j⽜ܞCcyzM>c ,䊃TZ^r^̓tNkm 8e ;N: N50Vcy9q4OkAjnu2�?@$S`E̷&".~ڲyEhv+^ lG3r"#qlQ{`{a<bD} B̷!{W#GLE<(:ޏqXbcp< v=%Xjl?/+งfѫb,z^kh'ހOwy{ӁL?u<?]os2+qnŗ~ztcS[SyA{?Er\߁L8<�?TmP#Lu)y4u*Gn|N<TaUM2^_ NXԅB 2X`x&{ )u$Wh]1fG}Xs+Gs-_;ʑN7 Rw.$=M\0N;H4t8P %�ct�Y(�~Jd Sh TX9ttpAh$uy.5uw1Mah>:V@˳Cd̏@c=%{aZ(}|OE7~Hmc-V@`2! �&˶bf'`)Gu\^u._Q= E<Fsߡ_P B@D3"CP#g>�ɬ'Ϡ؀9RV-8\3;5PKDfv G:XYI4't8pb k "ޫpd Zz8I:GKy=#I'32/ (zvv O No6λ p޴a aQ-sNAsn㩨PK�����A�y0V��������org/gradle/wrapper/Logger.classoPǿ*1pL EWo1&d  f&Ki Xs9* OHfp+ۨ&Q,D];Bܕp A`w 32 I^m{]⯧>wީ}N6Ǫc;8ƄԵ]u`r壣FQs5mP힩TT-]鹎a-PDս +TYb|Tuz|<[(N$&cbXC3RWu:+mf=mdϞ:?2RlDLBZB3{/AQHGB< 2og\+j/>M\> {JQV \ZWHcHXŠ\_)Gt } РF If:B~@R &~ 6H#J2b&J9򼉖<MPiB!` Y;"h}2 eү.spN=p;U�,+N! JZ.c@wgX[W ^#H݃~Bۥ"GZKx- j:5(K:PK�����A�j j��V��8���org/gradle/wrapper/PathAssembler$LocalDistribution.classR[KAftq[yk[KA!|(L!N MJP?U<3Ҩ|ؙs.s˿�ū9`OM}Q&m5ȏT0芟"VY~ܪ+Mc56-ډiK` {DjyT򏄽Ilֲ$,4T*? {M Fɡkmjs M%ydw-~[ؑ%Ru<[5'_n$6�E<`=fAxwZIDډ?7 U46?S@h<pC@d,o;|/G[#PNzBf,Qe,!A+efc,c#naeM|D(#lQ[z`LHx|'ؽ >=W-ĝPK�����A�bi+����&���org/gradle/wrapper/PathAssembler.classV_.HZYm,c;`HeL!mM++NB눕WzM+=Һ}֑O{]K&A{7̛p<CPF) WQMYX*J1`NYevbPvE*np[1l(>'>ENs2|^m_1$R_/1UeFߌ[1|/G(''_V}?Ppl'Mǵ͵k,Zֲ {;(8uy~lrzjuiaj~lnjuaq>;sYA|~SuY0Dr\rbPpdn~#[%ǰ6E7c2E&"^RО\V(vtڴa/kEC e6el Kv!ܲrٰ3s1ƨ6iѱ~hple)fBEwt-pl}-ʹt ;ƌ.DF4v0l\U`6콃8[d^+AC-<|xp`LqJ^Mw;7kۮTg۵q c<ֺQ8 &66K7-װ屣 %K΂-,gY\"Me]AFǙ!hTHʃ n&+}_ xTqQ+&C 𴆧0!i^佖5w4<Wx, [ÏE')hFs 闸;p+0Y#*~kO.&eBr(⯂hGV؝XiwT)VMl0U 4Fw w񚆪D|Wu67@ 5cƋWa3ɛhx]0]֞db0Dz*"[QH`fnj<ƽJf9#+~[w:()Pni!gn Ca1 XVk{Qp'dlX ෯PrPXR.=d$�$ΉdeRxcgq.ݒы:pH!vkLf[BE/:Mm56}^n�dgUt_`#� I?g}r{l꽜T ʫ%1@BxJ08ݘH_C[?Byv`I$% &0/=oUʥjϤ5D.v_"RCWZG8t8Tq΋zPPdWRH ];pzd(8JFO!MC°t=ۘ"=~^f.x&.S�] 5 RxR|2\85iD4%wÖmԱ x8O<lmrt=3Do=8":xp'J 3g S}܍qGcd,} 1z1D%/:,{%>Km)tlA8n ^^4O2SӴ#㳞 <XU-vzC!I>: =W 'KqhJOkalq8Ub4^"/YwI%F&d t<:{`.w$-AGX'^dW{hB4z}9Xeu{d$ւ {̠T_FV�PK�����A�dzߒ��= ��0���org/gradle/wrapper/SystemPropertiesHandler.classV[S[U6I8!ʽ4H[[R/H%*$%^!&sIoZ_go8ԗ0Ȍ}GN"dk}{N?o}Ť)0uL+#o`VN"Rms }h¤Q9.H(MT,Ixx+Xk>ƻR'A#CuZlyz"Ls}-D(؆ 4MYfLgEKeu 4k{ڠy0hvk].Yq-نn'id,;JFJmZ:ۡnѷm3IS8(kX# ӬcBZ뼆cږ 1Y"L0#daz'2swH-t9ZXQuE#nk%?>YnMCyE.LV×.91a({7'\mƀ 1#ajNfgs}☾zT<(Z2I b\Q TZF" CnQrėcl%ήSMfԆn \mNfo6=[]rU&IRS&dH# Y/F戠]3お/𥂯T|oOR-X@-mn+6Pyds 9vDx)+#tLbGbT$5\/۳^*x;@"kdPbYNֶ|xXfMًeǠ*$LTaS?zeZ*g?e̪$"OfBX:]w"5Yr%.G5\k p82z ϯ%ZBG>yH?e}GR-�0A 0Q\,HCԭ57GxCC!5FFGrh׷u-zPDZ5}?Ρ�ёxT~wg9t?z=~9]g=9V9z Gq9?1q4#E;Yxq䱕 _Yg9Q'-aش> aWI\b>t.awCtGzm2)p+G%qg\k,s0K0Q,Nwڵ|_F^{_PK�����A� ^F����-���org/gradle/wrapper/WrapperConfiguration.classmOAփ>"!3 U1U H|Cp#]5Si"1e[733\_�(K`6 q?<7yBi<dm?V7]aqh~2 [ c:mI5-3T_Xlv_h3۱%x͐Xuo؎{ԲfmӳY$36ڞ߱Ϟy|lyNW]z&g!| .PorSzQ*.쫚FGgDg#~RTN?G=DhCz{ ~TY  ^_n.5[7u 3 ~!+5P̍ E8_:_F 0�R/_$|9`0v(ȏ?Rq8A2Sa/Fl r)`1PVQf<\y-Gٰgq\%n}s\ pp%&H p7 qdR 2A qF0[J 2I sR J  IHQL)AR2s%HU AI$@jJ RWj N}@d: PK�����A�����(���org/gradle/wrapper/WrapperExecutor.classW{,yY 1�B1wSP-jYKk{4iBz]ڴ!OzǴ`Ѹ =M>~yVb޼[_2DO O[Bxko 2;d4q.wXs $.{{yxsx>;Շx0Ga|O>gـϱeċ"ƗWC_g3! c}I7M, eq5oIh9ܛO zG%DNΜL[: aI/Zj:J*CB53uh>zh|=~ zSZDN.FA3-]+JXi^\g'+RGhKF'$Q1-}99eٜyT t=w ZCMIzNjnhvŪsV˔,YOk V'HQsTSgdSL8ZP6X-$Htٓ=g3Z8`Msie ֋H%1u b[ly (%3S Gv ("墩Yi9:3lʓa=O3toP!,P4kYE;UW,!! ɉR/U.2 S *{f 䣃mH mwR(ańm [1C>/9@U)vq[UڜPeϹEjj)wG+1_*QIg$Oې͔φi"U"y[QS[_]2䂷F%jm!V4My/0e\\qxͭ0 IǦh<:ID7o,nf "Rl` NpC ; S۹QVZQ*xŘ').) ~Ě$R5, 3JoN4'6FIOYLyT/"4[Ϙ* fM>}mrY(nN3>15ӭ_)g_oW%lU̱|qz`c!Oc=PG)?+ 'Ec!UCFT$|nMGf/>k٨ufP^[YO|ܮ<DU=5E1E9V(IУ8Z}9o)~]/->cK5<1UT1_UME?Zο[e5nx[LN.z c;߹ONQh tfZ"[vs?*_bͮxη k ZŵhD22܋af&g( [#F$~_/{Xɮ%|nq1i@ D:l#^Et^Ctz==z#"ܠi-f4/!}'QO+eFEe-М܄} rBHy4FKX<( %XYOG#'ѕt�?. hg.^F#-%eW<H<qM 2'W"~8e4cXFV4ẹ[c﵉=޽<ٛ Bhm{/u@x>wvY<wѸ@?cvPtwn=Ĺ{/^}~:�!܏S"rWbD$p@8~Xqb=4e\DE-yS2A;[)-9VDD~'lJĽ877!⏻b"ȶyl9VƎ 9*]&t�M&U28sl%WO$籗]<sNet#I|ivնR (&Bl|a¬nJbPnzϝA3gm=q08(Aäs9󵇧<)L;1ɑA̤@Gग़x?Ba9% #O BK$s""$2mPPn�8 qࠓ~xB PPU):##FJ!YWf<OTP%(UuԷLέxuN[OXY}JQcXMY؆''---:3"֬AjmF\7[nMD^PK�����A�_������#���gradle-wrapper-classpath.properties+(JM.)M/JLIM**+M�PK�����A���������������org/gradle/cli/�PK�����A�<��S��1���org/gradle/cli/AbstractCommandLineConverter.classT]oA= +mt>BHhBem` Cߢ/42YPa9{s?p# 5ϕBy9yEx\Ye ZpՆaEjw I7|ZU c:tiw]HaٲzQdu;BrQfظ&DV\%6# ǹƙun]s̷X3ػe9H%ҲIk.=,ȍ-1ak^1蔕)9(id0^}y_7бp:%`i6t*㰊jBL F vUWm_ImuוD I8H7Լ&%DDՄ3qONB :ACvsπb0\8y 3CEw&T*kY+$@B!K͡5IYF6V�ANwh`+&XEKH,έQFV# J#hRq +dAy ~#TN*)oްOOSxΑ 86'??'k<ų@zV`PK�����A�y0L�� ��;���org/gradle/cli/AbstractPropertiesCommandLineConverter.classV[WUN2a`rԄ IєR Mik$d&L0 ]w_ۗQ\KWuIH&Ҕ%Ys}g?�!>񉄋H厄Uܕ{K3 #;ul c[|b_J!7ØWˎ"1 4]W6jTѬjA5( qYTSt)p#kDTJ5QhtMhoFM5mMVjUK쪡S5 eDhfk0fC%QQr"g^&I]VZb*ZPr$iж"\<50jy6ZΒn"#iLdC+LF*ur52TѶRkZ )+>m2a*CQl#6zDE U4vryw0oQTE<洲nux|6zNjnԢzY ^:^79n{QN^dIvew' 9ؓQƾMȨBaFw@M lu{ ]ʈbعJwHrv2̼j<<V'd9x5|/#5S#DO?0k{ڟ$MzmtxVxˍts֥֘1>^.T߆ڰCzs.k%ꕊ uRj9M:d|",}@\xQ ک7X9|ue0 ޷�. |4lk;ɼIi ߮?+&Dw^z BuN2#iZk"pOag$7J(Bڌ"cܯ!16Cfkt[¤q0~xȱc3B( <qF?Ɵ 0eb!!Mv c#X!D/NX5Ӈ`s7yD]oׁssqn#7+I'%mJ L8=;RKnʿPK�����A�# G��K��1���org/gradle/cli/CommandLineArgumentException.classJ1O3Zm+Uו0tD23k*|(1IK-Y  2aEE9炧=OQzTqvK9fU v#tiˀFCb!ė*BE{2ȅ 9`)K,Ihh\x &J>p1YITةQcBW~ͿDcD y f@]tӻLA^%6uV18(c]3 2gy=ݴoa̝̩ kqv>PK�����A�'H ��g��)���org/gradle/cli/CommandLineConverter.classQMK@}ԯ'"4 FM)HQ ޷lI7ݔ6�MBya=t$S l)8A {Oyb :˄3I5' JXdT"qx{a/4OR1=Q615 ڹ6ƇEWbRh{'qj<R:O2%z \߮XΛ v+T@h`u-B-٬e65_TܚJVpXk{PK�����A�"zZ�� ��&���org/gradle/cli/CommandLineOption.classV[sV˱E  cP@HICEUGT\Y S¤tCg'C~GVdٲKΞ՞ow=m;0%c:6Lu`; fx%p -xK ܆*cAF>ꩂ4!#x,ƠpG(ߍ%qKFI{dY5~G*n4gXBǔ^4Ubkֿn ,[7'yBQbeI3%5r&u#q2Qy[wHHSYJWɬjJf@H7h[Ҭ,J8ڲ -7ܨq&nԝR�1GC (ONT4{Zj̨.2jyi~\PSDz5,žcKBO}p,̮V!^ԜUmH(Q"lbQ-#a("Zr<tNkMGk2S8r'?$|r`f[BoTt tw鰯5Lið8[WWH/Z$:aZAf7)_Pkd,OG×K)b{O lҴZ^d;KFY GFh* ➌ `L@BwcOd|#|\e;բ aOB 1_kB`/[ `kwu_bTWZ7"vߋ*U|r᎖w\R̬v_X:{z= _bwiu\fɰ{BϨ]ꇥ QՂc:l+UK%ͤ`ii=nQC39VU%" !sa ֖v(e`kMpʮsM .vY~%y!j~G;"\/eB<CS?A$3=A+Irpb/ @iڑsb|Ћ沛X<1ZD -<Wp G0N%cUq?W)jV9:ks(ܠU'�U'#Խk\9xw<q]T|יVrüΧsaӑوj@C< `I8fN99Qɟ^3t]^s(Y.c,sel&7w( 62� %V.zه],~vbaY]9\'ypB/ʯy pv\/S^v3^N襘*"Zuv+v7srNyH D Իۯ~طZmr䒡KL@əBnә#x}/8}Z4]rt'{dsyI 'jbf(֮N/ձUJR[g|5W/PK�����A�2_e������(���org/gradle/cli/CommandLineParser$1.classA 0EhZ v庈kCPEv-iIp.<S\p>?fxCDlnmMJ]k'iu#0BWՔ!f,By@wZ͕t!BI]#HI9|g|{ -|�PK�����A�F=�� ��;���org/gradle/cli/CommandLineParser$AfterFirstSubCommand.classVRPN[J;be-x)U.R(XE_( |�gtPq猏C8I K0㟳{v|gw9| E͈4тHA/pK\3*B;ExqOH0ԛoB Нҍ\$gȫy%ͫ!k)USdqW5ռ0l%^--1x[+T^ɓğҳr~I6T/ =]kS1&U`fV Ҍ)F2/ Y88f-IMy$ M1eLo H2ڼ})MzٛZʑ"P\\y6O\}9ݲ'JBKeվm gËYzK/ Z1}**2 ~CG&1%`Z 3t9eaёgںokǔ@g%Ku`|G%E5e uS閻5ϗ4ڪ۲b}B\EU6:YPFJjh>3L578")&COH)YslH#?nA;ΙZwQW+S ^ͼ8W'AE[=|>>Јs6MMT > wH{im::^A:HG8d"Fx&O+\AX!vѸq HW69Z e+˝}с; \G7Ĺ(( ]LSh`cB p�.9Bj1 D'($U<"z@'bj']GZ f<_PK�����A�_>ң��)��3���org/gradle/cli/CommandLineParser$AfterOptions.classmOP( @ 1Q|Dt1wkծ5wE#]|!Je<1Mz99s۟�PƊ'IW1!y &TU,2~=x6x !fu'\lkug!s6tL![/Hvfe׹[‘zdL[N!e`oyXuyej&!&)bA02]yr^5Vf6R\4dr] C0ԖQi26{{J.-ߏ޺&Ww{Ֆ/IU0ᘓ*3Le o13/#|]ˣ%D3zaS2u7XMc qaA78yWN*%1|avť-N x!*JB)>#�iIShln|AjTDVИF(J9DRR; %܊1C!˩1(|GFJf�Kmt$F5kj|EFߧjr dY+!kmrQ>э=ep2n&0ft9\9E'tN* iϤK49hPK�����A�C��| ��<���org/gradle/cli/CommandLineParser$BeforeFirstSubCommand.classVmSW~npqhH H@^A@ m6طMXdl?δ[g?swc&q_˹=Ϟ{o/�q_Y̝f^Fse,Z�K,s 2V9qS>1M8zta,gZTRwZ\SYVSݜnhUYB/n/1Oz_G86\fjAʚajJuG-UʙeZ7^[u(5}2(Y٪Zk$N(@yA(تeٺip|0Xh5UUJ`[QtZw&~ժ:ѷ3<MSn9j*ߌq\6 G J*yN;{2#hfc f*SL Èaq!y" Ǘ axIbtxh.x%0&X}?:Ui4^}R֚Be*"nE& ̗he;*J"b68pÕ7rC.?V-0fO~^y뱉6{*B^*Y-z=:7CRyʍհjHC*dfN͔W{M ^pCMPzkt_gBR,]x;^Vw+eM+" ;zf7i^/O3}xb]C[B]d5c|Q%G7GO=m~} ҈'ҮOC.i^yb;30rؑ^!yڢ{f U5$:2lދ9eX Lm1 }IbR$Yy<HsG-vVp01 1K3gtK.c{\q|\Ňԯvg9|,Q%\^@^)x.g! E0I>|M7PK�����A�4:����F���org/gradle/cli/CommandLineParser$CaseInsensitiveStringComparator.classS]oQ=wXXE-_vK_dbMh~ fmvƿŗ`|G.̝=sf_hĀ:Rx:UΐnIWmDrΐxCѕx;E ]osKu :<7ЗM.Cg0m~A (uCGXGZpw~ &>na{pwl͑HŦ2Pz�'l(G#f`?_A\Tlתk4հ A3d{rp4kHem2z7RR#d-GLbߤ2-4ʗ2c bZ`68Bg^&j) 4:zNegD7nY�}"GCδ ji&ߜԮd 5ݏHΔ- eֲ04fFyf̪mJ%OL}$N\9r7FzsyݘY-VV.Rx=z~>ƝgF\*;x@OPK�����A�?h����=���org/gradle/cli/CommandLineParser$KnownOptionParserState.classXkx~dY&�Vdsд!@LawL73ۙJ[^6V{zJB^֢Oy<}ߟ=gfvݰ?ܾs@;ޕw%!cL!XIy^l^^\OIcB w10X/LtL{$|Uz|={el}2oɷd|a=_P8? cu 'yy(agL K%yT¯$PkXk戄ٗmivOBsh['Xh43g~vt&@A˰<D]G`Tijq5Wp}!R,z6u\K}q5>qlŀ1bjn&5 wfbqԴ]L0 KB}qeF;zylXqLҟ;C Dl2XwiMU9^܀¬[2ԘnհT ky|qG\R-VlFtw[^ZpwL&tJ%Fڬuu[s-ά }A*I/m^E> @YK#m^:y}` b(`Vʎ7hZ8h W '̇۶g vGVlWpv(؉)BA 4.nS:vA :WV:ujմ\UTBq>:ȲI6)Oݦ%ݣ6uBsԤmq=ajl-N+8*7+xC bH”.k8e9tTij$ *9 cNsxF ^ ~ڛJjFN趾d/?BQ2xEE*~!Ů656ד#f"zHxS[,mUbj%pAMi /}2h4i-lӨo/F[Xulxz o,<#^Z<;^=F݉5y豖 g7eÿ%8#^+XLw6j=K֕� xr^ i/=Tqܼ42b6vc&>ܰh0g|,eŨCgm}C~Rqᦊl>hAdf^]2gbV% %jO2!ǃL&~spfc9OvtYSr\w 7V*9dpŮ[ [3Gҩ7~vOrDx (Sl}izKg:$D)ahJb9gP6hhVD +$D땐h<Y<*6JBx M'X>bAlwĶ</#S%?:ipdey.4pW̠iNG .oŚ 3OAOYAxwu i mps`YO6jIKxUf}X'0sX?p:W*`Ps:<%J:<hJ)gx)?%mxINeIeY{V6Z7luυNo~e:W,{fV[EՂfkQsbj/s<:JH4,m&fBA-umY 6M$Pu X#^H0!^q&śxXm\xR,9rv`nA3^*ǑtYQ G-M$b:T]6qfA2YշXPa A)Ѓ SpWa `0>%Y9!e} n/*XO߯i;B�PK�����A�RB ����<���org/gradle/cli/CommandLineParser$MissingOptionArgState.class]O`6d 2 c&/1N4`W5S/ ^H#2-sKƖ&9O״ 1UQ4%ի*gE)D15u KD10ؓp :@^3\ٲŪ+^1UqQ)B&@)ʊ! B@n!fU(AiW|ۤᲵMnR# yd8!>] ZEGO*%UoulGQ/\TMn˹-02#/hAlu@t%-q*2Nw e&BgsQ\g�ݧ-F͌& nX{~ϐ2zfZC R6A Ӱἆ%GAa3@Lv0Lc8~csC7]Saf=/b !i!WiXN$!9!:">i!z&�|'d&w`8Ig9NOXFiХ� uD5x Rl( 38qhKCO p:mO.6p�'%0o %rC?7o@Ӹ<=@QO b쫇|CMa"!˰߰d^7Uܰ^t^EY3G5Dh8%h+aVE3D*PK�����A�s����=���org/gradle/cli/CommandLineParser$OptionAwareParserState.classUNQ=]v~b 5ȧ 1$ 4Z֏G MD$>e[ZҚE̙swy' :iB&1!CŴ3*f5$0b^E!YsMKc2\(4ff[\xf}f!?\t]($zr,.épx| luj6%;fOxզPɭq…% v2^l"P+ܧ8ش癄[2۟kVMa*W`sQ(r Q.$uv\ y;>ަVv[fnYx2=Y#X13:ncTŢ%,Xѱ5Pg),V5H3 K; 3'<<JڵSΐ!/g&W;;^ݸ v,ly/x-/TQZ]܉\E/˕=lk<RwgrŁtZ.=EPhh+"J6mL+"F~OA-, f1B 娆E$5"y  3@AU'2P*= QE3D5@}C2Pw'3y=RHI8[-vtn5@ $}_+:)+C EoPK�����A�$ER����7���org/gradle/cli/CommandLineParser$OptionComparator.classUkOP~NQ: SV`PDpFd?̒%mG$({'<弗Տ�Jx+#91y 2D,ʤ[,aEF)JX%lH6- uA,&PrV>6IUngq>gg1(1mI5gڦV-M9a>oz>(7(-BK=VȧۦK %kVqbh4ZYNCu)t(zwKԊMe)-—E] 5A׭Zek_-r-ܖW7Y]k +x- #4[%l+`؎ Y6|[ubvǞA(@a*jc(us ZG?ԺAh j@.M94TBjdXh%j wqY4:i=#BvQ1_CjQd%\;C'vw]BڝmHq8!A>6'1"Нu%E<lgGDq˧xIM%fv('Qvk!H]b`gPNob: |dN!]pi<//@mG4,"Y.RIԈ}rxoPK�����A�l\ϧ����8���org/gradle/cli/CommandLineParser$OptionParserState.classR]KA=YvMMw)F)n_$"h@ <6IfVf'%O>w6 ivΝ=w _|dŦmTҜ1d{mC);BNDfQkikɄaXq2ЯܵRB7"$(XAy/A7A#>;:|(F ͹=U&KcEZP|yq- S+·\U|HQS$�dú݄31TMZ@wťK) sAChU0^]<. uʓGa(M"%z.업cg"W=d1fIٔd}F6,O).a9Ţ,se,~Bٿŋ#H<V jela',~&DaPK�����A�Gf����3���org/gradle/cli/CommandLineParser$OptionString.classSNA=]ʗ(U-e)'#  XlwD w+Bs;s=??~ &cAYs)TҘa! ei,pW]p_8 =[wfnsak璳bv0[=כ)m1$j^C0dlWv8Bv-(wAP ;e(|¯9<Zkߴ>o8ª;UZ-6d7_8'_AyKBkx�y>Eӂ4Ui 1 z]A@Z Moz~]{*Ka:r摎, b^hxc+ ݥNC:`U L1r) ;{2̜?3zE9nB;;esy-w'i< MI^NN֤ʍЛh8Yq|9wvc 91Ȩ(` (OaʓbmDm>X1#Ka ~B>FJg!6l|;4 kaCԍx0r/iA';`CG G¡c#'30@ÌnmaF⺙igM1*[Ĥ=\E7i PK�����A�m��U��=���org/gradle/cli/CommandLineParser$OptionStringComparator.classTOAfeZ`XR@)!&&M1m(2dKvN<؋H41o5vޏ{߼y_} u [b"obE<6I&MmX`TC0RS|jH8p3ZviG*`HsB_<=!Yu]Wp5o _:Ү;&lS ݜ`#kT؎pvYEJ}j{y~H8r^Oj}W*.7 A3$TaK}OZr=6_ϔtO%DCIb}9VD n @T>]q& Ry3l,0&h.6£p2=5=L\7al42/\ c)HOv�qt#F~`xFl{WkȞ0V7ڟ`b =о*<M Q 1d8|%>Uuv imS!Udpe=ҌvoNB E.0:Rڞ&$ɻc7.\1fi?>G (`iH mPD;PK�����A�-h����2���org/gradle/cli/CommandLineParser$ParserState.classSoPN) sT4706|3M$m f{@ú[s-?&>GmdiҞ|~p�F62΢jv,-4WׄTqB0;%v=^(>{J` <mOft慄ܱPzؙ :C[@CutXȡUۄX|lS^KB^<9j7N Y/ I 5,g+BK(& ާs)l a Ⱦs T;9$P/:hmM{=n( YU6[M {yhE[=߀ "X~(l{hp/peY+X0x wrXF+lfȱOX&3A1*L@ڜehf)[01Fpb(C_G욱mf;S(,hIKƤ54=pe)>aŴ9,D!PK�����A�b'��n��?���org/gradle/cli/CommandLineParser$UnknownOptionParserState.classURA==$a2@(""UZ`$SdL__\H*~7= B%q}�q[A :0A0iDq5k rCw˺& ezabq [vJ%nWL[F ! ɕgY6rkk X>O 6E=}9A{gN)Ҧ>曖.^]S`{jHO~噎-pe}^^rJI=R7٠cSޒ{2iOcinی1^#r޲t1b7?4yvo=)L^)aFZ֜[c9*z'̼n$T BAa F&00� mGd6a4j3 C'/n{._VƵձ+1DX UR%H}4+.L_c=:"L32}F7>L`�g�%.dJ\)+QLgq.:K^dI׵{""¾j4e8� ț#u`@Ts䥓?|pP{fr mEhQr!"XOٯUVH/.\ 鋘6?�9R?SͩUÕ@FcH:U1-NcjXc4Uy<blfq=PK�����A�g��*��&���org/gradle/cli/CommandLineParser.classY xTյ^+8$�<"c�B2DT0xHNL�Q*Q`}WVEIQbm}>l[{o{zk[L&={wD4OE>-NS:`ҩYti]:^4i:TN<B:<<.33Z"i<د$3LI:]Ó}\,#ggL4h}|3t 0Kubq Ȋ T *;S>DOnvsS\stZ5^qNxNHkD KuPft0\s|\'V9OfhH9_>|!E_Gx5n*kDK|(j|NWu\ ⊀_&Zy6xXr:͢ Ѣŭ>nvLdh]ȌŬӘU`$muN3j#Q1M3c֊p ǂN+)^n;ӸKÑ]a{rYƸ4Aŧ`*p3c1gkmj {m[Q{4qi9?B3ˬH׸3e]fʐ<~tk1j4/߲v40Wn3wU]`*jYVqh^4*b+;cTu `[،wE噳!3Ve3X2mj-!9Jme0l.\f(Um$5/XQT(ڲ*5>0X1MB)[C,] K\%uK6 :ZѵBQ+#fh Ġ;S>aogD|IJJ"/?{)KlllQK@pk l�w7[ɀ旤Q&3 1exp 2^Њ$W,fHB9iȰTA/L gnqי1:m^W y^937=&3; Y9lays@ 㴒A^WV![Nlf4 wCVMAs6l|r%%,X2ѭъc9#_z`Υ1 N{ng߈d HŊ&�oɑ4Vck i cQn2yU@C8dq~r4#3rafmA𝖵Ӑ-tچ1zǓ:H$m}]$ǝB<8&Hj60S+e1+UxvO L͈V)2dcKw$ԧT*fsM;s&A@+srPђd@X%~R,>AŅoBBotEQ ĢRtmAӠ/mAw2O^æa8nˠüzP>�6xk&q;( winҠ1⡎A_e+ ?#<ZY} x �ˎ-o ?71U )4Y7x `mEo;�h%Kc ŕe.C�m5/2Kiy͕eH48MVˣHâY HB3u{>C<w ?d|/=?j|l6&"%~Q˂f Nu& ~gۭTp&KOS[Fn924~(6+g >OicOYS}q1y)V\, >!i85~Q迁"%Fڶ!]8S8j/{3fcf]܂" lT/i$jp8/6[Z#ikM 'GmX܌Ƌw3*fTt ݪ¯jdLɱ8u7Gq3fۉfX )TeF'|_7 QdBɦ{6u^U߽S-J ~S%\hC8KpiG OF1wxp:bp4}GR/ŭA qp:UY?z-g= hະtp?"@4t44Ə(J}T0qefl/*_8xnqV?F:fVGdQCYA'vtXA] X1jM4XX"JV8#$Ӷa fdBYs@ֳ X]-pޚ J%+2'|Zy3+vܚ]yZ?IKUfQ S4p[])  7DI+6~6v=N Uҹp#ڢA[ !q7cODh$J5煰PcC`Ff"mXs4|'6ۭneX# kA:ŋ>+ Az /)2{ <dMÌOkfP;#\2fVZwE}Q*Fn2ld/-t\7*błQ%u„֌pUOHzYKxF/GnEտqW-{>Q8+(ڎk1hY?y^=܋h)s> 6GN,\ӛz)5ݽ4�BH&Z t0f !Gӗ1gkrʏTW*UnU.=Bb w?7SASٳB^:FSQSY{iLCyw8C|Rz~oOCg' =4Ӥ&Y:ߋ:)=44tEP/6e(fB- T-:KѺ= R{)G0ԇ)TŋXlDMHTѻ 5* fB<Αvj9Kn-U?D+}ͨ3Y z܆|Gh)tIW71e|<$\*R�ߍ46Tx-]NZKWi+uS3]M-tYHVDY=K黴~(DR0NH;\JqG]H;y#m͝t%參�Gi/?h?O񫴟ߦO7V+9=HndWkIqzGl2A3Þ.6iS$C?jAnnWJӠӠևzk9z=;1Iw2{EnGN_Q='1FQ=6Ľ z1:s1"!l�lJJ.-\Y bq/Xb%MDU)BJyfxܪv/}[ł0Zre19& D[ /7w5 q`{EG$\hT@'5x^b@̥8$sӇ IyyCMm*[|V5z鼆r·.qW(k�8)A6 QH(b0Yl.uK@. R~}NG`v&LO<}Q8ZؾJya `}tKg" h=v^ $%ӊ<Oim!K.3dX֫J/PShԺ=ZHgR\x)Oy;^x&ᖧS!x%K@& G BH iFBJN s!>B|L'Y�|zw\].J?W:OKb8)`E�%ds!1z 5HCo#<ߧi�'1;Ԋ(K5dC ~ORiq >5$i\~~D?FY 4plFnJ3I9hm=�U[/0fx#=./ˉh]OWf{Ik-\&4=Qx&�9ZRV&"s$Яy@( oh9}Pn+G?8*uzö?#܅ϝ頯ֆ�G med''EѮ@{vG!'Q-�KAT8ے۳RH,C H6~7�~Z$g/uT*^@ /4?h3 COm>o("N{0w-r 3=81EIBhG1 hAv|v7*8 D O$Wr"'߇LXV<#v?UgWaKwX.ҝQr$a1C2ԭZ?vPX>Bn3TV;Ng(!j<$}E]bݍ;[7+.C{=hg^uuv?ϢEh?7s�σ&7no?�PK�����A�[xn����&���org/gradle/cli/ParsedCommandLine.classWWFDUǎ&U8e9$x&M)qIpZ k"Ogȉ[JKЅ}34 &?7oW;3Kָ1/39;|g??0O 3Qq)ģ"Nݩ&I\x<#YKF\e'Nj2/%Ўd|9nq|E_~Uh&N~5X~]�|Cq@ěxK|K<-ofMrdyֶtȝ:XZ2 ծZ_GeI5.ȔiZ(i%}qժh sqQ5 Sr,9UfVfSƝ�-KZJiWlKEsgJ6nI)5K=_FHLMxf5 MBzʜWKgTKko&BM !}:ciRvt*݌x&H;b/�&|f $t4Ұ\Qq8@ecKgEY+l_PKUƍ¤ t2VeWvjKj:p HWVZ9٠+tt˺,Z=6kћիn 7k'u;aJڼ÷8d%gG\M Dբz5膹UOd״I�BA<&\K:@CfiĬYch&i6 ]'ZY`�>p+o(8Y:nY XGQWwe|W}?%d735V?6FeTs q83x[S/W&SwE>F)L+�5VeF'1Gy݌`RͫW5OޞJo0LA E.ȸ:V61'!unQúwTK7rXr̐[1E0w$DϞCόm  kV%8py'ЃZT1жO~0͢˚QX|l%>ow l (t((TyN,x0^*QpKWj'DG \! ׇ6!$>!À40d)w[@bbu1NbkP$LװMUWlV Gn"{$ G^FHbp,>"]vqvοVw΄1(|v3A?w2{10]/~ ^&I<qHa!ыG1ǘ%8oxvǝ!AHE ΤOIJ|B|VN#'t΅sKs!&1*ĸk=8qf ;&NG\#=CBz|u6uʝok>C9$Egd$}uvNrg5&<gSF2#SӤXLBi ,A|;"AmȜ2BXiL8 G:m$�x0$aOI<M}S!ݵiɱ3hduοë~ :HnR$쥯X e3~S;i %^v^hC:7DZK8/ z_ڴaMQ@''3.`C.?)htI6D%EOF(Fe\ߐ2KR]AEA0�C4neF]ޙh}41o ZL*Ғ-"vv;Hr ZJًg Pmv2\)Q\zn1=!,y/(}){VLCC"C%sPK�����A��� ��,���org/gradle/cli/ParsedCommandLineOption.classS[OA-"\A(-ʊP1!iĤoC;Ylw- &JD}G.I|9|93|`+)dPPQL!B0冊I,-,&q[~器+䲤⾊ {lqWAW-+ b^ f~,Cz2$J^YA4_T[Wjls6)5fn2Gsy;+c&k_2U`V]Rm4=a[T.ipoSP0/DND=t=Z+jpS0? KOrbjrjd0ajWKp &N&7YָE4Wl/xY92,BS*jx<Iy2=4yt Zc<N5DS)hyec{רΎ+[+G9 tGi`] ɾ]k4}LқЃ.wN "N{j9)RFv@xx!_죯*!(#F9#<bõr-!~c$!?_;B}S']q4jTǩB՗)t,;arА&1ZxFiW$Guw+&9#P4f|tgCR(+dcٶ9 PK�����A�A5l|�� ��:���org/gradle/cli/ProjectPropertiesCommandLineConverter.classKO@D|?Pâu#Q+$C;1m  JW&.(1D,9vo/�[@yl汕G)v }FHWkwLS!]nY7ZK:̿cJDZRysV;H+-)nkS#cruLXgh|BjFYDΏ%L%񎅎*_?ֈ:("<ڄbJՍ ؊tf^*K ߵ XUVi01k p8wZ8T0g?PaΛm=C Ss | 1\Zq-}C_JEˉjE+ w'PK�����A��;|����9���org/gradle/cli/SystemPropertiesCommandLineConverter.classJ@ثmjE5BDąR/P~ӑ$&BJW 'iAY3͜l "lYlE <& d@HgL{:rRs:C*X4NĬQ ۴;hZ3a ѽG!]Gv7S"5eb o}ɸGtFMz9y~X{()spL`7e.KV, TXxɢfDT�EGPWJmh~49AjxѰ sh gԙn85].FԒs9Q΢*s/@Ug J*ce+s+1 $p6/t-,;h-.Z >kZPK�����A�W���������gradle-cli-classpath.properties+(JM.)**+M�PK����A����������������build-receipt.properties+K-*+MJ-53�PKJm������PK������A������������ �����������������META-INF/PK������A�8J?���T����������������)���META-INF/MANIFEST.MFPK������A����������������������������org/PK������A������������ ����������������org/gradle/PK������A����������������������������org/gradle/wrapper/PK������A�zZ�� ��-���������������org/gradle/wrapper/BootstrapMainStarter.classPK������A�hdf������#�������������V��org/gradle/wrapper/Download$1.classPK������A�o:s��@��4�������������:��org/gradle/wrapper/Download$ProxyAuthenticator.classPK������A���&��!������������� ��org/gradle/wrapper/Download.classPK������A�N/ݡ������1���������������org/gradle/wrapper/DownloadProgressListener.classPK������A�~0^�� ��3���������������org/gradle/wrapper/ExclusiveFileAccessManager.classPK������A�z\��Q��-���������������org/gradle/wrapper/GradleUserHomeLookup.classPK������A�j ��4��*�������������/"��org/gradle/wrapper/GradleWrapperMain.classPK������A�Xs������"�������������-��org/gradle/wrapper/IDownload.classPK������A���6��"�������������.��org/gradle/wrapper/Install$1.classPK������A� @$��)�� �������������5��org/gradle/wrapper/Install.classPK������A�y0V������������������}H��org/gradle/wrapper/Logger.classPK������A�j j��V��8�������������K��org/gradle/wrapper/PathAssembler$LocalDistribution.classPK������A�bi+����&�������������M��org/gradle/wrapper/PathAssembler.classPK������A�dzߒ��= ��0�������������FT��org/gradle/wrapper/SystemPropertiesHandler.classPK������A� ^F����-�������������&Y��org/gradle/wrapper/WrapperConfiguration.classPK������A�����(�������������\��org/gradle/wrapper/WrapperExecutor.classPK������A�_������#�������������e��gradle-wrapper-classpath.propertiesPK������A�������������������������ne��org/gradle/cli/PK������A�<��S��1�������������e��org/gradle/cli/AbstractCommandLineConverter.classPK������A�y0L�� ��;�������������(h��org/gradle/cli/AbstractPropertiesCommandLineConverter.classPK������A�# G��K��1�������������l��org/gradle/cli/CommandLineArgumentException.classPK������A�'H ��g��)�������������cn��org/gradle/cli/CommandLineConverter.classPK������A�"zZ�� ��&�������������o��org/gradle/cli/CommandLineOption.classPK������A�2_e������(�������������u��org/gradle/cli/CommandLineParser$1.classPK������A�F=�� ��;�������������v��org/gradle/cli/CommandLineParser$AfterFirstSubCommand.classPK������A�_>ң��)��3�������������z��org/gradle/cli/CommandLineParser$AfterOptions.classPK������A�C��| ��<�������������}��org/gradle/cli/CommandLineParser$BeforeFirstSubCommand.classPK������A�4:����F�������������S��org/gradle/cli/CommandLineParser$CaseInsensitiveStringComparator.classPK������A�?h����=���������������org/gradle/cli/CommandLineParser$KnownOptionParserState.classPK������A�RB ����<���������������org/gradle/cli/CommandLineParser$MissingOptionArgState.classPK������A�s����=���������������org/gradle/cli/CommandLineParser$OptionAwareParserState.classPK������A�$ER����7���������������org/gradle/cli/CommandLineParser$OptionComparator.classPK������A�l\ϧ����8���������������org/gradle/cli/CommandLineParser$OptionParserState.classPK������A�Gf����3���������������org/gradle/cli/CommandLineParser$OptionString.classPK������A�m��U��=���������������org/gradle/cli/CommandLineParser$OptionStringComparator.classPK������A�-h����2�������������М��org/gradle/cli/CommandLineParser$ParserState.classPK������A�b'��n��?�������������'��org/gradle/cli/CommandLineParser$UnknownOptionParserState.classPK������A�g��*��&�������������e��org/gradle/cli/CommandLineParser.classPK������A�[xn����&���������������org/gradle/cli/ParsedCommandLine.classPK������A��� ��,���������������org/gradle/cli/ParsedCommandLineOption.classPK������A�A5l|�� ��:���������������org/gradle/cli/ProjectPropertiesCommandLineConverter.classPK������A��;|����9���������������org/gradle/cli/SystemPropertiesCommandLineConverter.classPK������A�W���������������������gradle-cli-classpath.propertiesPK�����A�Jm���������������������build-receipt.propertiesPK����2�2�_��?��������������������������������������������������������������������������������gradle-wrapper.properties���������������������������������������������������������������������������0000664�0000000�0000000�00000000310�13746324601�0036207�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_gradle_project/gradle/wrapper����������������������������������������������������������������������������distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-bin.zip ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_gradle_project/gradlew����������������0000775�0000000�0000000�00000012260�13746324601�0027700�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn () { echo "$*" } die () { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Escape application args save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } APP_ARGS=$(save "$@") # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then cd "$(dirname "$0")" fi exec "$JAVACMD" "$@" ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_gradle_project/gradlew.bat������������0000664�0000000�0000000�00000004324�13746324601�0030444�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������@if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_gradle_project/settings.gradle��������0000664�0000000�0000000�00000001122�13746324601�0031340�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * This settings file was generated by the Gradle 'init' task. * * The settings file is used to specify which projects to include in your build. * In a single project build this file can be empty or even removed. * * Detailed information about configuring a multi-project build in Gradle can be found * in the user guide at https://docs.gradle.org/4.1/userguide/multi_project_builds.html */ /* // To declare projects as part of a multi-project build use the 'include' method include 'shared' include 'api' include 'services:webservice' */ rootProject.name = 'simple_gradle_project' ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_gradle_project/src/�������������������0000775�0000000�0000000�00000000000�13746324601�0027113�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_gradle_project/src/main/��������������0000775�0000000�0000000�00000000000�13746324601�0030037�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_gradle_project/src/main/java/���������0000775�0000000�0000000�00000000000�13746324601�0030760�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_gradle_project/src/main/java/com/�����0000775�0000000�0000000�00000000000�13746324601�0031536�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_gradle_project/src/main/java/com/test/0000775�0000000�0000000�00000000000�13746324601�0032515�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������AbstractTestWidget.java�����������������������������������������������������������������������������0000664�0000000�0000000�00000000613�13746324601�0037050�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_gradle_project/src/main/java/com/test��������������������������������������������������������������������package com.test; public interface AbstractTestWidget { /** * Do the actually useful stuff. * * Eventually, you have to find the code which is useful, as opposed to just * boilerplate. */ public void doSomethingVaguelyUseful(); /** * Return runtime debugging info. * * Useful for finding the actual code which is useful. */ public String getWidgetInfo(); }; ���������������������������������������������������������������������������������������������������������������������TestFactory.java������������������������������������������������������������������������������������0000664�0000000�0000000�00000000647�13746324601�0035557�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_gradle_project/src/main/java/com/test��������������������������������������������������������������������package com.test; /** * @title TestFactory * * TestFactory is a pointless thing that OO programmers think is necessary * because they read about it in a book. * * All it does is instantiate the (one and only) concrete AbstractTestWidget * implementation */ public class TestFactory { public AbstractTestWidget getWidget( String info ) { AbstractTestWidget w = new TestWidgetImpl( info ); return w; } } �����������������������������������������������������������������������������������������TestLauncher.java�����������������������������������������������������������������������������������0000664�0000000�0000000�00000000604�13746324601�0035702�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_gradle_project/src/main/java/com/test��������������������������������������������������������������������package com.test; class TestLauncher { private TestFactory factory = new TestFactory(); private void Run() { AbstractTestWidget w = factory.getWidget( "Test" ); w.doSomethingVaguelyUseful(); System.out.println( "Did something useful: " + w.getWidgetInfo() ); } public static void main( String[] args ) { TestLauncher l = new TestLauncher(); l.Run(); } } ����������������������������������������������������������������������������������������������������������������������������TestWidgetImpl.java���������������������������������������������������������������������������������0000664�0000000�0000000�00000000727�13746324601�0036214�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_gradle_project/src/main/java/com/test��������������������������������������������������������������������package com.test; /** * This is the actual code that matters. * * This concrete implementation is the equivalent of the main function in other * languages */ class TestWidgetImpl implements AbstractTestWidget { private String info; TestWidgetImpl( String info ) { this.info = info; } @Override public void doSomethingVaguelyUseful() { System.out.println( this. ); } @Override public String getWidgetInfo() { return this.info; } } �����������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_gradle_project/src/test/��������������0000775�0000000�0000000�00000000000�13746324601�0030072�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_gradle_project/src/test/java/���������0000775�0000000�0000000�00000000000�13746324601�0031013�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_gradle_project/src/test/java/com/�����0000775�0000000�0000000�00000000000�13746324601�0031571�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_gradle_project/src/test/java/com/test/0000775�0000000�0000000�00000000000�13746324601�0032550�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������AppTest.java����������������������������������������������������������������������������������������0000664�0000000�0000000�00000001173�13746324601�0034716�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_gradle_project/src/test/java/com/test��������������������������������������������������������������������package com.test; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; /** * Unit test for simple App. */ public class AppTest extends TestCase { /** * Create the test case * * @param testName name of the test case */ public AppTest( String testName ) { super( testName ); } /** * @return the suite of tests being tested */ public static Test suite() { return new TestSuite( AppTest.class ); } /** * Rigorous Test :-) */ public void testApp() { assertTrue( true ); } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_maven_project/������������������������0000775�0000000�0000000�00000000000�13746324601�0026174�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_maven_project/.gitignore��������������0000664�0000000�0000000�00000000046�13746324601�0030164�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������target/ .classpath .project .settings ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_maven_project/pom.xml�����������������0000664�0000000�0000000�00000001225�13746324601�0027511�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.test</groupId> <artifactId>simple_maven_project</artifactId> <packaging>pom</packaging> <version>1.0-SNAPSHOT</version> <name>simple_maven_project</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> </project> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_maven_project/src/��������������������0000775�0000000�0000000�00000000000�13746324601�0026763�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_maven_project/src/main/���������������0000775�0000000�0000000�00000000000�13746324601�0027707�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_maven_project/src/main/java/����������0000775�0000000�0000000�00000000000�13746324601�0030630�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_maven_project/src/main/java/com/������0000775�0000000�0000000�00000000000�13746324601�0031406�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_maven_project/src/main/java/com/test/�0000775�0000000�0000000�00000000000�13746324601�0032365�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������AbstractTestWidget.java�����������������������������������������������������������������������������0000664�0000000�0000000�00000000613�13746324601�0036720�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_maven_project/src/main/java/com/test���������������������������������������������������������������������package com.test; public interface AbstractTestWidget { /** * Do the actually useful stuff. * * Eventually, you have to find the code which is useful, as opposed to just * boilerplate. */ public void doSomethingVaguelyUseful(); /** * Return runtime debugging info. * * Useful for finding the actual code which is useful. */ public String getWidgetInfo(); }; ���������������������������������������������������������������������������������������������������������������������TestFactory.java������������������������������������������������������������������������������������0000664�0000000�0000000�00000000647�13746324601�0035427�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_maven_project/src/main/java/com/test���������������������������������������������������������������������package com.test; /** * @title TestFactory * * TestFactory is a pointless thing that OO programmers think is necessary * because they read about it in a book. * * All it does is instantiate the (one and only) concrete AbstractTestWidget * implementation */ public class TestFactory { public AbstractTestWidget getWidget( String info ) { AbstractTestWidget w = new TestWidgetImpl( info ); return w; } } �����������������������������������������������������������������������������������������TestLauncher.java�����������������������������������������������������������������������������������0000664�0000000�0000000�00000000604�13746324601�0035552�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_maven_project/src/main/java/com/test���������������������������������������������������������������������package com.test; class TestLauncher { private TestFactory factory = new TestFactory(); private void Run() { AbstractTestWidget w = factory.getWidget( "Test" ); w.doSomethingVaguelyUseful(); System.out.println( "Did something useful: " + w.getWidgetInfo() ); } public static void main( String[] args ) { TestLauncher l = new TestLauncher(); l.Run(); } } ����������������������������������������������������������������������������������������������������������������������������TestWidgetImpl.java���������������������������������������������������������������������������������0000664�0000000�0000000�00000000726�13746324601�0036063�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_maven_project/src/main/java/com/test���������������������������������������������������������������������package com.test; /** * This is the actual code that matters. * * This concrete implementation is the equivalent of the main function in other * languages */ class TestWidgetImpl implements AbstractTestWidget { private String info; TestWidgetImpl( String info ) { this.info = info; } @Override public void doSomethingVaguelyUseful() { System.out.println( "42" ); } @Override public String getWidgetInfo() { return this.info; } } ������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_maven_project/src/test/���������������0000775�0000000�0000000�00000000000�13746324601�0027742�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_maven_project/src/test/java/����������0000775�0000000�0000000�00000000000�13746324601�0030663�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_maven_project/src/test/java/com/������0000775�0000000�0000000�00000000000�13746324601�0031441�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_maven_project/src/test/java/com/test/�0000775�0000000�0000000�00000000000�13746324601�0032420�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������AppTest.java����������������������������������������������������������������������������������������0000664�0000000�0000000�00000001173�13746324601�0034566�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/java/testdata/simple_maven_project/src/test/java/com/test���������������������������������������������������������������������package com.test; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; /** * Unit test for simple App. */ public class AppTest extends TestCase { /** * Create the test case * * @param testName name of the test case */ public AppTest( String testName ) { super( testName ); } /** * @return the suite of tests being tested */ public static Test suite() { return new TestSuite( AppTest.class ); } /** * Rigorous Test :-) */ public void testApp() { assertTrue( true ); } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascript/������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0021403�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascript/__init__.py�������������������������������������0000664�0000000�0000000�00000001623�13746324601�0023516�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import os from ycmd.tests.javascript.conftest import * # noqa def PathToTestFile( *args ): dir_of_current_script = os.path.dirname( os.path.abspath( __file__ ) ) return os.path.join( dir_of_current_script, 'testdata', *args ) �������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascript/conftest.py�������������������������������������0000664�0000000�0000000�00000007056�13746324601�0023612�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import pytest from unittest.mock import patch from ycmd.tests.test_utils import ( ClearCompletionsCache, IgnoreExtraConfOutsideTestsFolder, IsolatedApp, SetUpApp, StopCompleterServer, WaitUntilCompleterServerReady ) shared_app = None @pytest.fixture( scope='module', autouse=True ) def set_up_shared_app(): global shared_app with patch( 'ycmd.completers.javascript.hook.' 'ShouldEnableTernCompleter', return_value = False ): shared_app = SetUpApp() WaitUntilCompleterServerReady( shared_app, 'javascript' ) yield StopCompleterServer( shared_app, 'javascript' ) @pytest.fixture def app( request ): which = request.param[ 0 ] print( which ) assert which == 'isolated' or which == 'shared' if which == 'isolated': with patch( 'ycmd.completers.javascript.hook.' 'ShouldEnableTernCompleter', return_value = False ): with IsolatedApp( request.param[ 1 ] ) as app: yield app StopCompleterServer( app, 'javascript' ) else: global shared_app ClearCompletionsCache() with IgnoreExtraConfOutsideTestsFolder(): yield shared_app """Defines a decorator to be attached to tests of this package. This decorator passes the shared ycmd application as a parameter.""" SharedYcmd = pytest.mark.parametrize( # Name of the fixture/function argument 'app', # Fixture parameters, passed to app() as request.param [ ( 'shared', ) ], # Non-empty ids makes fixture parameters visible in pytest verbose output ids = [ '' ], # Execute the fixture, instead of passing parameters directly to the # function argument indirect = True ) def IsolatedYcmd( custom_options = {} ): """Defines a decorator to be attached to tests of this package. This decorator passes a unique ycmd application as a parameter. It should be used on tests that change the server state in a irreversible way (ex: a semantic subserver is stopped or restarted) or expect a clean state (ex: no semantic subserver started, no .ycm_extra_conf.py loaded, etc). Use the optional parameter |custom_options| to give additional options and/or override the default ones. Example usage: from ycmd.tests.python import IsolatedYcmd @IsolatedYcmd( { 'python_binary_path': '/some/path' } ) def CustomPythonBinaryPath_test( app ): ... """ return pytest.mark.parametrize( # Name of the fixture/function argument 'app', # Fixture parameters, passed to app() as request.param [ ( 'isolated', custom_options ) ], # Non-empty ids makes fixture parameters visible in pytest verbose output ids = [ '' ], # Execute the fixture, instead of passing parameters directly to the # function argument indirect = True ) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascript/debug_info_test.py������������������������������0000664�0000000�0000000�00000004450�13746324601�0025120�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from unittest.mock import patch from hamcrest import ( any_of, assert_that, contains_exactly, has_entries, has_entry, instance_of, none ) from ycmd.tests.javascript import IsolatedYcmd, SharedYcmd from ycmd.tests.test_utils import BuildRequest @SharedYcmd def DebugInfo_TypeScriptCompleter_test( app ): request_data = BuildRequest( filetype = 'javascript' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'TypeScript', 'servers': contains_exactly( has_entries( { 'name': 'TSServer', 'is_running': True, 'executable': instance_of( str ), 'pid': instance_of( int ), 'address': None, 'port': None, 'logfiles': contains_exactly( instance_of( str ) ), 'extras': contains_exactly( has_entries( { 'key': 'version', 'value': any_of( None, instance_of( str ) ) } ) ) } ) ) } ) ) ) @patch( 'ycmd.completers.javascript.hook.' 'ShouldEnableTypeScriptCompleter', return_value = False ) @patch( 'ycmd.completers.javascript.hook.' 'ShouldEnableTernCompleter', return_value = False ) @IsolatedYcmd def DebugInfo_NoCompleter_test( app, *args ): request_data = BuildRequest( filetype = 'javascript' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', none() ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascript/diagnostics_test.py�����������������������������0000664�0000000�0000000�00000010532�13746324601�0025324�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from __future__ import absolute_import from __future__ import unicode_literals from __future__ import print_function from __future__ import division from builtins import * # noqa from hamcrest import ( assert_that, contains_exactly, contains_inanyorder, has_entries, has_entry ) from ycmd.tests.javascript import PathToTestFile, SharedYcmd from ycmd.tests.test_utils import BuildRequest, LocationMatcher, RangeMatcher from ycmd.utils import ReadFile @SharedYcmd def Diagnostics_FileReadyToParse_test( app ): filepath = PathToTestFile( 'test.js' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'javascript', contents = contents, event_name = 'BufferVisit' ) app.post_json( '/event_notification', event_data ) event_data = BuildRequest( filepath = filepath, filetype = 'javascript', contents = contents, event_name = 'FileReadyToParse' ) assert_that( app.post_json( '/event_notification', event_data ).json, contains_inanyorder( has_entries( { 'kind': 'ERROR', 'text': "Property 'm' does not exist on type 'Foo'.", 'location': LocationMatcher( filepath, 14, 5 ), 'location_extent': RangeMatcher( filepath, ( 14, 5 ), ( 14, 6 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 14, 5 ), ( 14, 6 ) ) ), 'fixit_available': False } ), has_entries( { 'kind': 'ERROR', 'text': "Property 'nonExistingMethod' does not exist on type 'Bar'.", 'location': LocationMatcher( filepath, 32, 5 ), 'location_extent': RangeMatcher( filepath, ( 32, 5 ), ( 32, 22 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 32, 5 ), ( 32, 22 ) ) ), 'fixit_available': True } ), has_entries( { 'kind': 'ERROR', 'text': 'Expected 1-2 arguments, but got 0.', 'location': LocationMatcher( filepath, 34, 5 ), 'location_extent': RangeMatcher( filepath, ( 34, 5 ), ( 34, 12 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 34, 5 ), ( 34, 12 ) ) ), 'fixit_available': False } ), has_entries( { 'kind': 'ERROR', 'text': "Cannot find name 'Bår'.", 'location': LocationMatcher( filepath, 36, 1 ), 'location_extent': RangeMatcher( filepath, ( 36, 1 ), ( 36, 5 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 36, 1 ), ( 36, 5 ) ) ), 'fixit_available': True } ), ) ) @SharedYcmd def Diagnostics_DetailedDiagnostics_test( app ): filepath = PathToTestFile( 'test.js' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'javascript', contents = contents, event_name = 'BufferVisit' ) app.post_json( '/event_notification', event_data ) diagnostic_data = BuildRequest( filepath = filepath, filetype = 'javascript', contents = contents, line_num = 32, column_num = 13 ) assert_that( app.post_json( '/detailed_diagnostic', diagnostic_data ).json, has_entry( 'message', "Property 'nonExistingMethod' does not exist on type 'Bar'." ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ����������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascript/get_completions_test.py�������������������������0000664�0000000�0000000�00000013473�13746324601�0026217�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_exactly, contains_inanyorder, equal_to, has_entries, has_item, matches_regexp ) import pprint import requests from ycmd.tests.javascript import IsolatedYcmd, PathToTestFile, SharedYcmd from ycmd.tests.test_utils import ( BuildRequest, ChunkMatcher, CompletionEntryMatcher, LocationMatcher ) from ycmd.utils import ReadFile def RunTest( app, test ): contents = ReadFile( test[ 'request' ][ 'filepath' ] ) def CombineRequest( request, data ): kw = request request.update( data ) return BuildRequest( **kw ) app.post_json( '/event_notification', CombineRequest( test[ 'request' ], { 'contents': contents, 'filetype': 'javascript', 'event_name': 'BufferVisit' } ) ) response = app.post_json( '/completions', CombineRequest( test[ 'request' ], { 'contents': contents, 'filetype': 'javascript', 'force_semantic': True } ) ) print( f'completer response: { pprint.pformat( response.json ) }' ) assert_that( response.status_code, equal_to( test[ 'expect' ][ 'response' ] ) ) assert_that( response.json, test[ 'expect' ][ 'data' ] ) @SharedYcmd def GetCompletions_Basic_test( app ): RunTest( app, { 'description': 'Extra and detailed info when completions are methods', 'request': { 'line_num': 14, 'column_num': 6, 'filepath': PathToTestFile( 'test.js' ) }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_inanyorder( CompletionEntryMatcher( 'methodA', '(method) Foo.methodA(): void', extra_params = { 'kind': 'method', 'detailed_info': '(method) Foo.methodA(): void\n\n' 'Unicode string: 说话' } ), CompletionEntryMatcher( 'methodB', '(method) Foo.methodB(): void', extra_params = { 'kind': 'method', 'detailed_info': '(method) Foo.methodB(): void' } ), CompletionEntryMatcher( 'methodC', '(method) Foo.methodC(foo: any, bar: any): void', extra_params = { 'kind': 'method', 'detailed_info': '(method) Foo.methodC(foo: any, bar: any): void' } ) ) } ) } } ) @SharedYcmd def GetCompletions_Keyword_test( app ): RunTest( app, { 'description': 'No extra and detailed info when completion is a keyword', 'request': { 'line_num': 1, 'column_num': 5, 'filepath': PathToTestFile( 'test.js' ), }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': has_item( { 'insertion_text': 'class', 'kind': 'keyword', 'extra_data': {} } ) } ) } } ) @SharedYcmd def GetCompletions_AutoImport_test( app ): filepath = PathToTestFile( 'test.js' ) RunTest( app, { 'description': 'Symbol from external module can be completed and ' 'its completion contains fixits to automatically import it', 'request': { 'line_num': 36, 'column_num': 5, 'filepath': filepath, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': has_item( has_entries( { 'insertion_text': 'Bår', 'extra_menu_info': 'class Bår', 'detailed_info': 'class Bår', 'kind': 'class', 'extra_data': has_entries( { 'fixits': contains_inanyorder( has_entries( { 'text': 'Import \'Bår\' from module "./unicode"', 'chunks': contains_exactly( ChunkMatcher( matches_regexp( '^import { Bår } from "./unicode";\r?\n' '\r?\n' ), LocationMatcher( filepath, 1, 1 ), LocationMatcher( filepath, 1, 1 ) ) ), 'location': LocationMatcher( filepath, 36, 5 ) } ) ) } ) } ) ) } ) } } ) @IsolatedYcmd() def GetCompletions_IgnoreIdentifiers_test( app ): RunTest( app, { 'description': 'Identifier "test" is not returned as a suggestion', 'request': { 'line_num': 5, 'column_num': 6, 'filepath': PathToTestFile( 'identifier', 'test.js' ), }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_exactly( CompletionEntryMatcher( 'foo', '(property) foo: string', extra_params = { 'kind': 'property', 'detailed_info': '(property) foo: string' } ) ) } ) } } ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascript/subcommands_test.py�����������������������������0000664�0000000�0000000�00000063705�13746324601�0025342�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_exactly, contains_inanyorder, equal_to, has_entries, matches_regexp ) import requests import pprint import pytest from ycmd.tests.javascript import IsolatedYcmd, PathToTestFile, SharedYcmd from ycmd.tests.test_utils import ( BuildRequest, ChunkMatcher, CombineRequest, ErrorMatcher, LocationMatcher, MessageMatcher, WaitUntilCompleterServerReady ) from ycmd.utils import ReadFile def RunTest( app, test ): contents = ReadFile( test[ 'request' ][ 'filepath' ] ) app.post_json( '/event_notification', CombineRequest( test[ 'request' ], { 'contents': contents, 'filetype': 'javascript', 'event_name': 'BufferVisit' } ) ) app.post_json( '/event_notification', CombineRequest( test[ 'request' ], { 'contents': contents, 'filetype': 'javascript', 'event_name': 'FileReadyToParse' } ) ) # We ignore errors here and check the response code ourself. # This is to allow testing of requests returning errors. response = app.post_json( '/run_completer_command', CombineRequest( test[ 'request' ], { 'contents': contents, 'filetype': 'javascript', 'command_arguments': ( [ test[ 'request' ][ 'command' ] ] + test[ 'request' ].get( 'arguments', [] ) ) } ), expect_errors = True ) print( f'completer response: { pprint.pformat( response.json ) }' ) assert_that( response.status_code, equal_to( test[ 'expect' ][ 'response' ] ) ) assert_that( response.json, test[ 'expect' ][ 'data' ] ) @IsolatedYcmd() def Subcommands_DefinedSubcommands_test( app ): subcommands_data = BuildRequest( completer_target = 'javascript' ) assert_that( app.post_json( '/defined_subcommands', subcommands_data ).json, contains_inanyorder( 'Format', 'GoTo', 'GoToDeclaration', 'GoToDefinition', 'GoToImplementation', 'GoToType', 'GetDoc', 'GetType', 'GoToReferences', 'GoToSymbol', 'FixIt', 'OrganizeImports', 'RefactorRename', 'RestartServer' ) ) @SharedYcmd def Subcommands_Format_WholeFile_Spaces_test( app ): filepath = PathToTestFile( 'test.js' ) RunTest( app, { 'description': 'Formatting is applied on the whole file ' 'with tabs composed of 4 spaces', 'request': { 'command': 'Format', 'filepath': filepath, 'options': { 'tab_size': 4, 'insert_spaces': True } }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( ' ', LocationMatcher( filepath, 2, 1 ), LocationMatcher( filepath, 2, 3 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 3, 1 ), LocationMatcher( filepath, 3, 3 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 3, 14 ), LocationMatcher( filepath, 3, 14 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 4, 1 ), LocationMatcher( filepath, 4, 3 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 4, 14 ), LocationMatcher( filepath, 4, 14 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 5, 1 ), LocationMatcher( filepath, 5, 3 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 6, 1 ), LocationMatcher( filepath, 6, 5 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 7, 1 ), LocationMatcher( filepath, 7, 5 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 8, 1 ), LocationMatcher( filepath, 8, 3 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 8, 6 ), LocationMatcher( filepath, 8, 6 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 18, 1 ), LocationMatcher( filepath, 18, 2 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 19, 1 ), LocationMatcher( filepath, 19, 2 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 20, 1 ), LocationMatcher( filepath, 20, 2 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 21, 1 ), LocationMatcher( filepath, 21, 2 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 24, 1 ), LocationMatcher( filepath, 24, 3 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 25, 1 ), LocationMatcher( filepath, 25, 4 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 26, 1 ), LocationMatcher( filepath, 26, 4 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 27, 1 ), LocationMatcher( filepath, 27, 3 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 27, 17 ), LocationMatcher( filepath, 27, 17 ) ), ) } ) ) } ) } } ) @SharedYcmd def Subcommands_Format_WholeFile_Tabs_test( app ): filepath = PathToTestFile( 'test.js' ) RunTest( app, { 'description': 'Formatting is applied on the whole file ' 'with tabs composed of 2 spaces', 'request': { 'command': 'Format', 'filepath': filepath, 'options': { 'tab_size': 4, 'insert_spaces': False } }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( '\t', LocationMatcher( filepath, 2, 1 ), LocationMatcher( filepath, 2, 3 ) ), ChunkMatcher( '\t', LocationMatcher( filepath, 3, 1 ), LocationMatcher( filepath, 3, 3 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 3, 14 ), LocationMatcher( filepath, 3, 14 ) ), ChunkMatcher( '\t', LocationMatcher( filepath, 4, 1 ), LocationMatcher( filepath, 4, 3 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 4, 14 ), LocationMatcher( filepath, 4, 14 ) ), ChunkMatcher( '\t', LocationMatcher( filepath, 5, 1 ), LocationMatcher( filepath, 5, 3 ) ), ChunkMatcher( '\t\t', LocationMatcher( filepath, 6, 1 ), LocationMatcher( filepath, 6, 5 ) ), ChunkMatcher( '\t\t', LocationMatcher( filepath, 7, 1 ), LocationMatcher( filepath, 7, 5 ) ), ChunkMatcher( '\t', LocationMatcher( filepath, 8, 1 ), LocationMatcher( filepath, 8, 3 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 8, 6 ), LocationMatcher( filepath, 8, 6 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 18, 1 ), LocationMatcher( filepath, 18, 2 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 19, 1 ), LocationMatcher( filepath, 19, 2 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 20, 1 ), LocationMatcher( filepath, 20, 2 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 21, 1 ), LocationMatcher( filepath, 21, 2 ) ), ChunkMatcher( '\t', LocationMatcher( filepath, 24, 1 ), LocationMatcher( filepath, 24, 3 ) ), ChunkMatcher( '\t ', LocationMatcher( filepath, 25, 1 ), LocationMatcher( filepath, 25, 4 ) ), ChunkMatcher( '\t ', LocationMatcher( filepath, 26, 1 ), LocationMatcher( filepath, 26, 4 ) ), ChunkMatcher( '\t', LocationMatcher( filepath, 27, 1 ), LocationMatcher( filepath, 27, 3 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 27, 17 ), LocationMatcher( filepath, 27, 17 ) ), ) } ) ) } ) } } ) @SharedYcmd def Subcommands_Format_Range_Spaces_test( app ): filepath = PathToTestFile( 'test.js' ) RunTest( app, { 'description': 'Formatting is applied on some part of the file ' 'with tabs composed of 4 spaces by default', 'request': { 'command': 'Format', 'filepath': filepath, 'range': { 'start': { 'line_num': 5, 'column_num': 3, }, 'end': { 'line_num': 8, 'column_num': 6 } }, 'options': { 'tab_size': 4, 'insert_spaces': True } }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( ' ', LocationMatcher( filepath, 5, 1 ), LocationMatcher( filepath, 5, 3 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 6, 1 ), LocationMatcher( filepath, 6, 5 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 7, 1 ), LocationMatcher( filepath, 7, 5 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 8, 1 ), LocationMatcher( filepath, 8, 3 ) ), ) } ) ) } ) } } ) @IsolatedYcmd() def Subcommands_Format_Range_Tabs_test( app ): filepath = PathToTestFile( 'test.js' ) RunTest( app, { 'description': 'Formatting is applied on some part of the file ' 'with tabs instead of spaces', 'request': { 'command': 'Format', 'filepath': filepath, 'range': { 'start': { 'line_num': 5, 'column_num': 3, }, 'end': { 'line_num': 8, 'column_num': 6 } }, 'options': { 'tab_size': 4, 'insert_spaces': False } }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( '\t', LocationMatcher( filepath, 5, 1 ), LocationMatcher( filepath, 5, 3 ) ), ChunkMatcher( '\t\t', LocationMatcher( filepath, 6, 1 ), LocationMatcher( filepath, 6, 5 ) ), ChunkMatcher( '\t\t', LocationMatcher( filepath, 7, 1 ), LocationMatcher( filepath, 7, 5 ) ), ChunkMatcher( '\t', LocationMatcher( filepath, 8, 1 ), LocationMatcher( filepath, 8, 3 ) ), ) } ) ) } ) } } ) @IsolatedYcmd( { 'global_ycm_extra_conf': PathToTestFile( 'extra_confs', 'brace_on_same_line.py' ) } ) def Subcommands_Format_ExtraConf_BraceOnSameLine_test( app ): WaitUntilCompleterServerReady( app, 'javascript' ) filepath = PathToTestFile( 'extra_confs', 'func.js' ) RunTest( app, { 'description': 'Format with an extra conf, braces on new line', 'request': { 'command': 'Format', 'filepath': filepath, 'options': { 'tab_size': 4, 'insert_spaces': True } }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( ' ', LocationMatcher( filepath, 2, 1 ), LocationMatcher( filepath, 2, 1 ) ), ) } ) ) } ) } } ) @IsolatedYcmd( { 'global_ycm_extra_conf': PathToTestFile( 'extra_confs', 'brace_on_new_line.py' ) } ) def Subcommands_Format_ExtraConf_BraceOnNewLine_test( app ): WaitUntilCompleterServerReady( app, 'javascript' ) filepath = PathToTestFile( 'extra_confs', 'func.js' ) RunTest( app, { 'description': 'Format with an extra conf, braces on new line', 'request': { 'command': 'Format', 'filepath': filepath, 'options': { 'tab_size': 4, 'insert_spaces': True } }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( matches_regexp( '\r?\n' ), LocationMatcher( filepath, 1, 19 ), LocationMatcher( filepath, 1, 20 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 2, 1 ), LocationMatcher( filepath, 2, 1 ) ), ) } ) ) } ) } } ) @SharedYcmd def Subcommands_GetType_test( app ): RunTest( app, { 'description': 'GetType works', 'request': { 'command': 'GetType', 'line_num': 14, 'column_num': 1, 'filepath': PathToTestFile( 'test.js' ), }, 'expect': { 'response': requests.codes.ok, 'data': MessageMatcher( 'var foo: Foo' ) } } ) @SharedYcmd def Subcommands_GetDoc_Method_test( app ): RunTest( app, { 'description': 'GetDoc on a method returns its docstring', 'request': { 'command': 'GetDoc', 'line_num': 31, 'column_num': 5, 'filepath': PathToTestFile( 'test.js' ), }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'detailed_info': '(method) Bar.testMethod(): void\n\n' 'Method documentation' } ) } } ) @SharedYcmd def Subcommands_GetDoc_Class_test( app ): RunTest( app, { 'description': 'GetDoc on a class returns its docstring', 'request': { 'command': 'GetDoc', 'line_num': 34, 'column_num': 3, 'filepath': PathToTestFile( 'test.js' ), }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'detailed_info': 'class Bar\n\n' 'Class documentation\n\n' 'Multi-line' } ) } } ) @SharedYcmd def Subcommands_GoToReferences_test( app ): RunTest( app, { 'description': 'GoToReferences works', 'request': { 'command': 'GoToReferences', 'line_num': 30, 'column_num': 5, 'filepath': PathToTestFile( 'test.js' ), }, 'expect': { 'response': requests.codes.ok, 'data': contains_inanyorder( has_entries( { 'description': 'var bar = new Bar();', 'line_num' : 30, 'column_num' : 5, 'filepath' : PathToTestFile( 'test.js' ) } ), has_entries( { 'description': 'bar.testMethod();', 'line_num' : 31, 'column_num' : 1, 'filepath' : PathToTestFile( 'test.js' ) } ), has_entries( { 'description': 'bar.nonExistingMethod();', 'line_num' : 32, 'column_num' : 1, 'filepath' : PathToTestFile( 'test.js' ) } ), has_entries( { 'description': 'var bar = new Bar();', 'line_num' : 1, 'column_num' : 5, 'filepath' : PathToTestFile( 'file3.js' ) } ), has_entries( { 'description': 'bar.testMethod();', 'line_num' : 2, 'column_num' : 1, 'filepath' : PathToTestFile( 'file3.js' ) } ) ) } } ) @pytest.mark.parametrize( "req,rep", [ ( ( 'file3.js', 1, 1, 'testMethod' ), ( 'test.js', 27, 3, 'testMethod' ) ), ( ( 'file3.js', 1, 1, 'BAR' ), [ ( 'file3.js', 1, 5, 'bar' ), ( 'test.js', 30, 5, 'bar' ), ( 'test.js', 22, 1, 'Bar' ) ] ), ( ( 'file3.js', 1, 1, 'nothinghere' ), 'Symbol not found' ) ] ) @SharedYcmd def Subcommands_GoToSymbol_test( app, req, rep ): if isinstance( rep, tuple ): expect = { 'response': requests.codes.ok, 'data': LocationMatcher( PathToTestFile( rep[ 0 ] ), *rep[ 1: ] ) } elif isinstance( rep, list ): expect = { 'response': requests.codes.ok, 'data': contains_inanyorder( *[ LocationMatcher( PathToTestFile( r[ 0 ] ), *r[ 1: ] ) for r in rep ] ) } else: expect = { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( RuntimeError, rep ) } RunTest( app, { 'request': { 'command': 'GoToSymbol', 'arguments': [ req[ 3 ] ], 'line_num': req[ 1 ], 'column_num': req[ 2 ], 'filepath': PathToTestFile( req[ 0 ] ), }, 'expect': expect } ) def Subcommands_GoTo( app, goto_command ): RunTest( app, { 'description': goto_command + ' works within file', 'request': { 'command': goto_command, 'line_num': 31, 'column_num': 13, 'filepath': PathToTestFile( 'test.js' ), }, 'expect': { 'response': requests.codes.ok, 'data': LocationMatcher( PathToTestFile( 'test.js' ), 27, 3 ) } } ) @pytest.mark.parametrize( 'command', [ 'GoTo', 'GoToDefinition', 'GoToDeclaration' ] ) @SharedYcmd def Subcommands_GoTo_test( app, command ): Subcommands_GoTo( app, command ) @SharedYcmd def Subcommands_GoToType_test( app ): RunTest( app, { 'description': 'GoToType works', 'request': { 'command': 'GoToType', 'line_num': 11, 'column_num': 6, 'filepath': PathToTestFile( 'test.js' ), }, 'expect': { 'response': requests.codes.ok, 'data': LocationMatcher( PathToTestFile( 'test.js' ), 1, 7 ) } } ) @SharedYcmd def Subcommands_FixIt_test( app ): filepath = PathToTestFile( 'test.js' ) RunTest( app, { 'description': 'FixIt works on a non-existing method', 'request': { 'command': 'FixIt', 'line_num': 32, 'column_num': 19, 'filepath': filepath, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_inanyorder( has_entries( { 'text': "Declare method 'nonExistingMethod'", 'chunks': contains_exactly( ChunkMatcher( matches_regexp( '^\r?\n' ' nonExistingMethod\\(\\) {\r?\n' ' throw new Error\\("Method not implemented."\\);\r?\n' ' }$', ), LocationMatcher( filepath, 22, 12 ), LocationMatcher( filepath, 22, 12 ) ) ), 'location': LocationMatcher( filepath, 32, 19 ) } ) ) } ) } } ) @SharedYcmd def Subcommands_OrganizeImports_test( app ): filepath = PathToTestFile( 'imports.js' ) RunTest( app, { 'description': 'OrganizeImports removes unused imports, ' 'coalesces imports from the same module, and sorts them', 'request': { 'command': 'OrganizeImports', 'filepath': filepath, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( matches_regexp( 'import \\* as lib from "library";\r?\n' 'import func, { func1, func2 } from "library";\r?\n' ), LocationMatcher( filepath, 1, 1 ), LocationMatcher( filepath, 2, 1 ) ), ChunkMatcher( '', LocationMatcher( filepath, 5, 1 ), LocationMatcher( filepath, 6, 1 ) ), ChunkMatcher( '', LocationMatcher( filepath, 9, 1 ), LocationMatcher( filepath, 10, 1 ) ), ) } ) ) } ) } } ) @SharedYcmd def Subcommands_RefactorRename_Missing_test( app ): RunTest( app, { 'description': 'RefactorRename requires a parameter', 'request': { 'command': 'RefactorRename', 'line_num': 27, 'column_num': 8, 'filepath': PathToTestFile( 'test.js' ), }, 'expect': { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( ValueError, 'Please specify a new name to rename it to.\n' 'Usage: RefactorRename <new name>' ) } } ) @SharedYcmd def Subcommands_RefactorRename_NotPossible_test( app ): RunTest( app, { 'description': 'RefactorRename cannot rename a non-existing method', 'request': { 'command': 'RefactorRename', 'arguments': [ 'whatever' ], 'line_num': 35, 'column_num': 5, 'filepath': PathToTestFile( 'test.js' ), }, 'expect': { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( RuntimeError, 'Value cannot be renamed: ' 'You cannot rename this element.' ) } } ) @SharedYcmd def Subcommands_RefactorRename_Simple_test( app ): RunTest( app, { 'description': 'RefactorRename works on a class name', 'request': { 'command': 'RefactorRename', 'arguments': [ 'test' ], 'line_num': 1, 'column_num': 7, 'filepath': PathToTestFile( 'test.js' ), }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_inanyorder( ChunkMatcher( 'test', LocationMatcher( PathToTestFile( 'test.js' ), 11, 15 ), LocationMatcher( PathToTestFile( 'test.js' ), 11, 18 ) ), ChunkMatcher( 'test', LocationMatcher( PathToTestFile( 'test.js' ), 1, 7 ), LocationMatcher( PathToTestFile( 'test.js' ), 1, 10 ) ), ), 'location': LocationMatcher( PathToTestFile( 'test.js' ), 1, 7 ) } ) ) } ) } } ) @SharedYcmd def Subcommands_RefactorRename_MultipleFiles_test( app ): RunTest( app, { 'description': 'RefactorRename works across files', 'request': { 'command': 'RefactorRename', 'arguments': [ 'this-is-a-longer-string' ], 'line_num': 22, 'column_num': 8, 'filepath': PathToTestFile( 'test.js' ), }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_inanyorder( ChunkMatcher( 'this-is-a-longer-string', LocationMatcher( PathToTestFile( 'test.js' ), 22, 7 ), LocationMatcher( PathToTestFile( 'test.js' ), 22, 10 ) ), ChunkMatcher( 'this-is-a-longer-string', LocationMatcher( PathToTestFile( 'test.js' ), 30, 15 ), LocationMatcher( PathToTestFile( 'test.js' ), 30, 18 ) ), ChunkMatcher( 'this-is-a-longer-string', LocationMatcher( PathToTestFile( 'test.js' ), 34, 1 ), LocationMatcher( PathToTestFile( 'test.js' ), 34, 4 ) ), ChunkMatcher( 'this-is-a-longer-string', LocationMatcher( PathToTestFile( 'file2.js' ), 1, 5 ), LocationMatcher( PathToTestFile( 'file2.js' ), 1, 8 ) ), ChunkMatcher( 'this-is-a-longer-string', LocationMatcher( PathToTestFile( 'file3.js' ), 1, 15 ), LocationMatcher( PathToTestFile( 'file3.js' ), 1, 18 ) ), ), 'location': LocationMatcher( PathToTestFile( 'test.js' ), 22, 8 ) } ) ) } ) } } ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �����������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascript/testdata/���������������������������������������0000775�0000000�0000000�00000000000�13746324601�0023214�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascript/testdata/extra_confs/���������������������������0000775�0000000�0000000�00000000000�13746324601�0025527�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascript/testdata/extra_confs/brace_on_new_line.py�������0000664�0000000�0000000�00000000270�13746324601�0031530�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������def Settings( **kwargs ): assert kwargs[ 'language' ] == 'typescript' return { 'formatting_options': { 'placeOpenBraceOnNewLineForFunctions': True } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascript/testdata/extra_confs/brace_on_same_line.py������0000664�0000000�0000000�00000000271�13746324601�0031665�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������def Settings( **kwargs ): assert kwargs[ 'language' ] == 'typescript' return { 'formatting_options': { 'placeOpenBraceOnNewLineForFunctions': False } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascript/testdata/extra_confs/func.js��������������������0000664�0000000�0000000�00000000045�13746324601�0027017�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������function add(x, y) { return x + y; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascript/testdata/extra_confs/jsconfig.json��������������0000664�0000000�0000000�00000000063�13746324601�0030223�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "compilerOptions": { "checkJs": true } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascript/testdata/file2.js�������������������������������0000664�0000000�0000000�00000000030�13746324601�0024544�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������new Bar().testMethod(); ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascript/testdata/file3.js�������������������������������0000664�0000000�0000000�00000000047�13746324601�0024555�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������var bar = new Bar(); bar.testMethod(); �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascript/testdata/identifier/����������������������������0000775�0000000�0000000�00000000000�13746324601�0025336�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascript/testdata/identifier/jsconfig.json���������������0000664�0000000�0000000�00000000003�13746324601�0030024�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{} �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascript/testdata/identifier/test.js���������������������0000664�0000000�0000000�00000000047�13746324601�0026654�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������const test = { foo: 'bar' } test. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascript/testdata/imports.js�����������������������������0000664�0000000�0000000�00000000217�13746324601�0025247�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import func from "library"; func(); import * as lib from "library"; lib.func(); import { func1, func2 } from "library"; func1(); func2(); ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascript/testdata/jsconfig.json��������������������������0000664�0000000�0000000�00000000110�13746324601�0025701�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "compilerOptions": { "checkJs": true, "target": "ES6" } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascript/testdata/test.js��������������������������������0000664�0000000�0000000�00000000556�13746324601�0024537�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������class Foo { /** Unicode string: 说话 */ methodA() {} methodB() {} methodC( foo, bar ) {} } var foo = new Foo(); // line 14, column 6 foo.m /** * Class documentation * * Multi-line */ class Bar { /** * Method documentation */ testMethod() {} } var bar = new Bar(); bar.testMethod(); bar.nonExistingMethod(); Bar.apply() Bår ��������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascript/testdata/unicode.js�����������������������������0000664�0000000�0000000�00000000025�13746324601�0025175�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������export class Bår {} �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascriptreact/�������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0022422�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascriptreact/__init__.py��������������������������������0000664�0000000�0000000�00000001630�13746324601�0024533�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import os from ycmd.tests.javascriptreact.conftest import * # noqa def PathToTestFile( *args ): dir_of_current_script = os.path.dirname( os.path.abspath( __file__ ) ) return os.path.join( dir_of_current_script, 'testdata', *args ) ��������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascriptreact/conftest.py��������������������������������0000664�0000000�0000000�00000006426�13746324601�0024631�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import pytest from ycmd.tests.test_utils import ( ClearCompletionsCache, IgnoreExtraConfOutsideTestsFolder, IsolatedApp, SetUpApp, StopCompleterServer, WaitUntilCompleterServerReady ) shared_app = None @pytest.fixture( scope='module', autouse=True ) def set_up_shared_app(): global shared_app shared_app = SetUpApp() WaitUntilCompleterServerReady( shared_app, 'javascriptreact' ) yield StopCompleterServer( shared_app, 'javascriptreact' ) @pytest.fixture def app( request ): which = request.param[ 0 ] assert which == 'isolated' or which == 'shared' if which == 'isolated': with IsolatedApp( request.param[ 1 ] ) as app: yield app StopCompleterServer( app, 'javascriptreact' ) else: global shared_app ClearCompletionsCache() with IgnoreExtraConfOutsideTestsFolder(): yield shared_app """Defines a decorator to be attached to tests of this package. This decorator passes the shared ycmd application as a parameter.""" SharedYcmd = pytest.mark.parametrize( # Name of the fixture/function argument 'app', # Fixture parameters, passed to app() as request.param [ ( 'shared', ) ], # Non-empty ids makes fixture parameters visible in pytest verbose output ids = [ '' ], # Execute the fixture, instead of passing parameters directly to the # function argument indirect = True ) def IsolatedYcmd( custom_options = {} ): """Defines a decorator to be attached to tests of this package. This decorator passes a unique ycmd application as a parameter. It should be used on tests that change the server state in a irreversible way (ex: a semantic subserver is stopped or restarted) or expect a clean state (ex: no semantic subserver started, no .ycm_extra_conf.py loaded, etc). Use the optional parameter |custom_options| to give additional options and/or override the default ones. Example usage: from ycmd.tests.python import IsolatedYcmd @IsolatedYcmd( { 'python_binary_path': '/some/path' } ) def CustomPythonBinaryPath_test( app ): ... """ return pytest.mark.parametrize( # Name of the fixture/function argument 'app', # Fixture parameters, passed to app() as request.param [ ( 'isolated', custom_options ) ], # Non-empty ids makes fixture parameters visible in pytest verbose output ids = [ '' ], # Execute the fixture, instead of passing parameters directly to the # function argument indirect = True ) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascriptreact/get_completions_test.py��������������������0000664�0000000�0000000�00000004751�13746324601�0027235�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2015-2020 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 <http://www.gnu.org/licenses/>. import pprint import requests from hamcrest import assert_that, equal_to, has_entries, has_item from ycmd.tests.javascriptreact import PathToTestFile, SharedYcmd from ycmd.tests.test_utils import CombineRequest from ycmd.utils import ReadFile def RunTest( app, test ): contents = ReadFile( test[ 'request' ][ 'filepath' ] ) filetype = test[ 'request' ].get( 'filetype', 'javascriptreact' ) app.post_json( '/event_notification', CombineRequest( test[ 'request' ], { 'contents': contents, 'filetype': filetype, 'event_name': 'BufferVisit' } ) ) response = app.post_json( '/completions', CombineRequest( test[ 'request' ], { 'contents': contents, 'filetype': 'javascriptreact', 'force_semantic': True } ) ) print( f'completer response: { pprint.pformat( response.json ) }' ) assert_that( response.status_code, equal_to( test[ 'expect' ][ 'response' ] ) ) assert_that( response.json, test[ 'expect' ][ 'data' ] ) @SharedYcmd def GetCompletions_JavaScriptReact_DefaultTriggers_test( app ): filepath = PathToTestFile( 'test.jsx' ) RunTest( app, { 'description': 'No need to force after a semantic trigger', 'request': { 'line_num': 7, 'column_num': 12, 'filepath': filepath, 'filetype': 'javascriptreact' }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': has_item( has_entries( { 'insertion_text': 'alinkColor', 'extra_menu_info': '(property) Document.alinkColor: string', 'detailed_info': '(property) Document.alinkColor: string', 'kind': 'property', } ) ) } ) } } ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �����������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascriptreact/subcommands_test.py������������������������0000664�0000000�0000000�00000006106�13746324601�0026351�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2015-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_inanyorder, equal_to, has_entries ) import pprint import requests from ycmd.tests.test_utils import CombineRequest from ycmd.tests.javascriptreact import PathToTestFile, SharedYcmd from ycmd.utils import ReadFile def RunTest( app, test ): contents = ReadFile( test[ 'request' ][ 'filepath' ] ) app.post_json( '/event_notification', CombineRequest( test[ 'request' ], { 'contents': contents, 'filetype': 'javascriptreact', 'event_name': 'BufferVisit' } ) ) app.post_json( '/event_notification', CombineRequest( test[ 'request' ], { 'contents': contents, 'filetype': 'javascriptreact', 'event_name': 'FileReadyToParse' } ) ) # We ignore errors here and check the response code ourself. # This is to allow testing of requests returning errors. response = app.post_json( '/run_completer_command', CombineRequest( test[ 'request' ], { 'contents': contents, 'filetype': 'javascriptreact', 'command_arguments': ( [ test[ 'request' ][ 'command' ] ] + test[ 'request' ].get( 'arguments', [] ) ) } ), expect_errors = True ) print( f'completer response: { pprint.pformat( response.json ) }' ) assert_that( response.status_code, equal_to( test[ 'expect' ][ 'response' ] ) ) assert_that( response.json, test[ 'expect' ][ 'data' ] ) @SharedYcmd def Subcommands_GoToReferences_test( app ): RunTest( app, { 'description': 'GoToReferences works', 'request': { 'command': 'GoToReferences', 'line_num': 6, 'column_num': 4, 'filepath': PathToTestFile( 'test.jsx' ), }, 'expect': { 'response': requests.codes.ok, 'data': contains_inanyorder( has_entries( { 'description': 'function HelloMessage({ name }) {', 'line_num' : 1, 'column_num' : 10, 'filepath' : PathToTestFile( 'test.jsx' ) } ), has_entries( { 'description': ' <HelloMessage name="Taylor" />,', 'line_num' : 6, 'column_num' : 4, 'filepath' : PathToTestFile( 'test.jsx' ) } ), ) } } ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascriptreact/testdata/����������������������������������0000775�0000000�0000000�00000000000�13746324601�0024233�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascriptreact/testdata/jsconfig.json���������������������0000664�0000000�0000000�00000000134�13746324601�0026726�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "compilerOptions": { "jsx": "react", "checkJs": true, "target": "ES6" } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/javascriptreact/testdata/test.jsx��������������������������0000664�0000000�0000000�00000000244�13746324601�0025740�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������function HelloMessage({ name }) { return <div>Hello {name}</div>; } ReactDOM.render( <HelloMessage name="Taylor" />, document.getElementById('container') ); ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/language_server/�������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0022406�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/language_server/__init__.py��������������������������������0000664�0000000�0000000�00000002532�13746324601�0024521�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import os from ycmd.completers.language_server import language_server_completer as lsc from ycmd.tests.language_server.conftest import * # noqa def PathToTestFile( *args ): dir_of_current_script = os.path.dirname( os.path.abspath( __file__ ) ) return os.path.join( dir_of_current_script, 'testdata', *args ) class MockConnection( lsc.LanguageServerConnection ): def __init__( self, workspace_config_handler = None ): super().__init__( None, None, workspace_config_handler ) def TryServerConnectionBlocking( self ): return True def Shutdown( self ): pass def WriteData( self, data ): pass def ReadData( self, size = -1 ): return bytes( b'' ) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/language_server/conftest.py��������������������������������0000664�0000000�0000000�00000003755�13746324601�0024617�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import pytest from ycmd.tests.test_utils import IsolatedApp, StopCompleterServer @pytest.fixture def app( request ): with IsolatedApp( request.param ) as app: try: yield app finally: StopCompleterServer( app, 'foo' ) def IsolatedYcmd( custom_options = {} ): """Defines a decorator to be attached to tests of this package. This decorator passes a unique ycmd application as a parameter. It should be used on tests that change the server state in a irreversible way (ex: a semantic subserver is stopped or restarted) or expect a clean state (ex: no semantic subserver started, no .ycm_extra_conf.py loaded, etc). Use the optional parameter |custom_options| to give additional options and/or override the default ones. Example usage: from ycmd.tests.python import IsolatedYcmd @IsolatedYcmd( { 'python_binary_path': '/some/path' } ) def CustomPythonBinaryPath_test( app ): ... """ return pytest.mark.parametrize( # Name of the fixture/function argument 'app', # Fixture parameters, passed to app() as request.param [ custom_options ], # Non-empty ids makes fixture parameters visible in pytest verbose output ids = [ '' ], # Execute the fixture, instead of passing parameters directly to the # function argument indirect = True ) �������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/language_server/generic_completer_test.py������������������0000664�0000000�0000000�00000056140�13746324601�0027513�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2015-2020 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 <http://www.gnu.org/licenses/>. import json import requests from hamcrest import ( assert_that, contains_exactly, empty, equal_to, has_entries, has_entry, has_items, instance_of ) from unittest.mock import patch from os import path as p from ycmd.completers.language_server.language_server_completer import ( TCPSingleStreamConnection, ResponseFailedException ) from ycmd import handlers, utils from ycmd.tests.language_server import IsolatedYcmd, PathToTestFile from ycmd.tests.test_utils import ( BuildRequest, CompletionEntryMatcher, ErrorMatcher, LocationMatcher, RangeMatcher, SignatureAvailableMatcher, WaitUntilCompleterServerReady ) from ycmd.utils import ReadFile DIR_OF_THIS_SCRIPT = p.dirname( p.abspath( __file__ ) ) PATH_TO_GENERIC_COMPLETER = p.join( DIR_OF_THIS_SCRIPT, '..', '..', '..', 'third_party', 'generic_server', 'server', 'out', 'server.js' ) TEST_FILE = PathToTestFile( 'generic_server', 'test_file' ) TEST_FILE_CONTENT = ReadFile( TEST_FILE ) TEST_PORT = utils.GetUnusedLocalhostPort() @IsolatedYcmd( { 'language_server': [ { 'name': 'foo', 'filetypes': [ 'foo' ], 'cmdline': [ 'node', PATH_TO_GENERIC_COMPLETER, '--stdio' ] } ] } ) def GenericLSPCompleter_GetCompletions_NotACompletionProvider_test( app ): completer = handlers._server_state.GetFiletypeCompleter( [ 'foo' ] ) with patch.object( completer, '_is_completion_provider', False ): request = BuildRequest( filepath = TEST_FILE, filetype = 'foo', line_num = 1, column_num = 3, contents = 'Java', event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', request ) WaitUntilCompleterServerReady( app, 'foo' ) request.pop( 'event_name' ) response = app.post_json( '/completions', BuildRequest( **request ) ) assert_that( response.json, has_entries( { 'completions': contains_exactly( CompletionEntryMatcher( 'Java', '[ID]' ) ) } ) ) @IsolatedYcmd( { 'semantic_triggers': { 'foo': [ 're!.' ] }, 'language_server': [ { 'name': 'foo', 'filetypes': [ 'foo' ], 'cmdline': [ 'node', PATH_TO_GENERIC_COMPLETER, '--stdio' ] } ] } ) def GenericLSPCompleter_GetCompletions_FilteredNoForce_test( app ): request = BuildRequest( filepath = TEST_FILE, filetype = 'foo', line_num = 1, column_num = 3, contents = 'Java', event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', request ) WaitUntilCompleterServerReady( app, 'foo' ) request.pop( 'event_name' ) response = app.post_json( '/completions', BuildRequest( **request ) ) assert_that( response.status_code, equal_to( 200 ) ) print( f'Completer response: { json.dumps( response.json, indent = 2 ) }' ) assert_that( response.json, has_entries( { 'completions': contains_exactly( CompletionEntryMatcher( 'JavaScript', 'JavaScript details' ), ) } ) ) @IsolatedYcmd( { 'language_server': [ { 'name': 'foo', 'filetypes': [ 'foo' ], 'cmdline': [ 'node', PATH_TO_GENERIC_COMPLETER, '--stdio' ] } ] } ) def GenericLSPCompleter_GetCompletions_test( app ): request = BuildRequest( filepath = TEST_FILE, filetype = 'foo', line_num = 1, column_num = 1, contents = TEST_FILE_CONTENT, event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', request ) WaitUntilCompleterServerReady( app, 'foo' ) request[ 'force_semantic' ] = True request.pop( 'event_name' ) response = app.post_json( '/completions', BuildRequest( **request ) ) assert_that( response.status_code, equal_to( 200 ) ) print( f'Completer response: { json.dumps( response.json, indent = 2 ) }' ) assert_that( response.json, has_entries( { 'completions': contains_exactly( CompletionEntryMatcher( 'JavaScript', 'JavaScript details' ), CompletionEntryMatcher( 'TypeScript', 'TypeScript details' ), ) } ) ) @IsolatedYcmd( { 'language_server': [ { 'name': 'foo', 'filetypes': [ 'foo' ], 'cmdline': [ 'node', PATH_TO_GENERIC_COMPLETER, '--listen', str( TEST_PORT ) ], 'port': TEST_PORT } ] } ) def GenericLSPCompleter_GetCompletions_TCP_test( app ): request = BuildRequest( filepath = TEST_FILE, filetype = 'foo', line_num = 1, column_num = 1, contents = TEST_FILE_CONTENT, event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', request ) WaitUntilCompleterServerReady( app, 'foo' ) request[ 'force_semantic' ] = True request.pop( 'event_name' ) response = app.post_json( '/completions', BuildRequest( **request ) ) assert_that( response.status_code, equal_to( 200 ) ) print( f'Completer response: { json.dumps( response.json, indent = 2 ) }' ) assert_that( response.json, has_entries( { 'completions': contains_exactly( CompletionEntryMatcher( 'JavaScript', 'JavaScript details' ), CompletionEntryMatcher( 'TypeScript', 'TypeScript details' ), ) } ) ) @IsolatedYcmd( { 'language_server': [ { 'name': 'foo', 'filetypes': [ 'foo' ], 'cmdline': [ 'node', PATH_TO_GENERIC_COMPLETER, '--listen', str( TEST_PORT ) ], 'port': TEST_PORT } ] } ) def GenericLSPCompleter_DebugInfo_TCP_test( app ): request = BuildRequest( filepath = TEST_FILE, filetype = 'foo', line_num = 1, column_num = 1, contents = TEST_FILE_CONTENT, event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', request ) WaitUntilCompleterServerReady( app, 'foo' ) request.pop( 'event_name' ) response = app.post_json( '/debug_info', request ).json assert_that( response, has_entry( 'completer', has_entries( { 'name': 'GenericLSP', 'servers': contains_exactly( has_entries( { 'name': 'fooCompleter', 'port': TEST_PORT, 'pid': instance_of( int ), 'logfiles': contains_exactly( instance_of( str ), instance_of( str ) ), 'extras': contains_exactly( has_entries( { 'key': 'Server State', 'value': instance_of( str ), } ), has_entries( { 'key': 'Project Directory', 'value': PathToTestFile( 'generic_server' ), } ), has_entries( { 'key': 'Settings', 'value': '{}' } ), ) } ) ), } ) ) ) @IsolatedYcmd( { 'language_server': [ { 'name': 'foo', 'filetypes': [ 'foo' ], 'cmdline': [ 'node', PATH_TO_GENERIC_COMPLETER, '--listen', '${port}' ], 'port': '*' } ] } ) def GenericLSPCompleter_DebugInfo_TCP_GeneratePort_test( app ): request = BuildRequest( filepath = TEST_FILE, filetype = 'foo', line_num = 1, column_num = 1, contents = TEST_FILE_CONTENT, event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', request ) WaitUntilCompleterServerReady( app, 'foo' ) request.pop( 'event_name' ) response = app.post_json( '/debug_info', request ).json assert_that( response, has_entry( 'completer', has_entries( { 'name': 'GenericLSP', 'servers': contains_exactly( has_entries( { 'name': 'fooCompleter', 'port': instance_of( int ), 'pid': instance_of( int ), 'logfiles': contains_exactly( instance_of( str ), instance_of( str ) ), 'extras': contains_exactly( has_entries( { 'key': 'Server State', 'value': instance_of( str ), } ), has_entries( { 'key': 'Project Directory', 'value': PathToTestFile( 'generic_server' ), } ), has_entries( { 'key': 'Settings', 'value': '{}' } ), ) } ) ), } ) ) ) @IsolatedYcmd( { 'language_server': [ { 'name': 'foo', 'filetypes': [ 'foo' ], 'port': TEST_PORT } ] } ) def GenericLSPCompleter_ConnectTimeout_test( app ): with patch.object( TCPSingleStreamConnection, 'TCP_CONNECT_TIMEOUT', 1 ): request = BuildRequest( filepath = TEST_FILE, filetype = 'foo', line_num = 1, column_num = 1, contents = TEST_FILE_CONTENT, event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', request ) import time # We patched the timeout to 1s time.sleep( 1.5 ) request.pop( 'event_name' ) response = app.post_json( '/debug_info', request ).json assert_that( response, has_entry( 'completer', has_entries( { 'name': 'GenericLSP', 'servers': contains_exactly( has_entries( { 'name': 'fooCompleter', 'port': TEST_PORT, 'pid': None, 'logfiles': empty(), 'extras': contains_exactly( has_entries( { 'key': 'Server State', 'value': 'Dead', } ), has_entries( { 'key': 'Project Directory', 'value': None, } ), has_entries( { 'key': 'Settings', 'value': '{}' } ), ) } ) ), } ) ) ) @IsolatedYcmd( { 'language_server': [ { 'name': 'foo', 'filetypes': [ 'foo' ], 'cmdline': [ 'node', PATH_TO_GENERIC_COMPLETER, '--stdio' ] } ] } ) def GenericLSPCompleter_Diagnostics_test( app ): request = BuildRequest( filepath = TEST_FILE, filetype = 'foo', line_num = 1, column_num = 1, contents = TEST_FILE_CONTENT, event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', request ) WaitUntilCompleterServerReady( app, 'foo' ) request.pop( 'event_name' ) response = app.post_json( '/receive_messages', request ) assert_that( response.json, has_items( has_entries( { 'diagnostics': contains_exactly( has_entries( { 'kind': equal_to( 'WARNING' ), 'location': LocationMatcher( TEST_FILE, 2, 1 ), 'location_extent': RangeMatcher( TEST_FILE, ( 2, 1 ), ( 2, 4 ) ), 'text': equal_to( 'FOO is all uppercase.' ), 'fixit_available': False } ), has_entries( { 'kind': equal_to( 'WARNING' ), 'location': LocationMatcher( TEST_FILE, 3, 1 ), 'location_extent': RangeMatcher( TEST_FILE, ( 3, 1 ), ( 3, 4 ) ), 'text': equal_to( 'FOO is all uppercase.' ), 'fixit_available': False } ), has_entries( { 'kind': equal_to( 'WARNING' ), 'location': LocationMatcher( TEST_FILE, 4, 1 ), 'location_extent': RangeMatcher( TEST_FILE, ( 4, 1 ), ( 4, 4 ) ), 'text': equal_to( 'FOO is all uppercase.' ), 'fixit_available': False } ), has_entries( { 'kind': equal_to( 'WARNING' ), 'location': LocationMatcher( TEST_FILE, 5, 1 ), 'location_extent': RangeMatcher( TEST_FILE, ( 5, 1 ), ( 5, 4 ) ), 'text': equal_to( 'FOO is all uppercase.' ), 'fixit_available': False } ), ) } ) ) ) @IsolatedYcmd( { 'language_server': [ { 'name': 'foo', 'filetypes': [ 'foo' ], 'cmdline': [ 'node', PATH_TO_GENERIC_COMPLETER, '--stdio' ] } ] } ) def GenericLSPCompleter_Hover_RequestFails_test( app ): request = BuildRequest( filepath = TEST_FILE, filetype = 'foo', line_num = 1, column_num = 1, contents = TEST_FILE_CONTENT, event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', request ) WaitUntilCompleterServerReady( app, 'foo' ) request.pop( 'event_name' ) request[ 'command_arguments' ] = [ 'GetHover' ] response = app.post_json( '/run_completer_command', request, expect_errors = True ) assert_that( response.status_code, equal_to( requests.codes.internal_server_error ) ) assert_that( response.json, ErrorMatcher( ResponseFailedException, 'Request failed: -32601: Unhandled method textDocument/hover' ) ) @IsolatedYcmd( { 'language_server': [ { 'name': 'foo', 'filetypes': [ 'foo' ], 'cmdline': [ 'node', PATH_TO_GENERIC_COMPLETER, '--stdio' ] } ] } ) @patch( 'ycmd.completers.language_server.generic_lsp_completer.' 'GenericLSPCompleter.GetHoverResponse', return_value = 'asd' ) def GenericLSPCompleter_HoverIsString_test( get_hover, app ): request = BuildRequest( filepath = TEST_FILE, filetype = 'foo', line_num = 1, column_num = 1, contents = TEST_FILE_CONTENT, event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', request ) WaitUntilCompleterServerReady( app, 'foo' ) request.pop( 'event_name' ) request[ 'command_arguments' ] = [ 'GetHover' ] response = app.post_json( '/run_completer_command', request ).json assert_that( response, has_entry( 'detailed_info', 'asd' ) ) @IsolatedYcmd( { 'language_server': [ { 'name': 'foo', 'filetypes': [ 'foo' ], 'cmdline': [ 'node', PATH_TO_GENERIC_COMPLETER, '--stdio' ] } ] } ) @patch( 'ycmd.completers.language_server.generic_lsp_completer.' 'GenericLSPCompleter.GetHoverResponse', return_value = { 'whatever': 'blah', 'value': 'asd' } ) def GenericLSPCompleter_HoverIsDict_test( get_hover, app ): request = BuildRequest( filepath = TEST_FILE, filetype = 'foo', line_num = 1, column_num = 1, contents = TEST_FILE_CONTENT, event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', request ) WaitUntilCompleterServerReady( app, 'foo' ) request.pop( 'event_name' ) request[ 'command_arguments' ] = [ 'GetHover' ] response = app.post_json( '/run_completer_command', request ).json assert_that( response, has_entry( 'detailed_info', 'asd' ) ) @IsolatedYcmd( { 'language_server': [ { 'name': 'foo', 'filetypes': [ 'foo' ], 'cmdline': [ 'node', PATH_TO_GENERIC_COMPLETER, '--stdio' ] } ] } ) @patch( 'ycmd.completers.language_server.generic_lsp_completer.' 'GenericLSPCompleter.GetHoverResponse', return_value = [ { 'whatever': 'blah', 'value': 'asd' }, 'qe', { 'eh?': 'hover_sucks', 'value': 'yes, it does' } ] ) def GenericLSPCompleter_HoverIsList_test( get_hover, app ): request = BuildRequest( filepath = TEST_FILE, filetype = 'foo', line_num = 1, column_num = 1, contents = TEST_FILE_CONTENT, event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', request ) WaitUntilCompleterServerReady( app, 'foo' ) request.pop( 'event_name' ) request[ 'command_arguments' ] = [ 'GetHover' ] response = app.post_json( '/run_completer_command', request ).json assert_that( response, has_entry( 'detailed_info', 'asd\nqe\nyes, it does' ) ) @IsolatedYcmd( { 'language_server': [ { 'name': 'foo', 'filetypes': [ 'foo' ], 'project_root_files': [ 'proj_root' ], 'cmdline': [ 'node', PATH_TO_GENERIC_COMPLETER, '--stdio' ] } ] } ) def GenericLSPCompleter_DebugInfo_CustomRoot_test( app, *args ): test_file = PathToTestFile( 'generic_server', 'foo', 'bar', 'baz', 'test_file' ) request = BuildRequest( filepath = test_file, filetype = 'foo', line_num = 1, column_num = 1, contents = '', event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', request ) WaitUntilCompleterServerReady( app, 'foo' ) request.pop( 'event_name' ) response = app.post_json( '/debug_info', request ).json assert_that( response, has_entry( 'completer', has_entries( { 'name': 'GenericLSP', 'servers': contains_exactly( has_entries( { 'name': 'fooCompleter', 'is_running': instance_of( bool ), 'executable': contains_exactly( instance_of( str ), instance_of( str ), instance_of( str ) ), 'address': None, 'port': None, 'pid': instance_of( int ), 'logfiles': contains_exactly( instance_of( str ) ), 'extras': contains_exactly( has_entries( { 'key': 'Server State', 'value': instance_of( str ), } ), has_entries( { 'key': 'Project Directory', 'value': PathToTestFile( 'generic_server', 'foo' ), } ), has_entries( { 'key': 'Settings', 'value': '{}' } ), ) } ) ), } ) ) ) @IsolatedYcmd( { 'language_server': [ { 'name': 'foo', 'filetypes': [ 'foo' ], 'project_root_files': [ 'proj_root' ], 'cmdline': [ 'node', PATH_TO_GENERIC_COMPLETER, '--stdio' ] } ] } ) def GenericLSPCompleter_SignatureHelp_NoTriggers_test( app ): test_file = PathToTestFile( 'generic_server', 'foo', 'bar', 'baz', 'test_file' ) request = BuildRequest( filepath = test_file, filetype = 'foo', line_num = 1, column_num = 1, contents = '', event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', request ) WaitUntilCompleterServerReady( app, 'foo' ) request.pop( 'event_name' ) response = app.post_json( '/signature_help', request ).json assert_that( response, has_entries( { 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': empty() } ), 'errors': empty() } ) ) @IsolatedYcmd( { 'language_server': [ { 'name': 'foo', 'filetypes': [ 'foo' ], 'project_root_files': [ 'proj_root' ], 'cmdline': [ 'node', PATH_TO_GENERIC_COMPLETER, '--stdio' ] } ] } ) @patch( 'ycmd.completers.completer.Completer.ShouldUseSignatureHelpNow', return_value = True ) def GenericLSPCompleter_SignatureHelp_NotASigHelpProvider_test( should_use_sig, app ): test_file = PathToTestFile( 'generic_server', 'foo', 'bar', 'baz', 'test_file' ) request = BuildRequest( filepath = test_file, filetype = 'foo', line_num = 1, column_num = 1, contents = '', event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', request ) WaitUntilCompleterServerReady( app, 'foo' ) request.pop( 'event_name' ) response = app.post_json( '/signature_help', request ).json assert_that( response, has_entries( { 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': empty() } ), 'errors': empty() } ) ) @IsolatedYcmd( { 'language_server': [ { 'name': 'foo', 'filetypes': [ 'foo' ], 'project_root_files': [ 'proj_root' ], 'cmdline': [ 'node', PATH_TO_GENERIC_COMPLETER, '--stdio' ] } ] } ) def GenericLSPCompleter_SignatureHelp_NotSupported_test( app ): test_file = PathToTestFile( 'generic_server', 'foo', 'bar', 'baz', 'test_file' ) app.post_json( '/event_notification', BuildRequest( **{ 'filepath': test_file, 'event_name': 'FileReadyToParse', 'filetype': 'foo' } ), expect_errors = True ) WaitUntilCompleterServerReady( app, 'foo' ) response = app.get( '/signature_help_available', { 'subserver': 'foo' } ).json assert_that( response, SignatureAvailableMatcher( 'NO' ) ) @IsolatedYcmd( { 'global_ycm_extra_conf': PathToTestFile( 'generic_server', 'single_diag.py' ), 'language_server': [ { 'name': 'foo', 'filetypes': [ 'foo' ], 'capabilities': { 'workspace': { 'configuration': True } }, 'cmdline': [ 'node', PATH_TO_GENERIC_COMPLETER, '--stdio' ] } ] } ) def GenericLSPCompleter_SingleDiagnostics_test( app ): request = BuildRequest( filepath = TEST_FILE, filetype = 'foo', line_num = 1, column_num = 1, contents = TEST_FILE_CONTENT, event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', request ) WaitUntilCompleterServerReady( app, 'foo' ) request.pop( 'event_name' ) response = app.post_json( '/receive_messages', request ) assert_that( response.json, has_items( has_entries( { 'diagnostics': contains_exactly( has_entries( { 'kind': equal_to( 'WARNING' ), 'location': LocationMatcher( TEST_FILE, 2, 1 ), 'location_extent': RangeMatcher( TEST_FILE, ( 2, 1 ), ( 2, 4 ) ), 'text': equal_to( 'FOO is all uppercase.' ), 'fixit_available': False } ) ) } ) ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/language_server/language_server_completer_test.py����������0000664�0000000�0000000�00000136414�13746324601�0031253�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2017-2020 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 <http://www.gnu.org/licenses/>. import pytest from unittest.mock import patch from hamcrest import ( all_of, assert_that, calling, empty, ends_with, equal_to, contains_exactly, has_entries, has_entry, has_items, has_key, is_not, raises ) from ycmd.completers import completer from ycmd.completers.language_server import language_server_completer as lsc from ycmd.completers.language_server.language_server_completer import ( NoHoverInfoException, NO_HOVER_INFORMATION ) from ycmd.completers.language_server import language_server_protocol as lsp from ycmd.tests.language_server import MockConnection from ycmd.request_wrap import RequestWrap from ycmd.tests.test_utils import ( BuildRequest, ChunkMatcher, DummyCompleter, LocationMatcher, RangeMatcher ) from ycmd.tests.language_server import IsolatedYcmd, PathToTestFile from ycmd import handlers, utils, responses import os class MockCompleter( lsc.LanguageServerCompleter, DummyCompleter ): def __init__( self, custom_options = {} ): user_options = handlers._server_state._user_options.copy() user_options.update( custom_options ) super().__init__( user_options ) self._connection = MockConnection( lambda request: self.WorkspaceConfigurationResponse( request ) ) self._started = False def Language( self ): return 'foo' def StartServer( self, request_data, **kwargs ): self._started = True self._project_directory = self.GetProjectDirectory( request_data ) return True def GetConnection( self ): return self._connection def HandleServerCommand( self, request_data, command ): return super().HandleServerCommand( request_data, command ) def ServerIsHealthy( self ): return self._started def GetCommandLine( self ): return [ 'server' ] def GetServerName( self ): return 'mock_completer' @IsolatedYcmd( { 'global_ycm_extra_conf': PathToTestFile( 'extra_confs', 'settings_extra_conf.py' ) } ) def LanguageServerCompleter_ExtraConf_ServerReset_test( app ): filepath = PathToTestFile( 'extra_confs', 'foo' ) app.post_json( '/event_notification', BuildRequest( filepath = filepath, filetype = 'foo', contents = '', event_name = 'FileReadyToParse' ) ) request_data = RequestWrap( BuildRequest() ) completer = MockCompleter() assert_that( None, equal_to( completer._project_directory ) ) completer.OnFileReadyToParse( request_data ) assert_that( completer._project_directory, is_not( None ) ) assert_that( completer._settings.get( 'ls', {} ), is_not( empty() ) ) completer.ServerReset() assert_that( completer._settings.get( 'ls', {} ), empty() ) assert_that( None, equal_to( completer._project_directory ) ) @IsolatedYcmd( { 'global_ycm_extra_conf': PathToTestFile( 'extra_confs', 'empty_extra_conf.py' ) } ) def LanguageServerCompleter_ExtraConf_FileEmpty_test( app ): filepath = PathToTestFile( 'extra_confs', 'foo' ) completer = MockCompleter() request_data = RequestWrap( BuildRequest( filepath = filepath, filetype = 'ycmtest', contents = '' ) ) completer.OnFileReadyToParse( request_data ) assert_that( {}, equal_to( completer._settings.get( 'ls', {} ) ) ) # Simulate receipt of response and initialization complete initialize_response = { 'result': { 'capabilities': {} } } completer._HandleInitializeInPollThread( initialize_response ) assert_that( {}, equal_to( completer._settings.get( 'ls', {} ) ) ) # We shouldn't have used the extra_conf path for the project directory, but # that _also_ happens to be the path of the file we opened. assert_that( PathToTestFile( 'extra_confs' ), equal_to( completer._project_directory ) ) @IsolatedYcmd( { 'global_ycm_extra_conf': PathToTestFile( 'extra_confs', 'settings_none_extra_conf.py' ) } ) def LanguageServerCompleter_ExtraConf_SettingsReturnsNone_test( app ): filepath = PathToTestFile( 'extra_confs', 'foo' ) completer = MockCompleter() request_data = RequestWrap( BuildRequest( filepath = filepath, filetype = 'ycmtest', contents = '' ) ) completer.OnFileReadyToParse( request_data ) assert_that( {}, equal_to( completer._settings.get( 'ls', {} ) ) ) # We shouldn't have used the extra_conf path for the project directory, but # that _also_ happens to be the path of the file we opened. assert_that( PathToTestFile( 'extra_confs' ), equal_to( completer._project_directory ) ) @IsolatedYcmd( { 'global_ycm_extra_conf': PathToTestFile( 'extra_confs', 'settings_extra_conf.py' ) } ) def LanguageServerCompleter_ExtraConf_SettingValid_test( app ): filepath = PathToTestFile( 'extra_confs', 'foo' ) completer = MockCompleter() request_data = RequestWrap( BuildRequest( filepath = filepath, filetype = 'ycmtest', working_dir = PathToTestFile(), contents = '' ) ) assert_that( {}, equal_to( completer._settings.get( 'ls', {} ) ) ) completer.OnFileReadyToParse( request_data ) assert_that( { 'java.rename.enabled' : False }, equal_to( completer._settings.get( 'ls', {} ) ) ) # We use the working_dir not the path to the global extra conf (which is # ignored) assert_that( PathToTestFile(), equal_to( completer._project_directory ) ) @IsolatedYcmd( { 'extra_conf_globlist': [ '!*' ] } ) def LanguageServerCompleter_ExtraConf_NoExtraConf_test( app ): filepath = PathToTestFile( 'extra_confs', 'foo' ) completer = MockCompleter() request_data = RequestWrap( BuildRequest( filepath = filepath, filetype = 'ycmtest', working_dir = PathToTestFile(), contents = '' ) ) assert_that( {}, equal_to( completer._settings.get( 'ls', {} ) ) ) completer.OnFileReadyToParse( request_data ) assert_that( {}, equal_to( completer._settings.get( 'ls', {} ) ) ) # Simulate receipt of response and initialization complete initialize_response = { 'result': { 'capabilities': {} } } completer._HandleInitializeInPollThread( initialize_response ) assert_that( {}, equal_to( completer._settings.get( 'ls', {} ) ) ) # We use the client working directory assert_that( PathToTestFile(), equal_to( completer._project_directory ) ) @IsolatedYcmd( { 'extra_conf_globlist': [ '*' ] } ) def LanguageServerCompleter_ExtraConf_NonGlobal_test( app ): filepath = PathToTestFile( 'project', 'settings_extra_conf', 'foo' ) completer = MockCompleter() request_data = RequestWrap( BuildRequest( filepath = filepath, filetype = 'ycmtest', # ignored; ycm conf path used working_dir = 'ignore_this', contents = '' ) ) assert_that( {}, equal_to( completer._settings.get( 'ls', {} ) ) ) completer.OnFileReadyToParse( request_data ) assert_that( { 'java.rename.enabled' : False }, equal_to( completer._settings.get( 'ls', {} ) ) ) # Simulate receipt of response and initialization complete initialize_response = { 'result': { 'capabilities': {} } } completer._HandleInitializeInPollThread( initialize_response ) assert_that( PathToTestFile( 'project', 'settings_extra_conf' ), equal_to( completer._project_directory ) ) @IsolatedYcmd() def LanguageServerCompleter_Initialise_Aborted_test( app ): completer = MockCompleter() request_data = RequestWrap( BuildRequest() ) with patch.object( completer.GetConnection(), 'ReadData', side_effect = RuntimeError ): assert_that( completer.ServerIsReady(), equal_to( False ) ) completer.OnFileReadyToParse( request_data ) with patch.object( completer, '_HandleInitializeInPollThread' ) as handler: completer.GetConnection().run() handler.assert_not_called() assert_that( completer._initialize_event.is_set(), equal_to( False ) ) assert_that( completer.ServerIsReady(), equal_to( False ) ) with patch.object( completer, 'ServerIsHealthy', return_value = False ): assert_that( completer.ServerIsReady(), equal_to( False ) ) @IsolatedYcmd() def LanguageServerCompleter_Initialise_Shutdown_test( app ): completer = MockCompleter() request_data = RequestWrap( BuildRequest() ) with patch.object( completer.GetConnection(), 'ReadData', side_effect = lsc.LanguageServerConnectionStopped ): assert_that( completer.ServerIsReady(), equal_to( False ) ) completer.OnFileReadyToParse( request_data ) with patch.object( completer, '_HandleInitializeInPollThread' ) as handler: completer.GetConnection().run() handler.assert_not_called() assert_that( completer._initialize_event.is_set(), equal_to( False ) ) assert_that( completer.ServerIsReady(), equal_to( False ) ) with patch.object( completer, 'ServerIsHealthy', return_value = False ): assert_that( completer.ServerIsReady(), equal_to( False ) ) @IsolatedYcmd() def LanguageServerCompleter_GoTo_test( app ): if utils.OnWindows(): filepath = 'C:\\test.test' uri = 'file:///c:/test.test' else: filepath = '/test.test' uri = 'file:/test.test' contents = 'line1\nline2\nline3' completer = MockCompleter() # LSP server supports all code navigation features. completer._server_capabilities = { 'definitionProvider': True, 'declarationProvider': True, 'typeDefinitionProvider': True, 'implementationProvider': True, 'referencesProvider': True } request_data = RequestWrap( BuildRequest( filetype = 'ycmtest', filepath = filepath, contents = contents, line_num = 2, column_num = 3 ) ) @patch.object( completer, '_ServerIsInitialized', return_value = True ) def Test( responses, command, exception, throws, *args ): with patch.object( completer.GetConnection(), 'GetResponse', side_effect = responses ): if throws: assert_that( calling( completer.OnUserCommand ).with_args( [ command ], request_data ), raises( exception ) ) else: result = completer.OnUserCommand( [ command ], request_data ) print( f'Result: { result }' ) assert_that( result, exception ) location = { 'uri': uri, 'range': { 'start': { 'line': 0, 'character': 0 }, 'end': { 'line': 0, 'character': 0 }, } } goto_response = has_entries( { 'filepath': filepath, 'column_num': 1, 'line_num': 1, 'description': 'line1' } ) cases = [ ( [ { 'result': None } ], 'GoToDefinition', RuntimeError, True ), ( [ { 'result': location } ], 'GoToDeclaration', goto_response, False ), ( [ { 'result': {} } ], 'GoToType', RuntimeError, True ), ( [ { 'result': [] } ], 'GoToImplementation', RuntimeError, True ), ( [ { 'result': [ location ] } ], 'GoToReferences', goto_response, False ), ( [ { 'result': [ location, location ] } ], 'GoToReferences', contains_exactly( goto_response, goto_response ), False ), ] for response, goto_handlers, exception, throws in cases: Test( response, goto_handlers, exception, throws ) # All requests return an invalid URI. with patch( 'ycmd.completers.language_server.language_server_protocol.UriToFilePath', side_effect = lsp.InvalidUriException ): Test( [ { 'result': { 'uri': uri, 'range': { 'start': { 'line': 0, 'character': 0 }, 'end': { 'line': 0, 'character': 0 } } } } ], 'GoTo', LocationMatcher( '', 1, 1 ), False ) with patch( 'ycmd.completers.completer_utils.GetFileContents', side_effect = IOError ): Test( [ { 'result': { 'uri': uri, 'range': { 'start': { 'line': 0, 'character': 0 }, 'end': { 'line': 0, 'character': 0 } } } } ], 'GoToDefinition', LocationMatcher( filepath, 1, 1 ), False ) # Both requests return the location where the cursor is. Test( [ { 'result': { 'uri': uri, 'range': { 'start': { 'line': 1, 'character': 0 }, 'end': { 'line': 1, 'character': 4 } } } }, { 'result': { 'uri': uri, 'range': { 'start': { 'line': 1, 'character': 0 }, 'end': { 'line': 1, 'character': 4 }, } } } ], 'GoTo', LocationMatcher( filepath, 2, 1 ), False ) # First request returns two locations. Test( [ { 'result': [ { 'uri': uri, 'range': { 'start': { 'line': 0, 'character': 0 }, 'end': { 'line': 0, 'character': 4 } } }, { 'uri': uri, 'range': { 'start': { 'line': 1, 'character': 0 }, 'end': { 'line': 1, 'character': 4 }, } } ], } ], 'GoTo', contains_exactly( LocationMatcher( filepath, 1, 1 ), LocationMatcher( filepath, 2, 1 ) ), False ) # First request returns the location where the cursor is and second request # returns a different URI. if utils.OnWindows(): other_filepath = 'C:\\another.test' other_uri = 'file:///c:/another.test' else: other_filepath = '/another.test' other_uri = 'file:/another.test' Test( [ { 'result': { 'uri': uri, 'range': { 'start': { 'line': 1, 'character': 0 }, 'end': { 'line': 1, 'character': 4 } } } }, { 'result': { 'uri': other_uri, 'range': { 'start': { 'line': 1, 'character': 0 }, 'end': { 'line': 1, 'character': 4 }, } } } ], 'GoTo', LocationMatcher( other_filepath, 2, 1 ), False ) # First request returns a location before the cursor. Test( [ { 'result': { 'uri': uri, 'range': { 'start': { 'line': 0, 'character': 1 }, 'end': { 'line': 1, 'character': 1 } } } } ], 'GoTo', LocationMatcher( filepath, 1, 2 ), False ) # First request returns a location after the cursor. Test( [ { 'result': { 'uri': uri, 'range': { 'start': { 'line': 1, 'character': 3 }, 'end': { 'line': 2, 'character': 3 } } } } ], 'GoTo', LocationMatcher( filepath, 2, 4 ), False ) def GetCompletions_RejectInvalid_test(): if utils.OnWindows(): filepath = 'C:\\test.test' else: filepath = '/test.test' contents = 'line1.\nline2.\nline3.' request_data = RequestWrap( BuildRequest( filetype = 'ycmtest', filepath = filepath, contents = contents, line_num = 1, column_num = 7 ) ) text_edit = { 'newText': 'blah', 'range': { 'start': { 'line': 0, 'character': 6 }, 'end': { 'line': 0, 'character': 6 }, } } assert_that( lsc._GetCompletionItemStartCodepointOrReject( text_edit, request_data ), equal_to( 7 ) ) text_edit = { 'newText': 'blah', 'range': { 'start': { 'line': 0, 'character': 6 }, 'end': { 'line': 1, 'character': 6 }, } } assert_that( calling( lsc._GetCompletionItemStartCodepointOrReject ).with_args( text_edit, request_data ), raises( lsc.IncompatibleCompletionException ) ) text_edit = { 'newText': 'blah', 'range': { 'start': { 'line': 0, 'character': 20 }, 'end': { 'line': 0, 'character': 20 }, } } assert_that( lsc._GetCompletionItemStartCodepointOrReject( text_edit, request_data ), equal_to( 7 ) ) text_edit = { 'newText': 'blah', 'range': { 'start': { 'line': 0, 'character': 6 }, 'end': { 'line': 0, 'character': 5 }, } } assert_that( lsc._GetCompletionItemStartCodepointOrReject( text_edit, request_data ), equal_to( 7 ) ) def WorkspaceEditToFixIt_test(): if utils.OnWindows(): filepath = 'C:\\test.test' uri = 'file:///c:/test.test' else: filepath = '/test.test' uri = 'file:/test.test' contents = 'line1\nline2\nline3' request_data = RequestWrap( BuildRequest( filetype = 'ycmtest', filepath = filepath, contents = contents ) ) # Null response to textDocument/codeActions is valid assert_that( lsc.WorkspaceEditToFixIt( request_data, None ), equal_to( None ) ) # Empty WorkspaceEdit is not explicitly forbidden assert_that( lsc.WorkspaceEditToFixIt( request_data, {} ), equal_to( None ) ) # We don't support versioned documentChanges workspace_edit = { 'documentChanges': [ { 'textDocument': { 'version': 1, 'uri': uri }, 'edits': [ { 'newText': 'blah', 'range': { 'start': { 'line': 0, 'character': 5 }, 'end': { 'line': 0, 'character': 5 }, } } ] } ] } response = responses.BuildFixItResponse( [ lsc.WorkspaceEditToFixIt( request_data, workspace_edit, 'test' ) ] ) print( f'Response: { response }' ) assert_that( response, has_entries( { 'fixits': contains_exactly( has_entries( { 'text': 'test', 'chunks': contains_exactly( ChunkMatcher( 'blah', LocationMatcher( filepath, 1, 6 ), LocationMatcher( filepath, 1, 6 ) ) ) } ) ) } ) ) workspace_edit = { 'changes': { uri: [ { 'newText': 'blah', 'range': { 'start': { 'line': 0, 'character': 5 }, 'end': { 'line': 0, 'character': 5 }, } }, ] } } response = responses.BuildFixItResponse( [ lsc.WorkspaceEditToFixIt( request_data, workspace_edit, 'test' ) ] ) print( f'Response: { response }' ) print( f'Type Response: { type( response ) }' ) assert_that( response, has_entries( { 'fixits': contains_exactly( has_entries( { 'text': 'test', 'chunks': contains_exactly( ChunkMatcher( 'blah', LocationMatcher( filepath, 1, 6 ), LocationMatcher( filepath, 1, 6 ) ) ) } ) ) } ) ) @IsolatedYcmd( { 'extra_conf_globlist': [ '!*' ] } ) def LanguageServerCompleter_DelayedInitialization_test( app ): completer = MockCompleter() request_data = RequestWrap( BuildRequest( filepath = 'Test.ycmtest' ) ) with patch.object( completer, '_UpdateServerWithFileContents' ) as update: with patch.object( completer, '_PurgeFileFromServer' ) as purge: completer.OnFileReadyToParse( request_data ) completer.OnBufferUnload( request_data ) update.assert_not_called() purge.assert_not_called() # Simulate receipt of response and initialization complete initialize_response = { 'result': { 'capabilities': {} } } completer._HandleInitializeInPollThread( initialize_response ) update.assert_called_with( request_data ) purge.assert_called_with( 'Test.ycmtest' ) @IsolatedYcmd() def LanguageServerCompleter_RejectWorkspaceConfigurationRequest_test( app ): completer = MockCompleter() notification = { 'jsonrpc': '2.0', 'method': 'workspace/configuration', 'id': 1234, 'params': { 'items': [ { 'section': 'whatever' } ] } } with patch( 'ycmd.completers.language_server.' 'language_server_protocol.Reject' ) as reject: completer.GetConnection()._DispatchMessage( notification ) reject.assert_called_with( notification, lsp.Errors.MethodNotFound ) @IsolatedYcmd() def LanguageServerCompleter_ShowMessage_test( app ): completer = MockCompleter() request_data = RequestWrap( BuildRequest() ) notification = { 'method': 'window/showMessage', 'params': { 'message': 'this is a test' } } assert_that( completer.ConvertNotificationToMessage( request_data, notification ), has_entries( { 'message': 'this is a test' } ) ) @IsolatedYcmd() def LanguageServerCompleter_GetCompletions_List_test( app ): completer = MockCompleter() request_data = RequestWrap( BuildRequest() ) completion_response = { 'result': [ { 'label': 'test' } ] } resolve_responses = [ { 'result': { 'label': 'test' } }, ] with patch.object( completer, '_is_completion_provider', True ): with patch.object( completer.GetConnection(), 'GetResponse', side_effect = [ completion_response ] + resolve_responses ): assert_that( completer.ComputeCandidatesInner( request_data, 1 ), contains_exactly( has_items( has_entries( { 'insertion_text': 'test' } ) ), False ) ) @IsolatedYcmd() def LanguageServerCompleter_GetCompletions_UnsupportedKinds_test( app ): completer = MockCompleter() request_data = RequestWrap( BuildRequest() ) completion_response = { 'result': [ { 'label': 'test', 'kind': len( lsp.ITEM_KIND ) + 1 } ] } resolve_responses = [ { 'result': { 'label': 'test' } }, ] with patch.object( completer, '_is_completion_provider', True ): with patch.object( completer.GetConnection(), 'GetResponse', side_effect = [ completion_response ] + resolve_responses ): assert_that( completer.ComputeCandidatesInner( request_data, 1 ), contains_exactly( has_items( all_of( has_entry( 'insertion_text', 'test' ), is_not( has_key( 'kind' ) ) ) ), False ) ) @IsolatedYcmd() def LanguageServerCompleter_GetCompletions_NullNoError_test( app ): completer = MockCompleter() request_data = RequestWrap( BuildRequest() ) complete_response = { 'result': None } resolve_responses = [] with patch.object( completer, '_ServerIsInitialized', return_value = True ): with patch.object( completer, '_is_completion_provider', return_value = True ): with patch.object( completer.GetConnection(), 'GetResponse', side_effect = [ complete_response ] + resolve_responses ): assert_that( completer.ComputeCandidatesInner( request_data, 1 ), contains_exactly( empty(), False ) ) @IsolatedYcmd() def LanguageServerCompleter_GetCompletions_CompleteOnStartColumn_test( app ): completer = MockCompleter() completer._resolve_completion_items = False complete_response = { 'result': { 'items': [ { 'label': 'aa' }, { 'label': 'ac' }, { 'label': 'ab' } ], 'isIncomplete': False } } with patch.object( completer, '_is_completion_provider', True ): request_data = RequestWrap( BuildRequest( column_num = 2, contents = 'a', force_semantic = True ) ) with patch.object( completer.GetConnection(), 'GetResponse', return_value = complete_response ) as response: assert_that( completer.ComputeCandidates( request_data ), contains_exactly( has_entry( 'insertion_text', 'aa' ), has_entry( 'insertion_text', 'ab' ), has_entry( 'insertion_text', 'ac' ) ) ) # Nothing cached yet. assert_that( response.call_count, equal_to( 1 ) ) request_data = RequestWrap( BuildRequest( column_num = 3, contents = 'ab', force_semantic = True ) ) with patch.object( completer.GetConnection(), 'GetResponse', return_value = complete_response ) as response: assert_that( completer.ComputeCandidates( request_data ), contains_exactly( has_entry( 'insertion_text', 'ab' ) ) ) # Since the server returned a complete list of completions on the starting # column, no request should be sent to the server and the cache should be # used instead. assert_that( response.call_count, equal_to( 0 ) ) @IsolatedYcmd() def LanguageServerCompleter_GetCompletions_CompleteOnCurrentColumn_test( app ): completer = MockCompleter() completer._resolve_completion_items = False a_response = { 'result': { 'items': [ { 'label': 'aba' }, { 'label': 'aab' }, { 'label': 'aaa' } ], 'isIncomplete': True } } aa_response = { 'result': { 'items': [ { 'label': 'aab' }, { 'label': 'aaa' } ], 'isIncomplete': False } } aaa_response = { 'result': { 'items': [ { 'label': 'aaa' } ], 'isIncomplete': False } } ab_response = { 'result': { 'items': [ { 'label': 'abb' }, { 'label': 'aba' } ], 'isIncomplete': False } } with patch.object( completer, '_is_completion_provider', True ): # User starts by typing the character "a". request_data = RequestWrap( BuildRequest( column_num = 2, contents = 'a', force_semantic = True ) ) with patch.object( completer.GetConnection(), 'GetResponse', return_value = a_response ) as response: assert_that( completer.ComputeCandidates( request_data ), contains_exactly( has_entry( 'insertion_text', 'aaa' ), has_entry( 'insertion_text', 'aab' ), has_entry( 'insertion_text', 'aba' ) ) ) # Nothing cached yet. assert_that( response.call_count, equal_to( 1 ) ) # User types again the character "a". request_data = RequestWrap( BuildRequest( column_num = 3, contents = 'aa', force_semantic = True ) ) with patch.object( completer.GetConnection(), 'GetResponse', return_value = aa_response ) as response: assert_that( completer.ComputeCandidates( request_data ), contains_exactly( has_entry( 'insertion_text', 'aaa' ), has_entry( 'insertion_text', 'aab' ) ) ) # The server returned an incomplete list of completions the first time so # a new completion request should have been sent. assert_that( response.call_count, equal_to( 1 ) ) # User types the character "a" a third time. request_data = RequestWrap( BuildRequest( column_num = 4, contents = 'aaa', force_semantic = True ) ) with patch.object( completer.GetConnection(), 'GetResponse', return_value = aaa_response ) as response: assert_that( completer.ComputeCandidates( request_data ), contains_exactly( has_entry( 'insertion_text', 'aaa' ) ) ) # The server returned a complete list of completions the second time and # the new query is a prefix of the cached one ("aa" is a prefix of "aaa") # so the cache should be used. assert_that( response.call_count, equal_to( 0 ) ) # User deletes the third character. request_data = RequestWrap( BuildRequest( column_num = 3, contents = 'aa', force_semantic = True ) ) with patch.object( completer.GetConnection(), 'GetResponse', return_value = aa_response ) as response: assert_that( completer.ComputeCandidates( request_data ), contains_exactly( has_entry( 'insertion_text', 'aaa' ), has_entry( 'insertion_text', 'aab' ) ) ) # The new query is still a prefix of the cached one ("aa" is a prefix of # "aa") so the cache should again be used. assert_that( response.call_count, equal_to( 0 ) ) # User deletes the second character. request_data = RequestWrap( BuildRequest( column_num = 2, contents = 'a', force_semantic = True ) ) with patch.object( completer.GetConnection(), 'GetResponse', return_value = a_response ) as response: assert_that( completer.ComputeCandidates( request_data ), contains_exactly( has_entry( 'insertion_text', 'aaa' ), has_entry( 'insertion_text', 'aab' ), has_entry( 'insertion_text', 'aba' ) ) ) # The new query is not anymore a prefix of the cached one ("aa" is not a # prefix of "a") so the cache is invalidated and a new request is sent. assert_that( response.call_count, equal_to( 1 ) ) # Finally, user inserts the "b" character. request_data = RequestWrap( BuildRequest( column_num = 3, contents = 'ab', force_semantic = True ) ) with patch.object( completer.GetConnection(), 'GetResponse', return_value = ab_response ) as response: assert_that( completer.ComputeCandidates( request_data ), contains_exactly( has_entry( 'insertion_text', 'aba' ), has_entry( 'insertion_text', 'abb' ) ) ) # Last response was incomplete so the cache should not be used. assert_that( response.call_count, equal_to( 1 ) ) @pytest.mark.parametrize( 'line,text,overlap', [ ( '', '', 0 ), ( 'a', 'a', 1 ), ( 'a', 'b', 0 ), ( 'abcdef', 'abcdefg', 6 ), ( 'abcdefg', 'abcdef', 0 ), ( 'aaab', 'aaab', 4 ), ( 'abab', 'ab', 2 ), ( 'aab', 'caab', 0 ), ( 'abab', 'abababab', 4 ), ( 'aaab', 'baaa', 1 ), ( 'test.', 'test.test', 5 ), ( 'test.', 'test', 0 ), ( 'test', 'testtest', 4 ), ( '', 'testtest', 0 ), ( 'test', '', 0 ), ( 'Some CoCo', 'CoCo Beans', 4 ), ( 'Have some CoCo and CoCo', 'CoCo and CoCo is here.', 13 ), ( 'TEST xyAzA', 'xyAzA test', 5 ), ] ) def FindOverlapLength_test( line, text, overlap ): assert_that( lsc.FindOverlapLength( line, text ), equal_to( overlap ) ) @IsolatedYcmd() def LanguageServerCompleter_GetCodeActions_CursorOnEmptyLine_test( app ): completer = MockCompleter() request_data = RequestWrap( BuildRequest( line_num = 1, column_num = 1, contents = '' ) ) fixit_response = { 'result': [] } with patch.object( completer, '_ServerIsInitialized', return_value = True ): with patch.object( completer.GetConnection(), 'GetResponse', side_effect = [ fixit_response ] ): with patch( 'ycmd.completers.language_server.language_server_protocol.' 'CodeAction' ) as code_action: assert_that( completer.GetCodeActions( request_data, [] ), has_entry( 'fixits', empty() ) ) assert_that( # Range passed to lsp.CodeAction. # LSP requires to use the start of the next line as the end position # for a range that ends with a newline. code_action.call_args[ 0 ][ 2 ], has_entries( { 'start': has_entries( { 'line': 0, 'character': 0 } ), 'end': has_entries( { 'line': 1, 'character': 0 } ) } ) ) @IsolatedYcmd() def LanguageServerCompleter_Diagnostics_MaxDiagnosticsNumberExceeded_test( app ): completer = MockCompleter( { 'max_diagnostics_to_display': 1 } ) filepath = os.path.realpath( '/foo' ) uri = lsp.FilePathToUri( filepath ) request_data = RequestWrap( BuildRequest( line_num = 1, column_num = 1, filepath = filepath, contents = '' ) ) notification = { 'jsonrpc': '2.0', 'method': 'textDocument/publishDiagnostics', 'params': { 'uri': uri, 'diagnostics': [ { 'range': { 'start': { 'line': 3, 'character': 10 }, 'end': { 'line': 3, 'character': 11 } }, 'severity': 1, 'message': 'First error' }, { 'range': { 'start': { 'line': 4, 'character': 7 }, 'end': { 'line': 4, 'character': 13 } }, 'severity': 1, 'message': 'Second error [8]' } ] } } completer.GetConnection()._notifications.put( notification ) completer.HandleNotificationInPollThread( notification ) with patch.object( completer, '_ServerIsInitialized', return_value = True ): completer.OnFileReadyToParse( request_data ) # Simulate receipt of response and initialization complete initialize_response = { 'result': { 'capabilities': {} } } completer._HandleInitializeInPollThread( initialize_response ) diagnostics = contains_exactly( has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 4, 11 ), 'location_extent': RangeMatcher( filepath, ( 4, 11 ), ( 4, 12 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 4, 11 ), ( 4, 12 ) ) ), 'text': equal_to( 'First error' ), 'fixit_available': False } ), has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 1, 1 ), 'location_extent': RangeMatcher( filepath, ( 1, 1 ), ( 1, 1 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 1, 1 ), ( 1, 1 ) ) ), 'text': equal_to( 'Maximum number of diagnostics exceeded.' ), 'fixit_available': False } ) ) assert_that( completer.OnFileReadyToParse( request_data ), diagnostics ) assert_that( completer.PollForMessages( request_data ), contains_exactly( has_entries( { 'diagnostics': diagnostics, 'filepath': filepath } ) ) ) @IsolatedYcmd() def LanguageServerCompleter_Diagnostics_NoLimitToNumberOfDiagnostics_test( app ): completer = MockCompleter( { 'max_diagnostics_to_display': 0 } ) filepath = os.path.realpath( '/foo' ) uri = lsp.FilePathToUri( filepath ) request_data = RequestWrap( BuildRequest( line_num = 1, column_num = 1, filepath = filepath, contents = '' ) ) notification = { 'jsonrpc': '2.0', 'method': 'textDocument/publishDiagnostics', 'params': { 'uri': uri, 'diagnostics': [ { 'range': { 'start': { 'line': 3, 'character': 10 }, 'end': { 'line': 3, 'character': 11 } }, 'severity': 1, 'message': 'First error' }, { 'range': { 'start': { 'line': 4, 'character': 7 }, 'end': { 'line': 4, 'character': 13 } }, 'severity': 1, 'message': 'Second error' } ] } } completer.GetConnection()._notifications.put( notification ) completer.HandleNotificationInPollThread( notification ) with patch.object( completer, '_ServerIsInitialized', return_value = True ): completer.OnFileReadyToParse( request_data ) # Simulate receipt of response and initialization complete initialize_response = { 'result': { 'capabilities': {} } } completer._HandleInitializeInPollThread( initialize_response ) diagnostics = contains_exactly( has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 4, 11 ), 'location_extent': RangeMatcher( filepath, ( 4, 11 ), ( 4, 12 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 4, 11 ), ( 4, 12 ) ) ), 'text': equal_to( 'First error' ), 'fixit_available': False } ), has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 5, 8 ), 'location_extent': RangeMatcher( filepath, ( 5, 8 ), ( 5, 14 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 5, 8 ), ( 5, 14 ) ) ), 'text': equal_to( 'Second error' ), 'fixit_available': False } ) ) assert_that( completer.OnFileReadyToParse( request_data ), diagnostics ) assert_that( completer.PollForMessages( request_data ), contains_exactly( has_entries( { 'diagnostics': diagnostics, 'filepath': filepath } ) ) ) @IsolatedYcmd() def LanguageServerCompleter_GetHoverResponse_test( app ): completer = MockCompleter() request_data = RequestWrap( BuildRequest( line_num = 1, column_num = 1, contents = '' ) ) with patch.object( completer, '_ServerIsInitialized', return_value = True ): with patch.object( completer.GetConnection(), 'GetResponse', side_effect = [ { 'result': None } ] ): assert_that( calling( completer.GetHoverResponse ).with_args( request_data ), raises( NoHoverInfoException, NO_HOVER_INFORMATION ) ) with patch.object( completer.GetConnection(), 'GetResponse', side_effect = [ { 'result': { 'contents': 'test' } } ] ): assert_that( completer.GetHoverResponse( request_data ), equal_to( 'test' ) ) @IsolatedYcmd() def LanguageServerCompleter_Diagnostics_Code_test( app ): completer = MockCompleter() filepath = os.path.realpath( '/foo.cpp' ) uri = lsp.FilePathToUri( filepath ) request_data = RequestWrap( BuildRequest( line_num = 1, column_num = 1, filepath = filepath, contents = '' ) ) notification = { 'jsonrpc': '2.0', 'method': 'textDocument/publishDiagnostics', 'params': { 'uri': uri, 'diagnostics': [ { 'range': { 'start': { 'line': 3, 'character': 10 }, 'end': { 'line': 3, 'character': 11 } }, 'severity': 1, 'message': 'First error', 'code': 'random_error' }, { 'range': { 'start': { 'line': 3, 'character': 10 }, 'end': { 'line': 3, 'character': 11 } }, 'severity': 1, 'message': 'Second error', 'code': 8 }, { 'range': { 'start': { 'line': 3, 'character': 10 }, 'end': { 'line': 3, 'character': 11 } }, 'severity': 1, 'message': 'Third error', 'code': '8' } ] } } completer.GetConnection()._notifications.put( notification ) completer.HandleNotificationInPollThread( notification ) with patch.object( completer, 'ServerIsReady', return_value = True ): completer.OnFileReadyToParse( request_data ) # Simulate receipt of response and initialization complete initialize_response = { 'result': { 'capabilities': {} } } completer._HandleInitializeInPollThread( initialize_response ) diagnostics = contains_exactly( has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 4, 11 ), 'location_extent': RangeMatcher( filepath, ( 4, 11 ), ( 4, 12 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 4, 11 ), ( 4, 12 ) ) ), 'text': equal_to( 'First error [random_error]' ), 'fixit_available': False } ), has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 4, 11 ), 'location_extent': RangeMatcher( filepath, ( 4, 11 ), ( 4, 12 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 4, 11 ), ( 4, 12 ) ) ), 'text': equal_to( 'Second error [8]' ), 'fixit_available': False } ), has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 4, 11 ), 'location_extent': RangeMatcher( filepath, ( 4, 11 ), ( 4, 12 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 4, 11 ), ( 4, 12 ) ) ), 'text': equal_to( 'Third error [8]' ), 'fixit_available': False } ) ) assert_that( completer.OnFileReadyToParse( request_data ), diagnostics ) assert_that( completer.PollForMessages( request_data ), contains_exactly( has_entries( { 'diagnostics': diagnostics, 'filepath': filepath } ) ) ) @IsolatedYcmd() def LanguageServerCompleter_Diagnostics_PercentEncodeCannonical_test( app ): completer = MockCompleter() filepath = os.path.realpath( '/foo?' ) uri = lsp.FilePathToUri( filepath ) assert_that( uri, ends_with( '%3F' ) ) request_data = RequestWrap( BuildRequest( line_num = 1, column_num = 1, filepath = filepath, contents = '' ) ) notification = { 'jsonrpc': '2.0', 'method': 'textDocument/publishDiagnostics', 'params': { 'uri': uri.replace( '%3F', '%3f' ), 'diagnostics': [ { 'range': { 'start': { 'line': 3, 'character': 10 }, 'end': { 'line': 3, 'character': 11 } }, 'severity': 1, 'message': 'First error' } ] } } completer.GetConnection()._notifications.put( notification ) completer.HandleNotificationInPollThread( notification ) with patch.object( completer, '_ServerIsInitialized', return_value = True ): completer.OnFileReadyToParse( request_data ) # Simulate receipt of response and initialization complete initialize_response = { 'result': { 'capabilities': {} } } completer._HandleInitializeInPollThread( initialize_response ) diagnostics = contains_exactly( has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( filepath, 4, 11 ), 'location_extent': RangeMatcher( filepath, ( 4, 11 ), ( 4, 12 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 4, 11 ), ( 4, 12 ) ) ), 'text': equal_to( 'First error' ), 'fixit_available': False } ) ) assert_that( completer.OnFileReadyToParse( request_data ), diagnostics ) assert_that( completer.PollForMessages( request_data ), contains_exactly( has_entries( { 'diagnostics': diagnostics, 'filepath': filepath } ) ) ) @IsolatedYcmd() @patch.object( completer, 'MESSAGE_POLL_TIMEOUT', 0.01 ) def LanguageServerCompleter_PollForMessages_ServerNotStarted_test( app ): server = MockCompleter() request_data = RequestWrap( BuildRequest() ) assert_that( server.PollForMessages( request_data ), equal_to( True ) ) @IsolatedYcmd() def LanguageServerCompleter_OnFileSave_BeforeServerReady_test( app ): completer = MockCompleter() request_data = RequestWrap( BuildRequest() ) with patch.object( completer, 'ServerIsReady', return_value = False ): with patch.object( completer.GetConnection(), 'SendNotification' ) as send_notification: completer.OnFileSave( request_data ) send_notification.assert_not_called() @IsolatedYcmd() def LanguageServerCompleter_OnFileReadyToParse_InvalidURI_test( app ): completer = MockCompleter() filepath = os.path.realpath( '/foo?' ) uri = lsp.FilePathToUri( filepath ) request_data = RequestWrap( BuildRequest( line_num = 1, column_num = 1, filepath = filepath, contents = '' ) ) notification = { 'jsonrpc': '2.0', 'method': 'textDocument/publishDiagnostics', 'params': { 'uri': uri, 'diagnostics': [ { 'range': { 'start': { 'line': 3, 'character': 10 }, 'end': { 'line': 3, 'character': 11 } }, 'severity': 1, 'message': 'First error' } ] } } completer.GetConnection()._notifications.put( notification ) completer.HandleNotificationInPollThread( notification ) with patch.object( completer, '_ServerIsInitialized', return_value = True ): completer.OnFileReadyToParse( request_data ) # Simulate receipt of response and initialization complete initialize_response = { 'result': { 'capabilities': {} } } completer._HandleInitializeInPollThread( initialize_response ) diagnostics = contains_exactly( has_entries( { 'kind': equal_to( 'ERROR' ), 'location': LocationMatcher( '', 4, 11 ), 'location_extent': RangeMatcher( '', ( 4, 11 ), ( 4, 12 ) ), 'ranges': contains_exactly( RangeMatcher( '', ( 4, 11 ), ( 4, 12 ) ) ), 'text': equal_to( 'First error' ), 'fixit_available': False } ) ) with patch( 'ycmd.completers.language_server.language_server_protocol.' 'UriToFilePath', side_effect = lsp.InvalidUriException ) as \ uri_to_filepath: assert_that( completer.OnFileReadyToParse( request_data ), diagnostics ) uri_to_filepath.assert_called() def _TupleToLSPRange( tuple ): return { 'line': tuple[ 0 ], 'character': tuple[ 1 ] } def _Check_Distance( point, start, end, expected ): point = _TupleToLSPRange( point ) start = _TupleToLSPRange( start ) end = _TupleToLSPRange( end ) range = { 'start': start, 'end': end } result = lsc._DistanceOfPointToRange( point, range ) assert_that( result, equal_to( expected ) ) def LanguageServerCompleter_DistanceOfPointToRange_SingleLineRange_test(): # Point to the left of range. _Check_Distance( ( 0, 0 ), ( 0, 2 ), ( 0, 5 ) , 2 ) # Point inside range. _Check_Distance( ( 0, 4 ), ( 0, 2 ), ( 0, 5 ) , 0 ) # Point to the right of range. _Check_Distance( ( 0, 8 ), ( 0, 2 ), ( 0, 5 ) , 3 ) def LanguageServerCompleter_DistanceOfPointToRange_MultiLineRange_test(): # Point to the left of range. _Check_Distance( ( 0, 0 ), ( 0, 2 ), ( 3, 5 ) , 2 ) # Point inside range. _Check_Distance( ( 1, 4 ), ( 0, 2 ), ( 3, 5 ) , 0 ) # Point to the right of range. _Check_Distance( ( 3, 8 ), ( 0, 2 ), ( 3, 5 ) , 3 ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/language_server/language_server_connection_test.py���������0000664�0000000�0000000�00000013565�13746324601�0031421�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from unittest.mock import patch, MagicMock from ycmd.completers.language_server import language_server_completer as lsc from hamcrest import assert_that, calling, equal_to, raises from ycmd.tests.language_server import MockConnection import queue def LanguageServerConnection_ReadPartialMessage_test(): connection = MockConnection() return_values = [ bytes( b'Content-Length: 10\n\n{"abc":' ), bytes( b'""}' ), lsc.LanguageServerConnectionStopped ] with patch.object( connection, 'ReadData', side_effect = return_values ): with patch.object( connection, '_DispatchMessage' ) as dispatch_message: connection.run() dispatch_message.assert_called_with( { 'abc': '' } ) def LanguageServerConnection_MissingHeader_test(): connection = MockConnection() return_values = [ bytes( b'Content-NOTLENGTH: 10\n\n{"abc":' ), bytes( b'""}' ), lsc.LanguageServerConnectionStopped ] with patch.object( connection, 'ReadData', side_effect = return_values ): assert_that( calling( connection._ReadMessages ), raises( ValueError ) ) def LanguageServerConnection_RequestAbortCallback_test(): connection = MockConnection() return_values = [ lsc.LanguageServerConnectionStopped ] with patch.object( connection, 'ReadData', side_effect = return_values ): callback = MagicMock() response = connection.GetResponseAsync( 1, bytes( b'{"test":"test"}' ), response_callback = callback ) connection.run() callback.assert_called_with( response, None ) def LanguageServerConnection_RequestAbortAwait_test(): connection = MockConnection() return_values = [ lsc.LanguageServerConnectionStopped ] with patch.object( connection, 'ReadData', side_effect = return_values ): response = connection.GetResponseAsync( 1, bytes( b'{"test":"test"}' ) ) connection.run() assert_that( calling( response.AwaitResponse ).with_args( 10 ), raises( lsc.ResponseAbortedException ) ) def LanguageServerConnection_ServerConnectionDies_test(): connection = MockConnection() return_values = [ IOError ] with patch.object( connection, 'ReadData', side_effect = return_values ): # No exception is thrown connection.run() @patch( 'ycmd.completers.language_server.language_server_completer.' 'CONNECTION_TIMEOUT', 0.5 ) def LanguageServerConnection_ConnectionTimeout_test(): connection = MockConnection() with patch.object( connection, 'TryServerConnectionBlocking', side_effect=RuntimeError ): connection.Start() assert_that( calling( connection.AwaitServerConnection ), raises( lsc.LanguageServerConnectionTimeout ) ) assert_that( connection.is_alive(), equal_to( False ) ) def LanguageServerConnection_CloseTwice_test(): connection = MockConnection() with patch.object( connection, 'TryServerConnectionBlocking', side_effect=RuntimeError ): connection.Close() connection.Close() @patch.object( lsc, 'MAX_QUEUED_MESSAGES', 2 ) def LanguageServerConnection_AddNotificationToQueue_RingBuffer_test(): connection = MockConnection() notifications = connection._notifications # Queue empty assert_that( calling( notifications.get_nowait ), raises( queue.Empty ) ) # Queue partially full, then drained connection._AddNotificationToQueue( 'one' ) assert_that( notifications.get_nowait(), equal_to( 'one' ) ) assert_that( calling( notifications.get_nowait ), raises( queue.Empty ) ) # Queue full, then drained connection._AddNotificationToQueue( 'one' ) connection._AddNotificationToQueue( 'two' ) assert_that( notifications.get_nowait(), equal_to( 'one' ) ) assert_that( notifications.get_nowait(), equal_to( 'two' ) ) assert_that( calling( notifications.get_nowait ), raises( queue.Empty ) ) # Queue full, then new notification, then drained connection._AddNotificationToQueue( 'one' ) connection._AddNotificationToQueue( 'two' ) connection._AddNotificationToQueue( 'three' ) assert_that( notifications.get_nowait(), equal_to( 'two' ) ) assert_that( notifications.get_nowait(), equal_to( 'three' ) ) assert_that( calling( notifications.get_nowait ), raises( queue.Empty ) ) def LanguageServerConnection_RejectUnsupportedRequest_test(): connection = MockConnection() return_values = [ bytes( b'Content-Length: 26\r\n\r\n{"id":"1","method":"test"}' ), lsc.LanguageServerConnectionStopped ] expected_response = bytes( b'Content-Length: 79\r\n\r\n' b'{"error":{' b'"code":-32601,' b'"message":"Method not found"' b'},' b'"id":"1",' b'"jsonrpc":"2.0"}' ) with patch.object( connection, 'ReadData', side_effect = return_values ): with patch.object( connection, 'WriteData' ) as write_data: connection.run() write_data.assert_called_with( expected_response ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/language_server/language_server_protocol_test.py�����������0000664�0000000�0000000�00000022106�13746324601�0031112�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import pytest from ycmd.completers.language_server import language_server_protocol as lsp from hamcrest import assert_that, equal_to, calling, is_not, raises from ycmd.tests.test_utils import UnixOnly, WindowsOnly def ServerFileStateStore_RetrieveDelete_test(): store = lsp.ServerFileStateStore() # New state object created file1_state = store[ 'file1' ] assert_that( file1_state.version, equal_to( 0 ) ) assert_that( file1_state.checksum, equal_to( None ) ) assert_that( file1_state.state, equal_to( lsp.ServerFileState.CLOSED ) ) # Retrieve again unchanged file1_state = store[ 'file1' ] assert_that( file1_state.version, equal_to( 0 ) ) assert_that( file1_state.checksum, equal_to( None ) ) assert_that( file1_state.state, equal_to( lsp.ServerFileState.CLOSED ) ) # Retrieve/create another one (we don't actually open this one) file2_state = store[ 'file2' ] assert_that( file2_state.version, equal_to( 0 ) ) assert_that( file2_state.checksum, equal_to( None ) ) assert_that( file2_state.state, equal_to( lsp.ServerFileState.CLOSED ) ) # Checking for refresh on closed file is no-op assert_that( file1_state.GetSavedFileAction( 'blah' ), equal_to( lsp.ServerFileState.NO_ACTION ) ) assert_that( file1_state.version, equal_to( 0 ) ) assert_that( file1_state.checksum, equal_to( None ) ) assert_that( file1_state.state, equal_to( lsp.ServerFileState.CLOSED ) ) # Checking the next action progresses the state assert_that( file1_state.GetDirtyFileAction( 'test contents' ), equal_to( lsp.ServerFileState.OPEN_FILE ) ) assert_that( file1_state.version, equal_to( 1 ) ) assert_that( file1_state.checksum, is_not( equal_to( None ) ) ) assert_that( file1_state.state, equal_to( lsp.ServerFileState.OPEN ) ) # Replacing the same file is no-op assert_that( file1_state.GetDirtyFileAction( 'test contents' ), equal_to( lsp.ServerFileState.NO_ACTION ) ) assert_that( file1_state.version, equal_to( 1 ) ) assert_that( file1_state.checksum, is_not( equal_to( None ) ) ) assert_that( file1_state.state, equal_to( lsp.ServerFileState.OPEN ) ) # Changing the file creates a new version assert_that( file1_state.GetDirtyFileAction( 'test contents changed' ), equal_to( lsp.ServerFileState.CHANGE_FILE ) ) assert_that( file1_state.version, equal_to( 2 ) ) assert_that( file1_state.checksum, is_not( equal_to( None ) ) ) assert_that( file1_state.state, equal_to( lsp.ServerFileState.OPEN ) ) # Replacing the same file is no-op assert_that( file1_state.GetDirtyFileAction( 'test contents changed' ), equal_to( lsp.ServerFileState.NO_ACTION ) ) assert_that( file1_state.version, equal_to( 2 ) ) assert_that( file1_state.checksum, is_not( equal_to( None ) ) ) assert_that( file1_state.state, equal_to( lsp.ServerFileState.OPEN ) ) # Checking for refresh without change is no-op assert_that( file1_state.GetSavedFileAction( 'test contents changed' ), equal_to( lsp.ServerFileState.NO_ACTION ) ) assert_that( file1_state.version, equal_to( 2 ) ) assert_that( file1_state.checksum, is_not( equal_to( None ) ) ) assert_that( file1_state.state, equal_to( lsp.ServerFileState.OPEN ) ) # Changing the same file is a new version assert_that( file1_state.GetDirtyFileAction( 'test contents changed again' ), equal_to( lsp.ServerFileState.CHANGE_FILE ) ) assert_that( file1_state.version, equal_to( 3 ) ) assert_that( file1_state.checksum, is_not( equal_to( None ) ) ) assert_that( file1_state.state, equal_to( lsp.ServerFileState.OPEN ) ) # Checking for refresh with change is a new version assert_that( file1_state.GetSavedFileAction( 'test changed back' ), equal_to( lsp.ServerFileState.CHANGE_FILE ) ) assert_that( file1_state.version, equal_to( 4 ) ) assert_that( file1_state.checksum, is_not( equal_to( None ) ) ) assert_that( file1_state.state, equal_to( lsp.ServerFileState.OPEN ) ) # Closing an open file progressed the state assert_that( file1_state.GetFileCloseAction(), equal_to( lsp.ServerFileState.CLOSE_FILE ) ) assert_that( file1_state.version, equal_to( 4 ) ) assert_that( file1_state.checksum, is_not( equal_to( None ) ) ) assert_that( file1_state.state, equal_to( lsp.ServerFileState.CLOSED ) ) # Replacing a closed file opens it assert_that( file1_state.GetDirtyFileAction( 'test contents again2' ), equal_to( lsp.ServerFileState.OPEN_FILE ) ) assert_that( file1_state.version, equal_to( 1 ) ) assert_that( file1_state.checksum, is_not( equal_to( None ) ) ) assert_that( file1_state.state, equal_to( lsp.ServerFileState.OPEN ) ) # Closing an open file progressed the state assert_that( file1_state.GetFileCloseAction(), equal_to( lsp.ServerFileState.CLOSE_FILE ) ) assert_that( file1_state.version, equal_to( 1 ) ) assert_that( file1_state.checksum, is_not( equal_to( None ) ) ) assert_that( file1_state.state, equal_to( lsp.ServerFileState.CLOSED ) ) # You can del a closed file del store[ file1_state.filename ] # Replacing a del'd file opens it again file1_state = store[ 'file1' ] assert_that( file1_state.GetDirtyFileAction( 'test contents again3' ), equal_to( lsp.ServerFileState.OPEN_FILE ) ) assert_that( file1_state.version, equal_to( 1 ) ) assert_that( file1_state.checksum, is_not( equal_to( None ) ) ) assert_that( file1_state.state, equal_to( lsp.ServerFileState.OPEN ) ) # You can del an open file (though you probably shouldn't) del store[ file1_state.filename ] # Closing a closed file is a noop assert_that( file2_state.GetFileCloseAction(), equal_to( lsp.ServerFileState.NO_ACTION ) ) assert_that( file2_state.version, equal_to( 0 ) ) assert_that( file2_state.checksum, equal_to( None ) ) assert_that( file2_state.state, equal_to( lsp.ServerFileState.CLOSED ) ) @UnixOnly def UriToFilePath_Unix_test(): assert_that( calling( lsp.UriToFilePath ).with_args( 'test' ), raises( lsp.InvalidUriException ) ) assert_that( lsp.UriToFilePath( 'file:/usr/local/test/test.test' ), equal_to( '/usr/local/test/test.test' ) ) assert_that( lsp.UriToFilePath( 'file:///usr/local/test/test.test' ), equal_to( '/usr/local/test/test.test' ) ) @WindowsOnly def UriToFilePath_Windows_test(): assert_that( calling( lsp.UriToFilePath ).with_args( 'test' ), raises( lsp.InvalidUriException ) ) assert_that( lsp.UriToFilePath( 'file:c:/usr/local/test/test.test' ), equal_to( 'C:\\usr\\local\\test\\test.test' ) ) assert_that( lsp.UriToFilePath( 'file:c%3a/usr/local/test/test.test' ), equal_to( 'C:\\usr\\local\\test\\test.test' ) ) assert_that( lsp.UriToFilePath( 'file:c%3A/usr/local/test/test.test' ), equal_to( 'C:\\usr\\local\\test\\test.test' ) ) assert_that( lsp.UriToFilePath( 'file:///c:/usr/local/test/test.test' ), equal_to( 'C:\\usr\\local\\test\\test.test' ) ) assert_that( lsp.UriToFilePath( 'file:///c%3a/usr/local/test/test.test' ), equal_to( 'C:\\usr\\local\\test\\test.test' ) ) assert_that( lsp.UriToFilePath( 'file:///c%3A/usr/local/test/test.test' ), equal_to( 'C:\\usr\\local\\test\\test.test' ) ) @UnixOnly def FilePathToUri_Unix_test(): assert_that( lsp.FilePathToUri( '/usr/local/test/test.test' ), equal_to( 'file:///usr/local/test/test.test' ) ) @WindowsOnly def FilePathToUri_Windows_test(): assert_that( lsp.FilePathToUri( 'C:\\usr\\local\\test\\test.test' ), equal_to( 'file:///C:/usr/local/test/test.test' ) ) @pytest.mark.parametrize( 'line_value,codepoints,code_units', [ ( '', 0, 0 ), ( 'abcdef', 1, 1 ), ( 'abcdef', 2, 2 ), ( 'abc', 4, 4 ), ( '😉test', len( '😉' ), 2 ), ( '😉', len( '😉' ), 2 ), ( '😉test', len( '😉' ) + 1, 3 ), ( 'te😉st', 1, 1 ), ( 'te😉st', 2 + len( '😉' ) + 1, 5 ), ] ) def CodepointsToUTF16CodeUnitsAndReverse_test( line_value, codepoints, code_units ): assert_that( lsp.CodepointsToUTF16CodeUnits( line_value, codepoints ), equal_to( code_units ) ) assert_that( lsp.UTF16CodeUnitsToCodepoints( line_value, code_units ), equal_to( codepoints ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/language_server/testdata/����������������������������������0000775�0000000�0000000�00000000000�13746324601�0024217�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/language_server/testdata/extra_confs/����������������������0000775�0000000�0000000�00000000000�13746324601�0026532�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/language_server/testdata/extra_confs/empty_extra_conf.py���0000664�0000000�0000000�00000000000�13746324601�0032440�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/language_server/testdata/extra_confs/settings_extra_conf.py0000664�0000000�0000000�00000000121�13746324601�0033146�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������def Settings( **kwargs ): return { 'ls': { 'java.rename.enabled' : False } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������settings_none_extra_conf.py�������������������������������������������������������������������������0000664�0000000�0000000�00000000050�13746324601�0034107�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/language_server/testdata/extra_confs������������������������������������������������������������������������������������������def Settings( **kwargs ): return None ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/language_server/testdata/generic_server/�������������������0000775�0000000�0000000�00000000000�13746324601�0027221�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/language_server/testdata/generic_server/foo/���������������0000775�0000000�0000000�00000000000�13746324601�0030004�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/language_server/testdata/generic_server/foo/bar/�����������0000775�0000000�0000000�00000000000�13746324601�0030550�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/language_server/testdata/generic_server/foo/bar/baz/�������0000775�0000000�0000000�00000000000�13746324601�0031324�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������test_file�������������������������������������������������������������������������������������������0000664�0000000�0000000�00000000000�13746324601�0033134�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/language_server/testdata/generic_server/foo/bar/baz���������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/language_server/testdata/generic_server/foo/proj_root������0000664�0000000�0000000�00000000000�13746324601�0031732�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/language_server/testdata/generic_server/single_diag.py�����0000664�0000000�0000000�00000000272�13746324601�0032041�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������def Settings(**kwargs): return { "config_sections": { "languageServerExample": { "maxNumberOfProblems": 1 } } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/language_server/testdata/generic_server/test_file����������0000664�0000000�0000000�00000000021�13746324601�0031113�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ FOO FOO FOO FOO ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/language_server/testdata/project/��������������������������0000775�0000000�0000000�00000000000�13746324601�0025665�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/language_server/testdata/project/settings_extra_conf/������0000775�0000000�0000000�00000000000�13746324601�0031735�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������.ycm_extra_conf.py����������������������������������������������������������������������������������0000664�0000000�0000000�00000000121�13746324601�0035300�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/language_server/testdata/project/settings_extra_conf��������������������������������������������������������������������������def Settings( **kwargs ): return { 'ls': { 'java.rename.enabled' : False } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/misc_handlers_test.py��������������������������������������0000664�0000000�0000000�00000022153�13746324601�0023464�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2015-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( any_of, assert_that, contains_exactly, empty, equal_to, has_entries, instance_of ) from unittest.mock import patch import requests from ycmd.tests import IsolatedYcmd, PathToTestFile, SharedYcmd from ycmd.tests.test_utils import ( BuildRequest, DummyCompleter, PatchCompleter, SignatureAvailableMatcher, ErrorMatcher ) @SharedYcmd def MiscHandlers_Healthy_test( app ): assert_that( app.get( '/healthy' ).json, equal_to( True ) ) @SharedYcmd def MiscHandlers_Healthy_Subserver_test( app ): with PatchCompleter( DummyCompleter, filetype = 'dummy_filetype' ): assert_that( app.get( '/healthy', { 'subserver': 'dummy_filetype' } ).json, equal_to( True ) ) @SharedYcmd def MiscHandlers_SignatureHelpAvailable_test( app ): response = app.get( '/signature_help_available', expect_errors = True ).json assert_that( response, ErrorMatcher( RuntimeError, 'Subserver not specified' ) ) @SharedYcmd def MiscHandlers_SignatureHelpAvailable_Subserver_test( app ): with PatchCompleter( DummyCompleter, filetype = 'dummy_filetype' ): assert_that( app.get( '/signature_help_available', { 'subserver': 'dummy_filetype' } ).json, SignatureAvailableMatcher( 'NO' ) ) @SharedYcmd def MiscHandlers_SignatureHelpAvailable_NoSemanticCompleter_test( app ): assert_that( app.get( '/signature_help_available', { 'subserver': 'dummy_filetype' } ).json, SignatureAvailableMatcher( 'NO' ) ) @SharedYcmd def MiscHandlers_Ready_test( app ): assert_that( app.get( '/ready' ).json, equal_to( True ) ) @SharedYcmd def MiscHandlers_Ready_Subserver_test( app ): with PatchCompleter( DummyCompleter, filetype = 'dummy_filetype' ): assert_that( app.get( '/ready', { 'subserver': 'dummy_filetype' } ).json, equal_to( True ) ) @SharedYcmd def MiscHandlers_SemanticCompletionAvailable_test( app ): with PatchCompleter( DummyCompleter, filetype = 'dummy_filetype' ): request_data = BuildRequest( filetype = 'dummy_filetype' ) assert_that( app.post_json( '/semantic_completion_available', request_data ).json, equal_to( True ) ) @SharedYcmd def MiscHandlers_EventNotification_AlwaysJsonResponse_test( app ): event_data = BuildRequest( contents = 'foo foogoo ba', event_name = 'FileReadyToParse' ) assert_that( app.post_json( '/event_notification', event_data ).json, empty() ) @SharedYcmd def MiscHandlers_EventNotification_ReturnJsonOnBigFileError_test( app ): # We generate a content greater than Bottle.MEMFILE_MAX, which is set to 10MB. contents = "foo " * 5000000 event_data = BuildRequest( contents = contents, event_name = 'FileReadyToParse' ) response = app.post_json( '/event_notification', event_data, expect_errors = True ) assert_that( response.status_code, equal_to( requests.codes.request_entity_too_large ) ) assert_that( response.json, has_entries( { 'traceback': None, 'message': 'None', 'exception': None } ) ) @SharedYcmd def MiscHandlers_FilterAndSortCandidates_Basic_test( app ): candidate1 = { 'prop1': 'aoo', 'prop2': 'bar' } candidate2 = { 'prop1': 'bfo', 'prop2': 'zoo' } candidate3 = { 'prop1': 'cfo', 'prop2': 'moo' } data = { 'candidates': [ candidate3, candidate1, candidate2 ], 'sort_property': 'prop1', 'query': 'fo' } response_data = app.post_json( '/filter_and_sort_candidates', data ).json assert_that( response_data, contains_exactly( candidate2, candidate3 ) ) @SharedYcmd def MiscHandlers_LoadExtraConfFile_AlwaysJsonResponse_test( app ): filepath = PathToTestFile( 'extra_conf', 'project', '.ycm_extra_conf.py' ) extra_conf_data = BuildRequest( filepath = filepath ) assert_that( app.post_json( '/load_extra_conf_file', extra_conf_data ).json, equal_to( True ) ) @SharedYcmd def MiscHandlers_IgnoreExtraConfFile_AlwaysJsonResponse_test( app ): filepath = PathToTestFile( 'extra_conf', 'project', '.ycm_extra_conf.py' ) extra_conf_data = BuildRequest( filepath = filepath ) assert_that( app.post_json( '/ignore_extra_conf_file', extra_conf_data ).json, equal_to( True ) ) @IsolatedYcmd() def MiscHandlers_DebugInfo_ExtraConfLoaded_test( app ): filepath = PathToTestFile( 'extra_conf', 'project', '.ycm_extra_conf.py' ) app.post_json( '/load_extra_conf_file', { 'filepath': filepath } ) request_data = BuildRequest( filepath = filepath ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entries( { 'python': has_entries( { 'executable': instance_of( str ), 'version': instance_of( str ), } ), 'clang': has_entries( { 'has_support': instance_of( bool ), 'version': any_of( None, instance_of( str ) ) } ), 'extra_conf': has_entries( { 'path': instance_of( str ), 'is_loaded': True } ), 'completer': None } ) ) @SharedYcmd def MiscHandlers_DebugInfo_NoExtraConfFound_test( app ): request_data = BuildRequest() assert_that( app.post_json( '/debug_info', request_data ).json, has_entries( { 'python': has_entries( { 'executable': instance_of( str ), 'version': instance_of( str ), } ), 'clang': has_entries( { 'has_support': instance_of( bool ), 'version': any_of( None, instance_of( str ) ) } ), 'extra_conf': has_entries( { 'path': None, 'is_loaded': False } ), 'completer': None } ) ) @IsolatedYcmd() def MiscHandlers_DebugInfo_ExtraConfFoundButNotLoaded_test( app ): filepath = PathToTestFile( 'extra_conf', 'project', '.ycm_extra_conf.py' ) request_data = BuildRequest( filepath = filepath ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entries( { 'python': has_entries( { 'executable': instance_of( str ), 'version': instance_of( str ), } ), 'clang': has_entries( { 'has_support': instance_of( bool ), 'version': any_of( None, instance_of( str ) ) } ), 'extra_conf': has_entries( { 'path': instance_of( str ), 'is_loaded': False } ), 'completer': None } ) ) @SharedYcmd def MiscHandlers_ReceiveMessages_NoCompleter_test( app ): request_data = BuildRequest() assert_that( app.post_json( '/receive_messages', request_data ).json, equal_to( False ) ) @SharedYcmd def MiscHandlers_ReceiveMessages_NotSupportedByCompleter_test( app ): with PatchCompleter( DummyCompleter, filetype = 'dummy_filetype' ): request_data = BuildRequest( filetype = 'dummy_filetype' ) assert_that( app.post_json( '/receive_messages', request_data ).json, equal_to( False ) ) @SharedYcmd @patch( 'ycmd.completers.completer.Completer.ShouldUseSignatureHelpNow', return_value = True ) def MiscHandlers_SignatureHelp_DefaultEmptyResponse_test( should_use, app ): with PatchCompleter( DummyCompleter, filetype = 'dummy_filetype' ): request_data = BuildRequest( filetype = 'dummy_filetype' ) response = app.post_json( '/signature_help', request_data ).json assert_that( response, has_entries( { 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': empty() } ), 'errors': empty() } ) ) @SharedYcmd @patch( 'ycmd.completers.completer.Completer.ComputeSignatures', side_effect = RuntimeError ) def MiscHandlers_SignatureHelp_ComputeSignatureThrows_test( compute_sig, app ): with PatchCompleter( DummyCompleter, filetype = 'dummy_filetype' ): request_data = BuildRequest( filetype = 'dummy_filetype' ) response = app.post_json( '/signature_help', request_data ).json print( response ) assert_that( response, has_entries( { 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': empty() } ), 'errors': contains_exactly( ErrorMatcher( RuntimeError, '' ) ) } ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/����������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0020556�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/__init__.py�����������������������������������������0000664�0000000�0000000�00000001617�13746324601�0022674�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import os from ycmd.tests.python.conftest import * # noqa def PathToTestFile( *args ): dir_of_current_script = os.path.dirname( os.path.abspath( __file__ ) ) return os.path.join( dir_of_current_script, 'testdata', *args ) �����������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/conftest.py�����������������������������������������0000664�0000000�0000000�00000006013�13746324601�0022755�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import pytest from ycmd.tests.test_utils import ( ClearCompletionsCache, IgnoreExtraConfOutsideTestsFolder, IsolatedApp, SetUpApp ) shared_app = None @pytest.fixture( scope='module', autouse=True ) def set_up_shared_app(): global shared_app shared_app = SetUpApp() @pytest.fixture def app( request ): which = request.param[ 0 ] assert which == 'isolated' or which == 'shared' if which == 'isolated': custom_options = request.param[ 1 ] with IsolatedApp( custom_options ) as app: yield app else: global shared_app ClearCompletionsCache() with IgnoreExtraConfOutsideTestsFolder(): yield shared_app """Defines a decorator to be attached to tests of this package. This decorator passes the shared ycmd application as a parameter.""" SharedYcmd = pytest.mark.parametrize( # Name of the fixture/function argument 'app', # Fixture parameters, passed to app() as request.param [ ( 'shared', ) ], # Non-empty ids makes fixture parameters visible in pytest verbose output ids = [ '' ], # Execute the fixture, instead of passing parameters directly to the # function argument indirect = True ) def IsolatedYcmd( custom_options = {} ): """Defines a decorator to be attached to tests of this package. This decorator passes a unique ycmd application as a parameter. It should be used on tests that change the server state in a irreversible way (ex: a semantic subserver is stopped or restarted) or expect a clean state (ex: no semantic subserver started, no .ycm_extra_conf.py loaded, etc). Use the optional parameter |custom_options| to give additional options and/or override the default ones. Example usage: from ycmd.tests.python import IsolatedYcmd @IsolatedYcmd( { 'python_binary_path': '/some/path' } ) def CustomPythonBinaryPath_test( app ): ... """ return pytest.mark.parametrize( # Name of the fixture/function argument 'app', # Fixture parameters, passed to app() as request.param [ ( 'isolated', custom_options ) ], # Non-empty ids makes fixture parameters visible in pytest verbose output ids = [ '' ], # Execute the fixture, instead of passing parameters directly to the # function argument indirect = True ) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/debug_info_test.py����������������������������������0000664�0000000�0000000�00000003731�13746324601�0024274�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2016-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_exactly, has_entry, has_entries, instance_of ) from ycmd.tests.python import SharedYcmd from ycmd.tests.test_utils import BuildRequest @SharedYcmd def DebugInfo_test( app ): request_data = BuildRequest( filetype = 'python' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'Python', 'items': contains_exactly( has_entries( { 'key': 'Python interpreter', 'value': instance_of( str ) } ), has_entries( { 'key': 'Python root', 'value': instance_of( str ) } ), has_entries( { 'key': 'Python path', 'value': instance_of( str ) } ), has_entries( { 'key': 'Python version', 'value': instance_of( str ) } ), has_entries( { 'key': 'Jedi version', 'value': instance_of( str ) } ), has_entries( { 'key': 'Parso version', 'value': instance_of( str ) } ) ) } ) ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ���������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/get_completions_test.py�����������������������������0000664�0000000�0000000�00000032516�13746324601�0025371�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2015-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( all_of, assert_that, contains_exactly, contains_string, empty, equal_to, has_item, has_items, has_entry, has_entries, is_not ) import requests from ycmd.utils import ReadFile from ycmd.tests.python import IsolatedYcmd, PathToTestFile, SharedYcmd from ycmd.tests.test_utils import ( BuildRequest, CombineRequest, CompletionEntryMatcher, ErrorMatcher ) def RunTest( app, test ): """ Method to run a simple completion test and verify the result test is a dictionary containing: 'request': kwargs for BuildRequest 'expect': { 'response': server response code (e.g. httplib.OK) 'data': matcher for the server response json } """ contents = ReadFile( test[ 'request' ][ 'filepath' ] ) app.post_json( '/event_notification', CombineRequest( test[ 'request' ], { 'event_name': 'FileReadyToParse', 'contents': contents, } ), expect_errors = True ) # We ignore errors here and we check the response code ourself. # This is to allow testing of requests returning errors. response = app.post_json( '/completions', CombineRequest( test[ 'request' ], { 'contents': contents } ), expect_errors = True ) assert_that( response.status_code, equal_to( test[ 'expect' ][ 'response' ] ) ) assert_that( response.json, test[ 'expect' ][ 'data' ] ) @SharedYcmd def GetCompletions_Basic_test( app ): filepath = PathToTestFile( 'basic.py' ) completion_data = BuildRequest( filepath = filepath, filetype = 'python', contents = ReadFile( filepath ), line_num = 7, column_num = 3 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_items( CompletionEntryMatcher( 'a', 'self.a = 1', { 'detailed_info': '', 'kind': 'statement' } ), CompletionEntryMatcher( 'b', 'self.b = 2', { 'detailed_info': '', 'kind': 'statement' } ) ) ) completion_data = BuildRequest( filepath = filepath, filetype = 'python', contents = ReadFile( filepath ), line_num = 7, column_num = 4 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, all_of( has_item( CompletionEntryMatcher( 'a', 'self.a = 1', { 'detailed_info': '', 'kind': 'statement' } ) ), is_not( has_item( CompletionEntryMatcher( 'b' ) ) ) ) ) @SharedYcmd def GetCompletions_UnicodeDescription_test( app ): filepath = PathToTestFile( 'unicode.py' ) completion_data = BuildRequest( filepath = filepath, filetype = 'python', contents = ReadFile( filepath ), force_semantic = True, line_num = 5, column_num = 3 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, has_item( has_entry( 'detailed_info', contains_string( u'aafäö' ) ) ) ) @SharedYcmd def GetCompletions_NoSuggestions_Fallback_test( app ): # Python completer doesn't raise NO_COMPLETIONS_MESSAGE, so this is a # different code path to the Clang completer cases # TESTCASE2 (general_fallback/lang_python.py) RunTest( app, { 'description': 'param jedi does not know about (id). query="a_p"', 'request': { 'filetype' : 'python', 'filepath' : PathToTestFile( 'general_fallback', 'lang_python.py' ), 'line_num' : 28, 'column_num': 20, 'force_semantic': False, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_exactly( CompletionEntryMatcher( 'a_parameter', '[ID]' ), CompletionEntryMatcher( 'another_parameter', '[ID]' ), ), 'errors': empty() } ) } } ) @SharedYcmd def GetCompletions_Unicode_InLine_test( app ): RunTest( app, { 'description': 'return completions for strings with multi-byte chars', 'request': { 'filetype' : 'python', 'filepath' : PathToTestFile( 'unicode.py' ), 'line_num' : 7, 'column_num': 14 }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_exactly( CompletionEntryMatcher( 'center', 'def center(width: int, fillchar: str=...)' ) ), 'errors': empty() } ) } } ) @IsolatedYcmd( { 'global_ycm_extra_conf': PathToTestFile( 'project', 'empty_extra_conf.py' ) } ) def GetCompletions_SysPath_EmptyExtraConf_test( app ): RunTest( app, { 'description': 'Module is not added to sys.path through extra conf file', 'request': { 'filetype' : 'python', 'filepath' : PathToTestFile( 'project', '__main__.py' ), 'line_num' : 3, 'column_num': 8 }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': empty(), 'errors': empty() } ) } } ) @IsolatedYcmd( { 'global_ycm_extra_conf': PathToTestFile( 'project', 'settings_extra_conf.py' ) } ) def GetCompletions_SysPath_SettingsFunctionInExtraConf_test( app ): RunTest( app, { 'description': 'Module is added to sys.path through the Settings ' 'function in extra conf file', 'request': { 'filetype' : 'python', 'filepath' : PathToTestFile( 'project', '__main__.py' ), 'line_num' : 3, 'column_num': 8 }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': has_item( CompletionEntryMatcher( 'SOME_CONSTANT', 'SOME_CONSTANT = 1' ) ), 'errors': empty() } ) } } ) @IsolatedYcmd( { 'global_ycm_extra_conf': PathToTestFile( 'project', 'settings_extra_conf.py' ), 'disable_signature_help': True } ) def GetCompletions_SysPath_SettingsFunctionInExtraConf_DisableSig_test( app ): RunTest( app, { 'description': 'Module is added to sys.path through the Settings ' 'function in extra conf file', 'request': { 'filetype' : 'python', 'filepath' : PathToTestFile( 'project', '__main__.py' ), 'line_num' : 3, 'column_num': 8 }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': has_item( CompletionEntryMatcher( 'SOME_CONSTANT', 'SOME_CONSTANT = 1' ) ), 'errors': empty() } ) } } ) @IsolatedYcmd( { 'global_ycm_extra_conf': PathToTestFile( 'project', 'settings_empty_extra_conf.py' ) } ) def GetCompletions_SysPath_SettingsEmptyInExtraConf_test( app ): RunTest( app, { 'description': 'The Settings function returns an empty dictionary ' 'in extra conf file', 'request': { 'filetype' : 'python', 'filepath' : PathToTestFile( 'project', '__main__.py' ), 'line_num' : 3, 'column_num': 8 }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': empty(), 'errors': empty() } ) } } ) @IsolatedYcmd( { 'global_ycm_extra_conf': PathToTestFile( 'project', 'settings_none_extra_conf.py' ) } ) def GetCompletions_SysPath_SettingsNoneInExtraConf_test( app ): RunTest( app, { 'description': 'The Settings function returns None in extra conf file', 'request': { 'filetype' : 'python', 'filepath' : PathToTestFile( 'project', '__main__.py' ), 'line_num' : 3, 'column_num': 8 }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': empty(), 'errors': empty() } ) } } ) @IsolatedYcmd( { 'global_ycm_extra_conf': PathToTestFile( 'project', 'sys_path_extra_conf.py' ) } ) def GetCompletions_SysPath_PythonSysPathInExtraConf_test( app ): RunTest( app, { 'description': 'Module is added to sys.path through the PythonSysPath ' 'function in extra conf file', 'request': { 'filetype' : 'python', 'filepath' : PathToTestFile( 'project', '__main__.py' ), 'line_num' : 3, 'column_num': 8 }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': has_item( CompletionEntryMatcher( 'SOME_CONSTANT', 'SOME_CONSTANT = 1' ) ), 'errors': empty() } ) } } ) @IsolatedYcmd( { 'global_ycm_extra_conf': PathToTestFile( 'project', 'invalid_python_extra_conf.py' ) } ) def GetCompletions_PythonInterpreter_InvalidPythonInExtraConf_test( app ): RunTest( app, { 'description': 'Python interpreter path specified in extra conf file ' 'does not exist', 'request': { 'filetype' : 'python', 'filepath' : PathToTestFile( 'project', '__main__.py' ), 'line_num' : 3, 'column_num': 8 }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': empty(), 'errors': contains_exactly( ErrorMatcher( RuntimeError, 'Cannot find Python interpreter path ' '/non/existing/python.' ) ) } ) } } ) @IsolatedYcmd( { 'global_ycm_extra_conf': PathToTestFile( 'project', 'client_data_extra_conf.py' ) } ) def GetCompletions_PythonInterpreter_ExtraConfData_test( app ): filepath = PathToTestFile( 'project', '__main__.py' ) contents = ReadFile( filepath ) request = { 'filetype' : 'python', 'filepath' : filepath, 'contents' : contents, 'line_num' : 3, 'column_num': 8 } # Complete with a sys.path specified by the client that contains the path to a # third-party module. completion_request = CombineRequest( request, { 'extra_conf_data': { 'sys_path': [ PathToTestFile( 'project', 'third_party' ) ] } } ) assert_that( app.post_json( '/completions', completion_request ).json, has_entries( { 'completions': has_item( CompletionEntryMatcher( 'SOME_CONSTANT', 'SOME_CONSTANT = 1' ) ), 'errors': empty() } ) ) # Complete at the same position but no sys.path from the client. completion_request = CombineRequest( request, {} ) assert_that( app.post_json( '/completions', completion_request ).json, has_entries( { 'completions': empty(), 'errors': empty() } ) ) @SharedYcmd def GetCompletions_NumpyDoc_test( app ): RunTest( app, { 'description': 'Type hinting is working with docstrings ' 'in the Numpy format', 'request': { 'filetype' : 'python', 'filepath' : PathToTestFile( 'numpy_docstring.py' ), 'line_num' : 11, 'column_num': 18 }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_exactly( CompletionEntryMatcher( 'SomeMethod', 'def SomeMethod()' ), ), 'errors': empty() } ) } } ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/signature_help_test.py������������������������������0000664�0000000�0000000�00000023036�13746324601�0025204�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_exactly, empty, equal_to, has_entries ) import requests from ycmd.utils import ReadFile from ycmd.tests.python import PathToTestFile, IsolatedYcmd, SharedYcmd from ycmd.tests.test_utils import ( CombineRequest, ParameterMatcher, SignatureMatcher, SignatureAvailableMatcher ) def RunTest( app, test ): """ Method to run a simple signature help test and verify the result test is a dictionary containing: 'request': kwargs for BuildRequest 'expect': { 'response': server response code (e.g. httplib.OK) 'data': matcher for the server response json } """ contents = ReadFile( test[ 'request' ][ 'filepath' ] ) app.post_json( '/event_notification', CombineRequest( test[ 'request' ], { 'event_name': 'FileReadyToParse', 'contents': contents, } ), expect_errors = True ) # We ignore errors here and we check the response code ourself. # This is to allow testing of requests returning errors. response = app.post_json( '/signature_help', CombineRequest( test[ 'request' ], { 'contents': contents } ), expect_errors = True ) assert_that( response.status_code, equal_to( test[ 'expect' ][ 'response' ] ) ) assert_that( response.json, test[ 'expect' ][ 'data' ] ) @SharedYcmd def Signature_Help_Available_test( app ): response = app.get( '/signature_help_available', { 'subserver': 'python' } ).json assert_that( response, SignatureAvailableMatcher( 'YES' ) ) @SharedYcmd def SignatureHelp_MethodTrigger_test( app ): RunTest( app, { 'description': 'Trigger after (', 'request': { 'filetype' : 'python', 'filepath' : PathToTestFile( 'general_fallback', 'lang_python.py' ), 'line_num' : 6, 'column_num': 10, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': contains_exactly( SignatureMatcher( 'def hack( obj )', [ ParameterMatcher( 10, 13 ) ] ) ), } ), } ) } } ) @IsolatedYcmd( { 'disable_signature_help': True } ) def SignatureHelp_MethodTrigger_Disabled_test( app ): RunTest( app, { 'description': 'do not Trigger after (', 'request': { 'filetype' : 'python', 'filepath' : PathToTestFile( 'general_fallback', 'lang_python.py' ), 'line_num' : 6, 'column_num': 10, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': empty(), } ), } ) } } ) @SharedYcmd def SignatureHelp_MultipleParameters_test( app ): RunTest( app, { 'description': 'Trigger after ,', 'request': { 'filetype' : 'python', 'filepath' : PathToTestFile( 'signature_help.py' ), 'line_num' : 14, 'column_num': 50, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 1, 'signatures': contains_exactly( SignatureMatcher( 'def MultipleArguments( a, b, c )', [ ParameterMatcher( 23, 24 ), ParameterMatcher( 26, 27 ), ParameterMatcher( 29, 30 ) ] ) ), } ), } ) } } ) @SharedYcmd def SignatureHelp_CallWithinCall_test( app ): RunTest( app, { 'description': 'Trigger after , within a call-within-a-call', 'request': { 'filetype' : 'python', 'filepath' : PathToTestFile( 'signature_help.py' ), 'line_num' : 14, 'column_num': 43, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 1, 'signatures': contains_exactly( SignatureMatcher( 'def center( width: int, fillchar: str=... )', [ ParameterMatcher( 12, 22 ), ParameterMatcher( 24, 41 ) ] ) ), } ), } ) } } ) @SharedYcmd def SignatureHelp_Constructor_test( app ): RunTest( app, { 'description': 'Trigger after , within a call-within-a-call', 'request': { 'filetype' : 'python', 'filepath' : PathToTestFile( 'signature_help.py' ), 'line_num' : 14, 'column_num': 61, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': contains_exactly( SignatureMatcher( 'class Class( argument )', [ ParameterMatcher( 13, 21 ) ] ) ), } ), } ) } } ) @SharedYcmd def SignatureHelp_MultipleDefinitions_test( app ): RunTest( app, { 'description': 'Jedi returns multiple signatures - both active', 'request': { 'filetype' : 'python', 'filepath' : PathToTestFile( 'signature_help.py' ), 'line_num' : 30, 'column_num': 27, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 2, 'signatures': contains_exactly( SignatureMatcher( 'def MultipleDefinitions( a, b, c )', [ ParameterMatcher( 25, 26 ), ParameterMatcher( 28, 29 ), ParameterMatcher( 31, 32 ) ] ), SignatureMatcher( 'def MultipleDefinitions( many,' ' more,' ' arguments,' ' to,' ' this,' ' one )', [ ParameterMatcher( 25, 29 ), ParameterMatcher( 31, 35 ), ParameterMatcher( 37, 46 ), ParameterMatcher( 48, 50 ), ParameterMatcher( 52, 56 ), ParameterMatcher( 58, 61 ) ] ) ), } ), } ) } } ) @SharedYcmd def SignatureHelp_MultipleDefinitions_OneActive_test( app ): RunTest( app, { 'description': 'Jedi returns multiple signatures - both active', 'request': { 'filetype' : 'python', 'filepath' : PathToTestFile( 'signature_help.py' ), 'line_num' : 30, 'column_num': 30, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 1, 'activeParameter': 3, 'signatures': contains_exactly( SignatureMatcher( 'def MultipleDefinitions( a, b, c )', [ ParameterMatcher( 25, 26 ), ParameterMatcher( 28, 29 ), ParameterMatcher( 31, 32 ) ] ), SignatureMatcher( 'def MultipleDefinitions( many,' ' more,' ' arguments,' ' to,' ' this,' ' one )', [ ParameterMatcher( 25, 29 ), ParameterMatcher( 31, 35 ), ParameterMatcher( 37, 46 ), ParameterMatcher( 48, 50 ), ParameterMatcher( 52, 56 ), ParameterMatcher( 58, 61 ) ] ) ), } ), } ) } } ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/subcommands_test.py���������������������������������0000664�0000000�0000000�00000063615�13746324601�0024515�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2015-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_exactly, contains_inanyorder, equal_to, has_item, has_entries, has_entry, matches_regexp ) from pprint import pformat from unittest.mock import patch import os import pytest import requests from ycmd.utils import ReadFile from ycmd.tests.python import PathToTestFile, SharedYcmd from ycmd.tests.test_utils import ( BuildRequest, CombineRequest, ChunkMatcher, LocationMatcher, ErrorMatcher, ExpectedFailure ) TYPESHED_PATH = os.path.normpath( PathToTestFile( '..', '..', '..', '..', 'third_party', 'jedi_deps', 'jedi', 'jedi', 'third_party', 'typeshed', 'stdlib', '2and3', 'builtins.pyi' ) ) class JediDef: def __init__( self, col = None, line = None, path = None ): self.column = col self.line = line self.module_path = path self.description = '' def RunTest( app, test ): contents = ReadFile( test[ 'request' ][ 'filepath' ] ) # We ignore errors here and check the response code ourself. # This is to allow testing of requests returning errors. response = app.post_json( '/run_completer_command', CombineRequest( test[ 'request' ], { 'contents': contents, 'filetype': 'python', 'command_arguments': ( [ test[ 'request' ][ 'command' ] ] + test[ 'request' ].get( 'arguments', [] ) ) } ), expect_errors = True ) print( f'completer response: { pformat( response.json ) }' ) assert_that( response.status_code, equal_to( test[ 'expect' ][ 'response' ] ) ) assert_that( response.json, test[ 'expect' ][ 'data' ] ) def Subcommands_GoTo( app, test, command ): if isinstance( test[ 'response' ], list ): expect = { 'response': requests.codes.ok, 'data': contains_inanyorder( *[ LocationMatcher( PathToTestFile( 'goto', r[ 0 ] ), r[ 1 ], r[ 2 ] ) for r in test[ 'response' ] ] ) } elif isinstance( test[ 'response' ], tuple ): expect = { 'response': requests.codes.ok, 'data': LocationMatcher( PathToTestFile( 'goto', test[ 'response' ][ 0 ] ), test[ 'response' ][ 1 ], test[ 'response' ][ 2 ] ) } else: expect = { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( RuntimeError, test[ 'response' ] ) } req = test[ 'request' ] RunTest( app, { 'description': command + ' jumps to the right location', 'request': { 'command' : command, 'arguments': [] if len( req ) < 4 else req[ 3 ], 'filetype' : 'python', 'filepath' : PathToTestFile( 'goto', req[ 0 ] ), 'line_num' : req[ 1 ], 'column_num': req[ 2 ] }, 'expect': expect, } ) @pytest.mark.parametrize( 'cmd', [ 'GoTo', 'GoToDefinition', 'GoToDeclaration' ] ) @pytest.mark.parametrize( 'test', [ # Nothing { 'request': ( 'basic.py', 3, 5 ), 'response': 'Can\'t jump to ' 'definition.' }, # Keyword { 'request': ( 'basic.py', 4, 3 ), 'response': 'Can\'t jump to ' 'definition.' }, # Builtin { 'request': ( 'basic.py', 1, 4 ), 'response': ( 'basic.py', 1, 1 ) }, { 'request': ( 'basic.py', 1, 12 ), 'response': ( TYPESHED_PATH, 947, 7 ) }, { 'request': ( 'basic.py', 2, 2 ), 'response': ( 'basic.py', 1, 1 ) }, # Class { 'request': ( 'basic.py', 4, 7 ), 'response': ( 'basic.py', 4, 7 ) }, { 'request': ( 'basic.py', 4, 11 ), 'response': ( 'basic.py', 4, 7 ) }, { 'request': ( 'basic.py', 7, 19 ), 'response': ( 'basic.py', 4, 7 ) }, # Instance { 'request': ( 'basic.py', 7, 1 ), 'response': ( 'basic.py', 7, 1 ) }, { 'request': ( 'basic.py', 7, 11 ), 'response': ( 'basic.py', 7, 1 ) }, { 'request': ( 'basic.py', 8, 23 ), 'response': ( 'basic.py', 7, 1 ) }, # Instance reference { 'request': ( 'basic.py', 8, 1 ), 'response': ( 'basic.py', 8, 1 ) }, { 'request': ( 'basic.py', 8, 5 ), 'response': ( 'basic.py', 8, 1 ) }, { 'request': ( 'basic.py', 9, 12 ), 'response': ( 'basic.py', 8, 1 ) }, # Member access { 'request': ( 'child.py', 4, 12 ), 'response': ( 'parent.py', 2, 7 ) }, # Builtin from different file { 'request': ( 'multifile1.py', 2, 30 ), 'response': ( 'multifile2.py', 1, 24 ) }, { 'request': ( 'multifile1.py', 4, 5 ), 'response': ( 'multifile1.py', 2, 24 ) }, # Function from different file { 'request': ( 'multifile1.py', 1, 24 ), 'response': ( 'multifile3.py', 3, 5 ) }, { 'request': ( 'multifile1.py', 5, 4 ), 'response': ( 'multifile1.py', 1, 24 ) }, # Alias from different file { 'request': ( 'multifile1.py', 2, 47 ), 'response': ( 'multifile2.py', 1, 51 ) }, { 'request': ( 'multifile1.py', 6, 14 ), 'response': ( 'multifile1.py', 2, 36 ) }, # Absolute import from nested module { 'request': ( os.path.join( 'nested_import', 'importer.py' ), 1, 19 ), 'response': ( 'basic.py', 4, 7 ) }, { 'request': ( os.path.join( 'nested_import', 'importer.py' ), 2, 40 ), 'response': ( os.path.join( 'nested_import', 'to_import.py' ), 1, 5 ) }, # Relative within nested module { 'request': ( os.path.join( 'nested_import', 'importer.py' ), 3, 28 ), 'response': ( os.path.join( 'nested_import', 'to_import.py' ), 4, 5 ) }, ] ) @SharedYcmd def Subcommands_GoTo_test( app, cmd, test ): Subcommands_GoTo( app, test, cmd ) @pytest.mark.parametrize( 'test', [ { 'request': ( 'basic.py', 1, 1, [ 'MyClass' ] ), 'response': ( 'basic.py', 4, 7 ) }, { 'request': ( 'basic.py', 1, 1, [ 'class C' ] ), 'response': ( 'child.py', 2, 7 ) }, { 'request': ( 'basic.py', 1, 1, [ 'C.c' ] ), 'response': [ ( 'child.py', 3, 7 ), ( 'parent.py', 3, 7 ) ] }, { 'request': ( 'basic.py', 1, 1, [ 'nothing_here_mate' ] ), 'response': 'Symbol not found' } ] ) @SharedYcmd def Subcommands_GoToSymbol_test( app, test ): Subcommands_GoTo( app, test, 'GoToSymbol' ) @pytest.mark.parametrize( 'test', [ { 'request': ( 'basic.py', 1, 4 ), 'response': 'Can\'t jump to definition.', 'cmd': 'GoTo' }, { 'request': ( 'basic.py', 1, 4 ), 'response': 'Can\'t find references.', 'cmd': 'GoToReferences' }, { 'request': ( 'basic.py', 1, 4 ), 'response': 'Can\'t jump to type definition.', 'cmd': 'GoToType' } ] ) @SharedYcmd def Subcommands_GoTo_SingleInvalidJediDefinition_test( app, test ): with patch( 'ycmd.completers.python.python_completer.jedi.Script.infer', return_value = [ JediDef() ] ): with patch( 'ycmd.completers.python.python_completer.jedi.Script.goto', return_value = [ JediDef() ] ): with patch( 'ycmd.completers.python.python_completer.' 'jedi.Script.get_references', return_value = [ JediDef() ] ): Subcommands_GoTo( app, test, test.pop( 'cmd' ) ) def Subcommands_GetType( app, position, expected_message ): filepath = PathToTestFile( 'GetType.py' ) contents = ReadFile( filepath ) command_data = BuildRequest( filepath = filepath, filetype = 'python', line_num = position[ 0 ], column_num = position[ 1 ], contents = contents, command_arguments = [ 'GetType' ] ) assert_that( app.post_json( '/run_completer_command', command_data ).json, has_entry( 'message', expected_message ) ) @pytest.mark.parametrize( 'position,expected_message', [ ( ( 11, 7 ), 'instance int' ), ( ( 11, 20 ), 'def some_function()' ), ( ( 12, 15 ), 'class SomeClass(*args, **kwargs)' ), ( ( 13, 8 ), 'instance SomeClass' ), ( ( 13, 17 ), 'def SomeMethod(first_param, second_param)' ), ( ( 19, 4 ), matches_regexp( '^(instance str, instance int|' 'instance int, instance str)$' ) ) ] ) @SharedYcmd def Subcommands_GetType_test( app, position, expected_message ): Subcommands_GetType( app, position, expected_message ) @SharedYcmd def Subcommands_GetType_NoTypeInformation_test( app ): filepath = PathToTestFile( 'GetType.py' ) contents = ReadFile( filepath ) command_data = BuildRequest( filepath = filepath, filetype = 'python', line_num = 6, column_num = 3, contents = contents, command_arguments = [ 'GetType' ] ) response = app.post_json( '/run_completer_command', command_data, expect_errors = True ).json assert_that( response, ErrorMatcher( RuntimeError, 'No type information available.' ) ) @SharedYcmd def Subcommands_GetDoc_Method_test( app ): # Testcase1 filepath = PathToTestFile( 'GetDoc.py' ) contents = ReadFile( filepath ) command_data = BuildRequest( filepath = filepath, filetype = 'python', line_num = 17, column_num = 9, contents = contents, command_arguments = [ 'GetDoc' ] ) assert_that( app.post_json( '/run_completer_command', command_data ).json, has_entry( 'detailed_info', '_ModuleMethod()\n\n' 'Module method docs\n' 'Are dedented, like you might expect' ) ) @SharedYcmd def Subcommands_GetDoc_Class_test( app ): filepath = PathToTestFile( 'GetDoc.py' ) contents = ReadFile( filepath ) command_data = BuildRequest( filepath = filepath, filetype = 'python', line_num = 19, column_num = 6, contents = contents, command_arguments = [ 'GetDoc' ] ) response = app.post_json( '/run_completer_command', command_data ).json assert_that( response, has_entry( 'detailed_info', 'TestClass()\n\nClass Documentation', ) ) @SharedYcmd def Subcommands_GetDoc_WhitespaceOnly_test( app ): filepath = PathToTestFile( 'GetDoc.py' ) contents = ReadFile( filepath ) command_data = BuildRequest( filepath = filepath, filetype = 'python', line_num = 27, column_num = 10, contents = contents, command_arguments = [ 'GetDoc' ] ) response = app.post_json( '/run_completer_command', command_data, expect_errors = True ).json assert_that( response, ErrorMatcher( RuntimeError, 'No documentation available.' ) ) @SharedYcmd def Subcommands_GetDoc_NoDocumentation_test( app ): filepath = PathToTestFile( 'GetDoc.py' ) contents = ReadFile( filepath ) command_data = BuildRequest( filepath = filepath, filetype = 'python', line_num = 8, column_num = 23, contents = contents, command_arguments = [ 'GetDoc' ] ) response = app.post_json( '/run_completer_command', command_data, expect_errors = True ).json assert_that( response, ErrorMatcher( RuntimeError, 'No documentation available.' ) ) @pytest.mark.parametrize( 'test', [ { 'request': ( 'basic.py', 2, 1 ), 'response': ( TYPESHED_PATH, 947, 7 ) }, { 'request': ( 'basic.py', 8, 1 ), 'response': ( 'basic.py', 4, 7 ) }, { 'request': ( 'basic.py', 3, 1 ), 'response': 'Can\'t jump to type definition.' }, ] ) @SharedYcmd def Subcommands_GoToType_test( app, test ): Subcommands_GoTo( app, test, 'GoToType' ) @SharedYcmd def Subcommands_GoToReferences_Function_test( app ): filepath = PathToTestFile( 'goto', 'references.py' ) contents = ReadFile( filepath ) command_data = BuildRequest( filepath = filepath, filetype = 'python', line_num = 4, column_num = 5, contents = contents, command_arguments = [ 'GoToReferences' ] ) assert_that( app.post_json( '/run_completer_command', command_data ).json, contains_exactly( has_entries( { 'filepath': filepath, 'line_num': 1, 'column_num': 5, 'description': 'def f' } ), has_entries( { 'filepath': filepath, 'line_num': 4, 'column_num': 5, 'description': 'f' } ), has_entries( { 'filepath': filepath, 'line_num': 5, 'column_num': 5, 'description': 'f' } ), has_entries( { 'filepath': filepath, 'line_num': 6, 'column_num': 5, 'description': 'f' } ) ) ) @SharedYcmd def Subcommands_GoToReferences_Builtin_test( app ): filepath = PathToTestFile( 'goto', 'references.py' ) contents = ReadFile( filepath ) command_data = BuildRequest( filepath = filepath, filetype = 'python', line_num = 8, column_num = 1, contents = contents, command_arguments = [ 'GoToReferences' ] ) assert_that( app.post_json( '/run_completer_command', command_data ).json, has_item( has_entries( { 'filepath': filepath, 'line_num': 8, 'column_num': 1, 'description': 'str' } ) ) ) @SharedYcmd def Subcommands_GoToReferences_NoReferences_test( app ): filepath = PathToTestFile( 'goto', 'references.py' ) contents = ReadFile( filepath ) command_data = BuildRequest( filepath = filepath, filetype = 'python', line_num = 2, column_num = 5, contents = contents, command_arguments = [ 'GoToReferences' ] ) response = app.post_json( '/run_completer_command', command_data, expect_errors = True ).json assert_that( response, ErrorMatcher( RuntimeError, 'Can\'t find references.' ) ) @SharedYcmd def Subcommands_GoToReferences_InvalidJediReferences_test( app ): with patch( 'ycmd.completers.python.python_completer.' 'jedi.Script.get_references', return_value = [ JediDef(), JediDef( 1, 1, PathToTestFile( 'foo.py' ) ) ] ): filepath = PathToTestFile( 'goto', 'references.py' ) contents = ReadFile( filepath ) command_data = BuildRequest( filepath = filepath, filetype = 'python', line_num = 2, column_num = 5, contents = contents, command_arguments = [ 'GoToReferences' ] ) response = app.post_json( '/run_completer_command', command_data, expect_errors = True ).json assert_that( response, contains_exactly( has_entries( { 'line_num': 1, 'column_num': 2, # Jedi columns are 0 based 'filepath': PathToTestFile( 'foo.py' ) } ) ) ) @SharedYcmd def Subcommands_RefactorRename_NoNewName_test( app ): filepath = PathToTestFile( 'basic.py' ) contents = ReadFile( filepath ) command_data = BuildRequest( filepath = filepath, filetype = 'python', line_num = 3, column_num = 10, contents = contents, command_arguments = [ 'RefactorRename' ] ) response = app.post_json( '/run_completer_command', command_data, expect_errors = True ) assert_that( response.status_code, equal_to( requests.codes.internal_server_error ) ) assert_that( response.json, ErrorMatcher( RuntimeError, 'Must specify a new name' ) ) @SharedYcmd def Subcommands_RefactorRename_Same_test( app ): filepath = PathToTestFile( 'basic.py' ) contents = ReadFile( filepath ) command_data = BuildRequest( filepath = filepath, filetype = 'python', line_num = 3, column_num = 10, contents = contents, command_arguments = [ 'RefactorRename', 'c' ] ) response = app.post_json( '/run_completer_command', command_data ).json assert_that( response, has_entries( { 'fixits': contains_exactly( has_entries( { 'text': '', 'chunks': contains_exactly( ChunkMatcher( 'c', LocationMatcher( filepath, 3, 10 ), LocationMatcher( filepath, 3, 11 ) ), ChunkMatcher( 'c', LocationMatcher( filepath, 7, 3 ), LocationMatcher( filepath, 7, 4 ) ) ) } ) ) } ) ) @SharedYcmd def Subcommands_RefactorRename_Longer_test( app ): filepath = PathToTestFile( 'basic.py' ) contents = ReadFile( filepath ) command_data = BuildRequest( filepath = filepath, filetype = 'python', line_num = 3, column_num = 10, contents = contents, command_arguments = [ 'RefactorRename', 'booo' ] ) response = app.post_json( '/run_completer_command', command_data ).json assert_that( response, has_entries( { 'fixits': contains_exactly( has_entries( { 'text': '', 'chunks': contains_exactly( ChunkMatcher( 'booo', LocationMatcher( filepath, 3, 10 ), LocationMatcher( filepath, 3, 11 ) ), ChunkMatcher( 'booo', LocationMatcher( filepath, 7, 3 ), LocationMatcher( filepath, 7, 4 ) ) ) } ) ) } ) ) @SharedYcmd def Subcommands_RefactorRename_ShortenDelete_test( app ): filepath = PathToTestFile( 'basic.py' ) contents = ReadFile( filepath ) command_data = BuildRequest( filepath = filepath, filetype = 'python', line_num = 1, column_num = 8, contents = contents, command_arguments = [ 'RefactorRename', 'F' ] ) response = app.post_json( '/run_completer_command', command_data ).json assert_that( response, has_entries( { 'fixits': contains_exactly( has_entries( { 'text': '', 'chunks': contains_exactly( ChunkMatcher( '', LocationMatcher( filepath, 1, 8 ), LocationMatcher( filepath, 1, 10 ) ), ChunkMatcher( '', LocationMatcher( filepath, 6, 6 ), LocationMatcher( filepath, 6, 8 ) ) ) } ) ) } ) ) @SharedYcmd def Subcommands_RefactorRename_Shorten_test( app ): filepath = PathToTestFile( 'basic.py' ) contents = ReadFile( filepath ) command_data = BuildRequest( filepath = filepath, filetype = 'python', line_num = 1, column_num = 8, contents = contents, command_arguments = [ 'RefactorRename', 'G' ] ) response = app.post_json( '/run_completer_command', command_data ).json assert_that( response, has_entries( { 'fixits': contains_exactly( has_entries( { 'text': '', 'chunks': contains_exactly( ChunkMatcher( 'G', LocationMatcher( filepath, 1, 7 ), LocationMatcher( filepath, 1, 10 ) ), ChunkMatcher( 'G', LocationMatcher( filepath, 6, 5 ), LocationMatcher( filepath, 6, 8 ) ) ) } ) ) } ) ) @SharedYcmd def Subcommands_RefactorRename_StartOfFile_test( app ): one = PathToTestFile( 'rename', 'one.py' ) contents = ReadFile( one ) command_data = BuildRequest( filepath = one, filetype = 'python', line_num = 8, column_num = 44, contents = contents, command_arguments = [ 'RefactorRename', 'myvariable' ] ) response = app.post_json( '/run_completer_command', command_data ).json assert_that( response, has_entries( { 'fixits': contains_exactly( has_entries( { 'text': '', 'chunks': contains_exactly( ChunkMatcher( 'myvariable', LocationMatcher( one, 1, 1 ), LocationMatcher( one, 1, 13 ) ), ChunkMatcher( 'myvariable', LocationMatcher( one, 8, 33 ), LocationMatcher( one, 8, 45 ) ), ChunkMatcher( 'myvariable', LocationMatcher( one, 16, 32 ), LocationMatcher( one, 16, 44 ) ) ) } ) ) } ) ) @SharedYcmd def Subcommands_RefactorRename_MultiFIle_test( app ): one = PathToTestFile( 'rename', 'one.py' ) two = PathToTestFile( 'rename', 'two.py' ) contents = ReadFile( one ) command_data = BuildRequest( filepath = one, filetype = 'python', line_num = 4, column_num = 7, contents = contents, command_arguments = [ 'RefactorRename', 'OneLove' ] ) response = app.post_json( '/run_completer_command', command_data ).json assert_that( response, has_entries( { 'fixits': contains_exactly( has_entries( { 'text': '', 'chunks': contains_exactly( ChunkMatcher( 'eLov', LocationMatcher( one, 4, 9 ), LocationMatcher( one, 4, 9 ) ), ChunkMatcher( 'eLov', LocationMatcher( one, 9, 24 ), LocationMatcher( one, 9, 24 ) ), ChunkMatcher( 'Love', LocationMatcher( one, 16, 15 ), LocationMatcher( one, 16, 15 ) ), ChunkMatcher( 'eLov', LocationMatcher( two, 4, 18 ), LocationMatcher( two, 4, 18 ) ), ChunkMatcher( 'Love', LocationMatcher( two, 11, 14 ), LocationMatcher( two, 11, 14 ) ) ) } ) ) } ) ) @ExpectedFailure( 'file renames not implemented yet' ) @SharedYcmd def Subcommands_RefactorRename_Module_test( app ): one = PathToTestFile( 'rename', 'one.py' ) two = PathToTestFile( 'rename', 'two.py' ) contents = ReadFile( two ) command_data = BuildRequest( filepath = two, filetype = 'python', line_num = 1, column_num = 8, contents = contents, command_arguments = [ 'RefactorRename', 'pfivr' ] ) response = app.post_json( '/run_completer_command', command_data ).json assert_that( response, has_entries( { 'fixits': contains_exactly( has_entries( { 'text': '', 'chunks': contains_exactly( ChunkMatcher( 'pfivr', LocationMatcher( two, 1, 8 ), LocationMatcher( two, 1, 11 ) ), ChunkMatcher( 'pfivr', LocationMatcher( two, 4, 12 ), LocationMatcher( two, 4, 15 ) ) ), 'files': contains_exactly( has_entries( { 'operation': 'RENAME', 'old_file': one, 'new_file': PathToTestFile( 'rename', 'pfivr.py' ) } ) ) } ) ) } ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/�������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0022367�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/GetDoc.py����������������������������������0000664�0000000�0000000�00000000633�13746324601�0024110�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ class TestClass: """ Class Documentation""" def TestMethod(self): """ Method Documentation """ return self.member_variable def _ModuleMethod(): """ Module method docs Are dedented, like you might expect""" pass _ModuleMethod() tc = TestClass() tc.TestMethod() class BoringClass: def __init__( self ): self._buffers = None def BoringMethod( self ): self._buffers = [] �����������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/GetType.py���������������������������������0000664�0000000�0000000�00000000525�13746324601�0024324�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������def some_function(): return 123 class SomeClass(): def __init__(self, *args, **kwargs): pass def SomeMethod(self, first_param, second_param): pass some_integer = some_function() some_class = SomeClass() some_class.SomeMethod() if condition: variable = 'test' else: variable = some_function() variable ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/basic.py�����������������������������������0000664�0000000�0000000�00000000126�13746324601�0024021�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������class Foo(object): def __init__(self): self.a = 1 self.b = 2 f = Foo() f.a ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/general_fallback/��������������������������0000775�0000000�0000000�00000000000�13746324601�0025623�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/general_fallback/lang_python.py������������0000664�0000000�0000000�00000001754�13746324601�0030526�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������class DoSomething: def __init__(self): self.this_is_well_known = 'too easy' # Jedi is smart enough to spot self.a_parameter = 1, but not if we just hack # it in some other method hack(self) pass def hack( obj ): obj.a_parameter = 'secret' def a_method( abc, **kwargs ): print abc obj.another_parameter = a_method def Main(): a_thing = DoSomething() # TESTCASE1: param jedi knows about # 1 2 3 4 #234567890123456789012345678901234567890123456789 print a_thing.this_is_well_known # TESTCASE2: param jedi does not know about # 1 2 3 4 #234567890123456789012345678901234567890123456789 print a_thing.a_parameter # TESTCASE3: method jedi does not know about # 1 2 3 4 #234567890123456789012345678901234567890123456789 a_thing.another_parameter( 'test' ) # to ensure we can run this script to test the code actually makes sense! if __name__ == "__main__": Main() ��������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/goto/��������������������������������������0000775�0000000�0000000�00000000000�13746324601�0023337�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/goto/basic.py������������������������������0000664�0000000�0000000�00000000167�13746324601�0024776�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������my_dict = dict() my_dict class MyClass(): pass my_instance = MyClass() same_instance = my_instance same_instance ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/goto/child.py������������������������������0000664�0000000�0000000�00000000111�13746324601�0024765�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from parent import P class C( P ): def c( self ): self.from_base() �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/goto/multifile1.py�������������������������0000775�0000000�0000000�00000000202�13746324601�0025761�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from multifile3 import my_function from multifile2 import my_integer, my_function_alias my_integer my_function my_function_alias ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/goto/multifile2.py�������������������������0000775�0000000�0000000�00000000104�13746324601�0025763�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from multifile3 import my_integer, my_function as my_function_alias ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/goto/multifile3.py�������������������������0000775�0000000�0000000�00000000054�13746324601�0025770�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������my_integer = 1 def my_function(): pass ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/goto/nested_import/������������������������0000775�0000000�0000000�00000000000�13746324601�0026213�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/goto/nested_import/importer.py�������������0000664�0000000�0000000�00000000150�13746324601�0030422�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from basic import MyClass from nested_import.to_import import import_me from .to_import import relative ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/goto/nested_import/to_import.py������������0000664�0000000�0000000�00000000064�13746324601�0030601�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������def import_me(): pass def relative(): pass ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/goto/parent.py�����������������������������0000664�0000000�0000000�00000000075�13746324601�0025204�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������class P: def from_base( self ): pass def c( self ): pass �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/goto/references.py�������������������������0000664�0000000�0000000�00000000056�13746324601�0026033�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������def f(): pass a = f() b = f() c = f() str ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/goto/requirements.txt����������������������0000664�0000000�0000000�00000000115�13746324601�0026620�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# required so jedi understands this is the project root for absolute imports ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/numpy_docstring.py�������������������������0000664�0000000�0000000�00000000265�13746324601�0026170�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������class SomeClass(): def SomeMethod(self): pass def some_function(some_param): """ Parameters ---------- some_param : SomeClass """ some_param.sm �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/project/�����������������������������������0000775�0000000�0000000�00000000000�13746324601�0024035�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/project/__main__.py������������������������0000664�0000000�0000000�00000000027�13746324601�0026126�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import module module. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/project/client_data_extra_conf.py����������0000664�0000000�0000000�00000000163�13746324601�0031066�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import os def Settings( **kwargs ): return { 'sys_path': kwargs[ 'client_data' ].get( 'sys_path', [] ) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/project/empty_extra_conf.py����������������0000664�0000000�0000000�00000000000�13746324601�0027743�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/project/invalid_python_extra_conf.py�������0000664�0000000�0000000�00000000130�13746324601�0031640�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������def Settings( **kwargs ): return { 'interpreter_path': '/non/existing/python' } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/project/settings_empty_extra_conf.py�������0000664�0000000�0000000�00000000046�13746324601�0031675�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������def Settings( **kwargs ): return {} ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/project/settings_extra_conf.py�������������0000664�0000000�0000000�00000000363�13746324601�0030461�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import os import sys DIR_OF_THIS_SCRIPT = os.path.abspath( os.path.dirname( __file__ ) ) def Settings( **kwargs ): return { 'interpreter_path': sys.executable, 'sys_path': [ os.path.join( DIR_OF_THIS_SCRIPT, 'third_party' ) ] } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/project/settings_none_extra_conf.py��������0000664�0000000�0000000�00000000041�13746324601�0031471�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������def Settings( **kwargs ): pass �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/project/sys_path_extra_conf.py�������������0000664�0000000�0000000�00000000371�13746324601�0030452�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import os import sys DIR_OF_THIS_SCRIPT = os.path.abspath( os.path.dirname( __file__ ) ) def PythonSysPath( **kwargs ): sys_path = kwargs[ 'sys_path' ] sys_path.insert( 0, os.path.join( DIR_OF_THIS_SCRIPT, 'third_party' ) ) return sys_path �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/project/third_party/�����������������������0000775�0000000�0000000�00000000000�13746324601�0026366�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/project/third_party/module.py��������������0000664�0000000�0000000�00000000022�13746324601�0030217�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������SOME_CONSTANT = 1 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/rename/������������������������������������0000775�0000000�0000000�00000000000�13746324601�0023636�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/rename/__init__.py�������������������������0000664�0000000�0000000�00000000000�13746324601�0025735�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/rename/one.py������������������������������0000664�0000000�0000000�00000000547�13746324601�0024777�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������MODULE_SCOPE = 'module' class One: ClassVariable = 'one' def __init__( self, argument ): self.argument_ = argument + MODULE_SCOPE self.variable_ = One.ClassVariable if not argument else self.argument_ def InstanceMethod( self ): return self.variable_ @classmethod def ClassMethod( cls ): return One.ClassVariable + MODULE_SCOPE ���������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/rename/requirements.txt��������������������0000664�0000000�0000000�00000000000�13746324601�0027110�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/rename/two.py������������������������������0000664�0000000�0000000�00000000246�13746324601�0025023�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import one class Two( one.One ): def __init__( self, argument ): super().__init__( argument ) self.variable_ = 1 def AtTheEndOfTheFile(): x = one.One ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/signature_help.py��������������������������0000664�0000000�0000000�00000001134�13746324601�0025751�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������def MultipleArguments( a, b, c ): pass def KeywordArguments( d, e, *args, **kwargs ): pass class Class: def __init__( self, argument ): self.Method() self.MultipleArgumentsMethod( argument ) KeywordArguments( 1, 2, 4, 5, test=6 ) MultipleArguments( 'test'.center( 100, ' ' ), 10, Class( 10 ) ) def Method( self ): pass def MultipleArgumentsMethod( self, this_is_an_argument ): pass if something: def MultipleDefinitions( many, more, arguments, to, this, one ): pass else: def MultipleDefinitions( a, b, c ): pass MultipleDefinitions( 1, 2, 3, 4, ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python/testdata/unicode.py���������������������������������0000664�0000000�0000000�00000000065�13746324601�0024370�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������def Foo(): """aafäö""" pass Fo x = '†'.cen ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/python_support_test.py�������������������������������������0000664�0000000�0000000�00000006411�13746324601�0023765�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import os from hamcrest import assert_that, equal_to from ycmd.tests.test_utils import ClangOnly from ycmd.utils import ToBytes, OnWindows, ImportCore ycm_core = ImportCore() # We don't use PathToTestFile from test_utils module because this module # imports future modules that may change the path type. PATH_TO_TESTDATA = os.path.abspath( os.path.join( os.path.dirname( __file__ ), 'testdata' ) ) PATH_TO_COMPILE_COMMANDS = ( os.path.join( PATH_TO_TESTDATA, 'windows' ) if OnWindows() else os.path.join( PATH_TO_TESTDATA, 'unix' ) ) COMPILE_COMMANDS_WORKING_DIR = 'C:\\dir' if OnWindows() else '/dir' def GetUtf8String_Str_test(): assert_that( b'fo\xc3\xb8', equal_to( ycm_core.GetUtf8String( 'foø' ) ) ) def GetUtf8String_Bytes_test(): assert_that( b'fo\xc3\xb8', equal_to( ycm_core.GetUtf8String( bytes( 'foø', 'utf8' ) ) ) ) def GetUtf8String_Int_test(): assert_that( b'123', equal_to( ycm_core.GetUtf8String( 123 ) ) ) @ClangOnly def CompilationDatabase_Py3Bytes_test(): cc_dir = ToBytes( PATH_TO_COMPILE_COMMANDS ) cc_filename = ToBytes( os.path.join( COMPILE_COMMANDS_WORKING_DIR, 'example.cc' ) ) # Ctor reads ycmd/tests/testdata/[unix|windows]/compile_commands.json db = ycm_core.CompilationDatabase( cc_dir ) info = db.GetCompilationInfoForFile( cc_filename ) assert_that( str( info.compiler_working_dir_ ), equal_to( COMPILE_COMMANDS_WORKING_DIR ) ) assert_that( str( info.compiler_flags_[ 0 ] ), equal_to( '/usr/bin/clang++' ) ) assert_that( str( info.compiler_flags_[ 1 ] ), equal_to( '--driver-mode=g++' ) ) assert_that( str( info.compiler_flags_[ 2 ] ), equal_to( 'example.cc' ) ) @ClangOnly def CompilationDatabase_NativeString_test(): cc_dir = PATH_TO_COMPILE_COMMANDS cc_filename = os.path.join( COMPILE_COMMANDS_WORKING_DIR, 'example.cc' ) # Ctor reads ycmd/tests/testdata/[unix|windows]/compile_commands.json db = ycm_core.CompilationDatabase( cc_dir ) info = db.GetCompilationInfoForFile( cc_filename ) assert_that( str( info.compiler_working_dir_ ), equal_to( COMPILE_COMMANDS_WORKING_DIR ) ) assert_that( str( info.compiler_flags_[ 0 ] ), equal_to( '/usr/bin/clang++' ) ) assert_that( str( info.compiler_flags_[ 1 ] ), equal_to( '--driver-mode=g++' ) ) assert_that( str( info.compiler_flags_[ 2 ] ), equal_to( 'example.cc' ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/request_validation_test.py���������������������������������0000664�0000000�0000000�00000006011�13746324601�0024546�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from hamcrest import raises, assert_that, calling from ycmd.request_validation import EnsureRequestValid from ycmd.responses import ServerError def BasicData(): return { 'line_num': 1, 'column_num': 2, 'filepath': '/foo', 'file_data': { '/foo': { 'filetypes': [ 'text' ], 'contents': 'zoobar' } } } def EnsureRequestValid_AllOk_test(): assert_that( EnsureRequestValid( BasicData() ) ) def EnsureRequestValid_MissingLineNum_test(): data = BasicData() del data[ 'line_num' ] assert_that( calling( EnsureRequestValid ).with_args( data ), raises( ServerError, ".*line_num.*" ) ) def EnsureRequestValid_MissingColumnNum_test(): data = BasicData() del data[ 'column_num' ] assert_that( calling( EnsureRequestValid ).with_args( data ), raises( ServerError, ".*column_num.*" ) ) def EnsureRequestValid_MissingFilepath_test(): data = BasicData() del data[ 'filepath' ] assert_that( calling( EnsureRequestValid ).with_args( data ), raises( ServerError, ".*filepath.*" ) ) def EnsureRequestValid_MissingFileData_test(): data = BasicData() del data[ 'file_data' ] assert_that( calling( EnsureRequestValid ).with_args( data ), raises( ServerError, ".*file_data.*" ) ) def EnsureRequestValid_MissingFileDataContents_test(): data = BasicData() del data[ 'file_data' ][ '/foo' ][ 'contents' ] assert_that( calling( EnsureRequestValid ).with_args( data ), raises( ServerError, ".*contents.*" ) ) def EnsureRequestValid_MissingFileDataFiletypes_test(): data = BasicData() del data[ 'file_data' ][ '/foo' ][ 'filetypes' ] assert_that( calling( EnsureRequestValid ).with_args( data ), raises( ServerError, ".*filetypes.*" ) ) def EnsureRequestValid_EmptyFileDataFiletypes_test(): data = BasicData() del data[ 'file_data' ][ '/foo' ][ 'filetypes' ][ 0 ] assert_that( calling( EnsureRequestValid ).with_args( data ), raises( ServerError, ".*filetypes.*" ) ) def EnsureRequestValid_MissingEntryForFileInFileData_test(): data = BasicData() data[ 'filepath' ] = '/bar' assert_that( calling( EnsureRequestValid ).with_args( data ), raises( ServerError, ".*/bar.*" ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/request_wrap_test.py���������������������������������������0000664�0000000�0000000�00000033530�13746324601�0023373�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import pytest from hamcrest import ( assert_that, calling, contains_exactly, empty, equal_to, has_entry, has_string, raises ) from ycmd.utils import ToBytes from ycmd.request_wrap import RequestWrap def PrepareJson( contents = '', line_num = 1, column_num = 1, filetype = '', force_semantic = None, extra_conf_data = None ): message = { 'line_num': line_num, 'column_num': column_num, 'filepath': '/foo', 'file_data': { '/foo': { 'filetypes': [ filetype ], 'contents': contents } } } if force_semantic is not None: message[ 'force_semantic' ] = force_semantic if extra_conf_data is not None: message[ 'extra_conf_data' ] = extra_conf_data return message @pytest.mark.parametrize( 'line,col,prefix', [ ( 'abc.def', 5, 'abc.' ), ( 'abc.def', 6, 'abc.' ), ( 'abc.def', 8, 'abc.' ), ( 'abc.def', 4, '' ), ( 'abc.', 5, 'abc.' ), ( 'abc.', 4, '' ), ( '', 1, '' ), ] ) def Prefix_test( line, col, prefix ): assert_that( prefix, equal_to( RequestWrap( PrepareJson( line_num = 1, contents = line, column_num = col ) )[ 'prefix' ] ) ) def LineValue_OneLine_test(): assert_that( 'zoo', equal_to( RequestWrap( PrepareJson( line_num = 1, contents = 'zoo' ) )[ 'line_value' ] ) ) def LineValue_LastLine_test(): assert_that( 'zoo', equal_to( RequestWrap( PrepareJson( line_num = 3, contents = 'goo\nbar\nzoo' ) )[ 'line_value' ] ) ) def LineValue_MiddleLine_test(): assert_that( 'zoo', equal_to( RequestWrap( PrepareJson( line_num = 2, contents = 'goo\nzoo\nbar' ) )[ 'line_value' ] ) ) def LineValue_WindowsLines_test(): assert_that( 'zoo', equal_to( RequestWrap( PrepareJson( line_num = 3, contents = 'goo\r\nbar\r\nzoo' ) )[ 'line_value' ] ) ) def LineValue_MixedFormatLines_test(): assert_that( 'zoo', equal_to( RequestWrap( PrepareJson( line_num = 3, contents = 'goo\nbar\r\nzoo' ) )[ 'line_value' ] ) ) def LineValue_EmptyContents_test(): assert_that( '', equal_to( RequestWrap( PrepareJson( line_num = 1, contents = '' ) )[ 'line_value' ] ) ) def StartColumn_RightAfterDot_test(): assert_that( 5, equal_to( RequestWrap( PrepareJson( column_num = 5, contents = 'foo.' ) )[ 'start_column' ] ) ) def StartColumn_Dot_test(): assert_that( 5, equal_to( RequestWrap( PrepareJson( column_num = 8, contents = 'foo.bar' ) )[ 'start_column' ] ) ) def StartColumn_DotWithUnicode_test(): assert_that( 7, equal_to( RequestWrap( PrepareJson( column_num = 11, contents = 'fäö.bär' ) )[ 'start_column' ] ) ) def StartColumn_UnicodeNotIdentifier_test(): contents = "var x = '†es†ing'." # † not considered an identifier character for i in range( 13, 15 ): print( ToBytes( contents )[ i - 1 : i ] ) assert_that( 13, equal_to( RequestWrap( PrepareJson( column_num = i, contents = contents ) )[ 'start_column' ] ) ) assert_that( 13, equal_to( RequestWrap( PrepareJson( column_num = 15, contents = contents ) )[ 'start_column' ] ) ) for i in range( 18, 20 ): print( ToBytes( contents )[ i - 1 : i ] ) assert_that( 18, equal_to( RequestWrap( PrepareJson( column_num = i, contents = contents ) )[ 'start_column' ] ) ) def StartColumn_QueryIsUnicode_test(): contents = "var x = ålpha.alphå" assert_that( 16, equal_to( RequestWrap( PrepareJson( column_num = 16, contents = contents ) )[ 'start_column' ] ) ) assert_that( 16, equal_to( RequestWrap( PrepareJson( column_num = 19, contents = contents ) )[ 'start_column' ] ) ) def StartColumn_QueryStartsWithUnicode_test(): contents = "var x = ålpha.ålpha" assert_that( 16, equal_to( RequestWrap( PrepareJson( column_num = 16, contents = contents ) )[ 'start_column' ] ) ) assert_that( 16, equal_to( RequestWrap( PrepareJson( column_num = 19, contents = contents ) )[ 'start_column' ] ) ) def StartColumn_ThreeByteUnicode_test(): contents = "var x = '†'." assert_that( 15, equal_to( RequestWrap( PrepareJson( column_num = 15, contents = contents ) )[ 'start_column' ] ) ) def StartColumn_Paren_test(): assert_that( 5, equal_to( RequestWrap( PrepareJson( column_num = 8, contents = 'foo(bar' ) )[ 'start_column' ] ) ) def StartColumn_AfterWholeWord_test(): assert_that( 1, equal_to( RequestWrap( PrepareJson( column_num = 7, contents = 'foobar' ) )[ 'start_column' ] ) ) def StartColumn_AfterWholeWord_Html_test(): assert_that( 1, equal_to( RequestWrap( PrepareJson( column_num = 7, filetype = 'html', contents = 'fo-bar' ) )[ 'start_column' ] ) ) def StartColumn_AfterWholeUnicodeWord_test(): assert_that( 1, equal_to( RequestWrap( PrepareJson( column_num = 6, contents = u'fäö' ) )[ 'start_column' ] ) ) def StartColumn_InMiddleOfWholeWord_test(): assert_that( 1, equal_to( RequestWrap( PrepareJson( column_num = 4, contents = 'foobar' ) )[ 'start_column' ] ) ) def StartColumn_ColumnOne_test(): assert_that( 1, equal_to( RequestWrap( PrepareJson( column_num = 1, contents = 'foobar' ) )[ 'start_column' ] ) ) def Query_AtWordEnd_test(): assert_that( 'foo', equal_to( RequestWrap( PrepareJson( column_num = 4, contents = 'foo' ) )[ 'query' ] ) ) def Query_InWordMiddle_test(): assert_that( 'foo', equal_to( RequestWrap( PrepareJson( column_num = 4, contents = 'foobar' ) )[ 'query' ] ) ) def Query_StartOfLine_test(): assert_that( '', equal_to( RequestWrap( PrepareJson( column_num = 1, contents = 'foobar' ) )[ 'query' ] ) ) def Query_StopsAtParen_test(): assert_that( 'bar', equal_to( RequestWrap( PrepareJson( column_num = 8, contents = 'foo(bar' ) )[ 'query' ] ) ) def Query_InWhiteSpace_test(): assert_that( '', equal_to( RequestWrap( PrepareJson( column_num = 8, contents = 'foo ' ) )[ 'query' ] ) ) def Query_UnicodeSinglecharInclusive_test(): assert_that( 'ø', equal_to( RequestWrap( PrepareJson( column_num = 7, contents = 'abc.ø' ) )[ 'query' ] ) ) def Query_UnicodeSinglecharExclusive_test(): assert_that( '', equal_to( RequestWrap( PrepareJson( column_num = 5, contents = 'abc.ø' ) )[ 'query' ] ) ) def StartColumn_Set_test(): wrap = RequestWrap( PrepareJson( column_num = 11, contents = 'this \'test', filetype = 'javascript' ) ) assert_that( wrap[ 'start_column' ], equal_to( 7 ) ) assert_that( wrap[ 'start_codepoint' ], equal_to( 7 ) ) assert_that( wrap[ 'query' ], equal_to( "test" ) ) assert_that( wrap[ 'prefix' ], equal_to( "this '" ) ) wrap[ 'start_column' ] = 6 assert_that( wrap[ 'start_column' ], equal_to( 6 ) ) assert_that( wrap[ 'start_codepoint' ], equal_to( 6 ) ) assert_that( wrap[ 'query' ], equal_to( "'test" ) ) assert_that( wrap[ 'prefix' ], equal_to( "this " ) ) def StartColumn_SetUnicode_test(): wrap = RequestWrap( PrepareJson( column_num = 14, contents = '†e߆ \'test', filetype = 'javascript' ) ) assert_that( 7, equal_to( wrap[ 'start_codepoint' ] ) ) assert_that( 12, equal_to( wrap[ 'start_column' ] ) ) assert_that( wrap[ 'query' ], equal_to( "te" ) ) assert_that( wrap[ 'prefix' ], equal_to( "†e߆ \'" ) ) wrap[ 'start_column' ] = 11 assert_that( wrap[ 'start_column' ], equal_to( 11 ) ) assert_that( wrap[ 'start_codepoint' ], equal_to( 6 ) ) assert_that( wrap[ 'query' ], equal_to( "'te" ) ) assert_that( wrap[ 'prefix' ], equal_to( "†e߆ " ) ) def StartCodepoint_Set_test(): wrap = RequestWrap( PrepareJson( column_num = 11, contents = 'this \'test', filetype = 'javascript' ) ) assert_that( wrap[ 'start_column' ], equal_to( 7 ) ) assert_that( wrap[ 'start_codepoint' ], equal_to( 7 ) ) assert_that( wrap[ 'query' ], equal_to( "test" ) ) assert_that( wrap[ 'prefix' ], equal_to( "this '" ) ) wrap[ 'start_codepoint' ] = 6 assert_that( wrap[ 'start_column' ], equal_to( 6 ) ) assert_that( wrap[ 'start_codepoint' ], equal_to( 6 ) ) assert_that( wrap[ 'query' ], equal_to( "'test" ) ) assert_that( wrap[ 'prefix' ], equal_to( "this " ) ) def StartCodepoint_SetUnicode_test(): wrap = RequestWrap( PrepareJson( column_num = 14, contents = '†e߆ \'test', filetype = 'javascript' ) ) assert_that( 7, equal_to( wrap[ 'start_codepoint' ] ) ) assert_that( 12, equal_to( wrap[ 'start_column' ] ) ) assert_that( wrap[ 'query' ], equal_to( "te" ) ) assert_that( wrap[ 'prefix' ], equal_to( "†e߆ \'" ) ) wrap[ 'start_codepoint' ] = 6 assert_that( wrap[ 'start_column' ], equal_to( 11 ) ) assert_that( wrap[ 'start_codepoint' ], equal_to( 6 ) ) assert_that( wrap[ 'query' ], equal_to( "'te" ) ) assert_that( wrap[ 'prefix' ], equal_to( "†e߆ " ) ) def Calculated_SetMethod_test(): assert_that( calling( RequestWrap( PrepareJson() ).__setitem__ ).with_args( 'line_value', '' ), raises( ValueError, 'Key "line_value" is read-only' ) ) def Calculated_SetOperator_test(): # Differs from the above in that it use [] operator rather than __setitem__ # directly. And it uses a different property for extra credit. wrap = RequestWrap( PrepareJson() ) try: wrap[ 'query' ] = 'test' except ValueError as error: assert_that( str( error ), equal_to( 'Key "query" is read-only' ) ) else: raise AssertionError( 'Expected setting "query" to fail' ) def NonCalculated_Set_test(): # Differs from the above in that it use [] operator rather than __setitem__ # directly. And it uses a different property for extra credit. wrap = RequestWrap( PrepareJson() ) try: wrap[ 'column_num' ] = 10 except ValueError as error: assert_that( str( error ), equal_to( 'Key "column_num" is read-only' ) ) else: raise AssertionError( 'Expected setting "column_num" to fail' ) def ForceSemanticCompletion_test(): wrap = RequestWrap( PrepareJson() ) assert_that( wrap[ 'force_semantic' ], equal_to( False ) ) wrap = RequestWrap( PrepareJson( force_semantic = True ) ) assert_that( wrap[ 'force_semantic' ], equal_to( True ) ) wrap = RequestWrap( PrepareJson( force_semantic = 1 ) ) assert_that( wrap[ 'force_semantic' ], equal_to( True ) ) wrap = RequestWrap( PrepareJson( force_semantic = 0 ) ) assert_that( wrap[ 'force_semantic' ], equal_to( False ) ) wrap = RequestWrap( PrepareJson( force_semantic = 'No' ) ) assert_that( wrap[ 'force_semantic' ], equal_to( True ) ) def ExtraConfData_test(): wrap = RequestWrap( PrepareJson() ) assert_that( wrap[ 'extra_conf_data' ], empty() ) wrap = RequestWrap( PrepareJson( extra_conf_data = { 'key': [ 'value' ] } ) ) extra_conf_data = wrap[ 'extra_conf_data' ] assert_that( extra_conf_data, has_entry( 'key', contains_exactly( 'value' ) ) ) assert_that( extra_conf_data, has_string( equal_to( "<HashableDict {'key': ['value']}>" ) ) ) # Check that extra_conf_data can be used as a dictionary's key. assert_that( { extra_conf_data: 'extra conf data' }, has_entry( extra_conf_data, 'extra conf data' ) ) # Check that extra_conf_data's values are immutable. extra_conf_data[ 'key' ].append( 'another_value' ) assert_that( extra_conf_data, has_entry( 'key', contains_exactly( 'value' ) ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/rust/������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0020232�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/rust/__init__.py�������������������������������������������0000664�0000000�0000000�00000001336�13746324601�0022346�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2016-2020 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 <http://www.gnu.org/licenses/>. from ycmd.tests.rust.conftest import * # noqa ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/rust/conftest.py�������������������������������������������0000664�0000000�0000000�00000007472�13746324601�0022443�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import os import pytest from ycmd.tests.test_utils import ( BuildRequest, ClearCompletionsCache, IgnoreExtraConfOutsideTestsFolder, IsolatedApp, SetUpApp, StopCompleterServer, WaitUntilCompleterServerReady ) shared_app = None @pytest.fixture( scope='module', autouse=True ) def set_up_shared_app(): global shared_app shared_app = SetUpApp() with IgnoreExtraConfOutsideTestsFolder(): StartRustCompleterServerInDirectory( shared_app, PathToTestFile( 'common' ) ) yield StopCompleterServer( shared_app, 'rust' ) def StartRustCompleterServerInDirectory( app, directory ): app.post_json( '/event_notification', BuildRequest( filepath = os.path.join( directory, 'src', 'main.rs' ), event_name = 'FileReadyToParse', filetype = 'rust' ) ) WaitUntilCompleterServerReady( app, 'rust' ) @pytest.fixture def app( request ): which = request.param[ 0 ] assert which == 'isolated' or which == 'shared' if which == 'isolated': with IsolatedApp( {} ) as app: yield app StopCompleterServer( app, 'rust' ) else: global shared_app ClearCompletionsCache() with IgnoreExtraConfOutsideTestsFolder(): yield shared_app """Defines a decorator to be attached to tests of this package. This decorator passes the shared ycmd application as a parameter.""" SharedYcmd = pytest.mark.parametrize( # Name of the fixture/function argument 'app', # Fixture parameters, passed to app() as request.param [ ( 'shared', ) ], # Non-empty ids makes fixture parameters visible in pytest verbose output ids = [ '' ], # Execute the fixture, instead of passing parameters directly to the # function argument indirect = True ) """Defines a decorator to be attached to tests of this package. This decorator passes a unique ycmd application as a parameter. It should be used on tests that change the server state in a irreversible way (ex: a semantic subserver is stopped or restarted) or expect a clean state (ex: no semantic subserver started, no .ycm_extra_conf.py loaded, etc). Use the optional parameter |custom_options| to give additional options and/or override the default ones. Example usage: from ycmd.tests.python import IsolatedYcmd @IsolatedYcmd( { 'python_binary_path': '/some/path' } ) def CustomPythonBinaryPath_test( app ): ... """ IsolatedYcmd = pytest.mark.parametrize( # Name of the fixture/function argument 'app', # Fixture parameters, passed to app() as request.param [ ( 'isolated', ) ], # Non-empty ids makes fixture parameters visible in pytest verbose output ids = [ '' ], # Execute the fixture, instead of passing parameters directly to the # function argument indirect = True ) def PathToTestFile( *args ): dir_of_current_script = os.path.dirname( os.path.abspath( __file__ ) ) return os.path.join( dir_of_current_script, 'testdata', *args ) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/rust/debug_info_test.py������������������������������������0000664�0000000�0000000�00000007675�13746324601�0023763�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2016-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_exactly, has_entries, has_entry, instance_of, none ) from unittest.mock import patch from ycmd.tests.rust import ( IsolatedYcmd, PathToTestFile, SharedYcmd, StartRustCompleterServerInDirectory ) from ycmd.tests.test_utils import BuildRequest @SharedYcmd def DebugInfo_RlsVersion_test( app ): request_data = BuildRequest( filetype = 'rust' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'Rust', 'servers': contains_exactly( has_entries( { 'name': 'Rust Language Server', 'is_running': instance_of( bool ), 'executable': contains_exactly( instance_of( str ) ), 'pid': instance_of( int ), 'address': none(), 'port': none(), 'logfiles': contains_exactly( instance_of( str ) ), 'extras': contains_exactly( has_entries( { 'key': 'Server State', 'value': instance_of( str ) } ), has_entries( { 'key': 'Project Directory', 'value': instance_of( str ) } ), has_entries( { 'key': 'Settings', 'value': '{}' } ), has_entries( { 'key': 'Project State', 'value': instance_of( str ) } ), has_entries( { 'key': 'Version', 'value': instance_of( str ) } ), has_entries( { 'key': 'Rust Root', 'value': instance_of( str ) } ) ) } ) ) } ) ) ) @IsolatedYcmd @patch( 'ycmd.completers.rust.rust_completer._GetCommandOutput', return_value = '' ) def DebugInfo_NoRlsVersion_test( get_command_output, app ): StartRustCompleterServerInDirectory( app, PathToTestFile( 'common', 'src' ) ) request_data = BuildRequest( filetype = 'rust' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'Rust', 'servers': contains_exactly( has_entries( { 'name': 'Rust Language Server', 'is_running': instance_of( bool ), 'executable': contains_exactly( instance_of( str ) ), 'pid': instance_of( int ), 'address': none(), 'port': none(), 'logfiles': contains_exactly( instance_of( str ) ), 'extras': contains_exactly( has_entries( { 'key': 'Server State', 'value': instance_of( str ) } ), has_entries( { 'key': 'Project Directory', 'value': instance_of( str ) } ), has_entries( { 'key': 'Settings', 'value': '{}' } ), has_entries( { 'key': 'Project State', 'value': instance_of( str ) } ), has_entries( { 'key': 'Version', 'value': none() } ), has_entries( { 'key': 'Rust Root', 'value': instance_of( str ) } ) ) } ) ) } ) ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/rust/diagnostics_test.py�����������������������������������0000664�0000000�0000000�00000011663�13746324601�0024161�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_exactly, contains_inanyorder, has_entries, has_entry ) from pprint import pformat import json import os from ycmd.tests.rust import ( PathToTestFile, SharedYcmd, StartRustCompleterServerInDirectory ) from ycmd.tests.test_utils import ( BuildRequest, LocationMatcher, PollForMessages, PollForMessagesTimeoutException, RangeMatcher, WaitForDiagnosticsToBeReady, WithRetry ) from ycmd.utils import ReadFile MAIN_FILEPATH = PathToTestFile( 'common', 'src', 'main.rs' ) DIAG_MATCHERS_PER_FILE = { MAIN_FILEPATH: contains_inanyorder( has_entries( { 'kind': 'ERROR', 'text': 'no field `build_` on type `test::Builder`\nunknown field [E0609]', 'location': LocationMatcher( MAIN_FILEPATH, 14, 13 ), 'location_extent': RangeMatcher( MAIN_FILEPATH, ( 14, 13 ), ( 14, 19 ) ), 'ranges': contains_exactly( RangeMatcher( MAIN_FILEPATH, ( 14, 13 ), ( 14, 19 ) ) ), 'fixit_available': False } ) ) } @WithRetry @SharedYcmd def Diagnostics_DetailedDiags_test( app ): filepath = PathToTestFile( 'common', 'src', 'main.rs' ) contents = ReadFile( filepath ) WaitForDiagnosticsToBeReady( app, filepath, contents, 'rust' ) request_data = BuildRequest( contents = contents, filepath = filepath, filetype = 'rust', line_num = 14, column_num = 13 ) results = app.post_json( '/detailed_diagnostic', request_data ).json assert_that( results, has_entry( 'message', 'no field `build_` on type `test::Builder`\nunknown field' ) ) @WithRetry @SharedYcmd def Diagnostics_FileReadyToParse_test( app ): filepath = PathToTestFile( 'common', 'src', 'main.rs' ) contents = ReadFile( filepath ) # It can take a while for the diagnostics to be ready. results = WaitForDiagnosticsToBeReady( app, filepath, contents, 'rust' ) print( f'completer response: { pformat( results ) }' ) assert_that( results, DIAG_MATCHERS_PER_FILE[ filepath ] ) @SharedYcmd def Diagnostics_Poll_test( app ): project_dir = PathToTestFile( 'common' ) filepath = os.path.join( project_dir, 'src', 'main.rs' ) contents = ReadFile( filepath ) StartRustCompleterServerInDirectory( app, project_dir ) # Poll until we receive _all_ the diags asynchronously. to_see = sorted( DIAG_MATCHERS_PER_FILE.keys() ) seen = {} try: for message in PollForMessages( app, { 'filepath': filepath, 'contents': contents, 'filetype': 'rust' } ): print( f'Message { pformat( message ) }' ) if 'diagnostics' in message: if message[ 'diagnostics' ] == []: # Sometimes we get empty diagnostics before the real ones. continue seen[ message[ 'filepath' ] ] = True if message[ 'filepath' ] not in DIAG_MATCHERS_PER_FILE: raise AssertionError( 'Received diagnostics for unexpected file ' f'{ message[ "filepath" ] }. Only expected { to_see }' ) assert_that( message, has_entries( { 'diagnostics': DIAG_MATCHERS_PER_FILE[ message[ 'filepath' ] ], 'filepath': message[ 'filepath' ] } ) ) if sorted( seen.keys() ) == to_see: break # Eventually PollForMessages will throw a timeout exception and we'll fail # if we don't see all of the expected diags. except PollForMessagesTimeoutException as e: raise AssertionError( str( e ) + 'Timed out waiting for full set of diagnostics. ' f'Expected to see diags for { json.dumps( to_see, indent = 2 ) }, ' f'but only saw { json.dumps( sorted( seen.keys() ), indent = 2 ) }.' ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �����������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/rust/get_completions_test.py�������������������������������0000664�0000000�0000000�00000005762�13746324601�0025050�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2015-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_exactly, empty, has_key, is_not ) from pprint import pformat from ycmd.tests.rust import PathToTestFile, SharedYcmd from ycmd.tests.test_utils import ( BuildRequest, CompletionEntryMatcher, WithRetry ) from ycmd.utils import ReadFile @WithRetry @SharedYcmd def GetCompletions_Basic_test( app ): filepath = PathToTestFile( 'common', 'src', 'main.rs' ) contents = ReadFile( filepath ) completion_data = BuildRequest( filepath = filepath, filetype = 'rust', contents = contents, line_num = 14, column_num = 19 ) results = app.post_json( '/completions', completion_data ).json[ 'completions' ] assert_that( results, contains_exactly( CompletionEntryMatcher( 'build_rocket', 'pub fn build_rocket(&self)', { 'detailed_info': 'build_rocket\n\nDo not try at home', 'menu_text': 'build_rocket', 'kind': 'Method' } ), CompletionEntryMatcher( 'build_shuttle', 'pub fn build_shuttle(&self)', { 'detailed_info': 'build_shuttle\n\n', 'menu_text': 'build_shuttle', 'kind': 'Method' } ) ) ) # This completer does not require or support resolve assert_that( results[ 0 ], is_not( has_key( 'resolve' ) ) ) assert_that( results[ 0 ], is_not( has_key( 'item' ) ) ) # So (erroneously) resolving an item returns the item completion_data[ 'resolve' ] = 0 response = app.post_json( '/resolve_completion', completion_data ).json print( f"Resolve resolve: { pformat( response ) }" ) # We can't actually check the result because we don't know what completion # resolve ID 0 actually is (could be anything), so we just check that we get 1 # result, and that there are no errors. assert_that( response[ 'completion' ], is_not( None ) ) assert_that( response[ 'errors' ], empty() ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ��������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/rust/rust_completer_test.py��������������������������������0000664�0000000�0000000�00000004525�13746324601�0024720�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import os from unittest.mock import patch from hamcrest import assert_that, equal_to from ycmd import user_options_store from ycmd.completers.rust.hook import GetCompleter def GetCompleter_RAFound_test(): assert_that( GetCompleter( user_options_store.GetAll() ) ) @patch( 'ycmd.completers.rust.rust_completer.RA_EXECUTABLE', 'does_not_exist' ) def GetCompleter_RANotFound_test( *args ): assert_that( not GetCompleter( user_options_store.GetAll() ) ) @patch( 'ycmd.utils.FindExecutable', wraps = lambda x: x if 'rust-analyzer' in x else None ) @patch( 'os.access', return_value = True ) def GetCompleter_RAFromUserOption_test( *args ): user_options = user_options_store.GetAll().copy( rust_toolchain_root = 'rust-analyzer' ) assert_that( GetCompleter( user_options )._rust_root, equal_to( 'rust-analyzer' ) ) expected = os.path.join( 'rust-analyzer', 'bin', 'rust-analyzer' ) assert_that( GetCompleter( user_options )._ra_path, equal_to( expected ) ) def GetCompleter_InvalidRustRootFromUser_test( *args ): user_options = user_options_store.GetAll().copy( rust_toolchain_root = '/does/not/exist' ) assert_that( not GetCompleter( user_options ) ) @patch( 'ycmd.completers.rust.rust_completer.LOGGER', autospec = True ) def GetCompleter_WarnsAboutOldConfig_test( logger ): user_options = user_options_store.GetAll().copy( rls_binary_path = '/does/not/exist' ) GetCompleter( user_options ) logger.warning.assert_called_with( 'rls_binary_path detected. Did you mean rust_toolchain_root?' ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/rust/server_management_test.py�����������������������������0000664�0000000�0000000�00000010741�13746324601�0025350�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from hamcrest import assert_that, contains_exactly, equal_to, has_entry from unittest.mock import patch from ycmd.completers.language_server.language_server_completer import ( LanguageServerConnectionTimeout ) from ycmd.tests.rust import ( PathToTestFile, IsolatedYcmd, StartRustCompleterServerInDirectory ) from ycmd.tests.test_utils import ( BuildRequest, MockProcessTerminationTimingOut, WaitUntilCompleterServerReady ) def AssertRustCompleterServerIsRunning( app, is_running ): request_data = BuildRequest( filetype = 'rust' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entry( 'servers', contains_exactly( has_entry( 'is_running', is_running ) ) ) ) ) @IsolatedYcmd def ServerManagement_RestartServer_test( app ): filepath = PathToTestFile( 'common', 'src', 'main.rs' ) StartRustCompleterServerInDirectory( app, filepath ) AssertRustCompleterServerIsRunning( app, True ) app.post_json( '/run_completer_command', BuildRequest( filepath = filepath, filetype = 'rust', command_arguments = [ 'RestartServer' ], ), ) WaitUntilCompleterServerReady( app, 'rust' ) AssertRustCompleterServerIsRunning( app, True ) @IsolatedYcmd @patch( 'shutil.rmtree', side_effect = OSError ) @patch( 'ycmd.utils.WaitUntilProcessIsTerminated', MockProcessTerminationTimingOut ) def ServerManagement_CloseServer_Unclean_test( wait_until, app ): StartRustCompleterServerInDirectory( app, PathToTestFile( 'common', 'src' ) ) app.post_json( '/run_completer_command', BuildRequest( filetype = 'rust', command_arguments = [ 'StopServer' ] ) ) request_data = BuildRequest( filetype = 'rust' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entry( 'servers', contains_exactly( has_entry( 'is_running', False ) ) ) ) ) @IsolatedYcmd def ServerManagement_StopServerTwice_test( app ): StartRustCompleterServerInDirectory( app, PathToTestFile( 'common', 'src' ) ) app.post_json( '/run_completer_command', BuildRequest( filetype = 'rust', command_arguments = [ 'StopServer' ], ), ) AssertRustCompleterServerIsRunning( app, False ) # Stopping a stopped server is a no-op app.post_json( '/run_completer_command', BuildRequest( filetype = 'rust', command_arguments = [ 'StopServer' ], ), ) AssertRustCompleterServerIsRunning( app, False ) @IsolatedYcmd def ServerManagement_StartServer_Fails_test( app ): with patch( 'ycmd.completers.language_server.language_server_completer.' 'LanguageServerConnection.AwaitServerConnection', side_effect = LanguageServerConnectionTimeout ): resp = app.post_json( '/event_notification', BuildRequest( event_name = 'FileReadyToParse', filetype = 'rust', filepath = PathToTestFile( 'common', 'src', 'main.rs' ), contents = "" ) ) assert_that( resp.status_code, equal_to( 200 ) ) request_data = BuildRequest( filetype = 'rust' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entry( 'servers', contains_exactly( has_entry( 'is_running', False ) ) ) ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/rust/signature_help_test.py��������������������������������0000664�0000000�0000000�00000010404�13746324601�0024653�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from hamcrest import assert_that, contains_exactly, empty, equal_to, has_entries import requests from ycmd.utils import ReadFile from ycmd.tests.rust import PathToTestFile, SharedYcmd from ycmd.tests.test_utils import ( CombineRequest, SignatureMatcher, SignatureAvailableMatcher, WaitUntilCompleterServerReady, WithRetry ) def RunTest( app, test ): """ Method to run a simple signature help test and verify the result test is a dictionary containing: 'request': kwargs for BuildRequest 'expect': { 'response': server response code (e.g. httplib.OK) 'data': matcher for the server response json } """ contents = ReadFile( test[ 'request' ][ 'filepath' ] ) app.post_json( '/event_notification', CombineRequest( test[ 'request' ], { 'event_name': 'FileReadyToParse', 'contents': contents, } ), expect_errors = True ) # We ignore errors here and we check the response code ourself. # This is to allow testing of requests returning errors. response = app.post_json( '/signature_help', CombineRequest( test[ 'request' ], { 'contents': contents } ), expect_errors = True ) assert_that( response.status_code, equal_to( test[ 'expect' ][ 'response' ] ) ) assert_that( response.json, test[ 'expect' ][ 'data' ] ) @WithRetry @SharedYcmd def SignatureHelp_NoParams_test( app ): RunTest( app, { 'description': 'Trigger after (', 'request': { 'filetype' : 'rust', 'filepath' : PathToTestFile( 'common', 'src', 'test.rs' ), 'line_num' : 14, 'column_num': 14, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': contains_exactly( SignatureMatcher( 'fn sig_test()', [] ) ), } ), } ) } } ) @WithRetry @SharedYcmd def SignatureHelp_MethodTrigger_test( app ): RunTest( app, { 'description': 'Trigger after (', 'request': { 'filetype' : 'rust', 'filepath' : PathToTestFile( 'common', 'src', 'test.rs' ), 'line_num' : 13, 'column_num': 20, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': contains_exactly( SignatureMatcher( 'fn build_rocket(&self)', [] ) ), } ), } ) } } ) @WithRetry @SharedYcmd def Signature_Help_Available_test( app ): request = { 'filepath' : PathToTestFile( 'common', 'src', 'main.rs' ) } app.post_json( '/event_notification', CombineRequest( request, { 'event_name': 'FileReadyToParse', 'filetype': 'rust' } ), expect_errors = True ) WaitUntilCompleterServerReady( app, 'rust' ) response = app.get( '/signature_help_available', { 'subserver': 'rust' } ).json assert_that( response, SignatureAvailableMatcher( 'YES' ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/rust/subcommands_test.py�����������������������������������0000664�0000000�0000000�00000037276�13746324601�0024175�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2015-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_exactly, contains_inanyorder, empty, equal_to, has_entries, has_entry, matches_regexp ) from unittest.mock import patch from pprint import pformat import os import pytest import requests from ycmd import handlers from ycmd.tests.rust import PathToTestFile, SharedYcmd from ycmd.tests.test_utils import ( BuildRequest, ChunkMatcher, ErrorMatcher, ExpectedFailure, LocationMatcher, WithRetry ) from ycmd.utils import ReadFile RESPONSE_TIMEOUT = 5 def RunTest( app, test, contents = None ): if not contents: contents = ReadFile( test[ 'request' ][ 'filepath' ] ) def CombineRequest( request, data ): kw = request request.update( data ) return BuildRequest( **kw ) # Because we aren't testing this command, we *always* ignore errors. This # is mainly because we (may) want to test scenarios where the completer # throws an exception and the easiest way to do that is to throw from # within the FlagsForFile function. app.post_json( '/event_notification', CombineRequest( test[ 'request' ], { 'event_name': 'FileReadyToParse', 'contents': contents, 'filetype': 'rust', } ), expect_errors = True ) # We also ignore errors here, but then we check the response code # ourself. This is to allow testing of requests returning errors. response = app.post_json( '/run_completer_command', CombineRequest( test[ 'request' ], { 'completer_target': 'filetype_default', 'contents': contents, 'filetype': 'rust', 'command_arguments': ( [ test[ 'request' ][ 'command' ] ] + test[ 'request' ].get( 'arguments', [] ) ) } ), expect_errors = True ) print( f'completer response: { pformat( response.json ) }' ) assert_that( response.status_code, equal_to( test[ 'expect' ][ 'response' ] ) ) assert_that( response.json, test[ 'expect' ][ 'data' ] ) @SharedYcmd def Subcommands_DefinedSubcommands_test( app ): subcommands_data = BuildRequest( completer_target = 'rust' ) assert_that( app.post_json( '/defined_subcommands', subcommands_data ).json, contains_inanyorder( 'FixIt', 'Format', 'GetDoc', 'GetType', 'GoTo', 'GoToDeclaration', 'GoToDefinition', 'GoToImplementation', 'GoToReferences', 'GoToSymbol', 'GoToType', 'RefactorRename', 'RestartServer' ) ) @SharedYcmd def Subcommands_ServerNotInitialized_test( app ): filepath = PathToTestFile( 'common', 'src', 'main.rs' ) completer = handlers._server_state.GetFiletypeCompleter( [ 'rust' ] ) @patch.object( completer, '_ServerIsInitialized', return_value = False ) def Test( app, cmd, arguments, *args ): RunTest( app, { 'description': 'Subcommand ' + cmd + ' handles server not ready', 'request': { 'command': cmd, 'line_num': 1, 'column_num': 1, 'filepath': filepath, 'arguments': arguments, }, 'expect': { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( RuntimeError, 'Server is initializing. Please wait.' ), } } ) Test( app, 'Format', [] ) Test( app, 'FixIt', [] ) Test( app, 'GetType', [] ) Test( app, 'GetDoc', [] ) Test( app, 'GoTo', [] ) Test( app, 'GoToDeclaration', [] ) Test( app, 'GoToDefinition', [] ) Test( app, 'GoToImplementation', [] ) Test( app, 'GoToReferences', [] ) Test( app, 'RefactorRename', [ 'test' ] ) @SharedYcmd def Subcommands_Format_WholeFile_test( app ): filepath = PathToTestFile( 'common', 'src', 'main.rs' ) RunTest( app, { 'description': 'Formatting is applied on the whole file', 'request': { 'command': 'Format', 'filepath': filepath, 'options': { 'tab_size': 2, 'insert_spaces': True } }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( # Let's just rewrite the whole file... ChunkMatcher( "mod test;\n\nuse test::*;\n\nstruct Earth {}" "\nstruct Mars {}\ntrait Atmosphere {}\nimpl " "Atmosphere for Earth {}\nimpl Atmosphere for " "Mars {}\n\nfn main() {\n create_universe();" "\n let builder = Builder {};\n builder." "build_\n}\n\nfn format_test() {\n let a: " "i32 = 5;\n}\n", LocationMatcher( filepath, 1, 1 ), LocationMatcher( filepath, 23, 1 ) ), ) } ) ) } ) } } ) @ExpectedFailure( 'rangeFormat is not yet implemented', matches_regexp( '\nExpected: <200>\n but: was <500>\n' ) ) @SharedYcmd def Subcommands_Format_Range_test( app ): filepath = PathToTestFile( 'common', 'src', 'main.rs' ) RunTest( app, { 'description': 'Formatting is applied on some part of the file', 'request': { 'command': 'Format', 'filepath': filepath, 'range': { 'start': { 'line_num': 17, 'column_num': 1, }, 'end': { 'line_num': 22, 'column_num': 2 } }, 'options': { 'tab_size': 4, 'insert_spaces': False } }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( 'fn format_test() {\n' '\tlet a: i32 = 5;\n', LocationMatcher( filepath, 17, 1 ), LocationMatcher( filepath, 22, 1 ) ), ) } ) ) } ) } } ) @SharedYcmd def Subcommands_GetDoc_NoDocumentation_test( app ): RunTest( app, { 'description': 'GetDoc on a function with no documentation ' 'raises an error', 'request': { 'command': 'GetDoc', 'line_num': 4, 'column_num': 11, 'filepath': PathToTestFile( 'common', 'src', 'test.rs' ), }, 'expect': { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( RuntimeError, 'No documentation available.' ) } } ) @WithRetry @SharedYcmd def Subcommands_GetDoc_Function_test( app ): RunTest( app, { 'description': 'GetDoc on a function returns its documentation', 'request': { 'command': 'GetDoc', 'line_num': 2, 'column_num': 8, 'filepath': PathToTestFile( 'common', 'src', 'test.rs' ), }, 'expect': { 'response': requests.codes.ok, 'data': has_entry( 'detailed_info', 'common::test\n' 'pub fn create_universe()\n' '---\n' 'Be careful when using that function' ), } } ) @SharedYcmd def Subcommands_GetType_UnknownType_test( app ): RunTest( app, { 'description': 'GetType on a unknown type raises an error', 'request': { 'command': 'GetType', 'line_num': 2, 'column_num': 4, 'filepath': PathToTestFile( 'common', 'src', 'test.rs' ), }, 'expect': { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( RuntimeError, 'Unknown type.' ) } } ) @WithRetry @SharedYcmd def Subcommands_GetType_Function_test( app ): RunTest( app, { 'description': 'GetType on a function returns its type', 'request': { 'command': 'GetType', 'line_num': 2, 'column_num': 22, 'filepath': PathToTestFile( 'common', 'src', 'test.rs' ), }, 'expect': { 'response': requests.codes.ok, 'data': has_entry( 'message', 'pub fn create_universe()' ), } } ) def RunGoToTest( app, command, test ): folder = PathToTestFile( 'common', 'src' ) filepath = os.path.join( folder, test[ 'req' ][ 0 ] ) request = { 'command': command, 'line_num': test[ 'req' ][ 1 ], 'column_num': test[ 'req' ][ 2 ], 'filepath': filepath, } response = test[ 'res' ] if isinstance( response, list ): expect = { 'response': requests.codes.ok, 'data': contains_inanyorder( *[ LocationMatcher( os.path.join( folder, location[ 0 ] ), location[ 1 ], location[ 2 ] ) for location in response ] ) } elif isinstance( response, tuple ): expect = { 'response': requests.codes.ok, 'data': LocationMatcher( os.path.join( folder, response[ 0 ] ), response[ 1 ], response[ 2 ] ) } else: error_type = test.get( 'exc', RuntimeError ) expect = { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( error_type, test[ 'res' ] ) } RunTest( app, { 'request': request, 'expect' : expect } ) @pytest.mark.parametrize( 'test', [ # Variable { 'req': ( 'main.rs', 14, 5 ), 'res': ( 'test.rs', 4, 12 ) }, # Type { 'req': ( 'main.rs', 13, 19 ), 'res': ( 'test.rs', 4, 12 ) }, # Function { 'req': ( 'main.rs', 12, 14 ), 'res': 'Cannot jump to location' }, # Keyword { 'req': ( 'main.rs', 3, 2 ), 'res': 'Cannot jump to location' }, ] ) @SharedYcmd def Subcommands_GoToType_Basic_test( app, test ): RunGoToTest( app, 'GoToType', test ) @pytest.mark.parametrize( 'test', [ # Structure { 'req': ( 'main.rs', 8, 24 ), 'res': ( 'main.rs', 5, 8 ) }, # Function { 'req': ( 'main.rs', 12, 14 ), 'res': ( 'test.rs', 2, 8 ) }, # Implementation { 'req': ( 'main.rs', 9, 12 ), 'res': ( 'main.rs', 7, 7 ) }, # Keyword { 'req': ( 'main.rs', 3, 2 ), 'res': 'Cannot jump to location' }, ] ) @pytest.mark.parametrize( 'command', [ 'GoToDeclaration', 'GoToDefinition', 'GoTo' ] ) @WithRetry @SharedYcmd def Subcommands_GoTo_test( app, command, test ): RunGoToTest( app, command, test ) @pytest.mark.parametrize( 'test', [ # Structure { 'req': ( 'main.rs', 5, 9 ), 'res': ( 'main.rs', 8, 21 ) }, # Trait { 'req': ( 'main.rs', 7, 7 ), 'res': [ ( 'main.rs', 8, 21 ), ( 'main.rs', 9, 21 ) ] }, ] ) @WithRetry @SharedYcmd def Subcommands_GoToImplementation_test( app, test ): RunGoToTest( app, 'GoToImplementation', test ) @WithRetry @SharedYcmd def Subcommands_GoToImplementation_Failure_test( app ): RunGoToTest( app, 'GoToImplementation', { 'req': ( 'main.rs', 11, 2 ), 'res': 'Cannot jump to location', 'exc': RuntimeError } ) @pytest.mark.parametrize( 'test', [ # Struct { 'req': ( 'main.rs', 9, 22 ), 'res': [ ( 'main.rs', 6, 8 ), ( 'main.rs', 9, 21 ) ] }, # Function { 'req': ( 'main.rs', 12, 8 ), 'res': [ ( 'test.rs', 2, 8 ), ( 'main.rs', 12, 5 ) ] }, # Implementation { 'req': ( 'main.rs', 8, 10 ), 'res': [ ( 'main.rs', 7, 7 ), ( 'main.rs', 8, 6 ), ( 'main.rs', 9, 6 ) ] }, # Keyword { 'req': ( 'main.rs', 1, 1 ), 'res': 'Cannot jump to location' } ] ) @SharedYcmd def Subcommands_GoToReferences_test( app, test ): RunGoToTest( app, 'GoToReferences', test ) @WithRetry @SharedYcmd def Subcommands_RefactorRename_Works_test( app ): main_filepath = PathToTestFile( 'common', 'src', 'main.rs' ) test_filepath = PathToTestFile( 'common', 'src', 'test.rs' ) RunTest( app, { 'description': 'RefactorRename on a function renames all its occurences', 'request': { 'command': 'RefactorRename', 'arguments': [ 'update_universe' ], 'line_num': 12, 'column_num': 16, 'filepath': main_filepath }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'text': '', 'chunks': contains_exactly( ChunkMatcher( 'update_universe', LocationMatcher( test_filepath, 2, 8 ), LocationMatcher( test_filepath, 2, 23 ) ), ChunkMatcher( 'update_universe', LocationMatcher( main_filepath, 12, 5 ), LocationMatcher( main_filepath, 12, 20 ) ), ) } ) ) } ) } } ) @SharedYcmd def Subcommands_RefactorRename_Invalid_test( app ): RunTest( app, { 'description': 'RefactorRename raises an error when cursor is invalid', 'request': { 'command': 'RefactorRename', 'arguments': [ 'update_universe' ], 'line_num': 15, 'column_num': 7, 'filepath': PathToTestFile( 'common', 'src', 'main.rs' ) }, 'expect': { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( RuntimeError, 'Cannot rename the symbol under cursor.' ) } } ) @SharedYcmd def Subcommands_FixIt_EmptyResponse_test( app ): filepath = PathToTestFile( 'common', 'src', 'main.rs' ) RunTest( app, { 'description': 'FixIt on a line with no codeAction returns empty response', 'request': { 'command': 'FixIt', 'line_num': 22, 'column_num': 1, 'filepath': filepath }, 'expect': { 'response': requests.codes.ok, 'data': has_entry( 'fixits', empty() ) } } ) @SharedYcmd def Subcommands_FixIt_Basic_test( app ): filepath = PathToTestFile( 'common', 'src', 'main.rs' ) RunTest( app, { 'description': 'Simple FixIt test', 'request': { 'command': 'FixIt', 'line_num': 17, 'column_num': 2, 'filepath': filepath }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( 'pub(crate) ', LocationMatcher( filepath, 17, 1 ), LocationMatcher( filepath, 17, 1 ) ) ) } ) ) } ) }, } ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/rust/testdata/���������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0022043�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/rust/testdata/common/��������������������������������������0000775�0000000�0000000�00000000000�13746324601�0023333�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/rust/testdata/common/Cargo.lock����������������������������0000664�0000000�0000000�00000000212�13746324601�0025233�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "common" version = "0.1.0" ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/rust/testdata/common/Cargo.toml����������������������������0000664�0000000�0000000�00000000074�13746324601�0025264�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������[package] name = "common" version = "0.1.0" [dependencies] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/rust/testdata/common/src/����������������������������������0000775�0000000�0000000�00000000000�13746324601�0024122�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/rust/testdata/common/src/main.rs���������������������������0000664�0000000�0000000�00000000435�13746324601�0025416�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mod test; use test::*; struct Earth {} struct Mars {} trait Atmosphere {} impl Atmosphere for Earth {} impl Atmosphere for Mars {} fn main() { create_universe(); let builder = Builder {}; builder.build_ } fn format_test() { let a : i32 = 5; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/rust/testdata/common/src/test.rs���������������������������0000664�0000000�0000000�00000000426�13746324601�0025451�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/// Be careful when using that function pub fn create_universe() {} pub struct Builder {} impl Builder { /// Do not try at home pub fn build_rocket(&self) {} pub fn build_shuttle(&self) {} } fn sig_test() { let b = Builder{}; b.build_rocket(); sig_test(); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/shutdown_test.py�������������������������������������������0000664�0000000�0000000�00000007152�13746324601�0022526�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from hamcrest import assert_that, equal_to from threading import Event import time import requests import pytest from ycmd.tests.client_test import Client_test from ycmd.utils import StartThread # Time to wait (int seconds) for all the servers to shutdown. Tweak for the CI # environment. SUBSERVER_SHUTDOWN_TIMEOUT = 120 class Shutdown_test( Client_test ): @Client_test.CaptureLogfiles def FromHandlerWithoutSubserver_test( self ): self.Start() self.AssertServersAreRunning() try: response = self.PostRequest( 'shutdown' ) response.raise_for_status() self.AssertResponse( response ) assert_that( response.json(), equal_to( True ) ) except requests.exceptions.ConnectionError: pass self.AssertServersShutDown( timeout = SUBSERVER_SHUTDOWN_TIMEOUT ) self.AssertLogfilesAreRemoved() @pytest.mark.valgrind_skip @Client_test.CaptureLogfiles def FromHandlerWithSubservers_test( self ): self.Start() filetypes = [ 'cpp', 'cs', 'go', 'javascript', 'typescript', 'rust' ] for filetype in filetypes: self.StartSubserverForFiletype( filetype ) self.AssertServersAreRunning() try: response = self.PostRequest( 'shutdown' ) response.raise_for_status() self.AssertResponse( response ) assert_that( response.json(), equal_to( True ) ) except requests.exceptions.ConnectionError: pass self.AssertServersShutDown( timeout = SUBSERVER_SHUTDOWN_TIMEOUT ) self.AssertLogfilesAreRemoved() @Client_test.CaptureLogfiles def FromWatchdogWithoutSubserver_test( self ): self.Start( idle_suicide_seconds = 2, check_interval_seconds = 1 ) self.AssertServersAreRunning() self.AssertServersShutDown( timeout = SUBSERVER_SHUTDOWN_TIMEOUT ) self.AssertLogfilesAreRemoved() @pytest.mark.valgrind_skip @Client_test.CaptureLogfiles def FromWatchdogWithSubservers_test( self ): all_servers_are_running = Event() def KeepServerAliveInAnotherThread(): while not all_servers_are_running.is_set(): try: self.GetRequest( 'ready' ) except requests.exceptions.ConnectionError: pass finally: time.sleep( 0.1 ) self.Start( idle_suicide_seconds = 2, check_interval_seconds = 1 ) StartThread( KeepServerAliveInAnotherThread ) try: filetypes = [ 'cpp', 'cs', 'go', 'javascript', 'typescript', 'rust' ] for filetype in filetypes: self.StartSubserverForFiletype( filetype ) self.AssertServersAreRunning() finally: all_servers_are_running.set() self.AssertServersShutDown( timeout = SUBSERVER_SHUTDOWN_TIMEOUT + 10 ) self.AssertLogfilesAreRemoved() def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/signature_help_test.py�������������������������������������0000664�0000000�0000000�00000004210�13746324601�0023654�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, empty, has_entries ) from ycmd.tests import SharedYcmd, IsolatedYcmd from ycmd.tests.test_utils import ( EMPTY_SIGNATURE_HELP, BuildRequest ) @SharedYcmd def SignatureHelp_IdentifierCompleter_test( app ): event_data = BuildRequest( contents = 'foo foogoo ba', event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', event_data ) # query is 'oo' request_data = BuildRequest( contents = 'oo foo foogoo ba', column_num = 3 ) response_data = app.post_json( '/signature_help', request_data ).json assert_that( response_data, has_entries( { 'errors': empty(), 'signature_help': EMPTY_SIGNATURE_HELP } ) ) @IsolatedYcmd( { 'disable_signature_help': 1 } ) def SignatureHelp_IdentifierCompleter_disabled_test( app ): event_data = BuildRequest( contents = 'foo foogoo ba', event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', event_data ) # query is 'oo' request_data = BuildRequest( contents = 'oo foo foogoo ba', column_num = 3 ) response_data = app.post_json( '/signature_help', request_data ).json assert_that( response_data, has_entries( { 'errors': empty(), 'signature_help': EMPTY_SIGNATURE_HELP } ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/subcommands_test.py����������������������������������������0000664�0000000�0000000�00000004076�13746324601�0023170�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2013 Google Inc. # 2020 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 <http://www.gnu.org/licenses/>. from hamcrest import assert_that, contains_exactly from unittest.mock import patch from ycmd.tests import SharedYcmd from ycmd.tests.test_utils import BuildRequest, DummyCompleter, PatchCompleter @SharedYcmd @patch( 'ycmd.tests.test_utils.DummyCompleter.GetSubcommandsMap', return_value = { 'A': lambda x: x, 'B': lambda x: x, 'C': lambda x: x } ) def Subcommands_Basic_test( get_subcmd_map, app ): with PatchCompleter( DummyCompleter, 'dummy_filetype' ): subcommands_data = BuildRequest( completer_target = 'dummy_filetype' ) assert_that( app.post_json( '/defined_subcommands', subcommands_data ).json, contains_exactly( 'A', 'B', 'C' ) ) @SharedYcmd @patch( 'ycmd.tests.test_utils.DummyCompleter.GetSubcommandsMap', return_value = { 'A': lambda x: x, 'B': lambda x: x, 'C': lambda x: x } ) def Subcommands_NoExplicitCompleterTargetSpecified_test( get_subcmd_map, app ): with PatchCompleter( DummyCompleter, 'dummy_filetype' ): subcommands_data = BuildRequest( filetype = 'dummy_filetype' ) assert_that( app.post_json( '/defined_subcommands', subcommands_data ).json, contains_exactly( 'A', 'B', 'C' ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/tern/������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0020205�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/tern/__init__.py�������������������������������������������0000664�0000000�0000000�00000001336�13746324601�0022321�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2016-2020 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 <http://www.gnu.org/licenses/>. from ycmd.tests.tern.conftest import * # noqa ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/tern/conftest.py�������������������������������������������0000664�0000000�0000000�00000007444�13746324601�0022415�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import os import pytest from ycmd.tests.test_utils import ( BuildRequest, ClearCompletionsCache, IsolatedApp, SetUpApp, StopCompleterServer, WaitUntilCompleterServerReady ) shared_app = None @pytest.fixture( scope='module', autouse=True ) def set_up_shared_app(): """Initializes the ycmd server as a WebTest application that will be shared by all tests using the SharedYcmd decorator in this package. Additional configuration that is common to these tests, like starting a semantic subserver, should be done here.""" global shared_app shared_app = SetUpApp() StartJavaScriptCompleterServerInDirectory( shared_app, PathToTestFile() ) yield StopCompleterServer( shared_app, 'java' ) def StartJavaScriptCompleterServerInDirectory( app, directory ): app.post_json( '/event_notification', BuildRequest( filepath = os.path.join( directory, 'test.js' ), event_name = 'FileReadyToParse', filetype = 'javascript' ) ) WaitUntilCompleterServerReady( app, 'javascript' ) @pytest.fixture def app( request ): which = request.param[ 0 ] assert which == 'isolated' or which == 'shared' if which == 'isolated': with IsolatedApp( {} ) as app: yield app StopCompleterServer( app, 'javascript' ) else: global shared_app ClearCompletionsCache() yield shared_app """Defines a decorator to be attached to tests of this package. This decorator passes the shared ycmd application as a parameter.""" SharedYcmd = pytest.mark.parametrize( # Name of the fixture/function argument 'app', # Fixture parameters, passed to app() as request.param [ ( 'shared', ) ], # Non-empty ids makes fixture parameters visible in pytest verbose output ids = [ '' ], # Execute the fixture, instead of passing parameters directly to the # function argument indirect = True ) """Defines a decorator to be attached to tests of this package. This decorator passes a unique ycmd application as a parameter. It should be used on tests that change the server state in a irreversible way (ex: a semantic subserver is stopped or restarted) or expect a clean state (ex: no semantic subserver started, no .ycm_extra_conf.py loaded, etc). Use the optional parameter |custom_options| to give additional options and/or override the default ones. Example usage: from ycmd.tests.python import IsolatedYcmd @IsolatedYcmd( { 'python_binary_path': '/some/path' } ) def CustomPythonBinaryPath_test( app ): ... """ IsolatedYcmd = pytest.mark.parametrize( # Name of the fixture/function argument 'app', # Fixture parameters, passed to app() as request.param [ ( 'isolated', ) ], # Non-empty ids makes fixture parameters visible in pytest verbose output ids = [ '' ], # Execute the fixture, instead of passing parameters directly to the # function argument indirect = True ) def PathToTestFile( *args ): dir_of_current_script = os.path.dirname( os.path.abspath( __file__ ) ) return os.path.join( dir_of_current_script, 'testdata', *args ) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/tern/debug_info_test.py������������������������������������0000664�0000000�0000000�00000004012�13746324601�0023714�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2016-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_exactly, empty, has_entries, has_entry, instance_of ) from ycmd.tests.tern import SharedYcmd from ycmd.tests.test_utils import BuildRequest @SharedYcmd def DebugInfo_test( app ): request_data = BuildRequest( filetype = 'javascript' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'JavaScript', 'servers': contains_exactly( has_entries( { 'name': 'Tern', 'is_running': instance_of( bool ), 'executable': instance_of( str ), 'pid': instance_of( int ), 'address': instance_of( str ), 'port': instance_of( int ), 'logfiles': contains_exactly( instance_of( str ), instance_of( str ) ), 'extras': contains_exactly( has_entries( { 'key': 'configuration file', 'value': instance_of( str ) } ), has_entries( { 'key': 'working directory', 'value': instance_of( str ) } ) ), } ) ), 'items': empty() } ) ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/tern/event_notification_test.py����������������������������0000664�0000000�0000000�00000023344�13746324601�0025513�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2015-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_exactly, empty, equal_to, has_entries, none ) from unittest.mock import patch from pprint import pformat import os import requests from ycmd.tests.test_utils import BuildRequest, ErrorMatcher from ycmd.tests.tern import IsolatedYcmd, PathToTestFile from ycmd import utils @IsolatedYcmd def EventNotification_OnFileReadyToParse_ProjectFile_cwd_test( app ): response = app.post_json( '/event_notification', BuildRequest( filepath = PathToTestFile(), event_name = 'FileReadyToParse', filetype = 'javascript' ), expect_errors = True ) assert_that( response.status_code, equal_to( requests.codes.ok ) ) assert_that( response.json, empty() ) debug_info = app.post_json( '/debug_info', BuildRequest( filetype = 'javascript' ) ).json assert_that( debug_info[ 'completer' ][ 'servers' ][ 0 ][ 'extras' ], contains_exactly( has_entries( { 'key': 'configuration file', 'value': PathToTestFile( '.tern-project' ) } ), has_entries( { 'key': 'working directory', 'value': PathToTestFile() } ) ) ) @IsolatedYcmd def EventNotification_OnFileReadyToParse_ProjectFile_parentdir_test( app ): response = app.post_json( '/event_notification', BuildRequest( filepath = PathToTestFile( 'lamelib' ), event_name = 'FileReadyToParse', filetype = 'javascript' ), expect_errors = True ) assert_that( response.status_code, equal_to( requests.codes.ok ) ) assert_that( response.json, empty() ) debug_info = app.post_json( '/debug_info', BuildRequest( filetype = 'javascript' ) ).json assert_that( debug_info[ 'completer' ][ 'servers' ][ 0 ][ 'extras' ], contains_exactly( has_entries( { 'key': 'configuration file', 'value': PathToTestFile( '.tern-project' ) } ), has_entries( { 'key': 'working directory', 'value': PathToTestFile() } ) ) ) @IsolatedYcmd @patch( 'ycmd.completers.javascript.tern_completer.GlobalConfigExists', return_value = False ) def EventNotification_OnFileReadyToParse_NoProjectFile_test( global_config_exists, app ): # We raise an error if we can't detect a .tern-project file. # We only do this on the first OnFileReadyToParse event after a # server startup. response = app.post_json( '/event_notification', BuildRequest( filepath = PathToTestFile( '..' ), event_name = 'FileReadyToParse', filetype = 'javascript' ), expect_errors = True ) print( f'event response: { pformat( response.json ) }' ) assert_that( response.status_code, equal_to( requests.codes.internal_server_error ) ) assert_that( response.json, ErrorMatcher( RuntimeError, 'Warning: Unable to detect a .tern-project file ' 'in the hierarchy before ' + PathToTestFile( '..' ) + ' and no global .tern-config file was found. ' 'This is required for accurate JavaScript ' 'completion. Please see the User Guide for ' 'details.' ) ) debug_info = app.post_json( '/debug_info', BuildRequest( filetype = 'javascript' ) ).json assert_that( debug_info[ 'completer' ][ 'servers' ][ 0 ][ 'extras' ], contains_exactly( has_entries( { 'key': 'configuration file', 'value': none() } ), has_entries( { 'key': 'working directory', 'value': utils.GetCurrentDirectory() } ) ) ) # Check that a subsequent call does *not* raise the error. response = app.post_json( '/event_notification', BuildRequest( event_name = 'FileReadyToParse', filetype = 'javascript' ), expect_errors = True ) print( f'event response: { pformat( response.json ) }' ) assert_that( response.status_code, equal_to( requests.codes.ok ) ) assert_that( response.json, empty() ) # Restart the server and check that it raises it again. app.post_json( '/run_completer_command', BuildRequest( filepath = PathToTestFile( '..' ), command_arguments = [ 'RestartServer' ], filetype = 'javascript' ) ) response = app.post_json( '/event_notification', BuildRequest( filepath = PathToTestFile( '..' ), event_name = 'FileReadyToParse', filetype = 'javascript' ), expect_errors = True ) print( f'event response: { pformat( response.json ) }' ) assert_that( response.status_code, equal_to( requests.codes.internal_server_error ) ) assert_that( response.json, ErrorMatcher( RuntimeError, 'Warning: Unable to detect a .tern-project file ' 'in the hierarchy before ' + PathToTestFile( '..' ) + ' and no global .tern-config file was found. ' 'This is required for accurate JavaScript ' 'completion. Please see the User Guide for ' 'details.' ) ) # Finally, restart the server in a folder containing a .tern-project file. We # expect no error in that case. app.post_json( '/run_completer_command', BuildRequest( filepath = PathToTestFile(), command_arguments = [ 'RestartServer' ], filetype = 'javascript' ) ) response = app.post_json( '/event_notification', BuildRequest( filepath = PathToTestFile(), event_name = 'FileReadyToParse', filetype = 'javascript' ), expect_errors = True ) print( f'event response: { pformat( response.json ) }' ) assert_that( response.status_code, equal_to( requests.codes.ok ) ) assert_that( response.json, empty() ) debug_info = app.post_json( '/debug_info', BuildRequest( filetype = 'javascript' ) ).json assert_that( debug_info[ 'completer' ][ 'servers' ][ 0 ][ 'extras' ], contains_exactly( has_entries( { 'key': 'configuration file', 'value': PathToTestFile( '.tern-project' ) } ), has_entries( { 'key': 'working directory', 'value': PathToTestFile() } ) ) ) @IsolatedYcmd @patch( 'ycmd.completers.javascript.tern_completer.GlobalConfigExists', return_value = True ) def EventNotification_OnFileReadyToParse_UseGlobalConfig_test( global_config_exists, app ): # No working directory is given. response = app.post_json( '/event_notification', BuildRequest( filepath = PathToTestFile( '..' ), event_name = 'FileReadyToParse', filetype = 'javascript' ), expect_errors = True ) print( f'event response: { pformat( response.json ) }' ) assert_that( response.status_code, equal_to( requests.codes.ok ) ) assert_that( response.json, empty() ) debug_info = app.post_json( '/debug_info', BuildRequest( filetype = 'javascript' ) ).json assert_that( debug_info[ 'completer' ][ 'servers' ][ 0 ][ 'extras' ], contains_exactly( has_entries( { 'key': 'configuration file', 'value': os.path.join( os.path.expanduser( '~' ), '.tern-config' ) } ), has_entries( { 'key': 'working directory', 'value': utils.GetCurrentDirectory() } ) ) ) # Restart the server with a working directory. app.post_json( '/run_completer_command', BuildRequest( filepath = PathToTestFile( '..' ), command_arguments = [ 'RestartServer' ], filetype = 'javascript', working_dir = PathToTestFile() ) ) debug_info = app.post_json( '/debug_info', BuildRequest( filetype = 'javascript' ) ).json assert_that( debug_info[ 'completer' ][ 'servers' ][ 0 ][ 'extras' ], contains_exactly( has_entries( { 'key': 'configuration file', 'value': os.path.join( os.path.expanduser( '~' ), '.tern-config' ) } ), has_entries( { 'key': 'working directory', 'value': PathToTestFile() } ) ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/tern/get_completions_test.py�������������������������������0000664�0000000�0000000�00000037625�13746324601�0025026�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2015-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_exactly, contains_inanyorder, empty, equal_to, has_entries ) from pprint import pformat import requests from ycmd.tests.tern import ( IsolatedYcmd, PathToTestFile, SharedYcmd, StartJavaScriptCompleterServerInDirectory ) from ycmd.tests.test_utils import CombineRequest, CompletionEntryMatcher from ycmd.utils import ReadFile # The following properties/methods are in Object.prototype, so are present # on all objects: # # toString() # toLocaleString() # valueOf() # hasOwnProperty() # propertyIsEnumerable() # isPrototypeOf() def RunTest( app, test ): """ Method to run a simple completion test and verify the result test is a dictionary containing: 'request': kwargs for BuildRequest 'expect': { 'response': server response code (e.g. httplib.OK) 'data': matcher for the server response json } """ contents = ReadFile( test[ 'request' ][ 'filepath' ] ) app.post_json( '/event_notification', CombineRequest( test[ 'request' ], { 'event_name': 'FileReadyToParse', 'contents': contents, } ), expect_errors = True ) # We ignore errors here and we check the response code ourself. # This is to allow testing of requests returning errors. response = app.post_json( '/completions', CombineRequest( test[ 'request' ], { 'contents': contents } ), expect_errors = True ) print( f'completer response: { pformat( response.json ) }' ) assert_that( response.status_code, equal_to( test[ 'expect' ][ 'response' ] ) ) assert_that( response.json, test[ 'expect' ][ 'data' ] ) @SharedYcmd def GetCompletions_NoQuery_test( app ): RunTest( app, { 'description': 'semantic completion works for simple object no query', 'request': { 'filetype' : 'javascript', 'filepath' : PathToTestFile( 'simple_test.js' ), 'line_num' : 13, 'column_num': 43, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_inanyorder( CompletionEntryMatcher( 'a_simple_function', 'fn(param: ?) -> string' ), CompletionEntryMatcher( 'basic_type', 'number' ), CompletionEntryMatcher( 'object', 'object' ), CompletionEntryMatcher( 'toString', 'fn() -> string' ), CompletionEntryMatcher( 'toLocaleString', 'fn() -> string' ), CompletionEntryMatcher( 'valueOf', 'fn() -> number' ), CompletionEntryMatcher( 'hasOwnProperty', 'fn(prop: string) -> bool' ), CompletionEntryMatcher( 'isPrototypeOf', 'fn(obj: ?) -> bool' ), CompletionEntryMatcher( 'propertyIsEnumerable', 'fn(prop: string) -> bool' ), ), 'errors': empty(), } ) }, } ) @SharedYcmd def GetCompletions_Query_test( app ): RunTest( app, { 'description': 'semantic completion works for simple object with query', 'request': { 'filetype' : 'javascript', 'filepath' : PathToTestFile( 'simple_test.js' ), 'line_num' : 14, 'column_num': 45, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_exactly( CompletionEntryMatcher( 'basic_type', 'number' ), CompletionEntryMatcher( 'isPrototypeOf', 'fn(obj: ?) -> bool' ), ), 'errors': empty(), } ) }, } ) @SharedYcmd def GetCompletions_Require_NoQuery_test( app ): RunTest( app, { 'description': 'semantic completion works for simple object no query', 'request': { 'filetype' : 'javascript', 'filepath' : PathToTestFile( 'requirejs_test.js' ), 'line_num' : 2, 'column_num': 15, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_inanyorder( CompletionEntryMatcher( 'mine_bitcoin', 'fn(how_much: ?) -> number' ), CompletionEntryMatcher( 'get_number', 'number' ), CompletionEntryMatcher( 'get_string', 'string' ), CompletionEntryMatcher( 'get_thing', 'fn(a: ?) -> number|string' ), CompletionEntryMatcher( 'toString', 'fn() -> string' ), CompletionEntryMatcher( 'toLocaleString', 'fn() -> string' ), CompletionEntryMatcher( 'valueOf', 'fn() -> number' ), CompletionEntryMatcher( 'hasOwnProperty', 'fn(prop: string) -> bool' ), CompletionEntryMatcher( 'isPrototypeOf', 'fn(obj: ?) -> bool' ), CompletionEntryMatcher( 'propertyIsEnumerable', 'fn(prop: string) -> bool' ), ), 'errors': empty(), } ) }, } ) @SharedYcmd def GetCompletions_Require_Query_test( app ): RunTest( app, { 'description': 'semantic completion works for require object with query', 'request': { 'filetype' : 'javascript', 'filepath' : PathToTestFile( 'requirejs_test.js' ), 'line_num' : 3, 'column_num': 17, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_exactly( CompletionEntryMatcher( 'mine_bitcoin', 'fn(how_much: ?) -> number' ), ), 'errors': empty(), } ) }, } ) @SharedYcmd def GetCompletions_Require_Query_LCS_test( app ): RunTest( app, { 'description': ( 'completion works for require object ' 'with query not prefix' ), 'request': { 'filetype' : 'javascript', 'filepath' : PathToTestFile( 'requirejs_test.js' ), 'line_num' : 4, 'column_num': 17, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_exactly( CompletionEntryMatcher( 'get_number', 'number' ), CompletionEntryMatcher( 'get_thing', 'fn(a: ?) -> number|string' ), CompletionEntryMatcher( 'get_string', 'string' ), ), 'errors': empty(), } ) }, } ) @SharedYcmd def GetCompletions_DirtyNamedBuffers_test( app ): # This tests that when we have dirty buffers in our editor, tern actually # uses them correctly RunTest( app, { 'description': ( 'completion works for require object ' 'with query not prefix' ), 'request': { 'filetype' : 'javascript', 'filepath' : PathToTestFile( 'requirejs_test.js' ), 'line_num' : 18, 'column_num': 11, 'file_data': { PathToTestFile( 'no_such_lib', 'no_such_file.js' ): { 'contents': ( 'define( [], function() { return { big_endian_node: 1 } } )' ), 'filetypes': [ 'javascript' ] } }, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_inanyorder( CompletionEntryMatcher( 'big_endian_node', 'number' ), CompletionEntryMatcher( 'toString', 'fn() -> string' ), CompletionEntryMatcher( 'toLocaleString', 'fn() -> string' ), CompletionEntryMatcher( 'valueOf', 'fn() -> number' ), CompletionEntryMatcher( 'hasOwnProperty', 'fn(prop: string) -> bool' ), CompletionEntryMatcher( 'isPrototypeOf', 'fn(obj: ?) -> bool' ), CompletionEntryMatcher( 'propertyIsEnumerable', 'fn(prop: string) -> bool' ), ), 'errors': empty(), } ) }, } ) @SharedYcmd def GetCompletions_ReturnsDocsInCompletions_test( app ): # This tests that we supply docs for completions RunTest( app, { 'description': 'completions supply docs', 'request': { 'filetype' : 'javascript', 'filepath' : PathToTestFile( 'requirejs_test.js' ), 'line_num' : 8, 'column_num': 15, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_inanyorder( CompletionEntryMatcher( 'a_function', 'fn(bar: ?) -> {a_value: string}', { 'detailed_info': ( 'fn(bar: ?) -> {a_value: string}\n' 'This is a short documentation string' ), } ), CompletionEntryMatcher( 'options', 'options' ), CompletionEntryMatcher( 'toString', 'fn() -> string' ), CompletionEntryMatcher( 'toLocaleString', 'fn() -> string' ), CompletionEntryMatcher( 'valueOf', 'fn() -> number' ), CompletionEntryMatcher( 'hasOwnProperty', 'fn(prop: string) -> bool' ), CompletionEntryMatcher( 'isPrototypeOf', 'fn(obj: ?) -> bool' ), CompletionEntryMatcher( 'propertyIsEnumerable', 'fn(prop: string) -> bool' ), ), 'errors': empty(), } ) }, } ) @SharedYcmd def GetCompletions_IgoreNonJSFiles_test( app ): trivial1 = { 'filetypes': [ 'python' ], 'contents': ReadFile( PathToTestFile( 'trivial.js' ) ), } trivial2 = { 'filetypes': [ 'javascript' ], 'contents': ReadFile( PathToTestFile( 'trivial2.js' ) ), } request = { 'line_num': 1, 'column_num': 3, 'file_data': { PathToTestFile( 'trivial.js' ): trivial1, PathToTestFile( 'trivial2.js' ): trivial2, }, } app.post_json( '/event_notification', CombineRequest( request, { 'filepath': PathToTestFile( 'trivial2.js' ), 'event_name': 'FileReadyToParse', } ) ) response = app.post_json( '/completions', CombineRequest( request, { 'filepath': PathToTestFile( 'trivial2.js' ), } ) ).json print( f'completer response: { pformat( response ) }' ) assert_that( response, has_entries( { 'completion_start_column': 3, # Note: we do *not* see X.y and X.z because tern is not told about # the trivial.js file because we pretended it was Python 'completions': empty(), 'errors': empty(), } ) ) @SharedYcmd def GetCompletions_IncludeMultiFileType_test( app ): trivial1 = { 'filetypes': [ 'python', 'javascript' ], 'contents': ReadFile( PathToTestFile( 'trivial.js' ) ), } trivial2 = { 'filetypes': [ 'javascript' ], 'contents': ReadFile( PathToTestFile( 'trivial2.js' ) ), } request = { 'line_num': 1, 'column_num': 3, 'file_data': { PathToTestFile( 'trivial.js' ): trivial1, PathToTestFile( 'trivial2.js' ): trivial2, }, } app.post_json( '/event_notification', CombineRequest( request, { 'filepath': PathToTestFile( 'trivial2.js' ), 'event_name': 'FileReadyToParse', } ) ) response = app.post_json( '/completions', CombineRequest( request, { 'filepath': PathToTestFile( 'trivial2.js' ), # We must force the use of semantic engine because the previous test would # have entered 'empty' results into the completion cache. 'force_semantic': True, } ) ).json print( f'completer response: { pformat( response, indent = 2 ) }' ) assert_that( response, has_entries( { 'completion_start_column': 3, # Note: This time, we *do* see the completions, because one of the 2 # filetypes for trivial.js is javascript. 'completions': contains_inanyorder( CompletionEntryMatcher( 'y', 'string' ), CompletionEntryMatcher( 'z', 'string' ), CompletionEntryMatcher( 'toString', 'fn() -> string' ), CompletionEntryMatcher( 'toLocaleString', 'fn() -> string' ), CompletionEntryMatcher( 'valueOf', 'fn() -> number' ), CompletionEntryMatcher( 'hasOwnProperty', 'fn(prop: string) -> bool' ), CompletionEntryMatcher( 'isPrototypeOf', 'fn(obj: ?) -> bool' ), CompletionEntryMatcher( 'propertyIsEnumerable', 'fn(prop: string) -> bool' ), ), 'errors': empty(), } ) ) @SharedYcmd def GetCompletions_Unicode_AfterLine_test( app ): RunTest( app, { 'description': 'completions work with unicode chars in the file', 'request': { 'filetype' : 'javascript', 'filepath' : PathToTestFile( 'unicode.js' ), 'line_num' : 1, 'column_num': 16, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_inanyorder( CompletionEntryMatcher( 'charAt', 'fn(i: number) -> string' ), CompletionEntryMatcher( 'charCodeAt', 'fn(i: number) -> number' ), ), 'completion_start_column': 13, 'errors': empty(), } ) }, } ) @SharedYcmd def GetCompletions_Unicode_InLine_test( app ): RunTest( app, { 'description': 'completions work with unicode chars in the file', 'request': { 'filetype' : 'javascript', 'filepath' : PathToTestFile( 'unicode.js' ), 'line_num' : 2, 'column_num': 18, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_inanyorder( CompletionEntryMatcher( 'charAt', 'fn(i: number) -> string' ), CompletionEntryMatcher( 'charCodeAt', 'fn(i: number) -> number' ), ), 'completion_start_column': 15, 'errors': empty(), } ) }, } ) @SharedYcmd def GetCompletions_Unicode_InFile_test( app ): RunTest( app, { 'description': 'completions work with unicode chars in the file', 'request': { 'filetype' : 'javascript', 'filepath' : PathToTestFile( 'unicode.js' ), 'line_num' : 3, 'column_num': 16, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_inanyorder( CompletionEntryMatcher( 'charAt', 'fn(i: number) -> string' ), CompletionEntryMatcher( 'charCodeAt', 'fn(i: number) -> number' ), ), 'completion_start_column': 13, 'errors': empty(), } ) }, } ) @IsolatedYcmd def GetCompletions_ChangeStartColumn_test( app ): StartJavaScriptCompleterServerInDirectory( app, PathToTestFile( 'node' ) ) RunTest( app, { 'description': 'the completion_start_column is updated by tern', 'request': { 'filetype' : 'javascript', 'filepath' : PathToTestFile( 'node', 'node_test.js' ), 'line_num' : 1, 'column_num' : 17, 'force_semantic': True, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_exactly( CompletionEntryMatcher( '"path"', 'path' ) ), 'completion_start_column': 14, 'errors': empty(), } ) }, } ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �����������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/tern/subcommands_test.py�����������������������������������0000664�0000000�0000000�00000040734�13746324601�0024141�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2015-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_exactly, contains_inanyorder, equal_to, has_entry, has_entries ) from unittest.mock import patch from pprint import pformat import requests from ycmd.tests.tern import ( IsolatedYcmd, PathToTestFile, SharedYcmd, StartJavaScriptCompleterServerInDirectory ) from ycmd.tests.test_utils import ( BuildRequest, ChunkMatcher, CombineRequest, ErrorMatcher, LocationMatcher, MockProcessTerminationTimingOut ) from ycmd.utils import ReadFile @SharedYcmd def Subcommands_DefinedSubcommands_test( app ): subcommands_data = BuildRequest( completer_target = 'javascript' ) assert_that( app.post_json( '/defined_subcommands', subcommands_data ).json, contains_inanyorder( 'GoToDefinition', 'GoTo', 'GetDoc', 'GetType', 'GoToReferences', 'RefactorRename', 'RestartServer' ) ) def RunTest( app, test, contents = None ): if not contents: contents = ReadFile( test[ 'request' ][ 'filepath' ] ) # Because we aren't testing this command, we *always* ignore errors. This # is mainly because we (may) want to test scenarios where the completer # throws an exception and the easiest way to do that is to throw from # within the FlagsForFile function. app.post_json( '/event_notification', CombineRequest( test[ 'request' ], { 'event_name': 'FileReadyToParse', 'contents': contents, 'filetype': 'javascript', } ), expect_errors = True ) # We also ignore errors here, but then we check the response code # ourself. This is to allow testing of requests returning errors. response = app.post_json( '/run_completer_command', CombineRequest( test[ 'request' ], { 'completer_target': 'filetype_default', 'contents': contents, 'filetype': 'javascript', 'command_arguments': ( [ test[ 'request' ][ 'command' ] ] + test[ 'request' ].get( 'arguments', [] ) ) } ), expect_errors = True ) print( f'completer response: { pformat( response.json ) }' ) assert_that( response.status_code, equal_to( test[ 'expect' ][ 'response' ] ) ) assert_that( response.json, test[ 'expect' ][ 'data' ] ) @SharedYcmd def Subcommands_GoToDefinition_test( app ): RunTest( app, { 'description': 'GoToDefinition works within file', 'request': { 'command': 'GoToDefinition', 'line_num': 13, 'column_num': 25, 'filepath': PathToTestFile( 'simple_test.js' ), }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'filepath': PathToTestFile( 'simple_test.js' ), 'line_num': 1, 'column_num': 5, } ) } } ) @SharedYcmd def Subcommands_GoToDefinition_Unicode_test( app ): RunTest( app, { 'description': 'GoToDefinition works within file with unicode', 'request': { 'command': 'GoToDefinition', 'line_num': 11, 'column_num': 12, 'filepath': PathToTestFile( 'unicode.js' ), }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'filepath': PathToTestFile( 'unicode.js' ), 'line_num': 6, 'column_num': 26, } ) } } ) @SharedYcmd def Subcommands_GoTo_test( app ): RunTest( app, { 'description': 'GoTo works the same as GoToDefinition within file', 'request': { 'command': 'GoTo', 'line_num': 13, 'column_num': 25, 'filepath': PathToTestFile( 'simple_test.js' ), }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'filepath': PathToTestFile( 'simple_test.js' ), 'line_num': 1, 'column_num': 5, } ) } } ) @IsolatedYcmd def Subcommands_GoTo_RelativePath_test( app ): StartJavaScriptCompleterServerInDirectory( app, PathToTestFile() ) RunTest( app, { 'description': 'GoTo works when the buffer differs from the file on disk', 'request': { 'command': 'GoTo', 'line_num': 43, 'column_num': 25, 'filepath': PathToTestFile( 'simple_test.js' ), }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'filepath': PathToTestFile( 'simple_test.js' ), 'line_num': 31, 'column_num': 5, } ) } }, contents = ReadFile( PathToTestFile( 'simple_test.modified.js' ) ) ) @SharedYcmd def Subcommands_GetDoc_test( app ): RunTest( app, { 'description': 'GetDoc works within file', 'request': { 'command': 'GetDoc', 'line_num': 7, 'column_num': 16, 'filepath': PathToTestFile( 'coollib', 'cool_object.js' ), }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'detailed_info': ( 'Name: mine_bitcoin\n' 'Type: fn(how_much: ?) -> number\n\n' 'This function takes a number and invests it in bitcoin. It ' 'returns\nthe expected value (in notional currency) after 1 year.' ) } ) } } ) @SharedYcmd def Subcommands_GetType_test( app ): RunTest( app, { 'description': 'GetType works within file', 'request': { 'command': 'GetType', 'line_num': 11, 'column_num': 14, 'filepath': PathToTestFile( 'coollib', 'cool_object.js' ), }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'message': 'number' } ) } } ) @SharedYcmd def Subcommands_GoToReferences_test( app ): RunTest( app, { 'description': 'GoToReferences works within file', 'request': { 'command': 'GoToReferences', 'line_num': 17, 'column_num': 29, 'filepath': PathToTestFile( 'coollib', 'cool_object.js' ), }, 'expect': { 'response': requests.codes.ok, 'data': contains_inanyorder( has_entries( { 'filepath': PathToTestFile( 'coollib', 'cool_object.js' ), 'line_num': 17, 'column_num': 29, } ), has_entries( { 'filepath': PathToTestFile( 'coollib', 'cool_object.js' ), 'line_num': 12, 'column_num': 9, } ) ) } } ) @SharedYcmd def Subcommands_GoToReferences_Unicode_test( app ): RunTest( app, { 'description': 'GoToReferences works within file with unicode chars', 'request': { 'command': 'GoToReferences', 'line_num': 11, 'column_num': 5, 'filepath': PathToTestFile( 'unicode.js' ), }, 'expect': { 'response': requests.codes.ok, 'data': contains_inanyorder( has_entries( { 'filepath': PathToTestFile( 'unicode.js' ), 'line_num': 5, 'column_num': 5, } ), has_entries( { 'filepath': PathToTestFile( 'unicode.js' ), 'line_num': 9, 'column_num': 1, } ), has_entries( { 'filepath': PathToTestFile( 'unicode.js' ), 'line_num': 11, 'column_num': 1, } ) ) } } ) @SharedYcmd def Subcommands_GetDocWithNoItendifier_test( app ): RunTest( app, { 'description': 'GetDoc works when no identifier', 'request': { 'command': 'GetDoc', 'filepath': PathToTestFile( 'simple_test.js' ), 'line_num': 12, 'column_num': 1, }, 'expect': { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( RuntimeError, 'TernError: No type found ' 'at the given position.' ), } } ) @SharedYcmd def Subcommands_RefactorRename_Simple_test( app ): filepath = PathToTestFile( 'simple_test.js' ) RunTest( app, { 'description': 'RefactorRename works within a single scope/file', 'request': { 'command': 'RefactorRename', 'arguments': [ 'test' ], 'filepath': filepath, 'line_num': 15, 'column_num': 32, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( 'test', LocationMatcher( filepath, 1, 5 ), LocationMatcher( filepath, 1, 22 ) ), ChunkMatcher( 'test', LocationMatcher( filepath, 13, 25 ), LocationMatcher( filepath, 13, 42 ) ), ChunkMatcher( 'test', LocationMatcher( filepath, 14, 24 ), LocationMatcher( filepath, 14, 41 ) ), ChunkMatcher( 'test', LocationMatcher( filepath, 15, 24 ), LocationMatcher( filepath, 15, 41 ) ), ChunkMatcher( 'test', LocationMatcher( filepath, 21, 7 ), LocationMatcher( filepath, 21, 24 ) ), # On the same line, ensuring offsets are as expected (as # unmodified source, similar to clang) ChunkMatcher( 'test', LocationMatcher( filepath, 21, 28 ), LocationMatcher( filepath, 21, 45 ) ), ), 'location': LocationMatcher( filepath, 15, 32 ) } ) ) } ) } } ) @SharedYcmd def Subcommands_RefactorRename_MultipleFiles_test( app ): file1 = PathToTestFile( 'file1.js' ) file2 = PathToTestFile( 'file2.js' ) file3 = PathToTestFile( 'file3.js' ) RunTest( app, { 'description': 'RefactorRename works across files', 'request': { 'command': 'RefactorRename', 'arguments': [ 'a-quite-long-string' ], 'filepath': file1, 'line_num': 3, 'column_num': 14, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( 'a-quite-long-string', LocationMatcher( file1, 1, 5 ), LocationMatcher( file1, 1, 11 ) ), ChunkMatcher( 'a-quite-long-string', LocationMatcher( file1, 3, 14 ), LocationMatcher( file1, 3, 20 ) ), ChunkMatcher( 'a-quite-long-string', LocationMatcher( file2, 2, 14 ), LocationMatcher( file2, 2, 20 ) ), ChunkMatcher( 'a-quite-long-string', LocationMatcher( file3, 3, 12 ), LocationMatcher( file3, 3, 18 ) ) ), 'location': LocationMatcher( file1, 3, 14 ) } ) ) } ) } } ) # Needs to be isolated to prevent interfering with other tests (this test loads # an extra file into tern's project memory) @IsolatedYcmd def Subcommands_RefactorRename_MultipleFiles_OnFileReadyToParse_test( app ): StartJavaScriptCompleterServerInDirectory( app, PathToTestFile() ) file1 = PathToTestFile( 'file1.js' ) file2 = PathToTestFile( 'file2.js' ) file3 = PathToTestFile( 'file3.js' ) # This test is roughly the same as the previous one, except here file4.js is # pushed into the Tern engine via 'opening it in the editor' (i.e. # FileReadyToParse event). The first 3 are loaded into the tern server # because they are listed in the .tern-project file's loadEagerly option. file4 = PathToTestFile( 'file4.js' ) app.post_json( '/event_notification', BuildRequest( **{ 'filetype': 'javascript', 'event_name': 'FileReadyToParse', 'contents': ReadFile( file4 ), 'filepath': file4, } ), expect_errors = False ) RunTest( app, { 'description': 'FileReadyToParse loads files into tern server', 'request': { 'command': 'RefactorRename', 'arguments': [ 'a-quite-long-string' ], 'filepath': file1, 'line_num': 3, 'column_num': 14, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( 'a-quite-long-string', LocationMatcher( file1, 1, 5 ), LocationMatcher( file1, 1, 11 ) ), ChunkMatcher( 'a-quite-long-string', LocationMatcher( file1, 3, 14 ), LocationMatcher( file1, 3, 20 ) ), ChunkMatcher( 'a-quite-long-string', LocationMatcher( file2, 2, 14 ), LocationMatcher( file2, 2, 20 ) ), ChunkMatcher( 'a-quite-long-string', LocationMatcher( file3, 3, 12 ), LocationMatcher( file3, 3, 18 ) ), ChunkMatcher( 'a-quite-long-string', LocationMatcher( file4, 4, 22 ), LocationMatcher( file4, 4, 28 ) ) ), 'location': LocationMatcher( file1, 3, 14 ) } ) ) } ) } } ) @SharedYcmd def Subcommands_RefactorRename_Missing_New_Name_test( app ): RunTest( app, { 'description': 'RefactorRename raises an error without new name', 'request': { 'command': 'RefactorRename', 'line_num': 17, 'column_num': 29, 'filepath': PathToTestFile( 'coollib', 'cool_object.js' ), }, 'expect': { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( ValueError, 'Please specify a new name to rename it to.\n' 'Usage: RefactorRename <new name>' ), } } ) @SharedYcmd def Subcommands_RefactorRename_Unicode_test( app ): filepath = PathToTestFile( 'unicode.js' ) RunTest( app, { 'description': 'RefactorRename works with unicode identifiers', 'request': { 'command': 'RefactorRename', 'arguments': [ '†es†' ], 'filepath': filepath, 'line_num': 11, 'column_num': 3, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( '†es†', LocationMatcher( filepath, 5, 5 ), LocationMatcher( filepath, 5, 13 ) ), ChunkMatcher( '†es†', LocationMatcher( filepath, 9, 1 ), LocationMatcher( filepath, 9, 9 ) ), ChunkMatcher( '†es†', LocationMatcher( filepath, 11, 1 ), LocationMatcher( filepath, 11, 9 ) ) ), 'location': LocationMatcher( filepath, 11, 3 ) } ) ) } ) } } ) @IsolatedYcmd @patch( 'ycmd.utils.WaitUntilProcessIsTerminated', MockProcessTerminationTimingOut ) def Subcommands_StopServer_Timeout_test( app ): StartJavaScriptCompleterServerInDirectory( app, PathToTestFile() ) app.post_json( '/run_completer_command', BuildRequest( filetype = 'javascript', command_arguments = [ 'StopServer' ] ) ) request_data = BuildRequest( filetype = 'javascript' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entry( 'servers', contains_exactly( has_entry( 'is_running', False ) ) ) ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/tern/testdata/���������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0022016�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/tern/testdata/.tern-project��������������������������������0000664�0000000�0000000�00000000323�13746324601�0024431�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "libs": [ "browser", "jquery" ], "loadEagerly": [ "file1.js", "file2.js", "file3.js" ], "plugins": { "requirejs": { "baseURL": "./", "paths": {} } } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/tern/testdata/coollib/�������������������������������������0000775�0000000�0000000�00000000000�13746324601�0023441�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/tern/testdata/coollib/cool_object.js�����������������������0000664�0000000�0000000�00000001046�13746324601�0026262�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������define( [], function() { return { /** * This function takes a number and invests it in bitcoin. It returns * the expected value (in notional currency) after 1 year. */ mine_bitcoin: function( how_much ) { return how_much * 0.001; }, get_number: 10, get_string: 'string', get_thing: function( a ) { if ( a ) { return this.get_number; } else { return this.get_string; } } }; } ); ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/tern/testdata/coollib/cool_widget.js�����������������������0000664�0000000�0000000�00000000543�13746324601�0026300�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������define( ['lamelib/lame_widget'], function( lame_widget ) { $.widget( 'cool_widget', { options: { 'secret_test': 100 }, _create: function() { this.a_number = 20; }, b_function: function( bar ) { return { b_value: 'biz' }; } } ); } ); �������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/tern/testdata/file1.js�������������������������������������0000664�0000000�0000000�00000000067�13746324601�0023357�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������var global = 'this is a test' console.log( global ); �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/tern/testdata/file2.js�������������������������������������0000664�0000000�0000000�00000000031�13746324601�0023347�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ console.log( global ); �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/tern/testdata/file3.js�������������������������������������0000664�0000000�0000000�00000000056�13746324601�0023357�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ function method( xyz ) { return global } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/tern/testdata/file4.js�������������������������������������0000664�0000000�0000000�00000000421�13746324601�0023354�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// This file contains a usage of the global value 'global', but it does *not* // appear in .tern-project's 'loadEagerly'. This means that it does not get // renamed when we run the rename command. window.eval( 'xyz' + global ); // Though it does if we tell tern about it. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/tern/testdata/lamelib/�������������������������������������0000775�0000000�0000000�00000000000�13746324601�0023423�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/tern/testdata/lamelib/lame_widget.js�����������������������0000664�0000000�0000000�00000000510�13746324601�0026236�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������define( ['lamelib/lame_widget'], function( lame_widget ) { return { options: { 'test': 200 }, /** * This is a short documentation string */ a_function: function( bar ) { return { a_value: 'baz' }; } }; } ); ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/tern/testdata/node/����������������������������������������0000775�0000000�0000000�00000000000�13746324601�0022743�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/tern/testdata/node/.tern-project���������������������������0000664�0000000�0000000�00000000046�13746324601�0025360�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "plugins": { "node": {} } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/tern/testdata/node/node_test.js����������������������������0000664�0000000�0000000�00000000033�13746324601�0025261�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������p = require( "path" ) p.jo �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/tern/testdata/requirejs_test.js����������������������������0000664�0000000�0000000�00000000646�13746324601�0025432�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������require( [ 'coollib/cool_object' ], function ( cool_object ) { cool_object. cool_object.min cool_object.gn } ); require( [ 'lamelib/lame_widget' ], function ( lame_widget ) { lame_widget. lame_widget.options. } ); require( [ 'coollib/cool_widget' ], function ( cool_widget ) { cool_widget.options. cool_widget. } ); require( 'no_such_lib/no_such_file', function( whatsit ) { whatsit. whatsit.ben } ); ������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/tern/testdata/simple_test.js�������������������������������0000664�0000000�0000000�00000000665�13746324601�0024713�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������var simple_test_obect = { 'a_simple_function': function( param ) { return 'yes'; }, 'basic_type': 100, 'object': { 'basic_type': 'test_string' } }; var simple_assignment = simple_test_obect. var query_assignment = simple_test_obect.typ var query_assignment = simple_test_obect.asf function blah( simple_test_obect ) { simple_test_obect.a_simple_function; } blah( simple_test_obect ); simple_test_obect = null; ���������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/tern/testdata/simple_test.modified.js����������������������0000664�0000000�0000000�00000000723�13746324601�0026465�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ var simple_test_obect = { 'a_simple_function': function( param ) { return 'yes'; }, 'basic_type': 100, 'object': { 'basic_type': 'test_string' } }; var simple_assignment = simple_test_obect. var query_assignment = simple_test_obect.typ var query_assignment = simple_test_obect.asf function blah( simple_test_obect ) { simple_test_obect.a_simple_function; } blah( simple_test_obect ); simple_test_obect = null; ���������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/tern/testdata/trivial.js�����������������������������������0000664�0000000�0000000�00000000053�13746324601�0024024�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������var X = { y: 'string', z: 'integer' }; �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/tern/testdata/trivial2.js����������������������������������0000664�0000000�0000000�00000000003�13746324601�0024101�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������X. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/tern/testdata/unicode.js�����������������������������������0000664�0000000�0000000�00000000220�13746324601�0023774�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������var x = 't'.cha var y = '†'.cha var x = 't'.cha var øbjecø = { /* unicøde comment */ 'ålpha': '∫eta' }; øbjecø.a øbjecø.ålpha ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/test_utils.py����������������������������������������������0000664�0000000�0000000�00000035260�13746324601�0022014�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2013-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_exactly, contains_string, empty, equal_to, has_entries, has_entry, has_item ) from unittest.mock import patch from pprint import pformat from webtest import TestApp import bottle import contextlib import pytest import functools import os import tempfile import time import stat import shutil import json from ycmd import extra_conf_store, handlers, user_options_store from ycmd.completers.completer import Completer from ycmd.responses import BuildCompletionData from ycmd.utils import ( GetCurrentDirectory, ImportCore, OnMac, OnWindows, ToUnicode, WaitUntilProcessIsTerminated ) ycm_core = ImportCore() from unittest import skipIf TESTS_DIR = os.path.abspath( os.path.dirname( __file__ ) ) TEST_OPTIONS = { # The 'client' represented by the tests supports on-demand resolve, but the # server default config doesn't for backward compatibility 'max_num_candidates_to_detail': 10 } WindowsOnly = skipIf( not OnWindows(), 'Windows only' ) ClangOnly = skipIf( not ycm_core.HasClangSupport(), 'Only when Clang support available' ) MacOnly = skipIf( not OnMac(), 'Mac only' ) UnixOnly = skipIf( OnWindows(), 'Unix only' ) EMPTY_SIGNATURE_HELP = has_entries( { 'activeParameter': 0, 'activeSignature': 0, 'signatures': empty(), } ) def BuildRequest( **kwargs ): filepath = kwargs[ 'filepath' ] if 'filepath' in kwargs else '/foo' contents = kwargs[ 'contents' ] if 'contents' in kwargs else '' filetype = kwargs[ 'filetype' ] if 'filetype' in kwargs else 'foo' filetypes = kwargs[ 'filetypes' ] if 'filetypes' in kwargs else [ filetype ] request = { 'line_num': 1, 'column_num': 1, 'filepath': filepath, 'file_data': { filepath: { 'contents': contents, 'filetypes': filetypes } } } for key, value in kwargs.items(): if key in [ 'contents', 'filetype', 'filepath' ]: continue if key in request and isinstance( request[ key ], dict ): # allow updating the 'file_data' entry request[ key ].update( value ) else: request[ key ] = value return request def CombineRequest( request, data ): kwargs = request.copy() kwargs.update( data ) return BuildRequest( **kwargs ) def ErrorMatcher( cls, msg = None ): """ Returns a hamcrest matcher for a server exception response """ entry = { 'exception': has_entry( 'TYPE', cls.__name__ ) } if msg: entry.update( { 'message': msg } ) return has_entries( entry ) def CompletionEntryMatcher( insertion_text, extra_menu_info = None, extra_params = None ): match = { 'insertion_text': insertion_text } if extra_menu_info: match.update( { 'extra_menu_info': extra_menu_info } ) if extra_params: match.update( extra_params ) return has_entries( match ) def MessageMatcher( msg ): return has_entry( 'message', contains_string( msg ) ) def LocationMatcher( filepath, line_num, column_num, description=None ): entry = { 'line_num': line_num, 'column_num': column_num, 'filepath': filepath } if description is not None: entry[ 'description' ] = description return has_entries( entry ) def RangeMatcher( filepath, start, end ): return has_entries( { 'start': LocationMatcher( filepath, *start ), 'end': LocationMatcher( filepath, *end ), } ) def ChunkMatcher( replacement_text, start, end ): return has_entries( { 'replacement_text': replacement_text, 'range': has_entries( { 'start': start, 'end': end } ) } ) def LineColMatcher( line, col ): return has_entries( { 'line_num': line, 'column_num': col } ) def CompleterProjectDirectoryMatcher( project_directory ): return has_entry( 'completer', has_entry( 'servers', contains_exactly( has_entry( 'extras', has_item( has_entries( { 'key': 'Project Directory', 'value': project_directory, } ) ) ) ) ) ) def SignatureMatcher( label, parameters ): return has_entries( { 'label': equal_to( label ), 'parameters': contains_exactly( *parameters ) } ) def SignatureAvailableMatcher( available ): return has_entries( { 'available': equal_to( available ) } ) def ParameterMatcher( begin, end ): return has_entries( { 'label': contains_exactly( begin, end ) } ) @contextlib.contextmanager def PatchCompleter( completer, filetype ): user_options = handlers._server_state._user_options with patch.dict( 'ycmd.handlers._server_state._filetype_completers', { filetype: completer( user_options ) } ): yield @contextlib.contextmanager def CurrentWorkingDirectory( path ): old_cwd = GetCurrentDirectory() os.chdir( path ) try: yield old_cwd finally: os.chdir( old_cwd ) # The "exe" suffix is needed on Windows and not harmful on other platforms. @contextlib.contextmanager def TemporaryExecutable( extension = '.exe' ): with tempfile.NamedTemporaryFile( prefix = 'Temp', suffix = extension ) as executable: os.chmod( executable.name, stat.S_IXUSR ) yield executable.name @contextlib.contextmanager def TemporarySymlink( source, link ): os.symlink( source, link ) try: yield finally: os.remove( link ) def SetUpApp( custom_options = {} ): bottle.debug( True ) options = user_options_store.DefaultOptions() options.update( TEST_OPTIONS ) options.update( custom_options ) handlers.UpdateUserOptions( options ) extra_conf_store.Reset() return TestApp( handlers.app ) @contextlib.contextmanager def IgnoreExtraConfOutsideTestsFolder(): with patch( 'ycmd.utils.IsRootDirectory', lambda path, parent: path in [ parent, TESTS_DIR ] ): yield @contextlib.contextmanager def IsolatedApp( custom_options = {} ): old_server_state = handlers._server_state old_extra_conf_store_state = extra_conf_store.Get() old_options = user_options_store.GetAll() try: with IgnoreExtraConfOutsideTestsFolder(): yield SetUpApp( custom_options ) finally: handlers._server_state = old_server_state extra_conf_store.Set( old_extra_conf_store_state ) user_options_store.SetAll( old_options ) def StartCompleterServer( app, filetype, filepath = '/foo' ): app.post_json( '/run_completer_command', BuildRequest( command_arguments = [ 'RestartServer' ], filetype = filetype, filepath = filepath ) ) def StopCompleterServer( app, filetype, filepath = '/foo' ): app.post_json( '/run_completer_command', BuildRequest( command_arguments = [ 'StopServer' ], filetype = filetype, filepath = filepath ), expect_errors = True ) def WaitUntilCompleterServerReady( app, filetype, timeout = 30 ): expiration = time.time() + timeout while True: if time.time() > expiration: raise RuntimeError( f'Waited for the { filetype } subserver to be ready ' f'for { timeout } seconds, aborting.' ) if app.get( '/ready', { 'subserver': filetype } ).json: return time.sleep( 0.1 ) def MockProcessTerminationTimingOut( handle, timeout = 5 ): WaitUntilProcessIsTerminated( handle, timeout ) raise RuntimeError( f'Waited process to terminate for { timeout } seconds, ' 'aborting.' ) def ClearCompletionsCache(): """Invalidates cached completions for completers stored in the server state: filetype completers and general completers (identifier, filename, and ultisnips completers). This function is used when sharing the application between tests so that no completions are cached by previous tests.""" server_state = handlers._server_state for completer in server_state.GetLoadedFiletypeCompleters(): completer._completions_cache.Invalidate() general_completer = server_state.GetGeneralCompleter() for completer in general_completer._all_completers: completer._completions_cache.Invalidate() class DummyCompleter( Completer ): def __init__( self, user_options ): super().__init__( user_options ) def SupportedFiletypes( self ): return [] def ComputeCandidatesInner( self, request_data ): return [ BuildCompletionData( candidate ) for candidate in self.CandidatesList() ] # This method is here for testing purpose, so it can be mocked during tests def CandidatesList( self ): return [] def ExpectedFailure( reason, *exception_matchers ): """Defines a decorator to be attached to tests. This decorator marks the test as being known to fail, e.g. where documenting or exercising known incorrect behaviour. The parameters are: - |reason| a textual description of the reason for the known issue. This is used for the skip reason - |exception_matchers| additional arguments are hamcrest matchers to apply to the exception thrown. If the matchers don't match, then the test is marked as error, with the original exception. If the test fails (for the correct reason), then it is marked as skipped. If it fails for any other reason, it is marked as failed. If the test passes, then it is also marked as failed.""" def decorator( test ): @functools.wraps( test ) def Wrapper( *args, **kwargs ): try: test( *args, **kwargs ) except Exception as test_exception: # Ensure that we failed for the right reason test_exception_message = ToUnicode( test_exception ) try: for matcher in exception_matchers: assert_that( test_exception_message, matcher ) except AssertionError: # Failed for the wrong reason! import traceback print( 'Test failed for the wrong reason: ' + traceback.format_exc() ) # Real failure reason is the *original* exception, we're only trapping # and ignoring the exception that is expected. raise test_exception # Failed for the right reason pytest.skip( reason ) else: raise AssertionError( f'Test was expected to fail: { reason }' ) return Wrapper return decorator @contextlib.contextmanager def TemporaryTestDir(): """Context manager to execute a test with a temporary workspace area. The workspace is deleted upon completion of the test. This is useful particularly for testing project detection (e.g. compilation databases, etc.), by ensuring that the directory is empty and not affected by the user's filesystem.""" tmp_dir = tempfile.mkdtemp() try: yield tmp_dir finally: shutil.rmtree( tmp_dir ) def WithRetry( *args, **kwargs ): """Decorator to be applied to tests that retries the test over and over""" if len( args ) == 1 and callable( args[ 0 ] ): # We are the decorator f = args[ 0 ] def ReturnDecorator( wrapper ): return wrapper( f ) else: # We need to return the decorator def ReturnDecorator( wrapper ): return wrapper if os.environ.get( 'YCM_TEST_NO_RETRY' ) == 'XFAIL': return ReturnDecorator( pytest.mark.xfail( strict = False ) ) elif os.environ.get( 'YCM_TEST_NO_RETRY' ): # This is a "null" decorator return ReturnDecorator( lambda f: f ) else: opts = { 'reruns': 20, 'reruns_delay': 0.5 } opts.update( kwargs ) return ReturnDecorator( pytest.mark.flaky( **opts ) ) @contextlib.contextmanager def TemporaryClangProject( tmp_dir, compile_commands ): """Context manager to create a compilation database in a directory and delete it when the test completes. |tmp_dir| is the directory in which to create the database file (typically used in conjunction with |TemporaryTestDir|) and |compile_commands| is a python object representing the compilation database. e.g.: with TemporaryTestDir() as tmp_dir: database = [ { 'directory': os.path.join( tmp_dir, dir ), 'command': compiler_invocation, 'file': os.path.join( tmp_dir, dir, filename ) }, ... ] with TemporaryClangProject( tmp_dir, database ): <test here> The context manager does not yield anything. """ path = os.path.join( tmp_dir, 'compile_commands.json' ) with open( path, 'w' ) as f: f.write( ToUnicode( json.dumps( compile_commands, indent = 2 ) ) ) try: yield finally: os.remove( path ) def WaitForDiagnosticsToBeReady( app, filepath, contents, filetype, **kwargs ): results = None for tries in range( 0, 60 ): event_data = BuildRequest( event_name = 'FileReadyToParse', contents = contents, filepath = filepath, filetype = filetype, **kwargs ) results = app.post_json( '/event_notification', event_data ).json if results: break time.sleep( 0.5 ) return results class PollForMessagesTimeoutException( Exception ): pass def PollForMessages( app, request_data, timeout = 60 ): expiration = time.time() + timeout while True: if time.time() > expiration: raise PollForMessagesTimeoutException( 'Waited for diagnostics to be ' f'ready for { timeout } seconds, aborting.' ) default_args = { 'line_num' : 1, 'column_num': 1, } args = dict( default_args ) args.update( request_data ) response = app.post_json( '/receive_messages', BuildRequest( **args ) ).json print( f'poll response: { pformat( response ) }' ) if isinstance( response, bool ): if not response: raise RuntimeError( 'The message poll was aborted by the server' ) elif isinstance( response, list ): for message in response: yield message else: raise AssertionError( f'Message poll response was wrong type: { type( response ).__name__ }' ) time.sleep( 0.25 ) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/��������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0021046�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/basic.tags����������������������������������������0000664�0000000�0000000�00000000745�13746324601�0023015�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ !_TAG_FILE_SORTED 2 /0=unsorted, 1=sorted, 2=foldcase/ !_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/ !_TAG_PROGRAM_NAME Exuberant Ctags // !_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/ !_TAG_PROGRAM_VERSION 5.8 // i1 foo junky;'junklanguage:C++ i1 bar junky;'junklanguage:C++ foosy foo junky;"'junk language:C++ zanzibar fooaaa bar junky;"'junk language:C++ zanzibar ���������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/client/�������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0022324�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/client/.ycm_extra_conf.py�������������������������0000664�0000000�0000000�00000000000�13746324601�0025742�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/client/cs_solution.sln����������������������������0000664�0000000�0000000�00000000000�13746324601�0025371�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/client/some_file����������������������������������0000664�0000000�0000000�00000000000�13746324601�0024177�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/extra_conf/���������������������������������������0000775�0000000�0000000�00000000000�13746324601�0023176�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/extra_conf/erroneous_extra_conf.py����������������0000664�0000000�0000000�00000000046�13746324601�0030001�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������raise Exception( 'Exception raised' ) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/extra_conf/global_extra_conf.py�������������������0000664�0000000�0000000�00000000136�13746324601�0027220�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������def NoException(): pass def RaiseException(): raise Exception( 'Exception raised' ) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/extra_conf/project/�������������������������������0000775�0000000�0000000�00000000000�13746324601�0024644�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/extra_conf/project/.ycm_extra_conf.py�������������0000664�0000000�0000000�00000000000�13746324601�0030262�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/filename_completer/�������������������������������0000775�0000000�0000000�00000000000�13746324601�0024700�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/filename_completer/inner_dir/���������������������0000775�0000000�0000000�00000000000�13746324601�0026651�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������dir with spaces (x64)/������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0032266�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/filename_completer/inner_dir�����������������������������������������������������������������������������������������Qt/�������������������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0032652�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/filename_completer/inner_dir/dir with spaces (x64)�������������������������������������������������������������������QtGui�����������������������������������������������������������������������������������������������0000664�0000000�0000000�00000000050�13746324601�0033621�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/filename_completer/inner_dir/dir with spaces (x64)/Qt����������������������������������������������������������������// This file includes all QtGui headers ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������QtGui/����������������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0033317�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/filename_completer/inner_dir/dir with spaces (x64)�������������������������������������������������������������������QDialog���������������������������������������������������������������������������������������������0000664�0000000�0000000�00000000000�13746324601�0034550�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/filename_completer/inner_dir/dir with spaces (x64)/QtGui�������������������������������������������������������������QWidget���������������������������������������������������������������������������������������������0000664�0000000�0000000�00000000000�13746324601�0034574�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/filename_completer/inner_dir/dir with spaces (x64)/QtGui�������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/filename_completer/inner_dir/foo漢字.txt��������0000664�0000000�0000000�00000000000�13746324601�0032340�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/filename_completer/inner_dir/test.cpp�������������0000664�0000000�0000000�00000000072�13746324601�0030333�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "test.hpp" #include <QtGui> const char* c = ""; ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/filename_completer/inner_dir/test.hpp�������������0000664�0000000�0000000�00000000000�13746324601�0030327�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/filename_completer/∂†∫/���������������������0000775�0000000�0000000�00000000000�13746324601�0027762�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/filename_completer/∂†∫/†es†.txt���������0000664�0000000�0000000�00000000000�13746324601�0033124�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/python-future/������������������������������������0000775�0000000�0000000�00000000000�13746324601�0023677�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/python-future/embedded_standard_library/����������0000775�0000000�0000000�00000000000�13746324601�0031034�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������python35.zip����������������������������������������������������������������������������������������0000664�0000000�0000000�00000000000�13746324601�0033160�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/python-future/embedded_standard_library������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/python-future/standard_library/�������������������0000775�0000000�0000000�00000000000�13746324601�0027223�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/python-future/standard_library/os.py��������������0000664�0000000�0000000�00000000000�13746324601�0030204�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/python-future/virtualenv_library/�����������������0000775�0000000�0000000�00000000000�13746324601�0027622�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/python-future/virtualenv_library/orig-prefix.txt��0000664�0000000�0000000�00000000000�13746324601�0032604�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/python-future/virtualenv_library/os.py������������0000664�0000000�0000000�00000000000�13746324601�0030603�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/unix/���������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0022031�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/unix/compile_commands.json������������������������0000664�0000000�0000000�00000000142�13746324601�0026232�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ { "directory": "/dir", "command": "/usr/bin/clang++ example.cc", "file": "example.cc" } ] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/uni¢od€.py�������������������������������������0000664�0000000�0000000�00000000040�13746324601�0024314�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������def SomeMethod(): return True ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/windows/������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0022540�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/testdata/windows/compile_commands.json���������������������0000664�0000000�0000000�00000000145�13746324601�0026744�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������[ { "directory": "C:\\dir", "command": "/usr/bin/clang++ example.cc", "file": "example.cc" } ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/typescript/������������������������������������������������0000775�0000000�0000000�00000000000�13746324601�0021443�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/typescript/__init__.py�������������������������������������0000664�0000000�0000000�00000001623�13746324601�0023556�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import os from ycmd.tests.typescript.conftest import * # noqa def PathToTestFile( *args ): dir_of_current_script = os.path.dirname( os.path.abspath( __file__ ) ) return os.path.join( dir_of_current_script, 'testdata', *args ) �������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/typescript/conftest.py�������������������������������������0000664�0000000�0000000�00000006407�13746324601�0023651�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import pytest from ycmd.tests.test_utils import ( ClearCompletionsCache, IgnoreExtraConfOutsideTestsFolder, IsolatedApp, SetUpApp, StopCompleterServer, WaitUntilCompleterServerReady ) shared_app = None @pytest.fixture( scope='module', autouse=True ) def set_up_shared_app(): global shared_app shared_app = SetUpApp() WaitUntilCompleterServerReady( shared_app, 'typescript' ) yield StopCompleterServer( shared_app, 'typescript' ) @pytest.fixture def app( request ): which = request.param[ 0 ] assert which == 'isolated' or which == 'shared' if which == 'isolated': with IsolatedApp( request.param[ 1 ] ) as app: yield app StopCompleterServer( app, 'typescript' ) else: global shared_app ClearCompletionsCache() with IgnoreExtraConfOutsideTestsFolder(): yield shared_app """Defines a decorator to be attached to tests of this package. This decorator passes the shared ycmd application as a parameter.""" SharedYcmd = pytest.mark.parametrize( # Name of the fixture/function argument 'app', # Fixture parameters, passed to app() as request.param [ ( 'shared', ) ], # Non-empty ids makes fixture parameters visible in pytest verbose output ids = [ '' ], # Execute the fixture, instead of passing parameters directly to the # function argument indirect = True ) def IsolatedYcmd( custom_options = {} ): """Defines a decorator to be attached to tests of this package. This decorator passes a unique ycmd application as a parameter. It should be used on tests that change the server state in a irreversible way (ex: a semantic subserver is stopped or restarted) or expect a clean state (ex: no semantic subserver started, no .ycm_extra_conf.py loaded, etc). Use the optional parameter |custom_options| to give additional options and/or override the default ones. Example usage: from ycmd.tests.python import IsolatedYcmd @IsolatedYcmd( { 'python_binary_path': '/some/path' } ) def CustomPythonBinaryPath_test( app ): ... """ return pytest.mark.parametrize( # Name of the fixture/function argument 'app', # Fixture parameters, passed to app() as request.param [ ( 'isolated', custom_options ) ], # Non-empty ids makes fixture parameters visible in pytest verbose output ids = [ '' ], # Execute the fixture, instead of passing parameters directly to the # function argument indirect = True ) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/typescript/debug_info_test.py������������������������������0000664�0000000�0000000�00000003424�13746324601�0025160�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2016-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( any_of, assert_that, contains_exactly, has_entries, has_entry, instance_of ) from ycmd.tests.typescript import SharedYcmd from ycmd.tests.test_utils import BuildRequest @SharedYcmd def DebugInfo_test( app ): request_data = BuildRequest( filetype = 'typescript' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entries( { 'name': 'TypeScript', 'servers': contains_exactly( has_entries( { 'name': 'TSServer', 'is_running': True, 'executable': instance_of( str ), 'pid': instance_of( int ), 'address': None, 'port': None, 'logfiles': contains_exactly( instance_of( str ) ), 'extras': contains_exactly( has_entries( { 'key': 'version', 'value': any_of( None, instance_of( str ) ) } ) ) } ) ) } ) ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/typescript/diagnostics_test.py�����������������������������0000664�0000000�0000000�00000013556�13746324601�0025375�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2017-2020 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 <http://www.gnu.org/licenses/>. from __future__ import absolute_import from __future__ import unicode_literals from __future__ import print_function from __future__ import division from builtins import * # noqa from hamcrest import ( assert_that, contains_exactly, contains_inanyorder, has_entries, has_entry ) from ycmd.tests.typescript import IsolatedYcmd, PathToTestFile, SharedYcmd from ycmd.tests.test_utils import BuildRequest, LocationMatcher, RangeMatcher from ycmd.utils import ReadFile @SharedYcmd def Diagnostics_FileReadyToParse_test( app ): filepath = PathToTestFile( 'test.ts' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'typescript', contents = contents, event_name = 'BufferVisit' ) app.post_json( '/event_notification', event_data ) event_data = BuildRequest( filepath = filepath, filetype = 'typescript', contents = contents, event_name = 'FileReadyToParse' ) assert_that( app.post_json( '/event_notification', event_data ).json, contains_inanyorder( has_entries( { 'kind': 'ERROR', 'text': "Property 'mA' does not exist on type 'Foo'.", 'location': LocationMatcher( filepath, 17, 5 ), 'location_extent': RangeMatcher( filepath, ( 17, 5 ), ( 17, 7 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 17, 5 ), ( 17, 7 ) ) ), 'fixit_available': True } ), has_entries( { 'kind': 'ERROR', 'text': "Property 'nonExistingMethod' does not exist on type 'Bar'.", 'location': LocationMatcher( filepath, 35, 5 ), 'location_extent': RangeMatcher( filepath, ( 35, 5 ), ( 35, 22 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 35, 5 ), ( 35, 22 ) ) ), 'fixit_available': True } ), has_entries( { 'kind': 'ERROR', 'text': 'Expected 1-2 arguments, but got 0.', 'location': LocationMatcher( filepath, 37, 5 ), 'location_extent': RangeMatcher( filepath, ( 37, 5 ), ( 37, 12 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 37, 5 ), ( 37, 12 ) ) ), 'fixit_available': False } ), has_entries( { 'kind': 'ERROR', 'text': "Cannot find name 'Bår'.", 'location': LocationMatcher( filepath, 39, 1 ), 'location_extent': RangeMatcher( filepath, ( 39, 1 ), ( 39, 5 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 39, 1 ), ( 39, 5 ) ) ), 'fixit_available': True } ), ) ) @SharedYcmd def Diagnostics_DetailedDiagnostics_test( app ): filepath = PathToTestFile( 'test.ts' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'typescript', contents = contents, event_name = 'BufferVisit' ) app.post_json( '/event_notification', event_data ) diagnostic_data = BuildRequest( filepath = filepath, filetype = 'typescript', contents = contents, line_num = 35, column_num = 6 ) assert_that( app.post_json( '/detailed_diagnostic', diagnostic_data ).json, has_entry( 'message', "Property 'nonExistingMethod' does not exist on type 'Bar'." ) ) @IsolatedYcmd( { 'max_diagnostics_to_display': 1 } ) def Diagnostics_MaximumDiagnosticsNumberExceeded_test( app ): filepath = PathToTestFile( 'test.ts' ) contents = ReadFile( filepath ) event_data = BuildRequest( filepath = filepath, filetype = 'typescript', contents = contents, event_name = 'BufferVisit' ) app.post_json( '/event_notification', event_data ) event_data = BuildRequest( filepath = filepath, filetype = 'typescript', contents = contents, event_name = 'FileReadyToParse' ) assert_that( app.post_json( '/event_notification', event_data ).json, contains_inanyorder( has_entries( { 'kind': 'ERROR', 'text': "Property 'mA' does not exist on type 'Foo'.", 'location': LocationMatcher( filepath, 17, 5 ), 'location_extent': RangeMatcher( filepath, ( 17, 5 ), ( 17, 7 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 17, 5 ), ( 17, 7 ) ) ), 'fixit_available': True } ), has_entries( { 'kind': 'ERROR', 'text': 'Maximum number of diagnostics exceeded.', 'location': LocationMatcher( filepath, 1, 1 ), 'location_extent': RangeMatcher( filepath, ( 1, 1 ), ( 1, 1 ) ), 'ranges': contains_exactly( RangeMatcher( filepath, ( 1, 1 ), ( 1, 1 ) ) ), 'fixit_available': False } ), ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ��������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/typescript/event_notification_test.py����������������������0000664�0000000�0000000�00000011262�13746324601�0026745�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2016-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import assert_that, contains_exactly, has_entries from ycmd.tests.typescript import IsolatedYcmd, PathToTestFile from ycmd.tests.test_utils import BuildRequest, CompletionEntryMatcher from ycmd.utils import ReadFile @IsolatedYcmd() def EventNotification_OnBufferUnload_CloseFile_test( app ): # Open main.ts file in a buffer. main_filepath = PathToTestFile( 'buffer_unload', 'main.ts' ) main_contents = ReadFile( main_filepath ) event_data = BuildRequest( filepath = main_filepath, filetype = 'typescript', contents = main_contents, event_name = 'BufferVisit' ) app.post_json( '/event_notification', event_data ) # Complete in main.ts buffer an object defined in imported.ts. completion_data = BuildRequest( filepath = main_filepath, filetype = 'typescript', contents = main_contents, line_num = 3, column_num = 10 ) response = app.post_json( '/completions', completion_data ) assert_that( response.json, has_entries( { 'completions': contains_exactly( CompletionEntryMatcher( 'method' ) ) } ) ) # Open imported.ts file in another buffer. imported_filepath = PathToTestFile( 'buffer_unload', 'imported.ts' ) imported_contents = ReadFile( imported_filepath ) event_data = BuildRequest( filepath = imported_filepath, filetype = 'typescript', contents = imported_contents, event_name = 'BufferVisit' ) app.post_json( '/event_notification', event_data ) # Modify imported.ts buffer without writing the changes to disk. modified_imported_contents = imported_contents.replace( 'method', 'modified_method' ) # FIXME: TypeScript completer should not rely on the FileReadyToParse events # to synchronize the contents of dirty buffers but use instead the file_data # field of the request. event_data = BuildRequest( filepath = imported_filepath, filetype = 'typescript', contents = modified_imported_contents, event_name = 'FileReadyToParse' ) app.post_json( '/event_notification', event_data ) # Complete at same location in main.ts buffer. imported_data = { imported_filepath: { 'filetypes': [ 'typescript' ], 'contents': modified_imported_contents } } completion_data = BuildRequest( filepath = main_filepath, filetype = 'typescript', contents = main_contents, line_num = 3, column_num = 10, file_data = imported_data ) response = app.post_json( '/completions', completion_data ) assert_that( response.json, has_entries( { 'completions': contains_exactly( CompletionEntryMatcher( 'modified_method' ) ) } ) ) # Unload imported.ts buffer. event_data = BuildRequest( filepath = imported_filepath, filetype = 'typescript', contents = imported_contents, event_name = 'BufferUnload' ) app.post_json( '/event_notification', event_data ) # Complete at same location in main.ts buffer. completion_data = BuildRequest( filepath = main_filepath, filetype = 'typescript', contents = main_contents, line_num = 3, column_num = 10 ) response = app.post_json( '/completions', completion_data ) assert_that( response.json, has_entries( { 'completions': contains_exactly( CompletionEntryMatcher( 'method' ) ) } ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/typescript/get_completions_test.py�������������������������0000664�0000000�0000000�00000026036�13746324601�0026256�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2015-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, calling, contains_exactly, contains_inanyorder, equal_to, has_entries, has_item, matches_regexp, raises ) from webtest import AppError import pprint import requests from ycmd.tests.typescript import IsolatedYcmd, PathToTestFile, SharedYcmd from ycmd.tests.test_utils import ( BuildRequest, ChunkMatcher, CombineRequest, CompletionEntryMatcher, LocationMatcher, StopCompleterServer ) from ycmd.utils import ReadFile def RunTest( app, test ): contents = ReadFile( test[ 'request' ][ 'filepath' ] ) filetype = test[ 'request' ].get( 'filetype', 'typescript' ) app.post_json( '/event_notification', CombineRequest( test[ 'request' ], { 'contents': contents, 'filetype': filetype, 'event_name': 'BufferVisit' } ) ) response = app.post_json( '/completions', CombineRequest( test[ 'request' ], { 'contents': contents, 'filetype': 'typescript', 'force_semantic': True } ) ) print( f'completer response: { pprint.pformat( response.json ) }' ) assert_that( response.status_code, equal_to( test[ 'expect' ][ 'response' ] ) ) assert_that( response.json, test[ 'expect' ][ 'data' ] ) @SharedYcmd def GetCompletions_Basic_test( app ): RunTest( app, { 'description': 'Extra and detailed info when completions are methods', 'request': { 'line_num': 17, 'column_num': 6, 'filepath': PathToTestFile( 'test.ts' ) }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_inanyorder( CompletionEntryMatcher( 'methodA', '(method) Foo.methodA(): void', extra_params = { 'kind': 'method', 'detailed_info': '(method) Foo.methodA(): void\n\n' 'Unicode string: 说话' } ), CompletionEntryMatcher( 'methodB', '(method) Foo.methodB(): void', extra_params = { 'kind': 'method', 'detailed_info': '(method) Foo.methodB(): void' } ), CompletionEntryMatcher( 'methodC', '(method) Foo.methodC(a: { foo: string; bar: number; }): void', extra_params = { 'kind': 'method', 'detailed_info': '(method) Foo.methodC(a: {\n' ' foo: string;\n' ' bar: number;\n' '}): void' } ) ) } ) } } ) RunTest( app, { 'description': 'Filtering works', 'request': { 'line_num': 17, 'column_num': 7, 'filepath': PathToTestFile( 'test.ts' ) }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_inanyorder( CompletionEntryMatcher( 'methodA', '(method) Foo.methodA(): void', extra_params = { 'kind': 'method', 'detailed_info': '(method) Foo.methodA(): void\n\n' 'Unicode string: 说话' } ) ) } ) } } ) @IsolatedYcmd( { 'disable_signature_help': True } ) def GetCompletions_Basic_NoSigHelp_test( app ): RunTest( app, { 'description': 'Extra and detailed info when completions are methods', 'request': { 'line_num': 17, 'column_num': 6, 'filepath': PathToTestFile( 'test.ts' ) }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': contains_inanyorder( CompletionEntryMatcher( 'methodA', '(method) Foo.methodA(): void', extra_params = { 'kind': 'method', 'detailed_info': '(method) Foo.methodA(): void\n\n' 'Unicode string: 说话' } ), CompletionEntryMatcher( 'methodB', '(method) Foo.methodB(): void', extra_params = { 'kind': 'method', 'detailed_info': '(method) Foo.methodB(): void' } ), CompletionEntryMatcher( 'methodC', '(method) Foo.methodC(a: { foo: string; bar: number; }): void', extra_params = { 'kind': 'method', 'detailed_info': '(method) Foo.methodC(a: {\n' ' foo: string;\n' ' bar: number;\n' '}): void' } ) ) } ) } } ) @SharedYcmd def GetCompletions_Keyword_test( app ): RunTest( app, { 'description': 'No extra and detailed info when completion is a keyword', 'request': { 'line_num': 2, 'column_num': 5, 'filepath': PathToTestFile( 'test.ts' ), }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': has_item( { 'insertion_text': 'class', 'kind': 'keyword', 'extra_data': {} } ) } ) } } ) @SharedYcmd def GetCompletions_AfterRestart_test( app ): filepath = PathToTestFile( 'test.ts' ) app.post_json( '/run_completer_command', BuildRequest( completer_target = 'filetype_default', command_arguments = [ 'RestartServer' ], filetype = 'typescript', filepath = filepath ) ) completion_data = BuildRequest( filepath = filepath, filetype = 'typescript', contents = ReadFile( filepath ), force_semantic = True, line_num = 17, column_num = 6 ) assert_that( app.post_json( '/completions', completion_data ).json, has_entries( { 'completions': contains_inanyorder( CompletionEntryMatcher( 'methodA', '(method) Foo.methodA(): void', extra_params = { 'kind': 'method' } ), CompletionEntryMatcher( 'methodB', '(method) Foo.methodB(): void', extra_params = { 'kind': 'method', 'detailed_info': '(method) Foo.methodB(): void' } ), CompletionEntryMatcher( 'methodC', '(method) Foo.methodC(a: { foo: string; bar: number; }): void', extra_params = { 'kind': 'method', 'detailed_info': '(method) Foo.methodC(a: {\n' ' foo: string;\n' ' bar: number;\n' '}): void' } ) ) } ) ) @IsolatedYcmd() def GetCompletions_ServerIsNotRunning_test( app ): StopCompleterServer( app, filetype = 'typescript' ) filepath = PathToTestFile( 'test.ts' ) contents = ReadFile( filepath ) # Check that sending a request to TSServer (the response is ignored) raises # the proper exception. event_data = BuildRequest( filepath = filepath, filetype = 'typescript', contents = contents, event_name = 'BufferVisit' ) assert_that( calling( app.post_json ).with_args( '/event_notification', event_data ), raises( AppError, 'TSServer is not running.' ) ) # Check that sending a command to TSServer (the response is processed) raises # the proper exception. completion_data = BuildRequest( filepath = filepath, filetype = 'typescript', contents = contents, force_semantic = True, line_num = 17, column_num = 6 ) assert_that( calling( app.post_json ).with_args( '/completions', completion_data ), raises( AppError, 'TSServer is not running.' ) ) @SharedYcmd def GetCompletions_AutoImport_test( app ): filepath = PathToTestFile( 'test.ts' ) RunTest( app, { 'description': 'Symbol from external module can be completed and ' 'its completion contains fixits to automatically import it', 'request': { 'line_num': 39, 'column_num': 5, 'filepath': filepath, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': has_item( has_entries( { 'insertion_text': 'Bår', 'extra_menu_info': 'class Bår', 'detailed_info': 'class Bår', 'kind': 'class', 'extra_data': has_entries( { 'fixits': contains_inanyorder( has_entries( { 'text': 'Import \'Bår\' from module "./unicode"', 'chunks': contains_exactly( ChunkMatcher( matches_regexp( '^import { Bår } from "./unicode";\r?\n' ), LocationMatcher( filepath, 1, 1 ), LocationMatcher( filepath, 1, 1 ) ) ), 'location': LocationMatcher( filepath, 39, 5 ) } ) ) } ) } ) ) } ) } } ) @SharedYcmd def GetCompletions_TypeScriptReact_DefaultTriggers_test( app ): filepath = PathToTestFile( 'test.tsx' ) RunTest( app, { 'description': 'No need to force after a semantic trigger', 'request': { 'line_num': 17, 'column_num': 3, 'filepath': filepath, 'filetype': 'typescriptreact' }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'completions': has_item( has_entries( { 'insertion_text': 'foo', 'extra_menu_info': "(property) 'foo': number", 'detailed_info': "(property) 'foo': number", 'kind': 'property', } ) ) } ) } } ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/typescript/signature_help_test.py��������������������������0000664�0000000�0000000�00000017212�13746324601�0026070�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from hamcrest import assert_that, contains_exactly, empty, equal_to, has_entries import requests from ycmd.tests.typescript import PathToTestFile, IsolatedYcmd, SharedYcmd from ycmd.tests.test_utils import ( CombineRequest, ParameterMatcher, SignatureMatcher, SignatureAvailableMatcher, WaitUntilCompleterServerReady ) from ycmd.utils import ReadFile def RunTest( app, test ): """ Method to run a simple signature help test and verify the result test is a dictionary containing: 'request': kwargs for BuildRequest 'expect': { 'response': server response code (e.g. httplib.OK) 'data': matcher for the server response json } """ contents = ReadFile( test[ 'request' ][ 'filepath' ] ) WaitUntilCompleterServerReady( app, 'typescript' ) app.post_json( '/event_notification', CombineRequest( test[ 'request' ], { 'event_name': 'BufferVisit', 'contents': contents, 'filetype': 'typescript', } ), expect_errors = True ) # We ignore errors here and we check the response code ourself. # This is to allow testing of requests returning errors. response = app.post_json( '/signature_help', CombineRequest( test[ 'request' ], { 'contents': contents, 'filetype': 'typescript', } ), expect_errors = True ) assert_that( response.status_code, equal_to( test[ 'expect' ][ 'response' ] ) ) assert_that( response.json, test[ 'expect' ][ 'data' ] ) @SharedYcmd def Signature_Help_Available_test( app ): response = app.get( '/signature_help_available', { 'subserver': 'typescript' } ).json assert_that( response, SignatureAvailableMatcher( 'YES' ) ) # Triggering on '(', ',' and '<' @SharedYcmd def Signature_Help_Trigger_Paren_test( app ): RunTest( app, { 'description': 'Trigger after (', 'request': { 'filetype' : 'typescript', 'filepath' : PathToTestFile( 'signatures.ts' ), 'line_num' : 27, 'column_num': 29, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': contains_exactly( SignatureMatcher( 'single_argument_with_return(a: string): string', [ ParameterMatcher( 28, 37 ) ] ) ), } ), } ) } } ) @IsolatedYcmd( { 'disable_signature_help': True } ) def Signature_Help_Trigger_Paren_Disabled_test( app ): RunTest( app, { 'description': 'Trigger after (', 'request': { 'filetype' : 'typescript', 'filepath' : PathToTestFile( 'signatures.ts' ), 'line_num' : 27, 'column_num': 29, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': empty() } ), } ) } } ) @SharedYcmd def Signature_Help_Trigger_Comma_test( app ): RunTest( app, { 'description': 'Trigger after ,', 'request': { 'filetype' : 'typescript', 'filepath' : PathToTestFile( 'signatures.ts' ), 'line_num' : 60, 'column_num': 32, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 1, 'signatures': contains_exactly( SignatureMatcher( ( 'multi_argument_no_return(løng_våriable_name: number, ' 'untyped_argument: any): number' ), [ ParameterMatcher( 25, 53 ), ParameterMatcher( 55, 76 ) ] ) ), } ), } ) } } ) @SharedYcmd def Signature_Help_Trigger_AngleBracket_test( app ): RunTest( app, { 'description': 'Trigger after <', 'request': { 'filetype' : 'typescript', 'filepath' : PathToTestFile( 'signatures.ts' ), 'line_num' : 68, 'column_num': 9, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': contains_exactly( SignatureMatcher( 'generic<TYPE extends ReturnValue>(t: SomeClass): string', [ ParameterMatcher( 8, 32 ) ] ) ), } ), } ) } } ) @SharedYcmd def Signature_Help_Multiple_Signatures_test( app ): RunTest( app, { 'description': 'Test overloaded methods', 'request': { 'filetype' : 'typescript', 'filepath' : PathToTestFile( 'signatures.ts' ), 'line_num' : 89, 'column_num': 18, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 1, 'activeParameter': 1, 'signatures': contains_exactly( SignatureMatcher( 'øverløåd(a: number): string', [ ParameterMatcher( 12, 21 ) ] ), SignatureMatcher( 'øverløåd(a: string, b: number): string', [ ParameterMatcher( 12, 21 ), ParameterMatcher( 23, 32 ) ] ) ), } ), } ) } } ) @SharedYcmd def Signature_Help_NoSignatures_test( app ): RunTest( app, { 'description': 'Test overloaded methods', 'request': { 'filetype' : 'typescript', 'filepath' : PathToTestFile( 'signatures.ts' ), 'line_num' : 68, 'column_num': 22, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': empty(), } ), } ) } } ) @SharedYcmd def Signature_Help_NoErrorWhenNoSignatureInfo_test( app ): RunTest( app, { 'description': 'Test dodgy (', 'request': { 'filetype' : 'typescript', 'filepath' : PathToTestFile( 'signatures.ts' ), 'line_num' : 92, 'column_num': 5, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'errors': empty(), 'signature_help': has_entries( { 'activeSignature': 0, 'activeParameter': 0, 'signatures': empty(), } ), } ) } } ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/typescript/subcommands_test.py�����������������������������0000664�0000000�0000000�00000110545�13746324601�0025375�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2015-2020 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 <http://www.gnu.org/licenses/>. from hamcrest import ( assert_that, contains_exactly, contains_inanyorder, equal_to, has_entries, has_entry, has_items, matches_regexp ) from unittest.mock import patch import requests import pprint import pytest from ycmd.tests.typescript import IsolatedYcmd, PathToTestFile, SharedYcmd from ycmd.tests.test_utils import ( BuildRequest, ChunkMatcher, CombineRequest, ErrorMatcher, LocationMatcher, MessageMatcher, MockProcessTerminationTimingOut, WaitUntilCompleterServerReady ) from ycmd.utils import ReadFile def RunTest( app, test ): contents = ReadFile( test[ 'request' ][ 'filepath' ] ) app.post_json( '/event_notification', CombineRequest( test[ 'request' ], { 'contents': contents, 'filetype': 'typescript', 'event_name': 'BufferVisit' } ) ) app.post_json( '/event_notification', CombineRequest( test[ 'request' ], { 'contents': contents, 'filetype': 'typescript', 'event_name': 'FileReadyToParse' } ) ) # We ignore errors here and check the response code ourself. # This is to allow testing of requests returning errors. response = app.post_json( '/run_completer_command', CombineRequest( test[ 'request' ], { 'contents': contents, 'filetype': 'typescript', 'command_arguments': ( [ test[ 'request' ][ 'command' ] ] + test[ 'request' ].get( 'arguments', [] ) ) } ), expect_errors = True ) print( f'completer response: { pprint.pformat( response.json ) }' ) assert_that( response.status_code, equal_to( test[ 'expect' ][ 'response' ] ) ) assert_that( response.json, test[ 'expect' ][ 'data' ] ) @SharedYcmd def Subcommands_DefinedSubcommands_test( app ): subcommands_data = BuildRequest( completer_target = 'typescript' ) assert_that( app.post_json( '/defined_subcommands', subcommands_data ).json, contains_inanyorder( 'Format', 'GoTo', 'GoToDeclaration', 'GoToDefinition', 'GoToImplementation', 'GoToType', 'GetDoc', 'GetType', 'GoToReferences', 'GoToSymbol', 'FixIt', 'OrganizeImports', 'RefactorRename', 'RestartServer' ) ) @SharedYcmd def Subcommands_Format_WholeFile_Spaces_test( app ): filepath = PathToTestFile( 'test.ts' ) RunTest( app, { 'description': 'Formatting is applied on the whole file ' 'with tabs composed of 4 spaces', 'request': { 'command': 'Format', 'filepath': filepath, 'options': { 'tab_size': 4, 'insert_spaces': True } }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( ' ', LocationMatcher( filepath, 3, 1 ), LocationMatcher( filepath, 3, 3 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 4, 1 ), LocationMatcher( filepath, 4, 3 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 4, 14 ), LocationMatcher( filepath, 4, 14 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 5, 1 ), LocationMatcher( filepath, 5, 3 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 5, 14 ), LocationMatcher( filepath, 5, 14 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 6, 1 ), LocationMatcher( filepath, 6, 3 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 7, 1 ), LocationMatcher( filepath, 7, 5 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 8, 1 ), LocationMatcher( filepath, 8, 7 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 9, 1 ), LocationMatcher( filepath, 9, 7 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 10, 1 ), LocationMatcher( filepath, 10, 5 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 11, 1 ), LocationMatcher( filepath, 11, 3 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 11, 6 ), LocationMatcher( filepath, 11, 6 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 21, 1 ), LocationMatcher( filepath, 21, 2 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 22, 1 ), LocationMatcher( filepath, 22, 2 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 23, 1 ), LocationMatcher( filepath, 23, 2 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 24, 1 ), LocationMatcher( filepath, 24, 2 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 27, 1 ), LocationMatcher( filepath, 27, 3 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 28, 1 ), LocationMatcher( filepath, 28, 4 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 29, 1 ), LocationMatcher( filepath, 29, 4 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 30, 1 ), LocationMatcher( filepath, 30, 3 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 30, 17 ), LocationMatcher( filepath, 30, 17 ) ), ) } ) ) } ) } } ) @SharedYcmd def Subcommands_Format_WholeFile_Tabs_test( app ): filepath = PathToTestFile( 'test.ts' ) RunTest( app, { 'description': 'Formatting is applied on the whole file ' 'with tabs composed of 2 spaces', 'request': { 'command': 'Format', 'filepath': filepath, 'options': { 'tab_size': 4, 'insert_spaces': False } }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( '\t', LocationMatcher( filepath, 3, 1 ), LocationMatcher( filepath, 3, 3 ) ), ChunkMatcher( '\t', LocationMatcher( filepath, 4, 1 ), LocationMatcher( filepath, 4, 3 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 4, 14 ), LocationMatcher( filepath, 4, 14 ) ), ChunkMatcher( '\t', LocationMatcher( filepath, 5, 1 ), LocationMatcher( filepath, 5, 3 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 5, 14 ), LocationMatcher( filepath, 5, 14 ) ), ChunkMatcher( '\t', LocationMatcher( filepath, 6, 1 ), LocationMatcher( filepath, 6, 3 ) ), ChunkMatcher( '\t\t', LocationMatcher( filepath, 7, 1 ), LocationMatcher( filepath, 7, 5 ) ), ChunkMatcher( '\t\t\t', LocationMatcher( filepath, 8, 1 ), LocationMatcher( filepath, 8, 7 ) ), ChunkMatcher( '\t\t\t', LocationMatcher( filepath, 9, 1 ), LocationMatcher( filepath, 9, 7 ) ), ChunkMatcher( '\t\t', LocationMatcher( filepath, 10, 1 ), LocationMatcher( filepath, 10, 5 ) ), ChunkMatcher( '\t', LocationMatcher( filepath, 11, 1 ), LocationMatcher( filepath, 11, 3 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 11, 6 ), LocationMatcher( filepath, 11, 6 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 21, 1 ), LocationMatcher( filepath, 21, 2 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 22, 1 ), LocationMatcher( filepath, 22, 2 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 23, 1 ), LocationMatcher( filepath, 23, 2 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 24, 1 ), LocationMatcher( filepath, 24, 2 ) ), ChunkMatcher( '\t', LocationMatcher( filepath, 27, 1 ), LocationMatcher( filepath, 27, 3 ) ), ChunkMatcher( '\t ', LocationMatcher( filepath, 28, 1 ), LocationMatcher( filepath, 28, 4 ) ), ChunkMatcher( '\t ', LocationMatcher( filepath, 29, 1 ), LocationMatcher( filepath, 29, 4 ) ), ChunkMatcher( '\t', LocationMatcher( filepath, 30, 1 ), LocationMatcher( filepath, 30, 3 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 30, 17 ), LocationMatcher( filepath, 30, 17 ) ), ) } ) ) } ) } } ) @SharedYcmd def Subcommands_Format_Range_Spaces_test( app ): filepath = PathToTestFile( 'test.ts' ) RunTest( app, { 'description': 'Formatting is applied on some part of the file ' 'with tabs composed of 4 spaces by default', 'request': { 'command': 'Format', 'filepath': filepath, 'range': { 'start': { 'line_num': 6, 'column_num': 3, }, 'end': { 'line_num': 11, 'column_num': 6 } }, 'options': { 'tab_size': 4, 'insert_spaces': True } }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( ' ', LocationMatcher( filepath, 6, 1 ), LocationMatcher( filepath, 6, 3 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 7, 1 ), LocationMatcher( filepath, 7, 5 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 8, 1 ), LocationMatcher( filepath, 8, 7 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 9, 1 ), LocationMatcher( filepath, 9, 7 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 10, 1 ), LocationMatcher( filepath, 10, 5 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 11, 1 ), LocationMatcher( filepath, 11, 3 ) ), ) } ) ) } ) } } ) @IsolatedYcmd() def Subcommands_Format_Range_Tabs_test( app ): WaitUntilCompleterServerReady( app, 'typescript' ) filepath = PathToTestFile( 'test.ts' ) RunTest( app, { 'description': 'Formatting is applied on some part of the file ' 'with tabs instead of spaces', 'request': { 'command': 'Format', 'filepath': filepath, 'range': { 'start': { 'line_num': 6, 'column_num': 3, }, 'end': { 'line_num': 11, 'column_num': 6 } }, 'options': { 'tab_size': 4, 'insert_spaces': False } }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( '\t', LocationMatcher( filepath, 6, 1 ), LocationMatcher( filepath, 6, 3 ) ), ChunkMatcher( '\t\t', LocationMatcher( filepath, 7, 1 ), LocationMatcher( filepath, 7, 5 ) ), ChunkMatcher( '\t\t\t', LocationMatcher( filepath, 8, 1 ), LocationMatcher( filepath, 8, 7 ) ), ChunkMatcher( '\t\t\t', LocationMatcher( filepath, 9, 1 ), LocationMatcher( filepath, 9, 7 ) ), ChunkMatcher( '\t\t', LocationMatcher( filepath, 10, 1 ), LocationMatcher( filepath, 10, 5 ) ), ChunkMatcher( '\t', LocationMatcher( filepath, 11, 1 ), LocationMatcher( filepath, 11, 3 ) ), ) } ) ) } ) } } ) @IsolatedYcmd( { 'global_ycm_extra_conf': PathToTestFile( 'extra_confs', 'brace_on_same_line.py' ) } ) def Subcommands_Format_ExtraConf_BraceOnSameLine_test( app ): WaitUntilCompleterServerReady( app, 'typescript' ) filepath = PathToTestFile( 'extra_confs', 'func.ts' ) RunTest( app, { 'description': 'Format with an extra conf, braces on new line', 'request': { 'command': 'Format', 'filepath': filepath, 'options': { 'tab_size': 4, 'insert_spaces': True } }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( ' ', LocationMatcher( filepath, 2, 1 ), LocationMatcher( filepath, 2, 1 ) ), ) } ) ) } ) } } ) @IsolatedYcmd( { 'global_ycm_extra_conf': PathToTestFile( 'extra_confs', 'brace_on_new_line.py' ) } ) def Subcommands_Format_ExtraConf_BraceOnNewLine_test( app ): WaitUntilCompleterServerReady( app, 'typescript' ) filepath = PathToTestFile( 'extra_confs', 'func.ts' ) RunTest( app, { 'description': 'Format with an extra conf, braces on new line', 'request': { 'command': 'Format', 'filepath': filepath, 'options': { 'tab_size': 4, 'insert_spaces': True } }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( matches_regexp( '\n?\n' ), LocationMatcher( filepath, 1, 19 ), LocationMatcher( filepath, 1, 20 ) ), ChunkMatcher( ' ', LocationMatcher( filepath, 2, 1 ), LocationMatcher( filepath, 2, 1 ) ), ) } ) ) } ) } } ) @SharedYcmd def Subcommands_GetType_Basic_test( app ): RunTest( app, { 'description': 'GetType works on a variable', 'request': { 'command': 'GetType', 'line_num': 17, 'column_num': 1, 'filepath': PathToTestFile( 'test.ts' ), }, 'expect': { 'response': requests.codes.ok, 'data': MessageMatcher( 'var foo: Foo' ) } } ) @SharedYcmd def Subcommands_GetType_HasNoType_test( app ): RunTest( app, { 'description': 'GetType returns an error on a keyword', 'request': { 'command': 'GetType', 'line_num': 32, 'column_num': 1, 'filepath': PathToTestFile( 'test.ts' ), }, 'expect': { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( RuntimeError, 'No content available.' ) } } ) @SharedYcmd def Subcommands_GetDoc_Method_test( app ): RunTest( app, { 'description': 'GetDoc on a method returns its docstring', 'request': { 'command': 'GetDoc', 'line_num': 34, 'column_num': 9, 'filepath': PathToTestFile( 'test.ts' ), }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'detailed_info': '(method) Bar.testMethod(): void\n\n' 'Method documentation' } ) } } ) @SharedYcmd def Subcommands_GetDoc_Class_test( app ): RunTest( app, { 'description': 'GetDoc on a class returns its docstring', 'request': { 'command': 'GetDoc', 'line_num': 37, 'column_num': 2, 'filepath': PathToTestFile( 'test.ts' ), }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'detailed_info': 'class Bar\n\n' 'Class documentation\n\n' 'Multi-line' } ) } } ) @SharedYcmd def Subcommands_GetDoc_Class_Unicode_test( app ): RunTest( app, { 'description': 'GetDoc works with Unicode characters', 'request': { 'command': 'GetDoc', 'line_num': 35, 'column_num': 12, 'filepath': PathToTestFile( 'unicode.ts' ), }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'detailed_info': 'class Båøz\n\n' 'Test unicøde st††††', } ) } } ) @SharedYcmd def Subcommands_GoToReferences_test( app ): RunTest( app, { 'description': 'GoToReferences works', 'request': { 'command': 'GoToReferences', 'line_num': 33, 'column_num': 6, 'filepath': PathToTestFile( 'test.ts' ), }, 'expect': { 'response': requests.codes.ok, 'data': contains_inanyorder( has_entries( { 'description': 'var bar = new Bar();', 'line_num' : 33, 'column_num' : 5, 'filepath' : PathToTestFile( 'test.ts' ) } ), has_entries( { 'description': 'bar.testMethod();', 'line_num' : 34, 'column_num' : 1, 'filepath' : PathToTestFile( 'test.ts' ) } ), has_entries( { 'description': 'bar.nonExistingMethod();', 'line_num' : 35, 'column_num' : 1, 'filepath' : PathToTestFile( 'test.ts' ) } ), has_entries( { 'description': 'var bar = new Bar();', 'line_num' : 1, 'column_num' : 5, 'filepath' : PathToTestFile( 'file3.ts' ) } ), has_entries( { 'description': 'bar.testMethod();', 'line_num' : 2, 'column_num' : 1, 'filepath' : PathToTestFile( 'file3.ts' ) } ) ) } } ) @SharedYcmd def Subcommands_GoToImplementation_test( app ): RunTest( app, { 'description': 'GoToImplementation works', 'request': { 'command': 'GoToImplementation', 'line_num': 6, 'column_num': 11, 'filepath': PathToTestFile( 'signatures.ts' ), }, 'expect': { 'response': requests.codes.ok, 'data': contains_inanyorder( has_entries( { 'description': ' return {', 'line_num' : 12, 'column_num' : 10, 'filepath' : PathToTestFile( 'signatures.ts' ) } ), has_entries( { 'description': 'class SomeClass ' 'implements ReturnValue {', 'line_num' : 35, 'column_num' : 7, 'filepath' : PathToTestFile( 'signatures.ts' ) } ), ) } } ) @SharedYcmd def Subcommands_GoToImplementation_InvalidLocation_test( app ): RunTest( app, { 'description': 'GoToImplementation on an invalid location raises exception', 'request': { 'command': 'GoToImplementation', 'line_num': 1, 'column_num': 1, 'filepath': PathToTestFile( 'signatures.ts' ), }, 'expect': { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( RuntimeError, 'No implementation found.' ) } } ) @SharedYcmd def Subcommands_GoToReferences_Unicode_test( app ): RunTest( app, { 'description': 'GoToReferences works with Unicode characters', 'request': { 'command': 'GoToReferences', 'line_num': 14, 'column_num': 3, 'filepath': PathToTestFile( 'unicode.ts' ), }, 'expect': { 'response': requests.codes.ok, 'data': contains_inanyorder( has_entries( { 'description': ' å: number;', 'line_num' : 14, 'column_num' : 3, 'filepath' : PathToTestFile( 'unicode.ts' ) } ), has_entries( { 'description': 'var baz = new Bår(); baz.å;', 'line_num' : 20, 'column_num' : 27, 'filepath' : PathToTestFile( 'unicode.ts' ) } ), has_entries( { 'description': 'baz.å;', 'line_num' : 23, 'column_num' : 5, 'filepath' : PathToTestFile( 'unicode.ts' ) } ), has_entries( { 'description': 'føø_long_long.å;', 'line_num' : 27, 'column_num' : 17, 'filepath' : PathToTestFile( 'unicode.ts' ) } ) ) } } ) def Subcommands_GoTo_Basic( app, goto_command ): RunTest( app, { 'description': goto_command + ' works within file', 'request': { 'command': goto_command, 'line_num': 34, 'column_num': 8, 'filepath': PathToTestFile( 'test.ts' ), }, 'expect': { 'response': requests.codes.ok, 'data': LocationMatcher( PathToTestFile( 'test.ts' ), 30, 3 ) } } ) @pytest.mark.parametrize( "req,rep", [ ( ( 'signatures.ts', 1, 1, 'no_arguments_no_return' ), ( 'signatures.ts', 3, 1, 'no_arguments_no_return' ) ), ( ( 'signatures.ts', 1, 1, 'ReturnValue' ), [ ( 'signatures.ts', 6, 1, 'ReturnValue' ) ] ), ( ( 'signatures.ts', 1, 1, 'Foo' ), [ ( 'test.ts', 14, 5, 'foo' ), ( 'test.ts', 2, 1, 'Foo' ) ] ), ( ( 'signatures.ts', 1, 1, 'nothinghere' ), 'Symbol not found' ) ] ) @SharedYcmd def Subcommands_GoToSymbol_test( app, req, rep ): if isinstance( rep, tuple ): expect = { 'response': requests.codes.ok, 'data': LocationMatcher( PathToTestFile( rep[ 0 ] ), *rep[ 1: ] ) } elif isinstance( rep, list ): # NOTE: We use has_items here because tsserver will include results from # node_modules and all sorts of other random places. expect = { 'response': requests.codes.ok, 'data': has_items( *[ LocationMatcher( PathToTestFile( r[ 0 ] ), *r[ 1: ] ) for r in rep ] ) } else: expect = { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( RuntimeError, rep ) } RunTest( app, { 'request': { 'command': 'GoToSymbol', 'arguments': [ req[ 3 ] ], 'line_num': req[ 1 ], 'column_num': req[ 2 ], 'filepath': PathToTestFile( req[ 0 ] ), }, 'expect': expect } ) @pytest.mark.parametrize( 'command', [ 'GoTo', 'GoToDefinition', 'GoToDeclaration' ] ) @SharedYcmd def Subcommands_GoTo_Basic_test( app, command ): Subcommands_GoTo_Basic( app, command ) def Subcommands_GoTo_Unicode( app, goto_command ): RunTest( app, { 'description': goto_command + ' works with Unicode characters', 'request': { 'command': goto_command, 'line_num': 28, 'column_num': 19, 'filepath': PathToTestFile( 'unicode.ts' ), }, 'expect': { 'response': requests.codes.ok, 'data': LocationMatcher( PathToTestFile( 'unicode.ts' ), 15, 3 ) } } ) @pytest.mark.parametrize( 'command', [ 'GoTo', 'GoToDefinition', 'GoToDeclaration' ] ) @SharedYcmd def Subcommands_GoTo_Unicode_test( app, command ): Subcommands_GoTo_Unicode( app, command ) def Subcommands_GoTo_Fail( app, goto_command ): RunTest( app, { 'description': goto_command + ' fails on non-existing method', 'request': { 'command': goto_command, 'line_num': 35, 'column_num': 6, 'filepath': PathToTestFile( 'test.ts' ), }, 'expect': { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( RuntimeError, 'Could not find definition.' ) } } ) @pytest.mark.parametrize( 'command', [ 'GoTo', 'GoToDefinition', 'GoToDeclaration' ] ) @SharedYcmd def Subcommands_GoTo_Fail_test( app, command ): Subcommands_GoTo_Fail( app, command ) @SharedYcmd def Subcommands_GoToType_test( app ): RunTest( app, { 'description': 'GoToType works', 'request': { 'command': 'GoToType', 'line_num': 14, 'column_num': 6, 'filepath': PathToTestFile( 'test.ts' ), }, 'expect': { 'response': requests.codes.ok, 'data': LocationMatcher( PathToTestFile( 'test.ts' ), 2, 7 ) } } ) @SharedYcmd def Subcommands_GoToType_Fail_test( app ): RunTest( app, { 'description': 'GoToType fails outside the buffer', 'request': { 'command': 'GoToType', 'line_num': 39, 'column_num': 8, 'filepath': PathToTestFile( 'test.ts' ), }, 'expect': { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( RuntimeError, 'Could not find type definition.' ) } } ) @SharedYcmd def Subcommands_FixIt_test( app ): RunTest( app, { 'description': 'FixIt works on a non-existing method', 'request': { 'command': 'FixIt', 'line_num': 35, 'column_num': 12, 'filepath': PathToTestFile( 'test.ts' ), }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_inanyorder( has_entries( { 'text': "Declare method 'nonExistingMethod'", 'chunks': contains_exactly( ChunkMatcher( matches_regexp( '^\r?\n' ' nonExistingMethod\\(\\) {\r?\n' ' throw new Error\\("Method not implemented."\\);\r?\n' ' }$', ), LocationMatcher( PathToTestFile( 'test.ts' ), 25, 12 ), LocationMatcher( PathToTestFile( 'test.ts' ), 25, 12 ) ) ), 'location': LocationMatcher( PathToTestFile( 'test.ts' ), 35, 12 ) } ), has_entries( { 'text': "Declare property 'nonExistingMethod'", 'chunks': contains_exactly( ChunkMatcher( matches_regexp( '^\r?\n' ' nonExistingMethod: any;$' ), LocationMatcher( PathToTestFile( 'test.ts' ), 25, 12 ), LocationMatcher( PathToTestFile( 'test.ts' ), 25, 12 ) ) ), 'location': LocationMatcher( PathToTestFile( 'test.ts' ), 35, 12 ) } ), has_entries( { 'text': "Add index signature for property 'nonExistingMethod'", 'chunks': contains_exactly( ChunkMatcher( matches_regexp( '^\r?\n' ' \\[x: string\\]: any;$' ), LocationMatcher( PathToTestFile( 'test.ts' ), 25, 12 ), LocationMatcher( PathToTestFile( 'test.ts' ), 25, 12 ) ) ), 'location': LocationMatcher( PathToTestFile( 'test.ts' ), 35, 12 ) } ) ) } ) } } ) @SharedYcmd def Subcommands_OrganizeImports_test( app ): filepath = PathToTestFile( 'imports.ts' ) RunTest( app, { 'description': 'OrganizeImports removes unused imports, ' 'coalesces imports from the same module, and sorts them', 'request': { 'command': 'OrganizeImports', 'filepath': filepath, }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_exactly( ChunkMatcher( matches_regexp( 'import \\* as lib from "library";\r?\n' 'import func, { func1, func2 } from "library";\r?\n' ), LocationMatcher( filepath, 1, 1 ), LocationMatcher( filepath, 2, 1 ) ), ChunkMatcher( '', LocationMatcher( filepath, 5, 1 ), LocationMatcher( filepath, 6, 1 ) ), ChunkMatcher( '', LocationMatcher( filepath, 9, 1 ), LocationMatcher( filepath, 10, 1 ) ), ) } ) ) } ) } } ) @SharedYcmd def Subcommands_RefactorRename_Missing_test( app ): RunTest( app, { 'description': 'RefactorRename requires a parameter', 'request': { 'command': 'RefactorRename', 'line_num': 30, 'column_num': 6, 'filepath': PathToTestFile( 'test.ts' ), }, 'expect': { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( ValueError, 'Please specify a new name to rename it to.\n' 'Usage: RefactorRename <new name>' ) } } ) @SharedYcmd def Subcommands_RefactorRename_NotPossible_test( app ): RunTest( app, { 'description': 'RefactorRename cannot rename a non-existing method', 'request': { 'command': 'RefactorRename', 'arguments': [ 'whatever' ], 'line_num': 35, 'column_num': 5, 'filepath': PathToTestFile( 'test.ts' ), }, 'expect': { 'response': requests.codes.internal_server_error, 'data': ErrorMatcher( RuntimeError, 'Value cannot be renamed: ' 'You cannot rename this element.' ) } } ) @SharedYcmd def Subcommands_RefactorRename_Simple_test( app ): RunTest( app, { 'description': 'RefactorRename works on a class name', 'request': { 'command': 'RefactorRename', 'arguments': [ 'test' ], 'line_num': 2, 'column_num': 8, 'filepath': PathToTestFile( 'test.ts' ), }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_inanyorder( ChunkMatcher( 'test', LocationMatcher( PathToTestFile( 'test.ts' ), 14, 15 ), LocationMatcher( PathToTestFile( 'test.ts' ), 14, 18 ) ), ChunkMatcher( 'test', LocationMatcher( PathToTestFile( 'test.ts' ), 2, 7 ), LocationMatcher( PathToTestFile( 'test.ts' ), 2, 10 ) ), ), 'location': LocationMatcher( PathToTestFile( 'test.ts' ), 2, 8 ) } ) ) } ) } } ) @SharedYcmd def Subcommands_RefactorRename_MultipleFiles_test( app ): RunTest( app, { 'description': 'RefactorRename works across files', 'request': { 'command': 'RefactorRename', 'arguments': [ 'this-is-a-longer-string' ], 'line_num': 25, 'column_num': 9, 'filepath': PathToTestFile( 'test.ts' ), }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_inanyorder( ChunkMatcher( 'this-is-a-longer-string', LocationMatcher( PathToTestFile( 'test.ts' ), 25, 7 ), LocationMatcher( PathToTestFile( 'test.ts' ), 25, 10 ) ), ChunkMatcher( 'this-is-a-longer-string', LocationMatcher( PathToTestFile( 'test.ts' ), 33, 15 ), LocationMatcher( PathToTestFile( 'test.ts' ), 33, 18 ) ), ChunkMatcher( 'this-is-a-longer-string', LocationMatcher( PathToTestFile( 'test.ts' ), 37, 1 ), LocationMatcher( PathToTestFile( 'test.ts' ), 37, 4 ) ), ChunkMatcher( 'this-is-a-longer-string', LocationMatcher( PathToTestFile( 'file2.ts' ), 1, 5 ), LocationMatcher( PathToTestFile( 'file2.ts' ), 1, 8 ) ), ChunkMatcher( 'this-is-a-longer-string', LocationMatcher( PathToTestFile( 'file3.ts' ), 1, 15 ), LocationMatcher( PathToTestFile( 'file3.ts' ), 1, 18 ) ), ChunkMatcher( 'this-is-a-longer-string', LocationMatcher( PathToTestFile( 'test.tsx' ), 10, 8 ), LocationMatcher( PathToTestFile( 'test.tsx' ), 10, 11 ) ), ), 'location': LocationMatcher( PathToTestFile( 'test.ts' ), 25, 9 ) } ) ) } ) } } ) @SharedYcmd def Subcommands_RefactorRename_SimpleUnicode_test( app ): RunTest( app, { 'description': 'RefactorRename works with Unicode characters', 'request': { 'command': 'RefactorRename', 'arguments': [ 'ø' ], 'line_num': 14, 'column_num': 3, 'filepath': PathToTestFile( 'unicode.ts' ), }, 'expect': { 'response': requests.codes.ok, 'data': has_entries( { 'fixits': contains_exactly( has_entries( { 'chunks': contains_inanyorder( ChunkMatcher( 'ø', LocationMatcher( PathToTestFile( 'unicode.ts' ), 14, 3 ), LocationMatcher( PathToTestFile( 'unicode.ts' ), 14, 5 ) ), ChunkMatcher( 'ø', LocationMatcher( PathToTestFile( 'unicode.ts' ), 20, 27 ), LocationMatcher( PathToTestFile( 'unicode.ts' ), 20, 29 ) ), ChunkMatcher( 'ø', LocationMatcher( PathToTestFile( 'unicode.ts' ), 23, 5 ), LocationMatcher( PathToTestFile( 'unicode.ts' ), 23, 7 ) ), ChunkMatcher( 'ø', LocationMatcher( PathToTestFile( 'unicode.ts' ), 27, 17 ), LocationMatcher( PathToTestFile( 'unicode.ts' ), 27, 19 ) ), ), 'location': LocationMatcher( PathToTestFile( 'unicode.ts' ), 14, 3 ) } ) ) } ) } } ) @IsolatedYcmd() @patch( 'ycmd.utils.WaitUntilProcessIsTerminated', MockProcessTerminationTimingOut ) def Subcommands_StopServer_Timeout_test( app ): WaitUntilCompleterServerReady( app, 'typescript' ) app.post_json( '/run_completer_command', BuildRequest( filetype = 'typescript', command_arguments = [ 'StopServer' ] ) ) request_data = BuildRequest( filetype = 'typescript' ) assert_that( app.post_json( '/debug_info', request_data ).json, has_entry( 'completer', has_entry( 'servers', contains_exactly( has_entry( 'is_running', False ) ) ) ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True �����������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/typescript/testdata/���������������������������������������0000775�0000000�0000000�00000000000�13746324601�0023254�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/typescript/testdata/buffer_unload/�������������������������0000775�0000000�0000000�00000000000�13746324601�0026067�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/typescript/testdata/buffer_unload/imported.ts��������������0000664�0000000�0000000�00000000057�13746324601�0030264�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������export class Imported { method() { } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/typescript/testdata/buffer_unload/main.ts������������������0000664�0000000�0000000�00000000120�13746324601�0027354�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import { Imported } from "./imported"; let imported = new Imported(); imported. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/typescript/testdata/extra_confs/���������������������������0000775�0000000�0000000�00000000000�13746324601�0025567�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/typescript/testdata/extra_confs/brace_on_new_line.py�������0000664�0000000�0000000�00000000270�13746324601�0031570�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������def Settings( **kwargs ): assert kwargs[ 'language' ] == 'typescript' return { 'formatting_options': { 'placeOpenBraceOnNewLineForFunctions': True } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/typescript/testdata/extra_confs/brace_on_same_line.py������0000664�0000000�0000000�00000000271�13746324601�0031725�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������def Settings( **kwargs ): assert kwargs[ 'language' ] == 'typescript' return { 'formatting_options': { 'placeOpenBraceOnNewLineForFunctions': False } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/typescript/testdata/extra_confs/func.ts��������������������0000664�0000000�0000000�00000000045�13746324601�0027071�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������function add(x, y) { return x + y; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/typescript/testdata/extra_confs/tsconfig.json��������������0000664�0000000�0000000�00000000004�13746324601�0030270�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/typescript/testdata/file2.ts�������������������������������0000664�0000000�0000000�00000000030�13746324601�0024616�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������new Bar().testMethod(); ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/typescript/testdata/file3.ts�������������������������������0000664�0000000�0000000�00000000047�13746324601�0024627�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������var bar = new Bar(); bar.testMethod(); �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/typescript/testdata/imports.ts�����������������������������0000664�0000000�0000000�00000000217�13746324601�0025321�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import func from "library"; func(); import * as lib from "library"; lib.func(); import { func1, func2 } from "library"; func1(); func2(); ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/typescript/testdata/signatures.ts��������������������������0000664�0000000�0000000�00000003213�13746324601�0026007�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// SIMPLE FUNCTIONS function no_arguments_no_return() { } interface ReturnValue { a: string; b: number; }; function no_arguments_with_return() : ReturnValue { return { a: 'test', b: 100 }; } function single_argument_no_return( a: string ) { return 's'; } function single_argument_with_return( a: string ) : string { return 's'; } no_arguments_no_return(); single_argument_with_return( single_argument_no_return( no_arguments_with_return().a ) ) // CLASSES class SomeClass implements ReturnValue { a: string; b: number; constructor( public a_: string, public b_: string ) { this.a = a_; this.b = parseInt( b_ ); } public Test( c: number ) : number { return this.b * c; } public TestAgain( d ) : number { return this.b * d; } }; function multi_argument_no_return( løng_våriable_name: number, untyped_argument ) { var c = new SomeClass( untyped_argument, løng_våriable_name.toString() ); return c.Test( 10 ); } multi_argument_no_return( 1000, 'test' ); // GENERICS function generic<TYPE extends ReturnValue>( t: TYPE ): string { return t.a; } generic<SomeClass>( new SomeClass( 'a', '1' ) ); // OVERLOADS function overload_fake( a: string | number, b : string ) : boolean | string { if ( typeof a === 'string' ) { return true; } else { return b; } } overload_fake( 1, 'two' ) function øverløåd( a: number ) : string; function øverløåd( a: string, b :number ) : string; function øverløåd( a: string | number, b: number = 1 ) : string { return '1'; } øverløåd( 'a', 1 ); øverløåd( 1 ); dod( �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/typescript/testdata/test.ts��������������������������������0000664�0000000�0000000�00000000624�13746324601�0024605�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ class Foo { /** Unicode string: 说话 */ methodA() {} methodB() {} methodC( a: { foo: string; bar: number; } ) {} } var foo = new Foo(); // line 17, column 6 foo.mA /** * Class documentation * * Multi-line */ class Bar { /** * Method documentation */ testMethod() {} } var bar = new Bar(); bar.testMethod(); bar.nonExistingMethod(); Bar.apply() Bår ������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/typescript/testdata/test.tsx�������������������������������0000664�0000000�0000000�00000000410�13746324601�0024766�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import * as React from 'react'; interface MyComponentProps { children: any; } const TestComponent: React.FunctionComponent<MyComponentProps> = (props) => { return ( <div> <Bar /> {props.children} </div> ); }; const a = { 'foo': 3 } a. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/typescript/testdata/tsconfig.json��������������������������0000664�0000000�0000000�00000000004�13746324601�0025755�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/typescript/testdata/unicode.ts�����������������������������0000664�0000000�0000000�00000000664�13746324601�0025260�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ export class Bår { methodA( a: { foo: string; bar: number; } ) {} methød() { return '†est'.cha } å: number; åbc: number; abc: number; a: number; } var baz = new Bår(); baz.å; baz.abc; baz.a; baz.å; var føø_long_long = new Bår(); føø_long_long.methød(); føø_long_long.å; føø_long_long.åbc; føø_long_long.abc; /** * Test unicøde st†††† */ class Båøz { } ����������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/typescript/typescript_completer_test.py��������������������0000664�0000000�0000000�00000003305�13746324601�0027335�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2017-2020 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 <http://www.gnu.org/licenses/>. from unittest.mock import patch from hamcrest import assert_that, equal_to from ycmd import user_options_store from ycmd.completers.typescript.typescript_completer import ( ShouldEnableTypeScriptCompleter, FindTSServer ) def ShouldEnableTypeScriptCompleter_NodeAndTsserverFound_test(): user_options = user_options_store.GetAll() assert_that( ShouldEnableTypeScriptCompleter( user_options ) ) @patch( 'ycmd.utils.FindExecutable', return_value = None ) def ShouldEnableTypeScriptCompleter_TsserverNotFound_test( *args ): user_options = user_options_store.GetAll() assert_that( not ShouldEnableTypeScriptCompleter( user_options ) ) @patch( 'ycmd.utils.FindExecutableWithFallback', wraps = lambda x, fb: x if x == 'tsserver' else None ) @patch( 'os.path.isfile', return_value = True ) def FindTSServer_CustomTsserverPath_test( *args ): assert_that( 'tsserver', equal_to( FindTSServer( 'tsserver' ) ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/tests/utils_test.py����������������������������������������������0000664�0000000�0000000�00000043753�13746324601�0022022�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2016-2020 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 <http://www.gnu.org/licenses/>. import os import pytest import subprocess import tempfile from hamcrest import ( assert_that, calling, contains_exactly, empty, equal_to, has_length, has_property, instance_of, raises ) from unittest.mock import patch, call from types import ModuleType from ycmd import utils from ycmd.tests.test_utils import ( WindowsOnly, UnixOnly, CurrentWorkingDirectory, TemporaryExecutable ) from ycmd.tests import PathToTestFile from ycmd.utils import ImportAndCheckCore # NOTE: isinstance() vs type() is carefully used in this test file. Before # changing things here, read the comments in utils.ToBytes. def ToBytes_Bytes_test(): value = utils.ToBytes( bytes( b'abc' ) ) assert_that( value, equal_to( bytes( b'abc' ) ) ) assert_that( type( value ), equal_to( bytes ) ) def ToBytes_Str_test(): value = utils.ToBytes( u'abc' ) assert_that( value, equal_to( bytes( b'abc' ) ) ) assert_that( type( value ), equal_to( bytes ) ) def ToBytes_Int_test(): value = utils.ToBytes( 123 ) assert_that( value, equal_to( bytes( b'123' ) ) ) assert_that( type( value ), equal_to( bytes ) ) def ToBytes_None_test(): value = utils.ToBytes( None ) assert_that( value, equal_to( bytes( b'' ) ) ) assert_that( type( value ), equal_to( bytes ) ) def ToUnicode_Bytes_test(): value = utils.ToUnicode( bytes( b'abc' ) ) assert_that( value, equal_to( u'abc' ) ) assert_that( isinstance( value, str ) ) def ToUnicode_Str_test(): value = utils.ToUnicode( u'abc' ) assert_that( value, equal_to( u'abc' ) ) assert_that( isinstance( value, str ) ) def ToUnicode_Int_test(): value = utils.ToUnicode( 123 ) assert_that( value, equal_to( u'123' ) ) assert_that( isinstance( value, str ) ) def ToUnicode_None_test(): value = utils.ToUnicode( None ) assert_that( value, equal_to( u'' ) ) assert_that( isinstance( value, str ) ) def JoinLinesAsUnicode_Bytes_test(): value = utils.JoinLinesAsUnicode( [ bytes( b'abc' ), bytes( b'xyz' ) ] ) assert_that( value, equal_to( u'abc\nxyz' ) ) assert_that( isinstance( value, str ) ) def JoinLinesAsUnicode_Str_test(): value = utils.JoinLinesAsUnicode( [ u'abc', u'xyz' ] ) assert_that( value, equal_to( u'abc\nxyz' ) ) assert_that( isinstance( value, str ) ) def JoinLinesAsUnicode_EmptyList_test(): value = utils.JoinLinesAsUnicode( [] ) assert_that( value, equal_to( u'' ) ) assert_that( isinstance( value, str ) ) def JoinLinesAsUnicode_BadInput_test(): assert_that( calling( utils.JoinLinesAsUnicode ).with_args( [ 42 ] ), raises( ValueError, 'lines must contain either strings or bytes' ) ) def RemoveIfExists_Exists_test(): tempfile = PathToTestFile( 'remove-if-exists' ) open( tempfile, 'a' ).close() assert_that( os.path.exists( tempfile ) ) utils.RemoveIfExists( tempfile ) assert_that( not os.path.exists( tempfile ) ) def RemoveIfExists_DoesntExist_test(): tempfile = PathToTestFile( 'remove-if-exists' ) assert_that( not os.path.exists( tempfile ) ) utils.RemoveIfExists( tempfile ) assert_that( not os.path.exists( tempfile ) ) def PathToFirstExistingExecutable_Basic_test(): if utils.OnWindows(): assert_that( utils.PathToFirstExistingExecutable( [ 'notepad.exe' ] ) ) else: assert_that( utils.PathToFirstExistingExecutable( [ 'cat' ] ) ) def PathToFirstExistingExecutable_Failure_test(): assert_that( not utils.PathToFirstExistingExecutable( [ 'ycmd-foobar' ] ) ) @UnixOnly @patch( 'subprocess.Popen' ) def SafePopen_RemoveStdinWindows_test( *args ): utils.SafePopen( [ 'foo' ], stdin_windows = 'bar' ) assert_that( subprocess.Popen.call_args, equal_to( call( [ 'foo' ] ) ) ) @WindowsOnly @patch( 'subprocess.Popen' ) def SafePopen_ReplaceStdinWindowsPIPEOnWindows_test( *args ): utils.SafePopen( [ 'foo' ], stdin_windows = subprocess.PIPE ) assert_that( subprocess.Popen.call_args, equal_to( call( [ 'foo' ], stdin = subprocess.PIPE, creationflags = utils.CREATE_NO_WINDOW ) ) ) @WindowsOnly @patch( 'subprocess.Popen' ) def SafePopen_WindowsPath_test( *args ): tempfile = PathToTestFile( 'safe-popen-file' ) open( tempfile, 'a' ).close() try: utils.SafePopen( [ 'foo', tempfile ], stdin_windows = subprocess.PIPE ) assert_that( subprocess.Popen.call_args, equal_to( call( [ 'foo', tempfile ], stdin = subprocess.PIPE, creationflags = utils.CREATE_NO_WINDOW ) ) ) finally: os.remove( tempfile ) def PathsToAllParentFolders_Basic_test(): assert_that( utils.PathsToAllParentFolders( '/home/user/projects/test.c' ), contains_exactly( os.path.normpath( '/home/user/projects' ), os.path.normpath( '/home/user' ), os.path.normpath( '/home' ), os.path.normpath( '/' ), ) ) @patch( 'os.path.isdir', return_value = True ) def PathsToAllParentFolders_IsDirectory_test( *args ): assert_that( utils.PathsToAllParentFolders( '/home/user/projects' ), contains_exactly( os.path.normpath( '/home/user/projects' ), os.path.normpath( '/home/user' ), os.path.normpath( '/home' ), os.path.normpath( '/' ) ) ) def PathsToAllParentFolders_FileAtRoot_test(): assert_that( utils.PathsToAllParentFolders( '/test.c' ), contains_exactly( os.path.normpath( '/' ) ) ) @WindowsOnly def PathsToAllParentFolders_WindowsPath_test(): assert_that( utils.PathsToAllParentFolders( r'C:\\foo\\goo\\zoo\\test.c' ), contains_exactly( os.path.normpath( r'C:\\foo\\goo\\zoo' ), os.path.normpath( r'C:\\foo\\goo' ), os.path.normpath( r'C:\\foo' ), os.path.normpath( r'C:\\' ) ) ) @pytest.mark.parametrize( 'path,expected', [ ( '', ( '', '' ) ), ( 'foo', ( 'foo', '' ) ), ( 'foo/bar', ( 'foo', 'bar' ) ), ( 'foo/bar/xyz', ( 'foo', 'bar/xyz' ) ), ( 'foo/bar/xyz/', ( 'foo', 'bar/xyz' ) ), ( '/', ( '/', '' ) ), ( '/foo', ( '/', 'foo' ) ), ( '/foo/bar', ( '/', 'foo/bar' ) ), ( '/foo/bar/xyz', ( '/', 'foo/bar/xyz' ) ), ( '/foo/bar/xyz/', ( '/', 'foo/bar/xyz' ) ) ] ) def PathLeftSplit_test( path, expected ): assert_that( utils.PathLeftSplit( path ), equal_to( expected ) ) @WindowsOnly @pytest.mark.parametrize( 'path,expected', [ ( 'foo\\bar', ( 'foo', 'bar' ) ), ( 'foo\\bar\\xyz', ( 'foo', 'bar\\xyz' ) ), ( 'foo\\bar\\xyz\\', ( 'foo', 'bar\\xyz' ) ), ( 'C:\\', ( 'C:\\', '' ) ), ( 'C:\\foo', ( 'C:\\', 'foo' ) ), ( 'C:\\foo\\bar', ( 'C:\\', 'foo\\bar' ) ), ( 'C:\\foo\\bar\\xyz', ( 'C:\\', 'foo\\bar\\xyz' ) ), ( 'C:\\foo\\bar\\xyz\\', ( 'C:\\', 'foo\\bar\\xyz' ) ) ] ) def PathLeftSplit_Windows_test( path, expected ): assert_that( utils.PathLeftSplit( path ), equal_to( expected ) ) def OpenForStdHandle_PrintDoesntThrowException_test(): try: temp = PathToTestFile( 'open-for-std-handle' ) with utils.OpenForStdHandle( temp ) as f: print( 'foo', file = f ) finally: os.remove( temp ) # Tuples of ( ( unicode_line_value, codepoint_offset ), expected_result ). @pytest.mark.parametrize( 'test,expected', [ # Simple ascii strings. ( ( 'test', 1 ), 1 ), ( ( 'test', 4 ), 4 ), ( ( 'test', 5 ), 5 ), # Unicode char at beginning. ( ( '†est', 1 ), 1 ), ( ( '†est', 2 ), 4 ), ( ( '†est', 4 ), 6 ), ( ( '†est', 5 ), 7 ), # Unicode char at end. ( ( 'tes†', 1 ), 1 ), ( ( 'tes†', 2 ), 2 ), ( ( 'tes†', 4 ), 4 ), ( ( 'tes†', 5 ), 7 ), # Unicode char in middle. ( ( 'tes†ing', 1 ), 1 ), ( ( 'tes†ing', 2 ), 2 ), ( ( 'tes†ing', 4 ), 4 ), ( ( 'tes†ing', 5 ), 7 ), ( ( 'tes†ing', 7 ), 9 ), ( ( 'tes†ing', 8 ), 10 ), # Converts bytes to Unicode. ( ( utils.ToBytes( '†est' ), 2 ), 4 ) ] ) def CodepointOffsetToByteOffset_test( test, expected ): assert_that( utils.CodepointOffsetToByteOffset( *test ), equal_to( expected ) ) # Tuples of ( ( unicode_line_value, byte_offset ), expected_result ). @pytest.mark.parametrize( 'test,expected', [ # Simple ascii strings. ( ( 'test', 1 ), 1 ), ( ( 'test', 4 ), 4 ), ( ( 'test', 5 ), 5 ), # Unicode char at beginning. ( ( '†est', 1 ), 1 ), ( ( '†est', 4 ), 2 ), ( ( '†est', 6 ), 4 ), ( ( '†est', 7 ), 5 ), # Unicode char at end. ( ( 'tes†', 1 ), 1 ), ( ( 'tes†', 2 ), 2 ), ( ( 'tes†', 4 ), 4 ), ( ( 'tes†', 7 ), 5 ), # Unicode char in middle. ( ( 'tes†ing', 1 ), 1 ), ( ( 'tes†ing', 2 ), 2 ), ( ( 'tes†ing', 4 ), 4 ), ( ( 'tes†ing', 7 ), 5 ), ( ( 'tes†ing', 9 ), 7 ), ( ( 'tes†ing', 10 ), 8 ), ] ) def ByteOffsetToCodepointOffset_test( test, expected ): assert_that( utils.ByteOffsetToCodepointOffset( *test ), equal_to( expected ) ) @pytest.mark.parametrize( 'lines,expected', [ ( '', [ '' ] ), ( ' ', [ ' ' ] ), ( '\n', [ '', '' ] ), ( ' \n', [ ' ', '' ] ), ( ' \n ', [ ' ', ' ' ] ), ( 'test\n', [ 'test', '' ] ), # Ignore \r on purpose. ( '\r', [ '\r' ] ), ( '\r ', [ '\r ' ] ), ( 'test\r', [ 'test\r' ] ), ( '\n\r', [ '', '\r' ] ), ( '\r\n', [ '\r', '' ] ), ( '\r\n\n', [ '\r', '', '' ] ), ( 'test\ntesting', [ 'test', 'testing' ] ), ( '\ntesting', [ '', 'testing' ] ), # Do not split lines on \f and \v characters. ( '\f\n\v', [ '\f', '\v' ] ) ] ) def SplitLines_test( lines, expected ): assert_that( utils.SplitLines( lines ), expected ) def FindExecutable_AbsolutePath_test(): with TemporaryExecutable() as executable: assert_that( executable, equal_to( utils.FindExecutable( executable ) ) ) def FindExecutable_RelativePath_test(): with TemporaryExecutable() as executable: dirname, exename = os.path.split( executable ) relative_executable = os.path.join( '.', exename ) with CurrentWorkingDirectory( dirname ): assert_that( relative_executable, equal_to( utils.FindExecutable( relative_executable ) ) ) @patch.dict( 'os.environ', { 'PATH': tempfile.gettempdir() } ) def FindExecutable_ExecutableNameInPath_test(): with TemporaryExecutable() as executable: dirname, exename = os.path.split( executable ) assert_that( executable, equal_to( utils.FindExecutable( exename ) ) ) def FindExecutable_ReturnNoneIfFileIsNotExecutable_test(): with tempfile.NamedTemporaryFile() as non_executable: assert_that( None, equal_to( utils.FindExecutable( non_executable.name ) ) ) @WindowsOnly def FindExecutable_CurrentDirectory_test(): with TemporaryExecutable() as executable: dirname, exename = os.path.split( executable ) with CurrentWorkingDirectory( dirname ): assert_that( executable, equal_to( utils.FindExecutable( exename ) ) ) @WindowsOnly @patch.dict( 'os.environ', { 'PATHEXT': '.xyz' } ) def FindExecutable_AdditionalPathExt_test(): with TemporaryExecutable( extension = '.xyz' ) as executable: assert_that( executable, equal_to( utils.FindExecutable( executable ) ) ) def FindExecutableWithFallback_Empty_test(): with TemporaryExecutable() as fallback: assert_that( utils.FindExecutableWithFallback( '', fallback ), equal_to( fallback ) ) @patch( 'ycmd.utils.FindExecutable', return_value = None ) def FindExecutableWithFallback_UserProvided_Invalid_test( find_executable ): with TemporaryExecutable() as executable: with TemporaryExecutable() as fallback: assert_that( utils.FindExecutableWithFallback( executable, fallback ), equal_to( None ) ) def FindExecutableWithFallback_UserProvided_test(): with TemporaryExecutable() as executable: with TemporaryExecutable() as fallback: assert_that( utils.FindExecutableWithFallback( executable, fallback ), equal_to( executable ) ) @patch( 'ycmd.utils.ProcessIsRunning', return_value = True ) def WaitUntilProcessIsTerminated_TimedOut_test( *args ): assert_that( calling( utils.WaitUntilProcessIsTerminated ).with_args( None, timeout = 0 ), raises( RuntimeError, 'Waited process to terminate for 0 seconds, aborting.' ) ) def LoadPythonSource_UnicodePath_test(): filename = PathToTestFile( u'uni¢od€.py' ) module = utils.LoadPythonSource( 'module_name', filename ) assert_that( module, instance_of( ModuleType ) ) assert_that( module.__file__, equal_to( filename ) ) assert_that( module.__name__, equal_to( 'module_name' ) ) assert_that( module, has_property( 'SomeMethod' ) ) assert_that( module.SomeMethod(), equal_to( True ) ) def GetCurrentDirectory_Py3NoCurrentDirectory_test(): with patch( 'os.getcwd', side_effect = FileNotFoundError ): # noqa assert_that( utils.GetCurrentDirectory(), equal_to( tempfile.gettempdir() ) ) def HashableDict_Equality_test(): dict1 = { 'key': 'value' } dict2 = { 'key': 'another_value' } assert_that( utils.HashableDict( dict1 ) == utils.HashableDict( dict1 ) ) assert_that( not utils.HashableDict( dict1 ) != utils.HashableDict( dict1 ) ) assert_that( not utils.HashableDict( dict1 ) == dict1 ) assert_that( utils.HashableDict( dict1 ) != dict1 ) assert_that( not utils.HashableDict( dict1 ) == utils.HashableDict( dict2 ) ) assert_that( utils.HashableDict( dict1 ) != utils.HashableDict( dict2 ) ) @patch( 'ycmd.utils.LOGGER', autospec = True ) def RunImportAndCheckCoreException( test, logger ): with patch( 'ycmd.utils.ImportCore', side_effect = ImportError( test[ 'exception_message' ] ) ): assert_that( ImportAndCheckCore(), equal_to( test[ 'exit_status' ] ) ) assert_that( logger.method_calls, has_length( 1 ) ) logger.exception.assert_called_with( test[ 'logged_message' ] ) @patch( 'ycmd.utils.LOGGER', autospec = True ) def ImportAndCheckCore_Compatible_test( logger ): assert_that( ImportAndCheckCore(), equal_to( 0 ) ) assert_that( logger.method_calls, empty() ) def ImportAndCheckCore_Unexpected_test(): RunImportAndCheckCoreException( { 'exception_message': 'unexpected import exception', 'exit_status': 3, 'logged_message': 'unexpected import exception' } ) def ImportAndCheckCore_Missing_test(): RunImportAndCheckCoreException( { 'exception_message': "No module named 'ycm_core'", 'exit_status': 4, 'logged_message': 'ycm_core library not detected; you need to compile it ' 'by running the build.py script. See the documentation ' 'for more details.' } ) @patch( 'ycm_core.YcmCoreVersion', side_effect = AttributeError() ) @patch( 'ycmd.utils.LOGGER', autospec = True ) def ImportAndCheckCore_Outdated_NoYcmCoreVersionMethod_test( logger, *args ): assert_that( ImportAndCheckCore(), equal_to( 7 ) ) assert_that( logger.method_calls, has_length( 1 ) ) logger.exception.assert_called_with( 'ycm_core library too old; PLEASE RECOMPILE by running the build.py ' 'script. See the documentation for more details.' ) @patch( 'ycm_core.YcmCoreVersion', return_value = 10 ) @patch( 'ycmd.utils.ExpectedCoreVersion', return_value = 11 ) @patch( 'ycmd.utils.LOGGER', autospec = True ) def ImportAndCheckCore_Outdated_NoVersionMatch_test( logger, *args ): assert_that( ImportAndCheckCore(), equal_to( 7 ) ) assert_that( logger.method_calls, has_length( 1 ) ) logger.error.assert_called_with( 'ycm_core library too old; PLEASE RECOMPILE by running the build.py ' 'script. See the documentation for more details.' ) @patch( 'ycmd.utils.ListDirectory', return_value = [] ) def GetClangResourceDir_NotFound_test( *args ): assert_that( calling( utils.GetClangResourceDir ), raises( RuntimeError, 'Cannot find Clang resource directory' ) ) @pytest.mark.parametrize( 'unsafe_name,safe_name', [ ( 'this is a test 0123 -x', 'this_is_a_test_0123__x' ), ( 'This Is A Test 0123 -x', 'this_is_a_test_0123__x' ), ( 'T˙^ß ^ß å †´ß† 0123 -x', 't______________0123__x' ), ( 'contains/slashes', 'contains_slashes' ), ( 'contains/newline/\n', 'contains_newline__' ), ( '', '' ), ] ) def MakeSafeFileNameString_test( unsafe_name, safe_name ): assert_that( utils.MakeSafeFileNameString( unsafe_name ), equal_to( safe_name ) ) @pytest.mark.parametrize( 'target,override,expected', [ ( {}, {}, {} ), ( { 1: 1 }, {}, { 1: 1 } ), ( {}, { 1: 1 }, { 1: 1 } ), ( { 1: { 4: 4 } }, { 1: { 2: { 3: 3 } } }, { 1: { 2: { 3: 3 }, 4: 4 } } ), ( { 1: {} }, { 1: 1 }, { 1: 1 } ), ( { 'outer': { 'inner': { 'key': 'oldValue', 'existingKey': True } } }, { 'outer': { 'inner': { 'key': 'newValue' } }, 'newKey': { 'newDict': True }, }, { 'outer': { 'inner': { 'key': 'newValue', 'existingKey': True } }, 'newKey': { 'newDict': True } } ), ] ) def UpdateDict_test( target, override, expected ): assert_that( utils.UpdateDict( target, override ), equal_to( expected ) ) def Dummy_test(): # Workaround for https://github.com/pytest-dev/pytest-rerunfailures/issues/51 assert True ���������������������ycmd-0+20201028+git1d415c5+ds/ycmd/user_options_store.py��������������������������������������������0000664�0000000�0000000�00000002261�13746324601�0022413�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. import json import os from ycmd.utils import HashableDict, ReadFile _USER_OPTIONS = {} def SetAll( new_options ): global _USER_OPTIONS _USER_OPTIONS = HashableDict( new_options ) def GetAll(): return _USER_OPTIONS def Value( key ): return _USER_OPTIONS[ key ] def DefaultOptions(): settings_path = os.path.join( os.path.dirname( os.path.abspath( __file__ ) ), 'default_settings.json' ) options = json.loads( ReadFile( settings_path ) ) options.pop( 'hmac_secret', None ) return options �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/utils.py���������������������������������������������������������0000664�0000000�0000000�00000042001�13746324601�0017602�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2011-2020 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 <http://www.gnu.org/licenses/>. import copy import json import logging import os import socket import subprocess import sys import tempfile import time import threading LOGGER = logging.getLogger( 'ycmd' ) ROOT_DIR = os.path.normpath( os.path.join( os.path.dirname( __file__ ), '..' ) ) DIR_OF_THIRD_PARTY = os.path.join( ROOT_DIR, 'third_party' ) LIBCLANG_DIR = os.path.join( DIR_OF_THIRD_PARTY, 'clang', 'lib' ) if hasattr( os, 'add_dll_directory' ): os.add_dll_directory( LIBCLANG_DIR ) from collections.abc import Mapping from urllib.parse import urljoin, urlparse, unquote, quote # noqa from urllib.request import pathname2url, url2pathname # noqa # We replace the re module with regex as it has better support for characters # on multiple code points. However, this module has a compiled component so we # can't import it in YCM if it is built for a different version of Python. We # fall back to the re module in that case. try: import regex as re except ImportError: # pragma: no cover import re # noqa # Creation flag to disable creating a console window on Windows. See # https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863.aspx CREATE_NO_WINDOW = 0x08000000 EXECUTABLE_FILE_MASK = os.F_OK | os.X_OK CORE_MISSING_ERROR_REGEX = re.compile( "No module named '?ycm_core'?" ) CORE_MISSING_MESSAGE = ( 'ycm_core library not detected; you need to compile it by running the ' 'build.py script. See the documentation for more details.' ) CORE_OUTDATED_MESSAGE = ( 'ycm_core library too old; PLEASE RECOMPILE by running the build.py script. ' 'See the documentation for more details.' ) # Exit statuses returned by the CompatibleWithCurrentCore function: # - CORE_COMPATIBLE_STATUS: ycm_core is compatible; # - CORE_UNEXPECTED_STATUS: unexpected error while loading ycm_core; # - CORE_MISSING_STATUS : ycm_core is missing; # - CORE_OUTDATED_STATUS : ycm_core version is outdated. # Values 1 and 2 are not used because 1 is for general errors and 2 has often a # special meaning for Unix programs. See # https://docs.python.org/2/library/sys.html#sys.exit CORE_COMPATIBLE_STATUS = 0 CORE_UNEXPECTED_STATUS = 3 CORE_MISSING_STATUS = 4 CORE_OUTDATED_STATUS = 7 # Python 3 complains on the common open(path).read() idiom because the file # doesn't get closed. So, a helper func. # Also, all files we read are UTF-8. def ReadFile( filepath ): with open( filepath, encoding = 'utf8' ) as f: return f.read() # Returns a file object that can be used to replace sys.stdout or sys.stderr def OpenForStdHandle( filepath ): # Since this function is used for logging purposes, we don't want the output # to be delayed. This means line buffering for text mode. # See https://docs.python.org/2/library/io.html#io.open return open( filepath, mode = 'w', buffering = 1 ) def MakeSafeFileNameString( s ): """Return a representation of |s| that is safe for use in a file name. Explicitly, returns s converted to lowercase with all non alphanumeric characters replaced with '_'.""" def is_ascii( c ): return ord( c ) < 128 return "".join( c if c.isalnum() and is_ascii( c ) else '_' for c in ToUnicode( s ).lower() ) def CreateLogfile( prefix = '' ): with tempfile.NamedTemporaryFile( prefix = prefix, suffix = '.log', delete = False ) as logfile: return logfile.name def ToUnicode( value ): if not value: return '' if isinstance( value, str ): return value if isinstance( value, bytes ): # All incoming text should be utf8 return str( value, 'utf8' ) return str( value ) # When lines is an iterable of all strings or all bytes, equivalent to # '\n'.join( ToUnicode( lines ) ) # but faster on large inputs. def JoinLinesAsUnicode( lines ): try: first = next( iter( lines ) ) except StopIteration: return str() if isinstance( first, str ): return ToUnicode( '\n'.join( lines ) ) if isinstance( first, bytes ): return ToUnicode( b'\n'.join( lines ) ) raise ValueError( 'lines must contain either strings or bytes.' ) def ToBytes( value ): if not value: return b'' if type( value ) == bytes: return value if isinstance( value, str ): return value.encode( 'utf-8' ) # This is meant to catch `int` and similar non-string/bytes types. return str( value ).encode( 'utf-8' ) def ByteOffsetToCodepointOffset( line_value, byte_offset ): """The API calls for byte offsets into the UTF-8 encoded version of the buffer. However, ycmd internally uses unicode strings. This means that when we need to walk 'characters' within the buffer, such as when checking for semantic triggers and similar, we must use codepoint offsets, rather than byte offsets. This method converts the |byte_offset|, which is a 1-based utf-8 byte offset, into a 1-based codepoint offset in the unicode string |line_value|.""" byte_line_value = ToBytes( line_value ) return len( ToUnicode( byte_line_value[ : byte_offset - 1 ] ) ) + 1 def CodepointOffsetToByteOffset( unicode_line_value, codepoint_offset ): """The API calls for byte offsets into the UTF-8 encoded version of the buffer. However, ycmd internally uses unicode strings. This means that when we need to walk 'characters' within the buffer, such as when checking for semantic triggers and similar, we must use codepoint offsets, rather than byte offsets. This method converts the |codepoint_offset| which is a 1-based unicode codepoint offset into a 1-based byte offset into the utf-8 encoded bytes version of |unicode_line_value|.""" # Should be a no-op, but in case someone passes a bytes instance. unicode_line_value = ToUnicode( unicode_line_value ) return len( ToBytes( unicode_line_value[ : codepoint_offset - 1 ] ) ) + 1 def GetUnusedLocalhostPort(): sock = socket.socket() # This tells the OS to give us any free port in the range [1024 - 65535] sock.bind( ( '', 0 ) ) port = sock.getsockname()[ 1 ] sock.close() return port def RemoveDirIfExists( dirname ): try: import shutil shutil.rmtree( dirname ) except OSError: pass def RemoveIfExists( filename ): try: os.remove( filename ) except OSError: pass def PathToFirstExistingExecutable( executable_name_list ): for executable_name in executable_name_list: path = FindExecutable( executable_name ) if path: return path return None def _GetWindowsExecutable( filename ): def _GetPossibleWindowsExecutable( filename ): pathext = [ ext.lower() for ext in os.environ.get( 'PATHEXT', '' ).split( os.pathsep ) ] base, extension = os.path.splitext( filename ) if extension.lower() in pathext: return [ filename ] else: return [ base + ext for ext in pathext ] for exe in _GetPossibleWindowsExecutable( filename ): if os.path.isfile( exe ): return exe return None # Check that a given file can be accessed as an executable file, so controlling # the access mask on Unix and if has a valid extension on Windows. It returns # the path to the executable or None if no executable was found. def GetExecutable( filename ): if OnWindows(): return _GetWindowsExecutable( filename ) if ( os.path.isfile( filename ) and os.access( filename, EXECUTABLE_FILE_MASK ) ): return filename return None # Adapted from https://github.com/python/cpython/blob/v3.6.0/Lib/shutil.py#L1087 # to be backward compatible with Python2 and more consistent to our codebase. def FindExecutable( executable ): # If we're given a path with a directory part, look it up directly rather # than referring to PATH directories. This includes checking relative to the # current directory, e.g. ./script if os.path.dirname( executable ): return GetExecutable( executable ) paths = os.environ[ 'PATH' ].split( os.pathsep ) if OnWindows(): # The current directory takes precedence on Windows. curdir = os.path.abspath( os.curdir ) if curdir not in paths: paths.insert( 0, curdir ) for path in paths: exe = GetExecutable( os.path.join( path, executable ) ) if exe: return exe return None def FindExecutableWithFallback( executable_path, fallback ): if executable_path: executable_path = FindExecutable( ExpandVariablesInPath( executable_path ) ) if not executable_path: # If the user told us to use a non-existing path, report an error. # Don't attempt to be too clever about the fallback. return None return executable_path else: return fallback def ExecutableName( executable ): return executable + ( '.exe' if OnWindows() else '' ) def ExpandVariablesInPath( path ): # Replace '~' with the home directory and expand environment variables in # path. return os.path.expanduser( os.path.expandvars( path ) ) def OnWindows(): return sys.platform == 'win32' def OnMac(): return sys.platform == 'darwin' def ProcessIsRunning( handle ): return handle is not None and handle.poll() is None def WaitUntilProcessIsTerminated( handle, timeout = 5 ): expiration = time.time() + timeout while True: if time.time() > expiration: raise RuntimeError( f'Waited process to terminate for { timeout } ' 'seconds, aborting.' ) if not ProcessIsRunning( handle ): return time.sleep( 0.1 ) def CloseStandardStreams( handle ): if not handle: return for stream in [ handle.stdin, handle.stdout, handle.stderr ]: if stream: stream.close() def IsRootDirectory( path, parent ): return path == parent def PathsToAllParentFolders( path ): folder = os.path.normpath( path ) if os.path.isdir( folder ): yield folder while True: parent = os.path.dirname( folder ) if IsRootDirectory( folder, parent ): break folder = parent yield folder def PathLeftSplit( path ): """Split a path as (head, tail) where head is the part before the first path separator and tail is everything after. If the path is absolute, head is the root component, tail everything else. If there is no separator, head is the whole path and tail the empty string.""" drive, path = os.path.splitdrive( path ) separators = '/\\' if OnWindows() else '/' path_length = len( path ) offset = 0 while offset < path_length and path[ offset ] not in separators: offset += 1 if offset == path_length: return drive + path, '' tail = path[ offset + 1 : ].rstrip( separators ) if offset == 0: return drive + path[ 0 ], tail return drive + path[ : offset ], tail # A wrapper for subprocess.Popen that fixes quirks on Windows. def SafePopen( args, **kwargs ): if OnWindows(): # We need this to start the server otherwise bad things happen. # See issue #637. if kwargs.get( 'stdin_windows' ) is subprocess.PIPE: kwargs[ 'stdin' ] = subprocess.PIPE # Do not create a console window kwargs[ 'creationflags' ] = CREATE_NO_WINDOW kwargs.pop( 'stdin_windows', None ) return subprocess.Popen( args, **kwargs ) # Shim for importlib.machinery.SourceFileLoader. # See upstream Python docs for info on what this does. def LoadPythonSource( name, pathname ): import importlib.machinery return importlib.machinery.SourceFileLoader( name, pathname ).load_module() def SplitLines( contents ): """Return a list of each of the lines in the unicode string |contents|.""" # We often want to get a list representation of a buffer such that we can # index all of the 'lines' within it. Python provides str.splitlines for this # purpose. However, this method not only splits on newline characters (\n, # \r\n, and \r) but also on line boundaries like \v and \f. Since old # Macintosh newlines (\r) are obsolete and Windows newlines (\r\n) end with a # \n character, we can ignore carriage return characters (\r) and only split # on \n. return contents.split( '\n' ) def GetCurrentDirectory(): """Returns the current directory as an unicode object. If the current directory does not exist anymore, returns the temporary folder instead.""" try: return os.getcwd() except FileNotFoundError: return tempfile.gettempdir() def StartThread( func, *args ): thread = threading.Thread( target = func, args = args ) thread.daemon = True thread.start() return thread class HashableDict( Mapping ): """An immutable dictionary that can be used in dictionary's keys. The dictionary must be JSON-encodable; in particular, all keys must be strings.""" def __init__( self, *args, **kwargs ): self._dict = dict( *args, **kwargs ) def __getitem__( self, key ): return copy.deepcopy( self._dict[ key ] ) def __iter__( self ): return iter( self._dict ) def __len__( self ): return len( self._dict ) def __repr__( self ): return '<HashableDict %s>' % repr( self._dict ) def __hash__( self ): try: return self._hash except AttributeError: self._hash = json.dumps( self._dict, separators = ( ',', ':' ), sort_keys = True ).__hash__() return self._hash def __eq__( self, other ): return isinstance( other, HashableDict ) and self._dict == other._dict def __ne__( self, other ): return not self == other def copy( self, **add_or_replace ): return self.__class__( self, **add_or_replace ) def ListDirectory( path ): try: # Path must be a Unicode string to get Unicode strings out of listdir. return os.listdir( ToUnicode( path ) ) except Exception: LOGGER.exception( 'Error while listing %s folder', path ) return [] def GetModificationTime( path ): try: return os.path.getmtime( path ) except OSError: LOGGER.exception( 'Cannot get modification time for path %s', path ) return 0 def ExpectedCoreVersion(): return int( ReadFile( os.path.join( ROOT_DIR, 'CORE_VERSION' ) ) ) def LoadYcmCoreDependencies(): for name in ListDirectory( LIBCLANG_DIR ): if name.startswith( 'libclang' ): libclang_path = os.path.join( LIBCLANG_DIR, name ) if os.path.isfile( libclang_path ): import ctypes ctypes.cdll.LoadLibrary( libclang_path ) return def ImportCore(): """Imports and returns the ycm_core module. This function exists for easily mocking this import in tests.""" import ycm_core as ycm_core return ycm_core def ImportAndCheckCore(): """Checks if ycm_core library is compatible and returns with an exit status.""" try: LoadYcmCoreDependencies() ycm_core = ImportCore() except ImportError as error: message = str( error ) if CORE_MISSING_ERROR_REGEX.match( message ): LOGGER.exception( CORE_MISSING_MESSAGE ) return CORE_MISSING_STATUS LOGGER.exception( message ) return CORE_UNEXPECTED_STATUS try: current_core_version = ycm_core.YcmCoreVersion() except AttributeError: LOGGER.exception( CORE_OUTDATED_MESSAGE ) return CORE_OUTDATED_STATUS if ExpectedCoreVersion() != current_core_version: LOGGER.error( CORE_OUTDATED_MESSAGE ) return CORE_OUTDATED_STATUS return CORE_COMPATIBLE_STATUS def GetClangResourceDir(): resource_dir = os.path.join( LIBCLANG_DIR, 'clang' ) for version in ListDirectory( resource_dir ): return os.path.join( resource_dir, version ) raise RuntimeError( 'Cannot find Clang resource directory.' ) CLANG_RESOURCE_DIR = GetClangResourceDir() def AbsolutePath( path, relative_to ): """Returns a normalised, absolute path to |path|. If |path| is relative, it is resolved relative to |relative_to|.""" if not os.path.isabs( path ): path = os.path.join( relative_to, path ) return os.path.normpath( path ) def UpdateDict( target, override ): """Apply the updates in |override| to the dict |target|. This is like dict.update, but recursive. i.e. if the existing element is a dict, then override elements of the sub-dict rather than wholesale replacing. e.g. UpdateDict( { 'outer': { 'inner': { 'key': 'oldValue', 'existingKey': True } } }, { 'outer': { 'inner': { 'key': 'newValue' } }, 'newKey': { 'newDict': True }, } ) yields: { 'outer': { 'inner': { 'key': 'newValue', 'existingKey': True } }, 'newKey': { newDict: True } } """ for key, value in override.items(): current_value = target.get( key ) if not isinstance( current_value, Mapping ): target[ key ] = value elif isinstance( value, Mapping ): target[ key ] = UpdateDict( current_value, value ) else: target[ key ] = value return target �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/watchdog_plugin.py�����������������������������������������������0000664�0000000�0000000�00000006372�13746324601�0021633�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2013-2020 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 <http://www.gnu.org/licenses/>. import time import copy from threading import Lock from ycmd.handlers import ServerShutdown from ycmd.utils import LOGGER, StartThread # This class implements the Bottle plugin API: # http://bottlepy.org/docs/dev/plugindev.html # # The idea here is to decorate every route handler automatically so that on # every request, we log when the request was made. Then a watchdog thread checks # every check_interval_seconds whether the server has been idle for a time # greater that the passed-in idle_suicide_seconds. If it has, we kill the # server. # # We want to do this so that if something goes bonkers in Vim and the server # never gets killed by the client, we don't end up with lots of zombie servers. class WatchdogPlugin: name = 'watchdog' api = 2 def __init__( self, idle_suicide_seconds, check_interval_seconds ): self._check_interval_seconds = check_interval_seconds self._idle_suicide_seconds = idle_suicide_seconds # No need for a lock on wakeup time since only the watchdog thread ever # reads or sets it. self._last_wakeup_time = time.time() self._last_request_time = time.time() self._last_request_time_lock = Lock() if idle_suicide_seconds <= 0: return StartThread( self._WatchdogMain ) def _GetLastRequestTime( self ): with self._last_request_time_lock: return copy.deepcopy( self._last_request_time ) def _SetLastRequestTime( self, new_value ): with self._last_request_time_lock: self._last_request_time = new_value def _TimeSinceLastRequest( self ): return time.time() - self._GetLastRequestTime() def _TimeSinceLastWakeup( self ): return time.time() - self._last_wakeup_time def _UpdateLastWakeupTime( self ): self._last_wakeup_time = time.time() def _WatchdogMain( self ): while True: time.sleep( self._check_interval_seconds ) # We make sure we don't terminate if we skipped a wakeup time. If we # skipped a check, that means the machine probably went to sleep and the # client might still actually be up. In such cases, we give it one more # wait interval to contact us before we die. if ( self._TimeSinceLastRequest() > self._idle_suicide_seconds and self._TimeSinceLastWakeup() < 2 * self._check_interval_seconds ): LOGGER.info( 'Shutting down server due to inactivity' ) ServerShutdown() self._UpdateLastWakeupTime() def __call__( self, callback ): def wrapper( *args, **kwargs ): self._SetLastRequestTime( time.time() ) return callback( *args, **kwargs ) return wrapper ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ycmd-0+20201028+git1d415c5+ds/ycmd/wsgi_server.py���������������������������������������������������0000664�0000000�0000000�00000003717�13746324601�0021014�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. from waitress.server import TcpWSGIServer import select class StoppableWSGIServer( TcpWSGIServer ): """StoppableWSGIServer is a subclass of the TcpWSGIServer Waitress server with a shutdown method. It is based on StopableWSGIServer class from webtest: https://github.com/Pylons/webtest/blob/master/webtest/http.py""" shutdown_requested = False def Run( self ): """Wrapper of TcpWSGIServer run method. It prevents a traceback from asyncore.""" # Message for compatibility with clients who expect the output from # waitress.serve here print( f'serving on http://{ self.effective_host }:{self.effective_port}' ) try: self.run() except select.error: if not self.shutdown_requested: raise def Shutdown( self ): """Properly shutdown the server.""" self.shutdown_requested = True # Shutdown waitress threads. self.task_dispatcher.shutdown() # Close asyncore channels. # We use list() here because _map is modified while looping through it. # NOTE: _map is an attribute from the asyncore.dispatcher class, which is a # base class of TcpWSGIServer. This may change in future versions of # waitress so extra care should be taken when updating waitress. for channel in list( self._map.values() ): channel.close() �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������