diff --git a/CMakeLists.txt b/CMakeLists.txt index e3364e3b..0e77fcd8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,6 +96,11 @@ set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) target_link_libraries(ccls PRIVATE Threads::Threads) +if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") +else() + target_link_libraries(ccls PRIVATE -lstdc++fs) +endif() + if(${CMAKE_SYSTEM_NAME} STREQUAL Linux) # loguru calls dladdr target_link_libraries(ccls PRIVATE ${CMAKE_DL_LIBS}) @@ -233,7 +238,6 @@ target_sources(ccls PRIVATE src/messages/text_document_did_open.cc src/messages/text_document_did_save.cc src/messages/text_document_document_highlight.cc - src/messages/text_document_document_link.cc src/messages/text_document_document_symbol.cc src/messages/text_document_hover.cc src/messages/text_document_references.cc diff --git a/src/filesystem.hh b/src/filesystem.hh new file mode 100644 index 00000000..0608bd71 --- /dev/null +++ b/src/filesystem.hh @@ -0,0 +1,5 @@ +#pragma once + +#include + +namespace fs = std::experimental::filesystem; diff --git a/src/lex_utils.cc b/src/lex_utils.cc index 5d66adf0..cb3ad0f5 100644 --- a/src/lex_utils.cc +++ b/src/lex_utils.cc @@ -24,116 +24,6 @@ int GetOffsetForPosition(lsPosition position, std::string_view content) { return int(i); } -// TODO: eliminate |line_number| param. -std::optional ExtractQuotedRange(int line_number, const std::string& line) { - // Find starting and ending quote. - int start = 0; - while (start < (int)line.size()) { - char c = line[start]; - ++start; - if (c == '"' || c == '<') - break; - } - if (start == (int)line.size()) - return std::nullopt; - - int end = (int)line.size(); - while (end > 0) { - char c = line[end]; - if (c == '"' || c == '>') - break; - --end; - } - - if (start >= end) - return std::nullopt; - - return lsRange(lsPosition(line_number, start), lsPosition(line_number, end)); -} - -void LexFunctionDeclaration(const std::string& buffer_content, - lsPosition declaration_spelling, - std::optional type_name, - std::string* insert_text, - int* newlines_after_name) { - int name_start = GetOffsetForPosition(declaration_spelling, buffer_content); - - bool parse_return_type = true; - // We need to check if we have a return type (ctors and dtors do not). - if (type_name) { - int name_end = name_start; - while (name_end < buffer_content.size()) { - char c = buffer_content[name_end]; - if (isspace(c) || c == '(') - break; - ++name_end; - } - - std::string func_name = - buffer_content.substr(name_start, name_end - name_start); - if (func_name == *type_name || func_name == ("~" + *type_name)) - parse_return_type = false; - } - - // We need to fetch the return type. This can get complex, ie, - // - // std::vector foo(); - // - int return_start = name_start; - if (parse_return_type) { - int paren_balance = 0; - int angle_balance = 0; - bool expect_token = true; - while (return_start > 0) { - char c = buffer_content[return_start - 1]; - if (paren_balance == 0 && angle_balance == 0) { - if (isspace(c) && !expect_token) { - break; - } - if (!isspace(c)) - expect_token = false; - } - - if (c == ')') - ++paren_balance; - if (c == '(') { - --paren_balance; - expect_token = true; - } - - if (c == '>') - ++angle_balance; - if (c == '<') { - --angle_balance; - expect_token = true; - } - - return_start -= 1; - } - } - - // We need to fetch the arguments. Just scan for the next ';'. - *newlines_after_name = 0; - int end = name_start; - while (end < buffer_content.size()) { - char c = buffer_content[end]; - if (c == ';') - break; - if (c == '\n') - *newlines_after_name += 1; - ++end; - } - - std::string result; - result += buffer_content.substr(return_start, name_start - return_start); - if (type_name && !type_name->empty()) - result += *type_name + "::"; - result += buffer_content.substr(name_start, end - name_start); - TrimEndInPlace(result); - result += " {\n}"; - *insert_text = result; -} - std::string_view LexIdentifierAroundPos(lsPosition position, std::string_view content) { int start = GetOffsetForPosition(position, content); diff --git a/src/lex_utils.h b/src/lex_utils.h index 36328ad3..13d6a043 100644 --- a/src/lex_utils.h +++ b/src/lex_utils.h @@ -3,22 +3,11 @@ #include "lsp.h" #include - -#include #include // Utility method to map |position| to an offset inside of |content|. int GetOffsetForPosition(lsPosition position, std::string_view content); -// TODO: eliminate |line_number| param. -std::optional ExtractQuotedRange(int line_number, const std::string& line); - -void LexFunctionDeclaration(const std::string& buffer_content, - lsPosition declaration_spelling, - std::optional type_name, - std::string* insert_text, - int* newlines_after_name); - std::string_view LexIdentifierAroundPos(lsPosition position, std::string_view content); diff --git a/src/messages/text_document_document_link.cc b/src/messages/text_document_document_link.cc deleted file mode 100644 index b44639df..00000000 --- a/src/messages/text_document_document_link.cc +++ /dev/null @@ -1,85 +0,0 @@ -#include "lex_utils.h" -#include "message_handler.h" -#include "queue_manager.h" -#include "working_files.h" - -#include - -namespace { -MethodType kMethodType = "textDocument/documentLink"; - -struct In_TextDocumentDocumentLink : public RequestInMessage { - MethodType GetMethodType() const override { return kMethodType; } - struct Params { - // The document to provide document links for. - lsTextDocumentIdentifier textDocument; - }; - Params params; -}; -MAKE_REFLECT_STRUCT(In_TextDocumentDocumentLink::Params, textDocument); -MAKE_REFLECT_STRUCT(In_TextDocumentDocumentLink, id, params); -REGISTER_IN_MESSAGE(In_TextDocumentDocumentLink); - -// A document link is a range in a text document that links to an internal or -// external resource, like another text document or a web site. -struct lsDocumentLink { - // The range this link applies to. - lsRange range; - // The uri this link points to. If missing a resolve request is sent later. - std::optional target; -}; -MAKE_REFLECT_STRUCT(lsDocumentLink, range, target); - -struct Out_TextDocumentDocumentLink - : public lsOutMessage { - lsRequestId id; - std::vector result; -}; -MAKE_REFLECT_STRUCT(Out_TextDocumentDocumentLink, jsonrpc, id, result); - -struct Handler_TextDocumentDocumentLink - : BaseMessageHandler { - MethodType GetMethodType() const override { return kMethodType; } - void Run(In_TextDocumentDocumentLink* request) override { - Out_TextDocumentDocumentLink out; - out.id = request->id; - - if (config->showDocumentLinksOnIncludes) { - QueryFile* file; - if (!FindFileOrFail(db, project, request->id, - request->params.textDocument.uri.GetPath(), &file)) { - return; - } - - WorkingFile* working_file = working_files->GetFileByFilename( - request->params.textDocument.uri.GetPath()); - if (!working_file) { - LOG_S(WARNING) << "Unable to find working file " - << request->params.textDocument.uri.GetPath(); - return; - } - for (const IndexInclude& include : file->def->includes) { - std::optional buffer_line = working_file->GetBufferPosFromIndexPos( - include.line, nullptr, false); - if (!buffer_line) - continue; - - // Subtract 1 from line because querydb stores 1-based lines but - // vscode expects 0-based lines. - std::optional between_quotes = ExtractQuotedRange( - *buffer_line, working_file->buffer_lines[*buffer_line]); - if (!between_quotes) - continue; - - lsDocumentLink link; - link.target = lsDocumentUri::FromPath(include.resolved_path); - link.range = *between_quotes; - out.result.push_back(link); - } - } - - QueueManager::WriteStdout(kMethodType, out); - } -}; -REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDocumentLink); -} // namespace diff --git a/src/project.cc b/src/project.cc index 198d60f9..4ff67a2a 100644 --- a/src/project.cc +++ b/src/project.cc @@ -292,7 +292,7 @@ Project::Entry GetCompilationEntryFromCompileCommandEntry( std::vector ReadCompilerArgumentsFromFile( const std::string& path) { std::vector args; - for (std::string line : ReadLinesWithEnding(path)) { + for (std::string line : ReadFileLines(path)) { TrimInPlace(line); if (line.empty() || StartsWith(line, "#")) continue; diff --git a/src/test.cc b/src/test.cc index 4c8bde2a..016f0382 100644 --- a/src/test.cc +++ b/src/test.cc @@ -343,7 +343,7 @@ bool RunIndexTests(const std::string& filter_path, bool enable_update) { std::cout << "Running " << path << std::endl; // Parse expected output from the test, parse it into JSON document. - std::vector lines_with_endings = ReadLinesWithEnding(path); + std::vector lines_with_endings = ReadFileLines(path); TextReplacer text_replacer; std::vector flags; std::unordered_map all_expected_output; diff --git a/src/utils.cc b/src/utils.cc index 614e1c67..19af1c3f 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -1,10 +1,10 @@ #include "utils.h" +#include "filesystem.hh" #include "platform.h" #include #include -#include #include #include @@ -18,10 +18,6 @@ #include #include -#if !defined(__APPLE__) -#include -#endif - // DEFAULT_RESOURCE_DIRECTORY is passed with quotes for non-MSVC compilers, ie, // foo vs "foo". #if defined(_MSC_VER) @@ -31,22 +27,15 @@ #define ENSURE_STRING_MACRO_ARGUMENT(x) x #endif -// See http://stackoverflow.com/a/217605 -void TrimStartInPlace(std::string& s) { +void TrimInPlace(std::string& s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(std::isspace)))); -} -void TrimEndInPlace(std::string& s) { s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))) .base(), s.end()); } -void TrimInPlace(std::string& s) { - TrimStartInPlace(s); - TrimEndInPlace(s); -} std::string Trim(std::string s) { TrimInPlace(s); return s; @@ -138,32 +127,8 @@ std::string GetBaseName(const std::string& path) { } std::string StripFileType(const std::string& path) { - size_t last_period = path.find_last_of('.'); - if (last_period != std::string::npos) - return path.substr(0, last_period); - return path; -} - -// See http://stackoverflow.com/a/29752943 -std::string ReplaceAll(const std::string& source, - const std::string& from, - const std::string& to) { - std::string result; - result.reserve(source.length()); // avoids a few memory allocations - - std::string::size_type last_pos = 0; - std::string::size_type find_pos; - - while (std::string::npos != (find_pos = source.find(from, last_pos))) { - result.append(source, last_pos, find_pos - last_pos); - result += to; - last_pos = find_pos + from.length(); - } - - // Care for the rest after last occurrence - result += source.substr(last_pos); - - return result; + fs::path p(path); + return p.parent_path() / p.stem(); } std::vector SplitString(const std::string& str, @@ -198,51 +163,25 @@ static void GetFilesInFolderHelper( bool recursive, std::string output_prefix, const std::function& handler) { - std::queue> q; - q.push(make_pair(folder, output_prefix)); + std::queue> q; + q.push(std::make_pair(fs::path(folder), fs::path(output_prefix))); while (!q.empty()) { - tinydir_dir dir; - if (tinydir_open(&dir, q.front().first.c_str()) == -1) { - LOG_S(WARNING) << "Unable to open directory " << folder; - goto bail; - } - - while (dir.has_next) { - tinydir_file file; - if (tinydir_readfile(&dir, &file) == -1) { - LOG_S(WARNING) << "Unable to read file " << file.name - << " when reading directory " << folder; - goto bail; - } - - // Skip all dot files except .ccls. - // - // The nested ifs are intentional, branching order is subtle here. - // - // Note that in the future if we do support dot directories/files, we must - // always ignore the '.' and '..' directories otherwise this will loop - // infinitely. - if (file.name[0] != '.' || strcmp(file.name, ".ccls") == 0) { - if (file.is_dir) { + for (auto it = fs::directory_iterator(q.front().first); it != fs::directory_iterator(); ++it) { + auto path = it->path(); + std::string filename = path.filename(); + if (filename[0] != '.' || filename == ".ccls") { + fs::file_status status = it->symlink_status(); + if (fs::is_regular_file(status)) + handler(q.front().second / filename); + else if (fs::is_directory(status) || fs::is_symlink(status)) { if (recursive) { - std::string child_dir = q.front().second + file.name + "/"; - if (!IsSymLink(file.path)) - q.push(make_pair(file.path, child_dir)); + std::string child_dir = q.front().second / filename; + if (fs::is_directory(status)) + q.push(make_pair(path, child_dir)); } - } else { - handler(q.front().second + file.name); } } - - if (tinydir_next(&dir) == -1) { - LOG_S(WARNING) << "Unable to fetch next file when reading directory " - << folder; - goto bail; - } } - - bail: - tinydir_close(&dir); q.pop(); } } @@ -311,8 +250,7 @@ std::istream& SafeGetline(std::istream& is, std::string& t) { } bool FileExists(const std::string& filename) { - std::ifstream cache(filename); - return cache.is_open(); + return fs::exists(filename); } std::optional ReadContent(const std::string& filename) { @@ -328,31 +266,20 @@ std::optional ReadContent(const std::string& filename) { } } -std::vector ReadLinesWithEnding(std::string filename) { +std::vector ReadFileLines(std::string filename) { std::vector result; - - std::ifstream input(filename); - for (std::string line; SafeGetline(input, line);) + std::ifstream fin(filename); + for (std::string line; std::getline(fin, line);) result.push_back(line); - return result; } -std::vector ToLines(const std::string& content, - bool trim_whitespace) { +std::vector ToLines(const std::string& content) { std::vector result; - std::istringstream lines(content); - std::string line; - while (getline(lines, line)) { - if (trim_whitespace) - TrimInPlace(line); - else - RemoveLastCR(line); + while (getline(lines, line)) result.push_back(line); - } - return result; } @@ -385,27 +312,6 @@ void WriteToFile(const std::string& filename, const std::string& content) { file << content; } -float GetProcessMemoryUsedInMb() { -#if defined(__APPLE__) - return 0.f; -#else - const float kBytesToMb = 1000000; - uint64_t memory_after = spp::GetProcessMemoryUsed(); - return memory_after / kBytesToMb; -#endif -} - -std::string FormatMicroseconds(long long microseconds) { - long long milliseconds = microseconds / 1000; - long long remaining = microseconds - milliseconds; - - // Only show two digits after the dot. - while (remaining >= 100) - remaining /= 10; - - return std::to_string(milliseconds) + "." + std::to_string(remaining) + "ms"; -} - std::string GetDefaultResourceDirectory() { std::string result; diff --git a/src/utils.h b/src/utils.h index 860a8fa2..4eb42ca6 100644 --- a/src/utils.h +++ b/src/utils.h @@ -10,11 +10,6 @@ #include #include -// Trim from start (in place) -void TrimStartInPlace(std::string& s); -// Trim from end (in place) -void TrimEndInPlace(std::string& s); -// Trim from both ends (in place) void TrimInPlace(std::string& s); std::string Trim(std::string s); @@ -41,10 +36,6 @@ std::string GetBaseName(const std::string& path); // Returns |path| without the filetype, ie, "foo/bar.cc" => "foo/bar". std::string StripFileType(const std::string& path); -std::string ReplaceAll(const std::string& source, - const std::string& from, - const std::string& to); - std::vector SplitString(const std::string& str, const std::string& delimiter); @@ -95,9 +86,8 @@ std::string EscapeFileName(std::string path); // FIXME: Move ReadContent into ICacheManager? bool FileExists(const std::string& filename); std::optional ReadContent(const std::string& filename); -std::vector ReadLinesWithEnding(std::string filename); -std::vector ToLines(const std::string& content, - bool trim_whitespace); +std::vector ReadFileLines(std::string filename); +std::vector ToLines(const std::string& content); struct TextReplacer { struct Replacement { @@ -163,11 +153,4 @@ inline void hash_combine(std::size_t& seed, const T& v, Rest... rest) { }; \ } -float GetProcessMemoryUsedInMb(); - -std::string FormatMicroseconds(long long microseconds); - std::string GetDefaultResourceDirectory(); - -// Makes sure all newlines in |output| are in \r\n format. -std::string UpdateToRnNewlines(std::string output); diff --git a/src/working_files.cc b/src/working_files.cc index 52fcd516..478a6ff5 100644 --- a/src/working_files.cc +++ b/src/working_files.cc @@ -215,14 +215,14 @@ WorkingFile::WorkingFile(const std::string& filename, } void WorkingFile::SetIndexContent(const std::string& index_content) { - index_lines = ToLines(index_content, false /*trim_whitespace*/); + index_lines = ToLines(index_content); index_to_buffer.clear(); buffer_to_index.clear(); } void WorkingFile::OnBufferContentUpdated() { - buffer_lines = ToLines(buffer_content, false /*trim_whitespace*/); + buffer_lines = ToLines(buffer_content); index_to_buffer.clear(); buffer_to_index.clear();