Misc changes to project

* Better LanguageId detection with clangDriver (e.g. .cu -> types::TY_CUDA)
* fallback when there is no .ccls or compile_commands.json

Also Hide clangTooling options from --help
This commit is contained in:
Fangrui Song 2018-10-13 23:22:29 -07:00
parent 4743124370
commit 79352b451c
10 changed files with 67 additions and 101 deletions

View File

@ -188,7 +188,6 @@ target_sources(ccls PRIVATE
src/include_complete.cc src/include_complete.cc
src/indexer.cc src/indexer.cc
src/method.cc src/method.cc
src/language.cc
src/log.cc src/log.cc
src/lsp.cc src/lsp.cc
src/match.cc src/match.cc

View File

@ -15,7 +15,6 @@ limitations under the License.
#pragma once #pragma once
#include "language.h"
#include "lsp.h" #include "lsp.h"
#include "lsp_diagnostic.h" #include "lsp_diagnostic.h"
#include "maybe.h" #include "maybe.h"
@ -35,6 +34,7 @@ limitations under the License.
#include <vector> #include <vector>
using Usr = uint64_t; using Usr = uint64_t;
enum class LanguageId;
struct SymbolIdx { struct SymbolIdx {
Usr usr; Usr usr;

View File

@ -1,45 +0,0 @@
/* Copyright 2017-2018 ccls Authors
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.
==============================================================================*/
#include "language.h"
#include "utils.h"
LanguageId SourceFileLanguage(std::string_view path) {
if (EndsWith(path, ".c"))
return LanguageId::C;
else if (EndsWith(path, ".cpp") || EndsWith(path, ".cc"))
return LanguageId::Cpp;
else if (EndsWith(path, ".mm"))
return LanguageId::ObjCpp;
else if (EndsWith(path, ".m"))
return LanguageId::ObjC;
return LanguageId::Unknown;
}
const char *LanguageIdentifier(LanguageId lang) {
switch (lang) {
case LanguageId::C:
return "c";
case LanguageId::Cpp:
return "cpp";
case LanguageId::ObjC:
return "objective-c";
case LanguageId::ObjCpp:
return "objective-cpp";
default:
return "";
}
}

View File

@ -1,29 +0,0 @@
/* Copyright 2017-2018 ccls Authors
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.
==============================================================================*/
#pragma once
#include "serializer.h"
#include <string_view>
// Used to identify the language at a file level. The ordering is important, as
// a file previously identified as `C`, will be changed to `Cpp` if it
// encounters a c++ declaration.
enum class LanguageId { Unknown = -1, C = 0, Cpp = 1, ObjC = 2, ObjCpp = 3 };
MAKE_REFLECT_TYPE_PROXY(LanguageId);
LanguageId SourceFileLanguage(std::string_view path);
const char *LanguageIdentifier(LanguageId lang);

View File

@ -346,3 +346,9 @@ struct Out_LocationList : public lsOutMessage<Out_LocationList> {
std::vector<lsLocation> result; std::vector<lsLocation> result;
}; };
MAKE_REFLECT_STRUCT(Out_LocationList, jsonrpc, id, result); MAKE_REFLECT_STRUCT(Out_LocationList, jsonrpc, id, result);
// Used to identify the language at a file level. The ordering is important, as
// a file previously identified as `C`, will be changed to `Cpp` if it
// encounters a c++ declaration.
enum class LanguageId { Unknown = -1, C = 0, Cpp = 1, ObjC = 2, ObjCpp = 3 };
MAKE_REFLECT_TYPE_PROXY(LanguageId);

View File

@ -42,17 +42,19 @@ using namespace llvm::cl;
std::string g_init_options; std::string g_init_options;
namespace { namespace {
opt<bool> opt_help("h", desc("Alias for -help")); OptionCategory C("ccls options");
opt<int> opt_verbose("v", desc("verbosity"), init(0));
opt<bool> opt_help("h", desc("Alias for -help"), cat(C));
opt<int> opt_verbose("v", desc("verbosity"), init(0), cat(C));
opt<std::string> opt_test_index("test-index", ValueOptional, init("!"), opt<std::string> opt_test_index("test-index", ValueOptional, init("!"),
desc("run index tests")); desc("run index tests"), cat(C));
opt<std::string> opt_init("init", desc("extra initialization options")); opt<std::string> opt_init("init", desc("extra initialization options in JSON"),
opt<std::string> opt_log_file("log-file", desc("log"), value_desc("filename")); cat(C));
opt<std::string> opt_log_file("log-file", desc("log"), value_desc("filename"),
cat(C));
opt<std::string> opt_log_file_append("log-file-append", desc("log"), opt<std::string> opt_log_file_append("log-file-append", desc("log"),
value_desc("filename")); value_desc("filename"), cat(C));
list<std::string> opt_extra(Positional, ZeroOrMore, desc("extra"));
void CloseLog() { fclose(ccls::log::file); } void CloseLog() { fclose(ccls::log::file); }
@ -62,6 +64,10 @@ int main(int argc, char **argv) {
TraceMe(); TraceMe();
sys::PrintStackTraceOnErrorSignal(argv[0]); sys::PrintStackTraceOnErrorSignal(argv[0]);
for (auto &I : TopLevelSubCommand->OptionsMap)
if (I.second->Category != &C)
I.second->setHiddenFlag(ReallyHidden);
ParseCommandLineOptions(argc, argv, ParseCommandLineOptions(argc, argv,
"C/C++/Objective-C language server\n\n" "C/C++/Objective-C language server\n\n"
"See more on https://github.com/MaskRay/ccls/wiki"); "See more on https://github.com/MaskRay/ccls/wiki");

View File

@ -127,7 +127,8 @@ struct Handler_TextDocumentDidOpen
// Submit new index request if it is not a header file or there is no // Submit new index request if it is not a header file or there is no
// pending index request. // pending index request.
if (SourceFileLanguage(path) != LanguageId::Unknown || std::pair<LanguageId, bool> lang = lookupExtension(path);
if ((lang.first != LanguageId::Unknown && !lang.second) ||
!pipeline::pending_index_requests) !pipeline::pending_index_requests)
pipeline::Index(path, args, IndexMode::Normal); pipeline::Index(path, args, IndexMode::Normal);

View File

@ -21,6 +21,18 @@ using namespace ccls;
namespace { namespace {
MethodType kMethodType = "textDocument/hover"; MethodType kMethodType = "textDocument/hover";
const char *LanguageIdentifier(LanguageId lang) {
switch (lang) {
// clang-format off
case LanguageId::C: return "c";
case LanguageId::Cpp: return "cpp";
case LanguageId::ObjC: return "objective-c";
case LanguageId::ObjCpp: return "objective-cpp";
default: return "";
// clang-format on
}
}
// Returns the hover or detailed name for `sym`, if any. // Returns the hover or detailed name for `sym`, if any.
std::pair<std::optional<lsMarkedString>, std::optional<lsMarkedString>> std::pair<std::optional<lsMarkedString>, std::optional<lsMarkedString>>
GetHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) { GetHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {

View File

@ -16,7 +16,6 @@ limitations under the License.
#include "project.h" #include "project.h"
#include "filesystem.hh" #include "filesystem.hh"
#include "language.h"
#include "log.hh" #include "log.hh"
#include "match.h" #include "match.h"
#include "pipeline.hh" #include "pipeline.hh"
@ -27,6 +26,7 @@ limitations under the License.
#include <clang/Driver/Compilation.h> #include <clang/Driver/Compilation.h>
#include <clang/Driver/Driver.h> #include <clang/Driver/Driver.h>
#include <clang/Driver/Types.h>
#include <clang/Frontend/CompilerInstance.h> #include <clang/Frontend/CompilerInstance.h>
#include <clang/Tooling/CompilationDatabase.h> #include <clang/Tooling/CompilationDatabase.h>
#include <llvm/ADT/STLExtras.h> #include <llvm/ADT/STLExtras.h>
@ -47,6 +47,25 @@ using namespace ccls;
using namespace clang; using namespace clang;
using namespace llvm; using namespace llvm;
std::pair<LanguageId, bool> lookupExtension(std::string_view filename) {
using namespace clang::driver;
auto I = types::lookupTypeForExtension(
sys::path::extension({filename.data(), filename.size()}).substr(1));
bool header = I == types::TY_CHeader || I == types::TY_CXXHeader ||
I == types::TY_ObjCXXHeader;
bool objc = types::isObjC(I);
LanguageId ret;
if (types::isCXX(I))
ret = objc ? LanguageId::ObjCpp : LanguageId::Cpp;
else if (objc)
ret = LanguageId::ObjC;
else if (I == types::TY_C || I == types::TY_CHeader)
ret = LanguageId::C;
else
ret = LanguageId::Unknown;
return {ret, header};
}
namespace { namespace {
enum class ProjectMode { CompileCommandsJson, DotCcls, ExternalCommand }; enum class ProjectMode { CompileCommandsJson, DotCcls, ExternalCommand };
@ -76,7 +95,7 @@ struct ProjectProcessor {
// Expand %c %cpp %clang // Expand %c %cpp %clang
std::vector<const char *> args; std::vector<const char *> args;
args.reserve(entry.args.size() + g_config->clang.extraArgs.size() + 1); args.reserve(entry.args.size() + g_config->clang.extraArgs.size() + 1);
const LanguageId lang = SourceFileLanguage(entry.filename); const LanguageId lang = lookupExtension(entry.filename).first;
for (const char *arg : entry.args) { for (const char *arg : entry.args) {
if (strncmp(arg, "%c ", 3) == 0) { if (strncmp(arg, "%c ", 3) == 0) {
if (lang == LanguageId::C) if (lang == LanguageId::C)
@ -98,7 +117,7 @@ struct ProjectProcessor {
size_t hash = std::hash<std::string>{}(entry.directory); size_t hash = std::hash<std::string>{}(entry.directory);
for (auto &arg : args) { for (auto &arg : args) {
if (arg[0] != '-' && EndsWith(arg, base_name)) { if (arg[0] != '-' && EndsWith(arg, base_name)) {
const LanguageId lang = SourceFileLanguage(arg); LanguageId lang = lookupExtension(arg).first;
if (lang != LanguageId::Unknown) { if (lang != LanguageId::Unknown) {
hash_combine(hash, size_t(lang)); hash_combine(hash, size_t(lang));
continue; continue;
@ -182,12 +201,6 @@ ReadCompilerArgumentsFromFile(const std::string &path) {
std::vector<Project::Entry> LoadFromDirectoryListing(ProjectConfig *config) { std::vector<Project::Entry> LoadFromDirectoryListing(ProjectConfig *config) {
std::vector<Project::Entry> result; std::vector<Project::Entry> result;
config->mode = ProjectMode::DotCcls; config->mode = ProjectMode::DotCcls;
SmallString<256> Path;
sys::path::append(Path, config->root, ".ccls");
LOG_IF_S(WARNING, !sys::fs::exists(Path) && g_config->clang.extraArgs.empty())
<< "ccls has no clang arguments. Use either "
"compile_commands.json or .ccls, See ccls README for "
"more information.";
std::unordered_map<std::string, std::vector<const char *>> folder_args; std::unordered_map<std::string, std::vector<const char *>> folder_args;
std::vector<std::string> files; std::vector<std::string> files;
@ -196,7 +209,8 @@ std::vector<Project::Entry> LoadFromDirectoryListing(ProjectConfig *config) {
GetFilesInFolder(root, true /*recursive*/, GetFilesInFolder(root, true /*recursive*/,
true /*add_folder_to_path*/, true /*add_folder_to_path*/,
[&folder_args, &files](const std::string &path) { [&folder_args, &files](const std::string &path) {
if (SourceFileLanguage(path) != LanguageId::Unknown) { std::pair<LanguageId, bool> lang = lookupExtension(path);
if (lang.first != LanguageId::Unknown && !lang.second) {
files.push_back(path); files.push_back(path);
} else if (sys::path::filename(path) == ".ccls") { } else if (sys::path::filename(path) == ".ccls") {
std::vector<const char *> args = ReadCompilerArgumentsFromFile(path); std::vector<const char *> args = ReadCompilerArgumentsFromFile(path);
@ -247,14 +261,13 @@ std::vector<Project::Entry>
LoadEntriesFromDirectory(ProjectConfig *project, LoadEntriesFromDirectory(ProjectConfig *project,
const std::string &opt_compdb_dir) { const std::string &opt_compdb_dir) {
// If there is a .ccls file always load using directory listing. // If there is a .ccls file always load using directory listing.
SmallString<256> Path; SmallString<256> Path, CclsPath;
sys::path::append(Path, project->root, ".ccls"); sys::path::append(CclsPath, project->root, ".ccls");
if (sys::fs::exists(Path)) if (sys::fs::exists(CclsPath))
return LoadFromDirectoryListing(project); return LoadFromDirectoryListing(project);
// If |compilationDatabaseCommand| is specified, execute it to get the compdb. // If |compilationDatabaseCommand| is specified, execute it to get the compdb.
std::string comp_db_dir; std::string comp_db_dir;
Path.clear();
if (g_config->compilationDatabaseCommand.empty()) { if (g_config->compilationDatabaseCommand.empty()) {
project->mode = ProjectMode::CompileCommandsJson; project->mode = ProjectMode::CompileCommandsJson;
// Try to load compile_commands.json, but fallback to a project listing. // Try to load compile_commands.json, but fallback to a project listing.
@ -296,8 +309,8 @@ LoadEntriesFromDirectory(ProjectConfig *project,
#endif #endif
} }
if (!CDB) { if (!CDB) {
LOG_S(WARNING) << "failed to load " << Path.c_str() << " " << err_msg; LOG_S(WARNING) << "no .ccls or compile_commands.json . Consider adding one";
return {}; return LoadFromDirectoryListing(project);
} }
LOG_S(INFO) << "loaded " << Path.c_str(); LOG_S(INFO) << "loaded " << Path.c_str();

View File

@ -16,6 +16,7 @@ limitations under the License.
#pragma once #pragma once
#include "config.h" #include "config.h"
#include "lsp.h"
#include "method.h" #include "method.h"
#include <functional> #include <functional>
@ -26,6 +27,8 @@ limitations under the License.
struct WorkingFiles; struct WorkingFiles;
std::pair<LanguageId, bool> lookupExtension(std::string_view filename);
struct Project { struct Project {
struct Entry { struct Entry {
std::string root; std::string root;