diff --git a/CMakeLists.txt b/CMakeLists.txt index b202db78..d48f36b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -216,6 +216,7 @@ target_sources(ccls PRIVATE src/messages/ccls_freshenIndex.cc src/messages/ccls_inheritanceHierarchy.cc src/messages/ccls_memberHierarchy.cc + src/messages/ccls_navigate.cc src/messages/ccls_vars.cc src/messages/exit.cc src/messages/initialize.cc diff --git a/src/messages/ccls_navigate.cc b/src/messages/ccls_navigate.cc new file mode 100644 index 00000000..921ffae1 --- /dev/null +++ b/src/messages/ccls_navigate.cc @@ -0,0 +1,115 @@ +/* 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 "message_handler.h" +#include "pipeline.hh" +#include "query_utils.h" +using namespace ccls; + +namespace { +MethodType kMethodType = "$ccls/navigate"; + +struct In_CclsNavigate : public RequestInMessage { + MethodType GetMethodType() const override { return kMethodType; } + struct Params { + lsTextDocumentIdentifier textDocument; + lsPosition position; + std::string direction; + } params; +}; +MAKE_REFLECT_STRUCT(In_CclsNavigate::Params, textDocument, position, direction); +MAKE_REFLECT_STRUCT(In_CclsNavigate, id, params); +REGISTER_IN_MESSAGE(In_CclsNavigate); + +struct Handler_CclsNavigate : BaseMessageHandler { + MethodType GetMethodType() const override { return kMethodType; } + void Run(In_CclsNavigate *request) override { + auto ¶ms = request->params; + QueryFile *file; + if (!FindFileOrFail(db, project, request->id, + params.textDocument.uri.GetPath(), &file)) + return; + + WorkingFile *wfile = + working_files->GetFileByFilename(file->def->path); + lsPosition ls_pos = request->params.position; + if (wfile && wfile->index_lines.size()) + if (auto line = wfile->GetIndexPosFromBufferPos( + ls_pos.line, &ls_pos.character, false)) + ls_pos.line = *line; + Position pos{(int16_t)ls_pos.line, (int16_t)ls_pos.character}; + + Maybe res; + switch (params.direction[0]) { + case 'D': { + Maybe parent; + for (auto [sym, refcnt] : file->outline2refcnt) + if (refcnt > 0 && sym.range.start <= pos && pos < sym.range.end && + (!parent || parent->start < sym.range.start)) + parent = sym.range; + for (auto [sym, refcnt] : file->outline2refcnt) + if (refcnt > 0 && pos < sym.range.start && + (!parent || sym.range.end <= parent->end) && + (!res || sym.range.start < res->start)) + res = sym.range; + break; + } + case 'L': + for (auto [sym, refcnt] : file->outline2refcnt) + if (refcnt > 0 && sym.range.end <= pos && + (!res || (res->end == sym.range.end ? sym.range.start < res->start + : res->end < sym.range.end))) + res = sym.range; + break; + case 'R': { + Maybe parent; + for (auto [sym, refcnt] : file->outline2refcnt) + if (refcnt > 0 && sym.range.start <= pos && pos < sym.range.end && + (!parent || parent->start < sym.range.start)) + parent = sym.range; + if (parent && parent->start.line == pos.line && pos < parent->end) { + pos = parent->end; + if (pos.column) + pos.column--; + } + for (auto [sym, refcnt] : file->outline2refcnt) + if (refcnt > 0 && pos < sym.range.start && + (!res || + (sym.range.start == res->start ? res->end < sym.range.end + : sym.range.start < res->start))) + res = sym.range; + break; + } + case 'U': + default: + for (auto [sym, refcnt] : file->outline2refcnt) + if (refcnt > 0 && sym.range.start < pos && pos < sym.range.end && + (!res || res->start < sym.range.start)) + res = sym.range; + break; + } + Out_LocationList out; + out.id = request->id; + if (res) + if (auto ls_range = GetLsRange(wfile, *res)) { + lsLocationEx &ls_loc = out.result.emplace_back(); + ls_loc.uri = params.textDocument.uri; + ls_loc.range = *ls_range; + } + pipeline::WriteStdout(kMethodType, out); + } +}; +REGISTER_MESSAGE_HANDLER(Handler_CclsNavigate); +} // namespace diff --git a/src/messages/textDocument_definition.cc b/src/messages/textDocument_definition.cc index 234a0a16..1b616c80 100644 --- a/src/messages/textDocument_definition.cc +++ b/src/messages/textDocument_definition.cc @@ -75,14 +75,17 @@ struct Handler_TextDocumentDefinition Out_TextDocumentDefinition out; out.id = request->id; + Maybe range; Maybe on_def; - bool has_symbol = false; 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; + else if (!(*range == sym.range)) + break; // Found symbol. Return definition. - has_symbol = true; // Special cases which are handled: // - symbol has declaration but no definition (ie, pure virtual) @@ -130,12 +133,12 @@ struct Handler_TextDocumentDefinition lsLocationEx result; result.uri = lsDocumentUri::FromPath(include.resolved_path); out.result.push_back(result); - has_symbol = true; + range = {{0, 0}, {0, 0}}; break; } } // Find the best match of the identifier at point. - if (!has_symbol) { + if (!range) { lsPosition position = request->params.position; const std::string &buffer = wfile->buffer_content; std::string_view query = LexIdentifierAroundPos(position, buffer); diff --git a/src/position.h b/src/position.h index 110676ca..5ff53515 100644 --- a/src/position.h +++ b/src/position.h @@ -40,6 +40,7 @@ struct Position { return line < o.line; return column < o.column; } + bool operator<=(const Position &o) const { return !(o < *this); } }; MAKE_HASHABLE(Position, t.line, t.column);