Use non-inferred entries and build preamble for .h; index on didOpen if no pending requests; documentHighlight

This commit is contained in:
Fangrui Song 2018-09-30 22:54:48 -07:00
parent 79373ba486
commit 84984c6c27
13 changed files with 92 additions and 93 deletions

View File

@ -231,16 +231,13 @@ void CompletionPreloadMain(CompletionManager *manager) {
if (!session)
continue;
// For inferred session, don't build preamble because changes in a.h will
// invalidate it.
if (!session->inferred) {
const auto &args = session->file.args;
WorkingFiles::Snapshot snapshot = session->wfiles->AsSnapshot(
{StripFileType(session->file.filename)});
if (std::unique_ptr<CompilerInvocation> CI =
BuildCompilerInvocation(args, session->FS))
session->BuildPreamble(*CI, request.path);
}
const auto &args = session->file.args;
WorkingFiles::Snapshot snapshot =
session->wfiles->AsSnapshot({StripFileType(session->file.filename)});
if (std::unique_ptr<CompilerInvocation> CI =
BuildCompilerInvocation(args, session->FS))
session->BuildPreamble(*CI, request.path);
int debounce =
is_open ? g_config->diagnostics.onOpen : g_config->diagnostics.onSave;
if (debounce >= 0) {
@ -539,7 +536,7 @@ bool CompletionManager::EnsureCompletionOrCreatePreloadSession(
// No CompletionSession, create new one.
auto session = std::make_shared<ccls::CompletionSession>(
project_->FindCompilationEntryForFile(path), working_files_, PCH);
project_->FindEntry(path, false), working_files_, PCH);
if (session->file.filename != path) {
session->inferred = true;
session->file.filename = path;
@ -568,7 +565,7 @@ CompletionManager::TryGetSession(const std::string &path, bool preload,
session = sessions.TryGet(path);
if (!session && !preload) {
session = std::make_shared<ccls::CompletionSession>(
project_->FindCompilationEntryForFile(path), working_files_, PCH);
project_->FindEntry(path, false), working_files_, PCH);
sessions.Insert(path, session);
LOG_S(INFO) << "create session for " << path;
if (is_open)

View File

@ -29,7 +29,6 @@ limitations under the License.
#include <llvm/ADT/CachedHashString.h>
#include <llvm/ADT/DenseMap.h>
#include <algorithm>
#include <stdint.h>
#include <string_view>
#include <unordered_map>

View File

@ -76,21 +76,11 @@ struct Handler_TextDocumentDefinition
Out_TextDocumentDefinition out;
out.id = request->id;
Maybe<Range> range;
SymbolKind kind;
Maybe<Use> on_def;
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
lsPosition &ls_pos = params.position;
for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, ls_pos)) {
if (!range) {
range = sym.range;
kind = sym.kind;
} else if (!(sym.range == *range && sym.kind == kind)) {
break;
}
// Found symbol. Return definition.
for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, ls_pos, true)) {
// Special cases which are handled:
// - symbol has declaration but no definition (ie, pure virtual)
// - goto declaration while in definition of recursive type
@ -129,6 +119,7 @@ struct Handler_TextDocumentDefinition
out.result.erase(std::unique(out.result.begin(), out.result.end()),
out.result.end());
} else {
Maybe<Range> range;
// Check #include
for (const IndexInclude &include : file->def->includes) {
if (include.line == ls_pos.line) {

View File

@ -125,8 +125,10 @@ struct Handler_TextDocumentDidOpen
if (args.size())
project->SetArgsForFile(args, path);
// Submit new index request if it is not a header file.
if (SourceFileLanguage(path) != LanguageId::Unknown)
// Submit new index request if it is not a header file or there is no
// pending index request.
if (SourceFileLanguage(path) != LanguageId::Unknown ||
!pipeline::pending_index_requests)
pipeline::Index(path, args, IndexMode::Normal);
clang_complete->NotifyView(path);

View File

@ -17,11 +17,28 @@ limitations under the License.
#include "pipeline.hh"
#include "query_utils.h"
#include "symbol.h"
#include <algorithm>
using namespace ccls;
namespace {
MethodType kMethodType = "textDocument/documentHighlight";
struct lsDocumentHighlight {
enum Kind { Text = 1, Read = 2, Write = 3 };
lsRange range;
int kind = 1;
// ccls extension
Role role = Role::None;
bool operator<(const lsDocumentHighlight &o) const {
return !(range == o.range) ? range < o.range : kind < o.kind;
}
};
MAKE_REFLECT_STRUCT(lsDocumentHighlight, range, kind, role);
struct In_TextDocumentDocumentHighlight : public RequestInMessage {
MethodType GetMethodType() const override { return kMethodType; }
lsTextDocumentPositionParams params;
@ -44,39 +61,41 @@ struct Handler_TextDocumentDocumentHighlight
QueryFile *file;
if (!FindFileOrFail(db, project, request->id,
request->params.textDocument.uri.GetPath(), &file,
&file_id)) {
&file_id))
return;
}
WorkingFile *working_file =
working_files->GetFileByFilename(file->def->path);
Out_TextDocumentDocumentHighlight out;
out.id = request->id;
for (SymbolRef sym :
FindSymbolsAtLocation(working_file, file, request->params.position)) {
// Found symbol. Return references to highlight.
EachOccurrence(db, sym, true, [&](Use use) {
if (use.file_id != file_id)
return;
if (std::optional<lsLocation> ls_loc =
GetLsLocation(db, working_files, use)) {
lsDocumentHighlight highlight;
highlight.range = ls_loc->range;
if (use.role & Role::Write)
highlight.kind = lsDocumentHighlightKind::Write;
else if (use.role & Role::Read)
highlight.kind = lsDocumentHighlightKind::Read;
else
highlight.kind = lsDocumentHighlightKind::Text;
highlight.role = use.role;
out.result.push_back(highlight);
}
});
break;
std::vector<SymbolRef> syms = FindSymbolsAtLocation(
working_file, file, request->params.position, true);
for (auto [sym, refcnt] : file->symbol2refcnt) {
if (refcnt <= 0)
continue;
Usr usr = sym.usr;
SymbolKind kind = sym.kind;
if (std::none_of(syms.begin(), syms.end(), [&](auto &sym1) {
return usr == sym1.usr && kind == sym1.kind;
}))
continue;
if (auto ls_loc =
GetLsLocation(db, working_files,
Use{{sym.range, usr, kind, sym.role}, file_id})) {
lsDocumentHighlight highlight;
highlight.range = ls_loc->range;
if (sym.role & Role::Write)
highlight.kind = lsDocumentHighlight::Write;
else if (sym.role & Role::Read)
highlight.kind = lsDocumentHighlight::Read;
else
highlight.kind = lsDocumentHighlight::Text;
highlight.role = sym.role;
out.result.push_back(highlight);
}
}
std::sort(out.result.begin(), out.result.end());
pipeline::WriteStdout(kMethodType, out);
}
};

View File

@ -62,7 +62,8 @@ bool VFS::Stamp(const std::string &path, int64_t ts, int step) {
namespace ccls::pipeline {
std::atomic<int64_t> loaded_ts = ATOMIC_VAR_INIT(0);
std::atomic<int64_t> loaded_ts = ATOMIC_VAR_INIT(0),
pending_index_requests = ATOMIC_VAR_INIT(0);
int64_t tick = 0;
namespace {
@ -175,6 +176,9 @@ bool Indexer_Parse(CompletionManager *completion, WorkingFiles *wfiles,
return false;
auto &request = *opt_request;
bool loud = request.mode != IndexMode::OnChange;
struct RAII {
~RAII() { pending_index_requests--; }
} raii;
// Dummy one to trigger refresh semantic highlight.
if (request.path.empty()) {
@ -189,7 +193,7 @@ bool Indexer_Parse(CompletionManager *completion, WorkingFiles *wfiles,
return false;
}
Project::Entry entry = project->FindCompilationEntryForFile(request.path);
Project::Entry entry = project->FindEntry(request.path, true);
if (request.args.size())
entry.args = request.args;
std::string path_to_index = entry.filename;
@ -547,6 +551,7 @@ void MainLoop() {
void Index(const std::string &path, const std::vector<const char *> &args,
IndexMode mode, lsRequestId id) {
pending_index_requests++;
index_request->PushBack({path, args, mode, id}, mode != IndexMode::NonInteractive);
}

View File

@ -39,7 +39,7 @@ enum class IndexMode {
};
namespace pipeline {
extern std::atomic<int64_t> loaded_ts;
extern std::atomic<int64_t> loaded_ts, pending_index_requests;
extern int64_t tick;
void Init();
void LaunchStdin();

View File

@ -386,13 +386,16 @@ void Project::SetArgsForFile(const std::vector<const char *> &args,
}
}
Project::Entry
Project::FindCompilationEntryForFile(const std::string &filename) {
Project::Entry Project::FindEntry(const std::string &path,
bool can_be_inferred) {
{
std::lock_guard<std::mutex> lock(mutex_);
auto it = path_to_entry_index.find(filename);
if (it != path_to_entry_index.end())
return entries[it->second];
auto it = path_to_entry_index.find(path);
if (it != path_to_entry_index.end()) {
Project::Entry &entry = entries[it->second];
if (can_be_inferred || entry.filename == path)
return entry;
}
}
// We couldn't find the file. Try to infer it.
@ -400,7 +403,7 @@ Project::FindCompilationEntryForFile(const std::string &filename) {
Entry *best_entry = nullptr;
int best_score = INT_MIN;
for (Entry &entry : entries) {
int score = ComputeGuessScore(filename, entry.filename);
int score = ComputeGuessScore(path, entry.filename);
if (score > best_score) {
best_score = score;
best_entry = &entry;
@ -409,10 +412,10 @@ Project::FindCompilationEntryForFile(const std::string &filename) {
Project::Entry result;
result.is_inferred = true;
result.filename = filename;
result.filename = path;
if (!best_entry) {
result.args.push_back("%clang");
result.args.push_back(Intern(filename));
result.args.push_back(Intern(path));
} else {
result.args = best_entry->args;
@ -424,7 +427,7 @@ Project::FindCompilationEntryForFile(const std::string &filename) {
try {
if (arg == best_entry->filename ||
sys::path::filename(arg) == best_entry_base_name)
arg = Intern(filename);
arg = Intern(path);
} catch (...) {
}
}

View File

@ -59,7 +59,7 @@ struct Project {
// Lookup the CompilationEntry for |filename|. If no entry was found this
// will infer one based on existing project structure.
Entry FindCompilationEntryForFile(const std::string &filename);
Entry FindEntry(const std::string &path, bool can_be_inferred);
// If the client has overridden the flags, or specified them for a file
// that is not in the compilation_database.json make sure those changes

View File

@ -308,7 +308,8 @@ std::optional<lsSymbolInformation> GetSymbolInfo(DB *db, SymbolIdx sym,
std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *wfile,
QueryFile *file,
lsPosition &ls_pos) {
lsPosition &ls_pos,
bool smallest) {
std::vector<SymbolRef> symbols;
// If multiVersion > 0, index may not exist and thus index_lines is empty.
if (wfile && wfile->index_lines.size()) {
@ -353,6 +354,14 @@ std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *wfile,
return t > 0;
return a.usr < b.usr;
});
if (symbols.size() && smallest) {
SymbolRef sym = symbols[0];
for (size_t i = 1; i < symbols.size(); i++)
if (!(sym.range == symbols[i].range && sym.kind == symbols[i].kind)) {
symbols.resize(i);
break;
}
}
return symbols;
}

View File

@ -51,7 +51,8 @@ std::optional<lsSymbolInformation> GetSymbolInfo(DB *db, SymbolIdx sym,
std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *working_file,
QueryFile *file,
lsPosition &ls_pos);
lsPosition &ls_pos,
bool smallest = false);
template <typename Fn> void WithEntity(DB *db, SymbolIdx sym, Fn &&fn) {
switch (sym.kind) {

View File

@ -46,32 +46,6 @@ inline Role operator|(Role lhs, Role rhs) {
return Role(uint16_t(lhs) | uint16_t(rhs));
}
// A document highlight kind.
enum class lsDocumentHighlightKind {
// A textual occurrence.
Text = 1,
// Read-access of a symbol, like reading a variable.
Read = 2,
// Write-access of a symbol, like writing to a variable.
Write = 3
};
MAKE_REFLECT_TYPE_PROXY(lsDocumentHighlightKind);
// A document highlight is a range inside a text document which deserves
// special attention. Usually a document highlight is visualized by changing
// the background color of its range.
struct lsDocumentHighlight {
// The range this highlight applies to.
lsRange range;
// The highlight kind, default is DocumentHighlightKind.Text.
lsDocumentHighlightKind kind = lsDocumentHighlightKind::Text;
// ccls extension
Role role = Role::None;
};
MAKE_REFLECT_STRUCT(lsDocumentHighlight, range, kind, role);
struct lsSymbolInformation {
std::string_view name;
lsSymbolKind kind;

View File

@ -18,7 +18,6 @@ limitations under the License.
#include <optional>
#include <string_view>
#include <algorithm>
#include <iterator>
#include <memory>
#include <string>