Browse Source

Std headers are now parsed. Also moved Source::ClangViewParse::get_compilation_commands to CompileCommands::get_arguments and some cleanup."

merge-requests/365/head
eidheim 8 years ago
parent
commit
8a006962bf
  1. 2
      libclangmm
  2. 63
      src/compile_commands.cc
  3. 3
      src/compile_commands.h
  4. 6
      src/config.cc
  5. 4
      src/config.h
  6. 9
      src/ctags.cc
  7. 8
      src/source.cc
  8. 158
      src/source_clang.cc
  9. 3
      src/source_clang.h
  10. 22
      tests/source_clang_test.cc
  11. 4
      tests/source_clang_test_files/build/compile_commands.json

2
libclangmm

@ -1 +1 @@
Subproject commit e687e95fa62b26fa3d6e930ffbbec8fb389cf30e Subproject commit 955c113c73ecdfbf5fecc1c0b8fef537693c2f25

63
src/compile_commands.cc

@ -1,5 +1,7 @@
#include "compile_commands.h" #include "compile_commands.h"
#include "clangmm.h"
#include <boost/property_tree/json_parser.hpp> #include <boost/property_tree/json_parser.hpp>
#include <regex>
std::vector<std::string> CompileCommands::Command::parameter_values(const std::string &parameter_name) const { std::vector<std::string> CompileCommands::Command::parameter_values(const std::string &parameter_name) const {
std::vector<std::string> parameter_values; std::vector<std::string> parameter_values;
@ -77,3 +79,64 @@ CompileCommands::CompileCommands(const boost::filesystem::path &build_path) {
} }
catch(...) {} catch(...) {}
} }
std::vector<std::string> CompileCommands::get_arguments(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path) {
clangmm::CompilationDatabase db(build_path.string());
clangmm::CompileCommands commands(file_path.string(), db);
std::vector<clangmm::CompileCommand> cmds = commands.get_commands();
std::vector<std::string> arguments;
for (auto &cmd : cmds) {
auto cmd_arguments = cmd.get_arguments();
bool ignore_next=false;
for (size_t c = 1; c < cmd_arguments.size(); c++) {
if(ignore_next) {
ignore_next=false;
continue;
}
else if(cmd_arguments[c]=="-o" || cmd_arguments[c]=="-c") {
ignore_next=true;
continue;
}
arguments.emplace_back(cmd_arguments[c]);
}
}
auto clang_version_string=clangmm::to_string(clang_getClangVersion());
const static std::regex clang_version_regex("^[A-Za-z ]+([0-9.]+).*$");
std::smatch sm;
if(std::regex_match(clang_version_string, sm, clang_version_regex)) {
auto clang_version=sm[1].str();
arguments.emplace_back("-I/usr/lib/clang/"+clang_version+"/include");
arguments.emplace_back("-I/usr/lib64/clang/"+clang_version+"/include"); // For Fedora
#if defined(__APPLE__) && CINDEX_VERSION_MAJOR==0 && CINDEX_VERSION_MINOR<32 // TODO: remove during 2018 if llvm3.7 is no longer in homebrew (CINDEX_VERSION_MINOR=32 equals clang-3.8 I think)
arguments.emplace_back("-I/usr/local/Cellar/llvm/"+clang_version+"/lib/clang/"+clang_version+"/include");
arguments.emplace_back("-I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1");
arguments.emplace_back("-I/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1"); //Added for OS X 10.11
#endif
#ifdef _WIN32
auto env_msystem_prefix=std::getenv("MSYSTEM_PREFIX");
if(env_msystem_prefix!=nullptr)
arguments.emplace_back("-I"+(boost::filesystem::path(env_msystem_prefix)/"lib/clang"/clang_version/"include").string());
#endif
}
arguments.emplace_back("-fretain-comments-from-system-headers");
auto extension=file_path.extension().string();
if(extension==".h" || //TODO: temporary fix for .h-files (parse as c++)
extension!=".c")
arguments.emplace_back("-xc++");
if(extension.empty()) { // For std headers
arguments.emplace_back("-xc++");
arguments.emplace_back("-w"); // Disable all warnings
}
if((extension.size()>1 && extension[1]=='h') || extension==".tcc")
arguments.emplace_back("-Wno-pragma-once-outside-header");
if(!build_path.empty()) {
arguments.emplace_back("-working-directory");
arguments.emplace_back(build_path.string());
}
return arguments;
}

3
src/compile_commands.h

@ -18,6 +18,9 @@ public:
CompileCommands(const boost::filesystem::path &build_path); CompileCommands(const boost::filesystem::path &build_path);
std::vector<Command> commands; std::vector<Command> commands;
/// Return arguments for the given file using libclangmm
static std::vector<std::string> get_arguments(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path);
}; };
#endif // JUCI_COMPILE_COMMANDS_H_ #endif // JUCI_COMPILE_COMMANDS_H_

6
src/config.cc

@ -11,12 +11,6 @@ Config::Config() {
if(home_path.empty()) if(home_path.empty())
throw std::runtime_error("Could not find home path"); throw std::runtime_error("Could not find home path");
home_juci_path=home_path/".juci"; home_juci_path=home_path/".juci";
#ifdef _WIN32
auto env_MSYSTEM_PREFIX=std::getenv("MSYSTEM_PREFIX");
if(env_MSYSTEM_PREFIX!=nullptr)
terminal.msys2_mingw_path=boost::filesystem::path(env_MSYSTEM_PREFIX);
#endif
} }
void Config::load() { void Config::load() {

4
src/config.h

@ -29,10 +29,6 @@ public:
int history_size; int history_size;
std::string font; std::string font;
bool show_progress; bool show_progress;
#ifdef _WIN32
boost::filesystem::path msys2_mingw_path;
#endif
}; };
class Project { class Project {

9
src/ctags.cc

@ -21,8 +21,13 @@ std::pair<boost::filesystem::path, std::unique_ptr<std::stringstream> > Ctags::g
if(!relative_debug_path.empty()) if(!relative_debug_path.empty())
exclude+=" --exclude="+relative_debug_path.string(); exclude+=" --exclude="+relative_debug_path.string();
} }
else else {
run_path=path; boost::system::error_code ec;
if(boost::filesystem::is_directory(path, ec) || ec)
run_path=path;
else
run_path=path.parent_path();
}
std::stringstream stdin_stream; std::stringstream stdin_stream;
//TODO: when debian stable gets newer g++ version that supports move on streams, remove unique_ptr below //TODO: when debian stable gets newer g++ version that supports move on streams, remove unique_ptr below

8
src/source.cc

@ -32,6 +32,14 @@ Glib::RefPtr<Gsv::Language> Source::guess_language(const boost::filesystem::path
language=language_manager->get_language("makefile"); language=language_manager->get_language("makefile");
else if(file_path.extension()==".tcc") else if(file_path.extension()==".tcc")
language=language_manager->get_language("cpphdr"); language=language_manager->get_language("cpphdr");
else if(!file_path.has_extension()) {
for(auto &part: file_path) {
if(part=="include") {
language=language_manager->get_language("cpphdr");
break;
}
}
}
} }
return language; return language;
} }

158
src/source_clang.cc

@ -10,6 +10,7 @@
#include "ctags.h" #include "ctags.h"
#include "selection_dialog.h" #include "selection_dialog.h"
#include "filesystem.h" #include "filesystem.h"
#include "compile_commands.h"
clangmm::Index Source::ClangViewParse::clang_index(0, 0); clangmm::Index Source::ClangViewParse::clang_index(0, 0);
@ -93,8 +94,15 @@ void Source::ClangViewParse::parse_initialize() {
pos++; pos++;
} }
auto &buffer_raw=const_cast<std::string&>(buffer.raw()); auto &buffer_raw=const_cast<std::string&>(buffer.raw());
remove_include_guard(buffer_raw); if(this->language && (this->language->get_id()=="chdr" || this->language->get_id()=="cpphdr"))
clang_tu = std::make_unique<clangmm::TranslationUnit>(clang_index, file_path.string(), get_compilation_commands(), buffer_raw); clangmm::remove_include_guard(buffer_raw);
auto build=Project::Build::create(file_path);
if(build->project_path.empty())
Info::get().print(file_path.filename().string()+": could not find a supported build system");
build->update_default();
auto arguments=CompileCommands::get_arguments(build->get_default_path(), file_path);
clang_tu = std::make_unique<clangmm::TranslationUnit>(clang_index, file_path.string(), arguments, buffer_raw);
clang_tokens=clang_tu->get_tokens(0, buffer.bytes()-1); clang_tokens=clang_tu->get_tokens(0, buffer.bytes()-1);
update_syntax(); update_syntax();
@ -124,7 +132,8 @@ void Source::ClangViewParse::parse_initialize() {
} }
else if (parse_process_state==ParseProcessState::PROCESSING && parse_lock.try_lock()) { else if (parse_process_state==ParseProcessState::PROCESSING && parse_lock.try_lock()) {
auto &parse_thread_buffer_raw=const_cast<std::string&>(parse_thread_buffer.raw()); auto &parse_thread_buffer_raw=const_cast<std::string&>(parse_thread_buffer.raw());
remove_include_guard(parse_thread_buffer_raw); if(this->language && (this->language->get_id()=="chdr" || this->language->get_id()=="cpphdr"))
clangmm::remove_include_guard(parse_thread_buffer_raw);
auto status=clang_tu->ReparseTranslationUnit(parse_thread_buffer_raw); auto status=clang_tu->ReparseTranslationUnit(parse_thread_buffer_raw);
parsing_in_progress->done("done"); parsing_in_progress->done("done");
if(status==0) { if(status==0) {
@ -190,146 +199,6 @@ void Source::ClangViewParse::soft_reparse() {
}, 1000); }, 1000);
} }
std::vector<std::string> Source::ClangViewParse::get_compilation_commands() {
auto build=Project::Build::create(file_path);
if(build->project_path.empty())
Info::get().print(file_path.filename().string()+": could not find a supported build system");
auto default_build_path=build->get_default_path();
build->update_default();
clangmm::CompilationDatabase db(default_build_path.string());
clangmm::CompileCommands commands(file_path.string(), db);
std::vector<clangmm::CompileCommand> cmds = commands.get_commands();
std::vector<std::string> arguments;
for (auto &i : cmds) {
std::vector<std::string> lol = i.get_command_as_args();
for (size_t a = 1; a < lol.size()-4; a++) {
arguments.emplace_back(lol[a]);
}
}
auto clang_version_string=clangmm::to_string(clang_getClangVersion());
const static std::regex clang_version_regex("^[A-Za-z ]+([0-9.]+).*$");
std::smatch sm;
if(std::regex_match(clang_version_string, sm, clang_version_regex)) {
auto clang_version=sm[1].str();
arguments.emplace_back("-I/usr/lib/clang/"+clang_version+"/include");
arguments.emplace_back("-I/usr/lib64/clang/"+clang_version+"/include"); // For Fedora
#if defined(__APPLE__) && CINDEX_VERSION_MAJOR==0 && CINDEX_VERSION_MINOR<32 // TODO: remove during 2018 if llvm3.7 is no longer in homebrew (CINDEX_VERSION_MINOR=32 equals clang-3.8 I think)
arguments.emplace_back("-I/usr/local/Cellar/llvm/"+clang_version+"/lib/clang/"+clang_version+"/include");
arguments.emplace_back("-I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1");
arguments.emplace_back("-I/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1"); //Added for OS X 10.11
#endif
#ifdef _WIN32
if(!Config::get().terminal.msys2_mingw_path.empty())
arguments.emplace_back("-I"+(Config::get().terminal.msys2_mingw_path/"lib/clang"/clang_version/"include").string());
#endif
}
arguments.emplace_back("-fretain-comments-from-system-headers");
if(file_path.extension()==".h" || //TODO: temporary fix for .h-files (parse as c++)
(language && (language->get_id()=="cpp" || language->get_id()=="cpphdr")))
arguments.emplace_back("-xc++");
if(language && (language->get_id()=="chdr" || language->get_id()=="cpphdr"))
arguments.emplace_back("-Wno-pragma-once-outside-header");
if(!default_build_path.empty()) {
arguments.emplace_back("-working-directory");
arguments.emplace_back(default_build_path.string());
}
return arguments;
}
void Source::ClangViewParse::remove_include_guard(std::string &buffer) {
if(!(language && (language->get_id()=="chdr" || language->get_id()=="cpphdr")))
return;
static std::regex ifndef_regex1("^[ \t]*#ifndef[ \t]+([A-Za-z0-9_]+).*$");
static std::regex ifndef_regex2("^[ \t]*#if[ \t]+![ \t]*defined[ \t]*\\([ \t]*([A-Za-z0-9_]+).*$");
static std::regex define_regex("^[ \t]*#define[ \t]+([A-Za-z0-9_]+).*$");
static std::regex endif_regex("^[ \t]*#endif.*$");
std::vector<std::pair<size_t, size_t>> ranges;
bool found_ifndef=false, found_define=false;
bool line_comment=false, multiline_comment=false;
size_t start_of_line=0;
std::string line;
std::string preprocessor_identifier;
for(size_t c=0;c<buffer.size();++c) {
if(!line_comment && !multiline_comment && buffer[c]=='/' && c+1<buffer.size() && (buffer[c+1]=='/' || buffer[c+1]=='*')) {
if(buffer[c+1]=='/')
line_comment=true;
else
multiline_comment=true;
++c;
}
else if(multiline_comment && buffer[c]=='*' && c+1<buffer.size() && buffer[c+1]=='/') {
multiline_comment=false;
++c;
}
else if(buffer[c]=='\n') {
bool empty_line=true;
for(auto &chr: line) {
if(chr!=' ' && chr!='\t') {
empty_line=false;
break;
}
}
std::smatch sm;
if(empty_line) {}
else if(!found_ifndef && (std::regex_match(line, sm, ifndef_regex1) || std::regex_match(line, sm, ifndef_regex2))) {
found_ifndef=true;
ranges.emplace_back(start_of_line, c);
preprocessor_identifier=sm[1].str();
}
else if(found_ifndef && std::regex_match(line, sm, define_regex)) {
found_define=true;
ranges.emplace_back(start_of_line, c);
if(preprocessor_identifier!=sm[1].str())
return;
break;
}
else
return;
line_comment=false;
line.clear();
if(c+1<buffer.size())
start_of_line=c+1;
else
return;
}
else if(!line_comment && !multiline_comment && buffer[c]!='\r')
line+=buffer[c];
}
if(found_ifndef && found_define) {
size_t last_char_pos=std::string::npos;
for(size_t c=buffer.size()-1;c!=std::string::npos;--c) {
if(last_char_pos==std::string::npos) {
if(buffer[c]!=' ' && buffer[c]!='\t' && buffer[c]!='\r' && buffer[c]!='\n')
last_char_pos=c;
}
else {
if(buffer[c]=='\n' && c+1<buffer.size()) {
auto line=buffer.substr(c+1, last_char_pos-c);
std::smatch sm;
if(std::regex_match(line, sm, endif_regex)) {
ranges.emplace_back(c+1, last_char_pos+1);
for(auto &range: ranges) {
for(size_t c=range.first;c<range.second;++c) {
if(buffer[c]!='\r')
buffer[c]=' ';
}
}
return;
}
return;
}
}
}
}
}
void Source::ClangViewParse::update_syntax() { void Source::ClangViewParse::update_syntax() {
auto buffer=get_buffer(); auto buffer=get_buffer();
const auto apply_tag=[this, buffer](const std::pair<clangmm::Offset, clangmm::Offset> &offsets, int type) { const auto apply_tag=[this, buffer](const std::pair<clangmm::Offset, clangmm::Offset> &offsets, int type) {
@ -642,7 +511,8 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa
autocomplete.get_suggestions=[this](std::string &buffer, int line_number, int column) { autocomplete.get_suggestions=[this](std::string &buffer, int line_number, int column) {
auto suggestions=std::make_shared<std::vector<Suggestion>>(); auto suggestions=std::make_shared<std::vector<Suggestion>>();
remove_include_guard(buffer); if(this->language && (this->language->get_id()=="chdr" || this->language->get_id()=="cpphdr"))
clangmm::remove_include_guard(buffer);
auto results=clang_tu->get_code_completions(buffer, line_number, column); auto results=clang_tu->get_code_completions(buffer, line_number, column);
if(results.cx_results==nullptr) { if(results.cx_results==nullptr) {
auto expected=ParseState::PROCESSING; auto expected=ParseState::PROCESSING;

3
src/source_clang.h

@ -33,8 +33,6 @@ namespace Source {
std::shared_ptr<Terminal::InProgress> parsing_in_progress; std::shared_ptr<Terminal::InProgress> parsing_in_progress;
void remove_include_guard(std::string &buffer);
void show_diagnostic_tooltips(const Gdk::Rectangle &rectangle) override; void show_diagnostic_tooltips(const Gdk::Rectangle &rectangle) override;
void show_type_tooltips(const Gdk::Rectangle &rectangle) override; void show_type_tooltips(const Gdk::Rectangle &rectangle) override;
@ -55,7 +53,6 @@ namespace Source {
std::vector<clangmm::Diagnostic> clang_diagnostics; std::vector<clangmm::Diagnostic> clang_diagnostics;
static clangmm::Index clang_index; static clangmm::Index clang_index;
std::vector<std::string> get_compilation_commands();
}; };
class ClangViewAutocomplete : public virtual ClangViewParse { class ClangViewAutocomplete : public virtual ClangViewParse {

22
tests/source_clang_test.cc

@ -84,50 +84,50 @@ int main() {
{ {
clang_view->language=Gsv::LanguageManager::get_default()->get_language("chdr"); clang_view->language=Gsv::LanguageManager::get_default()->get_language("chdr");
std::string source="#ifndef F\n#define F\n#endif // F"; std::string source="#ifndef F\n#define F\n#endif // F";
clang_view->remove_include_guard(source); clangmm::remove_include_guard(source);
g_assert_cmpstr(source.c_str(),==," \n \n "); g_assert_cmpstr(source.c_str(),==," \n \n ");
source="#ifndef F\n#define F\n#endif // F\n"; source="#ifndef F\n#define F\n#endif // F\n";
clang_view->remove_include_guard(source); clangmm::remove_include_guard(source);
g_assert_cmpstr(source.c_str(),==," \n \n \n"); g_assert_cmpstr(source.c_str(),==," \n \n \n");
source="/*test*/\n#ifndef F\n#define F\n#endif // F\n"; source="/*test*/\n#ifndef F\n#define F\n#endif // F\n";
clang_view->remove_include_guard(source); clangmm::remove_include_guard(source);
g_assert_cmpstr(source.c_str(),==,"/*test*/\n \n \n \n"); g_assert_cmpstr(source.c_str(),==,"/*test*/\n \n \n \n");
source="//test\n#ifndef F\n#define F\n#endif // F\n"; source="//test\n#ifndef F\n#define F\n#endif // F\n";
clang_view->remove_include_guard(source); clangmm::remove_include_guard(source);
g_assert_cmpstr(source.c_str(),==,"//test\n \n \n \n"); g_assert_cmpstr(source.c_str(),==,"//test\n \n \n \n");
source="#ifndef F /*test*/\n#define F\n#endif // F"; source="#ifndef F /*test*/\n#define F\n#endif // F";
clang_view->remove_include_guard(source); clangmm::remove_include_guard(source);
g_assert_cmpstr(source.c_str(),==," \n \n "); g_assert_cmpstr(source.c_str(),==," \n \n ");
source="#ifndef F //test\n#define F\n#endif // F"; source="#ifndef F //test\n#define F\n#endif // F";
clang_view->remove_include_guard(source); clangmm::remove_include_guard(source);
g_assert_cmpstr(source.c_str(),==," \n \n "); g_assert_cmpstr(source.c_str(),==," \n \n ");
source="#ifndef F\n//test\n#define F\n#endif // F\n"; source="#ifndef F\n//test\n#define F\n#endif // F\n";
clang_view->remove_include_guard(source); clangmm::remove_include_guard(source);
g_assert_cmpstr(source.c_str(),==," \n//test\n \n \n"); g_assert_cmpstr(source.c_str(),==," \n//test\n \n \n");
source="#if !defined(F)\n#define F\n#endif\n"; source="#if !defined(F)\n#define F\n#endif\n";
clang_view->remove_include_guard(source); clangmm::remove_include_guard(source);
g_assert_cmpstr(source.c_str(),==," \n \n \n"); g_assert_cmpstr(source.c_str(),==," \n \n \n");
source="#ifndef F\ntest\n#define F\n#endif // F\n"; source="#ifndef F\ntest\n#define F\n#endif // F\n";
auto old_source=source; auto old_source=source;
clang_view->remove_include_guard(source); clangmm::remove_include_guard(source);
g_assert_cmpstr(old_source.c_str(),==,source.c_str()); g_assert_cmpstr(old_source.c_str(),==,source.c_str());
source="test\n#ifndef F\n#define F\n#endif // F\n"; source="test\n#ifndef F\n#define F\n#endif // F\n";
old_source=source; old_source=source;
clang_view->remove_include_guard(source); clangmm::remove_include_guard(source);
g_assert_cmpstr(old_source.c_str(),==,source.c_str()); g_assert_cmpstr(old_source.c_str(),==,source.c_str());
source="#ifndef F\n#define F\n#endif // F\ntest\n"; source="#ifndef F\n#define F\n#endif // F\ntest\n";
old_source=source; old_source=source;
clang_view->remove_include_guard(source); clangmm::remove_include_guard(source);
g_assert_cmpstr(old_source.c_str(),==,source.c_str()); g_assert_cmpstr(old_source.c_str(),==,source.c_str());
} }

4
tests/source_clang_test_files/build/compile_commands.json

@ -1,7 +1,7 @@
[ [
{ {
"directory": ".", "directory": ".",
"command": "c++ -std=c++1y -Wall -Wextra -Wno-unused-parameter -o CMakeFiles/test.dir/main.cpp.o main.cpp", "command": "c++ -std=c++1y -Wall -Wextra -Wno-unused-parameter -o CMakeFiles/test.dir/main.cpp.o -c main.cpp",
"file": "../main.cpp" "file": "../main.cpp"
} }
] ]

Loading…
Cancel
Save