Use clang::isIdentifierBody and clean up utils/working_files

This commit is contained in:
Fangrui Song 2018-11-17 10:23:12 -08:00
parent d2969b5185
commit c2cf488114
11 changed files with 74 additions and 218 deletions

View File

@ -20,7 +20,7 @@ std::string PathFromFileEntry(const FileEntry &file) {
std::string ret = NormalizePath(Name); std::string ret = NormalizePath(Name);
// Resolve /usr/include/c++/7.3.0 symlink. // Resolve /usr/include/c++/7.3.0 symlink.
if (!llvm::any_of(g_config->workspaceFolders, [&](const std::string &root) { if (!llvm::any_of(g_config->workspaceFolders, [&](const std::string &root) {
return StartsWith(ret, root); return StringRef(ret).startswith(root);
})) { })) {
SmallString<256> dest; SmallString<256> dest;
llvm::sys::fs::real_path(ret, dest); llvm::sys::fs::real_path(ret, dest);

View File

@ -144,7 +144,11 @@ void IncludeComplete::InsertCompletionItem(const std::string &absolute_path,
} }
void IncludeComplete::AddFile(const std::string &path) { void IncludeComplete::AddFile(const std::string &path) {
if (!EndsWithAny(path, g_config->completion.include.suffixWhitelist)) bool ok = false;
for (StringRef suffix : g_config->completion.include.suffixWhitelist)
if (StringRef(path).endswith(suffix))
ok = true;
if (!ok)
return; return;
if (match_ && !match_->IsMatch(path)) if (match_ && !match_->IsMatch(path))
return; return;
@ -171,8 +175,11 @@ void IncludeComplete::InsertIncludesFromDirectory(std::string directory,
GetFilesInFolder( GetFilesInFolder(
directory, true /*recursive*/, false /*add_folder_to_path*/, directory, true /*recursive*/, false /*add_folder_to_path*/,
[&](const std::string &path) { [&](const std::string &path) {
if (!include_cpp && bool ok = include_cpp;
!EndsWithAny(path, g_config->completion.include.suffixWhitelist)) for (StringRef suffix : g_config->completion.include.suffixWhitelist)
if (StringRef(path).endswith(suffix))
ok = true;
if (!ok)
return; return;
if (match_ && !match_->IsMatch(directory + path)) if (match_ && !match_->IsMatch(directory + path))
return; return;

View File

@ -506,8 +506,8 @@ public:
} }
auto i = name.find(short_name); auto i = name.find(short_name);
if (short_name.size()) if (short_name.size())
while (i != std::string::npos && ((i && isalnum(name[i - 1])) || while (i != std::string::npos && ((i && isIdentifierBody(name[i - 1])) ||
isalnum(name[i + short_name.size()]))) isIdentifierBody(name[i + short_name.size()])))
i = name.find(short_name, i + short_name.size()); i = name.find(short_name, i + short_name.size());
if (i == std::string::npos) { if (i == std::string::npos) {
// e.g. operator type-parameter-1 // e.g. operator type-parameter-1
@ -526,7 +526,7 @@ public:
paren++; paren++;
else if (name[i - 1] == '(') else if (name[i - 1] == '(')
paren--; paren--;
else if (!(paren > 0 || isalnum(name[i - 1]) || name[i - 1] == '_' || else if (!(paren > 0 || isIdentifierBody(name[i - 1]) ||
name[i - 1] == ':')) name[i - 1] == ':'))
break; break;
} }

View File

@ -129,7 +129,7 @@ std::string AppendSerializationFormat(const std::string &base) {
std::string GetCachePath(const std::string &source_file) { std::string GetCachePath(const std::string &source_file) {
for (auto &root : g_config->workspaceFolders) for (auto &root : g_config->workspaceFolders)
if (StartsWith(source_file, root)) { if (StringRef(source_file).startswith(root)) {
auto len = root.size(); auto len = root.size();
return g_config->cacheDirectory + return g_config->cacheDirectory +
EscapeFileName(root.substr(0, len - 1)) + '/' + EscapeFileName(root.substr(0, len - 1)) + '/' +

View File

@ -435,7 +435,7 @@ Project::Entry Project::FindEntry(const std::string &path,
best_entry = &entry; best_entry = &entry;
} }
} }
if (StartsWith(path, root)) if (StringRef(path).startswith(root))
result.root = root; result.root = root;
} }
if (result.root.empty()) if (result.root.empty())

View File

@ -201,7 +201,7 @@ void Reflect(Writer &visitor, IndexInclude &value) {
REFLECT_MEMBER(line); REFLECT_MEMBER(line);
if (gTestOutputMode) { if (gTestOutputMode) {
std::string basename = llvm::sys::path::filename(value.resolved_path); std::string basename = llvm::sys::path::filename(value.resolved_path);
if (!StartsWith(value.resolved_path, "&")) if (value.resolved_path[0] != '&')
basename = "&" + basename; basename = "&" + basename;
REFLECT_MEMBER2("resolved_path", basename); REFLECT_MEMBER2("resolved_path", basename);
} else { } else {

View File

@ -13,7 +13,6 @@
#include <llvm/Config/llvm-config.h> #include <llvm/Config/llvm-config.h>
#include <llvm/ADT/StringRef.h> #include <llvm/ADT/StringRef.h>
using namespace llvm;
#include <rapidjson/document.h> #include <rapidjson/document.h>
#include <rapidjson/prettywriter.h> #include <rapidjson/prettywriter.h>
@ -30,6 +29,8 @@ using namespace llvm;
#include <unistd.h> #include <unistd.h>
#endif #endif
using namespace llvm;
extern bool gTestOutputMode; extern bool gTestOutputMode;
namespace ccls { namespace ccls {
@ -78,6 +79,24 @@ void TrimInPlace(std::string &s) {
s.erase(std::find_if(s.rbegin(), s.rend(), f).base(), s.end()); s.erase(std::find_if(s.rbegin(), s.rend(), f).base(), s.end());
} }
std::vector<std::string> SplitString(const std::string &str,
const std::string &delimiter) {
// http://stackoverflow.com/a/13172514
std::vector<std::string> strings;
std::string::size_type pos = 0;
std::string::size_type prev = 0;
while ((pos = str.find(delimiter, prev)) != std::string::npos) {
strings.push_back(str.substr(prev, pos - prev));
prev = pos + 1;
}
// To get the last substring (or only, if delimiter is not found)
strings.push_back(str.substr(prev));
return strings;
}
void ParseTestExpectation( void ParseTestExpectation(
const std::string &filename, const std::string &filename,
const std::vector<std::string> &lines_with_endings, TextReplacer *replacer, const std::vector<std::string> &lines_with_endings, TextReplacer *replacer,
@ -86,10 +105,10 @@ void ParseTestExpectation(
// Scan for EXTRA_FLAGS: // Scan for EXTRA_FLAGS:
{ {
bool in_output = false; bool in_output = false;
for (std::string line : lines_with_endings) { for (StringRef line : lines_with_endings) {
line = StringRef(line).trim().str(); line = line.trim();
if (StartsWith(line, "EXTRA_FLAGS:")) { if (line.startswith("EXTRA_FLAGS:")) {
assert(!in_output && "multiple EXTRA_FLAGS sections"); assert(!in_output && "multiple EXTRA_FLAGS sections");
in_output = true; in_output = true;
continue; continue;
@ -99,7 +118,7 @@ void ParseTestExpectation(
break; break;
if (in_output) if (in_output)
flags->push_back(line); flags->push_back(line.str());
} }
} }
@ -109,11 +128,11 @@ void ParseTestExpectation(
std::string active_output_contents; std::string active_output_contents;
bool in_output = false; bool in_output = false;
for (std::string line_with_ending : lines_with_endings) { for (StringRef line_with_ending : lines_with_endings) {
if (StartsWith(line_with_ending, "*/")) if (line_with_ending.startswith("*/"))
break; break;
if (StartsWith(line_with_ending, "OUTPUT:")) { if (line_with_ending.startswith("OUTPUT:")) {
// Terminate the previous output section if we found a new one. // Terminate the previous output section if we found a new one.
if (in_output) { if (in_output) {
(*output_sections)[active_output_filename] = active_output_contents; (*output_sections)[active_output_filename] = active_output_contents;
@ -121,9 +140,10 @@ void ParseTestExpectation(
// Try to tokenize OUTPUT: based one whitespace. If there is more than // Try to tokenize OUTPUT: based one whitespace. If there is more than
// one token assume it is a filename. // one token assume it is a filename.
std::vector<std::string> tokens = SplitString(line_with_ending, " "); SmallVector<StringRef, 2> tokens;
line_with_ending.split(tokens, ' ');
if (tokens.size() > 1) { if (tokens.size() > 1) {
active_output_filename = StringRef(tokens[1]).trim().str(); active_output_filename = tokens[1].str();
} else { } else {
active_output_filename = filename; active_output_filename = filename;
} }
@ -217,7 +237,7 @@ std::string FindExpectedOutputForFilename(
std::string filename, std::string filename,
const std::unordered_map<std::string, std::string> &expected) { const std::unordered_map<std::string, std::string> &expected) {
for (const auto &entry : expected) { for (const auto &entry : expected) {
if (EndsWith(entry.first, filename)) if (StringRef(entry.first).endswith(filename))
return entry.second; return entry.second;
} }
@ -231,7 +251,7 @@ IndexFile *
FindDbForPathEnding(const std::string &path, FindDbForPathEnding(const std::string &path,
const std::vector<std::unique_ptr<IndexFile>> &dbs) { const std::vector<std::unique_ptr<IndexFile>> &dbs) {
for (auto &db : dbs) { for (auto &db : dbs) {
if (EndsWith(db->path, path)) if (StringRef(db->path).endswith(path))
return db.get(); return db.get();
} }
return nullptr; return nullptr;
@ -264,18 +284,6 @@ bool RunIndexTests(const std::string &filter_path, bool enable_update) {
[&](const std::string &path) { [&](const std::string &path) {
bool is_fail_allowed = false; bool is_fail_allowed = false;
if (EndsWithAny(path, {".m", ".mm"})) {
#ifndef __APPLE__
return;
#endif
// objective-c tests are often not updated right away. do not bring
// down
// CI if they fail.
if (!enable_update)
is_fail_allowed = true;
}
if (path.find(filter_path) == std::string::npos) if (path.find(filter_path) == std::string::npos)
return; return;

View File

@ -22,7 +22,7 @@ using namespace llvm;
#include <unordered_map> #include <unordered_map>
namespace ccls { namespace ccls {
uint64_t HashUsr(std::string_view s) { uint64_t HashUsr(llvm::StringRef s) {
union { union {
uint64_t ret; uint64_t ret;
uint8_t out[8]; uint8_t out[8];
@ -35,51 +35,6 @@ uint64_t HashUsr(std::string_view s) {
return ret; return ret;
} }
uint64_t HashUsr(llvm::StringRef s) {
return HashUsr(std::string_view(s.data(), s.size()));
}
bool EndsWith(std::string_view s, std::string_view suffix) {
return s.size() >= suffix.size() &&
std::equal(suffix.rbegin(), suffix.rend(), s.rbegin());
}
bool StartsWith(std::string_view s, std::string_view prefix) {
return s.size() >= prefix.size() &&
std::equal(prefix.begin(), prefix.end(), s.begin());
}
bool EndsWithAny(std::string_view s, const std::vector<std::string> &ss) {
return std::any_of(ss.begin(), ss.end(),
std::bind(EndsWith, s, std::placeholders::_1));
}
bool FindAnyPartial(const std::string &value,
const std::vector<std::string> &values) {
return std::any_of(std::begin(values), std::end(values),
[&value](const std::string &v) {
return value.find(v) != std::string::npos;
});
}
std::vector<std::string> SplitString(const std::string &str,
const std::string &delimiter) {
// http://stackoverflow.com/a/13172514
std::vector<std::string> strings;
std::string::size_type pos = 0;
std::string::size_type prev = 0;
while ((pos = str.find(delimiter, prev)) != std::string::npos) {
strings.push_back(str.substr(prev, pos - prev));
prev = pos + 1;
}
// To get the last substring (or only, if delimiter is not found)
strings.push_back(str.substr(prev));
return strings;
}
std::string LowerPathIfInsensitive(const std::string &path) { std::string LowerPathIfInsensitive(const std::string &path) {
#if defined(_WIN32) #if defined(_WIN32)
std::string ret = path; std::string ret = path;

View File

@ -15,19 +15,8 @@ class StringRef;
} }
namespace ccls { namespace ccls {
uint64_t HashUsr(std::string_view s);
uint64_t HashUsr(llvm::StringRef s); uint64_t HashUsr(llvm::StringRef s);
// Returns true if |value| starts/ends with |start| or |ending|.
bool StartsWith(std::string_view value, std::string_view start);
bool EndsWith(std::string_view value, std::string_view ending);
bool EndsWithAny(std::string_view s, const std::vector<std::string> &ss);
bool FindAnyPartial(const std::string &value,
const std::vector<std::string> &values);
std::vector<std::string> SplitString(const std::string &str,
const std::string &delimiter);
std::string LowerPathIfInsensitive(const std::string &path); std::string LowerPathIfInsensitive(const std::string &path);
// Ensures that |path| ends in a slash. // Ensures that |path| ends in a slash.

View File

@ -6,11 +6,16 @@
#include "log.hh" #include "log.hh"
#include "position.hh" #include "position.hh"
#include <clang/Basic/CharInfo.h>
#include <algorithm> #include <algorithm>
#include <climits> #include <climits>
#include <numeric> #include <numeric>
#include <sstream> #include <sstream>
using namespace clang;
using namespace llvm;
namespace ccls { namespace ccls {
namespace { namespace {
@ -236,7 +241,7 @@ void WorkingFile::ComputeLineMapping() {
// For index line i, set index_to_buffer[i] to -1 if line i is duplicated. // For index line i, set index_to_buffer[i] to -1 if line i is duplicated.
int i = 0; int i = 0;
for (auto &line : index_lines) { for (StringRef line : index_lines) {
uint64_t h = HashUsr(line); uint64_t h = HashUsr(line);
auto it = hash_to_unique.find(h); auto it = hash_to_unique.find(h);
if (it == hash_to_unique.end()) { if (it == hash_to_unique.end()) {
@ -253,7 +258,7 @@ void WorkingFile::ComputeLineMapping() {
// For buffer line i, set buffer_to_index[i] to -1 if line i is duplicated. // For buffer line i, set buffer_to_index[i] to -1 if line i is duplicated.
i = 0; i = 0;
hash_to_unique.clear(); hash_to_unique.clear();
for (auto &line : buffer_lines) { for (StringRef line : buffer_lines) {
uint64_t h = HashUsr(line); uint64_t h = HashUsr(line);
auto it = hash_to_unique.find(h); auto it = hash_to_unique.find(h);
if (it == hash_to_unique.end()) { if (it == hash_to_unique.end()) {
@ -329,80 +334,22 @@ std::optional<int> WorkingFile::GetIndexPosFromBufferPos(int line, int *column,
index_lines, is_end); index_lines, is_end);
} }
std::string
WorkingFile::FindClosestCallNameInBuffer(Position position,
int *active_parameter,
Position *completion_position) const {
*active_parameter = 0;
int offset = GetOffsetForPosition(position, buffer_content);
// If vscode auto-inserts closing ')' we will begin on ')' token in foo()
// which will make the below algorithm think it's a nested call.
if (offset > 0 && buffer_content[offset] == ')')
--offset;
// Scan back out of call context.
int balance = 0;
while (offset > 0) {
char c = buffer_content[offset];
if (c == ')')
++balance;
else if (c == '(')
--balance;
if (balance == 0 && c == ',')
*active_parameter += 1;
--offset;
if (balance == -1)
break;
}
if (offset < 0)
return "";
// Scan back entire identifier.
int start_offset = offset;
while (offset > 0) {
char c = buffer_content[offset - 1];
if (isalnum(c) == false && c != '_')
break;
--offset;
}
if (completion_position)
*completion_position = GetPositionForOffset(buffer_content, offset);
return buffer_content.substr(offset, start_offset - offset + 1);
}
Position Position
WorkingFile::FindStableCompletionSource(Position position, WorkingFile::FindStableCompletionSource(Position position,
std::string *existing_completion, std::string *existing_completion,
Position *replace_end_pos) const { Position *replace_end_pos) const {
int start_offset = GetOffsetForPosition(position, buffer_content); int start = GetOffsetForPosition(position, buffer_content);
int offset = start_offset; int i = start;
while (i > 0 && isIdentifierBody(buffer_content[i - 1]))
while (offset > 0) { --i;
char c = buffer_content[offset - 1];
if (!isalnum(c) && c != '_')
break;
--offset;
}
*replace_end_pos = position; *replace_end_pos = position;
for (int i = start_offset; i < buffer_content.size(); i++) { for (int i = start;
char c = buffer_content[i]; i < buffer_content.size() && isIdentifierBody(buffer_content[i]); i++)
if (!isalnum(c) && c != '_')
break;
// We know that replace_end_pos and position are on the same line.
replace_end_pos->character++; replace_end_pos->character++;
}
*existing_completion = buffer_content.substr(offset, start_offset - offset); *existing_completion = buffer_content.substr(i, start - i);
return GetPositionForOffset(buffer_content, offset); return GetPositionForOffset(buffer_content, i);
} }
WorkingFile *WorkingFiles::GetFileByFilename(const std::string &filename) { WorkingFile *WorkingFiles::GetFileByFilename(const std::string &filename) {
@ -427,19 +374,6 @@ std::string WorkingFiles::GetContent(const std::string &filename) {
return ""; return "";
} }
void WorkingFiles::DoAction(const std::function<void()> &action) {
std::lock_guard<std::mutex> lock(files_mutex);
action();
}
void WorkingFiles::DoActionOnFile(
const std::string &filename,
const std::function<void(WorkingFile *file)> &action) {
std::lock_guard<std::mutex> lock(files_mutex);
WorkingFile *file = GetFileByFilenameNoLock(filename);
action(file);
}
WorkingFile *WorkingFiles::OnOpen(const TextDocumentItem &open) { WorkingFile *WorkingFiles::OnOpen(const TextDocumentItem &open) {
std::lock_guard<std::mutex> lock(files_mutex); std::lock_guard<std::mutex> lock(files_mutex);
@ -510,19 +444,6 @@ void WorkingFiles::OnClose(const TextDocumentIdentifier &close) {
<< " because it was not open"; << " because it was not open";
} }
WorkingFiles::Snapshot
WorkingFiles::AsSnapshot(const std::vector<std::string> &filter_paths) {
std::lock_guard<std::mutex> lock(files_mutex);
Snapshot result;
result.files.reserve(files.size());
for (const auto &file : files) {
if (filter_paths.empty() || FindAnyPartial(file->filename, filter_paths))
result.files.push_back({file->filename, file->buffer_content});
}
return result;
}
// VSCode (UTF-16) disagrees with Emacs lsp-mode (UTF-8) on how to represent // VSCode (UTF-16) disagrees with Emacs lsp-mode (UTF-8) on how to represent
// text documents. // text documents.
// We use a UTF-8 iterator to approximate UTF-16 in the specification (weird). // We use a UTF-8 iterator to approximate UTF-16 in the specification (weird).
@ -545,24 +466,19 @@ int GetOffsetForPosition(Position pos, std::string_view content) {
std::string_view LexIdentifierAroundPos(Position position, std::string_view LexIdentifierAroundPos(Position position,
std::string_view content) { std::string_view content) {
int start = GetOffsetForPosition(position, content); int start = GetOffsetForPosition(position, content), end = start + 1;
int end = start + 1;
char c; char c;
// We search for :: before the cursor but not after to get the qualifier. // We search for :: before the cursor but not after to get the qualifier.
for (; start > 0; start--) { for (; start > 0; start--) {
c = content[start - 1]; c = content[start - 1];
if (isalnum(c) || c == '_') if (c == ':' && start > 1 && content[start - 2] == ':')
;
else if (c == ':' && start > 1 && content[start - 2] == ':')
start--; start--;
else else if (!isIdentifierBody(c))
break; break;
} }
for (; end < content.size() && isIdentifierBody(content[end]); end++)
for (; end < (int)content.size(); end++) ;
if (c = content[end], !(isalnum(c) || c == '_'))
break;
return content.substr(start, end - start); return content.substr(start, end - start);
} }

View File

@ -50,19 +50,6 @@ struct WorkingFile {
// Also resolves |column| if not NULL. // Also resolves |column| if not NULL.
std::optional<int> GetIndexPosFromBufferPos(int line, int *column, std::optional<int> GetIndexPosFromBufferPos(int line, int *column,
bool is_end); bool is_end);
// TODO: Move FindClosestCallNameInBuffer and FindStableCompletionSource into
// lex_utils.h/cc
// Finds the closest 'callable' name prior to position. This is used for
// signature help to filter code completion results.
//
// |completion_position| will be point to a good code completion location to
// for fetching signatures.
std::string
FindClosestCallNameInBuffer(Position position, int *active_parameter,
Position *completion_position = nullptr) const;
// Returns a relatively stable completion position (it jumps back until there // Returns a relatively stable completion position (it jumps back until there
// is a non-alphanumeric character). // is a non-alphanumeric character).
// //
@ -99,21 +86,15 @@ struct WorkingFiles {
std::string GetContent(const std::string &filename); std::string GetContent(const std::string &filename);
// Run |action| under the lock. // Run |action| under the lock.
void DoAction(const std::function<void()> &action); template <typename Fn> void DoAction(Fn &&fn) {
// Run |action| on the file identified by |filename|. This executes under the std::lock_guard<std::mutex> lock(files_mutex);
// lock. fn();
void DoActionOnFile(const std::string &filename, }
const std::function<void(WorkingFile *file)> &action);
WorkingFile *OnOpen(const TextDocumentItem &open); WorkingFile *OnOpen(const TextDocumentItem &open);
void OnChange(const TextDocumentDidChangeParam &change); void OnChange(const TextDocumentDidChangeParam &change);
void OnClose(const TextDocumentIdentifier &close); void OnClose(const TextDocumentIdentifier &close);
// If |filter_paths| is non-empty, only files which contain any of the given
// strings. For example, {"foo", "bar"} means that every result has either the
// string "foo" or "bar" contained within it.
Snapshot AsSnapshot(const std::vector<std::string> &filter_paths);
// Use unique_ptrs so we can handout WorkingFile ptrs and not have them // Use unique_ptrs so we can handout WorkingFile ptrs and not have them
// invalidated if we resize files. // invalidated if we resize files.
std::vector<std::unique_ptr<WorkingFile>> files; std::vector<std::unique_ptr<WorkingFile>> files;