mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 19:00:11 +01:00
language-server: Support simple semantic queries. (#7639)
Add support for "jump to declaration", "find references", type information on hover. This support is strictly single-file for now; only references and declarations within the same file are found. We could go a bit beyond that, but to properly handle cross-file references we'll need to build an index and a compilation database, which is beyond the scope of this change. On hover, we provide the type information for the instruction under the cursor as-is. This is frequently not very useful, as the type of a function F is simply "<type of F>", but is a starting point for richer information. Assisted-by: Claude Code
This commit is contained in:
@@ -86,6 +86,9 @@ class CompilationUnit {
|
||||
auto parse_tree_and_subtrees() const -> const Parse::TreeAndSubtrees& {
|
||||
return GetParseTreeAndSubtrees();
|
||||
}
|
||||
// Only present once the check phase has run.
|
||||
auto has_sem_ir() const -> bool { return sem_ir_.has_value(); }
|
||||
auto sem_ir() const -> const SemIR::File& { return *sem_ir_; }
|
||||
|
||||
private:
|
||||
// Do codegen. Returns true on success.
|
||||
|
||||
@@ -27,11 +27,29 @@ cc_library(
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "sem_ir_index",
|
||||
srcs = ["sem_ir_index.cpp"],
|
||||
hdrs = ["sem_ir_index.h"],
|
||||
deps = [
|
||||
"//common:check",
|
||||
"//toolchain/lex:token_index",
|
||||
"//toolchain/lex:token_kind",
|
||||
"//toolchain/lex:tokenized_buffer",
|
||||
"//toolchain/parse:node_kind",
|
||||
"//toolchain/parse:tree",
|
||||
"//toolchain/sem_ir:file",
|
||||
"//toolchain/sem_ir:typed_insts",
|
||||
"@llvm-project//llvm:Support",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "context",
|
||||
srcs = ["context.cpp"],
|
||||
hdrs = ["context.h"],
|
||||
deps = [
|
||||
":sem_ir_index",
|
||||
"//common:check",
|
||||
"//common:map",
|
||||
"//common:raw_string_ostream",
|
||||
@@ -55,13 +73,33 @@ cc_library(
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "position",
|
||||
srcs = ["position.cpp"],
|
||||
hdrs = ["position.h"],
|
||||
deps = [
|
||||
":context",
|
||||
":sem_ir_index",
|
||||
"//toolchain/lex:token_index",
|
||||
"//toolchain/lex:tokenized_buffer",
|
||||
"//toolchain/parse:tree",
|
||||
"//toolchain/sem_ir:file",
|
||||
"//toolchain/sem_ir:typed_insts",
|
||||
"@llvm-project//clang-tools-extra/clangd:ClangDaemon",
|
||||
"@llvm-project//llvm:Support",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "handle",
|
||||
srcs = glob(["handle_*"]),
|
||||
hdrs = ["handle.h"],
|
||||
deps = [
|
||||
":context",
|
||||
":position",
|
||||
":sem_ir_index",
|
||||
"//common:check",
|
||||
"//common:raw_string_ostream",
|
||||
"//toolchain/base:shared_value_stores",
|
||||
"//toolchain/lex",
|
||||
"//toolchain/lex:token_index",
|
||||
@@ -69,6 +107,9 @@ cc_library(
|
||||
"//toolchain/parse",
|
||||
"//toolchain/parse:node_kind",
|
||||
"//toolchain/parse:tree",
|
||||
"//toolchain/sem_ir:file",
|
||||
"//toolchain/sem_ir:stringify",
|
||||
"//toolchain/sem_ir:typed_insts",
|
||||
"//toolchain/source:source_buffer",
|
||||
"@llvm-project//clang-tools-extra/clangd:ClangDaemon",
|
||||
"@llvm-project//llvm:Support",
|
||||
|
||||
@@ -205,10 +205,22 @@ Context::Context(const InstallPaths* installation,
|
||||
vfs_ = vfs;
|
||||
}
|
||||
|
||||
auto Context::File::sem_ir_index() const -> const SemIRIndex* {
|
||||
if (!sem_ir_index_) {
|
||||
const auto* sem_ir = this->sem_ir();
|
||||
if (!sem_ir) {
|
||||
return nullptr;
|
||||
}
|
||||
sem_ir_index_.emplace(*sem_ir, tree_and_subtrees());
|
||||
}
|
||||
return &*sem_ir_index_;
|
||||
}
|
||||
|
||||
auto Context::File::SetText(Context& context, std::optional<int64_t> version,
|
||||
llvm::StringRef text) -> void {
|
||||
// Clear state dependent on the source text.
|
||||
compile_driver_.reset();
|
||||
sem_ir_index_.reset();
|
||||
|
||||
text_ = text.str();
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "toolchain/driver/codegen_options.h"
|
||||
#include "toolchain/driver/compile_driver.h"
|
||||
#include "toolchain/driver/compile_options.h"
|
||||
#include "toolchain/language_server/sem_ir_index.h"
|
||||
#include "toolchain/lex/tokenized_buffer.h"
|
||||
#include "toolchain/parse/tree_and_subtrees.h"
|
||||
#include "toolchain/sem_ir/file.h"
|
||||
@@ -39,16 +40,42 @@ class Context {
|
||||
auto SetText(Context& context, std::optional<int64_t> version,
|
||||
llvm::StringRef text) -> void;
|
||||
|
||||
auto uri() const -> const clang::clangd::URIForFile& { return uri_; }
|
||||
auto filename() const -> llvm::StringRef { return filename_; }
|
||||
auto text() const -> llvm::StringRef { return text_; }
|
||||
|
||||
auto tree_and_subtrees() const -> const Parse::TreeAndSubtrees& {
|
||||
CARBON_CHECK(compile_driver_);
|
||||
return compile_driver_->units()[compile_driver_->first_input_index()]
|
||||
->parse_tree_and_subtrees();
|
||||
return unit().parse_tree_and_subtrees();
|
||||
}
|
||||
|
||||
auto tokens() const -> const Lex::TokenizedBuffer& {
|
||||
return unit().tokens();
|
||||
}
|
||||
|
||||
// Returns the checked IR, or null if checking didn't get far enough to
|
||||
// produce one.
|
||||
auto sem_ir() const -> const SemIR::File* {
|
||||
const auto& compilation_unit = unit();
|
||||
return compilation_unit.has_sem_ir() ? &compilation_unit.sem_ir()
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
// Returns an index of this file's instructions by token, building it if
|
||||
// this is the first query since the text last changed. Returns null if
|
||||
// there's no checked IR to index.
|
||||
//
|
||||
// This is deliberately not built by `SetText`: most text changes are
|
||||
// followed by another text change rather than by a query, and the work
|
||||
// would land on the path that produces diagnostics, which is the latency
|
||||
// users actually notice.
|
||||
auto sem_ir_index() const -> const SemIRIndex*;
|
||||
|
||||
private:
|
||||
auto unit() const -> const CompilationUnit& {
|
||||
CARBON_CHECK(compile_driver_);
|
||||
return *compile_driver_->units()[compile_driver_->first_input_index()];
|
||||
}
|
||||
|
||||
// The filename, stable across instances.
|
||||
clang::clangd::URIForFile uri_;
|
||||
std::string filename_;
|
||||
@@ -59,6 +86,9 @@ class Context {
|
||||
CodegenOptions codegen_options_;
|
||||
CompileOptions options_;
|
||||
std::unique_ptr<CompileDriver> compile_driver_;
|
||||
|
||||
// Built on demand by `sem_ir_index()`, and discarded by `SetText`.
|
||||
mutable std::optional<SemIRIndex> sem_ir_index_;
|
||||
};
|
||||
|
||||
// `vlog_stream` is optional; other parameters are required.
|
||||
|
||||
@@ -10,6 +10,13 @@
|
||||
|
||||
namespace Carbon::LanguageServer {
|
||||
|
||||
// Locates the entity named at a position.
|
||||
auto HandleDefinition(
|
||||
Context& context, const clang::clangd::TextDocumentPositionParams& params,
|
||||
llvm::function_ref<
|
||||
auto(llvm::Expected<std::vector<clang::clangd::Location>>)->void>
|
||||
on_done) -> void;
|
||||
|
||||
// Stores the content of newly-opened documents.
|
||||
auto HandleDidChangeTextDocument(
|
||||
Context& context, const clang::clangd::DidChangeTextDocumentParams& params)
|
||||
@@ -37,6 +44,12 @@ auto HandleDocumentSymbol(
|
||||
auto(llvm::Expected<std::vector<clang::clangd::DocumentSymbol>>)->void>
|
||||
on_done) -> void;
|
||||
|
||||
// Provides the type of the entity at a position.
|
||||
auto HandleHover(
|
||||
Context& context, const clang::clangd::TextDocumentPositionParams& params,
|
||||
llvm::function_ref<auto(llvm::Expected<clang::clangd::Hover>)->void>
|
||||
on_done) -> void;
|
||||
|
||||
// Tells the client what features are supported, and negotiates the position
|
||||
// encoding.
|
||||
auto HandleInitialize(
|
||||
@@ -48,6 +61,13 @@ auto HandleInitialize(
|
||||
auto HandleInitialized(Context& context, const clang::clangd::NoParams& params)
|
||||
-> void;
|
||||
|
||||
// Finds references to the entity named at a position, within this file only.
|
||||
auto HandleReferences(
|
||||
Context& context, const clang::clangd::ReferenceParams& params,
|
||||
llvm::function_ref<
|
||||
auto(llvm::Expected<std::vector<clang::clangd::Location>>)->void>
|
||||
on_done) -> void;
|
||||
|
||||
// Prepares LSP for shutdown.
|
||||
auto HandleShutdown(
|
||||
Context& /*context*/,
|
||||
@@ -55,6 +75,13 @@ auto HandleShutdown(
|
||||
llvm::function_ref<auto(llvm::Expected<std::nullptr_t>)->void> on_done)
|
||||
-> void;
|
||||
|
||||
// Locates the type of the entity named at a position.
|
||||
auto HandleTypeDefinition(
|
||||
Context& context, const clang::clangd::TextDocumentPositionParams& params,
|
||||
llvm::function_ref<
|
||||
auto(llvm::Expected<std::vector<clang::clangd::Location>>)->void>
|
||||
on_done) -> void;
|
||||
|
||||
} // namespace Carbon::LanguageServer
|
||||
|
||||
#endif // CARBON_TOOLCHAIN_LANGUAGE_SERVER_HANDLE_H_
|
||||
|
||||
@@ -30,9 +30,14 @@ auto HandleInitialize(
|
||||
auto encoding = NegotiatePositionEncoding(params.capabilities);
|
||||
context.SetPositionEncoding(encoding);
|
||||
|
||||
llvm::json::Object capabilities{{"documentSymbolProvider", true},
|
||||
llvm::json::Object capabilities{{"declarationProvider", true},
|
||||
{"definitionProvider", true},
|
||||
{"documentSymbolProvider", true},
|
||||
{"hoverProvider", true},
|
||||
{"positionEncoding", encoding},
|
||||
{"referencesProvider", true},
|
||||
{"textDocumentSync", /*Incremental=*/2},
|
||||
{"positionEncoding", encoding}};
|
||||
{"typeDefinitionProvider", true}};
|
||||
llvm::json::Object reply{{"capabilities", std::move(capabilities)}};
|
||||
on_done(reply);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/raw_string_ostream.h"
|
||||
#include "toolchain/language_server/handle.h"
|
||||
#include "toolchain/language_server/position.h"
|
||||
#include "toolchain/language_server/sem_ir_index.h"
|
||||
#include "toolchain/sem_ir/file.h"
|
||||
#include "toolchain/sem_ir/stringify.h"
|
||||
#include "toolchain/sem_ir/typed_insts.h"
|
||||
|
||||
namespace Carbon::LanguageServer {
|
||||
|
||||
// Returns the type of `inst_id` rendered as Carbon source, or an empty string
|
||||
// if it has no type. Instructions that aren't values, such as declarations of
|
||||
// namespaces, have no type to show.
|
||||
//
|
||||
// TODO: `StringifyConstantInst` renders some types as placeholders such as
|
||||
// `<type of F>` for a function and `<pattern for i32>` for a binding pattern,
|
||||
// which is unhelpful as hover text. Show the signature for a function, and the
|
||||
// bound type rather than the pattern type for a binding.
|
||||
static auto StringifyTypeOfInst(const SemIR::File& sem_ir,
|
||||
SemIR::InstId inst_id) -> std::string {
|
||||
auto type_id = sem_ir.insts().Get(inst_id).type_id();
|
||||
if (!type_id.has_value()) {
|
||||
return "";
|
||||
}
|
||||
return SemIR::StringifyConstantInst(sem_ir,
|
||||
sem_ir.types().GetTypeInstId(type_id));
|
||||
}
|
||||
|
||||
// Given a position-based query, returns the corresponding position information.
|
||||
// If the request is invalid or there is no instruction at that position,
|
||||
// produces a response to the query: an InvalidParams error or a
|
||||
// default-constructed reply, as appropriate.
|
||||
//
|
||||
// TODO: If a default-constructed response is not the correct way to handle an
|
||||
// invalid location, we will need to extend this function to accept a fallback
|
||||
// value. For now, it's right for all the queries we support.
|
||||
template <typename ResponseType>
|
||||
static auto FindInstAtPositionOrFail(
|
||||
Context& context, const clang::clangd::TextDocumentPositionParams& params,
|
||||
llvm::function_ref<auto(llvm::Expected<ResponseType>)->void> on_done)
|
||||
-> PositionInfo {
|
||||
auto* file = context.LookupFile(params.textDocument.uri.file());
|
||||
if (!file) {
|
||||
on_done(llvm::make_error<clang::clangd::LSPError>(
|
||||
llvm::formatv("Unknown textDocument `{0}`",
|
||||
params.textDocument.uri.file()),
|
||||
clang::clangd::ErrorCode::InvalidParams));
|
||||
return {};
|
||||
}
|
||||
|
||||
auto info = FindPositionInfo(*file, params.position);
|
||||
if (!info.has_inst()) {
|
||||
on_done(ResponseType());
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
// Implements `textDocument/hover`:
|
||||
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_hover
|
||||
auto HandleHover(
|
||||
Context& context, const clang::clangd::TextDocumentPositionParams& params,
|
||||
llvm::function_ref<auto(llvm::Expected<clang::clangd::Hover>)->void>
|
||||
on_done) -> void {
|
||||
auto info = FindInstAtPositionOrFail(context, params, on_done);
|
||||
if (!info.has_inst()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& sem_ir = *info.file->sem_ir();
|
||||
RawStringOstream text;
|
||||
text << "```carbon\n" << info.file->tokens().GetTokenText(info.token);
|
||||
if (auto type = StringifyTypeOfInst(sem_ir, info.inst_id); !type.empty()) {
|
||||
text << ": " << type;
|
||||
}
|
||||
text << "\n```";
|
||||
|
||||
on_done(clang::clangd::Hover{
|
||||
.contents = {.kind = clang::clangd::MarkupKind::Markdown,
|
||||
.value = text.TakeStr()},
|
||||
.range = GetTokenRange(info.file->tokens(), info.token)});
|
||||
}
|
||||
|
||||
// Shared implementation of the goto-style requests, which differ only in which
|
||||
// instruction they resolve to.
|
||||
static auto HandleGoto(
|
||||
Context& context, const clang::clangd::TextDocumentPositionParams& params,
|
||||
bool use_type,
|
||||
llvm::function_ref<
|
||||
auto(llvm::Expected<std::vector<clang::clangd::Location>>)->void>
|
||||
on_done) -> void {
|
||||
auto info = FindInstAtPositionOrFail(context, params, on_done);
|
||||
if (!info.has_inst()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& sem_ir = *info.file->sem_ir();
|
||||
auto target_id = GetReferencedInst(sem_ir, info.inst_id);
|
||||
if (use_type) {
|
||||
auto type_id = sem_ir.insts().Get(target_id).type_id();
|
||||
if (!type_id.has_value()) {
|
||||
on_done(std::vector<clang::clangd::Location>());
|
||||
return;
|
||||
}
|
||||
target_id = sem_ir.types().GetTypeInstId(type_id);
|
||||
}
|
||||
|
||||
std::vector<clang::clangd::Location> locations;
|
||||
if (auto location = GetInstLocation(*info.file, target_id)) {
|
||||
locations.push_back(*location);
|
||||
}
|
||||
on_done(std::move(locations));
|
||||
}
|
||||
|
||||
// Implements `textDocument/definition` and `textDocument/declaration`:
|
||||
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_definition
|
||||
//
|
||||
// Carbon separates declaration from definition, but SemIR resolves a name to a
|
||||
// single entity instruction, so both requests currently answer the same way.
|
||||
// TODO: Point `definition` at the definition when an entity is declared in one
|
||||
// place and defined in another.
|
||||
auto HandleDefinition(
|
||||
Context& context, const clang::clangd::TextDocumentPositionParams& params,
|
||||
llvm::function_ref<
|
||||
auto(llvm::Expected<std::vector<clang::clangd::Location>>)->void>
|
||||
on_done) -> void {
|
||||
HandleGoto(context, params, /*use_type=*/false, on_done);
|
||||
}
|
||||
|
||||
// Implements `textDocument/typeDefinition`:
|
||||
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_typeDefinition
|
||||
auto HandleTypeDefinition(
|
||||
Context& context, const clang::clangd::TextDocumentPositionParams& params,
|
||||
llvm::function_ref<
|
||||
auto(llvm::Expected<std::vector<clang::clangd::Location>>)->void>
|
||||
on_done) -> void {
|
||||
HandleGoto(context, params, /*use_type=*/true, on_done);
|
||||
}
|
||||
|
||||
// Implements `textDocument/references`:
|
||||
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_references
|
||||
//
|
||||
// Only finds references within the file being edited. Without a project-wide
|
||||
// index there's no way to see other files, so results may be incomplete.
|
||||
auto HandleReferences(
|
||||
Context& context, const clang::clangd::ReferenceParams& params,
|
||||
llvm::function_ref<
|
||||
auto(llvm::Expected<std::vector<clang::clangd::Location>>)->void>
|
||||
on_done) -> void {
|
||||
auto info = FindInstAtPositionOrFail(context, params, on_done);
|
||||
if (!info.has_inst()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& sem_ir = *info.file->sem_ir();
|
||||
auto target_token =
|
||||
GetInstNameToken(*info.file, GetReferencedInst(sem_ir, info.inst_id));
|
||||
if (!target_token.has_value()) {
|
||||
on_done(std::vector<clang::clangd::Location>());
|
||||
return;
|
||||
}
|
||||
|
||||
// This is the one request the token index can't serve: it needs every
|
||||
// instruction referring to an entity, which is the opposite direction from
|
||||
// the index. A scan is inherent, and cheap next to the compile that produced
|
||||
// the IR.
|
||||
std::vector<clang::clangd::Location> locations;
|
||||
if (params.context.includeDeclaration) {
|
||||
locations.push_back(
|
||||
{.uri = info.file->uri(),
|
||||
.range = GetTokenRange(info.file->tokens(), target_token)});
|
||||
}
|
||||
for (auto [inst_id, inst] : sem_ir.insts().enumerate()) {
|
||||
auto name_ref = inst.TryAs<SemIR::NameRef>();
|
||||
if (!name_ref ||
|
||||
GetInstNameToken(*info.file, name_ref->value_id) != target_token) {
|
||||
continue;
|
||||
}
|
||||
if (auto location = GetInstLocation(*info.file, inst_id)) {
|
||||
locations.push_back(*location);
|
||||
}
|
||||
}
|
||||
on_done(std::move(locations));
|
||||
}
|
||||
|
||||
} // namespace Carbon::LanguageServer
|
||||
@@ -73,7 +73,12 @@ auto IncomingMessages::AddNotificationHandler(
|
||||
IncomingMessages::IncomingMessages(clang::clangd::Transport* transport,
|
||||
Context* context)
|
||||
: transport_(transport), context_(context) {
|
||||
AddCallHandler("textDocument/declaration", &HandleDefinition);
|
||||
AddCallHandler("textDocument/definition", &HandleDefinition);
|
||||
AddCallHandler("textDocument/documentSymbol", &HandleDocumentSymbol);
|
||||
AddCallHandler("textDocument/hover", &HandleHover);
|
||||
AddCallHandler("textDocument/references", &HandleReferences);
|
||||
AddCallHandler("textDocument/typeDefinition", &HandleTypeDefinition);
|
||||
AddCallHandler("initialize", &HandleInitialize);
|
||||
AddCallHandler("shutdown", &HandleShutdown);
|
||||
AddNotificationHandler("initialized", &HandleInitialized);
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "toolchain/language_server/position.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "toolchain/language_server/sem_ir_index.h"
|
||||
#include "toolchain/parse/tree.h"
|
||||
#include "toolchain/sem_ir/file.h"
|
||||
#include "toolchain/sem_ir/typed_insts.h"
|
||||
|
||||
namespace Carbon::LanguageServer {
|
||||
|
||||
auto GetTokenRange(const Lex::TokenizedBuffer& tokens, Lex::TokenIndex start,
|
||||
Lex::TokenIndex end) -> clang::clangd::Range {
|
||||
auto start_line = tokens.GetLine(start);
|
||||
auto start_col = tokens.GetColumnNumber(start);
|
||||
auto [end_line, end_col] = tokens.GetEndLoc(end);
|
||||
return clang::clangd::Range{
|
||||
.start = {.line = start_line.index, .character = start_col - 1},
|
||||
.end = {.line = end_line.index, .character = end_col - 1},
|
||||
};
|
||||
}
|
||||
|
||||
// Returns the 0-based (line, column) where a token starts, for comparison
|
||||
// against an LSP position.
|
||||
static auto GetTokenStart(const Lex::TokenizedBuffer& tokens,
|
||||
Lex::TokenIndex token) -> std::pair<int, int> {
|
||||
return {tokens.GetLine(token).index, tokens.GetColumnNumber(token) - 1};
|
||||
}
|
||||
|
||||
auto FindToken(const Lex::TokenizedBuffer& tokens,
|
||||
const clang::clangd::Position& position) -> Lex::TokenIndex {
|
||||
std::pair<int, int> target = {position.line, position.character};
|
||||
|
||||
// Tokens are in source order, so find the first one starting after
|
||||
// `position`; only the token before that can contain it. Searching on
|
||||
// (line, column) avoids converting the position to a byte offset, which would
|
||||
// mean rescanning the text for line boundaries.
|
||||
//
|
||||
// `tokens()` excludes the recovery tokens added after lexing finished, which
|
||||
// matters here: those are appended rather than inserted in source order, so
|
||||
// including them would break the ordering this search relies on.
|
||||
auto range = tokens.tokens();
|
||||
auto after = llvm::partition_point(range, [&](Lex::TokenIndex token) {
|
||||
return GetTokenStart(tokens, token) <= target;
|
||||
});
|
||||
if (after == range.begin()) {
|
||||
return Lex::TokenIndex::None;
|
||||
}
|
||||
|
||||
// The position is in that token only if it's also before the token's end;
|
||||
// otherwise it falls in whitespace or a comment.
|
||||
auto token = *std::prev(after);
|
||||
auto [end_line, end_col] = tokens.GetEndLoc(token);
|
||||
if (target < std::pair<int, int>(end_line.index, end_col - 1)) {
|
||||
return token;
|
||||
}
|
||||
return Lex::TokenIndex::None;
|
||||
}
|
||||
|
||||
auto FindPositionInfo(const Context::File& file,
|
||||
const clang::clangd::Position& position) -> PositionInfo {
|
||||
const auto* sem_ir = file.sem_ir();
|
||||
const auto* index = file.sem_ir_index();
|
||||
if (!sem_ir || !index) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto token = FindToken(file.tokens(), position);
|
||||
if (!token.has_value()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
PositionInfo info = {.file = &file, .token = token};
|
||||
auto insts = index->InstsForToken(token);
|
||||
for (auto inst_id : insts) {
|
||||
// Prefer a name reference: a token such as the name in `fn F()` also has
|
||||
// instructions for the declaration itself, but a request at a name is about
|
||||
// the name.
|
||||
if (sem_ir->insts().Is<SemIR::NameRef>(inst_id)) {
|
||||
info.inst_id = inst_id;
|
||||
return info;
|
||||
}
|
||||
}
|
||||
if (!insts.empty()) {
|
||||
info.inst_id = insts.front();
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
auto GetReferencedInst(const SemIR::File& sem_ir, SemIR::InstId inst_id)
|
||||
-> SemIR::InstId {
|
||||
if (auto name_ref = sem_ir.insts().TryGetAs<SemIR::NameRef>(inst_id)) {
|
||||
return name_ref->value_id;
|
||||
}
|
||||
return inst_id;
|
||||
}
|
||||
|
||||
auto GetInstNameToken(const Context::File& file, SemIR::InstId inst_id)
|
||||
-> Lex::TokenIndex {
|
||||
const auto* sem_ir = file.sem_ir();
|
||||
if (!sem_ir || !inst_id.has_value()) {
|
||||
return Lex::TokenIndex::None;
|
||||
}
|
||||
auto loc_id = sem_ir->insts().GetCanonicalLocId(inst_id);
|
||||
if (loc_id.kind() != SemIR::LocId::Kind::NodeId) {
|
||||
// Imported from another file, which we can't yet name a location in.
|
||||
return Lex::TokenIndex::None;
|
||||
}
|
||||
auto node_id = loc_id.node_id();
|
||||
if (!node_id.has_value()) {
|
||||
return Lex::TokenIndex::None;
|
||||
}
|
||||
return GetNameToken(file.tree_and_subtrees(), node_id);
|
||||
}
|
||||
|
||||
auto GetInstLocation(const Context::File& file, SemIR::InstId inst_id)
|
||||
-> std::optional<clang::clangd::Location> {
|
||||
auto token = GetInstNameToken(file, inst_id);
|
||||
if (!token.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return clang::clangd::Location{.uri = file.uri(),
|
||||
.range = GetTokenRange(file.tokens(), token)};
|
||||
}
|
||||
|
||||
} // namespace Carbon::LanguageServer
|
||||
@@ -0,0 +1,75 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#ifndef CARBON_TOOLCHAIN_LANGUAGE_SERVER_POSITION_H_
|
||||
#define CARBON_TOOLCHAIN_LANGUAGE_SERVER_POSITION_H_
|
||||
|
||||
#include "clang-tools-extra/clangd/Protocol.h"
|
||||
#include "toolchain/language_server/context.h"
|
||||
#include "toolchain/lex/token_index.h"
|
||||
#include "toolchain/lex/tokenized_buffer.h"
|
||||
#include "toolchain/sem_ir/ids.h"
|
||||
|
||||
namespace Carbon::LanguageServer {
|
||||
|
||||
// Returns the range covering a closed interval of tokens.
|
||||
auto GetTokenRange(const Lex::TokenizedBuffer& tokens, Lex::TokenIndex start,
|
||||
Lex::TokenIndex end) -> clang::clangd::Range;
|
||||
|
||||
// Returns the range covering a single token.
|
||||
inline auto GetTokenRange(const Lex::TokenizedBuffer& tokens,
|
||||
Lex::TokenIndex token) -> clang::clangd::Range {
|
||||
return GetTokenRange(tokens, token, token);
|
||||
}
|
||||
|
||||
// Returns the token containing `position`, or `None` if the position isn't
|
||||
// within a token. Positions in whitespace and comments produce `None`, which is
|
||||
// why hovering over blank space yields no result rather than the nearest token.
|
||||
auto FindToken(const Lex::TokenizedBuffer& tokens,
|
||||
const clang::clangd::Position& position) -> Lex::TokenIndex;
|
||||
|
||||
// What a request at a source position refers to.
|
||||
struct PositionInfo {
|
||||
// The file that the request points into.
|
||||
const Context::File* file = nullptr;
|
||||
|
||||
// The token at the position, or `None` if there isn't one.
|
||||
Lex::TokenIndex token = Lex::TokenIndex::None;
|
||||
|
||||
// The instruction to answer the request from, or `None` if the position has
|
||||
// no instruction. A position can have several instructions; this is the one
|
||||
// that names something, if any, because that's what these requests are about.
|
||||
SemIR::InstId inst_id = SemIR::InstId::None;
|
||||
|
||||
auto has_inst() const -> bool { return inst_id.has_value(); }
|
||||
};
|
||||
|
||||
// Resolves a position to the token and instruction it refers to. Returns an
|
||||
// empty result if the file has no checked IR, or the position isn't in a token,
|
||||
// or the token produced no instructions.
|
||||
auto FindPositionInfo(const Context::File& file,
|
||||
const clang::clangd::Position& position) -> PositionInfo;
|
||||
|
||||
// Returns the instruction that `inst_id` names, which for a name reference is
|
||||
// the referenced entity and otherwise is `inst_id` itself. This is what
|
||||
// `definition` and `references` are both anchored on: it gives every mention of
|
||||
// an entity, including its declaration, the same identity.
|
||||
auto GetReferencedInst(const SemIR::File& sem_ir, SemIR::InstId inst_id)
|
||||
-> SemIR::InstId;
|
||||
|
||||
// Returns the token naming `inst_id`, or `None` if it isn't located in this
|
||||
// file. Two instructions naming the same token denote the same entity, which is
|
||||
// how a declaration and the references to it are matched up: they don't share
|
||||
// an instruction, but they do share a name.
|
||||
auto GetInstNameToken(const Context::File& file, SemIR::InstId inst_id)
|
||||
-> Lex::TokenIndex;
|
||||
|
||||
// Returns the location of `inst_id` in `file`, or nullopt if it isn't located
|
||||
// in this file. Instructions imported from elsewhere have no location here.
|
||||
auto GetInstLocation(const Context::File& file, SemIR::InstId inst_id)
|
||||
-> std::optional<clang::clangd::Location>;
|
||||
|
||||
} // namespace Carbon::LanguageServer
|
||||
|
||||
#endif // CARBON_TOOLCHAIN_LANGUAGE_SERVER_POSITION_H_
|
||||
@@ -0,0 +1,101 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "toolchain/language_server/sem_ir_index.h"
|
||||
|
||||
#include "common/check.h"
|
||||
#include "toolchain/lex/token_kind.h"
|
||||
#include "toolchain/parse/node_kind.h"
|
||||
#include "toolchain/parse/tree.h"
|
||||
#include "toolchain/sem_ir/ids.h"
|
||||
|
||||
namespace Carbon::LanguageServer {
|
||||
|
||||
auto GetNameToken(const Parse::TreeAndSubtrees& tree_and_subtrees,
|
||||
Parse::NodeId node_id) -> Lex::TokenIndex {
|
||||
const auto& tree = tree_and_subtrees.tree();
|
||||
const auto& tokens = tree.tokens();
|
||||
for (auto child : tree_and_subtrees.children(node_id)) {
|
||||
switch (tree.node_kind(child)) {
|
||||
case Parse::NodeKind::IdentifierNameMaybeBeforeSignature:
|
||||
case Parse::NodeKind::IdentifierNameNotBeforeSignature: {
|
||||
auto token = tree.node_token(child);
|
||||
if (tokens.GetKind(token) == Lex::TokenKind::Identifier) {
|
||||
return token;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return tree.node_token(node_id);
|
||||
}
|
||||
|
||||
// Returns the token that `inst_id` was checked from, or `None` if it has no
|
||||
// location in this file. Instructions imported from another file are located by
|
||||
// an `ImportIRInstId`, and desugared instructions by the instruction they were
|
||||
// desugared from, so only a `NodeId` location refers to this file's tokens.
|
||||
static auto GetTokenForInst(const SemIR::File& sem_ir,
|
||||
const Parse::TreeAndSubtrees& tree_and_subtrees,
|
||||
SemIR::InstId inst_id) -> Lex::TokenIndex {
|
||||
auto loc_id = sem_ir.insts().GetCanonicalLocId(inst_id);
|
||||
if (loc_id.kind() != SemIR::LocId::Kind::NodeId) {
|
||||
return Lex::TokenIndex::None;
|
||||
}
|
||||
auto node_id = loc_id.node_id();
|
||||
if (!node_id.has_value()) {
|
||||
return Lex::TokenIndex::None;
|
||||
}
|
||||
return GetNameToken(tree_and_subtrees, node_id);
|
||||
}
|
||||
|
||||
SemIRIndex::SemIRIndex(const SemIR::File& sem_ir,
|
||||
const Parse::TreeAndSubtrees& tree_and_subtrees) {
|
||||
const auto& tokens = tree_and_subtrees.tree().tokens();
|
||||
// Count the instructions per token, leaving a leading zero so that the counts
|
||||
// can be turned into start offsets in place.
|
||||
token_starts_.assign(tokens.size() + 1, 0);
|
||||
int32_t total = 0;
|
||||
for (auto [inst_id, inst] : sem_ir.insts().enumerate()) {
|
||||
auto token = GetTokenForInst(sem_ir, tree_and_subtrees, inst_id);
|
||||
if (!token.has_value()) {
|
||||
continue;
|
||||
}
|
||||
++token_starts_[token.index + 1];
|
||||
++total;
|
||||
}
|
||||
|
||||
// Turn the counts into start offsets.
|
||||
for (size_t i = 1; i < token_starts_.size(); ++i) {
|
||||
token_starts_[i] += token_starts_[i - 1];
|
||||
}
|
||||
CARBON_CHECK(token_starts_.back() == total);
|
||||
|
||||
// Fill each token's group. `next` tracks the next free slot per token, and
|
||||
// ends up equal to the following token's start, so the offsets stay valid.
|
||||
insts_.resize(total, SemIR::InstId::None);
|
||||
llvm::SmallVector<int32_t> next(token_starts_.begin(), token_starts_.end());
|
||||
for (auto [inst_id, inst] : sem_ir.insts().enumerate()) {
|
||||
auto token = GetTokenForInst(sem_ir, tree_and_subtrees, inst_id);
|
||||
if (!token.has_value()) {
|
||||
continue;
|
||||
}
|
||||
insts_[next[token.index]++] = inst_id;
|
||||
}
|
||||
}
|
||||
|
||||
auto SemIRIndex::InstsForToken(Lex::TokenIndex token) const
|
||||
-> llvm::ArrayRef<SemIR::InstId> {
|
||||
if (!token.has_value()) {
|
||||
return {};
|
||||
}
|
||||
CARBON_CHECK(static_cast<size_t>(token.index) + 1 < token_starts_.size(),
|
||||
"Token {0} is not from the indexed file", token.index);
|
||||
int32_t start = token_starts_[token.index];
|
||||
int32_t end = token_starts_[token.index + 1];
|
||||
return llvm::ArrayRef(insts_).slice(start, end - start);
|
||||
}
|
||||
|
||||
} // namespace Carbon::LanguageServer
|
||||
@@ -0,0 +1,69 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#ifndef CARBON_TOOLCHAIN_LANGUAGE_SERVER_SEM_IR_INDEX_H_
|
||||
#define CARBON_TOOLCHAIN_LANGUAGE_SERVER_SEM_IR_INDEX_H_
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "toolchain/lex/token_index.h"
|
||||
#include "toolchain/lex/tokenized_buffer.h"
|
||||
#include "toolchain/parse/node_ids.h"
|
||||
#include "toolchain/parse/tree_and_subtrees.h"
|
||||
#include "toolchain/sem_ir/file.h"
|
||||
|
||||
namespace Carbon::LanguageServer {
|
||||
|
||||
// Returns the token that identifies `node_id`: the name it declares, if it
|
||||
// declares one, and otherwise the node's own token.
|
||||
//
|
||||
// A declaration's own token is wherever the parser finished it -- the `{` of a
|
||||
// function body, or the `:` of a binding -- which is not what a user points at
|
||||
// to mean that declaration. Requests are answered at the name instead, so both
|
||||
// the index and the locations we report are keyed on it.
|
||||
auto GetNameToken(const Parse::TreeAndSubtrees& tree_and_subtrees,
|
||||
Parse::NodeId node_id) -> Lex::TokenIndex;
|
||||
|
||||
// Maps each token to the instructions that were checked from it.
|
||||
//
|
||||
// Position-based requests such as `hover` and `definition` all start from a
|
||||
// source position, so this is keyed by token rather than by parse node: the
|
||||
// caller has to find the token for a position anyway, and the parse node is
|
||||
// recoverable from an instruction's `LocId`. Instructions imported from other
|
||||
// files are excluded, because their locations refer to another file's parse
|
||||
// tree rather than to a token in this one.
|
||||
//
|
||||
// Build this lazily, on the first query after the file's text changes. Building
|
||||
// costs the same single pass over the instructions that an unindexed scan
|
||||
// would, and most text changes are never followed by a query, so building
|
||||
// eagerly would only add work to the latency-sensitive path that produces
|
||||
// diagnostics.
|
||||
class SemIRIndex {
|
||||
public:
|
||||
explicit SemIRIndex(const SemIR::File& sem_ir,
|
||||
const Parse::TreeAndSubtrees& tree_and_subtrees);
|
||||
|
||||
// Returns the instructions checked from `token`, in `InstId` order. Returns
|
||||
// an empty list for a token that produced no instructions, which is common:
|
||||
// punctuation and keywords usually contribute to an enclosing instruction
|
||||
// rather than producing one of their own.
|
||||
auto InstsForToken(Lex::TokenIndex token) const
|
||||
-> llvm::ArrayRef<SemIR::InstId>;
|
||||
|
||||
private:
|
||||
// Instructions grouped by token, in the compressed-sparse-row layout: the
|
||||
// group for token `i` is `insts_[token_starts_[i] .. token_starts_[i + 1])`.
|
||||
// `token_starts_` therefore has one more entry than there are tokens.
|
||||
//
|
||||
// Token indices are dense, so this is built by counting sort in a single pass
|
||||
// over the instructions, and looked up in constant time. A hash map would
|
||||
// need to handle the many-instructions-per-token case explicitly; here it
|
||||
// falls out of the layout.
|
||||
llvm::SmallVector<SemIR::InstId, 0> insts_;
|
||||
llvm::SmallVector<int32_t, 0> token_starts_;
|
||||
};
|
||||
|
||||
} // namespace Carbon::LanguageServer
|
||||
|
||||
#endif // CARBON_TOOLCHAIN_LANGUAGE_SERVER_SEM_IR_INDEX_H_
|
||||
+7
-2
@@ -15,16 +15,21 @@
|
||||
|
||||
// CHECK:STDERR: error: Input/output error [LanguageServerTransportError]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDOUT: Content-Length: 182{{\r}}
|
||||
// CHECK:STDOUT: Content-Length: 352{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 1,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": {
|
||||
// CHECK:STDOUT: "capabilities": {
|
||||
// CHECK:STDOUT: "declarationProvider": true,
|
||||
// CHECK:STDOUT: "definitionProvider": true,
|
||||
// CHECK:STDOUT: "documentSymbolProvider": true,
|
||||
// CHECK:STDOUT: "hoverProvider": true,
|
||||
// CHECK:STDOUT: "positionEncoding": "utf-16",
|
||||
// CHECK:STDOUT: "textDocumentSync": 2
|
||||
// CHECK:STDOUT: "referencesProvider": true,
|
||||
// CHECK:STDOUT: "textDocumentSync": 2,
|
||||
// CHECK:STDOUT: "typeDefinitionProvider": true
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 51{{\r}}
|
||||
|
||||
+21
-6
@@ -20,40 +20,55 @@
|
||||
|
||||
// --- AUTOUPDATE-SPLIT
|
||||
|
||||
// CHECK:STDOUT: Content-Length: 181{{\r}}
|
||||
// CHECK:STDOUT: Content-Length: 351{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 1,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": {
|
||||
// CHECK:STDOUT: "capabilities": {
|
||||
// CHECK:STDOUT: "declarationProvider": true,
|
||||
// CHECK:STDOUT: "definitionProvider": true,
|
||||
// CHECK:STDOUT: "documentSymbolProvider": true,
|
||||
// CHECK:STDOUT: "hoverProvider": true,
|
||||
// CHECK:STDOUT: "positionEncoding": "utf-8",
|
||||
// CHECK:STDOUT: "textDocumentSync": 2
|
||||
// CHECK:STDOUT: "referencesProvider": true,
|
||||
// CHECK:STDOUT: "textDocumentSync": 2,
|
||||
// CHECK:STDOUT: "typeDefinitionProvider": true
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 182{{\r}}
|
||||
// CHECK:STDOUT: }Content-Length: 352{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 2,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": {
|
||||
// CHECK:STDOUT: "capabilities": {
|
||||
// CHECK:STDOUT: "declarationProvider": true,
|
||||
// CHECK:STDOUT: "definitionProvider": true,
|
||||
// CHECK:STDOUT: "documentSymbolProvider": true,
|
||||
// CHECK:STDOUT: "hoverProvider": true,
|
||||
// CHECK:STDOUT: "positionEncoding": "utf-16",
|
||||
// CHECK:STDOUT: "textDocumentSync": 2
|
||||
// CHECK:STDOUT: "referencesProvider": true,
|
||||
// CHECK:STDOUT: "textDocumentSync": 2,
|
||||
// CHECK:STDOUT: "typeDefinitionProvider": true
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 182{{\r}}
|
||||
// CHECK:STDOUT: }Content-Length: 352{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 3,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": {
|
||||
// CHECK:STDOUT: "capabilities": {
|
||||
// CHECK:STDOUT: "declarationProvider": true,
|
||||
// CHECK:STDOUT: "definitionProvider": true,
|
||||
// CHECK:STDOUT: "documentSymbolProvider": true,
|
||||
// CHECK:STDOUT: "hoverProvider": true,
|
||||
// CHECK:STDOUT: "positionEncoding": "utf-16",
|
||||
// CHECK:STDOUT: "textDocumentSync": 2
|
||||
// CHECK:STDOUT: "referencesProvider": true,
|
||||
// CHECK:STDOUT: "textDocumentSync": 2,
|
||||
// CHECK:STDOUT: "typeDefinitionProvider": true
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 51{{\r}}
|
||||
|
||||
@@ -19,16 +19,21 @@
|
||||
|
||||
// CHECK:STDERR: warning: -32602: in call to `textDocument/didOpen`, JSON parse failed: missing value at (root).textDocument [LanguageServerNotificationParseError]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDOUT: Content-Length: 182{{\r}}
|
||||
// CHECK:STDOUT: Content-Length: 352{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 1,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": {
|
||||
// CHECK:STDOUT: "capabilities": {
|
||||
// CHECK:STDOUT: "declarationProvider": true,
|
||||
// CHECK:STDOUT: "definitionProvider": true,
|
||||
// CHECK:STDOUT: "documentSymbolProvider": true,
|
||||
// CHECK:STDOUT: "hoverProvider": true,
|
||||
// CHECK:STDOUT: "positionEncoding": "utf-16",
|
||||
// CHECK:STDOUT: "textDocumentSync": 2
|
||||
// CHECK:STDOUT: "referencesProvider": true,
|
||||
// CHECK:STDOUT: "textDocumentSync": 2,
|
||||
// CHECK:STDOUT: "typeDefinitionProvider": true
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 51{{\r}}
|
||||
|
||||
@@ -36,16 +36,21 @@
|
||||
|
||||
// --- AUTOUPDATE-SPLIT
|
||||
|
||||
// CHECK:STDOUT: Content-Length: 182{{\r}}
|
||||
// CHECK:STDOUT: Content-Length: 352{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 1,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": {
|
||||
// CHECK:STDOUT: "capabilities": {
|
||||
// CHECK:STDOUT: "declarationProvider": true,
|
||||
// CHECK:STDOUT: "definitionProvider": true,
|
||||
// CHECK:STDOUT: "documentSymbolProvider": true,
|
||||
// CHECK:STDOUT: "hoverProvider": true,
|
||||
// CHECK:STDOUT: "positionEncoding": "utf-16",
|
||||
// CHECK:STDOUT: "textDocumentSync": 2
|
||||
// CHECK:STDOUT: "referencesProvider": true,
|
||||
// CHECK:STDOUT: "textDocumentSync": 2,
|
||||
// CHECK:STDOUT: "typeDefinitionProvider": true
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 145{{\r}}
|
||||
|
||||
@@ -30,16 +30,21 @@ choice Colors {
|
||||
[[@LSP-CALL:shutdown]]
|
||||
[[@LSP-NOTIFY:exit]]
|
||||
|
||||
// CHECK:STDOUT: Content-Length: 182{{\r}}
|
||||
// CHECK:STDOUT: Content-Length: 352{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 1,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": {
|
||||
// CHECK:STDOUT: "capabilities": {
|
||||
// CHECK:STDOUT: "declarationProvider": true,
|
||||
// CHECK:STDOUT: "definitionProvider": true,
|
||||
// CHECK:STDOUT: "documentSymbolProvider": true,
|
||||
// CHECK:STDOUT: "hoverProvider": true,
|
||||
// CHECK:STDOUT: "positionEncoding": "utf-16",
|
||||
// CHECK:STDOUT: "textDocumentSync": 2
|
||||
// CHECK:STDOUT: "referencesProvider": true,
|
||||
// CHECK:STDOUT: "textDocumentSync": 2,
|
||||
// CHECK:STDOUT: "typeDefinitionProvider": true
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 504{{\r}}
|
||||
|
||||
+7
-2
@@ -33,16 +33,21 @@ fn G() {}
|
||||
[[@LSP-CALL:shutdown]]
|
||||
[[@LSP-NOTIFY:exit]]
|
||||
|
||||
// CHECK:STDOUT: Content-Length: 182{{\r}}
|
||||
// CHECK:STDOUT: Content-Length: 352{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 1,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": {
|
||||
// CHECK:STDOUT: "capabilities": {
|
||||
// CHECK:STDOUT: "declarationProvider": true,
|
||||
// CHECK:STDOUT: "definitionProvider": true,
|
||||
// CHECK:STDOUT: "documentSymbolProvider": true,
|
||||
// CHECK:STDOUT: "hoverProvider": true,
|
||||
// CHECK:STDOUT: "positionEncoding": "utf-16",
|
||||
// CHECK:STDOUT: "textDocumentSync": 2
|
||||
// CHECK:STDOUT: "referencesProvider": true,
|
||||
// CHECK:STDOUT: "textDocumentSync": 2,
|
||||
// CHECK:STDOUT: "typeDefinitionProvider": true
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 877{{\r}}
|
||||
|
||||
@@ -29,16 +29,21 @@ fn Builtin() = "int.make_type_32";
|
||||
[[@LSP-CALL:shutdown]]
|
||||
[[@LSP-NOTIFY:exit]]
|
||||
|
||||
// CHECK:STDOUT: Content-Length: 182{{\r}}
|
||||
// CHECK:STDOUT: Content-Length: 352{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 1,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": {
|
||||
// CHECK:STDOUT: "capabilities": {
|
||||
// CHECK:STDOUT: "declarationProvider": true,
|
||||
// CHECK:STDOUT: "definitionProvider": true,
|
||||
// CHECK:STDOUT: "documentSymbolProvider": true,
|
||||
// CHECK:STDOUT: "hoverProvider": true,
|
||||
// CHECK:STDOUT: "positionEncoding": "utf-16",
|
||||
// CHECK:STDOUT: "textDocumentSync": 2
|
||||
// CHECK:STDOUT: "referencesProvider": true,
|
||||
// CHECK:STDOUT: "textDocumentSync": 2,
|
||||
// CHECK:STDOUT: "typeDefinitionProvider": true
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 482{{\r}}
|
||||
|
||||
@@ -27,16 +27,21 @@ class Incomplete {
|
||||
[[@LSP-CALL:shutdown]]
|
||||
[[@LSP-NOTIFY:exit]]
|
||||
|
||||
// CHECK:STDOUT: Content-Length: 182{{\r}}
|
||||
// CHECK:STDOUT: Content-Length: 352{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 1,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": {
|
||||
// CHECK:STDOUT: "capabilities": {
|
||||
// CHECK:STDOUT: "declarationProvider": true,
|
||||
// CHECK:STDOUT: "definitionProvider": true,
|
||||
// CHECK:STDOUT: "documentSymbolProvider": true,
|
||||
// CHECK:STDOUT: "hoverProvider": true,
|
||||
// CHECK:STDOUT: "positionEncoding": "utf-16",
|
||||
// CHECK:STDOUT: "textDocumentSync": 2
|
||||
// CHECK:STDOUT: "referencesProvider": true,
|
||||
// CHECK:STDOUT: "textDocumentSync": 2,
|
||||
// CHECK:STDOUT: "typeDefinitionProvider": true
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 1552{{\r}}
|
||||
|
||||
@@ -20,16 +20,21 @@
|
||||
|
||||
// CHECK:STDERR: /test.cpp: warning: non-Carbon file requested [LanguageServerFileUnsupported]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDOUT: Content-Length: 182{{\r}}
|
||||
// CHECK:STDOUT: Content-Length: 352{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 1,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": {
|
||||
// CHECK:STDOUT: "capabilities": {
|
||||
// CHECK:STDOUT: "declarationProvider": true,
|
||||
// CHECK:STDOUT: "definitionProvider": true,
|
||||
// CHECK:STDOUT: "documentSymbolProvider": true,
|
||||
// CHECK:STDOUT: "hoverProvider": true,
|
||||
// CHECK:STDOUT: "positionEncoding": "utf-16",
|
||||
// CHECK:STDOUT: "textDocumentSync": 2
|
||||
// CHECK:STDOUT: "referencesProvider": true,
|
||||
// CHECK:STDOUT: "textDocumentSync": 2,
|
||||
// CHECK:STDOUT: "typeDefinitionProvider": true
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 51{{\r}}
|
||||
|
||||
@@ -28,16 +28,21 @@ fn Bar() {}
|
||||
[[@LSP-CALL:shutdown]]
|
||||
[[@LSP-NOTIFY:exit]]
|
||||
|
||||
// CHECK:STDOUT: Content-Length: 182{{\r}}
|
||||
// CHECK:STDOUT: Content-Length: 352{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 1,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": {
|
||||
// CHECK:STDOUT: "capabilities": {
|
||||
// CHECK:STDOUT: "declarationProvider": true,
|
||||
// CHECK:STDOUT: "definitionProvider": true,
|
||||
// CHECK:STDOUT: "documentSymbolProvider": true,
|
||||
// CHECK:STDOUT: "hoverProvider": true,
|
||||
// CHECK:STDOUT: "positionEncoding": "utf-16",
|
||||
// CHECK:STDOUT: "textDocumentSync": 2
|
||||
// CHECK:STDOUT: "referencesProvider": true,
|
||||
// CHECK:STDOUT: "textDocumentSync": 2,
|
||||
// CHECK:STDOUT: "typeDefinitionProvider": true
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 149{{\r}}
|
||||
|
||||
@@ -20,16 +20,21 @@
|
||||
|
||||
// CHECK:STDERR: /test.carbon: warning: unknown file requested [LanguageServerFileUnknown]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDOUT: Content-Length: 182{{\r}}
|
||||
// CHECK:STDOUT: Content-Length: 352{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 1,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": {
|
||||
// CHECK:STDOUT: "capabilities": {
|
||||
// CHECK:STDOUT: "declarationProvider": true,
|
||||
// CHECK:STDOUT: "definitionProvider": true,
|
||||
// CHECK:STDOUT: "documentSymbolProvider": true,
|
||||
// CHECK:STDOUT: "hoverProvider": true,
|
||||
// CHECK:STDOUT: "positionEncoding": "utf-16",
|
||||
// CHECK:STDOUT: "textDocumentSync": 2
|
||||
// CHECK:STDOUT: "referencesProvider": true,
|
||||
// CHECK:STDOUT: "textDocumentSync": 2,
|
||||
// CHECK:STDOUT: "typeDefinitionProvider": true
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 51{{\r}}
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/language_server/testdata/position/bad_params.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/language_server/testdata/position/bad_params.carbon
|
||||
// CHECK:STDERR: /other_file.carbon: warning: unknown file requested [LanguageServerFileUnknown]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: /other_file.carbon: warning: unknown file requested [LanguageServerFileUnknown]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: /other_file.carbon: warning: unknown file requested [LanguageServerFileUnknown]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: /other_file.carbon: warning: unknown file requested [LanguageServerFileUnknown]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: /other_file.carbon: warning: unknown file requested [LanguageServerFileUnknown]
|
||||
// CHECK:STDERR:
|
||||
|
||||
// --- STDIN
|
||||
[[@LSP-CALL:initialize:"capabilities": {}]]
|
||||
[[@LSP-NOTIFY:textDocument/didOpen:
|
||||
"textDocument": {
|
||||
"uri": "file:/some_file.carbon",
|
||||
"languageId": "carbon",
|
||||
"text": ""
|
||||
}
|
||||
]]
|
||||
[[@LSP-CALL:textDocument/hover:
|
||||
"textDocument": {"uri": "file:/other_file.carbon"},
|
||||
"position": {"line": 4, "character": 2}
|
||||
]]
|
||||
[[@LSP-CALL:textDocument/definition:
|
||||
"textDocument": {"uri": "file:/other_file.carbon"},
|
||||
"position": {"line": 4, "character": 2}
|
||||
]]
|
||||
[[@LSP-CALL:textDocument/definition:
|
||||
"textDocument": {"uri": "file:/other_file.carbon"},
|
||||
"position": {"line": 4, "character": 6}
|
||||
]]
|
||||
[[@LSP-CALL:textDocument/references:
|
||||
"textDocument": {"uri": "file:/other_file.carbon"},
|
||||
"position": {"line": 3, "character": 6},
|
||||
"context": {"includeDeclaration": true}
|
||||
]]
|
||||
[[@LSP-CALL:textDocument/typeDefinition:
|
||||
"textDocument": {"uri": "file:/other_file.carbon"},
|
||||
"position": {"line": 4, "character": 6}
|
||||
]]
|
||||
[[@LSP-CALL:shutdown]]
|
||||
[[@LSP-NOTIFY:exit]]
|
||||
|
||||
// CHECK:STDOUT: Content-Length: 352{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 1,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": {
|
||||
// CHECK:STDOUT: "capabilities": {
|
||||
// CHECK:STDOUT: "declarationProvider": true,
|
||||
// CHECK:STDOUT: "definitionProvider": true,
|
||||
// CHECK:STDOUT: "documentSymbolProvider": true,
|
||||
// CHECK:STDOUT: "hoverProvider": true,
|
||||
// CHECK:STDOUT: "positionEncoding": "utf-16",
|
||||
// CHECK:STDOUT: "referencesProvider": true,
|
||||
// CHECK:STDOUT: "textDocumentSync": 2,
|
||||
// CHECK:STDOUT: "typeDefinitionProvider": true
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 149{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "method": "textDocument/publishDiagnostics",
|
||||
// CHECK:STDOUT: "params": {
|
||||
// CHECK:STDOUT: "diagnostics": [],
|
||||
// CHECK:STDOUT: "uri": "file:///some_file.carbon"
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 130{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "error": {
|
||||
// CHECK:STDOUT: "code": -32602,
|
||||
// CHECK:STDOUT: "message": "Unknown textDocument `/other_file.carbon`"
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: "id": 2,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0"
|
||||
// CHECK:STDOUT: }Content-Length: 130{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "error": {
|
||||
// CHECK:STDOUT: "code": -32602,
|
||||
// CHECK:STDOUT: "message": "Unknown textDocument `/other_file.carbon`"
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: "id": 3,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0"
|
||||
// CHECK:STDOUT: }Content-Length: 130{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "error": {
|
||||
// CHECK:STDOUT: "code": -32602,
|
||||
// CHECK:STDOUT: "message": "Unknown textDocument `/other_file.carbon`"
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: "id": 4,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0"
|
||||
// CHECK:STDOUT: }Content-Length: 130{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "error": {
|
||||
// CHECK:STDOUT: "code": -32602,
|
||||
// CHECK:STDOUT: "message": "Unknown textDocument `/other_file.carbon`"
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: "id": 5,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0"
|
||||
// CHECK:STDOUT: }Content-Length: 130{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "error": {
|
||||
// CHECK:STDOUT: "code": -32602,
|
||||
// CHECK:STDOUT: "message": "Unknown textDocument `/other_file.carbon`"
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: "id": 6,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0"
|
||||
// CHECK:STDOUT: }Content-Length: 51{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 7,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": null
|
||||
// CHECK:STDOUT: }
|
||||
@@ -0,0 +1,263 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/language_server/testdata/position/hover_and_goto.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/language_server/testdata/position/hover_and_goto.carbon
|
||||
|
||||
// Requests are answered at the name a user points at, not at the token the
|
||||
// parser happened to finish a declaration on, so `definition` from the call on
|
||||
// line 4 lands on `Abs` on line 0 rather than on the `{` of its body.
|
||||
// `references` matches on that same name, which is why asking at the
|
||||
// declaration of `x` still finds its use.
|
||||
|
||||
// --- position.carbon
|
||||
fn Abs(n: i32) -> i32 { return n; }
|
||||
|
||||
fn Run() {
|
||||
var x: i32 = 1;
|
||||
Abs(x);
|
||||
}
|
||||
|
||||
// --- STDIN
|
||||
[[@LSP-CALL:initialize:"capabilities": {}]]
|
||||
[[@LSP-NOTIFY:textDocument/didOpen:
|
||||
"textDocument": {
|
||||
"uri": "file:/position.carbon",
|
||||
"languageId": "carbon",
|
||||
"text": "FROM_FILE_SPLIT"
|
||||
}
|
||||
]]
|
||||
[[@LSP-CALL:textDocument/hover:
|
||||
"textDocument": {"uri": "file:/position.carbon"},
|
||||
"position": {"line": 4, "character": 2}
|
||||
]]
|
||||
[[@LSP-CALL:textDocument/definition:
|
||||
"textDocument": {"uri": "file:/position.carbon"},
|
||||
"position": {"line": 4, "character": 2}
|
||||
]]
|
||||
[[@LSP-CALL:textDocument/definition:
|
||||
"textDocument": {"uri": "file:/position.carbon"},
|
||||
"position": {"line": 4, "character": 6}
|
||||
]]
|
||||
[[@LSP-CALL:textDocument/references:
|
||||
"textDocument": {"uri": "file:/position.carbon"},
|
||||
"position": {"line": 3, "character": 6},
|
||||
"context": {"includeDeclaration": true}
|
||||
]]
|
||||
[[@LSP-CALL:textDocument/typeDefinition:
|
||||
"textDocument": {"uri": "file:/position.carbon"},
|
||||
"position": {"line": 4, "character": 6}
|
||||
]]
|
||||
[[@LSP-CALL:textDocument/hover:
|
||||
"textDocument": {"uri": "file:/position.carbon"},
|
||||
"position": {"line": 2, "character": 0}
|
||||
]]
|
||||
[[@LSP-CALL:shutdown]]
|
||||
[[@LSP-NOTIFY:exit]]
|
||||
|
||||
// CHECK:STDOUT: Content-Length: 352{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 1,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": {
|
||||
// CHECK:STDOUT: "capabilities": {
|
||||
// CHECK:STDOUT: "declarationProvider": true,
|
||||
// CHECK:STDOUT: "definitionProvider": true,
|
||||
// CHECK:STDOUT: "documentSymbolProvider": true,
|
||||
// CHECK:STDOUT: "hoverProvider": true,
|
||||
// CHECK:STDOUT: "positionEncoding": "utf-16",
|
||||
// CHECK:STDOUT: "referencesProvider": true,
|
||||
// CHECK:STDOUT: "textDocumentSync": 2,
|
||||
// CHECK:STDOUT: "typeDefinitionProvider": true
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 1570{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "method": "textDocument/publishDiagnostics",
|
||||
// CHECK:STDOUT: "params": {
|
||||
// CHECK:STDOUT: "diagnostics": [
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "message": "`Core.Int` implicitly referenced here, but package `Core` not found",
|
||||
// CHECK:STDOUT: "range": {
|
||||
// CHECK:STDOUT: "end": {
|
||||
// CHECK:STDOUT: "character": 13,
|
||||
// CHECK:STDOUT: "line": 0
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: "start": {
|
||||
// CHECK:STDOUT: "character": 10,
|
||||
// CHECK:STDOUT: "line": 0
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: "severity": 1,
|
||||
// CHECK:STDOUT: "source": "carbon"
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "message": "`Core.Int` implicitly referenced here, but package `Core` not found",
|
||||
// CHECK:STDOUT: "range": {
|
||||
// CHECK:STDOUT: "end": {
|
||||
// CHECK:STDOUT: "character": 21,
|
||||
// CHECK:STDOUT: "line": 0
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: "start": {
|
||||
// CHECK:STDOUT: "character": 18,
|
||||
// CHECK:STDOUT: "line": 0
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: "severity": 1,
|
||||
// CHECK:STDOUT: "source": "carbon"
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "message": "`Core.Int` implicitly referenced here, but package `Core` not found",
|
||||
// CHECK:STDOUT: "range": {
|
||||
// CHECK:STDOUT: "end": {
|
||||
// CHECK:STDOUT: "character": 12,
|
||||
// CHECK:STDOUT: "line": 3
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: "start": {
|
||||
// CHECK:STDOUT: "character": 9,
|
||||
// CHECK:STDOUT: "line": 3
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: "severity": 1,
|
||||
// CHECK:STDOUT: "source": "carbon"
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "message": "`Core.Destroy` implicitly referenced here, but package `Core` not found",
|
||||
// CHECK:STDOUT: "range": {
|
||||
// CHECK:STDOUT: "end": {
|
||||
// CHECK:STDOUT: "character": 12,
|
||||
// CHECK:STDOUT: "line": 3
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: "start": {
|
||||
// CHECK:STDOUT: "character": 2,
|
||||
// CHECK:STDOUT: "line": 3
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: "severity": 1,
|
||||
// CHECK:STDOUT: "source": "carbon"
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: ],
|
||||
// CHECK:STDOUT: "uri": "file:///position.carbon"
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 309{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 2,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": {
|
||||
// CHECK:STDOUT: "contents": {
|
||||
// CHECK:STDOUT: "kind": "markdown",
|
||||
// CHECK:STDOUT: "value": "```carbon\nAbs: <type of Abs>\n```"
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: "range": {
|
||||
// CHECK:STDOUT: "end": {
|
||||
// CHECK:STDOUT: "character": 5,
|
||||
// CHECK:STDOUT: "line": 4
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: "start": {
|
||||
// CHECK:STDOUT: "character": 2,
|
||||
// CHECK:STDOUT: "line": 4
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 278{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 3,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": [
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "range": {
|
||||
// CHECK:STDOUT: "end": {
|
||||
// CHECK:STDOUT: "character": 6,
|
||||
// CHECK:STDOUT: "line": 0
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: "start": {
|
||||
// CHECK:STDOUT: "character": 3,
|
||||
// CHECK:STDOUT: "line": 0
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: "uri": "file:///position.carbon"
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: ]
|
||||
// CHECK:STDOUT: }Content-Length: 278{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 4,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": [
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "range": {
|
||||
// CHECK:STDOUT: "end": {
|
||||
// CHECK:STDOUT: "character": 7,
|
||||
// CHECK:STDOUT: "line": 3
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: "start": {
|
||||
// CHECK:STDOUT: "character": 6,
|
||||
// CHECK:STDOUT: "line": 3
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: "uri": "file:///position.carbon"
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: ]
|
||||
// CHECK:STDOUT: }Content-Length: 505{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 5,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": [
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "range": {
|
||||
// CHECK:STDOUT: "end": {
|
||||
// CHECK:STDOUT: "character": 7,
|
||||
// CHECK:STDOUT: "line": 3
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: "start": {
|
||||
// CHECK:STDOUT: "character": 6,
|
||||
// CHECK:STDOUT: "line": 3
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: "uri": "file:///position.carbon"
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "range": {
|
||||
// CHECK:STDOUT: "end": {
|
||||
// CHECK:STDOUT: "character": 7,
|
||||
// CHECK:STDOUT: "line": 4
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: "start": {
|
||||
// CHECK:STDOUT: "character": 6,
|
||||
// CHECK:STDOUT: "line": 4
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: "uri": "file:///position.carbon"
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: ]
|
||||
// CHECK:STDOUT: }Content-Length: 49{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 6,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": []
|
||||
// CHECK:STDOUT: }Content-Length: 73{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 7,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": {
|
||||
// CHECK:STDOUT: "contents": null
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 51{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 8,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": null
|
||||
// CHECK:STDOUT: }
|
||||
@@ -21,16 +21,21 @@
|
||||
|
||||
// CHECK:STDERR: /test.carbon: warning: unknown file requested [LanguageServerFileUnknown]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDOUT: Content-Length: 182{{\r}}
|
||||
// CHECK:STDOUT: Content-Length: 352{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 1,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": {
|
||||
// CHECK:STDOUT: "capabilities": {
|
||||
// CHECK:STDOUT: "declarationProvider": true,
|
||||
// CHECK:STDOUT: "definitionProvider": true,
|
||||
// CHECK:STDOUT: "documentSymbolProvider": true,
|
||||
// CHECK:STDOUT: "hoverProvider": true,
|
||||
// CHECK:STDOUT: "positionEncoding": "utf-16",
|
||||
// CHECK:STDOUT: "textDocumentSync": 2
|
||||
// CHECK:STDOUT: "referencesProvider": true,
|
||||
// CHECK:STDOUT: "textDocumentSync": 2,
|
||||
// CHECK:STDOUT: "typeDefinitionProvider": true
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 51{{\r}}
|
||||
|
||||
@@ -20,16 +20,21 @@
|
||||
|
||||
// CHECK:STDERR: /test.carbon: warning: tried closing unknown file; ignoring request [LanguageServerCloseUnknownFile]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDOUT: Content-Length: 182{{\r}}
|
||||
// CHECK:STDOUT: Content-Length: 352{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 1,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": {
|
||||
// CHECK:STDOUT: "capabilities": {
|
||||
// CHECK:STDOUT: "declarationProvider": true,
|
||||
// CHECK:STDOUT: "definitionProvider": true,
|
||||
// CHECK:STDOUT: "documentSymbolProvider": true,
|
||||
// CHECK:STDOUT: "hoverProvider": true,
|
||||
// CHECK:STDOUT: "positionEncoding": "utf-16",
|
||||
// CHECK:STDOUT: "textDocumentSync": 2
|
||||
// CHECK:STDOUT: "referencesProvider": true,
|
||||
// CHECK:STDOUT: "textDocumentSync": 2,
|
||||
// CHECK:STDOUT: "typeDefinitionProvider": true
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 51{{\r}}
|
||||
|
||||
@@ -26,16 +26,21 @@
|
||||
|
||||
// --- AUTOUPDATE-SPLIT
|
||||
|
||||
// CHECK:STDOUT: Content-Length: 182{{\r}}
|
||||
// CHECK:STDOUT: Content-Length: 352{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 1,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": {
|
||||
// CHECK:STDOUT: "capabilities": {
|
||||
// CHECK:STDOUT: "declarationProvider": true,
|
||||
// CHECK:STDOUT: "definitionProvider": true,
|
||||
// CHECK:STDOUT: "documentSymbolProvider": true,
|
||||
// CHECK:STDOUT: "hoverProvider": true,
|
||||
// CHECK:STDOUT: "positionEncoding": "utf-16",
|
||||
// CHECK:STDOUT: "textDocumentSync": 2
|
||||
// CHECK:STDOUT: "referencesProvider": true,
|
||||
// CHECK:STDOUT: "textDocumentSync": 2,
|
||||
// CHECK:STDOUT: "typeDefinitionProvider": true
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 1164{{\r}}
|
||||
|
||||
@@ -28,16 +28,21 @@
|
||||
|
||||
// --- AUTOUPDATE-SPLIT
|
||||
|
||||
// CHECK:STDOUT: Content-Length: 182{{\r}}
|
||||
// CHECK:STDOUT: Content-Length: 352{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 1,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": {
|
||||
// CHECK:STDOUT: "capabilities": {
|
||||
// CHECK:STDOUT: "declarationProvider": true,
|
||||
// CHECK:STDOUT: "definitionProvider": true,
|
||||
// CHECK:STDOUT: "documentSymbolProvider": true,
|
||||
// CHECK:STDOUT: "hoverProvider": true,
|
||||
// CHECK:STDOUT: "positionEncoding": "utf-16",
|
||||
// CHECK:STDOUT: "textDocumentSync": 2
|
||||
// CHECK:STDOUT: "referencesProvider": true,
|
||||
// CHECK:STDOUT: "textDocumentSync": 2,
|
||||
// CHECK:STDOUT: "typeDefinitionProvider": true
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 162{{\r}}
|
||||
|
||||
@@ -107,16 +107,21 @@
|
||||
|
||||
// --- AUTOUPDATE-SPLIT
|
||||
|
||||
// CHECK:STDOUT: Content-Length: 182{{\r}}
|
||||
// CHECK:STDOUT: Content-Length: 352{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 1,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": {
|
||||
// CHECK:STDOUT: "capabilities": {
|
||||
// CHECK:STDOUT: "declarationProvider": true,
|
||||
// CHECK:STDOUT: "definitionProvider": true,
|
||||
// CHECK:STDOUT: "documentSymbolProvider": true,
|
||||
// CHECK:STDOUT: "hoverProvider": true,
|
||||
// CHECK:STDOUT: "positionEncoding": "utf-16",
|
||||
// CHECK:STDOUT: "textDocumentSync": 2
|
||||
// CHECK:STDOUT: "referencesProvider": true,
|
||||
// CHECK:STDOUT: "textDocumentSync": 2,
|
||||
// CHECK:STDOUT: "typeDefinitionProvider": true
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 144{{\r}}
|
||||
|
||||
+7
-2
@@ -35,16 +35,21 @@
|
||||
|
||||
// --- AUTOUPDATE-SPLIT
|
||||
|
||||
// CHECK:STDOUT: Content-Length: 182{{\r}}
|
||||
// CHECK:STDOUT: Content-Length: 352{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 1,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": {
|
||||
// CHECK:STDOUT: "capabilities": {
|
||||
// CHECK:STDOUT: "declarationProvider": true,
|
||||
// CHECK:STDOUT: "definitionProvider": true,
|
||||
// CHECK:STDOUT: "documentSymbolProvider": true,
|
||||
// CHECK:STDOUT: "hoverProvider": true,
|
||||
// CHECK:STDOUT: "positionEncoding": "utf-16",
|
||||
// CHECK:STDOUT: "textDocumentSync": 2
|
||||
// CHECK:STDOUT: "referencesProvider": true,
|
||||
// CHECK:STDOUT: "textDocumentSync": 2,
|
||||
// CHECK:STDOUT: "typeDefinitionProvider": true
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 144{{\r}}
|
||||
|
||||
@@ -25,16 +25,21 @@
|
||||
|
||||
// --- AUTOUPDATE-SPLIT
|
||||
|
||||
// CHECK:STDOUT: Content-Length: 182{{\r}}
|
||||
// CHECK:STDOUT: Content-Length: 352{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 1,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": {
|
||||
// CHECK:STDOUT: "capabilities": {
|
||||
// CHECK:STDOUT: "declarationProvider": true,
|
||||
// CHECK:STDOUT: "definitionProvider": true,
|
||||
// CHECK:STDOUT: "documentSymbolProvider": true,
|
||||
// CHECK:STDOUT: "hoverProvider": true,
|
||||
// CHECK:STDOUT: "positionEncoding": "utf-16",
|
||||
// CHECK:STDOUT: "textDocumentSync": 2
|
||||
// CHECK:STDOUT: "referencesProvider": true,
|
||||
// CHECK:STDOUT: "textDocumentSync": 2,
|
||||
// CHECK:STDOUT: "typeDefinitionProvider": true
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 161{{\r}}
|
||||
|
||||
@@ -26,16 +26,21 @@
|
||||
|
||||
// --- AUTOUPDATE-SPLIT
|
||||
|
||||
// CHECK:STDOUT: Content-Length: 182{{\r}}
|
||||
// CHECK:STDOUT: Content-Length: 352{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 1,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": {
|
||||
// CHECK:STDOUT: "capabilities": {
|
||||
// CHECK:STDOUT: "declarationProvider": true,
|
||||
// CHECK:STDOUT: "definitionProvider": true,
|
||||
// CHECK:STDOUT: "documentSymbolProvider": true,
|
||||
// CHECK:STDOUT: "hoverProvider": true,
|
||||
// CHECK:STDOUT: "positionEncoding": "utf-16",
|
||||
// CHECK:STDOUT: "textDocumentSync": 2
|
||||
// CHECK:STDOUT: "referencesProvider": true,
|
||||
// CHECK:STDOUT: "textDocumentSync": 2,
|
||||
// CHECK:STDOUT: "typeDefinitionProvider": true
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 144{{\r}}
|
||||
|
||||
@@ -25,16 +25,21 @@
|
||||
|
||||
// CHECK:STDERR: /test.carbon: warning: duplicate open file request; updating content [LanguageServerOpenDuplicateFile]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDOUT: Content-Length: 182{{\r}}
|
||||
// CHECK:STDOUT: Content-Length: 352{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 1,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": {
|
||||
// CHECK:STDOUT: "capabilities": {
|
||||
// CHECK:STDOUT: "declarationProvider": true,
|
||||
// CHECK:STDOUT: "definitionProvider": true,
|
||||
// CHECK:STDOUT: "documentSymbolProvider": true,
|
||||
// CHECK:STDOUT: "hoverProvider": true,
|
||||
// CHECK:STDOUT: "positionEncoding": "utf-16",
|
||||
// CHECK:STDOUT: "textDocumentSync": 2
|
||||
// CHECK:STDOUT: "referencesProvider": true,
|
||||
// CHECK:STDOUT: "textDocumentSync": 2,
|
||||
// CHECK:STDOUT: "typeDefinitionProvider": true
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 144{{\r}}
|
||||
|
||||
+7
-2
@@ -22,16 +22,21 @@
|
||||
|
||||
// --- AUTOUPDATE-SPLIT
|
||||
|
||||
// CHECK:STDOUT: Content-Length: 182{{\r}}
|
||||
// CHECK:STDOUT: Content-Length: 352{{\r}}
|
||||
// CHECK:STDOUT: {{\r}}
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: "id": 1,
|
||||
// CHECK:STDOUT: "jsonrpc": "2.0",
|
||||
// CHECK:STDOUT: "result": {
|
||||
// CHECK:STDOUT: "capabilities": {
|
||||
// CHECK:STDOUT: "declarationProvider": true,
|
||||
// CHECK:STDOUT: "definitionProvider": true,
|
||||
// CHECK:STDOUT: "documentSymbolProvider": true,
|
||||
// CHECK:STDOUT: "hoverProvider": true,
|
||||
// CHECK:STDOUT: "positionEncoding": "utf-16",
|
||||
// CHECK:STDOUT: "textDocumentSync": 2
|
||||
// CHECK:STDOUT: "referencesProvider": true,
|
||||
// CHECK:STDOUT: "textDocumentSync": 2,
|
||||
// CHECK:STDOUT: "typeDefinitionProvider": true
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }Content-Length: 464{{\r}}
|
||||
|
||||
Reference in New Issue
Block a user