mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-25 00:55:08 +00:00
Remove WithFileContent & lex_utils.{cc,h}
This commit is contained in:
parent
da545f1139
commit
34dc1e93e9
@ -202,7 +202,6 @@ target_sources(ccls PRIVATE
|
||||
src/indexer.cc
|
||||
src/method.cc
|
||||
src/language.cc
|
||||
src/lex_utils.cc
|
||||
src/log.cc
|
||||
src/lsp.cc
|
||||
src/match.cc
|
||||
|
@ -3,9 +3,11 @@
|
||||
#include "log.hh"
|
||||
#include "platform.h"
|
||||
#include "serializer.h"
|
||||
#include "timer.h"
|
||||
#include "type_printer.h"
|
||||
|
||||
#include <llvm/Support/Timer.h>
|
||||
using namespace llvm;
|
||||
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
@ -2016,7 +2018,8 @@ std::vector<std::unique_ptr<IndexFile>> ClangIndexer::Index(
|
||||
|
||||
file = NormalizePath(file);
|
||||
|
||||
Timer timer;
|
||||
Timer timer("parse", "parse tu");
|
||||
timer.startTimer();
|
||||
|
||||
std::vector<CXUnsavedFile> unsaved_files;
|
||||
for (const FileContents& contents : file_contents) {
|
||||
@ -2034,7 +2037,7 @@ std::vector<std::unique_ptr<IndexFile>> ClangIndexer::Index(
|
||||
if (!tu)
|
||||
return {};
|
||||
|
||||
perf->index_parse = timer.ElapsedMicrosecondsAndReset();
|
||||
timer.stopTimer();
|
||||
|
||||
return ParseWithTu(vfs, perf, tu.get(), &index, file, args, unsaved_files);
|
||||
}
|
||||
@ -2048,6 +2051,7 @@ std::vector<std::unique_ptr<IndexFile>> ParseWithTu(
|
||||
const std::vector<std::string>& args,
|
||||
const std::vector<CXUnsavedFile>& file_contents) {
|
||||
Timer timer;
|
||||
timer.startTimer();
|
||||
|
||||
IndexerCallbacks callback = {0};
|
||||
// Available callbacks:
|
||||
@ -2085,13 +2089,12 @@ std::vector<std::unique_ptr<IndexFile>> ParseWithTu(
|
||||
<< " failed with errno=" << index_result;
|
||||
return {};
|
||||
}
|
||||
|
||||
clang_IndexAction_dispose(index_action);
|
||||
|
||||
ClangCursor(clang_getTranslationUnitCursor(tu->cx_tu))
|
||||
.VisitChildren(&VisitMacroDefinitionAndExpansions, ¶m);
|
||||
|
||||
perf->index_build = timer.ElapsedMicrosecondsAndReset();
|
||||
timer.stopTimer();
|
||||
|
||||
std::unordered_map<std::string, int> inc_to_line;
|
||||
// TODO
|
||||
|
@ -27,8 +27,6 @@ struct IndexFunc;
|
||||
struct IndexVar;
|
||||
struct QueryFile;
|
||||
|
||||
using RawId = uint32_t;
|
||||
|
||||
struct SymbolIdx {
|
||||
Usr usr;
|
||||
SymbolKind kind;
|
||||
@ -153,19 +151,9 @@ struct TypeDef : NameMixin<TypeDef> {
|
||||
NtString hover;
|
||||
NtString comments;
|
||||
|
||||
// While a class/type can technically have a separate declaration/definition,
|
||||
// it doesn't really happen in practice. The declaration never contains
|
||||
// comments or insightful information. The user always wants to jump from
|
||||
// the declaration to the definition - never the other way around like in
|
||||
// functions and (less often) variables.
|
||||
//
|
||||
// It's also difficult to identify a `class Foo;` statement with the clang
|
||||
// indexer API (it's doable using cursor AST traversal), so we don't bother
|
||||
// supporting the feature.
|
||||
Maybe<Use> spell;
|
||||
Maybe<Use> extent;
|
||||
|
||||
// Immediate parent types.
|
||||
std::vector<Usr> bases;
|
||||
|
||||
// Types, functions, and variables defined in this type.
|
||||
@ -221,8 +209,6 @@ struct VarDef : NameMixin<VarDef> {
|
||||
std::string detailed_name;
|
||||
NtString hover;
|
||||
NtString comments;
|
||||
// TODO: definitions should be a list of ranges, since there can be more
|
||||
// than one - when??
|
||||
Maybe<Use> spell;
|
||||
Maybe<Use> extent;
|
||||
|
||||
|
@ -1,87 +0,0 @@
|
||||
#include "lex_utils.h"
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
// VSCode (UTF-16) disagrees with Emacs lsp-mode (UTF-8) on how to represent
|
||||
// text documents.
|
||||
// We use a UTF-8 iterator to approximate UTF-16 in the specification (weird).
|
||||
// This is good enough and fails only for UTF-16 surrogate pairs.
|
||||
int GetOffsetForPosition(lsPosition position, std::string_view content) {
|
||||
size_t i = 0;
|
||||
for (; position.line > 0 && i < content.size(); i++)
|
||||
if (content[i] == '\n')
|
||||
position.line--;
|
||||
for (; position.character > 0 && i < content.size(); position.character--)
|
||||
if (uint8_t(content[i++]) >= 128) {
|
||||
// Skip 0b10xxxxxx
|
||||
while (i < content.size() && uint8_t(content[i]) >= 128 &&
|
||||
uint8_t(content[i]) < 192)
|
||||
i++;
|
||||
}
|
||||
return int(i);
|
||||
}
|
||||
|
||||
std::string_view LexIdentifierAroundPos(lsPosition position,
|
||||
std::string_view content) {
|
||||
int start = GetOffsetForPosition(position, content);
|
||||
int end = start + 1;
|
||||
char c;
|
||||
|
||||
// We search for :: before the cursor but not after to get the qualifier.
|
||||
for (; start > 0; start--) {
|
||||
c = content[start - 1];
|
||||
if (isalnum(c) || c == '_')
|
||||
;
|
||||
else if (c == ':' && start > 1 && content[start - 2] == ':')
|
||||
start--;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
for (; end < (int)content.size(); end++)
|
||||
if (c = content[end], !(isalnum(c) || c == '_'))
|
||||
break;
|
||||
|
||||
return content.substr(start, end - start);
|
||||
}
|
||||
|
||||
// Find discontinous |search| in |content|.
|
||||
// Return |found| and the count of skipped chars before found.
|
||||
int ReverseSubseqMatch(std::string_view pat,
|
||||
std::string_view text,
|
||||
int case_sensitivity) {
|
||||
if (case_sensitivity == 1)
|
||||
case_sensitivity = std::any_of(pat.begin(), pat.end(), isupper) ? 2 : 0;
|
||||
int j = pat.size();
|
||||
if (!j)
|
||||
return text.size();
|
||||
for (int i = text.size(); i--;)
|
||||
if ((case_sensitivity ? text[i] == pat[j - 1]
|
||||
: tolower(text[i]) == tolower(pat[j - 1])) &&
|
||||
!--j)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
TEST_SUITE("Offset") {
|
||||
TEST_CASE("past end") {
|
||||
std::string content = "foo";
|
||||
int offset = GetOffsetForPosition(lsPosition{10, 10}, content);
|
||||
REQUIRE(offset <= content.size());
|
||||
}
|
||||
|
||||
TEST_CASE("in middle of content") {
|
||||
std::string content = "abcdefghijk";
|
||||
for (int i = 0; i < content.size(); ++i) {
|
||||
int offset = GetOffsetForPosition(lsPosition{0, i}, content);
|
||||
REQUIRE(i == offset);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("at end of content") {
|
||||
REQUIRE(GetOffsetForPosition(lsPosition{0, 0}, "") == 0);
|
||||
REQUIRE(GetOffsetForPosition(lsPosition{0, 1}, "a") == 1);
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "lsp.h"
|
||||
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
|
||||
// Utility method to map |position| to an offset inside of |content|.
|
||||
int GetOffsetForPosition(lsPosition position, std::string_view content);
|
||||
|
||||
std::string_view LexIdentifierAroundPos(lsPosition position,
|
||||
std::string_view content);
|
||||
|
||||
int ReverseSubseqMatch(std::string_view pat,
|
||||
std::string_view text,
|
||||
int case_sensitivity);
|
@ -129,12 +129,10 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_map<MethodType, Timer> request_times;
|
||||
|
||||
// The thread that reads from stdin and dispatchs commands to the main thread.
|
||||
pipeline::LaunchStdin(&request_times);
|
||||
pipeline::LaunchStdin();
|
||||
// The thread that writes responses from the main thread to stdout.
|
||||
pipeline::LaunchStdout(&request_times);
|
||||
pipeline::LaunchStdout();
|
||||
// Main thread which also spawns indexer threads upon the "initialize" request.
|
||||
pipeline::MainLoop();
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include "message_handler.h"
|
||||
|
||||
#include "lex_utils.h"
|
||||
#include "log.hh"
|
||||
#include "project.h"
|
||||
#include "query_utils.h"
|
||||
|
@ -7,8 +7,6 @@
|
||||
#include "working_files.h"
|
||||
using namespace ccls;
|
||||
|
||||
#include "lex_utils.h"
|
||||
|
||||
#include <regex>
|
||||
|
||||
namespace {
|
||||
|
@ -1,4 +1,3 @@
|
||||
#include "lex_utils.h"
|
||||
#include "message_handler.h"
|
||||
#include "pipeline.hh"
|
||||
#include "query_utils.h"
|
||||
|
@ -1,72 +0,0 @@
|
||||
#include "clang_format.h"
|
||||
#include "lex_utils.h"
|
||||
#include "message_handler.h"
|
||||
#include "pipeline.hh"
|
||||
using namespace ccls;
|
||||
#include "working_files.h"
|
||||
|
||||
#include <loguru.hpp>
|
||||
|
||||
namespace {
|
||||
MethodType kMethodType = "textDocument/rangeFormatting";
|
||||
|
||||
struct lsTextDocumentRangeFormattingParams {
|
||||
lsTextDocumentIdentifier textDocument;
|
||||
lsRange range;
|
||||
lsFormattingOptions options;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(lsTextDocumentRangeFormattingParams,
|
||||
textDocument,
|
||||
range,
|
||||
options);
|
||||
|
||||
struct In_TextDocumentRangeFormatting : public RequestInMessage {
|
||||
MethodType GetMethodType() const override { return kMethodType; }
|
||||
lsTextDocumentRangeFormattingParams params;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(In_TextDocumentRangeFormatting, id, params);
|
||||
REGISTER_IN_MESSAGE(In_TextDocumentRangeFormatting);
|
||||
|
||||
struct Out_TextDocumentRangeFormatting
|
||||
: public lsOutMessage<Out_TextDocumentRangeFormatting> {
|
||||
lsRequestId id;
|
||||
std::vector<lsTextEdit> result;
|
||||
};
|
||||
MAKE_REFLECT_STRUCT(Out_TextDocumentRangeFormatting, jsonrpc, id, result);
|
||||
|
||||
struct Handler_TextDocumentRangeFormatting
|
||||
: BaseMessageHandler<In_TextDocumentRangeFormatting> {
|
||||
MethodType GetMethodType() const override { return kMethodType; }
|
||||
|
||||
void Run(In_TextDocumentRangeFormatting* request) override {
|
||||
Out_TextDocumentRangeFormatting response;
|
||||
response.id = request->id;
|
||||
#if USE_CLANG_CXX
|
||||
QueryFile* file;
|
||||
if (!FindFileOrFail(db, project, request->id,
|
||||
request->params.textDocument.uri.GetPath(), &file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
WorkingFile* working_file =
|
||||
working_files->GetFileByFilename(file->def->path);
|
||||
|
||||
int start = GetOffsetForPosition(request->params.range.start,
|
||||
working_file->buffer_content),
|
||||
end = GetOffsetForPosition(request->params.range.end,
|
||||
working_file->buffer_content);
|
||||
response.result = ConvertClangReplacementsIntoTextEdits(
|
||||
working_file->buffer_content,
|
||||
ClangFormatDocument(working_file, start, end, request->params.options));
|
||||
#else
|
||||
LOG_S(WARNING) << "You must compile ccls with --use-clang-cxx to use "
|
||||
"textDocument/rangeFormatting.";
|
||||
// TODO: Fallback to execute the clang-format binary?
|
||||
response.result = {};
|
||||
#endif
|
||||
|
||||
pipeline::WriteStdout(kMethodType, response);
|
||||
}
|
||||
};
|
||||
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentRangeFormatting);
|
||||
} // namespace
|
@ -1,5 +1,4 @@
|
||||
#include "fuzzy_match.h"
|
||||
#include "lex_utils.h"
|
||||
#include "message_handler.h"
|
||||
#include "pipeline.hh"
|
||||
#include "query_utils.h"
|
||||
|
@ -11,10 +11,10 @@
|
||||
#include "project.h"
|
||||
#include "query_utils.h"
|
||||
#include "pipeline.hh"
|
||||
#include "timer.h"
|
||||
|
||||
#include <llvm/ADT/Twine.h>
|
||||
#include <llvm/Support/Threading.h>
|
||||
#include <llvm/Support/Timer.h>
|
||||
using namespace llvm;
|
||||
|
||||
#include <thread>
|
||||
@ -237,14 +237,15 @@ bool Indexer_Parse(DiagnosticsEngine* diag_engine,
|
||||
|
||||
// Write current index to disk if requested.
|
||||
LOG_S(INFO) << "store index for " << path;
|
||||
Timer time;
|
||||
{
|
||||
Timer timer("write", "store index");
|
||||
timer.startTimer();
|
||||
std::string cache_path = GetCachePath(path);
|
||||
WriteToFile(cache_path, curr->file_contents);
|
||||
WriteToFile(AppendSerializationFormat(cache_path),
|
||||
Serialize(g_config->cacheFormat, *curr));
|
||||
timer.stopTimer();
|
||||
}
|
||||
perf.index_save_to_disk = time.ElapsedMicrosecondsAndReset();
|
||||
|
||||
vfs->Reset(path_to_index);
|
||||
if (entry.id >= 0) {
|
||||
@ -255,7 +256,6 @@ bool Indexer_Parse(DiagnosticsEngine* diag_engine,
|
||||
|
||||
// Build delta update.
|
||||
IndexUpdate update = IndexUpdate::CreateDelta(prev.get(), curr.get());
|
||||
perf.index_make_delta = time.ElapsedMicrosecondsAndReset();
|
||||
LOG_S(INFO) << "built index for " << path << " (is_delta=" << !!prev << ")";
|
||||
|
||||
on_indexed->PushBack({std::move(update), perf}, request.is_interactive);
|
||||
@ -264,15 +264,6 @@ bool Indexer_Parse(DiagnosticsEngine* diag_engine,
|
||||
return true;
|
||||
}
|
||||
|
||||
// This function returns true if e2e timing should be displayed for the given
|
||||
// MethodId.
|
||||
bool ShouldDisplayMethodTiming(MethodType type) {
|
||||
return
|
||||
type != kMethodType_TextDocumentPublishDiagnostics &&
|
||||
type != kMethodType_CclsPublishInactiveRegions &&
|
||||
type != kMethodType_Unknown;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void Init() {
|
||||
@ -316,20 +307,22 @@ void Main_OnIndexed(DB* db,
|
||||
return;
|
||||
}
|
||||
|
||||
Timer time;
|
||||
Timer timer("apply", "apply index");
|
||||
timer.startTimer();
|
||||
db->ApplyIndexUpdate(&response->update);
|
||||
timer.stopTimer();
|
||||
|
||||
// Update indexed content, inactive lines, and semantic highlighting.
|
||||
if (response->update.files_def_update) {
|
||||
auto& update = *response->update.files_def_update;
|
||||
time.ResetAndPrint("apply index for " + update.value.path);
|
||||
LOG_S(INFO) << "apply index for " << update.first.path;
|
||||
if (WorkingFile* working_file =
|
||||
working_files->GetFileByFilename(update.value.path)) {
|
||||
working_files->GetFileByFilename(update.first.path)) {
|
||||
// Update indexed content.
|
||||
working_file->SetIndexContent(update.file_content);
|
||||
working_file->SetIndexContent(update.second);
|
||||
|
||||
// Inactive lines.
|
||||
EmitInactiveLines(working_file, update.value.inactive_regions);
|
||||
EmitInactiveLines(working_file, update.first.inactive_regions);
|
||||
|
||||
// Semantic highlighting.
|
||||
int file_id =
|
||||
@ -340,8 +333,8 @@ void Main_OnIndexed(DB* db,
|
||||
}
|
||||
}
|
||||
|
||||
void LaunchStdin(std::unordered_map<MethodType, Timer>* request_times) {
|
||||
std::thread([request_times]() {
|
||||
void LaunchStdin() {
|
||||
std::thread([]() {
|
||||
set_thread_name("stdin");
|
||||
while (true) {
|
||||
std::unique_ptr<InMessage> message;
|
||||
@ -367,7 +360,6 @@ void LaunchStdin(std::unordered_map<MethodType, Timer>* request_times) {
|
||||
|
||||
// Cache |method_id| so we can access it after moving |message|.
|
||||
MethodType method_type = message->GetMethodType();
|
||||
(*request_times)[method_type] = Timer();
|
||||
|
||||
on_request->PushBack(std::move(message));
|
||||
|
||||
@ -379,7 +371,7 @@ void LaunchStdin(std::unordered_map<MethodType, Timer>* request_times) {
|
||||
}).detach();
|
||||
}
|
||||
|
||||
void LaunchStdout(std::unordered_map<MethodType, Timer>* request_times) {
|
||||
void LaunchStdout() {
|
||||
std::thread([=]() {
|
||||
set_thread_name("stdout");
|
||||
|
||||
@ -391,11 +383,6 @@ void LaunchStdout(std::unordered_map<MethodType, Timer>* request_times) {
|
||||
}
|
||||
|
||||
for (auto& message : messages) {
|
||||
if (ShouldDisplayMethodTiming(message.method)) {
|
||||
Timer time = (*request_times)[message.method];
|
||||
time.ResetAndPrint("[e2e] Running " + std::string(message.method));
|
||||
}
|
||||
|
||||
fwrite(message.content.c_str(), message.content.size(), 1, stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
#include "method.h"
|
||||
#include "query.h"
|
||||
#include "timer.h"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
@ -17,8 +16,8 @@ struct lsBaseOutMessage;
|
||||
namespace ccls::pipeline {
|
||||
|
||||
void Init();
|
||||
void LaunchStdin(std::unordered_map<MethodType, Timer>* request_times);
|
||||
void LaunchStdout(std::unordered_map<MethodType, Timer>* request_times);
|
||||
void LaunchStdin();
|
||||
void LaunchStdout();
|
||||
void Indexer_Main(DiagnosticsEngine* diag_engine,
|
||||
VFS* vfs,
|
||||
Project* project,
|
||||
|
@ -148,7 +148,7 @@ QueryFile::DefUpdate BuildFileDefUpdate(const IndexFile& indexed) {
|
||||
return a.range.start < b.range.start;
|
||||
});
|
||||
|
||||
return QueryFile::DefUpdate(def, indexed.file_contents);
|
||||
return {std::move(def), std::move(indexed.file_contents)};
|
||||
}
|
||||
|
||||
// Returns true if an element with the same file is found.
|
||||
@ -342,11 +342,11 @@ void DB::ApplyIndexUpdate(IndexUpdate* u) {
|
||||
|
||||
int DB::Update(QueryFile::DefUpdate&& u) {
|
||||
int id = files.size();
|
||||
auto it = name2file_id.try_emplace(LowerPathIfInsensitive(u.value.path), id);
|
||||
auto it = name2file_id.try_emplace(LowerPathIfInsensitive(u.first.path), id);
|
||||
if (it.second)
|
||||
files.emplace_back().id = id;
|
||||
QueryFile& existing = files[it.first->second];
|
||||
existing.def = u.value;
|
||||
existing.def = u.first;
|
||||
return existing.id;
|
||||
}
|
||||
|
||||
|
17
src/query.h
17
src/query.h
@ -7,21 +7,6 @@
|
||||
#include <llvm/ADT/SmallVector.h>
|
||||
#include <llvm/ADT/StringMap.h>
|
||||
|
||||
struct QueryFile;
|
||||
struct QueryType;
|
||||
struct QueryFunc;
|
||||
struct QueryVar;
|
||||
struct DB;
|
||||
|
||||
template <typename T>
|
||||
struct WithFileContent {
|
||||
T value;
|
||||
std::string file_content;
|
||||
|
||||
WithFileContent(const T& value, const std::string& file_content)
|
||||
: value(value), file_content(file_content) {}
|
||||
};
|
||||
|
||||
struct QueryFile {
|
||||
struct Def {
|
||||
std::string path;
|
||||
@ -39,7 +24,7 @@ struct QueryFile {
|
||||
std::vector<std::string> dependencies;
|
||||
};
|
||||
|
||||
using DefUpdate = WithFileContent<Def>;
|
||||
using DefUpdate = std::pair<Def, std::string>;
|
||||
|
||||
int id = -1;
|
||||
std::optional<Def> def;
|
||||
|
18
src/utils.cc
18
src/utils.cc
@ -134,6 +134,24 @@ std::optional<int64_t> LastWriteTime(const std::string& filename) {
|
||||
return Status.getLastModificationTime().time_since_epoch().count();
|
||||
}
|
||||
|
||||
// Find discontinous |search| in |content|.
|
||||
// Return |found| and the count of skipped chars before found.
|
||||
int ReverseSubseqMatch(std::string_view pat,
|
||||
std::string_view text,
|
||||
int case_sensitivity) {
|
||||
if (case_sensitivity == 1)
|
||||
case_sensitivity = std::any_of(pat.begin(), pat.end(), isupper) ? 2 : 0;
|
||||
int j = pat.size();
|
||||
if (!j)
|
||||
return text.size();
|
||||
for (int i = text.size(); i--;)
|
||||
if ((case_sensitivity ? text[i] == pat[j - 1]
|
||||
: tolower(text[i]) == tolower(pat[j - 1])) &&
|
||||
!--j)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string GetDefaultResourceDirectory() {
|
||||
return DEFAULT_RESOURCE_DIRECTORY;
|
||||
}
|
||||
|
@ -58,6 +58,10 @@ std::optional<std::string> ReadContent(const std::string& filename);
|
||||
void WriteToFile(const std::string& filename, const std::string& content);
|
||||
std::optional<int64_t> LastWriteTime(const std::string& filename);
|
||||
|
||||
int ReverseSubseqMatch(std::string_view pat,
|
||||
std::string_view text,
|
||||
int case_sensitivity);
|
||||
|
||||
// http://stackoverflow.com/a/38140932
|
||||
//
|
||||
// struct SomeHashKey {
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include "working_files.h"
|
||||
|
||||
#include "lex_utils.h"
|
||||
#include "log.hh"
|
||||
#include "position.h"
|
||||
|
||||
@ -545,3 +544,46 @@ WorkingFiles::Snapshot WorkingFiles::AsSnapshot(
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// VSCode (UTF-16) disagrees with Emacs lsp-mode (UTF-8) on how to represent
|
||||
// text documents.
|
||||
// We use a UTF-8 iterator to approximate UTF-16 in the specification (weird).
|
||||
// This is good enough and fails only for UTF-16 surrogate pairs.
|
||||
int GetOffsetForPosition(lsPosition position, std::string_view content) {
|
||||
size_t i = 0;
|
||||
for (; position.line > 0 && i < content.size(); i++)
|
||||
if (content[i] == '\n')
|
||||
position.line--;
|
||||
for (; position.character > 0 && i < content.size(); position.character--)
|
||||
if (uint8_t(content[i++]) >= 128) {
|
||||
// Skip 0b10xxxxxx
|
||||
while (i < content.size() && uint8_t(content[i]) >= 128 &&
|
||||
uint8_t(content[i]) < 192)
|
||||
i++;
|
||||
}
|
||||
return int(i);
|
||||
}
|
||||
|
||||
std::string_view LexIdentifierAroundPos(lsPosition position,
|
||||
std::string_view content) {
|
||||
int start = GetOffsetForPosition(position, content);
|
||||
int end = start + 1;
|
||||
char c;
|
||||
|
||||
// We search for :: before the cursor but not after to get the qualifier.
|
||||
for (; start > 0; start--) {
|
||||
c = content[start - 1];
|
||||
if (isalnum(c) || c == '_')
|
||||
;
|
||||
else if (c == ':' && start > 1 && content[start - 2] == ':')
|
||||
start--;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
for (; end < (int)content.size(); end++)
|
||||
if (c = content[end], !(isalnum(c) || c == '_'))
|
||||
break;
|
||||
|
||||
return content.substr(start, end - start);
|
||||
}
|
||||
|
@ -117,3 +117,8 @@ struct WorkingFiles {
|
||||
std::vector<std::unique_ptr<WorkingFile>> files;
|
||||
std::mutex files_mutex; // Protects |files|.
|
||||
};
|
||||
|
||||
int GetOffsetForPosition(lsPosition position, std::string_view content);
|
||||
|
||||
std::string_view LexIdentifierAroundPos(lsPosition position,
|
||||
std::string_view content);
|
||||
|
Loading…
Reference in New Issue
Block a user